@coinlist-co/react 0.12.2-rc.726778a → 0.12.2-rc.9fb6364

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.
Files changed (32) hide show
  1. package/dist/{chunk-IRV5YU42.js → chunk-BNMF2HMW.js} +98 -2
  2. package/dist/chunk-BNMF2HMW.js.map +1 -0
  3. package/dist/{chunk-3UQPCAU4.js → chunk-KRA6EM2L.js} +2 -2
  4. package/dist/{chunk-C6F5FOFP.js → chunk-STAHYDLY.js} +52 -19
  5. package/dist/chunk-STAHYDLY.js.map +1 -0
  6. package/dist/client/index.cjs +287 -53
  7. package/dist/client/index.cjs.map +1 -1
  8. package/dist/client/index.d.cts +26 -8
  9. package/dist/client/index.d.ts +26 -8
  10. package/dist/client/index.js +182 -39
  11. package/dist/client/index.js.map +1 -1
  12. package/dist/server/index.cjs +435 -76
  13. package/dist/server/index.cjs.map +1 -1
  14. package/dist/server/index.d.cts +47 -38
  15. package/dist/server/index.d.ts +47 -38
  16. package/dist/server/index.js +316 -61
  17. package/dist/server/index.js.map +1 -1
  18. package/dist/time-B5YOQxuh.d.ts +50 -0
  19. package/dist/time-BY3apBpc.d.cts +50 -0
  20. package/dist/{token-registry-snapshot-BNBNuDu1.d.ts → token-registry-snapshot-C1NxdBmF.d.ts} +1 -1
  21. package/dist/{token-registry-snapshot-CqI6dXM_.d.cts → token-registry-snapshot-DyjrA32p.d.cts} +1 -1
  22. package/dist/{tokens-namespace-CIWBtgt2.d.cts → tokens-namespace-BJfFeowE.d.cts} +53 -1
  23. package/dist/{tokens-namespace-CIWBtgt2.d.ts → tokens-namespace-BJfFeowE.d.ts} +53 -1
  24. package/dist/universal/index.cjs +83 -1
  25. package/dist/universal/index.cjs.map +1 -1
  26. package/dist/universal/index.d.cts +5 -4
  27. package/dist/universal/index.d.ts +5 -4
  28. package/dist/universal/index.js +10 -2
  29. package/package.json +1 -1
  30. package/dist/chunk-C6F5FOFP.js.map +0 -1
  31. package/dist/chunk-IRV5YU42.js.map +0 -1
  32. /package/dist/{chunk-3UQPCAU4.js.map → chunk-KRA6EM2L.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ButtonHTMLAttributes } from 'react';
