# bunny-cdn-adapter.helpers ## Purpose Internal helpers used by `BunnyCdnAdapter` and its sibling streams module. Splits boundary concerns (option parsing, header resolution, abort guard, response-shape interpretation, outcome classification) out of the public class so the latter stays under the 300-line budget and the helpers can be unit-tested in isolation. ## Features - `resolveConfig(options)` — parses options through `BunnyCdnAdapterOptionsSchema`; throws `STORAGE_CDN_BUNNYCDN.INVALID_REGION` for region typos and `INVALID_OPTIONS` for every other validation failure. - `resolveHeaders(resolver, key)` — static record or async per-op callback; defensive copy on return. - `throwIfAborted(signal)` — pre-fetch abort guard producing `STORAGE_CDN_BUNNYCDN.UPSTREAM_ERROR` (with `cause = signal.reason`). - `outcomeFromError(err)` — translates any thrown error to the `outcome` attribute the OTel wrapper records. - `parseMetadata(response)` — extracts `StorageMetadata` from a HEAD response. - `consumeHead(response, zone, region, success)` — processes a HEAD response: 404 → `null`, 401/403 → `STORAGE_AUTH_FAILED`, other non-ok → `UPSTREAM_ERROR`, 2xx → `success(response)`. ## Usage: Basic ```ts import { resolveConfig, parseMetadata } from "@suaveplan/storage-cdn-bunnycdn"; const config = resolveConfig({ storageZone: "myz", storageAccessKey: "ak", pullZoneHostname: "mycdn.b-cdn.net", }); ``` ## Usage: Advanced ```ts import { consumeHead, outcomeFromError, resolveHeaders, throwIfAborted, } from "@suaveplan/storage-cdn-bunnycdn"; async function customExists(ctx, key, signal) { throwIfAborted(signal); const headers = await resolveHeaders(ctx.options.headers, key); const response = await ctx.fetch(buildUrl(ctx, key), { method: "HEAD", headers, signal }); try { return consumeHead(response, ctx.zone, ctx.region, () => true) ?? false; } catch (err) { recordOp({ op: "exists", outcome: outcomeFromError(err), durationMs: 0, storageRegion: ctx.region }); throw err; } } ``` ## API Reference ### `interface ResolvedConfig` `{ options, storageZone, accessKey, pullZoneHostname, region, tokenAuthKey, purgeApiKey, fetch, now }`. ### `resolveConfig(options): ResolvedConfig` Routes region-specific schema failures to `INVALID_REGION`; all others to `INVALID_OPTIONS`. ### `resolveHeaders(resolver, key): Promise>` - `undefined` → `{}`. - Static record → shallow copy. - Function → awaited, then shallow-copied. ### `throwIfAborted(signal?): void` Throws when `signal?.aborted === true`. ### `outcomeFromError(err): "auth_failed" | "not_found" | "upstream_error"` `StorageCdnBunnycdnError` → maps by `code`; anything else → `"upstream_error"`. ### `parseMetadata(response): StorageMetadata` Extracts `Content-Length` (defaulting `size` to 0 when absent or unparsed) and `Last-Modified` (defaulting `modifiedAt` to `Date.now()` when missing or unparseable). Returns `createdAt === modifiedAt`. ### `consumeHead(response, zone, region, success): T | null` - `404` → `null`. - `401`/`403` → `STORAGE_CDN_BUNNYCDN.STORAGE_AUTH_FAILED`. - other non-ok → `STORAGE_CDN_BUNNYCDN.UPSTREAM_ERROR`. - `2xx` → `success(response)`. ## Implementation Notes - The error-status table maps `401` and `403` both to `STORAGE_AUTH_FAILED` because BunnyCDN returns the same access-denied semantics for both — distinguishing them at the adapter would force every caller to handle two paths with identical recovery. - `outcomeFromError` returns a narrower enum than the OTel attribute set because the adapter's `wrap` augments the outcome with `"ok"` on the success path. Adding a new error code requires extending the switch. - `resolveHeaders` defensively copies its inputs so a caller mutating their static record between calls doesn't leak into in-flight requests. - `resolveConfig` reads the first issue to detect a region-specific failure; only the dedicated `INVALID_REGION` code makes it to operators on that single failure mode.