4.5 KiB
HttpBackend
(no submodules)
Purpose
HttpBackend is the canonical browser-tier translation source for the
@suaveplan/i18n family. It fetches <loadPath> per (locale, namespace) via an injectable fetchFn (defaulting to
globalThis.fetch) and parses the response body via an injectable
parsePayload (defaulting to a JSON-object parser that rejects arrays,
null, and primitive payloads). HTTP 404 maps to
BACKEND_LOCALE_NOT_AVAILABLE — the chained-backend fallthrough signal
— while HTTP 5xx, network failures, and parse failures map to
BACKEND_READ_FAILED, the chain-stopping signal that prevents silent
degradation when the transport itself is broken.
The backend is read-only by design (canSave: false,
canTrackModification: false, canWatch: false). Consumers needing
write-back wire MultiFileFsBackend or StorageBackend behind the
catalog instead. Universal — runs identically in browsers, Bun,
and Node.js >= 18; older Node runtimes get a clear
BACKEND_READ_FAILED from defaultFetch() instructing them to inject
fetchFn.
Features
- 5-flag capability matrix
{ canRead: true, canReadMulti: false, canSave: false, canTrackModification: false, canWatch: false }. - URL templating via
{locale}/{namespace}placeholders withencodeURIComponentper segment. - Pluggable
parsePayloadaccepts non-JSON bodies (YAML, MessagePack, schema-validated JSON). - Pluggable
customHeadersbuilder per request. crossDomain: trueshorthand forcredentials: "include"+mode: "cors"(preserves explicit overrides inrequestOptions).- Optional
retry?: HttpBackendRetry(structurally compatible with@suaveplan/retry'sRetryPolicy) wraps every fetch. - Optional
signal?: AbortSignal— pre-aborted signals throwBACKEND_TIMEOUTwithout issuing a network request; mid-flight AbortError surfaces asBACKEND_TIMEOUT; the signal is threaded into the underlyingRequestInitsofetchcan cancel mid-flight. - Zod boundary validation throws
INVALID_OPTIONSat construction. - Per-call OTel instrumentation under
i18n.backend.http.*.
Usage: Basic Example
import { HttpBackend } from "@suaveplan/i18n-backend-http";
const backend = new HttpBackend({
loadPath: "/i18n/{locale}/{namespace}.json",
});
const record = await backend.read("en", "auth");
Usage: Advanced Example
import { HttpBackend } from "@suaveplan/i18n-backend-http";
const backend = new HttpBackend({
loadPath: "/i18n/{locale}/{namespace}.yaml",
crossDomain: true,
customHeaders: (locale) => ({ "x-locale": locale }),
parsePayload: async (res) => parseYaml(await res.text()),
retry: {
run: async (op) => {
for (let i = 0; i < 3; i++) {
try {
return await op();
} catch (err) {
if (i === 2) throw err;
}
}
throw new Error("unreachable");
},
},
});
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 2_000);
const record = await backend.read("en", "auth", ctrl.signal);
API Reference
class HttpBackend implements Backend
new HttpBackend(options: HttpBackendOptions)— Zod-validated; throwsI18nError(I18N.INVALID_OPTIONS)on malformed input.read(locale, namespace, signal?): Promise<TranslationRecord>— fetches and parses the URL. ThrowsBACKEND_LOCALE_NOT_AVAILABLEon 404;BACKEND_READ_FAILEDon 5xx / network / parse failures;BACKEND_TIMEOUTon signal abort.
Helpers
buildUrl(loadPath, locale, namespace): string— substitutes{locale}and{namespace}withencodeURIComponent-applied values.defaultParsePayload: ParsePayloadFn— JSON object parser. Rejects arrays, null, and primitives asBACKEND_READ_FAILED.HttpBackendOptionsSchema: z.ZodObject— Zod boundary schema.
Implementation Notes
The class delegates to a private #fetchOnce helper that classifies
network failures: AbortError / ABORT_ERR codes surface as
BACKEND_TIMEOUT (so consumers can distinguish cancellation from
transport faults), and I18nError(I18N.BACKEND_TIMEOUT) thrown from
inside fetchFn (e.g. an injected fake) propagates unchanged.
Everything else from the network surface — TypeError, refused
connections, DNS failures, CORS preflights — maps to
BACKEND_READ_FAILED. Capability flags stay false for write paths
because the HTTP transport has no canonical PUT semantics across
CDNs; consumers needing write-back wire a different backend.