Pre-configured upstream HTTP client (typically from
createUpstreamHttpClient). Should be configured with
auth: { kind: 'dynamic_bearer', getToken: (ctx) => ... } so the
factory's getAuthContext output flows through to bearer selection.
Path on the upstream API that returns the buyer's accounts. Common
shapes: /v1/adaccounts, /me/adaccounts, /customers.
OptionalidProperty on each upstream row matching the wire AccountReference.account_id.
Defaults to 'id'.
Footgun: when the generic TUpstreamRow is inferred (no explicit
type argument), keyof TUpstreamRow & string collapses to string and
a typo in idField compiles fine but silently always returns null.
Pass an explicit TUpstreamRow (or a dummy interface with the upstream's
field names) to get compile-time field validation.
OptionalrowsProperty on the upstream response body that contains the array of rows.
Defaults to 'data' (Snap, Meta envelope shape). Set to null when the
response body itself is the array (some flat-list APIs).
Single-segment only. APIs with deeper nesting (TikTok's data.list,
for example) need a custom response transform — wrap the upstream client
with a fetch override that flattens, or use this factory only for the
70%-fit upstream shape and write a hand-rolled resolver for the rest.
Google Ads' customers:listAccessibleCustomers returns string resource
names (not row objects) and is not a fit for this factory.
OptionalgetExtract the auth context to forward to the upstream's
dynamic_bearer.getToken(ctx) resolver. The return value flows through
as the authContext per-call option on httpClient.get.
Defaults to forwarding ctx?.authInfo verbatim — works when the http
client's getToken resolver reads ctx.authInfo.token /
ctx.authInfo.credential.token.
Map an upstream row to a framework Account<TCtxMeta>. Adapters
typically embed the raw row (or distilled fields) in ctx_metadata so
downstream specialism methods can read upstream IDs, tokens, etc.
without re-resolving.
Treat ctx_metadata.accessToken (or any embedded credential) as a
secret. The framework strips ctx_metadata from the wire response,
but adopter code that throws an error containing JSON.stringify(account)
or logs ctx.account at info level WILL leak the token. Either don't
embed the bearer (re-derive from ctx.authInfo on each downstream
method), or audit your error projections.
OptionalcacheOptional in-memory listing cache. Caches the full row array per buyer
(keyed on the auth context); resolves account_id matches in-memory
within the TTL window. One upstream hit per buyer per TTL period
regardless of how many account_ids that buyer queries.
Set ttlMs to enable (default: no cache, every resolve hits upstream).
TTL guidance:
get_products, list_creative_formats): 60–300s.
Buyer-side latency dominates; longer TTL absorbs bursts.create_media_buy, update_media_buy,
sync_creatives): 0–30s, or compose composeMethod to skip cache
on these paths. A revoked-but-not-yet-expired bearer would otherwise
continue authorizing mutations for the full TTL window.getCacheKey defaults to a SHA-256 hash of the JSON-serialized auth
context. Hashing (vs raw stringification) keeps the bearer token out
of Map keys — heap dumps and util.inspect(cache) no longer surface
plaintext credentials. Provide a custom key when the auth context
contains noise (timestamps, request ids) that would defeat caching.
Cache size is naturally bounded by the number of distinct buyers
times one entry each. Adopters with token-rotation churn can cap with
maxEntries (LRU eviction).
OptionalgetCacheKey?: (authContext: AuthContext | undefined) => stringOptionalmaxEntries?: numberLRU eviction cap. Defaults to 1024 — enough for most multi-tenant deployments.
Options for createOAuthPassthroughResolver.