3
- import { A as AuthorizationCode, C as CodeVerifier, L as Logger, W as WalletError, E as Erc20Namespace, a as EthereumChain, b as EvmContractAddress, B as BlockchainAmount, c as CoinListTokenSaleNamespace, O as OfferId, d as OfferOptionId, e as AssetId, P as Participation, f as CoinListTokenSaleNamespaceImpl, U as UniversalNamespaceContext, g as OndoNamespace, h as AssetSymbol, i as OfferSwapContract, j as OndoBuyTransaction, k as OndoSellTransaction, l as OndoSwapTransactionCore, m as OrderBookSide, n as OndoNamespaceImpl, S as SuperstateSwapNamespace, o as WalletsNamespace, p as Bps, q as EvmWalletAddress, r as SuperstateSwapNamespaceImpl, R as RequirementsNamespace, s as Requirement, t as RequirementsNamespaceImpl, u as Config, v as OAuthAccessToken, w as OffersNamespace, T as TokensNamespace, x as StablecoinSymbol, y as Erc20Asset, z as OfferDetail, D as TokenMetadata, F as OfferType, G as OndoSwapOfferDetail, H as CoinListTokenSaleOfferDetail, I as SuperstateSwapOfferDetail, J as Offer, K as RequirementStatusInfo, M as RequirementType, N as RequirementStatusValue, Q as OfferOptionAddress, V as RequirementId, X as KycLevelName, Y as DocumentSubmission, Z as WalletChallengeType, _ as PinoLoggerOptions, $ as OndoTradingStatus, a0 as AssetDecimals, a1 as BuildOndoBuyParams, a2 as BuildOndoSellParams, a3 as OndoQuote, a4 as OndoQuoteSize, a5 as TokenIdentifier, a6 as OfferOptionAddressId, a7 as TokenLogo } from '../tokens-namespace-CIWBtgt2.cjs';
4
- export { a8 as DebugEvent, a9 as FrontlineEventId, aa as HttpError, ab as HttpResponse, ac as KycToken, ad as LogBinding, ae as LogBindings, af as LogCause, ag as LogLevel, ah as LogScope, ai as LogValue, aj as ProductionLogLevel, ak as RedactedWalletError, al as RequestId, am as RpcFailureReason, an as SafeEvent, ao as SafeFields, ap as UnredactedFields } from '../tokens-namespace-CIWBtgt2.cjs';
5
- import { P as PKCEConfig, E as EvmWallet, S as SwapQuote, A as AssetIconUrl, a as ShortenedWalletAddress, F as FormattedAmountAssetUi, b as FormattedPercentUi, T as TxExplorerUrl, C as ConnectWallet, N as NonEmptyArray, c as TokenRegistrySnapshot } from '../token-registry-snapshot-CqI6dXM_.cjs';
6
- export { B as BroadcastTxParams, d as EvmSigner, W as WriteContractParams } from '../token-registry-snapshot-CqI6dXM_.cjs';
3
+ import { A as AuthorizationCode, C as CodeVerifier, L as Logger, W as WalletError, E as Erc20Namespace, a as EthereumChain, b as EvmContractAddress, B as BlockchainAmount, c as CoinListTokenSaleNamespace, O as OfferId, d as OfferOptionId, e as AssetId, P as Participation, f as CoinListTokenSaleNamespaceImpl, U as UniversalNamespaceContext, g as OndoNamespace, h as AssetSymbol, i as OfferSwapContract, j as OndoBuyTransaction, k as OndoSellTransaction, l as OndoSwapTransactionCore, m as OrderBookSide, n as OndoNamespaceImpl, S as SuperstateSwapNamespace, o as WalletsNamespace, p as Bps, q as EvmWalletAddress, r as SuperstateSwapNamespaceImpl, R as RequirementsNamespace, s as Requirement, t as RequirementsNamespaceImpl, u as Config, v as OAuthAccessToken, w as OffersNamespace, T as TokensNamespace, x as StablecoinSymbol, y as Erc20Asset, z as OfferDetail, D as TokenMetadata, F as OfferType, G as OndoSwapOfferDetail, H as CoinListTokenSaleOfferDetail, I as SuperstateSwapOfferDetail, J as Offer, K as RequirementStatusInfo, M as RequirementType, N as RequirementStatusValue, Q as OfferOptionAddress, V as RequirementId, X as KycLevelName, Y as DocumentSubmission, Z as WalletChallengeType, _ as PinoLoggerOptions, $ as OndoTradingStatus, a0 as AssetDecimals, a1 as BuildOndoBuyParams, a2 as BuildOndoSellParams, a3 as OndoQuote, a4 as OndoQuoteSize, a5 as TokenIdentifier, a6 as OfferOptionAddressId, a7 as TokenLogo } from '../tokens-namespace-BJfFeowE.cjs';
4
+ export { a8 as DebugEvent, a9 as FrontlineEventId, aa as HttpError, ab as HttpResponse, ac as KycToken, ad as LogBinding, ae as LogBindings, af as LogCause, ag as LogLevel, ah as LogScope, ai as LogValue, aj as ProductionLogLevel, ak as RedactedWalletError, al as RequestId, am as RpcFailureReason, an as SafeEvent, ao as SafeFields, ap as UnredactedFields } from '../tokens-namespace-BJfFeowE.cjs';
5
+ import { P as PKCEConfig, E as EvmWallet, S as SwapQuote, A as AssetIconUrl, a as ShortenedWalletAddress, F as FormattedAmountAssetUi, b as FormattedPercentUi, T as TxExplorerUrl, C as ConnectWallet, N as NonEmptyArray, c as TokenRegistrySnapshot } from '../token-registry-snapshot-DyjrA32p.cjs';
6
+ export { B as BroadcastTxParams, d as EvmSigner, W as WriteContractParams } from '../token-registry-snapshot-DyjrA32p.cjs';
7
7
  import { Hash } from 'viem';
