@cardanowall/sdk-ts 0.3.0 → 0.5.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 (49) hide show
  1. package/dist/client/index.cjs +1146 -365
  2. package/dist/client/index.cjs.map +1 -1
  3. package/dist/client/index.d.cts +48 -7
  4. package/dist/client/index.d.ts +48 -7
  5. package/dist/client/index.js +1144 -367
  6. package/dist/client/index.js.map +1 -1
  7. package/dist/conformance/cli.cjs +4400 -2121
  8. package/dist/conformance/cli.cjs.map +1 -1
  9. package/dist/conformance/cli.js +4401 -2122
  10. package/dist/conformance/cli.js.map +1 -1
  11. package/dist/fetch/index.cjs +33 -14
  12. package/dist/fetch/index.cjs.map +1 -1
  13. package/dist/fetch/index.d.cts +2 -2
  14. package/dist/fetch/index.d.ts +2 -2
  15. package/dist/fetch/index.js +32 -15
  16. package/dist/fetch/index.js.map +1 -1
  17. package/dist/{fetch-outbound-BT5-NiYN.d.cts → fetch-outbound-dOK3ZxYa.d.cts} +7 -3
  18. package/dist/{fetch-outbound-BT5-NiYN.d.ts → fetch-outbound-dOK3ZxYa.d.ts} +7 -3
  19. package/dist/hash/index.cjs +1 -1
  20. package/dist/hash/index.cjs.map +1 -1
  21. package/dist/hash/index.js +1 -1
  22. package/dist/hash/index.js.map +1 -1
  23. package/dist/identity/index.cjs +356 -230
  24. package/dist/identity/index.cjs.map +1 -1
  25. package/dist/identity/index.d.cts +3 -2
  26. package/dist/identity/index.d.ts +3 -2
  27. package/dist/identity/index.js +356 -230
  28. package/dist/identity/index.js.map +1 -1
  29. package/dist/index.cjs +5480 -2520
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +7 -7
  32. package/dist/index.d.ts +7 -7
  33. package/dist/index.js +5460 -2516
  34. package/dist/index.js.map +1 -1
  35. package/dist/merkle/index.cjs +1 -1
  36. package/dist/merkle/index.js +1 -1
  37. package/dist/types-Cexm4VH9.d.cts +119 -0
  38. package/dist/types-CgoBub9J.d.ts +119 -0
  39. package/dist/{types-DGsZTMuZ.d.cts → types-DNu_IrWZ.d.cts} +236 -7
  40. package/dist/{types-DGsZTMuZ.d.ts → types-DNu_IrWZ.d.ts} +236 -7
  41. package/dist/verifier/index.cjs +4419 -2147
  42. package/dist/verifier/index.cjs.map +1 -1
  43. package/dist/verifier/index.d.cts +159 -111
  44. package/dist/verifier/index.d.ts +159 -111
  45. package/dist/verifier/index.js +4407 -2143
  46. package/dist/verifier/index.js.map +1 -1
  47. package/package.json +3 -3
  48. package/dist/types-B8Q3gW54.d.ts +0 -123
  49. package/dist/types-CLXdbjqr.d.cts +0 -123
@@ -12,6 +12,12 @@ var DenyHostError = class extends Error {
12
12
  this.url = url;
13
13
  }
14
14
  };
