4.0 KiB
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 throughBunnyCdnAdapterOptionsSchema; throwsSTORAGE_CDN_BUNNYCDN.INVALID_REGIONfor region typos andINVALID_OPTIONSfor every other validation failure.resolveHeaders(resolver, key)— static record or async per-op callback; defensive copy on return.throwIfAborted(signal)— pre-fetch abort guard producingSTORAGE_CDN_BUNNYCDN.UPSTREAM_ERROR(withcause = signal.reason).outcomeFromError(err)— translates any thrown error to theoutcomeattribute the OTel wrapper records.parseMetadata(response)— extractsStorageMetadatafrom 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
import { resolveConfig, parseMetadata } from "@suaveplan/storage-cdn-bunnycdn";
const config = resolveConfig({
storageZone: "myz",
storageAccessKey: "ak",
pullZoneHostname: "mycdn.b-cdn.net",
});
Usage: Advanced
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<Record<string, string>>
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<T>(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
401and403both toSTORAGE_AUTH_FAILEDbecause 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. outcomeFromErrorreturns a narrower enum than the OTel attribute set because the adapter'swrapaugments the outcome with"ok"on the success path. Adding a new error code requires extending the switch.resolveHeadersdefensively copies its inputs so a caller mutating their static record between calls doesn't leak into in-flight requests.resolveConfigreads the first issue to detect a region-specific failure; only the dedicatedINVALID_REGIONcode makes it to operators on that single failure mode.