8
8
  import * as class_variance_authority_types from 'class-variance-authority/types';
9
9
  import { VariantProps } from 'class-variance-authority';
@@ -698,10 +698,24 @@ declare class ClientSupportNamespaceImpl implements ClientSupportNamespace {
698
698
 
699
699
  interface ClientConfig extends Config {
700
700
  /**
701
- * The SDK user must provide this function. It is responsible for fetching a
702
- * valid {@link OAuthAccessToken}, or **null** for logged-out users. If your
703
- * backend is in TypeScript, it is recommended to use
704
- * `CoinListServer#auth.getAccessToken` from `@/server/core/coinlist-server`.
701
+ * The SDK user must provide this function. It fetches a valid
702
+ * {@link OAuthAccessToken} from your backend. If your backend is in
703
+ * TypeScript, serve `CoinListServer#auth.getAccessToken` from it.
704
+ *
705
+ * Two answers, and they must not be confused:
706
+ *
707
+ * - **Resolve `null`** when there is no session - e.g. your endpoint
708
+ * answered 204. After a 401 the SDK asks once more a second later before
709
+ * it believes it, then signs the user out and calls
710
+ * {@link Config.onSessionExpired}.
711
+ * - **Throw** when you could not find out - a 5xx, a network failure. The
712
+ * SDK keeps its current auth state and fails only the request in hand.
713
+ *
714
+ * So a backend that answers "no session" for its own failures turns every
715
+ * outage into a logout. Map those to an error status and throw on it.
716
+ *
717
+ * The SDK calls this at most once at a time: concurrent requests that all
718
+ * need a new token share one call.
705
719
  */
706
720
  getAccessToken: () => Promise<OAuthAccessToken | null>;
707
721
  /**
@@ -810,6 +824,10 @@ interface CoinListClient {
810
824
  * the user's {@link AuthState}. This is called automatically by
811
825
  * {@link CoinListProvider}; you typically do not need to call it yourself
812
826
  * unless you are using the client outside of the provider.
827
+ *
828
+ * @throws whatever {@link ClientConfig.getAccessToken} throws. The auth
829
+ * state is then left as it was (`'unknown'` on a first call), since a
830
+ * failure to find out is not a logout.
813
831
  */
814
832
  init(): Promise<AuthState>;
815
833
  }
@@ -1,9 +1,9 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ButtonHTMLAttributes } from 'react';
3
- import { A as AuthorizationCode, C as CodeVerifier, L as Logger, W as WalletError, E as Erc20Namespace, a as EthereumChain, b as EvmContractAddress, B as BlockchainAmount, c as CoinListTokenSaleNamespace, O as OfferId, d as OfferOptionId, e as AssetId, P as Participation, f as CoinListTokenSaleNamespaceImpl, U as UniversalNamespaceContext, g as OndoNamespace, h as AssetSymbol, i as OfferSwapContract, j as OndoBuyTransaction, k as OndoSellTransaction, l as OndoSwapTransactionCore, m as OrderBookSide, n as OndoNamespaceImpl, S as SuperstateSwapNamespace, o as WalletsNamespace, p as Bps, q as EvmWalletAddress, r as SuperstateSwapNamespaceImpl, R as RequirementsNamespace, s as Requirement, t as RequirementsNamespaceImpl, u as Config, v as OAuthAccessToken, w as OffersNamespace, T as TokensNamespace, x as StablecoinSymbol, y as Erc20Asset, z as OfferDetail, D as TokenMetadata, F as OfferType, G as OndoSwapOfferDetail, H as CoinListTokenSaleOfferDetail, I as SuperstateSwapOfferDetail, J as Offer, K as RequirementStatusInfo, M as RequirementType, N as RequirementStatusValue, Q as OfferOptionAddress, V as RequirementId, X as KycLevelName, Y as DocumentSubmission, Z as WalletChallengeType, _ as PinoLoggerOptions, $ as OndoTradingStatus, a0 as AssetDecimals, a1 as BuildOndoBuyParams, a2 as BuildOndoSellParams, a3 as OndoQuote, a4 as OndoQuoteSize, a5 as TokenIdentifier, a6 as OfferOptionAddressId, a7 as TokenLogo } from '../tokens-namespace-CIWBtgt2.js';
4
- export { a8 as DebugEvent, a9 as FrontlineEventId, aa as HttpError, ab as HttpResponse, ac as KycToken, ad as LogBinding, ae as LogBindings, af as LogCause, ag as LogLevel, ah as LogScope, ai as LogValue, aj as ProductionLogLevel, ak as RedactedWalletError, al as RequestId, am as RpcFailureReason, an as SafeEvent, ao as SafeFields, ap as UnredactedFields } from '../tokens-namespace-CIWBtgt2.js';
5
- import { P as PKCEConfig, E as EvmWallet, S as SwapQuote, A as AssetIconUrl, a as ShortenedWalletAddress, F as FormattedAmountAssetUi, b as FormattedPercentUi, T as TxExplorerUrl, C as ConnectWallet, N as NonEmptyArray, c as TokenRegistrySnapshot } from '../token-registry-snapshot-BNBNuDu1.js';
6
- export { B as BroadcastTxParams, d as EvmSigner, W as WriteContractParams } from '../token-registry-snapshot-BNBNuDu1.js';
3
+ import { A as AuthorizationCode, C as CodeVerifier, L as Logger, W as WalletError, E as Erc20Namespace, a as EthereumChain, b as EvmContractAddress, B as BlockchainAmount, c as CoinListTokenSaleNamespace, O as OfferId, d as OfferOptionId, e as AssetId, P as Participation, f as CoinListTokenSaleNamespaceImpl, U as UniversalNamespaceContext, g as OndoNamespace, h as AssetSymbol, i as OfferSwapContract, j as OndoBuyTransaction, k as OndoSellTransaction, l as OndoSwapTransactionCore, m as OrderBookSide, n as OndoNamespaceImpl, S as SuperstateSwapNamespace, o as WalletsNamespace, p as Bps, q as EvmWalletAddress, r as SuperstateSwapNamespaceImpl, R as RequirementsNamespace, s as Requirement, t as RequirementsNamespaceImpl, u as Config, v as OAuthAccessToken, w as OffersNamespace, T as TokensNamespace, x as StablecoinSymbol, y as Erc20Asset, z as OfferDetail, D as TokenMetadata, F as OfferType, G as OndoSwapOfferDetail, H as CoinListTokenSaleOfferDetail, I as SuperstateSwapOfferDetail, J as Offer, K as RequirementStatusInfo, M as RequirementType, N as RequirementStatusValue, Q as OfferOptionAddress, V as RequirementId, X as KycLevelName, Y as DocumentSubmission, Z as WalletChallengeType, _ as PinoLoggerOptions, $ as OndoTradingStatus, a0 as AssetDecimals, a1 as BuildOndoBuyParams, a2 as BuildOndoSellParams, a3 as OndoQuote, a4 as OndoQuoteSize, a5 as TokenIdentifier, a6 as OfferOptionAddressId, a7 as TokenLogo } from '../tokens-namespace-BJfFeowE.js';
4
+ export { a8 as DebugEvent, a9 as FrontlineEventId, aa as HttpError, ab as HttpResponse, ac as KycToken, ad as LogBinding, ae as LogBindings, af as LogCause, ag as LogLevel, ah as LogScope, ai as LogValue, aj as ProductionLogLevel, ak as RedactedWalletError, al as RequestId, am as RpcFailureReason, an as SafeEvent, ao as SafeFields, ap as UnredactedFields } from '../tokens-namespace-BJfFeowE.js';
5
+ import { P as PKCEConfig, E as EvmWallet, S as SwapQuote, A as AssetIconUrl, a as ShortenedWalletAddress, F as FormattedAmountAssetUi, b as FormattedPercentUi, T as TxExplorerUrl, C as ConnectWallet, N as NonEmptyArray, c as TokenRegistrySnapshot } from '../token-registry-snapshot-C1NxdBmF.js';
6
+ export { B as BroadcastTxParams, d as EvmSigner, W as WriteContractParams } from '../token-registry-snapshot-C1NxdBmF.js';
7
7
  import { Hash } from 'viem';