15
+ function isDenyHostError(e) {
16
+ return e instanceof DenyHostError || typeof e === "object" && e !== null && e.code === "SERVICE_INDEPENDENCE_VIOLATION";
17
+ }
18
+ function isBodyTooLargeError(e) {
19
+ return e instanceof BodyTooLargeError || typeof e === "object" && e !== null && e.code === "OUTBOUND_BODY_TOO_LARGE";
20
+ }
15
21
  var UnsupportedProtocolError = class extends Error {
16
22
  code = "UNSUPPORTED_PROTOCOL";
17
23
  protocol;
@@ -117,12 +123,23 @@ var defaultFetchOutbound = async (url, opts) => {
117
123
  const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
118
124
  const init = {
119
125
  method: opts.method,
120
- signal: controller.signal
126
+ signal: controller.signal,
127
+ // Redirects are never followed — deny-host and protocol validation ran
128
+ // against the original URL only, so a 3xx from an allowed host could
129
+ // otherwise pivot the fetch to any target (e.g. `302 Location:
130
+ // http://127.0.0.1/…`) behind the verifier's back. Every target must be
131
+ // validated, so a redirect is a fetch failure; all SDKs behave
132
+ // identically. A readable 3xx flows through as a non-2xx status and the
133
+ // caller's attempt handling marks it failed, like a 5xx.
134
+ redirect: "manual"
121
135
  };
122
136
  if (opts.headers) init.headers = { ...opts.headers };
123
137
  if (opts.body !== void 0) init.body = opts.body;
124
138
  try {
125
139
  const res = await fetch(url, init);
140
+ if (res.type === "opaqueredirect") {
141
+ throw new Error(`redirect refused (opaqueredirect): ${url} answered with a redirect`);
142
+ }
126
143
  const declared = res.headers.get("content-length");
127
144
  if (declared !== null) {
128
145
  const declaredLen = Number(declared);
@@ -182,9 +199,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
182
199
  audit.push({
183
200
  url,
184
201
  method: "GET",
185
- status: 0,
202
+ status: null,
186
203
  bytes: 0,
187
- duration_ms: 0,
204
+ durationMs: 0,
188
205
  purpose: opts.purpose
189
206
  });
190
207
  throw new Error(
@@ -196,9 +213,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
196
213
  audit.push({
197
214
  url,
198
215
  method: "GET",
199
- status: 0,
216
+ status: null,
200
217
  bytes: 0,
201
- duration_ms: 0,
218
+ durationMs: 0,
202
219
  purpose: opts.purpose
203
220
  });
204
221
  throw new UnsupportedProtocolError(protocol ?? "", url);
@@ -207,9 +224,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
207
224
  audit.push({
208
225
  url,
209
226
  method: "GET",
210
- status: 0,
227
+ status: null,
211
228
  bytes: 0,
212
- duration_ms: 0,
229
+ durationMs: 0,
213
230
  purpose: opts.purpose
214
231
  });
215
232
  throw new UnsupportedMethodError(opts.method, url);
@@ -220,9 +237,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
220
237
  audit.push({
221
238
  url,
222
239
  method: opts.method,
223
- status: 0,
240
+ status: null,
224
241
  bytes: 0,
225
- duration_ms: 0,
242
+ durationMs: 0,
226
243
  purpose: opts.purpose
227
244
  });
228
245
  throw new DenyHostError(canonicaliseHost(host), url);
@@ -240,7 +257,7 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
240
257
  method: opts.method,
241
258
  status: result.status,
242
259
  bytes: result.bytes.byteLength,
243
- duration_ms: result.durationMs,
260
+ durationMs: result.durationMs,
244
261
  purpose: opts.purpose
245
262
  });
246
263
  if (retryableStatuses.includes(result.status) && retries > 0) {
@@ -258,9 +275,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
258
275
  audit.push({
259
276
  url,
260
277
  method: opts.method,
261
- status: 0,
278
+ status: null,
262
279
  bytes: 0,
263
- duration_ms: durationMs,
280
+ durationMs,
264
281
  purpose: opts.purpose
265
282
  });
266
283
  throw e;
@@ -268,9 +285,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
268
285
  audit.push({
269
286
  url,
270
287
  method: opts.method,
271
- status: 0,
288
+ status: null,
272
289
  bytes: 0,
273
- duration_ms: durationMs,
290
+ durationMs,
274
291
  purpose: opts.purpose
275
292
  });
276
293
  lastError = e;
@@ -329,6 +346,8 @@ exports.UnsupportedProtocolError = UnsupportedProtocolError;
329
346
  exports.defaultFetchOutbound = defaultFetchOutbound;
330
347
  exports.denyHostsFetch = denyHostsFetch;
331
348
  exports.fetchOutbound = fetchOutbound;
349
+ exports.isBodyTooLargeError = isBodyTooLargeError;
350
+ exports.isDenyHostError = isDenyHostError;
332
351
  exports.matchesDenyList = matchesDenyList;
333
352
  exports.wrapFetchOutbound = wrapFetchOutbound;
334
353
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fetch/fetch-outbound.ts","../../src/fetch/deny-hosts.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Canonical outbound HTTP wrapper: deny-list short-circuit, protocol/method\n// allowlist, bounded timeout, exp-backoff retry with jitter, audit trail.\n\n// Universal loopback deny-host list a service-independent verifier MUST reject\n// so a record can never be made to \"verify\" only because it reached a loopback\n// address. This default carries no operator-specific entries: a deployment that\n// wants to forbid its own gateway/viewer hosts appends those at construction\n// time. Producers SHOULD pass this through `denyHosts` on every verifier\n// invocation; the wrapper accepts arbitrary lists but exports the canonical\n// loopback set so callers don't duplicate it inline. (RFC-1918 / link-local IP\n// ranges are blocked separately by the SSRF guard, not by this name list.)\nexport const DENY_HOSTS_DEFAULT: ReadonlyArray<string> = ['localhost', '127.0.0.1'];\n\n// Every outbound call carries a purpose tag from the closed set\n// `{cardano, arweave, ipfs}` (the three v1 gateway-chain purposes).\n// `https` is a transitional legacy tag for non-storage HTTPS\n// auxiliaries; new code SHOULD pick one of the three normative purposes.\n// `webhook` is the user-supplied-URL purpose: it triggers the SSRF guard\n// (DNS resolution + IP range check + connection pinning + redirect-chain\n// re-checking + body-size cap), and MUST be used for any fetch where the\n// target URL came from end-user input.\nexport type HttpPurpose = 'cardano' | 'arweave' | 'ipfs' | 'https' | 'webhook';\nexport type HttpMethod = 'GET' | 'POST';\n\nexport interface FetchOutboundOptions {\n readonly method: HttpMethod;\n readonly purpose: HttpPurpose;\n readonly headers?: Readonly<Record<string, string>>;\n readonly body?: string;\n // Hard cap on the response body the primitive will buffer. Gateway content\n // (ar:// / ipfs:// / https) is producer-chosen and therefore UNTRUSTED — the\n // verifier never trusts the producer — so a malicious gateway could otherwise\n // stream unbounded bytes into memory. Omit to use DEFAULT_OUTBOUND_MAX_BYTES.\n readonly maxBytes?: number;\n}\n\nexport interface FetchOutboundResult {\n readonly status: number;\n readonly bytes: Uint8Array;\n readonly durationMs: number;\n}\n\nexport type FetchOutbound = (\n url: string,\n opts: FetchOutboundOptions,\n) => Promise<FetchOutboundResult>;\n\n// Audit-log entry for one outbound HTTP fetch. Field names are snake_case so\n// the record can land directly on `VerifyReport.http_calls[]` (which IS the\n// wire shape) without a key-renaming pass.\nexport interface HttpCallRecord {\n readonly url: string;\n readonly method: HttpMethod;\n readonly status: number;\n readonly bytes: number;\n readonly duration_ms: number;\n readonly purpose: HttpPurpose;\n}\n\nexport interface RetryConfig {\n readonly timeoutMs?: number;\n readonly retries?: number;\n readonly retryableStatuses?: ReadonlyArray<number>;\n}\n\nexport interface WrapFetchOutboundConfig extends RetryConfig {\n readonly denyHosts?: ReadonlyArray<string>;\n}\n\nexport class DenyHostError extends Error {\n readonly code = 'SERVICE_INDEPENDENCE_VIOLATION';\n readonly host: string;\n readonly url: string;\n constructor(host: string, url: string) {\n super(`SERVICE_INDEPENDENCE_VIOLATION: host \"${host}\" is in denyHosts (url=${url})`);\n this.name = 'DenyHostError';\n this.host = host;\n this.url = url;\n }\n}\n\nexport class UnsupportedProtocolError extends Error {\n readonly code = 'UNSUPPORTED_PROTOCOL';\n readonly protocol: string;\n readonly url: string;\n constructor(protocol: string, url: string) {\n super(`UNSUPPORTED_PROTOCOL: \"${protocol}\" not in {http:, https:} (url=${url})`);\n this.name = 'UnsupportedProtocolError';\n this.protocol = protocol;\n this.url = url;\n }\n}\n\nexport class UnsupportedMethodError extends Error {\n readonly code = 'UNSUPPORTED_METHOD';\n readonly method: string;\n readonly url: string;\n constructor(method: string, url: string) {\n super(`UNSUPPORTED_METHOD: \"${method}\" not in {GET, POST} (url=${url})`);\n this.name = 'UnsupportedMethodError';\n this.method = method;\n this.url = url;\n }\n}\n\nexport class BodyTooLargeError extends Error {\n readonly code = 'OUTBOUND_BODY_TOO_LARGE';\n readonly url: string;\n readonly limitBytes: number;\n constructor(url: string, limitBytes: number) {\n super(`OUTBOUND_BODY_TOO_LARGE: response exceeded ${limitBytes} bytes (url=${url})`);\n this.name = 'BodyTooLargeError';\n this.url = url;\n this.limitBytes = limitBytes;\n }\n}\n\nexport class OutboundExhaustedError extends Error {\n readonly code = 'OUTBOUND_EXHAUSTED';\n readonly url: string;\n readonly attempts: number;\n readonly lastStatus: number | undefined;\n readonly lastError: Error | undefined;\n constructor(args: {\n url: string;\n attempts: number;\n lastStatus?: number | undefined;\n lastError?: Error | undefined;\n }) {\n super(\n `OUTBOUND_EXHAUSTED: ${args.attempts} attempts exhausted (url=${args.url}, lastStatus=${args.lastStatus ?? '-'})`,\n );\n this.name = 'OutboundExhaustedError';\n this.url = args.url;\n this.attempts = args.attempts;\n this.lastStatus = args.lastStatus;\n this.lastError = args.lastError;\n }\n}\n\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n// Default response-body cap for the verifier's gateway fetches. 64 MiB sits\n// well above any single sealed-PoE ciphertext or merkle-leaf payload a verifier\n// would realistically recompute a hash over, while bounding the memory a hostile\n// gateway can force the verifier to allocate for one request. Callers that\n// legitimately handle larger content raise it per-call via `opts.maxBytes`.\nexport const DEFAULT_OUTBOUND_MAX_BYTES = 64 * 1024 * 1024;\nexport const DEFAULT_RETRYABLE_STATUSES: ReadonlyArray<number> = [502, 503, 504];\nconst BACKOFF_BASE_MS: ReadonlyArray<number> = [1000, 2000, 4000];\nconst JITTER_RATIO = 0.25;\n\nfunction canonicaliseHost(host: string): string {\n return host.replace(/^\\[/, '').replace(/\\]$/, '').replace(/\\.$/, '').toLowerCase();\n}\n\nexport function matchesDenyList(host: string, denyHosts: ReadonlyArray<string>): boolean {\n const h = canonicaliseHost(host);\n for (const raw of denyHosts) {\n const pattern = raw.replace(/\\.$/, '').toLowerCase();\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2);\n if (h.endsWith('.' + suffix)) return true;\n continue;\n }\n if (h === pattern) return true;\n if (pattern === 'localhost') {\n if (h === '::1' || h === '0.0.0.0' || h === '169.254.169.254') return true;\n }\n if (pattern === '127.0.0.1') {\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(h)) return true;\n }\n }\n return false;\n}\n\nfunction parseProtocol(url: string): string | null {\n try {\n return new URL(url).protocol;\n } catch {\n return null;\n }\n}\n\nfunction isAllowedMethod(method: string): method is HttpMethod {\n return method === 'GET' || method === 'POST';\n}\n\nfunction backoffJitteredMs(attemptIndex: number): number {\n const idx = Math.min(attemptIndex, BACKOFF_BASE_MS.length - 1);\n const base = BACKOFF_BASE_MS[idx] ?? BACKOFF_BASE_MS[BACKOFF_BASE_MS.length - 1]!;\n const jitter = 1 + (Math.random() - 0.5) * 2 * JITTER_RATIO;\n return base * jitter;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport const defaultFetchOutbound: FetchOutbound = async (url, opts) => {\n const t0 = Date.now();\n const maxBytes = opts.maxBytes ?? DEFAULT_OUTBOUND_MAX_BYTES;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n const init: RequestInit = {\n method: opts.method,\n signal: controller.signal,\n };\n if (opts.headers) init.headers = { ...opts.headers };\n if (opts.body !== undefined) init.body = opts.body;\n try {\n // allow-raw-fetch: canonical defaultFetchOutbound — single egress point\n const res = await fetch(url, init);\n\n // Fast path: a truthful Content-Length over the cap lets us bail before\n // reading a single body byte. A lying/absent header is still caught by the\n // streaming counter below — the header is an optimisation, not the guard.\n const declared = res.headers.get('content-length');\n if (declared !== null) {\n const declaredLen = Number(declared);\n if (Number.isFinite(declaredLen) && declaredLen > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n }\n\n const bytes = await readBodyCapped(res, url, maxBytes, controller);\n return { status: res.status, bytes, durationMs: Date.now() - t0 };\n } finally {\n clearTimeout(timeout);\n }\n};\n\n// Stream the response body, aborting the underlying request the instant the\n// running byte count exceeds `maxBytes`. This is the actual OOM guard: a\n// gateway that withholds or lies about Content-Length still cannot make us\n// buffer more than the cap, because we stop reading and tear the socket down.\nasync function readBodyCapped(\n res: Response,\n url: string,\n maxBytes: number,\n controller: AbortController,\n): Promise<Uint8Array> {\n const body = res.body;\n if (body === null) {\n // No stream (e.g. a 204, or a fetch polyfill that buffered eagerly). Fall\n // back to arrayBuffer but still enforce the cap on the materialised length.\n const buf = await res.arrayBuffer();\n if (buf.byteLength > maxBytes) {\n throw new BodyTooLargeError(url, maxBytes);\n }\n return new Uint8Array(buf);\n }\n\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value === undefined) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nexport function wrapFetchOutbound(\n inner: FetchOutbound,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig | ReadonlyArray<string> | undefined = undefined,\n): FetchOutbound {\n // Accept either a denyHosts array (positional) or the full config object.\n const normConfig: WrapFetchOutboundConfig =\n config === undefined\n ? {}\n : Array.isArray(config)\n ? { denyHosts: config as ReadonlyArray<string> }\n : (config as WrapFetchOutboundConfig);\n\n const denyHosts = normConfig.denyHosts ?? [];\n // Default retries=0 (single attempt). Callers opt in via explicit `retries`;\n // the top-level `fetchOutbound` entrypoint forwards caller config.\n const retries = normConfig.retries ?? 0;\n const retryableStatuses = normConfig.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;\n\n return async (url, opts) => {\n // The `webhook` purpose has bespoke requirements (DNS pinning,\n // per-hop redirect re-checking, body-size cap) that the generic\n // wrapper cannot satisfy. Force callers to use `fetchWebhook`\n // instead of silently accepting the call here.\n if (opts.purpose === 'webhook') {\n audit.push({\n url,\n method: 'GET',\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new Error(\n `webhook purpose must be sent via fetchWebhook, not fetchOutbound (url=${url})`,\n );\n }\n\n // Protocol allowlist.\n const protocol = parseProtocol(url);\n if (protocol !== 'http:' && protocol !== 'https:') {\n audit.push({\n url,\n method: 'GET',\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedProtocolError(protocol ?? '', url);\n }\n\n // Method allowlist.\n if (!isAllowedMethod(opts.method)) {\n audit.push({\n url,\n method: 'GET',\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedMethodError(opts.method, url);\n }\n\n // Deny-list short-circuit.\n if (denyHosts.length > 0) {\n const host = new URL(url).hostname;\n if (matchesDenyList(host, denyHosts)) {\n audit.push({\n url,\n method: opts.method,\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new DenyHostError(canonicaliseHost(host), url);\n }\n }\n\n // Retry loop. retries=0 → single attempt, return-or-rethrow original.\n let lastStatus: number | undefined;\n let lastError: Error | undefined;\n const totalAttempts = retries + 1;\n for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n const t0 = Date.now();\n try {\n const result = await inner(url, opts);\n audit.push({\n url,\n method: opts.method,\n status: result.status,\n bytes: result.bytes.byteLength,\n duration_ms: result.durationMs,\n purpose: opts.purpose,\n });\n if (retryableStatuses.includes(result.status) && retries > 0) {\n lastStatus = result.status;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n return result;\n } catch (e) {\n const durationMs = Date.now() - t0;\n if (\n e instanceof DenyHostError ||\n e instanceof UnsupportedProtocolError ||\n e instanceof UnsupportedMethodError\n ) {\n audit.push({\n url,\n method: opts.method,\n status: 0,\n bytes: 0,\n duration_ms: durationMs,\n purpose: opts.purpose,\n });\n throw e;\n }\n audit.push({\n url,\n method: opts.method,\n status: 0,\n bytes: 0,\n duration_ms: durationMs,\n purpose: opts.purpose,\n });\n lastError = e as Error;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n }\n // Single-attempt mode re-throws the original verbatim so callers can match\n // by identity; retry mode wraps the terminal failure in OutboundExhaustedError.\n if (retries === 0 && lastError !== undefined) {\n throw lastError;\n }\n throw new OutboundExhaustedError({ url, attempts: totalAttempts, lastStatus, lastError });\n };\n}\n\nexport async function fetchOutbound(\n url: string,\n opts: FetchOutboundOptions,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig = {},\n): Promise<FetchOutboundResult> {\n const wrapped = wrapFetchOutbound(defaultFetchOutbound, audit, config);\n return wrapped(url, opts);\n}\n","// Public denyHostsFetch surface — thin adapter over the canonical\n// fetchOutbound primitive in ./fetch-outbound.ts.\n\nimport {\n DenyHostError,\n type FetchOutbound,\n type FetchOutboundOptions,\n type HttpCallRecord,\n type HttpMethod,\n UnsupportedMethodError,\n UnsupportedProtocolError,\n wrapFetchOutbound,\n} from './fetch-outbound';\n\nexport { DenyHostError, UnsupportedMethodError, UnsupportedProtocolError };\n\nexport type HttpCall = HttpCallRecord;\n\nexport type DenyHostsFetchOptions = {\n readonly denyHosts: readonly string[];\n readonly audit: HttpCall[];\n readonly purpose: HttpCall['purpose'];\n readonly fetchImpl?: typeof fetch;\n};\n\nexport async function denyHostsFetch(\n url: string,\n init: RequestInit | undefined,\n opts: DenyHostsFetchOptions,\n): Promise<Response> {\n // Forward the raw method so the canonical wrap surfaces\n // UnsupportedMethodError instead of silently rewriting to GET.\n const rawMethod = (init?.method ?? 'GET') as HttpMethod;\n const headers = init?.headers as Record<string, string> | undefined;\n const bodyValue = init?.body;\n const body: string | undefined = typeof bodyValue === 'string' ? bodyValue : undefined;\n const fetchImpl: typeof fetch = opts.fetchImpl ?? globalThis.fetch;\n\n const innerOpts: FetchOutboundOptions = headers\n ? body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, headers, body }\n : { method: rawMethod, purpose: opts.purpose, headers }\n : body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, body }\n : { method: rawMethod, purpose: opts.purpose };\n\n let storedResponse: Response | null = null;\n const inner: FetchOutbound = async (innerUrl, innerOptsArg) => {\n const t0 = Date.now();\n const reqInit: RequestInit = { method: innerOptsArg.method };\n if (innerOptsArg.headers) reqInit.headers = { ...innerOptsArg.headers };\n if (innerOptsArg.body !== undefined) reqInit.body = innerOptsArg.body;\n const response = await fetchImpl(innerUrl, reqInit);\n storedResponse = response;\n const buf = await response.clone().arrayBuffer();\n return {\n status: response.status,\n bytes: new Uint8Array(buf),\n durationMs: Date.now() - t0,\n };\n };\n\n const wrapped = wrapFetchOutbound(inner, opts.audit, { denyHosts: opts.denyHosts });\n await wrapped(url, innerOpts);\n return storedResponse!;\n}\n"]}
1
+ {"version":3,"sources":["../../src/fetch/fetch-outbound.ts","../../src/fetch/deny-hosts.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Canonical outbound HTTP wrapper: deny-list short-circuit, protocol/method\n// allowlist, bounded timeout, exp-backoff retry with jitter, audit trail.\n\n// Universal loopback deny-host list a service-independent verifier MUST reject\n// so a record can never be made to \"verify\" only because it reached a loopback\n// address. This default carries no operator-specific entries: a deployment that\n// wants to forbid its own gateway/viewer hosts appends those at construction\n// time. Producers SHOULD pass this through `denyHosts` on every verifier\n// invocation; the wrapper accepts arbitrary lists but exports the canonical\n// loopback set so callers don't duplicate it inline. (RFC-1918 / link-local IP\n// ranges are blocked separately by the SSRF guard, not by this name list.)\nexport const DENY_HOSTS_DEFAULT: ReadonlyArray<string> = ['localhost', '127.0.0.1'];\n\n// Every outbound call carries a purpose tag from the closed set\n// `{cardano, arweave, ipfs}` (the three v1 gateway-chain purposes).\n// `https` is a transitional legacy tag for non-storage HTTPS\n// auxiliaries; new code SHOULD pick one of the three normative purposes.\n// `webhook` is the user-supplied-URL purpose: it triggers the SSRF guard\n// (DNS resolution + IP range check + connection pinning + redirect-chain\n// re-checking + body-size cap), and MUST be used for any fetch where the\n// target URL came from end-user input.\nexport type HttpPurpose = 'cardano' | 'arweave' | 'ipfs' | 'https' | 'webhook';\nexport type HttpMethod = 'GET' | 'POST';\n\nexport interface FetchOutboundOptions {\n readonly method: HttpMethod;\n readonly purpose: HttpPurpose;\n readonly headers?: Readonly<Record<string, string>>;\n readonly body?: string;\n // Hard cap on the response body the primitive will buffer. Gateway content\n // (ar:// / ipfs:// / https) is producer-chosen and therefore UNTRUSTED — the\n // verifier never trusts the producer — so a malicious gateway could otherwise\n // stream unbounded bytes into memory. Omit to use DEFAULT_OUTBOUND_MAX_BYTES.\n readonly maxBytes?: number;\n}\n\nexport interface FetchOutboundResult {\n readonly status: number;\n readonly bytes: Uint8Array;\n readonly durationMs: number;\n}\n\nexport type FetchOutbound = (\n url: string,\n opts: FetchOutboundOptions,\n) => Promise<FetchOutboundResult>;\n\n// Audit-log entry for one outbound HTTP fetch. The field set and names match\n// the verifier report's audit-trail entry exactly, so the record lands on\n// `VerifyReport.auditTrail[]` without a key-renaming pass. `status` is the\n// HTTP status when a response was received and `null` when none was (refused\n// call, transport failure).\nexport interface HttpCallRecord {\n readonly url: string;\n readonly method: HttpMethod;\n readonly status: number | null;\n readonly bytes: number;\n readonly durationMs: number;\n readonly purpose: HttpPurpose;\n}\n\nexport interface RetryConfig {\n readonly timeoutMs?: number;\n readonly retries?: number;\n readonly retryableStatuses?: ReadonlyArray<number>;\n}\n\nexport interface WrapFetchOutboundConfig extends RetryConfig {\n readonly denyHosts?: ReadonlyArray<string>;\n}\n\nexport class DenyHostError extends Error {\n readonly code = 'SERVICE_INDEPENDENCE_VIOLATION';\n readonly host: string;\n readonly url: string;\n constructor(host: string, url: string) {\n super(`SERVICE_INDEPENDENCE_VIOLATION: host \"${host}\" is in denyHosts (url=${url})`);\n this.name = 'DenyHostError';\n this.host = host;\n this.url = url;\n }\n}\n\n// The typed errors discriminate on their stable `code` property, never on\n// class identity: the package ships several entry points in two module\n// formats, so a consumer's `BodyTooLargeError` (thrown by a custom transport\n// that imported it from another entry) is a different class object than the\n// verifier's. `instanceof` is kept as the fast path for the common\n// same-module case.\n\n/** Whether `e` is a deny-host refusal (`SERVICE_INDEPENDENCE_VIOLATION`). */\nexport function isDenyHostError(e: unknown): e is DenyHostError {\n return (\n e instanceof DenyHostError ||\n (typeof e === 'object' &&\n e !== null &&\n (e as { code?: unknown }).code === 'SERVICE_INDEPENDENCE_VIOLATION')\n );\n}\n\n/** Whether `e` is a body-cap abort (`OUTBOUND_BODY_TOO_LARGE`). */\nexport function isBodyTooLargeError(e: unknown): e is BodyTooLargeError {\n return (\n e instanceof BodyTooLargeError ||\n (typeof e === 'object' &&\n e !== null &&\n (e as { code?: unknown }).code === 'OUTBOUND_BODY_TOO_LARGE')\n );\n}\n\nexport class UnsupportedProtocolError extends Error {\n readonly code = 'UNSUPPORTED_PROTOCOL';\n readonly protocol: string;\n readonly url: string;\n constructor(protocol: string, url: string) {\n super(`UNSUPPORTED_PROTOCOL: \"${protocol}\" not in {http:, https:} (url=${url})`);\n this.name = 'UnsupportedProtocolError';\n this.protocol = protocol;\n this.url = url;\n }\n}\n\nexport class UnsupportedMethodError extends Error {\n readonly code = 'UNSUPPORTED_METHOD';\n readonly method: string;\n readonly url: string;\n constructor(method: string, url: string) {\n super(`UNSUPPORTED_METHOD: \"${method}\" not in {GET, POST} (url=${url})`);\n this.name = 'UnsupportedMethodError';\n this.method = method;\n this.url = url;\n }\n}\n\nexport class BodyTooLargeError extends Error {\n readonly code = 'OUTBOUND_BODY_TOO_LARGE';\n readonly url: string;\n readonly limitBytes: number;\n constructor(url: string, limitBytes: number) {\n super(`OUTBOUND_BODY_TOO_LARGE: response exceeded ${limitBytes} bytes (url=${url})`);\n this.name = 'BodyTooLargeError';\n this.url = url;\n this.limitBytes = limitBytes;\n }\n}\n\nexport class OutboundExhaustedError extends Error {\n readonly code = 'OUTBOUND_EXHAUSTED';\n readonly url: string;\n readonly attempts: number;\n readonly lastStatus: number | undefined;\n readonly lastError: Error | undefined;\n constructor(args: {\n url: string;\n attempts: number;\n lastStatus?: number | undefined;\n lastError?: Error | undefined;\n }) {\n super(\n `OUTBOUND_EXHAUSTED: ${args.attempts} attempts exhausted (url=${args.url}, lastStatus=${args.lastStatus ?? '-'})`,\n );\n this.name = 'OutboundExhaustedError';\n this.url = args.url;\n this.attempts = args.attempts;\n this.lastStatus = args.lastStatus;\n this.lastError = args.lastError;\n }\n}\n\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n// Default response-body cap for the verifier's gateway fetches. 64 MiB sits\n// well above any single sealed-PoE ciphertext or merkle-leaf payload a verifier\n// would realistically recompute a hash over, while bounding the memory a hostile\n// gateway can force the verifier to allocate for one request. Callers that\n// legitimately handle larger content raise it per-call via `opts.maxBytes`.\nexport const DEFAULT_OUTBOUND_MAX_BYTES = 64 * 1024 * 1024;\nexport const DEFAULT_RETRYABLE_STATUSES: ReadonlyArray<number> = [502, 503, 504];\nconst BACKOFF_BASE_MS: ReadonlyArray<number> = [1000, 2000, 4000];\nconst JITTER_RATIO = 0.25;\n\nfunction canonicaliseHost(host: string): string {\n return host.replace(/^\\[/, '').replace(/\\]$/, '').replace(/\\.$/, '').toLowerCase();\n}\n\nexport function matchesDenyList(host: string, denyHosts: ReadonlyArray<string>): boolean {\n const h = canonicaliseHost(host);\n for (const raw of denyHosts) {\n const pattern = raw.replace(/\\.$/, '').toLowerCase();\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2);\n if (h.endsWith('.' + suffix)) return true;\n continue;\n }\n if (h === pattern) return true;\n if (pattern === 'localhost') {\n if (h === '::1' || h === '0.0.0.0' || h === '169.254.169.254') return true;\n }\n if (pattern === '127.0.0.1') {\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(h)) return true;\n }\n }\n return false;\n}\n\nfunction parseProtocol(url: string): string | null {\n try {\n return new URL(url).protocol;\n } catch {\n return null;\n }\n}\n\nfunction isAllowedMethod(method: string): method is HttpMethod {\n return method === 'GET' || method === 'POST';\n}\n\nfunction backoffJitteredMs(attemptIndex: number): number {\n const idx = Math.min(attemptIndex, BACKOFF_BASE_MS.length - 1);\n const base = BACKOFF_BASE_MS[idx] ?? BACKOFF_BASE_MS[BACKOFF_BASE_MS.length - 1]!;\n const jitter = 1 + (Math.random() - 0.5) * 2 * JITTER_RATIO;\n return base * jitter;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport const defaultFetchOutbound: FetchOutbound = async (url, opts) => {\n const t0 = Date.now();\n const maxBytes = opts.maxBytes ?? DEFAULT_OUTBOUND_MAX_BYTES;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n const init: RequestInit = {\n method: opts.method,\n signal: controller.signal,\n // Redirects are never followed — deny-host and protocol validation ran\n // against the original URL only, so a 3xx from an allowed host could\n // otherwise pivot the fetch to any target (e.g. `302 Location:\n // http://127.0.0.1/…`) behind the verifier's back. Every target must be\n // validated, so a redirect is a fetch failure; all SDKs behave\n // identically. A readable 3xx flows through as a non-2xx status and the\n // caller's attempt handling marks it failed, like a 5xx.\n redirect: 'manual',\n };\n if (opts.headers) init.headers = { ...opts.headers };\n if (opts.body !== undefined) init.body = opts.body;\n try {\n // allow-raw-fetch: canonical defaultFetchOutbound — single egress point\n const res = await fetch(url, init);\n\n // Browser runtimes surface a refused redirect as an opaque response\n // (type 'opaqueredirect', status 0) with no readable status or body;\n // there is nothing to report from it, so it fails like a transport error.\n if (res.type === 'opaqueredirect') {\n throw new Error(`redirect refused (opaqueredirect): ${url} answered with a redirect`);\n }\n\n // Fast path: a truthful Content-Length over the cap lets us bail before\n // reading a single body byte. A lying/absent header is still caught by the\n // streaming counter below — the header is an optimisation, not the guard.\n const declared = res.headers.get('content-length');\n if (declared !== null) {\n const declaredLen = Number(declared);\n if (Number.isFinite(declaredLen) && declaredLen > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n }\n\n const bytes = await readBodyCapped(res, url, maxBytes, controller);\n return { status: res.status, bytes, durationMs: Date.now() - t0 };\n } finally {\n clearTimeout(timeout);\n }\n};\n\n// Stream the response body, aborting the underlying request the instant the\n// running byte count exceeds `maxBytes`. This is the actual OOM guard: a\n// gateway that withholds or lies about Content-Length still cannot make us\n// buffer more than the cap, because we stop reading and tear the socket down.\nasync function readBodyCapped(\n res: Response,\n url: string,\n maxBytes: number,\n controller: AbortController,\n): Promise<Uint8Array> {\n const body = res.body;\n if (body === null) {\n // No stream (e.g. a 204, or a fetch polyfill that buffered eagerly). Fall\n // back to arrayBuffer but still enforce the cap on the materialised length.\n const buf = await res.arrayBuffer();\n if (buf.byteLength > maxBytes) {\n throw new BodyTooLargeError(url, maxBytes);\n }\n return new Uint8Array(buf);\n }\n\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value === undefined) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nexport function wrapFetchOutbound(\n inner: FetchOutbound,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig | ReadonlyArray<string> | undefined = undefined,\n): FetchOutbound {\n // Accept either a denyHosts array (positional) or the full config object.\n const normConfig: WrapFetchOutboundConfig =\n config === undefined\n ? {}\n : Array.isArray(config)\n ? { denyHosts: config as ReadonlyArray<string> }\n : (config as WrapFetchOutboundConfig);\n\n const denyHosts = normConfig.denyHosts ?? [];\n // Default retries=0 (single attempt). Callers opt in via explicit `retries`;\n // the top-level `fetchOutbound` entrypoint forwards caller config.\n const retries = normConfig.retries ?? 0;\n const retryableStatuses = normConfig.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;\n\n return async (url, opts) => {\n // The `webhook` purpose has bespoke requirements (DNS pinning,\n // per-hop redirect re-checking, body-size cap) that the generic\n // wrapper cannot satisfy. Force callers to use `fetchWebhook`\n // instead of silently accepting the call here.\n if (opts.purpose === 'webhook') {\n audit.push({\n url,\n method: 'GET',\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new Error(\n `webhook purpose must be sent via fetchWebhook, not fetchOutbound (url=${url})`,\n );\n }\n\n // Protocol allowlist.\n const protocol = parseProtocol(url);\n if (protocol !== 'http:' && protocol !== 'https:') {\n audit.push({\n url,\n method: 'GET',\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedProtocolError(protocol ?? '', url);\n }\n\n // Method allowlist.\n if (!isAllowedMethod(opts.method)) {\n audit.push({\n url,\n method: 'GET',\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedMethodError(opts.method, url);\n }\n\n // Deny-list short-circuit.\n if (denyHosts.length > 0) {\n const host = new URL(url).hostname;\n if (matchesDenyList(host, denyHosts)) {\n audit.push({\n url,\n method: opts.method,\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new DenyHostError(canonicaliseHost(host), url);\n }\n }\n\n // Retry loop. retries=0 → single attempt, return-or-rethrow original.\n let lastStatus: number | undefined;\n let lastError: Error | undefined;\n const totalAttempts = retries + 1;\n for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n const t0 = Date.now();\n try {\n const result = await inner(url, opts);\n audit.push({\n url,\n method: opts.method,\n status: result.status,\n bytes: result.bytes.byteLength,\n durationMs: result.durationMs,\n purpose: opts.purpose,\n });\n if (retryableStatuses.includes(result.status) && retries > 0) {\n lastStatus = result.status;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n return result;\n } catch (e) {\n const durationMs = Date.now() - t0;\n if (\n e instanceof DenyHostError ||\n e instanceof UnsupportedProtocolError ||\n e instanceof UnsupportedMethodError\n ) {\n audit.push({\n url,\n method: opts.method,\n status: null,\n bytes: 0,\n durationMs,\n purpose: opts.purpose,\n });\n throw e;\n }\n audit.push({\n url,\n method: opts.method,\n status: null,\n bytes: 0,\n durationMs,\n purpose: opts.purpose,\n });\n lastError = e as Error;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n }\n // Single-attempt mode re-throws the original verbatim so callers can match\n // by identity; retry mode wraps the terminal failure in OutboundExhaustedError.\n if (retries === 0 && lastError !== undefined) {\n throw lastError;\n }\n throw new OutboundExhaustedError({ url, attempts: totalAttempts, lastStatus, lastError });\n };\n}\n\nexport async function fetchOutbound(\n url: string,\n opts: FetchOutboundOptions,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig = {},\n): Promise<FetchOutboundResult> {\n const wrapped = wrapFetchOutbound(defaultFetchOutbound, audit, config);\n return wrapped(url, opts);\n}\n","// Public denyHostsFetch surface — thin adapter over the canonical\n// fetchOutbound primitive in ./fetch-outbound.ts.\n\nimport {\n DenyHostError,\n type FetchOutbound,\n type FetchOutboundOptions,\n type HttpCallRecord,\n type HttpMethod,\n UnsupportedMethodError,\n UnsupportedProtocolError,\n wrapFetchOutbound,\n} from './fetch-outbound';\n\nexport { DenyHostError, UnsupportedMethodError, UnsupportedProtocolError };\n\nexport type HttpCall = HttpCallRecord;\n\nexport type DenyHostsFetchOptions = {\n readonly denyHosts: readonly string[];\n readonly audit: HttpCall[];\n readonly purpose: HttpCall['purpose'];\n readonly fetchImpl?: typeof fetch;\n};\n\nexport async function denyHostsFetch(\n url: string,\n init: RequestInit | undefined,\n opts: DenyHostsFetchOptions,\n): Promise<Response> {\n // Forward the raw method so the canonical wrap surfaces\n // UnsupportedMethodError instead of silently rewriting to GET.\n const rawMethod = (init?.method ?? 'GET') as HttpMethod;\n const headers = init?.headers as Record<string, string> | undefined;\n const bodyValue = init?.body;\n const body: string | undefined = typeof bodyValue === 'string' ? bodyValue : undefined;\n const fetchImpl: typeof fetch = opts.fetchImpl ?? globalThis.fetch;\n\n const innerOpts: FetchOutboundOptions = headers\n ? body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, headers, body }\n : { method: rawMethod, purpose: opts.purpose, headers }\n : body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, body }\n : { method: rawMethod, purpose: opts.purpose };\n\n let storedResponse: Response | null = null;\n const inner: FetchOutbound = async (innerUrl, innerOptsArg) => {\n const t0 = Date.now();\n const reqInit: RequestInit = { method: innerOptsArg.method };\n if (innerOptsArg.headers) reqInit.headers = { ...innerOptsArg.headers };\n if (innerOptsArg.body !== undefined) reqInit.body = innerOptsArg.body;\n const response = await fetchImpl(innerUrl, reqInit);\n storedResponse = response;\n const buf = await response.clone().arrayBuffer();\n return {\n status: response.status,\n bytes: new Uint8Array(buf),\n durationMs: Date.now() - t0,\n };\n };\n\n const wrapped = wrapFetchOutbound(inner, opts.audit, { denyHosts: opts.denyHosts });\n await wrapped(url, innerOpts);\n return storedResponse!;\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { H as HttpCallRecord } from '../fetch-outbound-BT5-NiYN.cjs';
2
- export { B as BodyTooLargeError, D as DEFAULT_OUTBOUND_MAX_BYTES, b as DenyHostError, F as FetchOutbound, c as FetchOutboundOptions, d as FetchOutboundResult, e as HttpMethod, f as HttpPurpose, O as OutboundExhaustedError, R as RetryConfig, U as UnsupportedMethodError, g as UnsupportedProtocolError, W as WrapFetchOutboundConfig, h as defaultFetchOutbound, i as fetchOutbound, m as matchesDenyList, w as wrapFetchOutbound } from '../fetch-outbound-BT5-NiYN.cjs';
1
+ import { H as HttpCallRecord } from '../fetch-outbound-dOK3ZxYa.cjs';
2
+ export { B as BodyTooLargeError, D as DEFAULT_OUTBOUND_MAX_BYTES, b as DenyHostError, F as FetchOutbound, c as FetchOutboundOptions, d as FetchOutboundResult, e as HttpMethod, f as HttpPurpose, O as OutboundExhaustedError, R as RetryConfig, U as UnsupportedMethodError, g as UnsupportedProtocolError, W as WrapFetchOutboundConfig, h as defaultFetchOutbound, i as fetchOutbound, j as isBodyTooLargeError, k as isDenyHostError, m as matchesDenyList, w as wrapFetchOutbound } from '../fetch-outbound-dOK3ZxYa.cjs';
3
3
 
4
4
  type HttpCall = HttpCallRecord;
5
5
  type DenyHostsFetchOptions = {
@@ -1,5 +1,5 @@
1
- import { H as HttpCallRecord } from '../fetch-outbound-BT5-NiYN.js';
2
- export { B as BodyTooLargeError, D as DEFAULT_OUTBOUND_MAX_BYTES, b as DenyHostError, F as FetchOutbound, c as FetchOutboundOptions, d as FetchOutboundResult, e as HttpMethod, f as HttpPurpose, O as OutboundExhaustedError, R as RetryConfig, U as UnsupportedMethodError, g as UnsupportedProtocolError, W as WrapFetchOutboundConfig, h as defaultFetchOutbound, i as fetchOutbound, m as matchesDenyList, w as wrapFetchOutbound } from '../fetch-outbound-BT5-NiYN.js';
1
+ import { H as HttpCallRecord } from '../fetch-outbound-dOK3ZxYa.js';
2
+ export { B as BodyTooLargeError, D as DEFAULT_OUTBOUND_MAX_BYTES, b as DenyHostError, F as FetchOutbound, c as FetchOutboundOptions, d as FetchOutboundResult, e as HttpMethod, f as HttpPurpose, O as OutboundExhaustedError, R as RetryConfig, U as UnsupportedMethodError, g as UnsupportedProtocolError, W as WrapFetchOutboundConfig, h as defaultFetchOutbound, i as fetchOutbound, j as isBodyTooLargeError, k as isDenyHostError, m as matchesDenyList, w as wrapFetchOutbound } from '../fetch-outbound-dOK3ZxYa.js';
3
3
 
4
4
  type HttpCall = HttpCallRecord;
5
5
  type DenyHostsFetchOptions = {
@@ -10,6 +10,12 @@ var DenyHostError = class extends Error {
10
10
  this.url = url;
11
11
  }
12
12
  };
13
+ function isDenyHostError(e) {
14
+ return e instanceof DenyHostError || typeof e === "object" && e !== null && e.code === "SERVICE_INDEPENDENCE_VIOLATION";
15
+ }
16
+ function isBodyTooLargeError(e) {
17
+ return e instanceof BodyTooLargeError || typeof e === "object" && e !== null && e.code === "OUTBOUND_BODY_TOO_LARGE";
18
+ }
13
19
  var UnsupportedProtocolError = class extends Error {
14
20
  code = "UNSUPPORTED_PROTOCOL";
15
21
  protocol;
@@ -115,12 +121,23 @@ var defaultFetchOutbound = async (url, opts) => {
115
121
  const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
116
122
  const init = {
117
123
  method: opts.method,
118
- signal: controller.signal
124
+ signal: controller.signal,
125
+ // Redirects are never followed — deny-host and protocol validation ran
126
+ // against the original URL only, so a 3xx from an allowed host could
127
+ // otherwise pivot the fetch to any target (e.g. `302 Location:
128
+ // http://127.0.0.1/…`) behind the verifier's back. Every target must be
129
+ // validated, so a redirect is a fetch failure; all SDKs behave
130
+ // identically. A readable 3xx flows through as a non-2xx status and the
131
+ // caller's attempt handling marks it failed, like a 5xx.
132
+ redirect: "manual"
119
133
  };
120
134
  if (opts.headers) init.headers = { ...opts.headers };
121
135
  if (opts.body !== void 0) init.body = opts.body;
122
136
  try {
123
137
  const res = await fetch(url, init);
138
+ if (res.type === "opaqueredirect") {
139
+ throw new Error(`redirect refused (opaqueredirect): ${url} answered with a redirect`);
140
+ }
124
141
  const declared = res.headers.get("content-length");
125
142
  if (declared !== null) {
126
143
  const declaredLen = Number(declared);
@@ -180,9 +197,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
180
197
  audit.push({
181
198
  url,
182
199
  method: "GET",
183
- status: 0,
200
+ status: null,
184
201
  bytes: 0,
185
- duration_ms: 0,
202
+ durationMs: 0,
186
203
  purpose: opts.purpose
187
204
  });
188
205
  throw new Error(
@@ -194,9 +211,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
194
211
  audit.push({
195
212
  url,
196
213
  method: "GET",
197
- status: 0,
214
+ status: null,
198
215
  bytes: 0,
199
- duration_ms: 0,
216
+ durationMs: 0,
200
217
  purpose: opts.purpose
201
218
  });
202
219
  throw new UnsupportedProtocolError(protocol ?? "", url);
@@ -205,9 +222,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
205
222
  audit.push({
206
223
  url,
207
224
  method: "GET",
208
- status: 0,
225
+ status: null,
209
226
  bytes: 0,
210
- duration_ms: 0,
227
+ durationMs: 0,
211
228
  purpose: opts.purpose
212
229
  });
213
230
  throw new UnsupportedMethodError(opts.method, url);
@@ -218,9 +235,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
218
235
  audit.push({
219
236
  url,
220
237
  method: opts.method,
221
- status: 0,
238
+ status: null,
222
239
  bytes: 0,
223
- duration_ms: 0,
240
+ durationMs: 0,
224
241
  purpose: opts.purpose
225
242
  });
226
243
  throw new DenyHostError(canonicaliseHost(host), url);
@@ -238,7 +255,7 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
238
255
  method: opts.method,
239
256
  status: result.status,
240
257
  bytes: result.bytes.byteLength,
241
- duration_ms: result.durationMs,
258
+ durationMs: result.durationMs,
242
259
  purpose: opts.purpose
243
260
  });
244
261
  if (retryableStatuses.includes(result.status) && retries > 0) {
@@ -256,9 +273,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
256
273
  audit.push({
257
274
  url,
258
275
  method: opts.method,
259
- status: 0,
276
+ status: null,
260
277
  bytes: 0,
261
- duration_ms: durationMs,
278
+ durationMs,
262
279
  purpose: opts.purpose
263
280
  });
264
281
  throw e;
@@ -266,9 +283,9 @@ function wrapFetchOutbound(inner, audit, config = void 0) {
266
283
  audit.push({
267
284
  url,
268
285
  method: opts.method,
269
- status: 0,
286
+ status: null,
270
287
  bytes: 0,
271
- duration_ms: durationMs,
288
+ durationMs,
272
289
  purpose: opts.purpose
273
290
  });
274
291
  lastError = e;
@@ -318,6 +335,6 @@ async function denyHostsFetch(url, init, opts) {
318
335
  return storedResponse;
319
336
  }
320
337
 
321
- export { BodyTooLargeError, DEFAULT_OUTBOUND_MAX_BYTES, DenyHostError, OutboundExhaustedError, UnsupportedMethodError, UnsupportedProtocolError, defaultFetchOutbound, denyHostsFetch, fetchOutbound, matchesDenyList, wrapFetchOutbound };
338
+ export { BodyTooLargeError, DEFAULT_OUTBOUND_MAX_BYTES, DenyHostError, OutboundExhaustedError, UnsupportedMethodError, UnsupportedProtocolError, defaultFetchOutbound, denyHostsFetch, fetchOutbound, isBodyTooLargeError, isDenyHostError, matchesDenyList, wrapFetchOutbound };
322
339
  //# sourceMappingURL=index.js.map
323
340
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fetch/fetch-outbound.ts","../../src/fetch/deny-hosts.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Canonical outbound HTTP wrapper: deny-list short-circuit, protocol/method\n// allowlist, bounded timeout, exp-backoff retry with jitter, audit trail.\n\n// Universal loopback deny-host list a service-independent verifier MUST reject\n// so a record can never be made to \"verify\" only because it reached a loopback\n// address. This default carries no operator-specific entries: a deployment that\n// wants to forbid its own gateway/viewer hosts appends those at construction\n// time. Producers SHOULD pass this through `denyHosts` on every verifier\n// invocation; the wrapper accepts arbitrary lists but exports the canonical\n// loopback set so callers don't duplicate it inline. (RFC-1918 / link-local IP\n// ranges are blocked separately by the SSRF guard, not by this name list.)\nexport const DENY_HOSTS_DEFAULT: ReadonlyArray<string> = ['localhost', '127.0.0.1'];\n\n// Every outbound call carries a purpose tag from the closed set\n// `{cardano, arweave, ipfs}` (the three v1 gateway-chain purposes).\n// `https` is a transitional legacy tag for non-storage HTTPS\n// auxiliaries; new code SHOULD pick one of the three normative purposes.\n// `webhook` is the user-supplied-URL purpose: it triggers the SSRF guard\n// (DNS resolution + IP range check + connection pinning + redirect-chain\n// re-checking + body-size cap), and MUST be used for any fetch where the\n// target URL came from end-user input.\nexport type HttpPurpose = 'cardano' | 'arweave' | 'ipfs' | 'https' | 'webhook';\nexport type HttpMethod = 'GET' | 'POST';\n\nexport interface FetchOutboundOptions {\n readonly method: HttpMethod;\n readonly purpose: HttpPurpose;\n readonly headers?: Readonly<Record<string, string>>;\n readonly body?: string;\n // Hard cap on the response body the primitive will buffer. Gateway content\n // (ar:// / ipfs:// / https) is producer-chosen and therefore UNTRUSTED — the\n // verifier never trusts the producer — so a malicious gateway could otherwise\n // stream unbounded bytes into memory. Omit to use DEFAULT_OUTBOUND_MAX_BYTES.\n readonly maxBytes?: number;\n}\n\nexport interface FetchOutboundResult {\n readonly status: number;\n readonly bytes: Uint8Array;\n readonly durationMs: number;\n}\n\nexport type FetchOutbound = (\n url: string,\n opts: FetchOutboundOptions,\n) => Promise<FetchOutboundResult>;\n\n// Audit-log entry for one outbound HTTP fetch. Field names are snake_case so\n// the record can land directly on `VerifyReport.http_calls[]` (which IS the\n// wire shape) without a key-renaming pass.\nexport interface HttpCallRecord {\n readonly url: string;\n readonly method: HttpMethod;\n readonly status: number;\n readonly bytes: number;\n readonly duration_ms: number;\n readonly purpose: HttpPurpose;\n}\n\nexport interface RetryConfig {\n readonly timeoutMs?: number;\n readonly retries?: number;\n readonly retryableStatuses?: ReadonlyArray<number>;\n}\n\nexport interface WrapFetchOutboundConfig extends RetryConfig {\n readonly denyHosts?: ReadonlyArray<string>;\n}\n\nexport class DenyHostError extends Error {\n readonly code = 'SERVICE_INDEPENDENCE_VIOLATION';\n readonly host: string;\n readonly url: string;\n constructor(host: string, url: string) {\n super(`SERVICE_INDEPENDENCE_VIOLATION: host \"${host}\" is in denyHosts (url=${url})`);\n this.name = 'DenyHostError';\n this.host = host;\n this.url = url;\n }\n}\n\nexport class UnsupportedProtocolError extends Error {\n readonly code = 'UNSUPPORTED_PROTOCOL';\n readonly protocol: string;\n readonly url: string;\n constructor(protocol: string, url: string) {\n super(`UNSUPPORTED_PROTOCOL: \"${protocol}\" not in {http:, https:} (url=${url})`);\n this.name = 'UnsupportedProtocolError';\n this.protocol = protocol;\n this.url = url;\n }\n}\n\nexport class UnsupportedMethodError extends Error {\n readonly code = 'UNSUPPORTED_METHOD';\n readonly method: string;\n readonly url: string;\n constructor(method: string, url: string) {\n super(`UNSUPPORTED_METHOD: \"${method}\" not in {GET, POST} (url=${url})`);\n this.name = 'UnsupportedMethodError';\n this.method = method;\n this.url = url;\n }\n}\n\nexport class BodyTooLargeError extends Error {\n readonly code = 'OUTBOUND_BODY_TOO_LARGE';\n readonly url: string;\n readonly limitBytes: number;\n constructor(url: string, limitBytes: number) {\n super(`OUTBOUND_BODY_TOO_LARGE: response exceeded ${limitBytes} bytes (url=${url})`);\n this.name = 'BodyTooLargeError';\n this.url = url;\n this.limitBytes = limitBytes;\n }\n}\n\nexport class OutboundExhaustedError extends Error {\n readonly code = 'OUTBOUND_EXHAUSTED';\n readonly url: string;\n readonly attempts: number;\n readonly lastStatus: number | undefined;\n readonly lastError: Error | undefined;\n constructor(args: {\n url: string;\n attempts: number;\n lastStatus?: number | undefined;\n lastError?: Error | undefined;\n }) {\n super(\n `OUTBOUND_EXHAUSTED: ${args.attempts} attempts exhausted (url=${args.url}, lastStatus=${args.lastStatus ?? '-'})`,\n );\n this.name = 'OutboundExhaustedError';\n this.url = args.url;\n this.attempts = args.attempts;\n this.lastStatus = args.lastStatus;\n this.lastError = args.lastError;\n }\n}\n\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n// Default response-body cap for the verifier's gateway fetches. 64 MiB sits\n// well above any single sealed-PoE ciphertext or merkle-leaf payload a verifier\n// would realistically recompute a hash over, while bounding the memory a hostile\n// gateway can force the verifier to allocate for one request. Callers that\n// legitimately handle larger content raise it per-call via `opts.maxBytes`.\nexport const DEFAULT_OUTBOUND_MAX_BYTES = 64 * 1024 * 1024;\nexport const DEFAULT_RETRYABLE_STATUSES: ReadonlyArray<number> = [502, 503, 504];\nconst BACKOFF_BASE_MS: ReadonlyArray<number> = [1000, 2000, 4000];\nconst JITTER_RATIO = 0.25;\n\nfunction canonicaliseHost(host: string): string {\n return host.replace(/^\\[/, '').replace(/\\]$/, '').replace(/\\.$/, '').toLowerCase();\n}\n\nexport function matchesDenyList(host: string, denyHosts: ReadonlyArray<string>): boolean {\n const h = canonicaliseHost(host);\n for (const raw of denyHosts) {\n const pattern = raw.replace(/\\.$/, '').toLowerCase();\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2);\n if (h.endsWith('.' + suffix)) return true;\n continue;\n }\n if (h === pattern) return true;\n if (pattern === 'localhost') {\n if (h === '::1' || h === '0.0.0.0' || h === '169.254.169.254') return true;\n }\n if (pattern === '127.0.0.1') {\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(h)) return true;\n }\n }\n return false;\n}\n\nfunction parseProtocol(url: string): string | null {\n try {\n return new URL(url).protocol;\n } catch {\n return null;\n }\n}\n\nfunction isAllowedMethod(method: string): method is HttpMethod {\n return method === 'GET' || method === 'POST';\n}\n\nfunction backoffJitteredMs(attemptIndex: number): number {\n const idx = Math.min(attemptIndex, BACKOFF_BASE_MS.length - 1);\n const base = BACKOFF_BASE_MS[idx] ?? BACKOFF_BASE_MS[BACKOFF_BASE_MS.length - 1]!;\n const jitter = 1 + (Math.random() - 0.5) * 2 * JITTER_RATIO;\n return base * jitter;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport const defaultFetchOutbound: FetchOutbound = async (url, opts) => {\n const t0 = Date.now();\n const maxBytes = opts.maxBytes ?? DEFAULT_OUTBOUND_MAX_BYTES;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n const init: RequestInit = {\n method: opts.method,\n signal: controller.signal,\n };\n if (opts.headers) init.headers = { ...opts.headers };\n if (opts.body !== undefined) init.body = opts.body;\n try {\n // allow-raw-fetch: canonical defaultFetchOutbound — single egress point\n const res = await fetch(url, init);\n\n // Fast path: a truthful Content-Length over the cap lets us bail before\n // reading a single body byte. A lying/absent header is still caught by the\n // streaming counter below — the header is an optimisation, not the guard.\n const declared = res.headers.get('content-length');\n if (declared !== null) {\n const declaredLen = Number(declared);\n if (Number.isFinite(declaredLen) && declaredLen > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n }\n\n const bytes = await readBodyCapped(res, url, maxBytes, controller);\n return { status: res.status, bytes, durationMs: Date.now() - t0 };\n } finally {\n clearTimeout(timeout);\n }\n};\n\n// Stream the response body, aborting the underlying request the instant the\n// running byte count exceeds `maxBytes`. This is the actual OOM guard: a\n// gateway that withholds or lies about Content-Length still cannot make us\n// buffer more than the cap, because we stop reading and tear the socket down.\nasync function readBodyCapped(\n res: Response,\n url: string,\n maxBytes: number,\n controller: AbortController,\n): Promise<Uint8Array> {\n const body = res.body;\n if (body === null) {\n // No stream (e.g. a 204, or a fetch polyfill that buffered eagerly). Fall\n // back to arrayBuffer but still enforce the cap on the materialised length.\n const buf = await res.arrayBuffer();\n if (buf.byteLength > maxBytes) {\n throw new BodyTooLargeError(url, maxBytes);\n }\n return new Uint8Array(buf);\n }\n\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value === undefined) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nexport function wrapFetchOutbound(\n inner: FetchOutbound,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig | ReadonlyArray<string> | undefined = undefined,\n): FetchOutbound {\n // Accept either a denyHosts array (positional) or the full config object.\n const normConfig: WrapFetchOutboundConfig =\n config === undefined\n ? {}\n : Array.isArray(config)\n ? { denyHosts: config as ReadonlyArray<string> }\n : (config as WrapFetchOutboundConfig);\n\n const denyHosts = normConfig.denyHosts ?? [];\n // Default retries=0 (single attempt). Callers opt in via explicit `retries`;\n // the top-level `fetchOutbound` entrypoint forwards caller config.\n const retries = normConfig.retries ?? 0;\n const retryableStatuses = normConfig.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;\n\n return async (url, opts) => {\n // The `webhook` purpose has bespoke requirements (DNS pinning,\n // per-hop redirect re-checking, body-size cap) that the generic\n // wrapper cannot satisfy. Force callers to use `fetchWebhook`\n // instead of silently accepting the call here.\n if (opts.purpose === 'webhook') {\n audit.push({\n url,\n method: 'GET',\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new Error(\n `webhook purpose must be sent via fetchWebhook, not fetchOutbound (url=${url})`,\n );\n }\n\n // Protocol allowlist.\n const protocol = parseProtocol(url);\n if (protocol !== 'http:' && protocol !== 'https:') {\n audit.push({\n url,\n method: 'GET',\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedProtocolError(protocol ?? '', url);\n }\n\n // Method allowlist.\n if (!isAllowedMethod(opts.method)) {\n audit.push({\n url,\n method: 'GET',\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedMethodError(opts.method, url);\n }\n\n // Deny-list short-circuit.\n if (denyHosts.length > 0) {\n const host = new URL(url).hostname;\n if (matchesDenyList(host, denyHosts)) {\n audit.push({\n url,\n method: opts.method,\n status: 0,\n bytes: 0,\n duration_ms: 0,\n purpose: opts.purpose,\n });\n throw new DenyHostError(canonicaliseHost(host), url);\n }\n }\n\n // Retry loop. retries=0 → single attempt, return-or-rethrow original.\n let lastStatus: number | undefined;\n let lastError: Error | undefined;\n const totalAttempts = retries + 1;\n for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n const t0 = Date.now();\n try {\n const result = await inner(url, opts);\n audit.push({\n url,\n method: opts.method,\n status: result.status,\n bytes: result.bytes.byteLength,\n duration_ms: result.durationMs,\n purpose: opts.purpose,\n });\n if (retryableStatuses.includes(result.status) && retries > 0) {\n lastStatus = result.status;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n return result;\n } catch (e) {\n const durationMs = Date.now() - t0;\n if (\n e instanceof DenyHostError ||\n e instanceof UnsupportedProtocolError ||\n e instanceof UnsupportedMethodError\n ) {\n audit.push({\n url,\n method: opts.method,\n status: 0,\n bytes: 0,\n duration_ms: durationMs,\n purpose: opts.purpose,\n });\n throw e;\n }\n audit.push({\n url,\n method: opts.method,\n status: 0,\n bytes: 0,\n duration_ms: durationMs,\n purpose: opts.purpose,\n });\n lastError = e as Error;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n }\n // Single-attempt mode re-throws the original verbatim so callers can match\n // by identity; retry mode wraps the terminal failure in OutboundExhaustedError.\n if (retries === 0 && lastError !== undefined) {\n throw lastError;\n }\n throw new OutboundExhaustedError({ url, attempts: totalAttempts, lastStatus, lastError });\n };\n}\n\nexport async function fetchOutbound(\n url: string,\n opts: FetchOutboundOptions,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig = {},\n): Promise<FetchOutboundResult> {\n const wrapped = wrapFetchOutbound(defaultFetchOutbound, audit, config);\n return wrapped(url, opts);\n}\n","// Public denyHostsFetch surface — thin adapter over the canonical\n// fetchOutbound primitive in ./fetch-outbound.ts.\n\nimport {\n DenyHostError,\n type FetchOutbound,\n type FetchOutboundOptions,\n type HttpCallRecord,\n type HttpMethod,\n UnsupportedMethodError,\n UnsupportedProtocolError,\n wrapFetchOutbound,\n} from './fetch-outbound';\n\nexport { DenyHostError, UnsupportedMethodError, UnsupportedProtocolError };\n\nexport type HttpCall = HttpCallRecord;\n\nexport type DenyHostsFetchOptions = {\n readonly denyHosts: readonly string[];\n readonly audit: HttpCall[];\n readonly purpose: HttpCall['purpose'];\n readonly fetchImpl?: typeof fetch;\n};\n\nexport async function denyHostsFetch(\n url: string,\n init: RequestInit | undefined,\n opts: DenyHostsFetchOptions,\n): Promise<Response> {\n // Forward the raw method so the canonical wrap surfaces\n // UnsupportedMethodError instead of silently rewriting to GET.\n const rawMethod = (init?.method ?? 'GET') as HttpMethod;\n const headers = init?.headers as Record<string, string> | undefined;\n const bodyValue = init?.body;\n const body: string | undefined = typeof bodyValue === 'string' ? bodyValue : undefined;\n const fetchImpl: typeof fetch = opts.fetchImpl ?? globalThis.fetch;\n\n const innerOpts: FetchOutboundOptions = headers\n ? body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, headers, body }\n : { method: rawMethod, purpose: opts.purpose, headers }\n : body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, body }\n : { method: rawMethod, purpose: opts.purpose };\n\n let storedResponse: Response | null = null;\n const inner: FetchOutbound = async (innerUrl, innerOptsArg) => {\n const t0 = Date.now();\n const reqInit: RequestInit = { method: innerOptsArg.method };\n if (innerOptsArg.headers) reqInit.headers = { ...innerOptsArg.headers };\n if (innerOptsArg.body !== undefined) reqInit.body = innerOptsArg.body;\n const response = await fetchImpl(innerUrl, reqInit);\n storedResponse = response;\n const buf = await response.clone().arrayBuffer();\n return {\n status: response.status,\n bytes: new Uint8Array(buf),\n durationMs: Date.now() - t0,\n };\n };\n\n const wrapped = wrapFetchOutbound(inner, opts.audit, { denyHosts: opts.denyHosts });\n await wrapped(url, innerOpts);\n return storedResponse!;\n}\n"]}
1
+ {"version":3,"sources":["../../src/fetch/fetch-outbound.ts","../../src/fetch/deny-hosts.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Canonical outbound HTTP wrapper: deny-list short-circuit, protocol/method\n// allowlist, bounded timeout, exp-backoff retry with jitter, audit trail.\n\n// Universal loopback deny-host list a service-independent verifier MUST reject\n// so a record can never be made to \"verify\" only because it reached a loopback\n// address. This default carries no operator-specific entries: a deployment that\n// wants to forbid its own gateway/viewer hosts appends those at construction\n// time. Producers SHOULD pass this through `denyHosts` on every verifier\n// invocation; the wrapper accepts arbitrary lists but exports the canonical\n// loopback set so callers don't duplicate it inline. (RFC-1918 / link-local IP\n// ranges are blocked separately by the SSRF guard, not by this name list.)\nexport const DENY_HOSTS_DEFAULT: ReadonlyArray<string> = ['localhost', '127.0.0.1'];\n\n// Every outbound call carries a purpose tag from the closed set\n// `{cardano, arweave, ipfs}` (the three v1 gateway-chain purposes).\n// `https` is a transitional legacy tag for non-storage HTTPS\n// auxiliaries; new code SHOULD pick one of the three normative purposes.\n// `webhook` is the user-supplied-URL purpose: it triggers the SSRF guard\n// (DNS resolution + IP range check + connection pinning + redirect-chain\n// re-checking + body-size cap), and MUST be used for any fetch where the\n// target URL came from end-user input.\nexport type HttpPurpose = 'cardano' | 'arweave' | 'ipfs' | 'https' | 'webhook';\nexport type HttpMethod = 'GET' | 'POST';\n\nexport interface FetchOutboundOptions {\n readonly method: HttpMethod;\n readonly purpose: HttpPurpose;\n readonly headers?: Readonly<Record<string, string>>;\n readonly body?: string;\n // Hard cap on the response body the primitive will buffer. Gateway content\n // (ar:// / ipfs:// / https) is producer-chosen and therefore UNTRUSTED — the\n // verifier never trusts the producer — so a malicious gateway could otherwise\n // stream unbounded bytes into memory. Omit to use DEFAULT_OUTBOUND_MAX_BYTES.\n readonly maxBytes?: number;\n}\n\nexport interface FetchOutboundResult {\n readonly status: number;\n readonly bytes: Uint8Array;\n readonly durationMs: number;\n}\n\nexport type FetchOutbound = (\n url: string,\n opts: FetchOutboundOptions,\n) => Promise<FetchOutboundResult>;\n\n// Audit-log entry for one outbound HTTP fetch. The field set and names match\n// the verifier report's audit-trail entry exactly, so the record lands on\n// `VerifyReport.auditTrail[]` without a key-renaming pass. `status` is the\n// HTTP status when a response was received and `null` when none was (refused\n// call, transport failure).\nexport interface HttpCallRecord {\n readonly url: string;\n readonly method: HttpMethod;\n readonly status: number | null;\n readonly bytes: number;\n readonly durationMs: number;\n readonly purpose: HttpPurpose;\n}\n\nexport interface RetryConfig {\n readonly timeoutMs?: number;\n readonly retries?: number;\n readonly retryableStatuses?: ReadonlyArray<number>;\n}\n\nexport interface WrapFetchOutboundConfig extends RetryConfig {\n readonly denyHosts?: ReadonlyArray<string>;\n}\n\nexport class DenyHostError extends Error {\n readonly code = 'SERVICE_INDEPENDENCE_VIOLATION';\n readonly host: string;\n readonly url: string;\n constructor(host: string, url: string) {\n super(`SERVICE_INDEPENDENCE_VIOLATION: host \"${host}\" is in denyHosts (url=${url})`);\n this.name = 'DenyHostError';\n this.host = host;\n this.url = url;\n }\n}\n\n// The typed errors discriminate on their stable `code` property, never on\n// class identity: the package ships several entry points in two module\n// formats, so a consumer's `BodyTooLargeError` (thrown by a custom transport\n// that imported it from another entry) is a different class object than the\n// verifier's. `instanceof` is kept as the fast path for the common\n// same-module case.\n\n/** Whether `e` is a deny-host refusal (`SERVICE_INDEPENDENCE_VIOLATION`). */\nexport function isDenyHostError(e: unknown): e is DenyHostError {\n return (\n e instanceof DenyHostError ||\n (typeof e === 'object' &&\n e !== null &&\n (e as { code?: unknown }).code === 'SERVICE_INDEPENDENCE_VIOLATION')\n );\n}\n\n/** Whether `e` is a body-cap abort (`OUTBOUND_BODY_TOO_LARGE`). */\nexport function isBodyTooLargeError(e: unknown): e is BodyTooLargeError {\n return (\n e instanceof BodyTooLargeError ||\n (typeof e === 'object' &&\n e !== null &&\n (e as { code?: unknown }).code === 'OUTBOUND_BODY_TOO_LARGE')\n );\n}\n\nexport class UnsupportedProtocolError extends Error {\n readonly code = 'UNSUPPORTED_PROTOCOL';\n readonly protocol: string;\n readonly url: string;\n constructor(protocol: string, url: string) {\n super(`UNSUPPORTED_PROTOCOL: \"${protocol}\" not in {http:, https:} (url=${url})`);\n this.name = 'UnsupportedProtocolError';\n this.protocol = protocol;\n this.url = url;\n }\n}\n\nexport class UnsupportedMethodError extends Error {\n readonly code = 'UNSUPPORTED_METHOD';\n readonly method: string;\n readonly url: string;\n constructor(method: string, url: string) {\n super(`UNSUPPORTED_METHOD: \"${method}\" not in {GET, POST} (url=${url})`);\n this.name = 'UnsupportedMethodError';\n this.method = method;\n this.url = url;\n }\n}\n\nexport class BodyTooLargeError extends Error {\n readonly code = 'OUTBOUND_BODY_TOO_LARGE';\n readonly url: string;\n readonly limitBytes: number;\n constructor(url: string, limitBytes: number) {\n super(`OUTBOUND_BODY_TOO_LARGE: response exceeded ${limitBytes} bytes (url=${url})`);\n this.name = 'BodyTooLargeError';\n this.url = url;\n this.limitBytes = limitBytes;\n }\n}\n\nexport class OutboundExhaustedError extends Error {\n readonly code = 'OUTBOUND_EXHAUSTED';\n readonly url: string;\n readonly attempts: number;\n readonly lastStatus: number | undefined;\n readonly lastError: Error | undefined;\n constructor(args: {\n url: string;\n attempts: number;\n lastStatus?: number | undefined;\n lastError?: Error | undefined;\n }) {\n super(\n `OUTBOUND_EXHAUSTED: ${args.attempts} attempts exhausted (url=${args.url}, lastStatus=${args.lastStatus ?? '-'})`,\n );\n this.name = 'OutboundExhaustedError';\n this.url = args.url;\n this.attempts = args.attempts;\n this.lastStatus = args.lastStatus;\n this.lastError = args.lastError;\n }\n}\n\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n// Default response-body cap for the verifier's gateway fetches. 64 MiB sits\n// well above any single sealed-PoE ciphertext or merkle-leaf payload a verifier\n// would realistically recompute a hash over, while bounding the memory a hostile\n// gateway can force the verifier to allocate for one request. Callers that\n// legitimately handle larger content raise it per-call via `opts.maxBytes`.\nexport const DEFAULT_OUTBOUND_MAX_BYTES = 64 * 1024 * 1024;\nexport const DEFAULT_RETRYABLE_STATUSES: ReadonlyArray<number> = [502, 503, 504];\nconst BACKOFF_BASE_MS: ReadonlyArray<number> = [1000, 2000, 4000];\nconst JITTER_RATIO = 0.25;\n\nfunction canonicaliseHost(host: string): string {\n return host.replace(/^\\[/, '').replace(/\\]$/, '').replace(/\\.$/, '').toLowerCase();\n}\n\nexport function matchesDenyList(host: string, denyHosts: ReadonlyArray<string>): boolean {\n const h = canonicaliseHost(host);\n for (const raw of denyHosts) {\n const pattern = raw.replace(/\\.$/, '').toLowerCase();\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2);\n if (h.endsWith('.' + suffix)) return true;\n continue;\n }\n if (h === pattern) return true;\n if (pattern === 'localhost') {\n if (h === '::1' || h === '0.0.0.0' || h === '169.254.169.254') return true;\n }\n if (pattern === '127.0.0.1') {\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(h)) return true;\n }\n }\n return false;\n}\n\nfunction parseProtocol(url: string): string | null {\n try {\n return new URL(url).protocol;\n } catch {\n return null;\n }\n}\n\nfunction isAllowedMethod(method: string): method is HttpMethod {\n return method === 'GET' || method === 'POST';\n}\n\nfunction backoffJitteredMs(attemptIndex: number): number {\n const idx = Math.min(attemptIndex, BACKOFF_BASE_MS.length - 1);\n const base = BACKOFF_BASE_MS[idx] ?? BACKOFF_BASE_MS[BACKOFF_BASE_MS.length - 1]!;\n const jitter = 1 + (Math.random() - 0.5) * 2 * JITTER_RATIO;\n return base * jitter;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport const defaultFetchOutbound: FetchOutbound = async (url, opts) => {\n const t0 = Date.now();\n const maxBytes = opts.maxBytes ?? DEFAULT_OUTBOUND_MAX_BYTES;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n const init: RequestInit = {\n method: opts.method,\n signal: controller.signal,\n // Redirects are never followed — deny-host and protocol validation ran\n // against the original URL only, so a 3xx from an allowed host could\n // otherwise pivot the fetch to any target (e.g. `302 Location:\n // http://127.0.0.1/…`) behind the verifier's back. Every target must be\n // validated, so a redirect is a fetch failure; all SDKs behave\n // identically. A readable 3xx flows through as a non-2xx status and the\n // caller's attempt handling marks it failed, like a 5xx.\n redirect: 'manual',\n };\n if (opts.headers) init.headers = { ...opts.headers };\n if (opts.body !== undefined) init.body = opts.body;\n try {\n // allow-raw-fetch: canonical defaultFetchOutbound — single egress point\n const res = await fetch(url, init);\n\n // Browser runtimes surface a refused redirect as an opaque response\n // (type 'opaqueredirect', status 0) with no readable status or body;\n // there is nothing to report from it, so it fails like a transport error.\n if (res.type === 'opaqueredirect') {\n throw new Error(`redirect refused (opaqueredirect): ${url} answered with a redirect`);\n }\n\n // Fast path: a truthful Content-Length over the cap lets us bail before\n // reading a single body byte. A lying/absent header is still caught by the\n // streaming counter below — the header is an optimisation, not the guard.\n const declared = res.headers.get('content-length');\n if (declared !== null) {\n const declaredLen = Number(declared);\n if (Number.isFinite(declaredLen) && declaredLen > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n }\n\n const bytes = await readBodyCapped(res, url, maxBytes, controller);\n return { status: res.status, bytes, durationMs: Date.now() - t0 };\n } finally {\n clearTimeout(timeout);\n }\n};\n\n// Stream the response body, aborting the underlying request the instant the\n// running byte count exceeds `maxBytes`. This is the actual OOM guard: a\n// gateway that withholds or lies about Content-Length still cannot make us\n// buffer more than the cap, because we stop reading and tear the socket down.\nasync function readBodyCapped(\n res: Response,\n url: string,\n maxBytes: number,\n controller: AbortController,\n): Promise<Uint8Array> {\n const body = res.body;\n if (body === null) {\n // No stream (e.g. a 204, or a fetch polyfill that buffered eagerly). Fall\n // back to arrayBuffer but still enforce the cap on the materialised length.\n const buf = await res.arrayBuffer();\n if (buf.byteLength > maxBytes) {\n throw new BodyTooLargeError(url, maxBytes);\n }\n return new Uint8Array(buf);\n }\n\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value === undefined) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n controller.abort();\n throw new BodyTooLargeError(url, maxBytes);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nexport function wrapFetchOutbound(\n inner: FetchOutbound,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig | ReadonlyArray<string> | undefined = undefined,\n): FetchOutbound {\n // Accept either a denyHosts array (positional) or the full config object.\n const normConfig: WrapFetchOutboundConfig =\n config === undefined\n ? {}\n : Array.isArray(config)\n ? { denyHosts: config as ReadonlyArray<string> }\n : (config as WrapFetchOutboundConfig);\n\n const denyHosts = normConfig.denyHosts ?? [];\n // Default retries=0 (single attempt). Callers opt in via explicit `retries`;\n // the top-level `fetchOutbound` entrypoint forwards caller config.\n const retries = normConfig.retries ?? 0;\n const retryableStatuses = normConfig.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;\n\n return async (url, opts) => {\n // The `webhook` purpose has bespoke requirements (DNS pinning,\n // per-hop redirect re-checking, body-size cap) that the generic\n // wrapper cannot satisfy. Force callers to use `fetchWebhook`\n // instead of silently accepting the call here.\n if (opts.purpose === 'webhook') {\n audit.push({\n url,\n method: 'GET',\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new Error(\n `webhook purpose must be sent via fetchWebhook, not fetchOutbound (url=${url})`,\n );\n }\n\n // Protocol allowlist.\n const protocol = parseProtocol(url);\n if (protocol !== 'http:' && protocol !== 'https:') {\n audit.push({\n url,\n method: 'GET',\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedProtocolError(protocol ?? '', url);\n }\n\n // Method allowlist.\n if (!isAllowedMethod(opts.method)) {\n audit.push({\n url,\n method: 'GET',\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new UnsupportedMethodError(opts.method, url);\n }\n\n // Deny-list short-circuit.\n if (denyHosts.length > 0) {\n const host = new URL(url).hostname;\n if (matchesDenyList(host, denyHosts)) {\n audit.push({\n url,\n method: opts.method,\n status: null,\n bytes: 0,\n durationMs: 0,\n purpose: opts.purpose,\n });\n throw new DenyHostError(canonicaliseHost(host), url);\n }\n }\n\n // Retry loop. retries=0 → single attempt, return-or-rethrow original.\n let lastStatus: number | undefined;\n let lastError: Error | undefined;\n const totalAttempts = retries + 1;\n for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n const t0 = Date.now();\n try {\n const result = await inner(url, opts);\n audit.push({\n url,\n method: opts.method,\n status: result.status,\n bytes: result.bytes.byteLength,\n durationMs: result.durationMs,\n purpose: opts.purpose,\n });\n if (retryableStatuses.includes(result.status) && retries > 0) {\n lastStatus = result.status;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n return result;\n } catch (e) {\n const durationMs = Date.now() - t0;\n if (\n e instanceof DenyHostError ||\n e instanceof UnsupportedProtocolError ||\n e instanceof UnsupportedMethodError\n ) {\n audit.push({\n url,\n method: opts.method,\n status: null,\n bytes: 0,\n durationMs,\n purpose: opts.purpose,\n });\n throw e;\n }\n audit.push({\n url,\n method: opts.method,\n status: null,\n bytes: 0,\n durationMs,\n purpose: opts.purpose,\n });\n lastError = e as Error;\n if (attempt < totalAttempts) {\n await sleep(backoffJitteredMs(attempt - 1));\n continue;\n }\n break;\n }\n }\n // Single-attempt mode re-throws the original verbatim so callers can match\n // by identity; retry mode wraps the terminal failure in OutboundExhaustedError.\n if (retries === 0 && lastError !== undefined) {\n throw lastError;\n }\n throw new OutboundExhaustedError({ url, attempts: totalAttempts, lastStatus, lastError });\n };\n}\n\nexport async function fetchOutbound(\n url: string,\n opts: FetchOutboundOptions,\n audit: HttpCallRecord[],\n config: WrapFetchOutboundConfig = {},\n): Promise<FetchOutboundResult> {\n const wrapped = wrapFetchOutbound(defaultFetchOutbound, audit, config);\n return wrapped(url, opts);\n}\n","// Public denyHostsFetch surface — thin adapter over the canonical\n// fetchOutbound primitive in ./fetch-outbound.ts.\n\nimport {\n DenyHostError,\n type FetchOutbound,\n type FetchOutboundOptions,\n type HttpCallRecord,\n type HttpMethod,\n UnsupportedMethodError,\n UnsupportedProtocolError,\n wrapFetchOutbound,\n} from './fetch-outbound';\n\nexport { DenyHostError, UnsupportedMethodError, UnsupportedProtocolError };\n\nexport type HttpCall = HttpCallRecord;\n\nexport type DenyHostsFetchOptions = {\n readonly denyHosts: readonly string[];\n readonly audit: HttpCall[];\n readonly purpose: HttpCall['purpose'];\n readonly fetchImpl?: typeof fetch;\n};\n\nexport async function denyHostsFetch(\n url: string,\n init: RequestInit | undefined,\n opts: DenyHostsFetchOptions,\n): Promise<Response> {\n // Forward the raw method so the canonical wrap surfaces\n // UnsupportedMethodError instead of silently rewriting to GET.\n const rawMethod = (init?.method ?? 'GET') as HttpMethod;\n const headers = init?.headers as Record<string, string> | undefined;\n const bodyValue = init?.body;\n const body: string | undefined = typeof bodyValue === 'string' ? bodyValue : undefined;\n const fetchImpl: typeof fetch = opts.fetchImpl ?? globalThis.fetch;\n\n const innerOpts: FetchOutboundOptions = headers\n ? body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, headers, body }\n : { method: rawMethod, purpose: opts.purpose, headers }\n : body !== undefined\n ? { method: rawMethod, purpose: opts.purpose, body }\n : { method: rawMethod, purpose: opts.purpose };\n\n let storedResponse: Response | null = null;\n const inner: FetchOutbound = async (innerUrl, innerOptsArg) => {\n const t0 = Date.now();\n const reqInit: RequestInit = { method: innerOptsArg.method };\n if (innerOptsArg.headers) reqInit.headers = { ...innerOptsArg.headers };\n if (innerOptsArg.body !== undefined) reqInit.body = innerOptsArg.body;\n const response = await fetchImpl(innerUrl, reqInit);\n storedResponse = response;\n const buf = await response.clone().arrayBuffer();\n return {\n status: response.status,\n bytes: new Uint8Array(buf),\n durationMs: Date.now() - t0,\n };\n };\n\n const wrapped = wrapFetchOutbound(inner, opts.audit, { denyHosts: opts.denyHosts });\n await wrapped(url, innerOpts);\n return storedResponse!;\n}\n"]}
@@ -17,9 +17,9 @@ type FetchOutbound = (url: string, opts: FetchOutboundOptions) => Promise<FetchO
17
17
  interface HttpCallRecord {
18
18
  readonly url: string;
19
19
  readonly method: HttpMethod;
20
- readonly status: number;
20
+ readonly status: number | null;
21
21
  readonly bytes: number;
22
- readonly duration_ms: number;
22
+ readonly durationMs: number;
23
23
  readonly purpose: HttpPurpose;
24
24
  }
25
25
  interface RetryConfig {
@@ -36,6 +36,10 @@ declare class DenyHostError extends Error {
36
36
  readonly url: string;
37
37
  constructor(host: string, url: string);
38
38
  }
39
+ /** Whether `e` is a deny-host refusal (`SERVICE_INDEPENDENCE_VIOLATION`). */
40
+ declare function isDenyHostError(e: unknown): e is DenyHostError;
41
+ /** Whether `e` is a body-cap abort (`OUTBOUND_BODY_TOO_LARGE`). */
42
+ declare function isBodyTooLargeError(e: unknown): e is BodyTooLargeError;
39
43
  declare class UnsupportedProtocolError extends Error {
40
44
  readonly code = "UNSUPPORTED_PROTOCOL";
41
45
  readonly protocol: string;
@@ -73,4 +77,4 @@ declare const defaultFetchOutbound: FetchOutbound;
73
77
  declare function wrapFetchOutbound(inner: FetchOutbound, audit: HttpCallRecord[], config?: WrapFetchOutboundConfig | ReadonlyArray<string> | undefined): FetchOutbound;
74
78
  declare function fetchOutbound(url: string, opts: FetchOutboundOptions, audit: HttpCallRecord[], config?: WrapFetchOutboundConfig): Promise<FetchOutboundResult>;
75
79
 
76
- export { BodyTooLargeError as B, DEFAULT_OUTBOUND_MAX_BYTES as D, type FetchOutbound as F, type HttpCallRecord as H, OutboundExhaustedError as O, type RetryConfig as R, UnsupportedMethodError as U, type WrapFetchOutboundConfig as W, DENY_HOSTS_DEFAULT as a, DenyHostError as b, type FetchOutboundOptions as c, type FetchOutboundResult as d, type HttpMethod as e, type HttpPurpose as f, UnsupportedProtocolError as g, defaultFetchOutbound as h, fetchOutbound as i, matchesDenyList as m, wrapFetchOutbound as w };
80
+ export { BodyTooLargeError as B, DEFAULT_OUTBOUND_MAX_BYTES as D, type FetchOutbound as F, type HttpCallRecord as H, OutboundExhaustedError as O, type RetryConfig as R, UnsupportedMethodError as U, type WrapFetchOutboundConfig as W, DENY_HOSTS_DEFAULT as a, DenyHostError as b, type FetchOutboundOptions as c, type FetchOutboundResult as d, type HttpMethod as e, type HttpPurpose as f, UnsupportedProtocolError as g, defaultFetchOutbound as h, fetchOutbound as i, isBodyTooLargeError as j, isDenyHostError as k, matchesDenyList as m, wrapFetchOutbound as w };
@@ -17,9 +17,9 @@ type FetchOutbound = (url: string, opts: FetchOutboundOptions) => Promise<FetchO
17
17
  interface HttpCallRecord {
18
18
  readonly url: string;
19
19
  readonly method: HttpMethod;
20
- readonly status: number;
20
+ readonly status: number | null;
21
21
  readonly bytes: number;
22
- readonly duration_ms: number;
22
+ readonly durationMs: number;
23
23
  readonly purpose: HttpPurpose;
24
24
  }
25
25
  interface RetryConfig {
@@ -36,6 +36,10 @@ declare class DenyHostError extends Error {
36
36
  readonly url: string;
37
37
  constructor(host: string, url: string);
38
38
  }
39
+ /** Whether `e` is a deny-host refusal (`SERVICE_INDEPENDENCE_VIOLATION`). */
40
+ declare function isDenyHostError(e: unknown): e is DenyHostError;
41
+ /** Whether `e` is a body-cap abort (`OUTBOUND_BODY_TOO_LARGE`). */
42
+ declare function isBodyTooLargeError(e: unknown): e is BodyTooLargeError;
39
43
  declare class UnsupportedProtocolError extends Error {
40
44
  readonly code = "UNSUPPORTED_PROTOCOL";
41
45
  readonly protocol: string;
@@ -73,4 +77,4 @@ declare const defaultFetchOutbound: FetchOutbound;
73
77
  declare function wrapFetchOutbound(inner: FetchOutbound, audit: HttpCallRecord[], config?: WrapFetchOutboundConfig | ReadonlyArray<string> | undefined): FetchOutbound;
74
78
  declare function fetchOutbound(url: string, opts: FetchOutboundOptions, audit: HttpCallRecord[], config?: WrapFetchOutboundConfig): Promise<FetchOutboundResult>;
75
79
 
76
- export { BodyTooLargeError as B, DEFAULT_OUTBOUND_MAX_BYTES as D, type FetchOutbound as F, type HttpCallRecord as H, OutboundExhaustedError as O, type RetryConfig as R, UnsupportedMethodError as U, type WrapFetchOutboundConfig as W, DENY_HOSTS_DEFAULT as a, DenyHostError as b, type FetchOutboundOptions as c, type FetchOutboundResult as d, type HttpMethod as e, type HttpPurpose as f, UnsupportedProtocolError as g, defaultFetchOutbound as h, fetchOutbound as i, matchesDenyList as m, wrapFetchOutbound as w };
80
+ export { BodyTooLargeError as B, DEFAULT_OUTBOUND_MAX_BYTES as D, type FetchOutbound as F, type HttpCallRecord as H, OutboundExhaustedError as O, type RetryConfig as R, UnsupportedMethodError as U, type WrapFetchOutboundConfig as W, DENY_HOSTS_DEFAULT as a, DenyHostError as b, type FetchOutboundOptions as c, type FetchOutboundResult as d, type HttpMethod as e, type HttpPurpose as f, UnsupportedProtocolError as g, defaultFetchOutbound as h, fetchOutbound as i, isBodyTooLargeError as j, isDenyHostError as k, matchesDenyList as m, wrapFetchOutbound as w };