@uncefact/untp-utils 0.2.0 → 0.4.0

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 (262) hide show
  1. package/README.md +40 -12
  2. package/artefacts/context/untp/0.6.0/dcc.json +1241 -0
  3. package/artefacts/context/untp/0.6.0/dfr.json +619 -0
  4. package/artefacts/context/untp/0.6.0/dia.json +162 -0
  5. package/artefacts/context/untp/0.6.0/dpp.json +1078 -0
  6. package/artefacts/context/untp/0.6.0/dte.json +1285 -0
  7. package/artefacts/context/untp/0.6.1/dcc.json +1388 -0
  8. package/artefacts/context/untp/0.6.1/dfr.json +690 -0
  9. package/artefacts/context/untp/0.6.1/dia.json +196 -0
  10. package/artefacts/context/untp/0.6.1/dpp.json +1164 -0
  11. package/artefacts/context/untp/0.6.1/dte.json +1340 -0
  12. package/artefacts/context/untp/0.7.0/untp.json +3493 -0
  13. package/artefacts/context/vcdm/2/credentials.json +301 -0
  14. package/artefacts/manifest.json +214 -0
  15. package/artefacts/schema/untp/0.6.0/dcc.json +1131 -0
  16. package/artefacts/schema/untp/0.6.0/dfr.json +1028 -0
  17. package/artefacts/schema/untp/0.6.0/dia.json +236 -0
  18. package/artefacts/schema/untp/0.6.0/dpp.json +1219 -0
  19. package/artefacts/schema/untp/0.6.0/dte.json +845 -0
  20. package/artefacts/schema/untp/0.6.1/dcc.json +1196 -0
  21. package/artefacts/schema/untp/0.6.1/dfr.json +1037 -0
  22. package/artefacts/schema/untp/0.6.1/dia.json +236 -0
  23. package/artefacts/schema/untp/0.6.1/dpp.json +1229 -0
  24. package/artefacts/schema/untp/0.6.1/dte.json +845 -0
  25. package/artefacts/schema/untp/0.7.0/cvc.json +701 -0
  26. package/artefacts/schema/untp/0.7.0/dcc.json +1133 -0
  27. package/artefacts/schema/untp/0.7.0/dfr.json +1245 -0
  28. package/artefacts/schema/untp/0.7.0/dia.json +443 -0
  29. package/artefacts/schema/untp/0.7.0/dpp.json +1300 -0
  30. package/artefacts/schema/untp/0.7.0/dte.json +1226 -0
  31. package/artefacts/schema/untp/0.7.0/linkset.json +119 -0
  32. package/artefacts/schema/vcdm/2/verifiable-credential.json +318 -0
  33. package/build/artefacts/detect-version-from-context.d.ts +7 -2
  34. package/build/artefacts/detect-version-from-context.js +17 -6
  35. package/build/artefacts/detect-version-from-context.js.map +1 -1
  36. package/build/artefacts/detect-version-from-context.test.js +59 -0
  37. package/build/artefacts/detect-version-from-context.test.js.map +1 -1
  38. package/build/artefacts/urls.d.ts +8 -1
  39. package/build/artefacts/urls.js +17 -5
  40. package/build/artefacts/urls.js.map +1 -1
  41. package/build/artefacts/urls.test.js +63 -1
  42. package/build/artefacts/urls.test.js.map +1 -1
  43. package/build/bundle/artefacts/context/untp/0.6.0/dcc.d.ts +2 -0
  44. package/build/bundle/artefacts/context/untp/0.6.0/dcc.js +1244 -0
  45. package/build/bundle/artefacts/context/untp/0.6.0/dcc.js.map +1 -0
  46. package/build/bundle/artefacts/context/untp/0.6.0/dfr.d.ts +2 -0
  47. package/build/bundle/artefacts/context/untp/0.6.0/dfr.js +622 -0
  48. package/build/bundle/artefacts/context/untp/0.6.0/dfr.js.map +1 -0
  49. package/build/bundle/artefacts/context/untp/0.6.0/dia.d.ts +2 -0
  50. package/build/bundle/artefacts/context/untp/0.6.0/dia.js +165 -0
  51. package/build/bundle/artefacts/context/untp/0.6.0/dia.js.map +1 -0
  52. package/build/bundle/artefacts/context/untp/0.6.0/dpp.d.ts +2 -0
  53. package/build/bundle/artefacts/context/untp/0.6.0/dpp.js +1081 -0
  54. package/build/bundle/artefacts/context/untp/0.6.0/dpp.js.map +1 -0
  55. package/build/bundle/artefacts/context/untp/0.6.0/dte.d.ts +2 -0
  56. package/build/bundle/artefacts/context/untp/0.6.0/dte.js +1288 -0
  57. package/build/bundle/artefacts/context/untp/0.6.0/dte.js.map +1 -0
  58. package/build/bundle/artefacts/context/untp/0.6.1/dcc.d.ts +2 -0
  59. package/build/bundle/artefacts/context/untp/0.6.1/dcc.js +1391 -0
  60. package/build/bundle/artefacts/context/untp/0.6.1/dcc.js.map +1 -0
  61. package/build/bundle/artefacts/context/untp/0.6.1/dfr.d.ts +2 -0
  62. package/build/bundle/artefacts/context/untp/0.6.1/dfr.js +693 -0
  63. package/build/bundle/artefacts/context/untp/0.6.1/dfr.js.map +1 -0
  64. package/build/bundle/artefacts/context/untp/0.6.1/dia.d.ts +2 -0
  65. package/build/bundle/artefacts/context/untp/0.6.1/dia.js +199 -0
  66. package/build/bundle/artefacts/context/untp/0.6.1/dia.js.map +1 -0
  67. package/build/bundle/artefacts/context/untp/0.6.1/dpp.d.ts +2 -0
  68. package/build/bundle/artefacts/context/untp/0.6.1/dpp.js +1167 -0
  69. package/build/bundle/artefacts/context/untp/0.6.1/dpp.js.map +1 -0
  70. package/build/bundle/artefacts/context/untp/0.6.1/dte.d.ts +2 -0
  71. package/build/bundle/artefacts/context/untp/0.6.1/dte.js +1343 -0
  72. package/build/bundle/artefacts/context/untp/0.6.1/dte.js.map +1 -0
  73. package/build/bundle/artefacts/context/untp/0.7.0/untp.d.ts +2 -0
  74. package/build/bundle/artefacts/context/untp/0.7.0/untp.js +3496 -0
  75. package/build/bundle/artefacts/context/untp/0.7.0/untp.js.map +1 -0
  76. package/build/bundle/artefacts/context/vcdm/2/credentials.d.ts +2 -0
  77. package/build/bundle/artefacts/context/vcdm/2/credentials.js +304 -0
  78. package/build/bundle/artefacts/context/vcdm/2/credentials.js.map +1 -0
  79. package/build/bundle/artefacts/schema/untp/0.6.0/dcc.d.ts +2 -0
  80. package/build/bundle/artefacts/schema/untp/0.6.0/dcc.js +1134 -0
  81. package/build/bundle/artefacts/schema/untp/0.6.0/dcc.js.map +1 -0
  82. package/build/bundle/artefacts/schema/untp/0.6.0/dfr.d.ts +2 -0
  83. package/build/bundle/artefacts/schema/untp/0.6.0/dfr.js +1031 -0
  84. package/build/bundle/artefacts/schema/untp/0.6.0/dfr.js.map +1 -0
  85. package/build/bundle/artefacts/schema/untp/0.6.0/dia.d.ts +2 -0
  86. package/build/bundle/artefacts/schema/untp/0.6.0/dia.js +239 -0
  87. package/build/bundle/artefacts/schema/untp/0.6.0/dia.js.map +1 -0
  88. package/build/bundle/artefacts/schema/untp/0.6.0/dpp.d.ts +2 -0
  89. package/build/bundle/artefacts/schema/untp/0.6.0/dpp.js +1222 -0
  90. package/build/bundle/artefacts/schema/untp/0.6.0/dpp.js.map +1 -0
  91. package/build/bundle/artefacts/schema/untp/0.6.0/dte.d.ts +2 -0
  92. package/build/bundle/artefacts/schema/untp/0.6.0/dte.js +848 -0
  93. package/build/bundle/artefacts/schema/untp/0.6.0/dte.js.map +1 -0
  94. package/build/bundle/artefacts/schema/untp/0.6.1/dcc.d.ts +2 -0
  95. package/build/bundle/artefacts/schema/untp/0.6.1/dcc.js +1199 -0
  96. package/build/bundle/artefacts/schema/untp/0.6.1/dcc.js.map +1 -0
  97. package/build/bundle/artefacts/schema/untp/0.6.1/dfr.d.ts +2 -0
  98. package/build/bundle/artefacts/schema/untp/0.6.1/dfr.js +1040 -0
  99. package/build/bundle/artefacts/schema/untp/0.6.1/dfr.js.map +1 -0
  100. package/build/bundle/artefacts/schema/untp/0.6.1/dia.d.ts +2 -0
  101. package/build/bundle/artefacts/schema/untp/0.6.1/dia.js +239 -0
  102. package/build/bundle/artefacts/schema/untp/0.6.1/dia.js.map +1 -0
  103. package/build/bundle/artefacts/schema/untp/0.6.1/dpp.d.ts +2 -0
  104. package/build/bundle/artefacts/schema/untp/0.6.1/dpp.js +1232 -0
  105. package/build/bundle/artefacts/schema/untp/0.6.1/dpp.js.map +1 -0
  106. package/build/bundle/artefacts/schema/untp/0.6.1/dte.d.ts +2 -0
  107. package/build/bundle/artefacts/schema/untp/0.6.1/dte.js +848 -0
  108. package/build/bundle/artefacts/schema/untp/0.6.1/dte.js.map +1 -0
  109. package/build/bundle/artefacts/schema/untp/0.7.0/cvc.d.ts +2 -0
  110. package/build/bundle/artefacts/schema/untp/0.7.0/cvc.js +704 -0
  111. package/build/bundle/artefacts/schema/untp/0.7.0/cvc.js.map +1 -0
  112. package/build/bundle/artefacts/schema/untp/0.7.0/dcc.d.ts +2 -0
  113. package/build/bundle/artefacts/schema/untp/0.7.0/dcc.js +1136 -0
  114. package/build/bundle/artefacts/schema/untp/0.7.0/dcc.js.map +1 -0
  115. package/build/bundle/artefacts/schema/untp/0.7.0/dfr.d.ts +2 -0
  116. package/build/bundle/artefacts/schema/untp/0.7.0/dfr.js +1248 -0
  117. package/build/bundle/artefacts/schema/untp/0.7.0/dfr.js.map +1 -0
  118. package/build/bundle/artefacts/schema/untp/0.7.0/dia.d.ts +2 -0
  119. package/build/bundle/artefacts/schema/untp/0.7.0/dia.js +446 -0
  120. package/build/bundle/artefacts/schema/untp/0.7.0/dia.js.map +1 -0
  121. package/build/bundle/artefacts/schema/untp/0.7.0/dpp.d.ts +2 -0
  122. package/build/bundle/artefacts/schema/untp/0.7.0/dpp.js +1303 -0
  123. package/build/bundle/artefacts/schema/untp/0.7.0/dpp.js.map +1 -0
  124. package/build/bundle/artefacts/schema/untp/0.7.0/dte.d.ts +2 -0
  125. package/build/bundle/artefacts/schema/untp/0.7.0/dte.js +1229 -0
  126. package/build/bundle/artefacts/schema/untp/0.7.0/dte.js.map +1 -0
  127. package/build/bundle/artefacts/schema/untp/0.7.0/linkset.d.ts +2 -0
  128. package/build/bundle/artefacts/schema/untp/0.7.0/linkset.js +122 -0
  129. package/build/bundle/artefacts/schema/untp/0.7.0/linkset.js.map +1 -0
  130. package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.d.ts +2 -0
  131. package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.js +321 -0
  132. package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.js.map +1 -0
  133. package/build/bundle/fallback.d.ts +41 -0
  134. package/build/bundle/fallback.js +62 -0
  135. package/build/bundle/fallback.js.map +1 -0
  136. package/build/bundle/fallback.test.d.ts +1 -0
  137. package/build/bundle/fallback.test.js +124 -0
  138. package/build/bundle/fallback.test.js.map +1 -0
  139. package/build/bundle/index.d.ts +4 -0
  140. package/build/bundle/index.js +77 -0
  141. package/build/bundle/index.js.map +1 -0
  142. package/build/bundle/lookup.d.ts +14 -0
  143. package/build/bundle/lookup.js +40 -0
  144. package/build/bundle/lookup.js.map +1 -0
  145. package/build/bundle/lookup.test.d.ts +1 -0
  146. package/build/bundle/lookup.test.js +70 -0
  147. package/build/bundle/lookup.test.js.map +1 -0
  148. package/build/bundle/public.d.ts +29 -0
  149. package/build/bundle/public.js +60 -0
  150. package/build/bundle/public.js.map +1 -0
  151. package/build/bundle/public.test.d.ts +1 -0
  152. package/build/bundle/public.test.js +41 -0
  153. package/build/bundle/public.test.js.map +1 -0
  154. package/build/bundle/refresh-classify.test.d.ts +1 -0
  155. package/build/bundle/refresh-classify.test.js +20 -0
  156. package/build/bundle/refresh-classify.test.js.map +1 -0
  157. package/build/bundle/refresh-main.test.d.ts +1 -0
  158. package/build/bundle/refresh-main.test.js +98 -0
  159. package/build/bundle/refresh-main.test.js.map +1 -0
  160. package/build/common/as-date-time.d.ts +13 -0
  161. package/build/common/as-date-time.js +44 -0
  162. package/build/common/as-date-time.js.map +1 -0
  163. package/build/common/as-date-time.test.d.ts +1 -0
  164. package/build/common/as-date-time.test.js +42 -0
  165. package/build/common/as-date-time.test.js.map +1 -0
  166. package/build/common/as-non-empty-string.d.ts +11 -4
  167. package/build/common/as-non-empty-string.js +15 -5
  168. package/build/common/as-non-empty-string.js.map +1 -1
  169. package/build/common/as-non-empty-string.test.js +7 -2
  170. package/build/common/as-non-empty-string.test.js.map +1 -1
  171. package/build/common/canonical-json.d.ts +10 -0
  172. package/build/common/canonical-json.js +29 -0
  173. package/build/common/canonical-json.js.map +1 -0
  174. package/build/common/canonical-json.test.d.ts +1 -0
  175. package/build/common/canonical-json.test.js +20 -0
  176. package/build/common/canonical-json.test.js.map +1 -0
  177. package/build/common/index.d.ts +5 -0
  178. package/build/common/index.js +6 -0
  179. package/build/common/index.js.map +1 -0
  180. package/build/common/validity-window.d.ts +45 -0
  181. package/build/common/validity-window.js +68 -0
  182. package/build/common/validity-window.js.map +1 -0
  183. package/build/common/validity-window.test.d.ts +1 -0
  184. package/build/common/validity-window.test.js +67 -0
  185. package/build/common/validity-window.test.js.map +1 -0
  186. package/build/conformity-vocabulary/codes.d.ts +4 -0
  187. package/build/conformity-vocabulary/codes.js +4 -0
  188. package/build/conformity-vocabulary/codes.js.map +1 -1
  189. package/build/conformity-vocabulary/parse-conformity-scheme.test.js +150 -0
  190. package/build/conformity-vocabulary/parse-conformity-scheme.test.js.map +1 -1
  191. package/build/conformity-vocabulary/parsers/v0-7-0.parser.js +225 -0
  192. package/build/conformity-vocabulary/parsers/v0-7-0.parser.js.map +1 -1
  193. package/build/conformity-vocabulary/types.d.ts +69 -15
  194. package/build/conformity-vocabulary/validate-conformity-claim.d.ts +17 -11
  195. package/build/conformity-vocabulary/validate-conformity-claim.js +118 -13
  196. package/build/conformity-vocabulary/validate-conformity-claim.js.map +1 -1
  197. package/build/conformity-vocabulary/validate-conformity-claim.test.js +265 -0
  198. package/build/conformity-vocabulary/validate-conformity-claim.test.js.map +1 -1
  199. package/build/loaders/index.d.ts +2 -0
  200. package/build/loaders/index.js +1 -0
  201. package/build/loaders/index.js.map +1 -1
  202. package/build/loaders/jsonld-document-loader.d.ts +7 -5
  203. package/build/loaders/jsonld-document-loader.js +11 -2
  204. package/build/loaders/jsonld-document-loader.js.map +1 -1
  205. package/build/loaders/jsonld-document-loader.test.js +41 -1
  206. package/build/loaders/jsonld-document-loader.test.js.map +1 -1
  207. package/build/loaders/schema-loader.d.ts +13 -3
  208. package/build/loaders/schema-loader.js +24 -8
  209. package/build/loaders/schema-loader.js.map +1 -1
  210. package/build/loaders/schema-loader.test.js +56 -1
  211. package/build/loaders/schema-loader.test.js.map +1 -1
  212. package/build/node/errors.d.ts +9 -3
  213. package/build/node/errors.js +9 -3
  214. package/build/node/errors.js.map +1 -1
  215. package/build/node/index.d.ts +1 -1
  216. package/build/node/index.js +1 -1
  217. package/build/node/index.js.map +1 -1
  218. package/build/node/is-private-ip.d.ts +8 -4
  219. package/build/node/is-private-ip.fail-closed.test.d.ts +1 -0
  220. package/build/node/is-private-ip.fail-closed.test.js +36 -0
  221. package/build/node/is-private-ip.fail-closed.test.js.map +1 -0
  222. package/build/node/is-private-ip.js +77 -12
  223. package/build/node/is-private-ip.js.map +1 -1
  224. package/build/node/is-private-ip.test.js +28 -0
  225. package/build/node/is-private-ip.test.js.map +1 -1
  226. package/build/node/validate-public-url.d.ts +43 -11
  227. package/build/node/validate-public-url.js +68 -34
  228. package/build/node/validate-public-url.js.map +1 -1
  229. package/build/node/validate-public-url.test.js +161 -14
  230. package/build/node/validate-public-url.test.js.map +1 -1
  231. package/build/resolvers/errors.d.ts +36 -6
  232. package/build/resolvers/errors.js +41 -7
  233. package/build/resolvers/errors.js.map +1 -1
  234. package/build/resolvers/resolve-document-if-changed.test.js +7 -0
  235. package/build/resolvers/resolve-document-if-changed.test.js.map +1 -1
  236. package/build/resolvers/resolve-document.d.ts +29 -9
  237. package/build/resolvers/resolve-document.integration.test.d.ts +1 -0
  238. package/build/resolvers/resolve-document.integration.test.js +108 -0
  239. package/build/resolvers/resolve-document.integration.test.js.map +1 -0
  240. package/build/resolvers/resolve-document.js +129 -49
  241. package/build/resolvers/resolve-document.js.map +1 -1
  242. package/build/resolvers/resolve-document.test.js +462 -12
  243. package/build/resolvers/resolve-document.test.js.map +1 -1
  244. package/build/resolvers/resolve-json-document.d.ts +11 -8
  245. package/build/resolvers/resolve-json-document.js +7 -6
  246. package/build/resolvers/resolve-json-document.js.map +1 -1
  247. package/build/resolvers/resolve-json-document.test.js +21 -4
  248. package/build/resolvers/resolve-json-document.test.js.map +1 -1
  249. package/build/validation/describe-jsonld-failure.d.ts +48 -8
  250. package/build/validation/describe-jsonld-failure.js +94 -27
  251. package/build/validation/describe-jsonld-failure.js.map +1 -1
  252. package/build/validation/describe-jsonld-failure.test.js +98 -13
  253. package/build/validation/describe-jsonld-failure.test.js.map +1 -1
  254. package/build/validation/index.d.ts +3 -2
  255. package/build/validation/index.js +2 -2
  256. package/build/validation/index.js.map +1 -1
  257. package/build/validation/validate-jsonld.d.ts +29 -3
  258. package/build/validation/validate-jsonld.js +49 -22
  259. package/build/validation/validate-jsonld.js.map +1 -1
  260. package/build/validation/validate-jsonld.test.js +62 -5
  261. package/build/validation/validate-jsonld.test.js.map +1 -1
  262. package/package.json +23 -3
