The session lifecycle and request pipeline shared by the SDK API clients: persisted credentials, the login-backoff gate, the logOut-epoch protocol, single-flight session refresh, the resilience pipeline around every request, and the sync-cycle template that keeps the registry and the auto-sync timer honest.
Thin fetch-based HTTP client used internally by the consuming SDKs.
Thrown by HttpClient whenever an upstream response has a non-2xx
status. The shape mirrors what downstream code needs: response.status,
response.headers, and response.data.
Construction options for HttpClient.
Snapshot of the request that triggered an HttpError. Any
value naming a secret — header, body field or query parameter —
reads as ******.
Configuration accepted by HttpClient.request: body (data),
query (params), per-request headers, method, abort signal and URL.
Minimal response shape surfaced to callers.
Type guard for HTTP errors thrown by the internal HTTP client.
Reads a fetch Headers object into a plain record.
Callback bundle invoked around SDK lifecycle moments. All callbacks are optional and non-throwing — the SDK ignores any exceptions they raise so a buggy observer cannot break the request flow.
Logger interface for API call tracing.
Emitted when a request (possibly after retries) completes successfully.
Emitted when a request ultimately fails after exhausting its retries.
Identifies a single logical request across its lifecycle events.
Generated client-side via crypto.randomUUID() when each request
starts, so consumers can correlate a onRequestStart with its
eventual onRequestComplete or onRequestError — including across
retry attempts, which share the same correlationId.
Emitted each time a retry attempt is scheduled.
User-facing configuration every session-bearing SDK client accepts. A consuming SDK extends it with its own surface (credentials, locale, timezone, transport options, protocol switches).
Subclass-internal options injected into the SessionAPI constructor. Distinct from SessionAPIConfig (the user-facing surface) — these capture what the subclass knows that the user doesn't pick: the built transport, the sync cadence default, the sync runner closure, and the protocol's rate-limit / auth-failure vocabulary.
External storage adapter for persisting API session settings.
Emitted at the start of a request, before any retry attempts.
Callback invoked after sync operations, with consumer-defined scoping parameters (device ids, type filters…).
Base class for all errors thrown by the SDK family. Each consuming SDK's protocol errors extend this one class, so a host can catch the whole hierarchy through isAPIError whatever wire it talks to.
The server rejected the credentials, the login response could not be parsed, or the reactive re-authentication that followed an auth-failure status failed in turn.
The upstream is temporarily refusing SIGN-INS — a login throttle, not a rejected password. Retrying a login keeps the lockout alive, so the automatic re-login backoff widens when this error arms it; sessions established BEFORE the throttle keep working.
Upstream returned HTTP 429 (Too Many Requests), or the local rate-limit gate is still holding a pause window from a previous 429.
The sign-in round-trip was ACCEPTED but the enforced post-auth
registry sync failed: the session is established and the credentials
persisted, yet the registry could not be verified against the
server. Thrown by authenticate() with the sync's own failure (a
validation rejection, a transport error, any registry error)
preserved as cause, so consumers can tell "signed in, stale list"
from a refused credential BY TYPE instead of re-deriving the verdict
from isAuthenticated() — a discriminator with a real false
positive: a transport failure during a sign-in over a PRE-EXISTING
live session (a user switching accounts) reads "signed in" while the
new pair was never accepted. A refused credential is never wrapped
in this type: it stays AuthenticationError.
Thrown when a runtime validator rejects an upstream payload. The
consuming SDKs construct it at their zod boundaries (parseOrThrow,
which stays in each SDK); the class itself names no validator — the
validator's own error, typically a ZodError, rides the standard
cause chain as unknown. The context field surfaces which
boundary the payload came from (an endpoint or flow label such as
'login' or 'BFF /context') so consumer dashboards can group drift
alerts without parsing the message string.
User-defined type guard for APIError and its subclasses.
Accessor decorator that delegates storage to an external SettingManager
(e.g. persistent settings), falling back to the in-memory field when none
is configured. The setting key is resolved once at decoration time rather
than on every get/set.
Method decorator factory that invokes the host's sync notification
after the decorated method resolves, forwarding params verbatim.
Generic over the consumer's sync-params shape (device ids, a type
filter…) like SessionAPI itself: @syncDevices({ type }) forwards
a payload, @syncDevices() notifies without one. The host contract
is structural: the returned method's this names it, which types the
body and any .call(host) use, but a TC39 application site does not
check it — the method's own type carries no this — so it is
documented, not enforced, as it was in the SDKs' own copies. No
action is taken when the host exposes no hook.
The username/password pair a session mechanism signs in with. Every consuming SDK's login takes exactly this shape; a protocol that also posts it verbatim as its login body (Gizwits does) keeps that fact — and any extra wire field — in its own types.
Fully-resolved counterpart of an undefined-tolerant input shape:
every property present and defined (defaults applied). Under
exactOptionalPropertyTypes, Required<T> removes ? but keeps an
explicit | undefined in the property type; this also strips it.
null is preserved — in this domain it is a sentinel, not an
absence marker.
Optional form of T whose properties may also be explicitly
undefined — the input-side counterpart of Partial<T> under
exactOptionalPropertyTypes (whose mapped ? does not admit a
present-undefined key). For inputs whose runtime treats a
present-undefined key exactly like an absent one.
Abstract base for API call logging data, serializable to JSON with a
fixed set of log keys. Serialization redacts through the injected
Redaction — the same vocabulary the HttpError snapshot
uses, so a secret cannot reach a log through either route.
Structured log data for an outgoing API request.
Structured log data for an API response.
Reactive authentication retry. On an auth-failure status:
true if the session was successfully refreshed (token
exchange or full resumeSession) and false if it failed.Compose N policies into a single pipeline. The first policy in the array is the outermost wrapper — it sees the request before any inner policy gets to decorate it, and sees the result last.
Disposable wrapper around setTimeout for internal background
bookkeeping (e.g. the auto-sync cadence). Auto-clears the previous
timeout when rescheduled and unrefs the underlying handle so a
scheduled callback never keeps the Node event loop alive on its
own — callers are still notified on the regular loop, but a script
that has nothing left to do can exit immediately.
Thin wrapper around a LifecycleEvents bundle that swallows any exceptions raised by consumer callbacks and logs them at error level. A misbehaving observer must never be able to break the request or sync flow — observability is a side concern, never a blocker.
Tracks an upstream rate-limit window and lets callers check whether the gate is currently closed.
Rate-limit circuit breaker. Two responsibilities:
Retry-After header and surface a diagnostic
log before re-raising. Subsequent callers see the paused state
and refuse immediately (see point 1).One-shot retry budget limiter.
Manages periodic auto-sync with a configurable interval. Drives the consuming clients' periodic registry refresh.
Exponential-backoff retry for transient server-side failures (502, 503, 504 — see isTransientServerError). Wraps the attempt with withRetryBackoff using the caller-provided telemetry hook for every retry tick.
Log data extended with the error message from a failed API call.
Minimal structural shape required by the API call loggers.
Subset of Temporal.Duration field values accepted by
RateLimitGate's fallback configuration. Matches what callers
actually need to express a rate-limit pause window.
The redaction engine bound to one sensitive-key vocabulary — the ONE
surface shared by the call loggers and the HttpError snapshot, so a
secret cannot reach a log through either route. Built by
createRedaction; each consumer builds exactly one, seeded
with its wire's credential keys.
Unit of cross-cutting resilience logic around a request attempt.
Options for withRetryBackoff.
Callback surface for per-retry instrumentation.
The credential keys every consumer redacts, whatever wire it speaks: the generic HTTP carriers plus the account pair every login flow posts. This is the BASE a protocol vocabulary extends through createRedaction — it is deliberately the intersection of the consuming SDKs' vocabularies, so adopting the core can only ever redact MORE, never less.
The engine bound to BASE_SENSITIVE_KEYS alone — the default every redaction seat falls back to when no vocabulary is injected. A consumer SDK should build its own via createRedaction and thread it through every seat; this default guarantees the generic carriers are covered even where it forgets to.
Default transient-retry budget shared by every consuming client. Keeping these in one place prevents drift: if we decide to tune the upper bound or jitter ratio, we update a single constant instead of one per consumer.
HTTP status codes used across the SDK family. Single source so
callers don't redefine them per file (HTTP_STATUS_UNAUTHORIZED was
declared in three places before this module existed).
Number of milliseconds in one day.
Number of milliseconds in one minute.
Number of milliseconds in one second.
Placeholder written over any value whose key names a secret.
Forward window applied by the consumers' session-refresh hooks: trigger the session refresh when the persisted token is within this many ms of its real expiry, so no request pays the full re-auth round-trip on its critical path.
Create structured error log data from a failed HTTP request. Uses response data when the error carries one, otherwise falls back to request-only data.
Builds the Redaction engine for one protocol vocabulary. The mechanism is owned here; the vocabulary is the caller's — the consuming SDK passes every key that names a credential on ITS wire, and the engine unions them with BASE_SENSITIVE_KEYS.
The one sanctioned fire-and-forget seam: detach already-started work from the caller's critical path, logging a rejection instead of propagating it.
Render a Temporal.Duration in English diagnostic form, with
adaptive units: sub-minute windows render as seconds (e.g.
"20 seconds") so a short Retry-After never rounds to a misleading
"0 minutes"; longer windows render as "M minutes, S seconds".
Check whether an ISO 8601 expiry timestamp has passed or is malformed.
Predicate suitable for RetryBackoffOptions.isRetryable: returns
true for transient HTTP 5xx status codes (502 / 503 / 504) including
errors wrapped via Error.cause. All other inputs return false.
Run operation, retrying on errors accepted by options.isRetryable.
The mechanism the SDK family shares: the session lifecycle and request pipeline, the redaction-seated transport, the observability shells, the resilience primitives, the error hierarchy and the two decorators — everything a consuming SDK's PRODUCTION code reaches. The vitest-backed test helpers live on their own
./testingsubpath and are never re-exported from here.