8
8
  import * as class_variance_authority_types from 'class-variance-authority/types';
9
9
  import { VariantProps } from 'class-variance-authority';
@@ -698,10 +698,24 @@ declare class ClientSupportNamespaceImpl implements ClientSupportNamespace {
698
698
 
699
699
  interface ClientConfig extends Config {
700
700
  /**
701
- * The SDK user must provide this function. It is responsible for fetching a
702
- * valid {@link OAuthAccessToken}, or **null** for logged-out users. If your
703
- * backend is in TypeScript, it is recommended to use
704
- * `CoinListServer#auth.getAccessToken` from `@/server/core/coinlist-server`.
701
+ * The SDK user must provide this function. It fetches a valid
702
+ * {@link OAuthAccessToken} from your backend. If your backend is in
703
+ * TypeScript, serve `CoinListServer#auth.getAccessToken` from it.
704
+ *
705
+ * Two answers, and they must not be confused:
706
+ *
707
+ * - **Resolve `null`** when there is no session - e.g. your endpoint
708
+ * answered 204. After a 401 the SDK asks once more a second later before
709
+ * it believes it, then signs the user out and calls
710
+ * {@link Config.onSessionExpired}.
711
+ * - **Throw** when you could not find out - a 5xx, a network failure. The
712
+ * SDK keeps its current auth state and fails only the request in hand.
713
+ *
714
+ * So a backend that answers "no session" for its own failures turns every
715
+ * outage into a logout. Map those to an error status and throw on it.
716
+ *
717
+ * The SDK calls this at most once at a time: concurrent requests that all
718
+ * need a new token share one call.
705
719
  */
706
720
  getAccessToken: () => Promise<OAuthAccessToken | null>;
707
721
  /**
@@ -810,6 +824,10 @@ interface CoinListClient {
810
824
  * the user's {@link AuthState}. This is called automatically by
811
825
  * {@link CoinListProvider}; you typically do not need to call it yourself
812
826
  * unless you are using the client outside of the provider.
827
+ *
828
+ * @throws whatever {@link ClientConfig.getAccessToken} throws. The auth
829
+ * state is then left as it was (`'unknown'` on a first call), since a
830
+ * failure to find out is not a logout.
813
831
  */
814
832
  init(): Promise<AuthState>;
815
833
  }
@@ -4,8 +4,10 @@ import {
4
4
  PINO_LEVEL,
5
5
  SDK_LOGGER_NAME,
6
6
  loggerOverPino,
7
- publicClients
8
- } from "../chunk-C6F5FOFP.js";
7
+ notifySessionExpired,
8
+ publicClients,
9
+ systemDelay
10
+ } from "../chunk-STAHYDLY.js";
9
11
  import {
10
12
  AuthorizationCode,
11
13
  CodeVerifier,
@@ -39,7 +41,7 @@ import {
39
41
  ondoQuoteOfferDeadline,
40
42
  superstateSwapContractAddress,
41
43
  swapSpender
42
- } from "../chunk-3UQPCAU4.js";
44
+ } from "../chunk-KRA6EM2L.js";
43
45
  import {
44
46
  API_VERSION,
45
47
  AssetDecimals,
@@ -58,6 +60,7 @@ import {
58
60
  HEADER_USER_AGENT,
59
61
  HttpError,
60
62
  KycToken,
63
+ Millis,
61
64
  NA_AMOUNT_ASSET_UI,
62
65
  NotAuthenticatedError,
63
66
  OAUTH_PAGE_PATH,
@@ -67,6 +70,7 @@ import {
67
70
  RequirementsNamespaceImpl,
68
71
  SUPERSTATE_SWAP_ABI,
69
72
  SUPPORT_NEW_TICKET_URL,
73
+ SentWith,
70
74
  SuperstateSwapNamespaceImpl,
71
75
  TokensNamespaceImpl,
72
76
  VERIFY_IDENTITY_ACCREDITATION_PATH,
@@ -85,11 +89,12 @@ import {
85
89
  getNetworkName,
86
90
  getUUIDv4,
87
91
  internalLogger,
92
+ isRejectedToken,
88
93
  parseBlockchainAmount,
89
94
  shortenAddress,
90
95
  txExplorerUrl,
91
96
  usdAmount
92
- } from "../chunk-IRV5YU42.js";
97
+ } from "../chunk-BNMF2HMW.js";
93
98
 
94
99
  // src/client/CoinListProvider.tsx
95
100
  import {
@@ -149,6 +154,159 @@ var ApiClient = class {
149
154
  }
150
155
  };
151
156
 
157
+ // src/client/core/auth/client-access-token.ts
158
+ var CONFIRM_DELAY = Millis.of(1e3);
159
+ var Generation = {
160
+ initial: 0,
161
+ next: (generation) => generation + 1
162
+ };
163
+ var ClientAccessToken = class {
164
+ constructor(env) {
165
+ this.env = env;
166
+ this.state = { type: "unknown" };
167
+ this.hostCall = null;
168
+ /**
169
+ * Bumped by {@link clear}. A host call that started before a logout must not
170
+ * log the user back in when it lands, so every write checks it first. A 401
171
+ * that arrives after the logout never starts one: `'logged-out'` refuses it.
172
+ */
173
+ this.generation = Generation.initial;
174
+ this.log = internalLogger(env.logger, "AUTH").child({
175
+ op: "getAccessToken"
176
+ });
177
+ }
178
+ authState() {
179
+ return this.state.type;
180
+ }
181
+ /**
182
+ * Asks the host for the current token, whatever is cached.
183
+ *
184
+ * Never answers from a host call already in flight: one started before a
185
+ * login (the mount's `init()`, racing the OAuth callback's) would hand back
186
+ * the answer from before the session cookie existed. It waits for that call
187
+ * to settle instead, so that call's verdict still lands, then asks afresh.
188
+ * A logout while it waits still wins.
189
+ *
190
+ * @throws whatever the host's `getAccessToken` throws.
191
+ */
192
+ async load() {
193
+ const generation = this.generation;
194
+ if (this.hostCall !== null) {
195
+ await this.hostCall.catch(() => null);
196
+ }
197
+ await this.ask(SentWith.noUserToken, generation);
198
+ return this.authState();
199
+ }
200
+ /**
201
+ * The HTTP pipeline's token source: see {@link TokenTrigger}.
202
+ *
203
+ * @throws whatever the host's `getAccessToken` throws.
204
+ */
205
+ async get(trigger) {
206
+ const decision = decideToken(this.state, trigger);
207
+ switch (decision.type) {
208
+ case "serve":
209
+ return decision.token;
210
+ case "ask":
211
+ return this.ask(decision.rejected, this.generation);
212
+ }
213
+ }
214
+ /** Forgets the token: `auth.logout()`. Never an expiry, so never notifies. */
215
+ clear() {
216
+ this.generation = Generation.next(this.generation);
217
+ this.hostCall = null;
218
+ this.state = { type: "logged-out" };
219
+ }
220
+ /**
221
+ * A request that carried no user token was not refused for a token, so the
222
+ * host's first answer stands. One that was is confirmed before it is
223
+ * believed.
224
+ */
225
+ async ask(rejected, generation) {
226
+ if (generation !== this.generation) {
227
+ return currentToken(this.state);
228
+ }
229
+ const first = await this.callHost();
230
+ switch (rejected.type) {
231
+ case "no-user-token":
232
+ return this.commit(first, generation);
233
+ case "user-token":
234
+ return isFresh(first, rejected) ? this.commit(first, generation) : this.confirm(rejected, generation);
235
+ }
236
+ }
237
+ async confirm(rejected, generation) {
238
+ this.log.warn(() => ({
239
+ msg: "host returned no fresh token; asking again before signing out"
240
+ }));
241
+ await this.env.delay(CONFIRM_DELAY);
242
+ const second = await this.callHost();
243
+ return isFresh(second, rejected) ? this.commit(second, generation) : this.expire(generation);
244
+ }
245
+ /** The host's `getAccessToken`, joining a call already in flight. */
246
+ callHost() {
247
+ if (this.hostCall !== null) {
248
+ this.log.debug(() => ({ msg: "joined a host call already in flight" }));
249
+ return this.hostCall;
250
+ }
251
+ const call = (async () => this.env.getAccessToken())().finally(() => {
252
+ if (this.hostCall === call) {
253
+ this.hostCall = null;
254
+ }
255
+ });
256
+ this.hostCall = call;
257
+ return call;
258
+ }
259
+ commit(token, generation) {
260
+ if (generation !== this.generation) {
261
+ return currentToken(this.state);
262
+ }
263
+ this.state = token === null ? { type: "logged-out" } : { type: "logged-in", token };
264
+ return token;
265
+ }
266
+ expire(generation) {
267
+ if (generation !== this.generation) {
268
+ return null;
269
+ }
270
+ const wasLoggedIn = this.state.type === "logged-in";
271
+ this.state = { type: "logged-out" };
272
+ if (wasLoggedIn) {
273
+ this.log.info(() => ({ msg: "session expired" }));
274
+ notifySessionExpired(this.env.onSessionExpired, this.log);
275
+ }
276
+ return null;
277
+ }
278
+ };
279
+ function decideToken(state, trigger) {
280
+ switch (trigger.type) {
281
+ case "before-request":
282
+ return state.type === "unknown" ? { type: "ask", rejected: SentWith.noUserToken } : { type: "serve", token: currentToken(state) };
283
+ case "after-401":
284
+ switch (state.type) {
285
+ // A logout or an expiry verdict: only `load()` may log back in, so a
286
+ // 401 to a request sent before it must not ask the host again.
287
+ case "logged-out":
288
+ return { type: "serve", token: null };
289
+ // Another request may already have renewed since this one was sent.
290
+ case "logged-in":
291
+ return isRejectedToken(state.token, trigger.rejected) ? { type: "ask", rejected: trigger.rejected } : { type: "serve", token: state.token };
292
+ case "unknown":
293
+ return { type: "ask", rejected: trigger.rejected };
294
+ }
295
+ }
296
+ }
297
+ function currentToken(state) {
298
+ switch (state.type) {
299
+ case "logged-in":
300
+ return state.token;
301
+ case "unknown":
302
+ case "logged-out":
303
+ return null;
304
+ }
305
+ }
306
+ function isFresh(token, rejected) {
307
+ return token !== null && !isRejectedToken(token, rejected);
308
+ }
309
+
152
310
  // src/client/core/auth/client-auth-namespace.ts
153
311
  var OAUTH_STATE_KEY = "coinlist.oauth_state";
154
312
  var OAUTH_CODE_VERIFIER_KEY = "coinlist.oauth_code_verifier";
@@ -963,35 +1121,38 @@ var ClientSupportNamespaceImpl = class {
963
1121
 
964
1122
  // src/client/core/coinlist-client.ts
965
1123
  var CoinListClientImpl = class {
966
- constructor(_config) {
967
- this._config = _config;
968
- /** Cached access token: undefined = loading, null = no token, AccessToken = logged in */
969
- this._accessToken = void 0;
970
- const logger = _config.logger ?? null;
1124
+ constructor(config) {
1125
+ this.config = config;
1126
+ const logger = config.logger ?? null;
1127
+ const accessToken = new ClientAccessToken({
1128
+ getAccessToken: () => config.getAccessToken(),
1129
+ onSessionExpired: config.onSessionExpired,
1130
+ logger,
1131
+ ...systemDelay
1132
+ });
1133
+ this.accessToken = accessToken;
971
1134
  this.api = new ApiClient(
972
1135
  {
973
- baseUrl: _config.baseUrl || PUBLIC_API_BASE_URL,
1136
+ baseUrl: config.baseUrl || PUBLIC_API_BASE_URL,
974
1137
  xApiVersion: API_VERSION
975
1138
  },
976
- this.fetchAccessToken.bind(this),
1139
+ (trigger) => accessToken.get(trigger),
977
1140
  logger
978
1141
  );
979
1142
  const ctx = {
980
1143
  api: this.api,
981
- rpc: publicClients(this.api, _config.rpc, _config.logger ?? null),
1144
+ rpc: publicClients(this.api, config.rpc, config.logger ?? null),
982
1145
  logger,
983
1146
  ensureUserAuthenticated: async () => this.ensureAuthenticated()
984
1147
  };
985
- this.auth = new ClientAuthNamespaceImpl(_config, {
986
- getState: () => this.getAuthState(),
987
- clearAccessToken: () => {
988
- this._accessToken = null;
989
- }
1148
+ this.auth = new ClientAuthNamespaceImpl(config, {
1149
+ getState: () => accessToken.authState(),
1150
+ clearAccessToken: () => accessToken.clear()
990
1151
  });
991
1152
  this.offers = new OffersNamespaceImpl(ctx);
992
1153
  this.requirements = new ClientRequirementsNamespaceImpl(
993
1154
  ctx,
994
- _config.coinlistBaseUrl
1155
+ config.coinlistBaseUrl
995
1156
  );
996
1157
  this.wallets = new WalletsNamespaceImpl(ctx);
997
1158
  this.erc20 = new Erc20NamespaceImpl(ctx);
@@ -1003,34 +1164,16 @@ var CoinListClientImpl = class {
1003
1164
  );
1004
1165
  this.ondo = new ClientOndoNamespaceImpl(ctx, this.erc20);
1005
1166
  this.tokens = new TokensNamespaceImpl(
1006
- _config.tokensBaseUrl ?? NABU_BASE_URL,
1167
+ config.tokensBaseUrl ?? NABU_BASE_URL,
1007
1168
  logger
1008
1169
  );
1009
1170
  this.support = new ClientSupportNamespaceImpl(logger);
1010
1171
  }
1011
1172
  async init() {
1012
- await this.fetchAccessToken(true);
1013
- return this.getAuthState();
1014
- }
1015
- async fetchAccessToken(refresh) {
1016
- if (!refresh && this._accessToken !== void 0) return this._accessToken;
1017
- try {
1018
- const accessToken = await this._config.getAccessToken();
1019
- this._accessToken = accessToken;
1020
- return accessToken;
1021
- } catch (_) {
1022
- this._accessToken = null;
1023
- return null;
1024
- }
1025
- }
1026
- getAuthState() {
1027
- if (this._accessToken === void 0) {
1028
- return "unknown";
1029
- }
1030
- return this._accessToken !== null ? "logged-in" : "logged-out";
1173
+ return this.accessToken.load();
1031
1174
  }
1032
1175
  ensureAuthenticated() {
1033
- if (this.getAuthState() !== "logged-in") {
1176
+ if (this.accessToken.authState() !== "logged-in") {
1034
1177
  throw new NotAuthenticatedError();
1035
1178
  }
1036
1179
  }