@@ -9,7 +9,14 @@ import { StructuredError } from '../structured-error.js';
9
9
  */
10
10
  export class ResolverError extends StructuredError {
11
11
  }
12
- /** The fetch call rejected before producing a response (TCP/TLS error, DNS race, etc.). */
12
+ /**
13
+ * The fetch call rejected before producing a response (TCP/TLS error, DNS
14
+ * race, etc.), or the body read rejected after the headers arrived. Rejections
15
+ * that the resolver classifies as this request's timeout are reported as
16
+ * {@link ResolverTimedOutError} instead; every other rejection caught at those
17
+ * two awaits, including a non-abort-shaped one that arrives after the deadline
18
+ * has passed, arrives here.
19
+ */
13
20
  export class ResolverNetworkError extends ResolverError {
14
21
  constructor(url, cause) {
15
22
  super({
@@ -20,9 +27,14 @@ export class ResolverNetworkError extends ResolverError {
20
27
  });
21
28
  }
22
29
  }
23
- /** The remote returned a non-success HTTP status. */
30
+ /**
31
+ * The remote returned a non-success HTTP status. `url` is the URL of the hop
32
+ * that produced the status (the end of the redirect chain), exposed so
33
+ * callers can still report on the final URL of a failed resolution.
34
+ */
24
35
  export class ResolverHttpError extends ResolverError {
25
36
  status;
37
+ url;
26
38
  constructor(url, status) {
27
39
  super({
28
40
  code: 'resolver.http-error',
@@ -31,6 +43,7 @@ export class ResolverHttpError extends ResolverError {
31
43
  expected: '2xx',
32
44
  });
33
45
  this.status = status;
46
+ this.url = url;
34
47
  }
35
48
  }
36
49
  /** The response body exceeded the configured size cap. */
@@ -46,20 +59,35 @@ export class ResolverTooLargeError extends ResolverError {
46
59
  this.limit = limit;
47
60
  }
48
61
  }
49
- /** A redirect chain exceeded the configured maximum hop count. */
62
+ /**
63
+ * A redirect chain exceeded the configured maximum hop count. `.lastHopUrl` is
64
+ * optional. The resolver supplies it as the hop that answered the exhausting
65
+ * redirect whenever a chain exhausts a finite non-negative integer
66
+ * `maxRedirects`, which is the supported input. It is absent otherwise: for a
67
+ * negative or non-integer limit, where the loop names no responder, and for a
68
+ * direct two-argument construction, which this constructor permits.
69
+ */
50
70
  export class ResolverTooManyRedirectsError extends ResolverError {
51
71
  limit;
52
- constructor(url, limit) {
72
+ lastHopUrl;
73
+ constructor(startUrl, limit, lastHopUrl) {
53
74
  super({
54
75
  code: 'resolver.too-many-redirects',
55
- message: `Exceeded ${limit} redirect hops starting from ${url}.`,
76
+ message: `Exceeded ${limit} redirect hops starting from ${startUrl}.`,
56
77
  received: `> ${limit} hops`,
57
78
  expected: `<= ${limit} hops`,
58
79
  });
59
80
  this.limit = limit;
81
+ if (lastHopUrl !== undefined)
82
+ this.lastHopUrl = lastHopUrl;
60
83
  }
61
84
  }
62
- /** The fetch was aborted by the timeout signal. */
85
+ /**
86
+ * The request's total deadline expired. That covers expiry while the guard was
87
+ * still waiting on DNS, before any fetch began, as well as a fetch or body read
88
+ * that rejected with an abort-shaped error once this request's signal had
89
+ * fired.
90
+ */
63
91
  export class ResolverTimedOutError extends ResolverError {
64
92
  timeoutMs;
65
93
  constructor(url, timeoutMs, cause) {
@@ -84,8 +112,13 @@ export class ResolverRedirectMissingLocationError extends ResolverError {
84
112
  });
85
113
  }
86
114
  }
87
- /** The response body was fetched but could not be parsed as JSON. */
115
+ /**
116
+ * The response body was fetched but could not be parsed as JSON. `url` is
117
+ * the final URL the body was fetched from (after redirect chasing), exposed
118
+ * so callers can still report on the final URL of a failed resolution.
119
+ */
88
120
  export class ResolverInvalidJsonError extends ResolverError {
121
+ url;
89
122
  constructor(url, cause) {
90
123
  super({
91
124
  code: 'resolver.invalid-json',
@@ -94,6 +127,7 @@ export class ResolverInvalidJsonError extends ResolverError {
94
127
  expected: 'a JSON document',
95
128
  cause,
96
129
  });
130
+ this.url = url;
97
131
  }
98
132
  }
99
133
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/resolvers/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/resolvers/errors.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,13 @@ describe('resolveDocumentIfChanged', () => {
43
43
  expect(passedOptions.headers['accept']).toBe('application/json');
44
44
  expect(passedOptions.headers['if-none-match']).toBe('"abc"');
45
45
  });
46
+ it('forwards private permission alongside conditional headers', async () => {
47
+ resolveDocument.mockResolvedValue(loadResult({ status: 304, bodyDigest: await digestOf('') }));
48
+ await resolveDocumentIfChanged('https://example.com/', { etag: '"abc"' }, { allowPrivateAddresses: true });
49
+ const passedOptions = resolveDocument.mock.calls[0][1];
50
+ expect(passedOptions.allowPrivateAddresses).toBe(true);
51
+ expect(passedOptions.headers['if-none-match']).toBe('"abc"');
52
+ });
46
53
  it('does not emit duplicate conditional headers when the caller supplies a differently-cased variant', async () => {
47
54
  resolveDocument.mockResolvedValue(loadResult({ status: 304, bodyDigest: await digestOf('') }));
48
55
  await resolveDocumentIfChanged('https://example.com/', { etag: '"new"' },
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-document-if-changed.test.js","sourceRoot":"","sources":["../../src/resolvers/resolve-document-if-changed.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve-document-if-changed.test.js","sourceRoot":"","sources":["../../src/resolvers/resolve-document-if-changed.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { MultibaseDigest } from '../multibase-digest/index.js';
2
+ import { type PublicUrlLookup } from '../node/index.js';
2
3
  /**
3
4
  * Defaults applied by {@link resolveDocument} (and, via composition,
4
5
  * by {@link import('./resolve-document-if-changed.js').resolveDocumentIfChanged})
@@ -7,7 +8,7 @@ import { MultibaseDigest } from '../multibase-digest/index.js';
7
8
  export declare const RESOLVER_DEFAULTS: {
8
9
  /** Body-size cap in bytes; exceeding throws {@link ResolverTooLargeError}. */
9
10
  readonly maxResponseBytes: 1048576;
10
- /** Total request timeout in milliseconds (DNS + connect + TLS + first byte + body). */
11
+ /** Total wait for asynchronous DNS and fetch work in milliseconds. Synchronous work cannot be interrupted, and DNS itself is not cancelled. */
11
12
  readonly totalTimeoutMs: 10000;
12
13
  /** Maximum additional hops after the initial request; exceeding throws {@link ResolverTooManyRedirectsError}. */
13
14
  readonly maxRedirects: 3;
@@ -62,23 +63,42 @@ export interface ResolveDocumentOptions {
62
63
  headers?: Record<string, string>;
63
64
  /** Allowed URL schemes (forwarded to {@link import('../node/index.js').validatePublicUrl}). */
64
65
  allowedSchemes?: readonly string[];
66
+ /** Permit private and reserved destinations while retaining every other guard. */
67
+ allowPrivateAddresses?: boolean;
68
+ /**
69
+ * Optional hostname lookup for tests and diagnostics. This is not a policy
70
+ * switch: its answers still pass through `validatePublicUrl`'s hostname and
71
+ * address guard before they reach the pinned connector. When omitted, the
72
+ * existing `node:dns/promises` lookup is used.
73
+ */
74
+ lookup?: PublicUrlLookup;
65
75
  }
66
76
  /**
67
77
  * Fetches `url` with the standard SSRF / size / timeout / redirect guards
68
78
  * applied, returning the response body + metadata as a {@link LoadResult}.
69
79
  *
70
80
  * Each redirect hop is re-validated through
71
- * {@link import('../node/index.js').validatePublicUrl}, and the connection
72
- * to each hop is pinned to the IP that validation resolved, so an upstream
73
- * cannot redirect to a private URL or rebind its hostname between check
74
- * and connect.
81
+ * {@link import('../node/index.js').validatePublicUrl}, and the connection to
82
+ * each hop is pinned to the set of addresses its validation resolved, so an
83
+ * upstream cannot rebind its hostname between check and connect. Node's
84
+ * address selection tries those addresses within the request budget, so a
85
+ * `localhost` that resolves to both `::1` and `127.0.0.1` reaches whichever
86
+ * listens. Private and reserved destinations remain rejected unless
87
+ * `allowPrivateAddresses` is exactly true.
75
88
  *
76
- * @throws {UrlValidationError} for URL / scheme / hostname / DNS / private-address rejections from `validatePublicUrl`.
77
- * @throws {ResolverNetworkError} when fetch rejects before producing a response.
89
+ * Only two awaits convert a foreign rejection into a resolver error: the undici
90
+ * fetch and the body read. Every other throw inside this function, whether a
91
+ * guard error or a defect in this module, propagates exactly as thrown. That
92
+ * is the contract #995 settled, and it is why `verify-did-web.ts:104` now
93
+ * surfaces a resolver defect to its caller instead of reporting it as a
94
+ * retryable network fault.
95
+ *
96
+ * @throws {UrlValidationError} for URL / scheme / hostname / DNS / private-address rejections from `validatePublicUrl`, propagated unwrapped.
97
+ * @throws {ResolverNetworkError} when the fetch rejects before producing a response, or when a mid-body read rejects, and the rejection was not classified as this request's timeout; a non-abort-shaped rejection arriving after the deadline is also reported here.
78
98
  * @throws {ResolverHttpError} on a non-2xx response status (with `.status`).
79
99
  * @throws {ResolverTooLargeError} when the body exceeds the size cap (with `.limit`).
80
- * @throws {ResolverTooManyRedirectsError} when the redirect chain exceeds the hop cap (with `.limit`).
81
- * @throws {ResolverTimedOutError} when the total timeout fires (with `.timeoutMs`).
100
+ * @throws {ResolverTooManyRedirectsError} when the redirect chain exceeds the hop cap (with `.limit`, and `.lastHopUrl` for the hop that answered the exhausting redirect).
101
+ * @throws {ResolverTimedOutError} before a hop or while the guard was waiting on DNS, once the deadline has passed; in flight or mid-body, when this request's abort is what the failing call observed (with `.timeoutMs`).
82
102
  * @throws {ResolverRedirectMissingLocationError} for a 3xx with no / unparseable Location header.
83
103
  *
84
104
  * @see https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
@@ -0,0 +1,108 @@
1
+ import { createServer } from 'node:http';
2
+ import { resolveJsonDocument } from './resolve-json-document.js';
3
+ const listeners = [];
4
+ async function startListener(host, port, handler) {
5
+ const requests = [];
6
+ const server = createServer((request, response) => {
7
+ requests.push(request.url ?? '');
8
+ handler(request, response);
9
+ });
10
+ await new Promise((resolve, reject) => {
11
+ server.once('error', reject);
12
+ server.listen(port, host, resolve);
13
+ });
14
+ const address = server.address();
15
+ if (!address || typeof address === 'string') {
16
+ throw new Error(`Listener did not expose an address for ${host}`);
17
+ }
18
+ const { port: boundPort } = address;
19
+ const listener = {
20
+ host,
21
+ port: boundPort,
22
+ requests,
23
+ close: () => new Promise((resolve, reject) => {
24
+ server.close((error) => (error ? reject(error) : resolve()));
25
+ }),
26
+ };
27
+ listeners.push(listener);
28
+ return listener;
29
+ }
30
+ function jsonResponse(response, value) {
31
+ response.writeHead(200, { 'content-type': 'application/json' });
32
+ response.end(JSON.stringify(value));
33
+ }
34
+ function address(host) {
35
+ return { address: host, family: host.includes(':') ? 6 : 4 };
36
+ }
37
+ function controlledLookup(answers, calls) {
38
+ return async (hostname, _options) => {
39
+ calls.push(hostname);
40
+ const answer = answers.shift();
41
+ if (!answer)
42
+ throw new Error(`Unexpected lookup for ${hostname}`);
43
+ return answer;
44
+ };
45
+ }
46
+ afterEach(async () => {
47
+ await Promise.all(listeners.splice(0).map((listener) => listener.close()));
48
+ });
49
+ describe('resolveJsonDocument real socket pinning', () => {
50
+ // Both listeners use loopback addresses. The explicit private-address
51
+ // allowance is only for this local fixture; the callback's answers still
52
+ // pass through the resolver's hostname and address guard.
53
+ it('connects to the address validated first even when the next lookup answer changes', async () => {
54
+ const listenerA = await startListener('127.0.0.1', 0, (_request, response) => {
55
+ jsonResponse(response, { listener: 'A' });
56
+ });
57
+ const listenerB = await startListener('::1', listenerA.port, (_request, response) => {
58
+ jsonResponse(response, { listener: 'B' });
59
+ });
60
+ const lookupCalls = [];
61
+ const lookup = controlledLookup([[address(listenerA.host)], [address(listenerB.host)]], lookupCalls);
62
+ const result = await resolveJsonDocument(`http://accepted.test:${listenerA.port}/document`, {
63
+ allowPrivateAddresses: true,
64
+ lookup,
65
+ });
66
+ expect(result.json).toEqual({ listener: 'A' });
67
+ expect(listenerA.requests).toEqual(['/document']);
68
+ expect(listenerB.requests).toEqual([]);
69
+ expect(lookupCalls).toEqual(['accepted.test']);
70
+ });
71
+ it('re-validates and re-pins a redirect hop to the second listener', async () => {
72
+ let redirectLocation = '';
73
+ const listenerA = await startListener('127.0.0.1', 0, (_request, response) => {
74
+ response.writeHead(302, { location: redirectLocation });
75
+ response.end();
76
+ });
77
+ const listenerB = await startListener('::1', listenerA.port, (_request, response) => {
78
+ jsonResponse(response, { listener: 'B' });
79
+ });
80
+ redirectLocation = `http://redirect-target.test:${listenerA.port}/final`;
81
+ const lookupCalls = [];
82
+ const lookup = controlledLookup([[address(listenerA.host)], [address(listenerB.host)]], lookupCalls);
83
+ const result = await resolveJsonDocument(`http://redirect-source.test:${listenerA.port}/start`, {
84
+ allowPrivateAddresses: true,
85
+ lookup,
86
+ });
87
+ expect(result.json).toEqual({ listener: 'B' });
88
+ expect(listenerA.requests).toEqual(['/start']);
89
+ expect(listenerB.requests).toEqual(['/final']);
90
+ expect(lookupCalls).toEqual(['redirect-source.test', 'redirect-target.test']);
91
+ });
92
+ it('tries the second validated address when the first address refuses the connection', async () => {
93
+ const listenerB = await startListener('127.0.0.1', 0, (_request, response) => {
94
+ jsonResponse(response, { listener: 'B' });
95
+ });
96
+ const refusedAddress = '::1';
97
+ const lookupCalls = [];
98
+ const lookup = controlledLookup([[address(refusedAddress), address(listenerB.host)]], lookupCalls);
99
+ const result = await resolveJsonDocument(`http://failover.test:${listenerB.port}/document`, {
100
+ allowPrivateAddresses: true,
101
+ lookup,
102
+ });
103
+ expect(result.json).toEqual({ listener: 'B' });
104
+ expect(listenerB.requests).toEqual(['/document']);
105
+ expect(lookupCalls).toEqual(['failover.test']);
106
+ });
107
+ });
108
+ //# sourceMappingURL=resolve-document.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-document.integration.test.js","sourceRoot":"","sources":["../../src/resolvers/resolve-document.integration.test.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ import { ResolverHttpError, ResolverNetworkError, ResolverRedirectMissingLocatio
10
10
  export const RESOLVER_DEFAULTS = {
11
11
  /** Body-size cap in bytes; exceeding throws {@link ResolverTooLargeError}. */
12
12
  maxResponseBytes: 1_048_576,
13
- /** Total request timeout in milliseconds (DNS + connect + TLS + first byte + body). */
13
+ /** Total wait for asynchronous DNS and fetch work in milliseconds. Synchronous work cannot be interrupted, and DNS itself is not cancelled. */
14
14
  totalTimeoutMs: 10_000,
15
15
  /** Maximum additional hops after the initial request; exceeding throws {@link ResolverTooManyRedirectsError}. */
16
16
  maxRedirects: 3,
@@ -22,7 +22,9 @@ export const RESOLVER_DEFAULTS = {
22
22
  /**
23
23
  * Merges the resolved `User-Agent` into the caller's headers. A caller-supplied
24
24
  * `user-agent` (any casing) wins; otherwise the `RI_HTTP_USER_AGENT` env
25
- * override (blank treated as unset), then the default. The override is not
25
+ * override (blank treated as unset), then the default. Both branches copy the
26
+ * caller's headers before request conversion, so a throwing getter remains a
27
+ * caller defect rather than becoming a transport failure. The override is not
26
28
  * validated here: deployments validate it at boot via
27
29
  * {@link isValidHttpUserAgent} (the RI does, in instrumentation.node.ts),
28
30
  * and a value that slips through with control characters makes the fetch
@@ -31,7 +33,7 @@ export const RESOLVER_DEFAULTS = {
31
33
  function withUserAgent(headers) {
32
34
  const hasExplicit = Object.keys(headers ?? {}).some((key) => key.toLowerCase() === 'user-agent');
33
35
  if (hasExplicit)
34
- return headers;
36
+ return { ...(headers ?? {}) };
35
37
  const fromEnv = process.env[USER_AGENT_ENV_VAR];
36
38
  const userAgent = fromEnv !== undefined && fromEnv.trim() !== '' ? fromEnv : DEFAULT_USER_AGENT;
37
39
  return { ...headers, 'User-Agent': userAgent };
@@ -41,17 +43,27 @@ function withUserAgent(headers) {
41
43
  * applied, returning the response body + metadata as a {@link LoadResult}.
42
44
  *
43
45
  * Each redirect hop is re-validated through
44
- * {@link import('../node/index.js').validatePublicUrl}, and the connection
45
- * to each hop is pinned to the IP that validation resolved, so an upstream
46
- * cannot redirect to a private URL or rebind its hostname between check
47
- * and connect.
46
+ * {@link import('../node/index.js').validatePublicUrl}, and the connection to
47
+ * each hop is pinned to the set of addresses its validation resolved, so an
48
+ * upstream cannot rebind its hostname between check and connect. Node's
49
+ * address selection tries those addresses within the request budget, so a
50
+ * `localhost` that resolves to both `::1` and `127.0.0.1` reaches whichever
51
+ * listens. Private and reserved destinations remain rejected unless
52
+ * `allowPrivateAddresses` is exactly true.
48
53
  *
49
- * @throws {UrlValidationError} for URL / scheme / hostname / DNS / private-address rejections from `validatePublicUrl`.
50
- * @throws {ResolverNetworkError} when fetch rejects before producing a response.
54
+ * Only two awaits convert a foreign rejection into a resolver error: the undici
55
+ * fetch and the body read. Every other throw inside this function, whether a
56
+ * guard error or a defect in this module, propagates exactly as thrown. That
57
+ * is the contract #995 settled, and it is why `verify-did-web.ts:104` now
58
+ * surfaces a resolver defect to its caller instead of reporting it as a
59
+ * retryable network fault.
60
+ *
61
+ * @throws {UrlValidationError} for URL / scheme / hostname / DNS / private-address rejections from `validatePublicUrl`, propagated unwrapped.
62
+ * @throws {ResolverNetworkError} when the fetch rejects before producing a response, or when a mid-body read rejects, and the rejection was not classified as this request's timeout; a non-abort-shaped rejection arriving after the deadline is also reported here.
51
63
  * @throws {ResolverHttpError} on a non-2xx response status (with `.status`).
52
64
  * @throws {ResolverTooLargeError} when the body exceeds the size cap (with `.limit`).
53
- * @throws {ResolverTooManyRedirectsError} when the redirect chain exceeds the hop cap (with `.limit`).
54
- * @throws {ResolverTimedOutError} when the total timeout fires (with `.timeoutMs`).
65
+ * @throws {ResolverTooManyRedirectsError} when the redirect chain exceeds the hop cap (with `.limit`, and `.lastHopUrl` for the hop that answered the exhausting redirect).
66
+ * @throws {ResolverTimedOutError} before a hop or while the guard was waiting on DNS, once the deadline has passed; in flight or mid-body, when this request's abort is what the failing call observed (with `.timeoutMs`).
55
67
  * @throws {ResolverRedirectMissingLocationError} for a 3xx with no / unparseable Location header.
56
68
  *
57
69
  * @see https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
@@ -65,22 +77,59 @@ export async function resolveDocument(url, options) {
65
77
  const maxBytes = options?.maxResponseBytes ?? RESOLVER_DEFAULTS.maxResponseBytes;
66
78
  const totalTimeoutMs = options?.totalTimeoutMs ?? RESOLVER_DEFAULTS.totalTimeoutMs;
67
79
  const maxRedirects = options?.maxRedirects ?? RESOLVER_DEFAULTS.maxRedirects;
80
+ // The deadline protocol. `currentUrl` is hoisted above the abort promise so
81
+ // the promise, created once per request rather than once per hop, rejects
82
+ // with whichever hop was in flight when the timer fired: a timeout during
83
+ // DNS then names the host it was waiting on. Racing the guard against that
84
+ // promise bounds the wait without cancelling the lookup, because
85
+ // `dns.lookup` takes no signal. The bare promises are raced so that a
86
+ // rejection from whichever settles first still reaches the caller; the two
87
+ // `void ... .catch(...)` side channels exist only to keep the loser's
88
+ // rejection from surfacing as an unhandled rejection, and must not be moved
89
+ // into the race, where they would resolve the race with `undefined` and
90
+ // swallow a DNS failure. `signal.aborted` is checked before the guard is
91
+ // invoked, so a hop whose predecessor's cleanup consumed the budget makes no
92
+ // further guard call, and again after the race, so nothing is dispatched
93
+ // once the budget is spent. No `await` may be inserted between that second
94
+ // check and `new Agent`: any suspension there reopens the window in which
95
+ // the timer fires and an Agent is still constructed. Tests enforce all of
96
+ // this (no second-hop guard call, no Agent, fetch never called), not types.
97
+ let currentUrl = url;
68
98
  const controller = new AbortController();
99
+ let removeAbortListener = () => undefined;
100
+ const abortPromise = new Promise((_resolve, reject) => {
101
+ const onAbort = () => reject(new ResolverTimedOutError(currentUrl, totalTimeoutMs));
102
+ controller.signal.addEventListener('abort', onAbort, { once: true });
103
+ removeAbortListener = () => controller.signal.removeEventListener('abort', onAbort);
104
+ });
105
+ void abortPromise.catch(() => undefined);
69
106
  const timeoutHandle = setTimeout(() => controller.abort(), totalTimeoutMs);
70
107
  try {
71
- let currentUrl = url;
72
108
  for (let hop = 0; hop <= maxRedirects; hop += 1) {
73
- const { address: pinnedAddress, family: pinnedFamily } = await validatePublicUrl(currentUrl, {
109
+ if (controller.signal.aborted)
110
+ throw new ResolverTimedOutError(currentUrl, totalTimeoutMs);
111
+ const guard = validatePublicUrl(currentUrl, {
74
112
  allowedSchemes: options?.allowedSchemes,
113
+ allowPrivateAddresses: options?.allowPrivateAddresses,
114
+ ...(options?.lookup ? { lookup: options.lookup } : {}),
75
115
  });
116
+ void guard.catch(() => undefined);
117
+ const { addresses: pinnedAddresses } = await Promise.race([guard, abortPromise]);
118
+ if (controller.signal.aborted)
119
+ throw new ResolverTimedOutError(currentUrl, totalTimeoutMs);
76
120
  const dispatcher = new Agent({
77
121
  connect: {
78
- // undici resolves the connect target with `all: true` to support
79
- // happy-eyeballs; the callback receives a `LookupAddress[]`.
80
- // Return a single-entry array containing the IP we pinned via
81
- // `validatePublicUrl`, so the connection target is exactly the
82
- // address that the SSRF check validated.
83
- lookup: (_hostname, _opts, cb) => cb(null, [{ address: pinnedAddress, family: pinnedFamily }]),
122
+ // undici spreads this `lookup` option into `net.connect` and
123
+ // `tls.connect` and performs no lookup of its own. Under Node's
124
+ // default family autoselection it calls the callback with
125
+ // `all: true` and walks the returned list, so return every address
126
+ // `validatePublicUrl` validated (`addresses` is already in resolver
127
+ // order) and each address Node may try is one the SSRF check passed.
128
+ // Handing it only the first would refuse a name whose other
129
+ // addresses hold the listener: `localhost` resolves to both `::1`
130
+ // and `127.0.0.1`, and the walk is what reaches the second when the
131
+ // first is refused.
132
+ lookup: (_hostname, _opts, cb) => cb(null, pinnedAddresses.map(({ address, family }) => ({ address, family }))),
84
133
  },
85
134
  });
86
135
  // The entire request lifecycle (fetch + status-check + body read +
@@ -89,13 +138,28 @@ export async function resolveDocument(url, options) {
89
138
  // request is "drained" only once its body has been consumed. Closing
90
139
  // before `readWithLimit` deadlocks the close on the unconsumed body.
91
140
  try {
92
- const response = await undiciFetch(currentUrl, {
141
+ const headers = withUserAgent(options?.headers);
142
+ const requestOptions = {
93
143
  method: 'GET',
94
144
  redirect: 'manual',
95
145
  signal: controller.signal,
96
- headers: withUserAgent(options?.headers),
146
+ headers,
97
147
  dispatcher,
98
- });
148
+ };
149
+ let response;
150
+ try {
151
+ response = await undiciFetch(currentUrl, requestOptions);
152
+ }
153
+ catch (cause) {
154
+ // This catch also sees undici's Request-construction rejections, so a
155
+ // redirect hop carrying userinfo or a malformed header value is
156
+ // wrapped as a network error rather than propagating as a defect.
157
+ // That is the recorded exception to #995's wording.
158
+ if (isOurTimeout(cause, controller.signal)) {
159
+ throw new ResolverTimedOutError(currentUrl, totalTimeoutMs, cause);
160
+ }
161
+ throw new ResolverNetworkError(currentUrl, cause);
162
+ }
99
163
  // 304 Not Modified: body is intentionally empty; the caller maps it
100
164
  // to `unchanged` in `resolveDocumentIfChanged`.
101
165
  if (response.status === 304) {
@@ -128,6 +192,9 @@ export async function resolveDocument(url, options) {
128
192
  // proceed without waiting for body drain.
129
193
  if (response.body)
130
194
  await response.body.cancel().catch(() => undefined);
195
+ if (hop === maxRedirects) {
196
+ throw new ResolverTooManyRedirectsError(url, maxRedirects, currentUrl);
197
+ }
131
198
  currentUrl = nextUrl;
132
199
  continue;
133
200
  }
@@ -136,7 +203,7 @@ export async function resolveDocument(url, options) {
136
203
  await response.body.cancel().catch(() => undefined);
137
204
  throw new ResolverHttpError(currentUrl, response.status);
138
205
  }
139
- const body = await readWithLimit(response, maxBytes, currentUrl, totalTimeoutMs);
206
+ const body = await readWithLimit(response, maxBytes, currentUrl, totalTimeoutMs, controller.signal);
140
207
  const bodyDigest = await computeDigest(body);
141
208
  const headerView = extractHeaders(response.headers);
142
209
  return {
@@ -155,28 +222,32 @@ export async function resolveDocument(url, options) {
155
222
  }
156
223
  throw new ResolverTooManyRedirectsError(url, maxRedirects);
157
224
  }
158
- catch (cause) {
159
- if (cause instanceof Error && cause.code?.startsWith('resolver.')) {
160
- throw cause;
161
- }
162
- // Re-throw UrlValidationErrors and other StructuredErrors unwrapped.
163
- if (cause instanceof Error &&
164
- typeof cause.code === 'string' &&
165
- (cause.code.startsWith('url.') ||
166
- cause.code.startsWith('resolver.'))) {
167
- throw cause;
168
- }
169
- if (isAbortLikeError(cause)) {
170
- throw new ResolverTimedOutError(url, totalTimeoutMs, cause);
171
- }
172
- throw new ResolverNetworkError(url, cause);
173
- }
174
225
  finally {
175
226
  clearTimeout(timeoutHandle);
227
+ removeAbortListener();
176
228
  }
177
229
  }
230
+ /**
231
+ * The classification policy at the two transport catches: a rejection counts as
232
+ * this request's timeout only when this request's signal has fired and the
233
+ * error is named `AbortError` or `TimeoutError`. Everything else caught there
234
+ * becomes a {@link ResolverNetworkError}.
235
+ *
236
+ * The signal gate stops an abort-shaped rejection counting as this request's
237
+ * timeout while its signal is quiet, which would tell the operator "timed out
238
+ * after 10000ms" when 300 ms elapsed. The name gate keeps a non-abort-shaped
239
+ * rejection arriving after the deadline classified as a network fault. Neither
240
+ * gate proves the rejection was caused by this signal; this is the chosen
241
+ * policy, not a proof of cause.
242
+ */
243
+ function isOurTimeout(error, signal) {
244
+ return signal.aborted && isAbortLikeError(error);
245
+ }
178
246
  function isAbortLikeError(error) {
179
- return error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError');
247
+ if (typeof error !== 'object' || error === null)
248
+ return false;
249
+ const name = error.name;
250
+ return name === 'AbortError' || name === 'TimeoutError';
180
251
  }
181
252
  function extractHeaders(headers) {
182
253
  const view = {};
@@ -197,7 +268,7 @@ async function computeDigest(body) {
197
268
  base: RESOLVER_DEFAULTS.digestEncoding,
198
269
  });
199
270
  }
200
- async function readWithLimit(response, limit, url, totalTimeoutMs) {
271
+ async function readWithLimit(response, limit, url, totalTimeoutMs, signal) {
201
272
  if (!response.body)
202
273
  return new Uint8Array(0);
203
274
  const reader = response.body.getReader();
@@ -206,27 +277,36 @@ async function readWithLimit(response, limit, url, totalTimeoutMs) {
206
277
  try {
207
278
  // eslint-disable-next-line no-constant-condition
208
279
  while (true) {
209
- const { value, done } = await reader.read();
280
+ let readResult;
281
+ try {
282
+ readResult = await reader.read();
283
+ }
284
+ catch (error) {
285
+ if (isOurTimeout(error, signal))
286
+ throw new ResolverTimedOutError(url, totalTimeoutMs, error);
287
+ throw new ResolverNetworkError(url, error);
288
+ }
289
+ const { value, done } = readResult;
210
290
  if (done)
211
291
  break;
212
292
  if (!value)
213
293
  continue;
214
294
  total += value.byteLength;
215
295
  if (total > limit) {
216
- await reader.cancel().catch(() => undefined);
217
296
  throw new ResolverTooLargeError(url, limit);
218
297
  }
219
298
  chunks.push(value);
220
299
  }
221
300
  }
222
301
  catch (error) {
223
- // Release the reader so the upstream socket can be recycled.
302
+ // Cleanup only: release the reader so the upstream socket can be
303
+ // recycled, then rethrow the original value unchanged. Classification
304
+ // happens at the read await above, and the over-cap error is raised
305
+ // without cancelling inline precisely so cancellation stays here. A
306
+ // future edit that classified or replaced the error here would relabel
307
+ // both a defect and an already-classified resolver error.
224
308
  await reader.cancel().catch(() => undefined);
225
- if (error instanceof ResolverTooLargeError)
226
- throw error;
227
- if (isAbortLikeError(error))
228
- throw new ResolverTimedOutError(url, totalTimeoutMs, error);
229
- throw new ResolverNetworkError(url, error);
309
+ throw error;
230
310
  }
231
311
  const merged = new Uint8Array(total);
232
312
  let offset = 0;