@coinlist-co/react 0.12.2-rc.3de3751 → 0.12.2-rc.4cc3d7e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/core/api/api-client.ts","../../src/server/core/session-refresh.ts","../../src/server/shared/errors.ts","../../src/server/core/server-auth-namespace-impl.ts","../../src/server/core/server-offers-namespace.ts","../../src/server/core/server-requirements-namespace.ts","../../src/server/core/coinlist-server.ts","../../src/server/core/observability/pino-server-logger.ts","../../src/server/core/server-auth-namespace.ts"],"sourcesContent":["import { AuthenticatedApiClient } from '@/universal/api/authenticated-api-client';\nimport type { HttpClientConfig, HttpRequest } from '@/universal/api/http';\nimport type { InternalAccessTokenSource } from '@/universal/api/internal-access-token-source';\nimport type { Logger } from '@/universal/types/logger';\n\nexport class ApiClient {\n private readonly client: AuthenticatedApiClient;\n\n constructor(\n config: HttpClientConfig,\n fetchAccessToken: InternalAccessTokenSource,\n logger: Logger | null = null\n ) {\n this.client = new AuthenticatedApiClient(\n config,\n fetchAccessToken,\n [],\n logger\n );\n }\n\n async send<TResponse>(request: HttpRequest): Promise<TResponse> {\n return this.client.send<TResponse>(request);\n }\n}\n","import { systemTime, type TimeEff } from '@/universal/core/shared/effects';\nimport type { Newtype } from '@/universal/types/newtype';\nimport type { ClientId } from '@/universal/types/oauth';\nimport type { OAuthSession } from '@/universal/types/oauth-session';\nimport { EpochMillis, Millis } from '@/universal/types/time';\n\n/**\n * A second rejection of the same refresh token counts as the verdict only this\n * long after the first. A request sent just before the winning refresh's cookie\n * landed can still present the old token a moment later, and a verdict clears\n * the store - a clearing cookie that could overwrite the winner's - so this is\n * deliberately wider than any in-flight request should take.\n */\nexport const VERDICT_MIN_GAP: Millis = Millis.of(5_000);\n\n/**\n * How long a rejection is remembered. A rejection older than this counts as\n * the first again, which can only delay a verdict, never make a false one.\n */\nexport const REJECTION_MEMORY: Millis = Millis.of(60_000);\n\n/**\n * Identifies one session for coordination: the refresh token when it has one,\n * the access token when it does not (there is then nothing to exchange, but\n * the verdict still has to be reached once rather than once per request).\n */\nexport type RefreshKey = Newtype<string, 'RefreshKey'>;\n\nexport const RefreshKey = {\n of: ({\n clientId,\n session,\n }: {\n clientId: ClientId;\n session: OAuthSession;\n }): RefreshKey => {\n const identity =\n session.refreshToken === undefined\n ? ['access', session.accessToken.value]\n : ['refresh', session.refreshToken];\n // JSON quotes and escapes each part, so no client id and token can be\n // split two ways into the same key.\n return JSON.stringify([clientId, ...identity]) as RefreshKey;\n },\n};\n\n/** What one `POST /oauth/token` exchange answered, before it is judged. */\nexport type ExchangeResult =\n | { type: 'renewed'; session: OAuthSession }\n /** `400 invalid_grant`: rotated by a concurrent refresh, expired or revoked. */\n | { type: 'rejected' }\n /** The session carries no refresh token, so there is nothing to exchange. */\n | { type: 'unrenewable' }\n /**\n * 5xx, network failure, `invalid_client`, unparsable body: no answer at all,\n * so whether the session is alive is unknown.\n */\n | { type: 'unknown'; error: unknown };\n\n/**\n * What one exchange came to, judged once for every caller that joined it.\n *\n * - `suspected` - a first rejection, or a repeat inside {@link VERDICT_MIN_GAP}.\n * Write nothing: the rejection may be a race another request won.\n * - `expired` - the verdict. The caller that led the exchange clears its\n * store and notifies the host; the ones that joined only clear.\n * - `already-expired` - the verdict was reached earlier; no exchange was made.\n * Clear, and notify nobody.\n */\nexport type RefreshOutcome =\n | { type: 'renewed'; session: OAuthSession }\n | { type: 'suspected' }\n | { type: 'expired' }\n | { type: 'already-expired' }\n | { type: 'unknown'; error: unknown };\n\n/** What the coordinator remembers about a session it has seen rejected. */\nexport type Rejection =\n | { type: 'suspected'; since: EpochMillis }\n | { type: 'expired'; at: EpochMillis };\n\n/**\n * Deduplicates refresh-token exchanges and remembers rejections across every\n * `CoinListServer` in the process.\n *\n * **Process-wide on purpose.** Hosts build a `CoinListServer` per request,\n * bound to that request's cookies, so a per-instance slot would deduplicate\n * nothing: the herd this exists for is N concurrent requests carrying one\n * refresh token, each with its own server. It is the one sanctioned exception\n * to the SDK's \"no global mutable state\" rule (see ADR-12). An in-flight entry\n * is deleted when it settles. A rejection stops counting after\n * {@link REJECTION_MEMORY} and is swept out at the start of the next\n * refresh of any session, so an idle process keeps at most its last minute of\n * rejections - one small entry per dead refresh token - until traffic resumes.\n *\n * Per JavaScript module instance, not per machine: Next.js middleware and\n * route handlers can each hold their own, and separate processes never share\n * one. Losers across that boundary are what the rejection memory is for.\n *\n * Only the exchange is shared. Each caller still writes the outcome to its own\n * `SessionStore`, because each request has its own cookie jar.\n */\nexport class RefreshCoordinator {\n private readonly inFlight = new Map<RefreshKey, Promise<RefreshOutcome>>();\n private readonly rejections = new Map<RefreshKey, Rejection>();\n\n constructor(private readonly env: TimeEff = systemTime) {}\n\n /**\n * Runs `exchange` for `key` unless one is already in flight, in which case\n * the caller joins it. `joined` tells the caller whether it led: only the\n * leader reports the outcome, so a burst of N produces one verdict, one log\n * line and one `onSessionExpired`.\n */\n refresh(\n key: RefreshKey,\n exchange: () => Promise<ExchangeResult>\n ): { outcome: Promise<RefreshOutcome>; joined: boolean } {\n const existing = this.inFlight.get(key);\n if (existing !== undefined) {\n return { outcome: existing, joined: true };\n }\n const outcome = this.run(key, exchange).finally(() => {\n this.inFlight.delete(key);\n });\n this.inFlight.set(key, outcome);\n return { outcome, joined: false };\n }\n\n private async run(\n key: RefreshKey,\n exchange: () => Promise<ExchangeResult>\n ): Promise<RefreshOutcome> {\n this.forgetStale(this.env.time.now());\n if (this.rejections.get(key)?.type === 'expired') {\n return { type: 'already-expired' };\n }\n\n const result = await exchange();\n switch (result.type) {\n case 'renewed':\n case 'unknown':\n return result;\n case 'rejected':\n case 'unrenewable': {\n const judged = judge({\n result: result.type,\n prior: this.rejections.get(key),\n now: this.env.time.now(),\n });\n this.rejections.set(key, judged.memory);\n return { type: judged.outcome };\n }\n }\n }\n\n private forgetStale(now: EpochMillis): void {\n for (const [key, rejection] of this.rejections) {\n if (EpochMillis.elapsed(rejectedAt(rejection), now) > REJECTION_MEMORY) {\n this.rejections.delete(key);\n }\n }\n }\n}\n\n/**\n * Turns a rejection into a suspicion or a verdict, given what was remembered\n * about the same session. Pure, so the timing rules can be stated as\n * properties over every gap.\n *\n * `unrenewable` is a verdict at once: with no refresh token there is no race\n * to lose. A `rejected` is a verdict only when a remembered suspicion is at\n * least {@link VERDICT_MIN_GAP} old; the caller has already forgotten any\n * older than {@link REJECTION_MEMORY}.\n */\nexport function judge({\n result,\n prior,\n now,\n}: {\n result: 'rejected' | 'unrenewable';\n prior: Rejection | undefined;\n now: EpochMillis;\n}): { outcome: 'suspected' | 'expired'; memory: Rejection } {\n const verdict = {\n outcome: 'expired',\n memory: { type: 'expired', at: now },\n } as const;\n switch (result) {\n case 'unrenewable':\n return verdict;\n case 'rejected':\n switch (prior?.type) {\n case undefined:\n return {\n outcome: 'suspected',\n memory: { type: 'suspected', since: now },\n };\n case 'suspected':\n return EpochMillis.elapsed(prior.since, now) >= VERDICT_MIN_GAP\n ? verdict\n : { outcome: 'suspected', memory: prior };\n case 'expired':\n return { outcome: 'expired', memory: prior };\n }\n }\n}\n\nfunction rejectedAt(rejection: Rejection): EpochMillis {\n switch (rejection.type) {\n case 'suspected':\n return rejection.since;\n case 'expired':\n return rejection.at;\n }\n}\n\n/** The coordinator every `CoinListServer` in this module instance shares. */\nexport const processRefreshCoordinator = new RefreshCoordinator();\n","export class WritableSessionStoreRequiredError extends Error {\n constructor(\n message = 'This operation requires a writable SessionStore. Provide a `setSession` implementation in your SessionStore to persist or clear OAuth sessions.'\n ) {\n super(message);\n this.name = 'WritableSessionStoreRequiredError';\n }\n}\n","import type {\n CompleteOAuthParams,\n ServerAuthNamespace,\n SessionStore,\n} from '@/server/core/server-auth-namespace';\nimport {\n type ExchangeResult,\n type RefreshCoordinator,\n RefreshKey,\n} from '@/server/core/session-refresh';\nimport { WritableSessionStoreRequiredError } from '@/server/shared/errors';\nimport { HttpError } from '@/universal/api/http';\nimport { Attributes } from '@/universal/api/http-attributes';\nimport type { Sender } from '@/universal/api/http-client';\nimport {\n isRejectedToken,\n TokenTrigger,\n} from '@/universal/api/internal-access-token-source';\nimport { notifySessionExpired } from '@/universal/core/auth/session-expired';\nimport type { TimeEff } from '@/universal/core/shared/effects';\nimport {\n type InternalLogger,\n internalLogger,\n} from '@/universal/core/shared/observability/internal-logger';\nimport type { Config } from '@/universal/types/config';\nimport type { OAuthSessionDto } from '@/universal/types/dto/oauth-session';\nimport type { ClientSecret } from '@/universal/types/oauth';\nimport {\n ClientCredentialsOAuth,\n type OAuthAccessToken,\n type OAuthRefreshToken,\n OAuthSession,\n} from '@/universal/types/oauth-session';\nimport {\n EpochMillis,\n isExpired,\n type Millis,\n Seconds,\n} from '@/universal/types/time';\n\ntype SetSession = (session: OAuthSession | null) => Promise<void>;\n\n/** Whether the host's store can persist a renewed session. */\nexport type SessionStoreAccess =\n | { type: 'read-only' }\n | { type: 'writable'; setSession: SetSession };\n\n/**\n * What {@link ServerAuthNamespaceImpl} does with the stored session, decided\n * before any effect runs. See {@link classifyAccessToken}.\n */\nexport type AccessTokenClassification =\n | { type: 'no-session' }\n | { type: 'serve'; token: OAuthAccessToken }\n /** Past its buffer, but a read-only store cannot renew it. */\n | { type: 'serve-stale'; token: OAuthAccessToken }\n /** A read-only store holding the very token the API just refused. */\n | { type: 'refuse' }\n | {\n type: 'refresh';\n session: OAuthSession;\n setSession: SetSession;\n /** See {@link fallbackToken}. */\n fallback: OAuthAccessToken | null;\n };\n\n/**\n * The effects {@link ServerAuthNamespaceImpl} runs on: the clock, the host's\n * session store, and the process-wide refresh coordinator.\n */\nexport type ServerAuthEnv = TimeEff & {\n readonly sessionStore: SessionStore;\n /** Deduplicates exchanges and remembers rejections; see its TSDoc. */\n readonly refreshes: RefreshCoordinator;\n};\n\n/** What {@link ServerAuthNamespaceImpl} needs out of `ServerConfig`. */\nexport type ServerAuthConfig = Config &\n ServerAuthEnv & {\n readonly clientSecret: ClientSecret;\n /** How long before its expiry an access token counts as expired. */\n readonly accessTokenExpiryBufferSeconds: Seconds;\n /** Whether to rethrow errors the SDK could otherwise swallow (e.g. revoke). */\n readonly strict: boolean;\n };\n\n/**\n * The SDK's {@link ServerAuthNamespace}. Internal: not in the `./server`\n * barrel, because {@link tokenFor} and {@link ServerAuthConfig} are the\n * pipeline's seams rather than API a host calls.\n *\n * @internal\n */\nexport class ServerAuthNamespaceImpl implements ServerAuthNamespace {\n private readonly log: InternalLogger;\n\n constructor(\n private readonly api: Sender,\n private readonly config: ServerAuthConfig\n ) {\n this.log = internalLogger(config.logger ?? null, 'AUTH');\n }\n\n async completeOAuth(params: CompleteOAuthParams): Promise<OAuthSession> {\n return this.log.wrap('completeOAuth', undefined, async () => {\n const setSession = this.writableSessionStore();\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'authorization_code',\n code: params.code,\n redirect_uri: this.config.redirectUri,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n code_verifier: params.codeVerifier,\n },\n attributes: Attributes.notRetryable(),\n });\n const session = OAuthSession.fromDto(sessionDto);\n await setSession(session);\n return session;\n });\n }\n\n async getAccessToken(): Promise<OAuthAccessToken | null> {\n return this.tokenFor(TokenTrigger.beforeRequest);\n }\n\n /**\n * {@link getAccessToken} with the reason the SDK is asking, which is what\n * the authenticated HTTP pipeline calls: after a 401 the token's own expiry\n * no longer matters, because the API has answered for it.\n */\n async tokenFor(trigger: TokenTrigger): Promise<OAuthAccessToken | null> {\n // Wrapped despite running on every request: `sessionStore.getSession` is\n // host code, and when it throws there is nothing else between the failure\n // and the host's own catch to say which SDK call it came out of.\n return this.log.wrap('getAccessToken', undefined, () =>\n this.readAccessToken(trigger)\n );\n }\n\n private async readAccessToken(\n trigger: TokenTrigger\n ): Promise<OAuthAccessToken | null> {\n const sessionStore = this.config.sessionStore;\n const classification = classifyAccessToken({\n session: await sessionStore.getSession(),\n store: sessionStoreAccess(sessionStore),\n trigger,\n now: this.config.time.now(),\n buffer: Seconds.toMillis(this.config.accessTokenExpiryBufferSeconds),\n });\n switch (classification.type) {\n case 'no-session':\n case 'refuse':\n return null;\n case 'serve':\n return classification.token;\n case 'serve-stale':\n this.log.child({ op: 'getAccessToken' }).warn(() => ({\n msg: 'serving an expired token: the session store is read-only, so it cannot be refreshed',\n }));\n return classification.token;\n case 'refresh':\n return this.refresh(classification);\n }\n }\n\n /**\n * Refreshes through the process-wide coordinator, then acts on the outcome\n * for this caller's own store. The store is written before anything is\n * logged or the host is notified, so `onSessionExpired` always finds it\n * cleared.\n */\n private async refresh({\n session,\n setSession,\n fallback,\n }: Extract<\n AccessTokenClassification,\n { type: 'refresh' }\n >): Promise<OAuthAccessToken | null> {\n const log = this.log.child({ op: 'refresh' });\n const key = RefreshKey.of({ clientId: this.config.clientId, session });\n const { outcome, joined } = this.config.refreshes.refresh(key, () =>\n this.exchange(session.refreshToken)\n );\n if (joined) {\n log.debug(() => ({ msg: 'joined a refresh already in flight' }));\n }\n\n const result = await outcome;\n switch (result.type) {\n case 'renewed':\n await setSession(result.session);\n if (!joined) {\n log.info(() => ({ msg: 'session renewed' }));\n }\n return result.session.accessToken;\n case 'suspected':\n // Writing nothing is the point: a clearing cookie from this response\n // could land after the winner's and sign the user out.\n if (!joined) {\n log.warn(() => ({\n msg: 'refresh rejected; not clearing until it is confirmed',\n }));\n }\n return fallback;\n case 'expired':\n await setSession(null);\n if (!joined) {\n log.info(() => ({\n msg: 'session expired',\n fields: {\n reason:\n session.refreshToken === undefined\n ? 'no-refresh-token'\n : 'refresh-rejected',\n },\n }));\n notifySessionExpired(this.config.onSessionExpired, log);\n }\n return null;\n case 'already-expired':\n await setSession(null);\n return null;\n case 'unknown':\n if (fallback === null) {\n throw result.error;\n }\n if (!joined) {\n log.warning(\n () => ({ msg: 'refresh failed; serving the current token' }),\n result.error\n );\n }\n return fallback;\n }\n }\n\n /**\n * One `POST /oauth/token`, total: every failure comes back as a value, so\n * the coordinator can judge it once for everyone who joined.\n *\n * Never retried. The refresh token is single-use, so a retry of an attempt\n * that reached the server would present a token already rotated away, and\n * its `invalid_grant` would be judged as a rejection of the session.\n */\n private async exchange(\n refreshToken: OAuthRefreshToken | undefined\n ): Promise<ExchangeResult> {\n if (refreshToken === undefined) {\n return { type: 'unrenewable' };\n }\n try {\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n },\n attributes: Attributes.notRetryable(),\n });\n return { type: 'renewed', session: OAuthSession.fromDto(sessionDto) };\n } catch (error) {\n return isInvalidGrant(error)\n ? { type: 'rejected' }\n : { type: 'unknown', error };\n }\n }\n\n async clientCredentials(): Promise<ClientCredentialsOAuth> {\n return this.log.wrap('clientCredentials', undefined, async () => {\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'client_credentials',\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n },\n });\n const session = OAuthSession.fromDto(sessionDto);\n return ClientCredentialsOAuth(session.accessToken);\n });\n }\n\n async logout(): Promise<void> {\n return this.log.wrap('logout', undefined, () => this.revokeAndClear());\n }\n\n private async revokeAndClear(): Promise<void> {\n const setSession = this.writableSessionStore();\n const session = await this.config.sessionStore.getSession();\n if (session == null) return;\n\n try {\n await this.api.send({\n method: 'POST',\n url: `/oauth/revoke`,\n body: {\n token: session.accessToken.value,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n },\n });\n } catch (err) {\n // Whatever failed - an HTTP error, the network, anything else - the\n // local session is what logs the user out, so only strict callers hear\n // about a revoke that did not reach CoinList.\n if (this.config.strict) {\n throw err;\n }\n this.log\n .child({ op: 'logout' })\n .warning(\n () => ({ msg: 'revoke failed; local session cleared regardless' }),\n err\n );\n }\n await setSession(null);\n }\n\n /**\n * Returns the store's `setSession`, or throws - the guard every\n * session-writing operation shares.\n */\n private writableSessionStore(): SetSession {\n const store = sessionStoreAccess(this.config.sessionStore);\n switch (store.type) {\n case 'writable':\n return store.setSession;\n case 'read-only':\n throw new WritableSessionStoreRequiredError();\n }\n }\n}\n\n/**\n * Decides what to do with the stored session, before any effect runs. Pure,\n * so every combination of store, trigger and clock is a plain table.\n *\n * - A read-only store never refreshes. After a 401 it answers `null` only for\n * the very token that was just refused, which tells the renewal middleware\n * not to replay with it; a store that has moved on since hands its token\n * back.\n * - A writable store refreshes when the token is inside its buffer, or after\n * a 401 for the token it holds. A session with no refresh token whose token\n * still works is served as it is: nothing to renew with, but nothing lost\n * yet either, so the verdict waits for it to actually expire.\n */\nexport function classifyAccessToken({\n session,\n store,\n trigger,\n now,\n buffer,\n}: {\n session: OAuthSession | null;\n store: SessionStoreAccess;\n trigger: TokenTrigger;\n now: EpochMillis;\n buffer: Millis;\n}): AccessTokenClassification {\n if (session === null) {\n return { type: 'no-session' };\n }\n const token = session.accessToken;\n const stale = isExpired({\n expiresAt: EpochMillis.fromDate(token.expiresAt),\n now,\n buffer,\n });\n switch (store.type) {\n case 'read-only':\n return classifyReadOnly(token, trigger, stale);\n case 'writable': {\n if (!needsRefresh(token, trigger, stale)) {\n return { type: 'serve', token };\n }\n const fallback = fallbackToken(token, trigger, now);\n return session.refreshToken === undefined && fallback !== null\n ? { type: 'serve', token: fallback }\n : {\n type: 'refresh',\n session,\n setSession: store.setSession,\n fallback,\n };\n }\n }\n}\n\nfunction classifyReadOnly(\n token: OAuthAccessToken,\n trigger: TokenTrigger,\n stale: boolean\n): AccessTokenClassification {\n switch (trigger.type) {\n case 'after-401':\n return isRejectedToken(token, trigger.rejected)\n ? { type: 'refuse' }\n : { type: 'serve', token };\n case 'before-request':\n return stale ? { type: 'serve-stale', token } : { type: 'serve', token };\n }\n}\n\nfunction sessionStoreAccess(sessionStore: SessionStore): SessionStoreAccess {\n const setSession = sessionStore.setSession?.bind(sessionStore);\n return setSession === undefined\n ? { type: 'read-only' }\n : { type: 'writable', setSession };\n}\n\nfunction needsRefresh(\n token: OAuthAccessToken,\n trigger: TokenTrigger,\n stale: boolean\n): boolean {\n switch (trigger.type) {\n case 'before-request':\n return stale;\n case 'after-401':\n // The store may already hold a newer token than the refused one - a\n // refresh earlier in this same request - and that one is still good.\n return (\n trigger.rejected.type === 'no-user-token' ||\n isRejectedToken(token, trigger.rejected) ||\n stale\n );\n }\n}\n\n/**\n * The token to serve when a refresh cannot produce a new one: the current\n * one, as long as it has not actually expired - it is inside the early-refresh\n * buffer, not past it - and the API has not refused it. The backend keeps a\n * superseded access token valid until its own expiry, so serving it when the\n * refresh fails costs nothing.\n */\nfunction fallbackToken(\n token: OAuthAccessToken,\n trigger: TokenTrigger,\n now: EpochMillis\n): OAuthAccessToken | null {\n const unexpired = !isExpired({\n expiresAt: EpochMillis.fromDate(token.expiresAt),\n now,\n });\n switch (trigger.type) {\n case 'after-401':\n return unexpired && !isRejectedToken(token, trigger.rejected)\n ? token\n : null;\n case 'before-request':\n return unexpired ? token : null;\n }\n}\n\n/**\n * RFC 6749 §5.2's answer for a refresh token that is invalid, expired, revoked\n * or - under rotation - already used. CoinList's server does not say which.\n */\nfunction isInvalidGrant(error: unknown): boolean {\n if (!(error instanceof HttpError) || error.response.status !== 400) {\n return false;\n }\n const body: unknown = error.response.body;\n return (\n typeof body === 'object' &&\n body !== null &&\n 'error' in body &&\n body.error === 'invalid_grant'\n );\n}\n","import type { ServerNamespaceContext } from '@/server/core/server-namespace-context';\nimport * as offersApi from '@/universal/api/frontline/offers';\nimport type { OffersNamespace } from '@/universal/core/offers/offers-namespace';\nimport {\n type InternalLogger,\n internalLogger,\n} from '@/universal/core/shared/observability/internal-logger';\nimport type { ClientCredentialsOAuth } from '@/universal/types/oauth-session';\nimport type { Offer, OfferId } from '@/universal/types/offer';\nimport type { OfferDetail } from '@/universal/types/offer-detail';\nimport type {\n PaginatedResponse,\n PaginationParams,\n} from '@/universal/types/pagination';\n\n/**\n * The server-side offers namespace: the same reads as the shared\n * {@link OffersNamespace}, each additionally callable with an app-level token\n * from `ServerAuthNamespace.clientCredentials` instead of a user session.\n *\n * When a user session exists it takes precedence and `clientCreds` acts as a\n * fallback. Without either, the reads throw {@link NotAuthenticatedError}.\n */\nexport interface ServerOffersNamespace extends OffersNamespace {\n /** @param clientCreds app-level token to read without a user session. */\n list(clientCreds?: ClientCredentialsOAuth): Promise<Offer[]>;\n\n /** @param clientCreds app-level token to read without a user session. */\n listPage(\n params: PaginationParams,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<PaginatedResponse<Offer>>;\n\n /** @param clientCreds app-level token to read without a user session. */\n get(id: OfferId, clientCreds?: ClientCredentialsOAuth): Promise<OfferDetail>;\n}\n\n/** @internal */\nexport class ServerOffersNamespaceImpl implements ServerOffersNamespace {\n private readonly log: InternalLogger;\n\n constructor(private readonly ctx: ServerNamespaceContext) {\n this.log = internalLogger(ctx.logger, 'OFFERS');\n }\n\n async list(clientCreds?: ClientCredentialsOAuth): Promise<Offer[]> {\n return this.log.wrap('list', clientCreds, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return offersApi.fetchOffers(this.ctx.api, clientCreds);\n });\n }\n\n async listPage(\n params: PaginationParams,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<PaginatedResponse<Offer>> {\n return this.log.wrap('listPage', { params, clientCreds }, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return offersApi.fetchOffersPage(this.ctx.api, params, clientCreds);\n });\n }\n\n async get(\n id: OfferId,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<OfferDetail> {\n return this.log.wrap('get', { id, clientCreds }, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return offersApi.fetchOfferDetails(this.ctx.api, id, clientCreds);\n });\n }\n}\n","import type { ServerNamespaceContext } from '@/server/core/server-namespace-context';\nimport * as requirementsApi from '@/universal/api/frontline/requirements';\nimport {\n type RequirementsNamespace,\n RequirementsNamespaceImpl,\n} from '@/universal/core/requirements/requirements-namespace';\nimport type { UniversalNamespaceContext } from '@/universal/core/shared/namespace-context';\nimport type { ClientCredentialsOAuth } from '@/universal/types/oauth-session';\nimport type { OfferId } from '@/universal/types/offer';\nimport type { OfferOptionId } from '@/universal/types/offer-detail';\nimport type { Requirement } from '@/universal/types/requirement';\n\n/**\n * The server-side requirements namespace: the same surface as the shared\n * {@link RequirementsNamespace}, except {@link forOffer} — the offer's\n * requirement definitions are app-level data, so it is additionally callable\n * with a token from `ServerAuthNamespace.clientCredentials`.\n *\n * Everything else is per-user data and needs a user session. `handle` is\n * client-only: there is no browser tab to open on a server.\n */\nexport interface ServerRequirementsNamespace extends RequirementsNamespace {\n /** @param clientCreds app-level token to read without a user session. */\n forOffer(\n offerId: OfferId,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<Record<OfferOptionId, Requirement[]>>;\n}\n\n/** @internal */\nexport class ServerRequirementsNamespaceImpl\n extends RequirementsNamespaceImpl\n implements ServerRequirementsNamespace\n{\n constructor(\n private readonly serverCtx: UniversalNamespaceContext &\n ServerNamespaceContext\n ) {\n super(serverCtx);\n }\n\n async forOffer(\n offerId: OfferId,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<Record<OfferOptionId, Requirement[]>> {\n return this.log.wrap('forOffer', { offerId, clientCreds }, async () => {\n await this.serverCtx.ensureAuthenticated(clientCreds);\n return requirementsApi.fetchOfferRequirements(\n this.serverCtx.api,\n offerId,\n clientCreds\n );\n });\n }\n}\n","import { ApiClient } from '@/server/core/api/api-client';\nimport type {\n ServerAuthNamespace,\n SessionStore,\n} from '@/server/core/server-auth-namespace';\nimport { ServerAuthNamespaceImpl } from '@/server/core/server-auth-namespace-impl';\nimport type { ServerNamespaceContext } from '@/server/core/server-namespace-context';\nimport {\n type ServerOffersNamespace,\n ServerOffersNamespaceImpl,\n} from '@/server/core/server-offers-namespace';\nimport {\n type ServerRequirementsNamespace,\n ServerRequirementsNamespaceImpl,\n} from '@/server/core/server-requirements-namespace';\nimport { processRefreshCoordinator } from '@/server/core/session-refresh';\nimport {\n API_VERSION,\n PUBLIC_API_BASE_URL,\n} from '@/universal/api/frontline/config';\nimport { NABU_BASE_URL } from '@/universal/api/nabu/config';\nimport { publicClients } from '@/universal/api/rpc/public-clients';\nimport {\n type CentrifugeNamespace,\n CentrifugeNamespaceImpl,\n} from '@/universal/core/providers/checkout/centrifuge/centrifuge-namespace';\nimport {\n type CoinListTokenSaleNamespace,\n CoinListTokenSaleNamespaceImpl,\n} from '@/universal/core/providers/checkout/coin-list/token-sale-namespace';\nimport {\n type OndoNamespace,\n OndoNamespaceImpl,\n} from '@/universal/core/providers/checkout/ondo/ondo-namespace';\nimport {\n type SuperstateSwapNamespace,\n SuperstateSwapNamespaceImpl,\n} from '@/universal/core/providers/checkout/superstate/swap-namespace';\nimport {\n type Erc20Namespace,\n Erc20NamespaceImpl,\n} from '@/universal/core/shared/blockchain/erc20/erc20-namespace';\nimport { systemTime } from '@/universal/core/shared/effects';\nimport type { UniversalNamespaceContext } from '@/universal/core/shared/namespace-context';\nimport {\n type TokensNamespace,\n TokensNamespaceImpl,\n} from '@/universal/core/tokens/tokens-namespace';\nimport {\n type WalletsNamespace,\n WalletsNamespaceImpl,\n} from '@/universal/core/wallets/wallets-namespace';\nimport type { Config } from '@/universal/types/config';\nimport { NotAuthenticatedError } from '@/universal/types/errors';\nimport type { ClientSecret } from '@/universal/types/oauth';\nimport type { ClientCredentialsOAuth } from '@/universal/types/oauth-session';\nimport { Seconds } from '@/universal/types/time';\n\n/** Buffer before expiry to consider token expired for refresh. */\nconst ACCESS_TOKEN_EXPIRY_BUFFER: Seconds = Seconds.of(60);\n\nexport interface ServerConfig extends Config {\n readonly clientSecret: ClientSecret;\n readonly sessionStore: SessionStore;\n /**\n * How many seconds before its expiry an access token is refreshed. Defaults\n * to 60. Refreshing early moves the work to a single request while the\n * current token still works, and a refresh that fails inside this window\n * keeps serving the current token rather than failing the request.\n *\n * Build it with `Seconds.of(n)`, which throws a `ValidationError` unless\n * `n` is a non-negative integer.\n */\n readonly accessTokenExpiryBufferSeconds?: Seconds;\n /**\n * Whether the SDK throws where it would otherwise recover and log a\n * `'warn'` line. Defaults to `false`.\n *\n * Today it governs one thing: a failed token revoke in `auth.logout()`. By\n * default the session is cleared regardless; with `strict` the failure is\n * rethrown and the session is left untouched.\n */\n readonly strict?: boolean;\n}\n\n/**\n * Server-side CoinList SDK client.\n *\n * Functionality is grouped into namespaces — `coinlist.auth.getAccessToken()`,\n * `coinlist.offers.list()`, `coinlist.requirements.statuses(offerId)`.\n *\n * Operates in one of two modes depending on whether {@link SessionStore}\n * includes a `setSession` implementation:\n *\n * - **Writable store** (`setSession` provided) — full functionality: token\n * refresh, `auth.completeOAuth`, and `auth.logout` all work normally.\n *\n * - **Read-only store** (no `setSession`) — token refresh is skipped entirely,\n * meaning no network call is made and no refresh token is consumed.\n * `auth.completeOAuth` and `auth.logout` throw\n * {@link WritableSessionStoreRequiredError}. `auth.getAccessToken` may\n * return an expired token (see its docs). Use this mode in execution\n * contexts that can read the session but cannot write it back, such as\n * Next.js Server Components.\n *\n * Unless a namespace says otherwise, its methods require a user session and\n * throw {@link NotAuthenticatedError} when there is none. When the SDK could\n * not find out whether there is one - CoinList's token endpoint failing, or\n * the network - they throw that underlying error instead: see\n * {@link ServerAuthNamespace.getAccessToken}.\n */\nexport interface CoinListServer {\n /**\n * OAuth session lifecycle: exchanging an authorization code, serving a valid\n * access token to the browser, minting app-level tokens, and logging out —\n * e.g. `coinlist.auth.getAccessToken()`.\n */\n readonly auth: ServerAuthNamespace;\n\n /**\n * The offer catalogue — e.g. `coinlist.offers.list()`. Every read also\n * accepts an app-level token from {@link ServerAuthNamespace.clientCredentials}\n * so it can run without a user session.\n */\n readonly offers: ServerOffersNamespace;\n\n /**\n * Offer requirements and the operations that satisfy them — e.g.\n * `coinlist.requirements.statuses(offerId)`. `forOffer` also accepts an\n * app-level token; the rest is per-user data and needs a user session.\n */\n readonly requirements: ServerRequirementsNamespace;\n\n /**\n * The user's external wallets: ownership proofs and offer-option bindings —\n * e.g. `coinlist.wallets.list({ offerId, offerOptionId })`.\n */\n readonly wallets: WalletsNamespace;\n\n /**\n * Generic ERC-20 reads (token allowance and balance) — e.g.\n * `coinlist.erc20.getBalance({ ... })`.\n */\n readonly erc20: Erc20Namespace;\n\n /**\n * Token-sale operations: listing, reading, and recording participations —\n * e.g. `coinlist.tokenSale.list()`. The on-chain `execute`\n * flow is client-only and is not exposed here.\n */\n readonly tokenSale: CoinListTokenSaleNamespace;\n\n /**\n * On-chain swap operations: quoting a swap, reading swap-contract state, and\n * allow-listing a proven wallet — e.g.\n * `coinlist.superstate.getOutputToken({ contractAddress, chain })`.\n */\n readonly superstate: SuperstateSwapNamespace;\n\n /**\n * Ondo swap reads: trading status and quoting. Both are free to poll.\n *\n * No CoinList fee is applied to a quote; see {@link OndoNamespace}.\n *\n * This is Ondo's whole surface for now — the wallet-driven half arrives with\n * ENG-1680 and will be client-only, since it needs a wallet to sign with.\n */\n readonly ondo: OndoNamespace;\n\n /**\n * Centrifuge vault reads: the vault-wide ordering gate, and each wallet's\n * position. The status read is free to poll.\n */\n readonly centrifuge: CentrifugeNamespace;\n\n /**\n * Token display metadata (name, symbol, decimals, logos) from CoinList's\n * public token registry, keyed by chain + contract address — e.g.\n * `coinlist.tokens.get({ chain, address })` for an entry of\n * `Offer.tokens`, or `coinlist.tokens.list()` for the whole\n * catalogue in one request.\n *\n * Public and unauthenticated: unlike the other namespaces, its methods never\n * require a session, so it also works with a read-only store.\n */\n readonly tokens: TokensNamespace;\n}\n\nclass CoinListServerImpl implements CoinListServer {\n private readonly api: ApiClient;\n readonly auth: ServerAuthNamespace;\n readonly offers: ServerOffersNamespace;\n readonly requirements: ServerRequirementsNamespace;\n readonly wallets: WalletsNamespace;\n readonly erc20: Erc20Namespace;\n readonly tokenSale: CoinListTokenSaleNamespace;\n readonly superstate: SuperstateSwapNamespace;\n readonly ondo: OndoNamespace;\n readonly centrifuge: CentrifugeNamespace;\n readonly tokens: TokensNamespace;\n\n constructor(_config: ServerConfig) {\n const logger = _config.logger ?? null;\n this.api = new ApiClient(\n {\n baseUrl: _config.baseUrl ?? PUBLIC_API_BASE_URL,\n xApiVersion: API_VERSION,\n },\n (trigger) => auth.tokenFor(trigger),\n logger\n );\n const auth = new ServerAuthNamespaceImpl(this.api, {\n ..._config,\n accessTokenExpiryBufferSeconds:\n _config.accessTokenExpiryBufferSeconds ?? ACCESS_TOKEN_EXPIRY_BUFFER,\n strict: _config.strict ?? false,\n refreshes: processRefreshCoordinator,\n ...systemTime,\n });\n this.auth = auth;\n\n const ctx: UniversalNamespaceContext & ServerNamespaceContext = {\n api: this.api,\n rpc: publicClients(this.api, _config.rpc, _config.logger ?? null),\n logger,\n ensureUserAuthenticated: () => this.ensureAuthenticated(undefined),\n ensureAuthenticated: (clientCreds) =>\n this.ensureAuthenticated(clientCreds),\n };\n this.offers = new ServerOffersNamespaceImpl(ctx);\n this.requirements = new ServerRequirementsNamespaceImpl(ctx);\n this.wallets = new WalletsNamespaceImpl(ctx);\n this.erc20 = new Erc20NamespaceImpl(ctx);\n this.tokenSale = new CoinListTokenSaleNamespaceImpl(ctx);\n this.superstate = new SuperstateSwapNamespaceImpl(ctx);\n this.ondo = new OndoNamespaceImpl(ctx);\n this.centrifuge = new CentrifugeNamespaceImpl(ctx);\n this.tokens = new TokensNamespaceImpl(\n _config.tokensBaseUrl ?? NABU_BASE_URL,\n logger\n );\n }\n\n /**\n * An app-level token authenticates a request on its own; without one the\n * caller needs a user session.\n */\n private async ensureAuthenticated(\n clientCreds: ClientCredentialsOAuth | undefined\n ): Promise<void> {\n if (clientCreds) return;\n const token = await this.auth.getAccessToken();\n if (token === null) {\n throw new NotAuthenticatedError();\n }\n }\n}\n\nexport function createCoinListServer(config: ServerConfig): CoinListServer {\n return new CoinListServerImpl(config);\n}\n","import { pino } from 'pino';\nimport {\n loggerOverPino,\n PINO_LEVEL,\n SDK_LOGGER_NAME,\n} from '@/universal/core/shared/observability/pino-logger';\nimport type { Logger, PinoLoggerOptions } from '@/universal/types/logger';\n\n/**\n * A {@link Logger} that writes newline-delimited JSON to stdout through pino.\n *\n * ```ts\n * createCoinListServer({\n * ...config,\n * logger: pinoServerLogger({ isDev: false, level: 'info' }),\n * });\n * ```\n *\n * The Node counterpart of {@link pinoClientLogger}, for a route handler, a BFF\n * or SSR. One line per record, which is what every log aggregator ingests\n * without configuration.\n *\n * ## What it deliberately does not do\n *\n * **No transport, and no `pino-pretty`.** A pino transport runs on a worker\n * thread through `thread-stream`, which every bundler has to be told about\n * (`serverExternalPackages` in Next) and which edge runtimes cannot run at\n * all. Pretty-printing is a pipe the host owns rather than a dependency the\n * SDK takes on their behalf:\n *\n * ```sh\n * node server.js | npx pino-pretty\n * ```\n *\n * **No environment-dependent formatting.** `isDev` decides which levels are\n * legal and nothing else, so a development record and a production record are\n * the same shape - what you debug is what you ship.\n *\n * If you already run pino, or want transports, redaction or `destination`,\n * implement {@link Logger} yourself: it is four methods over an event, and\n * `@opentelemetry/instrumentation-pino` patches the pino module rather than an\n * instance, so trace correlation reaches this logger either way.\n *\n * ## Production\n *\n * **Not advised in production**, the same as everywhere else this seam is\n * documented: the SDK's default answer is to leave {@link Config.logger}\n * undefined outside development, staging and incident reproduction. A backend\n * is the least bad place to disregard that - the records go to your process\n * and your sink rather than to a page the end user can read - but it is still\n * a disregard, and the retention and access questions become yours.\n *\n * If you run one, `isDev: false` restricts you to `'error'`, `'warn'` and\n * `'info'` - `{ isDev: false, level: 'debug' }` does not compile, see\n * {@link PinoLoggerOptions}. Those levels are redacted by construction, which\n * is a mechanism the SDK holds itself to; whether that clears your bar is a\n * judgement the SDK is not in a position to make for you.\n *\n * Holds no module-level state: every call builds its own pino instance, and\n * the level is fixed at construction, so two loggers never interfere. Fixed is\n * this implementation's choice, not the seam's - the SDK re-reads\n * {@link Logger.level} before every log call - so if you want a level you can\n * change at runtime, implement {@link Logger} over your own pino instance\n * rather than calling this.\n */\nexport function pinoServerLogger(options: PinoLoggerOptions): Logger {\n return loggerOverPino(\n // `name` as a child binding rather than pino's `name` option: the option\n // is honoured by pino's node build and silently dropped by its browser\n // build, so a binding is the only spelling that identifies the SDK in\n // both environments.\n pino({\n level: PINO_LEVEL[options.level],\n }).child({ name: SDK_LOGGER_NAME }),\n options.level\n );\n}\n","import type { AuthorizationCode, CodeVerifier } from '@/universal/types/oauth';\nimport type {\n ClientCredentialsOAuth,\n OAuthAccessToken,\n OAuthSession,\n} from '@/universal/types/oauth-session';\n\nexport interface SessionStore {\n getSession(): Promise<OAuthSession | null>;\n /**\n * Persists or clears the OAuth session.\n *\n * **Omit this method to create a read-only store.** When absent, the SDK\n * skips token refresh entirely — no network call is made and no refresh\n * token is consumed. This is the correct approach for contexts that can read\n * the session but cannot write it back, such as Next.js Server Components.\n *\n * ⚠️ Do **not** implement this as a no-op (`async () => {}`). If the method\n * is present, the SDK assumes writes succeed: it will fire a token refresh\n * network call, consume the refresh token, then invoke `setSession` — which\n * would silently discard the new session and leave the browser holding an\n * invalidated refresh token. Simply **omit** `setSession` to prevent any\n * refresh from being attempted.\n *\n * {@link ServerAuthNamespace.completeOAuth} and\n * {@link ServerAuthNamespace.logout} always require a writable store and\n * throw {@link WritableSessionStoreRequiredError} if `setSession` is absent.\n */\n setSession?(session: OAuthSession | null): Promise<void>;\n}\n\n/**\n * The empty session store: holds no session, reads nothing, never writes.\n *\n * For server contexts that act without a user — the client-credentials grant\n * (`auth.clientCredentials`) and the public token registry (`tokens`) read\n * nothing from the store, so hosts building those paths need no store of\n * their own.\n *\n * `setSession` is deliberately absent, not a no-op — see\n * {@link SessionStore.setSession} for why a no-op is a bug.\n */\nexport function emptySessionStore(): SessionStore {\n return { getSession: async () => null };\n}\n\n/** The PKCE pair `CoinListClient#auth.completeOAuth` hands to the backend. */\nexport interface CompleteOAuthParams {\n /** The short-lived authorization code parsed out of the redirect URL. */\n code: AuthorizationCode;\n /** The `code_verifier` the browser generated when starting the flow. */\n codeVerifier: CodeVerifier;\n}\n\n/**\n * OAuth session lifecycle on your backend: exchanging authorization codes,\n * keeping the access token fresh, minting app-level tokens, and logging out.\n *\n * This namespace is server-only: every method needs the `clientSecret` and the\n * {@link SessionStore}, neither of which may exist in a browser.\n */\nexport interface ServerAuthNamespace {\n /**\n * Exchanges an authorization code from `CoinListClient#auth.completeOAuth`\n * for an OAuth session and persists it via {@link SessionStore.setSession}.\n *\n * The exchange is never retried automatically: an authorization code is\n * single-use, so a retry of an attempt that reached the server would fail\n * anyway, and could mask why the first one did.\n *\n * @throws {@link WritableSessionStoreRequiredError} if the session store\n * does not provide `setSession`.\n * @throws {@link HttpError} if CoinList refuses the exchange.\n */\n completeOAuth(params: CompleteOAuthParams): Promise<OAuthSession>;\n\n /**\n * Returns a valid access token for the current session, refreshing it if it\n * is expired or near expiry. This is what you serve to\n * {@link ClientConfig.getAccessToken}: answer `null` with \"no session\"\n * (e.g. a 204) and a throw with a failure (e.g. a 503), never the other way\n * round - the browser signs the user out on the first and not the second.\n *\n * **Writable store**: if the token is expired, the SDK exchanges the refresh\n * token for a new session, persists it, and returns the fresh access token.\n * Concurrent calls for the same session in this process share one exchange.\n *\n * A rejected refresh token (`400 invalid_grant`) is only a *suspicion* at\n * first, because the backend answers \"rotated a moment ago by a concurrent\n * request\" and \"expired or revoked\" identically: the SDK writes nothing,\n * so it cannot overwrite the session the winning request just stored, and\n * returns `null` - or the current access token, if that has not actually\n * expired yet. Only a second rejection of the same refresh token, 5 to 60\n * seconds later in this process, is the verdict: the session is cleared via\n * {@link SessionStore.setSession} and {@link Config.onSessionExpired} is\n * called. A browser confirming through your endpoint asks again after one\n * second, so it signs out on its own verdict and not on this one.\n *\n * **Read-only store** (no `setSession`): refresh is skipped entirely. The\n * stored token is returned as-is, even if it is expired — a non-null return\n * value does **not** guarantee the token is accepted by the API. Before\n * making API calls, check `token.expiresAt > new Date()`. Use a writable\n * store (e.g. in a Route Handler) when you need the SDK to renew the session\n * automatically.\n *\n * @returns the access token, or `null` if there is no session, or the\n * session was rejected (suspected or confirmed).\n * @throws the underlying error - an {@link HttpError} for a 5xx or\n * `invalid_client`, the `fetch` error for a network failure - when the SDK\n * could not find out whether a session exists and the current access token\n * has already expired. The session is left untouched. **Never treat this as\n * logged out**: it means \"try again\", and during an outage treating it as\n * logged out would sign out every user at once.\n */\n getAccessToken(): Promise<OAuthAccessToken | null>;\n\n /**\n * Obtains an app-level access token via the OAuth 2.0 `client_credentials`\n * grant (RFC 6749 §4.4). No user is involved: the token authenticates the\n * partner application itself and only grants access to app-level resources\n * such as offers and offer requirements.\n *\n * The token is **not** persisted to the {@link SessionStore} and has no\n * refresh token. It expires at `expiresAt`; once expired, call this method\n * again to obtain a fresh token — the SDK does not renew it automatically.\n *\n * Pass the result to `ServerOffersNamespace` or\n * `ServerRequirementsNamespace.forOffer` to read without a user session.\n */\n clientCredentials(): Promise<ClientCredentialsOAuth>;\n\n /**\n * Revokes the current token via `POST /oauth/revoke` and clears the session.\n * Does nothing when there is no session.\n *\n * The revoke is best-effort: if it fails for any reason - an\n * {@link HttpError}, a network failure, anything else - the failure is\n * logged at `'warn'` and the session is cleared regardless, because the\n * cleared session is what logs the user out. Set `strict` on the server\n * config to rethrow it instead.\n *\n * @throws {@link WritableSessionStoreRequiredError} if the session store does\n * not provide `setSession`.\n * @throws whatever the {@link SessionStore} throws, in either mode. A failed\n * `setSession(null)` means the session was not cleared.\n * @throws the revoke failure, only when `strict` is set. The session is then\n * left untouched, so the call can be retried.\n */\n logout(): Promise<void>;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/server/core/auth/errors.ts","../../src/server/core/auth/server-access-token.ts","../../src/server/core/auth/session-refresh.ts","../../src/universal/api/frontline/oauth.ts","../../src/server/core/auth/auth-namespace.ts","../../src/server/core/auth/session-store.ts","../../src/server/core/api/api-client.ts","../../src/server/core/offers/server-offers-namespace.ts","../../src/server/core/requirements/server-requirements-namespace.ts","../../src/server/core/coinlist-server.ts","../../src/server/core/shared/observability/pino-server-logger.ts"],"sourcesContent":["export class WritableSessionStoreRequiredError extends Error {\n constructor(\n message = 'This operation requires a writable SessionStore. Provide a `setSession` implementation in your SessionStore to persist or clear OAuth sessions.'\n ) {\n super(message);\n this.name = 'WritableSessionStoreRequiredError';\n }\n}\n","import type { SessionStore } from '@/server/core/auth/session-store';\nimport {\n isRejectedToken,\n type TokenTrigger,\n} from '@/universal/types/access-token-source';\nimport type {\n OAuthAccessToken,\n OAuthSession,\n} from '@/universal/types/oauth-session';\nimport { EpochMillis, isExpired, type Millis } from '@/universal/types/time';\n\n/** A writable store's `setSession`, bound to its store. */\nexport type SetSession = (session: OAuthSession | null) => Promise<void>;\n\n/** Whether the host's store can persist a renewed session. */\nexport type SessionStoreAccess =\n | { type: 'read-only' }\n | { type: 'writable'; setSession: SetSession };\n\n/**\n * What `ServerAuthNamespaceImpl` does with the stored session, decided\n * before any effect runs. See {@link classifyAccessToken}.\n */\nexport type AccessTokenClassification =\n | { type: 'no-session' }\n | { type: 'serve'; token: OAuthAccessToken }\n /** Past its buffer, but a read-only store cannot renew it. */\n | { type: 'serve-stale'; token: OAuthAccessToken }\n /** A read-only store holding the very token the API just refused. */\n | { type: 'refuse' }\n | {\n type: 'refresh';\n session: OAuthSession;\n setSession: SetSession;\n /** See {@link fallbackToken}. */\n fallback: OAuthAccessToken | null;\n };\n\n/**\n * Decides what to do with the stored session, before any effect runs. Pure,\n * so every combination of store, trigger and clock is a plain table. The\n * server's counterpart of `decideToken` in `client/core/auth/client-access-token`;\n * ADR-12 says why the two windows differ.\n *\n * - A read-only store never refreshes. After a 401 it answers `null` only for\n * the very token that was just refused, which tells the renewal middleware\n * not to replay with it; a store that has moved on since hands its token\n * back.\n * - A writable store refreshes when the token is inside its buffer, or after\n * a 401 for the token it holds. A session with no refresh token whose token\n * still works is served as it is: nothing to renew with, but nothing lost\n * yet either, so the verdict waits for it to actually expire.\n */\nexport function classifyAccessToken({\n session,\n store,\n trigger,\n now,\n buffer,\n}: {\n session: OAuthSession | null;\n store: SessionStoreAccess;\n trigger: TokenTrigger;\n now: EpochMillis;\n buffer: Millis;\n}): AccessTokenClassification {\n if (session === null) {\n return { type: 'no-session' };\n }\n const token = session.accessToken;\n const stale = isExpired({\n expiresAt: EpochMillis.fromDate(token.expiresAt),\n now,\n buffer,\n });\n switch (store.type) {\n case 'read-only':\n return classifyReadOnly(token, trigger, stale);\n case 'writable': {\n if (!needsRefresh(token, trigger, stale)) {\n return { type: 'serve', token };\n }\n const fallback = fallbackToken(token, trigger, now);\n return session.refreshToken === undefined && fallback !== null\n ? { type: 'serve', token: fallback }\n : {\n type: 'refresh',\n session,\n setSession: store.setSession,\n fallback,\n };\n }\n }\n}\n\nfunction classifyReadOnly(\n token: OAuthAccessToken,\n trigger: TokenTrigger,\n stale: boolean\n): AccessTokenClassification {\n switch (trigger.type) {\n case 'after-401':\n return isRejectedToken(token, trigger.rejected)\n ? { type: 'refuse' }\n : { type: 'serve', token };\n case 'before-request':\n return stale ? { type: 'serve-stale', token } : { type: 'serve', token };\n }\n}\n\nexport function sessionStoreAccess(\n sessionStore: SessionStore\n): SessionStoreAccess {\n const setSession = sessionStore.setSession?.bind(sessionStore);\n return setSession === undefined\n ? { type: 'read-only' }\n : { type: 'writable', setSession };\n}\n\nfunction needsRefresh(\n token: OAuthAccessToken,\n trigger: TokenTrigger,\n stale: boolean\n): boolean {\n switch (trigger.type) {\n case 'before-request':\n return stale;\n case 'after-401':\n // The store may already hold a newer token than the refused one - a\n // refresh earlier in this same request - and that one is still good.\n return (\n trigger.rejected.type === 'no-user-token' ||\n isRejectedToken(token, trigger.rejected) ||\n stale\n );\n }\n}\n\n/**\n * The token to serve when a refresh cannot produce a new one: the current\n * one, as long as it has not actually expired - it is inside the early-refresh\n * buffer, not past it - and the API has not refused it. The backend keeps a\n * superseded access token valid until its own expiry, so serving it when the\n * refresh fails costs nothing.\n */\nfunction fallbackToken(\n token: OAuthAccessToken,\n trigger: TokenTrigger,\n now: EpochMillis\n): OAuthAccessToken | null {\n const unexpired = !isExpired({\n expiresAt: EpochMillis.fromDate(token.expiresAt),\n now,\n });\n switch (trigger.type) {\n case 'after-401':\n return unexpired && !isRejectedToken(token, trigger.rejected)\n ? token\n : null;\n case 'before-request':\n return unexpired ? token : null;\n }\n}\n","import { systemTime, type TimeEff } from '@/universal/core/shared/effects';\nimport type { Newtype } from '@/universal/types/newtype';\nimport type { ClientId } from '@/universal/types/oauth';\nimport type { OAuthSession } from '@/universal/types/oauth-session';\nimport { EpochMillis, Millis } from '@/universal/types/time';\n\n/**\n * A second rejection of the same refresh token counts as the verdict only this\n * long after the first. A request sent just before the winning refresh's cookie\n * landed can still present the old token a moment later, and a verdict clears\n * the store - a clearing cookie that could overwrite the winner's - so this is\n * deliberately wider than any in-flight request should take.\n */\nexport const VERDICT_MIN_GAP: Millis = Millis.of(5_000);\n\n/**\n * How long a rejection is remembered. A rejection older than this counts as\n * the first again, which can only delay a verdict, never make a false one.\n */\nexport const REJECTION_MEMORY: Millis = Millis.of(60_000);\n\n/**\n * Identifies one session for coordination: the refresh token when it has one,\n * the access token when it does not (there is then nothing to exchange, but\n * the verdict still has to be reached once rather than once per request).\n */\nexport type RefreshKey = Newtype<string, 'RefreshKey'>;\n\nexport const RefreshKey = {\n of: ({\n clientId,\n session,\n }: {\n clientId: ClientId;\n session: OAuthSession;\n }): RefreshKey => {\n const identity =\n session.refreshToken === undefined\n ? ['access', session.accessToken.value]\n : ['refresh', session.refreshToken];\n // JSON quotes and escapes each part, so no client id and token can be\n // split two ways into the same key.\n return JSON.stringify([clientId, ...identity]) as RefreshKey;\n },\n};\n\n/** What one `POST /oauth/token` exchange answered, before it is judged. */\nexport type ExchangeResult =\n | { type: 'renewed'; session: OAuthSession }\n /** `400 invalid_grant`: rotated by a concurrent refresh, expired or revoked. */\n | { type: 'rejected' }\n /** The session carries no refresh token, so there is nothing to exchange. */\n | { type: 'unrenewable' }\n /**\n * 5xx, network failure, `invalid_client`, unparsable body: no answer at all,\n * so whether the session is alive is unknown.\n */\n | { type: 'unknown'; error: unknown };\n\n/**\n * What one exchange came to, judged once for every caller that joined it.\n *\n * - `suspected` - a first rejection, or a repeat inside {@link VERDICT_MIN_GAP}.\n * Write nothing: the rejection may be a race another request won.\n * - `expired` - the verdict. The caller that led the exchange clears its\n * store and notifies the host; the ones that joined only clear.\n * - `already-expired` - the verdict was reached earlier; no exchange was made.\n * Clear, and notify nobody.\n */\nexport type RefreshOutcome =\n | { type: 'renewed'; session: OAuthSession }\n | { type: 'suspected' }\n | { type: 'expired' }\n | { type: 'already-expired' }\n | { type: 'unknown'; error: unknown };\n\n/** What the coordinator remembers about a session it has seen rejected. */\nexport type Rejection =\n | { type: 'suspected'; since: EpochMillis }\n | { type: 'expired'; at: EpochMillis };\n\n/**\n * Deduplicates refresh-token exchanges and remembers rejections across every\n * `CoinListServer` in the process.\n *\n * **Process-wide on purpose.** Hosts build a `CoinListServer` per request,\n * bound to that request's cookies, so a per-instance slot would deduplicate\n * nothing: the herd this exists for is N concurrent requests carrying one\n * refresh token, each with its own server. It is the one sanctioned exception\n * to the SDK's \"no global mutable state\" rule (see ADR-12). An in-flight entry\n * is deleted when it settles. A rejection stops counting after\n * {@link REJECTION_MEMORY} and is swept out at the start of the next\n * refresh of any session, so an idle process keeps at most its last minute of\n * rejections - one small entry per dead refresh token - until traffic resumes.\n *\n * Per JavaScript module instance, not per machine: Next.js middleware and\n * route handlers can each hold their own, and separate processes never share\n * one. Losers across that boundary are what the rejection memory is for.\n *\n * Only the exchange is shared. Each caller still writes the outcome to its own\n * `SessionStore`, because each request has its own cookie jar.\n */\nexport class RefreshCoordinator {\n private readonly inFlight = new Map<RefreshKey, Promise<RefreshOutcome>>();\n private readonly rejections = new Map<RefreshKey, Rejection>();\n\n constructor(private readonly env: TimeEff = systemTime) {}\n\n /**\n * Runs `exchange` for `key` unless one is already in flight, in which case\n * the caller joins it. `joined` tells the caller whether it led: only the\n * leader reports the outcome, so a burst of N produces one verdict, one log\n * line and one `onSessionExpired`.\n */\n refresh(\n key: RefreshKey,\n exchange: () => Promise<ExchangeResult>\n ): { outcome: Promise<RefreshOutcome>; joined: boolean } {\n const existing = this.inFlight.get(key);\n if (existing !== undefined) {\n return { outcome: existing, joined: true };\n }\n const outcome = this.run(key, exchange).finally(() => {\n this.inFlight.delete(key);\n });\n this.inFlight.set(key, outcome);\n return { outcome, joined: false };\n }\n\n private async run(\n key: RefreshKey,\n exchange: () => Promise<ExchangeResult>\n ): Promise<RefreshOutcome> {\n this.forgetStale(this.env.time.now());\n if (this.rejections.get(key)?.type === 'expired') {\n return { type: 'already-expired' };\n }\n\n const result = await exchange();\n switch (result.type) {\n case 'renewed':\n case 'unknown':\n return result;\n case 'rejected':\n case 'unrenewable': {\n const judged = judge({\n result: result.type,\n prior: this.rejections.get(key),\n now: this.env.time.now(),\n });\n this.rejections.set(key, judged.memory);\n return { type: judged.outcome };\n }\n }\n }\n\n private forgetStale(now: EpochMillis): void {\n for (const [key, rejection] of this.rejections) {\n if (EpochMillis.elapsed(rejectedAt(rejection), now) > REJECTION_MEMORY) {\n this.rejections.delete(key);\n }\n }\n }\n}\n\n/**\n * Turns a rejection into a suspicion or a verdict, given what was remembered\n * about the same session. Pure, so the timing rules can be stated as\n * properties over every gap.\n *\n * `unrenewable` is a verdict at once: with no refresh token there is no race\n * to lose. A `rejected` is a verdict only when a remembered suspicion is at\n * least {@link VERDICT_MIN_GAP} old; the caller has already forgotten any\n * older than {@link REJECTION_MEMORY}.\n */\nexport function judge({\n result,\n prior,\n now,\n}: {\n result: 'rejected' | 'unrenewable';\n prior: Rejection | undefined;\n now: EpochMillis;\n}): { outcome: 'suspected' | 'expired'; memory: Rejection } {\n const verdict = {\n outcome: 'expired',\n memory: { type: 'expired', at: now },\n } as const;\n switch (result) {\n case 'unrenewable':\n return verdict;\n case 'rejected':\n switch (prior?.type) {\n case undefined:\n return {\n outcome: 'suspected',\n memory: { type: 'suspected', since: now },\n };\n case 'suspected':\n return EpochMillis.elapsed(prior.since, now) >= VERDICT_MIN_GAP\n ? verdict\n : { outcome: 'suspected', memory: prior };\n case 'expired':\n return { outcome: 'expired', memory: prior };\n }\n }\n}\n\nfunction rejectedAt(rejection: Rejection): EpochMillis {\n switch (rejection.type) {\n case 'suspected':\n return rejection.since;\n case 'expired':\n return rejection.at;\n }\n}\n\n/** The coordinator every `CoinListServer` in this module instance shares. */\nexport const processRefreshCoordinator = new RefreshCoordinator();\n","import { Attributes } from '@/universal/api/http-attributes';\nimport type { Sender } from '@/universal/api/sender';\nimport { ClientCredentialsOAuth } from '@/universal/types/client-credentials';\nimport type { OAuthSessionDto } from '@/universal/types/dto/oauth-session';\nimport { HttpError } from '@/universal/types/http';\nimport type {\n AuthorizationGrant,\n ClientId,\n ClientSecret,\n RedirectUri,\n} from '@/universal/types/oauth';\nimport {\n type OAuthAccessToken,\n type OAuthRefreshToken,\n OAuthSession,\n} from '@/universal/types/oauth-session';\n\n/**\n * The partner application's credentials, which every grant and the revoke\n * carry. Passed in rather than held, so this module stores no secret: only a\n * server has one to pass, and `oauth-grants-are-server-only` keeps every\n * other tree from importing it.\n */\nexport type OAuthClient = {\n clientId: ClientId;\n clientSecret: ClientSecret;\n};\n\n/** The outcome of a refresh-token grant the SDK has a remedy for. */\nexport type RefreshGrant =\n | { type: 'renewed'; session: OAuthSession }\n /**\n * RFC 6749 §5.2's `invalid_grant`: the refresh token is invalid, expired,\n * revoked or - under rotation - already used. CoinList does not say which.\n */\n | { type: 'invalid-grant' };\n\n/**\n * The `authorization_code` grant. Never retried: the code is single-use, so a\n * retry of an attempt that reached the server would fail anyway.\n */\nexport async function exchangeAuthorizationCode(\n api: Sender,\n params: OAuthClient & AuthorizationGrant & { redirectUri: RedirectUri }\n): Promise<OAuthSession> {\n const dto = await api.send<OAuthSessionDto>({\n method: 'POST',\n url: '/oauth/token',\n body: {\n grant_type: 'authorization_code',\n code: params.code,\n redirect_uri: params.redirectUri,\n client_id: params.clientId,\n client_secret: params.clientSecret,\n code_verifier: params.codeVerifier,\n },\n attributes: Attributes.notRetryable(),\n });\n return OAuthSession.fromDto(dto);\n}\n\n/**\n * The `refresh_token` grant, answering `invalid-grant` for a rejected refresh\n * token and throwing anything else.\n *\n * Never retried. The refresh token is single-use, so a retry of an attempt\n * that reached the server would present a token already rotated away, and its\n * `invalid_grant` would be judged as a rejection of the session.\n */\nexport async function refreshOAuthSession(\n api: Sender,\n params: OAuthClient & { refreshToken: OAuthRefreshToken }\n): Promise<RefreshGrant> {\n try {\n const dto = await api.send<OAuthSessionDto>({\n method: 'POST',\n url: '/oauth/token',\n body: {\n grant_type: 'refresh_token',\n refresh_token: params.refreshToken,\n client_id: params.clientId,\n client_secret: params.clientSecret,\n },\n attributes: Attributes.notRetryable(),\n });\n return { type: 'renewed', session: OAuthSession.fromDto(dto) };\n } catch (error) {\n if (isInvalidGrant(error)) {\n return { type: 'invalid-grant' };\n }\n throw error;\n }\n}\n\n/** The `client_credentials` grant (RFC 6749 §4.4): an app-level token. */\nexport async function requestClientCredentials(\n api: Sender,\n params: OAuthClient\n): Promise<ClientCredentialsOAuth> {\n const dto = await api.send<OAuthSessionDto>({\n method: 'POST',\n url: '/oauth/token',\n body: {\n grant_type: 'client_credentials',\n client_id: params.clientId,\n client_secret: params.clientSecret,\n },\n });\n return ClientCredentialsOAuth(OAuthSession.fromDto(dto).accessToken);\n}\n\n/** `POST /oauth/revoke` for an access token. */\nexport async function revokeOAuthToken(\n api: Sender,\n params: OAuthClient & { token: OAuthAccessToken }\n): Promise<void> {\n await api.send({\n method: 'POST',\n url: '/oauth/revoke',\n body: {\n token: params.token.value,\n client_id: params.clientId,\n client_secret: params.clientSecret,\n },\n });\n}\n\nfunction isInvalidGrant(error: unknown): boolean {\n if (!(error instanceof HttpError) || error.response.status !== 400) {\n return false;\n }\n const body: unknown = error.response.body;\n return (\n typeof body === 'object' &&\n body !== null &&\n 'error' in body &&\n body.error === 'invalid_grant'\n );\n}\n","import { WritableSessionStoreRequiredError } from '@/server/core/auth/errors';\nimport {\n type AccessTokenClassification,\n classifyAccessToken,\n type SetSession,\n sessionStoreAccess,\n} from '@/server/core/auth/server-access-token';\nimport {\n type ExchangeResult,\n type RefreshCoordinator,\n RefreshKey,\n} from '@/server/core/auth/session-refresh';\nimport type { SessionStore } from '@/server/core/auth/session-store';\nimport * as oauthApi from '@/universal/api/frontline/oauth';\nimport type { Sender } from '@/universal/api/sender';\nimport { notifySessionExpired } from '@/universal/core/auth/session-expired';\nimport type { TimeEff } from '@/universal/core/shared/effects';\nimport {\n type InternalLogger,\n internalLogger,\n} from '@/universal/core/shared/observability/internal-logger';\nimport { TokenTrigger } from '@/universal/types/access-token-source';\nimport type { ClientCredentialsOAuth } from '@/universal/types/client-credentials';\nimport type { Config } from '@/universal/types/config';\nimport type { AuthorizationGrant, ClientSecret } from '@/universal/types/oauth';\nimport type {\n OAuthAccessToken,\n OAuthRefreshToken,\n OAuthSession,\n} from '@/universal/types/oauth-session';\nimport { Seconds } from '@/universal/types/time';\n\n/**\n * OAuth session lifecycle on your backend: exchanging authorization codes,\n * keeping the access token fresh and serving it to the browser, minting\n * app-level tokens, and logging out - e.g. `coinlist.auth.getAccessToken()`.\n *\n * This namespace is server-only: every method needs the `clientSecret` and the\n * {@link SessionStore}, neither of which may exist in a browser.\n */\nexport interface ServerAuthNamespace {\n /**\n * Exchanges an authorization code from `CoinListClient#auth.completeOAuth`\n * for an OAuth session and persists it via {@link SessionStore.setSession}.\n *\n * The exchange is never retried automatically: an authorization code is\n * single-use, so a retry of an attempt that reached the server would fail\n * anyway, and could mask why the first one did.\n *\n * @throws {@link WritableSessionStoreRequiredError} if the session store\n * does not provide `setSession`.\n * @throws {@link HttpError} if CoinList refuses the exchange.\n */\n completeOAuth(grant: AuthorizationGrant): Promise<OAuthSession>;\n\n /**\n * Returns a valid access token for the current session, refreshing it if it\n * is expired or near expiry. This is what you serve to\n * {@link ClientConfig.getAccessToken}: answer `null` with \"no session\"\n * (e.g. a 204) and a throw with a failure (e.g. a 503), never the other way\n * round - the browser signs the user out on the first and not the second.\n *\n * **Writable store**: if the token is expired, the SDK exchanges the refresh\n * token for a new session, persists it, and returns the fresh access token.\n * Concurrent calls for the same session in this process share one exchange.\n *\n * A rejected refresh token (`400 invalid_grant`) is only a *suspicion* at\n * first, because the backend answers \"rotated a moment ago by a concurrent\n * request\" and \"expired or revoked\" identically: the SDK writes nothing,\n * so it cannot overwrite the session the winning request just stored, and\n * returns `null` - or the current access token, if that has not actually\n * expired yet. Only a second rejection of the same refresh token, 5 to 60\n * seconds later in this process, is the verdict: the session is cleared via\n * {@link SessionStore.setSession} and {@link Config.onSessionExpired} is\n * called. A browser confirming through your endpoint asks again after one\n * second, so it signs out on its own verdict and not on this one.\n *\n * **Read-only store** (no `setSession`): refresh is skipped entirely. The\n * stored token is returned as-is, even if it is expired — a non-null return\n * value does **not** guarantee the token is accepted by the API. Before\n * making API calls, check `token.expiresAt > new Date()`. Use a writable\n * store (e.g. in a Route Handler) when you need the SDK to renew the session\n * automatically.\n *\n * @returns the access token, or `null` if there is no session, or the\n * session was rejected (suspected or confirmed).\n * @throws the underlying error - an {@link HttpError} for a 5xx or\n * `invalid_client`, the `fetch` error for a network failure - when the SDK\n * could not find out whether a session exists and the current access token\n * has already expired. The session is left untouched. **Never treat this as\n * logged out**: it means \"try again\", and during an outage treating it as\n * logged out would sign out every user at once.\n */\n getAccessToken(): Promise<OAuthAccessToken | null>;\n\n /**\n * Obtains an app-level access token via the OAuth 2.0 `client_credentials`\n * grant (RFC 6749 §4.4). No user is involved: the token authenticates the\n * partner application itself and only grants access to app-level resources\n * such as offers and offer requirements.\n *\n * The token is **not** persisted to the {@link SessionStore} and has no\n * refresh token. It expires at `expiresAt`; once expired, call this method\n * again to obtain a fresh token — the SDK does not renew it automatically.\n *\n * Pass the result to `ServerOffersNamespace` or\n * `ServerRequirementsNamespace.forOffer` to read without a user session.\n */\n clientCredentials(): Promise<ClientCredentialsOAuth>;\n\n /**\n * Revokes the current token via `POST /oauth/revoke` and clears the session.\n * Does nothing when there is no session.\n *\n * The revoke is best-effort: if it fails for any reason - an\n * {@link HttpError}, a network failure, anything else - the failure is\n * logged at `'warn'` and the session is cleared regardless, because the\n * cleared session is what logs the user out. Set `strict` on the server\n * config to rethrow it instead.\n *\n * @throws {@link WritableSessionStoreRequiredError} if the session store does\n * not provide `setSession`.\n * @throws whatever the {@link SessionStore} throws, in either mode. A failed\n * `setSession(null)` means the session was not cleared.\n * @throws the revoke failure, only when `strict` is set. The session is then\n * left untouched, so the call can be retried.\n */\n logout(): Promise<void>;\n}\n\n/**\n * The effects {@link ServerAuthNamespaceImpl} runs on: the clock, the host's\n * session store, and the process-wide refresh coordinator.\n *\n * @internal\n */\nexport type ServerAuthEnv = TimeEff & {\n readonly sessionStore: SessionStore;\n /** Deduplicates exchanges and remembers rejections; see its TSDoc. */\n readonly refreshes: RefreshCoordinator;\n};\n\n/**\n * What {@link ServerAuthNamespaceImpl} needs out of `ServerConfig`.\n *\n * @internal\n */\nexport type ServerAuthConfig = Config &\n ServerAuthEnv & {\n readonly clientSecret: ClientSecret;\n /** How long before its expiry an access token counts as expired. */\n readonly accessTokenExpiryBufferSeconds: Seconds;\n /** Whether to rethrow errors the SDK could otherwise swallow (e.g. revoke). */\n readonly strict: boolean;\n };\n\n/**\n * The SDK's {@link ServerAuthNamespace}. {@link tokenFor} is the seam the\n * authenticated HTTP pipeline calls rather than API a host calls.\n *\n * @internal\n */\nexport class ServerAuthNamespaceImpl implements ServerAuthNamespace {\n private readonly log: InternalLogger;\n\n constructor(\n private readonly api: Sender,\n private readonly config: ServerAuthConfig\n ) {\n this.log = internalLogger(config.logger ?? null, 'AUTH');\n }\n\n async completeOAuth(grant: AuthorizationGrant): Promise<OAuthSession> {\n return this.log.wrap('completeOAuth', undefined, async () => {\n const setSession = this.writableSessionStore();\n const session = await oauthApi.exchangeAuthorizationCode(this.api, {\n ...this.oauthClient(),\n code: grant.code,\n codeVerifier: grant.codeVerifier,\n redirectUri: this.config.redirectUri,\n });\n await setSession(session);\n return session;\n });\n }\n\n async getAccessToken(): Promise<OAuthAccessToken | null> {\n return this.tokenFor(TokenTrigger.beforeRequest);\n }\n\n /**\n * {@link getAccessToken} with the reason the SDK is asking, which is what\n * the authenticated HTTP pipeline calls: after a 401 the token's own expiry\n * no longer matters, because the API has answered for it.\n */\n async tokenFor(trigger: TokenTrigger): Promise<OAuthAccessToken | null> {\n // Wrapped despite running on every request: `sessionStore.getSession` is\n // host code, and when it throws there is nothing else between the failure\n // and the host's own catch to say which SDK call it came out of.\n return this.log.wrap('getAccessToken', undefined, () =>\n this.readAccessToken(trigger)\n );\n }\n\n private async readAccessToken(\n trigger: TokenTrigger\n ): Promise<OAuthAccessToken | null> {\n const sessionStore = this.config.sessionStore;\n const classification = classifyAccessToken({\n session: await sessionStore.getSession(),\n store: sessionStoreAccess(sessionStore),\n trigger,\n now: this.config.time.now(),\n buffer: Seconds.toMillis(this.config.accessTokenExpiryBufferSeconds),\n });\n switch (classification.type) {\n case 'no-session':\n case 'refuse':\n return null;\n case 'serve':\n return classification.token;\n case 'serve-stale':\n this.log.child({ op: 'getAccessToken' }).warn(() => ({\n msg: 'serving an expired token: the session store is read-only, so it cannot be refreshed',\n }));\n return classification.token;\n case 'refresh':\n return this.refresh(classification);\n }\n }\n\n /**\n * Refreshes through the process-wide coordinator, then acts on the outcome\n * for this caller's own store. The store is written before anything is\n * logged or the host is notified, so `onSessionExpired` always finds it\n * cleared.\n */\n private async refresh({\n session,\n setSession,\n fallback,\n }: Extract<\n AccessTokenClassification,\n { type: 'refresh' }\n >): Promise<OAuthAccessToken | null> {\n const log = this.log.child({ op: 'refresh' });\n const key = RefreshKey.of({ clientId: this.config.clientId, session });\n const { outcome, joined } = this.config.refreshes.refresh(key, () =>\n this.exchange(session.refreshToken)\n );\n if (joined) {\n log.debug(() => ({ msg: 'joined a refresh already in flight' }));\n }\n\n const result = await outcome;\n switch (result.type) {\n case 'renewed':\n await setSession(result.session);\n if (!joined) {\n log.info(() => ({ msg: 'session renewed' }));\n }\n return result.session.accessToken;\n case 'suspected':\n // Writing nothing is the point: a clearing cookie from this response\n // could land after the winner's and sign the user out.\n if (!joined) {\n log.warn(() => ({\n msg: 'refresh rejected; not clearing until it is confirmed',\n }));\n }\n return fallback;\n case 'expired':\n await setSession(null);\n if (!joined) {\n log.info(() => ({\n msg: 'session expired',\n fields: {\n reason:\n session.refreshToken === undefined\n ? 'no-refresh-token'\n : 'refresh-rejected',\n },\n }));\n notifySessionExpired(this.config.onSessionExpired, log);\n }\n return null;\n case 'already-expired':\n await setSession(null);\n return null;\n case 'unknown':\n if (fallback === null) {\n throw result.error;\n }\n if (!joined) {\n log.warning(\n () => ({ msg: 'refresh failed; serving the current token' }),\n result.error\n );\n }\n return fallback;\n }\n }\n\n /**\n * One refresh-token grant, total: every failure comes back as a value, so\n * the coordinator can judge it once for everyone who joined.\n */\n private async exchange(\n refreshToken: OAuthRefreshToken | undefined\n ): Promise<ExchangeResult> {\n if (refreshToken === undefined) {\n return { type: 'unrenewable' };\n }\n try {\n const grant = await oauthApi.refreshOAuthSession(this.api, {\n ...this.oauthClient(),\n refreshToken,\n });\n switch (grant.type) {\n case 'renewed':\n return { type: 'renewed', session: grant.session };\n case 'invalid-grant':\n return { type: 'rejected' };\n }\n } catch (error) {\n return { type: 'unknown', error };\n }\n }\n\n async clientCredentials(): Promise<ClientCredentialsOAuth> {\n return this.log.wrap('clientCredentials', undefined, () =>\n oauthApi.requestClientCredentials(this.api, this.oauthClient())\n );\n }\n\n async logout(): Promise<void> {\n return this.log.wrap('logout', undefined, () => this.revokeAndClear());\n }\n\n private async revokeAndClear(): Promise<void> {\n const setSession = this.writableSessionStore();\n const session = await this.config.sessionStore.getSession();\n if (session == null) return;\n\n try {\n await oauthApi.revokeOAuthToken(this.api, {\n ...this.oauthClient(),\n token: session.accessToken,\n });\n } catch (err) {\n // Whatever failed - an HTTP error, the network, anything else - the\n // local session is what logs the user out, so only strict callers hear\n // about a revoke that did not reach CoinList.\n if (this.config.strict) {\n throw err;\n }\n this.log\n .child({ op: 'logout' })\n .warning(\n () => ({ msg: 'revoke failed; local session cleared regardless' }),\n err\n );\n }\n await setSession(null);\n }\n\n private oauthClient(): oauthApi.OAuthClient {\n return {\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n };\n }\n\n /**\n * Returns the store's `setSession`, or throws - the guard every\n * session-writing operation shares.\n */\n private writableSessionStore(): SetSession {\n const store = sessionStoreAccess(this.config.sessionStore);\n switch (store.type) {\n case 'writable':\n return store.setSession;\n case 'read-only':\n throw new WritableSessionStoreRequiredError();\n }\n }\n}\n","import type { OAuthSession } from '@/universal/types/oauth-session';\n\nexport interface SessionStore {\n getSession(): Promise<OAuthSession | null>;\n /**\n * Persists or clears the OAuth session.\n *\n * **Omit this method to create a read-only store.** When absent, the SDK\n * skips token refresh entirely — no network call is made and no refresh\n * token is consumed. This is the correct approach for contexts that can read\n * the session but cannot write it back, such as Next.js Server Components.\n *\n * ⚠️ Do **not** implement this as a no-op (`async () => {}`). If the method\n * is present, the SDK assumes writes succeed: it will fire a token refresh\n * network call, consume the refresh token, then invoke `setSession` — which\n * would silently discard the new session and leave the browser holding an\n * invalidated refresh token. Simply **omit** `setSession` to prevent any\n * refresh from being attempted.\n *\n * {@link ServerAuthNamespace.completeOAuth} and\n * {@link ServerAuthNamespace.logout} always require a writable store and\n * throw {@link WritableSessionStoreRequiredError} if `setSession` is absent.\n */\n setSession?(session: OAuthSession | null): Promise<void>;\n}\n\n/**\n * The empty session store: holds no session, reads nothing, never writes.\n *\n * For server contexts that act without a user — the client-credentials grant\n * (`auth.clientCredentials`) and the public token registry (`tokens`) read\n * nothing from the store, so hosts building those paths need no store of\n * their own.\n *\n * `setSession` is deliberately absent, not a no-op — see\n * {@link SessionStore.setSession} for why a no-op is a bug.\n */\nexport function emptySessionStore(): SessionStore {\n return { getSession: async () => null };\n}\n","import { AuthenticatedApiClient } from '@/universal/api/authenticated-api-client';\nimport type { HttpClientConfig } from '@/universal/api/http';\nimport type { HttpRequest } from '@/universal/api/sender';\nimport type { InternalAccessTokenSource } from '@/universal/types/access-token-source';\nimport type { Logger } from '@/universal/types/logger';\n\nexport class ApiClient {\n private readonly client: AuthenticatedApiClient;\n\n constructor(\n config: HttpClientConfig,\n fetchAccessToken: InternalAccessTokenSource,\n logger: Logger | null = null\n ) {\n this.client = new AuthenticatedApiClient(\n config,\n fetchAccessToken,\n [],\n logger\n );\n }\n\n async send<TResponse>(request: HttpRequest): Promise<TResponse> {\n return this.client.send<TResponse>(request);\n }\n}\n","import type { ServerNamespaceContext } from '@/server/core/shared/server-namespace-context';\nimport * as offersApi from '@/universal/api/frontline/offers';\nimport {\n type OffersNamespace,\n OffersNamespaceImpl,\n} from '@/universal/core/offers/offers-namespace';\nimport type { ClientCredentialsOAuth } from '@/universal/types/client-credentials';\nimport type { Offer, OfferId } from '@/universal/types/offer';\nimport type { OfferDetail } from '@/universal/types/offer-detail';\nimport type {\n PaginatedResponse,\n PaginationParams,\n} from '@/universal/types/pagination';\n\n/**\n * The server-side offers namespace: the same reads as the shared\n * {@link OffersNamespace}, each additionally callable with an app-level token\n * from `ServerAuthNamespace.clientCredentials` instead of a user session -\n * e.g. `coinlist.offers.list(await coinlist.auth.clientCredentials())`.\n *\n * When a user session exists it takes precedence and `clientCreds` acts as a\n * fallback. Without either, the reads throw {@link NotAuthenticatedError}.\n */\nexport interface ServerOffersNamespace extends OffersNamespace {\n /** @param clientCreds app-level token to read without a user session. */\n list(clientCreds?: ClientCredentialsOAuth): Promise<Offer[]>;\n\n /** @param clientCreds app-level token to read without a user session. */\n listPage(\n params: PaginationParams,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<PaginatedResponse<Offer>>;\n\n /** @param clientCreds app-level token to read without a user session. */\n get(id: OfferId, clientCreds?: ClientCredentialsOAuth): Promise<OfferDetail>;\n}\n\n/** @internal */\nexport class ServerOffersNamespaceImpl\n extends OffersNamespaceImpl<ServerNamespaceContext>\n implements ServerOffersNamespace\n{\n override async list(clientCreds?: ClientCredentialsOAuth): Promise<Offer[]> {\n return this.log.wrap('list', clientCreds, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return offersApi.fetchOffers(this.ctx.api, clientCreds);\n });\n }\n\n override async listPage(\n params: PaginationParams,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<PaginatedResponse<Offer>> {\n return this.log.wrap('listPage', { params, clientCreds }, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return offersApi.fetchOffersPage(this.ctx.api, params, clientCreds);\n });\n }\n\n override async get(\n id: OfferId,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<OfferDetail> {\n return this.log.wrap('get', { id, clientCreds }, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return offersApi.fetchOfferDetails(this.ctx.api, id, clientCreds);\n });\n }\n}\n","import type { ServerNamespaceContext } from '@/server/core/shared/server-namespace-context';\nimport * as requirementsApi from '@/universal/api/frontline/requirements';\nimport {\n type RequirementsNamespace,\n RequirementsNamespaceImpl,\n} from '@/universal/core/requirements/requirements-namespace';\nimport type { ClientCredentialsOAuth } from '@/universal/types/client-credentials';\nimport type { OfferId } from '@/universal/types/offer';\nimport type { OfferOptionId } from '@/universal/types/offer-detail';\nimport type { Requirement } from '@/universal/types/requirement';\n\n/**\n * The server-side requirements namespace: the same surface as the shared\n * {@link RequirementsNamespace}, except {@link forOffer} — the offer's\n * requirement definitions are app-level data, so it is additionally callable\n * with a token from `ServerAuthNamespace.clientCredentials`.\n *\n * Everything else is per-user data and needs a user session. `handle` is\n * client-only: there is no browser tab to open on a server.\n */\nexport interface ServerRequirementsNamespace extends RequirementsNamespace {\n /** @param clientCreds app-level token to read without a user session. */\n forOffer(\n offerId: OfferId,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<Record<OfferOptionId, Requirement[]>>;\n}\n\n/** @internal */\nexport class ServerRequirementsNamespaceImpl\n extends RequirementsNamespaceImpl<ServerNamespaceContext>\n implements ServerRequirementsNamespace\n{\n override async forOffer(\n offerId: OfferId,\n clientCreds?: ClientCredentialsOAuth\n ): Promise<Record<OfferOptionId, Requirement[]>> {\n return this.log.wrap('forOffer', { offerId, clientCreds }, async () => {\n await this.ctx.ensureAuthenticated(clientCreds);\n return requirementsApi.fetchOfferRequirements(\n this.ctx.api,\n offerId,\n clientCreds\n );\n });\n }\n}\n","import { ApiClient } from '@/server/core/api/api-client';\nimport {\n type ServerAuthNamespace,\n ServerAuthNamespaceImpl,\n} from '@/server/core/auth/auth-namespace';\nimport { processRefreshCoordinator } from '@/server/core/auth/session-refresh';\nimport type { SessionStore } from '@/server/core/auth/session-store';\nimport {\n type ServerOffersNamespace,\n ServerOffersNamespaceImpl,\n} from '@/server/core/offers/server-offers-namespace';\nimport {\n type ServerRequirementsNamespace,\n ServerRequirementsNamespaceImpl,\n} from '@/server/core/requirements/server-requirements-namespace';\nimport type { ServerNamespaceContext } from '@/server/core/shared/server-namespace-context';\nimport { API_VERSION } from '@/universal/api/frontline/config';\nimport { createNabuApiClient } from '@/universal/api/nabu/client';\nimport { publicClients } from '@/universal/api/rpc/public-clients';\nimport {\n type CentrifugeNamespace,\n CentrifugeNamespaceImpl,\n} from '@/universal/core/providers/centrifuge/centrifuge-namespace';\nimport {\n type CoinListTokenSaleNamespace,\n CoinListTokenSaleNamespaceImpl,\n} from '@/universal/core/providers/coin-list/token-sale-namespace';\nimport {\n type OndoNamespace,\n OndoNamespaceImpl,\n} from '@/universal/core/providers/ondo/ondo-namespace';\nimport {\n type SuperstateSwapNamespace,\n SuperstateSwapNamespaceImpl,\n} from '@/universal/core/providers/superstate/superstate-namespace';\nimport { baseUrls } from '@/universal/core/shared/base-urls';\nimport {\n type Erc20Namespace,\n Erc20NamespaceImpl,\n} from '@/universal/core/shared/blockchain/erc20/erc20-namespace';\nimport {\n type TokensNamespace,\n TokensNamespaceImpl,\n} from '@/universal/core/shared/blockchain/tokens/tokens-namespace';\nimport { systemTime } from '@/universal/core/shared/effects';\nimport {\n type WalletsNamespace,\n WalletsNamespaceImpl,\n} from '@/universal/core/wallets/wallets-namespace';\nimport type { ClientCredentialsOAuth } from '@/universal/types/client-credentials';\nimport type { Config } from '@/universal/types/config';\nimport { NotAuthenticatedError } from '@/universal/types/errors';\nimport type { ClientSecret } from '@/universal/types/oauth';\nimport { Seconds } from '@/universal/types/time';\n\n/** Buffer before expiry to consider token expired for refresh. */\nconst ACCESS_TOKEN_EXPIRY_BUFFER: Seconds = Seconds.of(60);\n\nexport interface ServerConfig extends Config {\n readonly clientSecret: ClientSecret;\n readonly sessionStore: SessionStore;\n /**\n * How many seconds before its expiry an access token is refreshed. Defaults\n * to 60. Refreshing early moves the work to a single request while the\n * current token still works, and a refresh that fails inside this window\n * keeps serving the current token rather than failing the request.\n *\n * Build it with `Seconds.of(n)`, which throws a `ValidationError` unless\n * `n` is a non-negative integer.\n */\n readonly accessTokenExpiryBufferSeconds?: Seconds;\n /**\n * Whether the SDK throws where it would otherwise recover and log a\n * `'warn'` line. Defaults to `false`.\n *\n * Today it governs one thing: a failed token revoke in `auth.logout()`. By\n * default the session is cleared regardless; with `strict` the failure is\n * rethrown and the session is left untouched.\n */\n readonly strict?: boolean;\n}\n\n/**\n * Server-side CoinList SDK client.\n *\n * Functionality is grouped into namespaces — `coinlist.auth.getAccessToken()`,\n * `coinlist.offers.list()`, `coinlist.requirements.statuses(offerId)`.\n *\n * Operates in one of two modes depending on whether {@link SessionStore}\n * includes a `setSession` implementation:\n *\n * - **Writable store** (`setSession` provided) — full functionality: token\n * refresh, `auth.completeOAuth`, and `auth.logout` all work normally.\n *\n * - **Read-only store** (no `setSession`) — token refresh is skipped entirely,\n * meaning no network call is made and no refresh token is consumed.\n * `auth.completeOAuth` and `auth.logout` throw\n * {@link WritableSessionStoreRequiredError}. `auth.getAccessToken` may\n * return an expired token (see its docs). Use this mode in execution\n * contexts that can read the session but cannot write it back, such as\n * Next.js Server Components.\n *\n * Unless a namespace says otherwise, its methods require a user session and\n * throw {@link NotAuthenticatedError} when there is none. When the SDK could\n * not find out whether there is one - CoinList's token endpoint failing, or\n * the network - they throw that underlying error instead: see\n * {@link ServerAuthNamespace.getAccessToken}.\n */\nexport interface CoinListServer {\n /** The OAuth session lifecycle on your backend - see {@link ServerAuthNamespace}. */\n readonly auth: ServerAuthNamespace;\n\n /** The offer catalogue, e.g. `coinlist.offers.list()` - see {@link ServerOffersNamespace}. */\n readonly offers: ServerOffersNamespace;\n\n /** What a user must satisfy to participate - see {@link ServerRequirementsNamespace}. */\n readonly requirements: ServerRequirementsNamespace;\n\n /** Wallet ownership proofs and offer-option bindings - see {@link WalletsNamespace}. */\n readonly wallets: WalletsNamespace;\n\n /** Generic ERC-20 allowance and balance reads - see {@link Erc20Namespace}. */\n readonly erc20: Erc20Namespace;\n\n /** Token-sale participations - see {@link CoinListTokenSaleNamespace}. */\n readonly tokenSale: CoinListTokenSaleNamespace;\n\n /** Superstate swap reads and wallet allow-listing - see {@link SuperstateSwapNamespace}. */\n readonly superstate: SuperstateSwapNamespace;\n\n /** Ondo swap quotes and transaction builders - see {@link OndoNamespace}. */\n readonly ondo: OndoNamespace;\n\n /** Centrifuge vault reads and calldata builders - see {@link CentrifugeNamespace}. */\n readonly centrifuge: CentrifugeNamespace;\n\n /** Public token display metadata - see {@link TokensNamespace}. */\n readonly tokens: TokensNamespace;\n}\n\nclass CoinListServerImpl implements CoinListServer {\n private readonly api: ApiClient;\n readonly auth: ServerAuthNamespace;\n readonly offers: ServerOffersNamespace;\n readonly requirements: ServerRequirementsNamespace;\n readonly wallets: WalletsNamespace;\n readonly erc20: Erc20Namespace;\n readonly tokenSale: CoinListTokenSaleNamespace;\n readonly superstate: SuperstateSwapNamespace;\n readonly ondo: OndoNamespace;\n readonly centrifuge: CentrifugeNamespace;\n readonly tokens: TokensNamespace;\n\n constructor(_config: ServerConfig) {\n const logger = _config.logger ?? null;\n const urls = baseUrls(_config);\n this.api = new ApiClient(\n {\n baseUrl: urls.api,\n xApiVersion: API_VERSION,\n },\n (trigger) => auth.tokenFor(trigger),\n logger\n );\n const auth = new ServerAuthNamespaceImpl(this.api, {\n ..._config,\n accessTokenExpiryBufferSeconds:\n _config.accessTokenExpiryBufferSeconds ?? ACCESS_TOKEN_EXPIRY_BUFFER,\n strict: _config.strict ?? false,\n refreshes: processRefreshCoordinator,\n ...systemTime,\n });\n this.auth = auth;\n\n const ctx: ServerNamespaceContext = {\n api: this.api,\n rpc: publicClients(this.api, _config.rpc, _config.logger ?? null),\n logger,\n ensureUserAuthenticated: () => this.ensureAuthenticated(undefined),\n ensureAuthenticated: (clientCreds) =>\n this.ensureAuthenticated(clientCreds),\n };\n this.offers = new ServerOffersNamespaceImpl(ctx);\n this.requirements = new ServerRequirementsNamespaceImpl(ctx);\n this.wallets = new WalletsNamespaceImpl(ctx);\n this.erc20 = new Erc20NamespaceImpl(ctx);\n this.tokenSale = new CoinListTokenSaleNamespaceImpl(ctx);\n this.superstate = new SuperstateSwapNamespaceImpl(ctx);\n this.ondo = new OndoNamespaceImpl(ctx);\n this.centrifuge = new CentrifugeNamespaceImpl(ctx);\n this.tokens = new TokensNamespaceImpl({\n nabu: createNabuApiClient(urls.tokens, logger),\n logger,\n });\n }\n\n /**\n * An app-level token authenticates a request on its own; without one the\n * caller needs a user session.\n */\n private async ensureAuthenticated(\n clientCreds: ClientCredentialsOAuth | undefined\n ): Promise<void> {\n if (clientCreds) return;\n const token = await this.auth.getAccessToken();\n if (token === null) {\n throw new NotAuthenticatedError();\n }\n }\n}\n\nexport function createCoinListServer(config: ServerConfig): CoinListServer {\n return new CoinListServerImpl(config);\n}\n","import { pino } from 'pino';\nimport {\n loggerOverPino,\n PINO_LEVEL,\n SDK_LOGGER_NAME,\n} from '@/universal/core/shared/observability/pino-logger';\nimport type { Logger, PinoLoggerOptions } from '@/universal/types/logger';\n\n/**\n * A {@link Logger} that writes newline-delimited JSON to stdout through pino.\n *\n * ```ts\n * createCoinListServer({\n * ...config,\n * logger: pinoServerLogger({ isDev: false, level: 'info' }),\n * });\n * ```\n *\n * The Node counterpart of {@link pinoClientLogger}, for a route handler, a BFF\n * or SSR. One line per record, which is what every log aggregator ingests\n * without configuration.\n *\n * ## What it deliberately does not do\n *\n * **No transport, and no `pino-pretty`.** A pino transport runs on a worker\n * thread through `thread-stream`, which every bundler has to be told about\n * (`serverExternalPackages` in Next) and which edge runtimes cannot run at\n * all. Pretty-printing is a pipe the host owns rather than a dependency the\n * SDK takes on their behalf:\n *\n * ```sh\n * node server.js | npx pino-pretty\n * ```\n *\n * **No environment-dependent formatting.** `isDev` decides which levels are\n * legal and nothing else, so a development record and a production record are\n * the same shape - what you debug is what you ship.\n *\n * If you already run pino, or want transports, redaction or `destination`,\n * implement {@link Logger} yourself: it is four methods over an event, and\n * `@opentelemetry/instrumentation-pino` patches the pino module rather than an\n * instance, so trace correlation reaches this logger either way.\n *\n * ## Production\n *\n * **Not advised in production**, the same as everywhere else this seam is\n * documented: the SDK's default answer is to leave {@link Config.logger}\n * undefined outside development, staging and incident reproduction. A backend\n * is the least bad place to disregard that - the records go to your process\n * and your sink rather than to a page the end user can read - but it is still\n * a disregard, and the retention and access questions become yours.\n *\n * If you run one, `isDev: false` restricts you to `'error'`, `'warn'` and\n * `'info'` - `{ isDev: false, level: 'debug' }` does not compile, see\n * {@link PinoLoggerOptions}. Those levels are redacted by construction, which\n * is a mechanism the SDK holds itself to; whether that clears your bar is a\n * judgement the SDK is not in a position to make for you.\n *\n * Holds no module-level state: every call builds its own pino instance, and\n * the level is fixed at construction, so two loggers never interfere. Fixed is\n * this implementation's choice, not the seam's - the SDK re-reads\n * {@link Logger.level} before every log call - so if you want a level you can\n * change at runtime, implement {@link Logger} over your own pino instance\n * rather than calling this.\n */\nexport function pinoServerLogger(options: PinoLoggerOptions): Logger {\n return loggerOverPino(\n // `name` as a child binding rather than pino's `name` option: the option\n // is honoured by pino's node build and silently dropped by its browser\n // build, so a binding is the only spelling that identifies the SDK in\n // both environments.\n pino({\n level: PINO_LEVEL[options.level],\n }).child({ name: SDK_LOGGER_NAME }),\n options.level\n );\n}\n"],"mappings":"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","names":[]}
@@ -19,6 +19,17 @@ type ClientId = Newtype<string, 'ClientId'>;
19
19
  declare const ClientId: (value: string) => ClientId;
20
20
  type ClientSecret = Newtype<string, 'ClientSecret'>;
21
21
  declare const ClientSecret: (value: string) => ClientSecret;
22
+ /**
23
+ * The PKCE pair a finished authorization hands from the browser to the
24
+ * partner's backend: `CoinListClient#auth.completeOAuth` produces it, and
25
+ * `CoinListServer#auth.completeOAuth` exchanges it for a session.
26
+ */
27
+ type AuthorizationGrant = {
28
+ /** The short-lived authorization code parsed out of the redirect URL. */
29
+ code: AuthorizationCode;
30
+ /** The `code_verifier` the browser generated when starting the flow. */
31
+ codeVerifier: CodeVerifier;
32
+ };
22
33
 
23
34
  /**
24
35
  * A classified wallet or transaction failure. The SDK derives this from
@@ -401,6 +412,12 @@ declare const AsyncOfferDetail: {
401
412
  type CoinListTokenSaleOfferDetail = OfferDetailBase & OfferEditorial & {
402
413
  type: 'coinlist::token_sale';
403
414
  logoUrl: string;
415
+ /**
416
+ * Every chain this sale takes participations on, with the contract a
417
+ * participation's approval names as its spender. Empty means it takes
418
+ * participations nowhere.
419
+ */
420
+ fundingContracts: OfferFundingContract[];
404
421
  };
405
422
  type SuperstateSwapOfferDetail = OfferDetailBase & OfferEditorial & {
406
423
  type: 'superstate::swap';
@@ -546,6 +563,19 @@ type OfferVaultContract = {
546
563
  chain: Chain;
547
564
  address: EvmContractAddress;
548
565
  };
566
+ /**
567
+ * A chain a CoinList token sale takes participations on, with the contract a
568
+ * participation's approval names as its spender: the sale pulls the payment
569
+ * through that allowance once it settles.
570
+ *
571
+ * Its own type rather than an {@link OfferVaultContract}, although the shape
572
+ * is the same, because the meaning is not: nothing is sent to this contract
573
+ * at checkout, it is only approved.
574
+ */
575
+ type OfferFundingContract = {
576
+ chain: Chain;
577
+ address: EvmContractAddress;
578
+ };
549
579
 
550
580
  /**
551
581
  * One vault: a share token on one chain. What the status and position reads
@@ -929,7 +959,9 @@ type BuildCentrifugeVaultBuyParams = CentrifugeWalletParams & {
929
959
  /**
930
960
  * Centrifuge vault reads and claims: the vault-wide ordering gate, each
931
961
  * wallet's position per direction, and the calldata that claims a filled
932
- * request.
962
+ * request - e.g. `coinlist.centrifuge.getVaultStatus({ ... })`. Broadcasting a
963
+ * request or a claim needs a wallet, so that half is client-only, on
964
+ * `ClientCentrifugeNamespace`.
933
965
  *
934
966
  * The reads are free to poll - none spends an attestation, so a client may
935
967
  * call them while the user sizes a deposit or waits on a fill.
@@ -1072,8 +1104,11 @@ interface ParticipationsPaginationParams extends PaginationParams {
1072
1104
 
1073
1105
  /**
1074
1106
  * Read/write operations for token sales: listing and reading the current user's
1075
- * participations, and recording a new one. The on-chain execution flow
1076
- * (`executeTokenSale`) is layered on top of this in the client-side namespace.
1107
+ * participations, and recording a new one - e.g. `coinlist.tokenSale.list()`.
1108
+ *
1109
+ * The on-chain purchase, `ClientCoinListTokenSaleNamespace.execute`, is
1110
+ * layered on top of this in the client-side namespace and is not available on
1111
+ * `CoinListServer`, since it needs a wallet to sign with.
1077
1112
  */
1078
1113
  interface CoinListTokenSaleNamespace {
1079
1114
  /**
@@ -1427,7 +1462,10 @@ type BuildOndoSellParams = BuildOndoSwapParamsCore & {
1427
1462
  };
1428
1463
 
1429
1464
  /**
1430
- * Ondo swap reads, plus the write that turns one into a fillable transaction.
1465
+ * Ondo swap reads - whether an asset is tradable right now and what it costs,
1466
+ * e.g. `coinlist.ondo.getQuote({ ... })` - plus the writes that turn a quote
1467
+ * into a fillable transaction. Approving and broadcasting need a wallet, so
1468
+ * they are client-only, on `ClientOndoNamespace`.
1431
1469
  *
1432
1470
  * The two reads are free to poll - neither spends an attestation, so a client
1433
1471
  * may call them while the user edits an order. The two builders are not:
@@ -1590,7 +1628,9 @@ type AllowWalletResponse = {
1590
1628
 
1591
1629
  /**
1592
1630
  * Read/write operations for the on-chain swap flow: quoting a swap, inspecting
1593
- * contract state, and proving/allow-listing wallet ownership.
1631
+ * contract state, and allow-listing a proven wallet - e.g.
1632
+ * `coinlist.superstate.getOutputToken({ contractAddress, chain })`. The
1633
+ * wallet-driven flows are client-only, on `ClientSuperstateSwapNamespace`.
1594
1634
  *
1595
1635
  * Every read here is a contract call the SDK makes itself. By default those go
1596
1636
  * through CoinList's authenticated JSON-RPC proxy, so a logged-out caller gets
@@ -1664,6 +1704,29 @@ type KycToken = {
1664
1704
  token: string;
1665
1705
  };
1666
1706
 
1707
+ type CreateKycTokenParams = {
1708
+ /**
1709
+ * Sumsub verification level to run. Defaults to the backend's standard
1710
+ * level.
1711
+ */
1712
+ levelName?: KycLevelName;
1713
+ /**
1714
+ * Resets the Sumsub applicant first, so an already-approved level can be
1715
+ * executed again (e.g. to update stale PII). Pass the `kycReset` value from
1716
+ * the requirement status, and never on mid-flow token refreshes.
1717
+ */
1718
+ reset?: boolean;
1719
+ };
1720
+ type SubmitDocumentParams = {
1721
+ /** Currently only `tax_certification` (W-8BEN / W-8BEN-E). */
1722
+ documentType: DocumentType;
1723
+ /**
1724
+ * Signing-form values keyed by the document's DocuSeal field names,
1725
+ * forwarded verbatim to pre-fill the document.
1726
+ */
1727
+ fields: Record<string, string>;
1728
+ };
1729
+
1667
1730
  /** Whether the PII belongs to an individual or a company/trust entity. */
1668
1731
  type PiiKind = 'person' | 'company';
1669
1732
  /** ISO 3166-1 alpha-2 country code (e.g. `'US'`). */
@@ -1723,33 +1786,11 @@ type RequirementStatusInfo = {
1723
1786
  kycReset?: boolean;
1724
1787
  };
1725
1788
 
1726
- type CreateKycTokenParams = {
1727
- /**
1728
- * Sumsub verification level to run. Defaults to the backend's standard
1729
- * level.
1730
- */
1731
- levelName?: KycLevelName;
1732
- /**
1733
- * Resets the Sumsub applicant first, so an already-approved level can be
1734
- * executed again (e.g. to update stale PII). Pass the `kycReset` value from
1735
- * the requirement status, and never on mid-flow token refreshes.
1736
- */
1737
- reset?: boolean;
1738
- };
1739
- type SubmitDocumentParams = {
1740
- /** Currently only `tax_certification` (W-8BEN / W-8BEN-E). */
1741
- documentType: DocumentType;
1742
- /**
1743
- * Signing-form values keyed by the document's DocuSeal field names,
1744
- * forwarded verbatim to pre-fill the document.
1745
- */
1746
- fields: Record<string, string>;
1747
- };
1748
1789
  /**
1749
1790
  * Everything a user must satisfy before they can participate in an offer:
1750
1791
  * reading the checklist and its live statuses, and the operations that satisfy
1751
- * individual requirements — identity verification (KYC) and tax-document
1752
- * signing.
1792
+ * individual requirements - identity verification (KYC) and tax-document
1793
+ * signing. E.g. `coinlist.requirements.statuses(offerId)`.
1753
1794
  *
1754
1795
  * Wallet requirements (`external_wallet`, `whitelisted_wallet`) are satisfied
1755
1796
  * through `WalletsNamespace` instead, since wallet proofs are also used
@@ -1877,11 +1918,13 @@ type RemoveOptionAddressParams = {
1877
1918
  /** Binding to remove, as returned by {@link WalletsNamespace.list}. */
1878
1919
  addressId: OfferOptionAddressId;
1879
1920
  };
1921
+
1880
1922
  /**
1881
1923
  * The user's external wallets: proving ownership of one, binding it to an
1882
- * offer option, and managing those bindings.
1924
+ * offer option, and managing those bindings - e.g.
1925
+ * `coinlist.wallets.list({ offerId, offerOptionId })`.
1883
1926
  *
1884
- * Ownership proof is a provider-agnostic primitive — the swap flow uses the
1927
+ * Ownership proof is a provider-agnostic primitive - the swap flow uses the
1885
1928
  * same challenge to allow-list a wallet — so it lives here rather than being
1886
1929
  * duplicated per product namespace.
1887
1930
  *
@@ -1915,11 +1958,12 @@ interface WalletsNamespace {
1915
1958
 
1916
1959
  /**
1917
1960
  * Reads over CoinList's offers: the catalogue a user can browse, and the full
1918
- * detail of a single offer.
1961
+ * detail of a single offer - e.g. `coinlist.offers.list()` or
1962
+ * `coinlist.offers.get(offerId)`.
1919
1963
  *
1920
1964
  * Every method requires an authenticated user and throws
1921
1965
  * {@link NotAuthenticatedError} otherwise. On the server, the same reads are
1922
- * additionally available with an app-level token — see
1966
+ * additionally available with an app-level token - see
1923
1967
  * `ServerOffersNamespace`.
1924
1968
  */
1925
1969
  interface OffersNamespace {
@@ -1956,7 +2000,8 @@ type GetTokenBalanceParams = {
1956
2000
  /**
1957
2001
  * Generic ERC-20 reads shared across on-chain flows (swap, token sale): the
1958
2002
  * allowance an owner has granted a spender, and the raw token balance an owner
1959
- * holds. These are plain token reads, not tied to any single product flow.
2003
+ * holds. These are plain token reads, not tied to any single product flow -
2004
+ * e.g. `coinlist.erc20.getBalance({ ... })`.
1960
2005
  *
1961
2006
  * Read from the chain rather than from a CoinList endpoint. By default that
1962
2007
  * goes through CoinList's authenticated JSON-RPC proxy, so a logged-out caller
@@ -1978,13 +2023,16 @@ interface Erc20Namespace {
1978
2023
  }
1979
2024
 
1980
2025
  /**
1981
- * Token display metadata — name, symbol, decimals, and logos — from
2026
+ * Token display metadata - name, symbol, decimals, and logos - from
1982
2027
  * CoinList's public token registry, keyed by {@link TokenIdentifier} (the
1983
- * same chain + address pairs `Offer.tokens` carries).
2028
+ * same chain + address pairs `Offer.tokens` carries) - e.g.
2029
+ * `coinlist.tokens.get({ chain, address })` for an entry of `Offer.tokens`,
2030
+ * or `coinlist.tokens.list()` for the whole catalogue in one request.
1984
2031
  *
1985
2032
  * Unlike the other namespaces, this one is public: no method requires an
1986
- * authenticated user, and nothing here touches the CoinList API — reads go to
1987
- * the registry's CDN.
2033
+ * authenticated user, and nothing here touches the CoinList API - reads go to
2034
+ * the registry's CDN. On `CoinListServer` it therefore also works with a
2035
+ * read-only `SessionStore`.
1988
2036
  */
1989
2037
  interface TokensNamespace {
1990
2038
  /**
@@ -2558,13 +2606,15 @@ interface Config {
2558
2606
  readonly redirectUri: RedirectUri;
2559
2607
  /**
2560
2608
  * Recommended to leave undefined. Used to change the CoinList environment;
2561
- * default is production.
2609
+ * default is production. An empty string throws a `ValidationError` when
2610
+ * the client is built: omit the field to use the default.
2562
2611
  */
2563
2612
  readonly baseUrl?: string;
2564
2613
  /**
2565
2614
  * Recommended to leave undefined. Overrides the base URL of the public
2566
2615
  * token registry backing `coinlist.tokens`; default is the production
2567
- * registry.
2616
+ * registry. An empty string throws a `ValidationError` when the client is
2617
+ * built: omit the field to use the default.
2568
2618
  */
2569
2619
  readonly tokensBaseUrl?: string;
2570
2620
  /**
@@ -2655,8 +2705,6 @@ interface Config {
2655
2705
  readonly onSessionExpired?: () => Promise<void>;
2656
2706
  }
2657
2707
 
2658
- type ClientCredentialsOAuth = Newtype<OAuthAccessToken, 'ClientCredentialsOAuth'>;
2659
- declare const ClientCredentialsOAuth: (value: OAuthAccessToken) => ClientCredentialsOAuth;
2660
2708
  type OAuthAccessToken = {
2661
2709
  value: string;
2662
2710
  expiresAt: Date;
@@ -2706,4 +2754,4 @@ declare const Seconds: {
2706
2754
  toMillis: (value: Seconds) => Millis;
2707
2755
  };
2708
2756
 
2709
- export { type CentrifugeVaultStatus as $, AuthorizationCode as A, BlockchainAmount as B, CodeVerifier as C, type Offer as D, EthereumChain as E, type TokenMetadata as F, type OfferDetail as G, StablecoinSymbol as H, type Erc20Asset as I, type CoinListTokenSaleOfferDetail as J, CentrifugeVaultLegId as K, type CentrifugeVaultOfferDetail as L, Seconds as M, AsyncOfferDetail as N, type OfferVaultContract as O, type Participation as P, type RequirementStatusInfo as Q, type RequirementsNamespace as R, type SwapQuote as S, type TokensNamespace as T, type KycLevelName as U, type RequirementType as V, type WalletError as W, type RequirementStatusValue as X, RequirementId as Y, type PinoLoggerOptions as Z, type Logger as _, AssetSymbol as a, type GetCentrifugeVaultStatusParams as a$, type CentrifugeVaultRef as a0, type BuildOndoBuyParams as a1, type OndoSwapOfferDetail as a2, type BuildOndoSellParams as a3, type OndoQuote as a4, type OndoQuoteSize as a5, type OndoTradingStatus as a6, AssetDecimals as a7, type SuperstateSwapOfferDetail as a8, CentrifugeVaultLeg as a9, type CentrifugeOrderReason as aA, type CentrifugeOrderability as aB, type CentrifugeRedeemPosition as aC, CentrifugeShares as aD, type CentrifugeVaultClaimProceedsTransaction as aE, type CentrifugeVaultClaimTransaction as aF, type CentrifugeVaultOffer as aG, type CentrifugeVaultReason as aH, type CentrifugeVaultResolution as aI, type CentrifugeVaultTransactionCore as aJ, type CentrifugeWalletParams as aK, Chain as aL, type CoinListTokenSaleOffer as aM, type ConnectExternalWalletParams as aN, type CreateKycTokenParams as aO, type CreateParticipationParams as aP, type CreateWalletOwnershipChallengeParams as aQ, Cursor as aR, type DebugEvent as aS, DecimalString as aT, type DocumentFormType as aU, type DocumentSubmissionStatus as aV, type DocumentType as aW, ETHEREUM_CHAINS as aX, EpochMillis as aY, type FaqItem as aZ, type FrontlineEventId as a_, type CentrifugeVault as aa, OfferOptionAddressId as ab, type DocumentSubmission as ac, type TokenIdentifier as ad, type OAuthSession as ae, ClientCredentialsOAuth as af, type PaginationParams as ag, type PaginatedResponse as ah, ClientSecret as ai, type RequestId as aj, ClientId as ak, RedirectUri as al, CodeChallenge as am, PKCEState as an, type SwapStatus as ao, KnownAssetSymbol as ap, type AllowWalletParams as aq, type AllowWalletResponse as ar, type Asset as as, AssetCode as at, type AsyncOfferType as au, type BaseEvent as av, type BuildCentrifugeVaultBuyParams as aw, type BuildOndoSwapParamsCore as ax, CentrifugeAssets as ay, type CentrifugeDepositPosition as az, EvmContractAddress as b, type TokenLogoImage as b$, type GetOndoQuoteParams as b0, type GetOndoTradingStatusParams as b1, type GetSwapAuthorizationParams as b2, type GetSwapPreviewParams as b3, type GetSwapQuoteParams as b4, type GetTokenAllowanceParams as b5, type GetTokenBalanceParams as b6, HexEncodedTransactionData as b7, Isin as b8, Iso2CountryCode as b9, ParticipationId as bA, type ParticipationStatus as bB, type ParticipationsPaginationParams as bC, PauseFlags as bD, type Pii as bE, type PiiAddress as bF, type PiiJurisdiction as bG, type PiiKind as bH, type ProductionLogLevel as bI, type ReadCentrifugePositionParams as bJ, type RedactedWalletError as bK, type RemoveOptionAddressParams as bL, type RequirementActionNeededReason as bM, type RpcConfig as bN, type RpcFailureReason as bO, SOLANA_CHAINS as bP, STABLE_DECIMALS as bQ, type SafeEvent as bR, type SafeFields as bS, SolanaChain as bT, type SubmitDocumentParams as bU, type SuperstateSwapOffer as bV, type SwapContractRef as bW, type SwapPreview as bX, type TermItem as bY, Ticker as bZ, type TokenLogo as b_, type KycToken as ba, type Link as bb, type ListOptionAddressesParams as bc, type LogBinding as bd, type LogBindings as be, type LogCause as bf, type LogLevel as bg, type LogScope as bh, type LogValue as bi, MAX_ASSET_DECIMALS as bj, MAX_UINT_256 as bk, type Milestone as bl, Millis as bm, type Newtype as bn, OAuthRefreshToken as bo, type OfferBase as bp, type OfferDetailBase as bq, type OfferEditorial as br, type OfferOption as bs, OfferOptionSlug as bt, OfferSlug as bu, type OfferToken as bv, type OfferType as bw, type OndoQuoteDuration as bx, type OndoSellOutcome as by, type OndoSwapOffer as bz, type CentrifugeVaultBuyTransaction as c, TokenLogoUrl as c0, type TokenRole as c1, type Tx as c2, type Uint256 as c3, type UnredactedFields as c4, type WalletOwnershipChallenge as c5, type WalletOwnershipChallengeParamsBase as c6, type WalletProtocol as c7, assertUint256 as c8, isChain as c9, isOfferType as ca, parseUint256 as cb, resolveCentrifugeVaults as cc, type CentrifugeClaimDirection as d, type CentrifugeNamespace as e, OfferId as f, OfferOptionId as g, AssetId as h, type CoinListTokenSaleNamespace as i, type OfferSwapContract as j, type OndoBuyTransaction as k, type OndoSellTransaction as l, type OndoNamespace as m, type OndoSwapTransactionCore as n, type OrderBookSide as o, Bps as p, EvmWalletAddress as q, type SuperstateSwapNamespace as r, type Requirement as s, type WalletChallengeType as t, type OfferOptionAddress as u, type WalletsNamespace as v, type Config as w, type OAuthAccessToken as x, type OffersNamespace as y, type Erc20Namespace as z };
2757
+ export { type CentrifugeVaultStatus as $, type AuthorizationGrant as A, BlockchainAmount as B, type CentrifugeVaultBuyTransaction as C, type Offer as D, EthereumChain as E, type TokenMetadata as F, type OfferDetail as G, type Erc20Asset as H, CentrifugeVaultLegId as I, type CentrifugeVaultOfferDetail as J, Seconds as K, AsyncOfferDetail as L, type RequirementStatusInfo as M, type KycLevelName as N, type OfferVaultContract as O, type Participation as P, type RequirementType as Q, type RequirementsNamespace as R, StablecoinSymbol as S, type TokensNamespace as T, type RequirementStatusValue as U, RequirementId as V, type WalletError as W, type PinoLoggerOptions as X, type Logger as Y, AuthorizationCode as Z, CodeVerifier as _, AssetSymbol as a, type GetCentrifugeVaultStatusParams as a$, type CentrifugeVaultRef as a0, type BuildOndoBuyParams as a1, type OndoSwapOfferDetail as a2, type BuildOndoSellParams as a3, type OndoQuote as a4, type OndoQuoteSize as a5, type OndoTradingStatus as a6, AssetDecimals as a7, type SuperstateSwapOfferDetail as a8, CentrifugeVaultLeg as a9, type CentrifugeOrderReason as aA, type CentrifugeOrderability as aB, type CentrifugeRedeemPosition as aC, CentrifugeShares as aD, type CentrifugeVaultClaimProceedsTransaction as aE, type CentrifugeVaultClaimTransaction as aF, type CentrifugeVaultOffer as aG, type CentrifugeVaultReason as aH, type CentrifugeVaultResolution as aI, type CentrifugeVaultTransactionCore as aJ, type CentrifugeWalletParams as aK, Chain as aL, type CoinListTokenSaleOffer as aM, type ConnectExternalWalletParams as aN, type CreateKycTokenParams as aO, type CreateParticipationParams as aP, type CreateWalletOwnershipChallengeParams as aQ, Cursor as aR, type DebugEvent as aS, DecimalString as aT, type DocumentFormType as aU, type DocumentSubmissionStatus as aV, type DocumentType as aW, ETHEREUM_CHAINS as aX, EpochMillis as aY, type FaqItem as aZ, type FrontlineEventId as a_, type CentrifugeVault as aa, OfferOptionAddressId as ab, type DocumentSubmission as ac, type TokenIdentifier as ad, type OAuthSession as ae, type PaginationParams as af, type PaginatedResponse as ag, ClientSecret as ah, ClientId as ai, RedirectUri as aj, CodeChallenge as ak, PKCEState as al, type RequestId as am, type SwapStatus as an, KnownAssetSymbol as ao, type AllowWalletParams as ap, type AllowWalletResponse as aq, type Asset as ar, AssetCode as as, AssetId as at, type AsyncOfferType as au, type BaseEvent as av, type BuildCentrifugeVaultBuyParams as aw, type BuildOndoSwapParamsCore as ax, CentrifugeAssets as ay, type CentrifugeDepositPosition as az, EvmContractAddress as b, type TokenLogo as b$, type GetOndoQuoteParams as b0, type GetOndoTradingStatusParams as b1, type GetSwapAuthorizationParams as b2, type GetSwapPreviewParams as b3, type GetSwapQuoteParams as b4, type GetTokenAllowanceParams as b5, type GetTokenBalanceParams as b6, HexEncodedTransactionData as b7, Isin as b8, Iso2CountryCode as b9, type OndoSwapOffer as bA, ParticipationId as bB, type ParticipationStatus as bC, type ParticipationsPaginationParams as bD, PauseFlags as bE, type Pii as bF, type PiiAddress as bG, type PiiJurisdiction as bH, type PiiKind as bI, type ProductionLogLevel as bJ, type ReadCentrifugePositionParams as bK, type RedactedWalletError as bL, type RemoveOptionAddressParams as bM, type RequirementActionNeededReason as bN, type RpcConfig as bO, type RpcFailureReason as bP, SOLANA_CHAINS as bQ, STABLE_DECIMALS as bR, type SafeEvent as bS, type SafeFields as bT, SolanaChain as bU, type SubmitDocumentParams as bV, type SuperstateSwapOffer as bW, type SwapContractRef as bX, type SwapPreview as bY, type TermItem as bZ, Ticker as b_, type KycToken as ba, type Link as bb, type ListOptionAddressesParams as bc, type LogBinding as bd, type LogBindings as be, type LogCause as bf, type LogLevel as bg, type LogScope as bh, type LogValue as bi, MAX_ASSET_DECIMALS as bj, MAX_UINT_256 as bk, type Milestone as bl, Millis as bm, type Newtype as bn, OAuthRefreshToken as bo, type OfferBase as bp, type OfferDetailBase as bq, type OfferEditorial as br, type OfferFundingContract as bs, type OfferOption as bt, OfferOptionSlug as bu, OfferSlug as bv, type OfferToken as bw, type OfferType as bx, type OndoQuoteDuration as by, type OndoSellOutcome as bz, type CentrifugeClaimDirection as c, type TokenLogoImage as c0, TokenLogoUrl as c1, type TokenRole as c2, type Tx as c3, type Uint256 as c4, type UnredactedFields as c5, type WalletOwnershipChallenge as c6, type WalletOwnershipChallengeParamsBase as c7, type WalletProtocol as c8, assertUint256 as c9, isChain as ca, isOfferType as cb, parseUint256 as cc, resolveCentrifugeVaults as cd, type CentrifugeNamespace as d, type CoinListTokenSaleOfferDetail as e, OfferOptionId as f, type CoinListTokenSaleNamespace as g, type OfferSwapContract as h, type OndoBuyTransaction as i, type OndoSellTransaction as j, type OndoNamespace as k, type OndoSwapTransactionCore as l, type OrderBookSide as m, OfferId as n, type SwapQuote as o, Bps as p, EvmWalletAddress as q, type SuperstateSwapNamespace as r, type Requirement as s, type WalletChallengeType as t, type OfferOptionAddress as u, type WalletsNamespace as v, type Config as w, type OAuthAccessToken as x, type OffersNamespace as y, type Erc20Namespace as z };
@@ -1,4 +1,4 @@
1
- import { q as EvmWalletAddress, E as EthereumChain, c2 as Tx, bn as Newtype, F as TokenMetadata, aL as Chain, b as EvmContractAddress, bv as OfferToken } from './time-DkU7jivQ.js';
1
+ import { q as EvmWalletAddress, E as EthereumChain, c3 as Tx, bn as Newtype, F as TokenMetadata, aL as Chain, b as EvmContractAddress, bw as OfferToken } from './time-BgR3LVER.js';
2
2
  import { Hex, Abi, Hash, TransactionReceipt } from 'viem';
3
3
 
4
4
  type WriteContractParams = {