@coinlist-co/react 0.12.2-rc.7b430dd → 0.12.2-rc.abd9d38

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 (33) hide show
  1. package/dist/{chunk-DCW7NQXH.js → chunk-45LAPB7U.js} +52 -19
  2. package/dist/chunk-45LAPB7U.js.map +1 -0
  3. package/dist/{chunk-MOE5S6CE.js → chunk-HDJEDOS4.js} +114 -4
  4. package/dist/chunk-HDJEDOS4.js.map +1 -0
  5. package/dist/{chunk-VH34YC3Q.js → chunk-M7WDZZPX.js} +11 -2
  6. package/dist/chunk-M7WDZZPX.js.map +1 -0
  7. package/dist/client/index.cjs +396 -107
  8. package/dist/client/index.cjs.map +1 -1
  9. package/dist/client/index.d.cts +102 -34
  10. package/dist/client/index.d.ts +102 -34
  11. package/dist/client/index.js +292 -113
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/server/index.cjs +442 -80
  14. package/dist/server/index.cjs.map +1 -1
  15. package/dist/server/index.d.cts +65 -41
  16. package/dist/server/index.d.ts +65 -41
  17. package/dist/server/index.js +321 -65
  18. package/dist/server/index.js.map +1 -1
  19. package/dist/time-BOCS1TbH.d.cts +50 -0
  20. package/dist/time-C7mhO5_a.d.ts +50 -0
  21. package/dist/{token-registry-snapshot-BNBNuDu1.d.ts → token-registry-snapshot-C-Qs3Lgs.d.ts} +1 -1
  22. package/dist/{token-registry-snapshot-CqI6dXM_.d.cts → token-registry-snapshot-CpzLmsc2.d.cts} +1 -1
  23. package/dist/{tokens-namespace-CIWBtgt2.d.cts → tokens-namespace-DrwRi0AQ.d.cts} +64 -1
  24. package/dist/{tokens-namespace-CIWBtgt2.d.ts → tokens-namespace-DrwRi0AQ.d.ts} +64 -1
  25. package/dist/universal/index.cjs +110 -3
  26. package/dist/universal/index.cjs.map +1 -1
  27. package/dist/universal/index.d.cts +43 -5
  28. package/dist/universal/index.d.ts +43 -5
  29. package/dist/universal/index.js +16 -2
  30. package/package.json +1 -1
  31. package/dist/chunk-DCW7NQXH.js.map +0 -1
  32. package/dist/chunk-MOE5S6CE.js.map +0 -1
  33. package/dist/chunk-VH34YC3Q.js.map +0 -1
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/server/index.ts
21
21
  var server_exports = {};
22
22
  __export(server_exports, {
23
- ServerAuthNamespaceImpl: () => ServerAuthNamespaceImpl,
24
23
  ServerOffersNamespaceImpl: () => ServerOffersNamespaceImpl,
25
24
  ServerRequirementsNamespaceImpl: () => ServerRequirementsNamespaceImpl,
26
25
  WritableSessionStoreRequiredError: () => WritableSessionStoreRequiredError,
@@ -30,6 +29,30 @@ __export(server_exports, {
30
29
  });
31
30
  module.exports = __toCommonJS(server_exports);
32
31
 
32
+ // src/universal/api/internal-access-token-source.ts
33
+ var SentWith = {
34
+ userToken: (token) => ({
35
+ type: "user-token",
36
+ token
37
+ }),
38
+ noUserToken: { type: "no-user-token" }
39
+ };
40
+ var TokenTrigger = {
41
+ beforeRequest: { type: "before-request" },
42
+ after401: (rejected) => ({
43
+ type: "after-401",
44
+ rejected
45
+ })
46
+ };
47
+ function isRejectedToken(token, rejected) {
48
+ switch (rejected.type) {
49
+ case "user-token":
50
+ return rejected.token.value === token.value;
51
+ case "no-user-token":
52
+ return false;
53
+ }
54
+ }
55
+
33
56
  // src/universal/api/http-attributes.ts
34
57
  var empty = {};
35
58
  var concat = (left, right) => ({
@@ -57,6 +80,10 @@ var retryAttempt = (attempt) => ({
57
80
  var renewAttempted = (value) => ({
58
81
  renewAttempted: value
59
82
  });
83
+ var notRetryable = () => ({ retryable: false });
84
+ var sentWith = (token) => ({
85
+ sentWith: token
86
+ });
60
87
  var requestId = (id) => ({ requestId: id });
61
88
  var getRequestId = (attrs) => attrs?.requestId ?? null;
62
89
  var isProtected = (attrs) => attrs?.protected === true;
@@ -64,6 +91,8 @@ var needUserAgent = (attrs) => attrs?.userAgent === true;
64
91
  var isIdempotent = (attrs) => attrs?.idempotencyKey === true;
65
92
  var getRetryAttempt = (attrs) => attrs?.retryAttempt ?? 0;
66
93
  var wasRenewAttempted = (attrs) => attrs?.renewAttempted === true;
94
+ var isRetryable = (attrs) => attrs?.retryable !== false;
95
+ var getSentWith = (attrs) => attrs?.sentWith === void 0 ? SentWith.noUserToken : SentWith.userToken(attrs.sentWith);
67
96
  var getClientCredentials = (attrs) => attrs?.clientCredentials;
68
97
  var Attributes = {
69
98
  empty,
@@ -79,6 +108,10 @@ var Attributes = {
79
108
  getRetryAttempt,
80
109
  renewAttempted,
81
110
  wasRenewAttempted,
111
+ notRetryable,
112
+ isRetryable,
113
+ sentWith,
114
+ getSentWith,
82
115
  clientCredentials,
83
116
  getClientCredentials,
84
117
  requestId,
@@ -448,11 +481,18 @@ var HttpClient = class {
448
481
  );
449
482
  let response = await this.executeRequest(preparedRequest);
450
483
  for (const middleware of this.middleware.afterRequest ?? []) {
484
+ let retried = false;
451
485
  response = await middleware({
452
486
  request: preparedRequest,
453
487
  response,
454
- retry: (nextRequest = preparedRequest) => this.runRequestWithAfterMiddleware(nextRequest)
488
+ retry: (nextRequest = preparedRequest) => {
489
+ retried = true;
490
+ return this.runRequestWithAfterMiddleware(nextRequest);
491
+ }
455
492
  });
493
+ if (retried) {
494
+ return response;
495
+ }
456
496
  }
457
497
  return response;
458
498
  }
@@ -585,23 +625,33 @@ function attachSessionMiddleware(fetchAccessToken) {
585
625
  if (!Attributes.isProtected(request.attributes)) {
586
626
  return request;
587
627
  }
588
- let accessToken = await fetchAccessToken(false);
589
- if (!accessToken) {
590
- const clientCreds = Attributes.getClientCredentials(request.attributes);
591
- if (clientCreds) {
592
- accessToken = clientCreds;
593
- }
628
+ const userToken = await fetchAccessToken(TokenTrigger.beforeRequest);
629
+ if (userToken !== null) {
630
+ return withBearer(
631
+ {
632
+ ...request,
633
+ attributes: Attributes.concat(
634
+ request.attributes ?? Attributes.empty,
635
+ Attributes.sentWith(userToken)
636
+ )
637
+ },
638
+ userToken.value
639
+ );
594
640
  }
595
- if (accessToken === null) {
596
- return request;
641
+ const clientCreds = Attributes.getClientCredentials(request.attributes);
642
+ if (clientCreds) {
643
+ return withBearer(request, clientCreds.value);
644
+ }
645
+ return request;
646
+ };
647
+ }
648
+ function withBearer(request, token) {
649
+ return {
650
+ ...request,
651
+ headers: {
652
+ ...request.headers ?? {},
653
+ Authorization: `Bearer ${token}`
597
654
  }
598
- return {
599
- ...request,
600
- headers: {
601
- ...request.headers ?? {},
602
- Authorization: `Bearer ${accessToken.value}`
603
- }
604
- };
605
655
  };
606
656
  }
607
657
 
@@ -660,7 +710,7 @@ function getRetryDelayMs(attempt) {
660
710
  function createRequestRetryMiddleware(options = {}) {
661
711
  const delayFn = options.delayFn ?? defaultDelay;
662
712
  return async ({ request, response, retry }) => {
663
- if (!isRetryableStatus(response.status)) {
713
+ if (!isRetryableStatus(response.status) || !Attributes.isRetryable(request.attributes)) {
664
714
  return response;
665
715
  }
666
716
  const currentAttempt = Attributes.getRetryAttempt(request.attributes);
@@ -693,7 +743,9 @@ function renewSessionMiddleware(fetchAccessToken) {
693
743
  if (Attributes.wasRenewAttempted(request.attributes)) {
694
744
  return response;
695
745
  }
696
- const newToken = await fetchAccessToken(true);
746
+ const newToken = await fetchAccessToken(
747
+ TokenTrigger.after401(Attributes.getSentWith(request.attributes))
748
+ );
697
749
  if (newToken) {
698
750
  const nextRequest = Request.concatAttributes(
699
751
  request,
@@ -750,6 +802,158 @@ var ApiClient = class {
750
802
  }
751
803
  };
752
804
 
805
+ // src/universal/types/time.ts
806
+ var MAX_EPOCH_MILLIS = 864e13;
807
+ var EpochMillis = {
808
+ /**
809
+ * @throws {@link ValidationError} unless `value` is an integer a `Date` can
810
+ * represent: at most {@link MAX_EPOCH_MILLIS} either side of the epoch.
811
+ */
812
+ of: (value) => {
813
+ if (!Number.isInteger(value) || Math.abs(value) > MAX_EPOCH_MILLIS) {
814
+ throw new ValidationError(
815
+ `EpochMillis must be an integer within \xB1${MAX_EPOCH_MILLIS}, got ${value}`
816
+ );
817
+ }
818
+ return value;
819
+ },
820
+ /** The wall clock. Code that needs the time takes a `TimeEff` instead. */
821
+ now: () => EpochMillis.of(Date.now()),
822
+ /** @throws {@link ValidationError} for an invalid `Date`. */
823
+ fromDate: (date) => EpochMillis.of(date.getTime()),
824
+ /** How long after `from` the instant `to` is; negative if it is before. */
825
+ elapsed: (from, to) => Millis.of(to - from),
826
+ plus: (at, duration) => EpochMillis.of(at + duration)
827
+ };
828
+ var Millis = {
829
+ of: (value) => value
830
+ };
831
+ var Seconds = {
832
+ /**
833
+ * @throws {@link ValidationError} unless `value` is a non-negative integer.
834
+ */
835
+ of: (value) => {
836
+ if (!Number.isInteger(value) || value < 0) {
837
+ throw new ValidationError(
838
+ `Seconds must be a non-negative integer, got ${value}`
839
+ );
840
+ }
841
+ return value;
842
+ },
843
+ toMillis: (value) => Millis.of(value * 1e3)
844
+ };
845
+ function isExpired({
846
+ expiresAt,
847
+ now,
848
+ buffer = Millis.of(0)
849
+ }) {
850
+ return now >= expiresAt - buffer;
851
+ }
852
+
853
+ // src/universal/core/shared/effects.ts
854
+ var systemTime = { time: { now: EpochMillis.now } };
855
+
856
+ // src/server/core/session-refresh.ts
857
+ var VERDICT_MIN_GAP = Millis.of(5e3);
858
+ var REJECTION_MEMORY = Millis.of(6e4);
859
+ var RefreshKey = {
860
+ of: ({
861
+ clientId,
862
+ session
863
+ }) => {
864
+ const identity = session.refreshToken === void 0 ? ["access", session.accessToken.value] : ["refresh", session.refreshToken];
865
+ return JSON.stringify([clientId, ...identity]);
866
+ }
867
+ };
868
+ var RefreshCoordinator = class {
869
+ constructor(env = systemTime) {
870
+ this.env = env;
871
+ this.inFlight = /* @__PURE__ */ new Map();
872
+ this.rejections = /* @__PURE__ */ new Map();
873
+ }
874
+ /**
875
+ * Runs `exchange` for `key` unless one is already in flight, in which case
876
+ * the caller joins it. `joined` tells the caller whether it led: only the
877
+ * leader reports the outcome, so a burst of N produces one verdict, one log
878
+ * line and one `onSessionExpired`.
879
+ */
880
+ refresh(key, exchange) {
881
+ const existing = this.inFlight.get(key);
882
+ if (existing !== void 0) {
883
+ return { outcome: existing, joined: true };
884
+ }
885
+ const outcome = this.run(key, exchange).finally(() => {
886
+ this.inFlight.delete(key);
887
+ });
888
+ this.inFlight.set(key, outcome);
889
+ return { outcome, joined: false };
890
+ }
891
+ async run(key, exchange) {
892
+ this.forgetStale(this.env.time.now());
893
+ if (this.rejections.get(key)?.type === "expired") {
894
+ return { type: "already-expired" };
895
+ }
896
+ const result = await exchange();
897
+ switch (result.type) {
898
+ case "renewed":
899
+ case "unknown":
900
+ return result;
901
+ case "rejected":
902
+ case "unrenewable": {
903
+ const judged = judge({
904
+ result: result.type,
905
+ prior: this.rejections.get(key),
906
+ now: this.env.time.now()
907
+ });
908
+ this.rejections.set(key, judged.memory);
909
+ return { type: judged.outcome };
910
+ }
911
+ }
912
+ }
913
+ forgetStale(now) {
914
+ for (const [key, rejection] of this.rejections) {
915
+ if (EpochMillis.elapsed(rejectedAt(rejection), now) > REJECTION_MEMORY) {
916
+ this.rejections.delete(key);
917
+ }
918
+ }
919
+ }
920
+ };
921
+ function judge({
922
+ result,
923
+ prior,
924
+ now
925
+ }) {
926
+ const verdict = {
927
+ outcome: "expired",
928
+ memory: { type: "expired", at: now }
929
+ };
930
+ switch (result) {
931
+ case "unrenewable":
932
+ return verdict;
933
+ case "rejected":
934
+ switch (prior?.type) {
935
+ case void 0:
936
+ return {
937
+ outcome: "suspected",
938
+ memory: { type: "suspected", since: now }
939
+ };
940
+ case "suspected":
941
+ return EpochMillis.elapsed(prior.since, now) >= VERDICT_MIN_GAP ? verdict : { outcome: "suspected", memory: prior };
942
+ case "expired":
943
+ return { outcome: "expired", memory: prior };
944
+ }
945
+ }
946
+ }
947
+ function rejectedAt(rejection) {
948
+ switch (rejection.type) {
949
+ case "suspected":
950
+ return rejection.since;
951
+ case "expired":
952
+ return rejection.at;
953
+ }
954
+ }
955
+ var processRefreshCoordinator = new RefreshCoordinator();
956
+
753
957
  // src/server/shared/errors.ts
754
958
  var WritableSessionStoreRequiredError = class extends Error {
755
959
  constructor(message = "This operation requires a writable SessionStore. Provide a `setSession` implementation in your SessionStore to persist or clear OAuth sessions.") {
@@ -758,6 +962,16 @@ var WritableSessionStoreRequiredError = class extends Error {
758
962
  }
759
963
  };
760
964
 
965
+ // src/universal/core/auth/session-expired.ts
966
+ function notifySessionExpired(onSessionExpired, log) {
967
+ if (onSessionExpired === void 0) {
968
+ return;
969
+ }
970
+ Promise.resolve().then(onSessionExpired).catch(
971
+ (error) => log.failure(() => ({ msg: "onSessionExpired threw" }), error)
972
+ );
973
+ }
974
+
761
975
  // src/universal/types/oauth-session.ts
762
976
  var ClientCredentialsOAuth = (value) => value;
763
977
  var OAuthRefreshToken = (value) => value;
@@ -774,10 +988,7 @@ var OAuthSession = {
774
988
  }
775
989
  };
776
990
 
777
- // src/server/core/server-auth-namespace.ts
778
- function emptySessionStore() {
779
- return { getSession: async () => null };
780
- }
991
+ // src/server/core/server-auth-namespace-impl.ts
781
992
  var ServerAuthNamespaceImpl = class {
782
993
  constructor(api, config) {
783
994
  this.api = api;
@@ -797,7 +1008,8 @@ var ServerAuthNamespaceImpl = class {
797
1008
  client_id: this.config.clientId,
798
1009
  client_secret: this.config.clientSecret,
799
1010
  code_verifier: params.codeVerifier
800
- }
1011
+ },
1012
+ attributes: Attributes.notRetryable()
801
1013
  });
802
1014
  const session = OAuthSession.fromDto(sessionDto);
803
1015
  await setSession(session);
@@ -805,39 +1017,118 @@ var ServerAuthNamespaceImpl = class {
805
1017
  });
806
1018
  }
807
1019
  async getAccessToken() {
1020
+ return this.tokenFor(TokenTrigger.beforeRequest);
1021
+ }
1022
+ /**
1023
+ * {@link getAccessToken} with the reason the SDK is asking, which is what
1024
+ * the authenticated HTTP pipeline calls: after a 401 the token's own expiry
1025
+ * no longer matters, because the API has answered for it.
1026
+ */
1027
+ async tokenFor(trigger) {
808
1028
  return this.log.wrap(
809
1029
  "getAccessToken",
810
1030
  void 0,
811
- () => this.readAccessToken()
1031
+ () => this.readAccessToken(trigger)
812
1032
  );
813
1033
  }
814
- async readAccessToken() {
1034
+ async readAccessToken(trigger) {
815
1035
  const sessionStore = this.config.sessionStore;
816
- const session = await sessionStore.getSession();
817
- if (session == null) return null;
818
- const now = Date.now();
819
- const expiresAt = session.accessToken.expiresAt.getTime();
820
- const bufferMs = this.config.accessTokenExpiryBufferSeconds * 1e3;
821
- if (expiresAt > now + bufferMs) {
822
- return session.accessToken;
823
- }
824
- const setSession = sessionStore.setSession?.bind(sessionStore);
825
- if (!setSession) {
826
- this.log.child({ op: "getAccessToken" }).warn(() => ({
827
- msg: "serving an expired token: the session store is read-only, so it cannot be refreshed"
828
- }));
829
- return session.accessToken;
1036
+ const classification = classifyAccessToken({
1037
+ session: await sessionStore.getSession(),
1038
+ store: sessionStoreAccess(sessionStore),
1039
+ trigger,
1040
+ now: this.config.time.now(),
1041
+ buffer: Seconds.toMillis(this.config.accessTokenExpiryBufferSeconds)
1042
+ });
1043
+ switch (classification.type) {
1044
+ case "no-session":
1045
+ case "refuse":
1046
+ return null;
1047
+ case "serve":
1048
+ return classification.token;
1049
+ case "serve-stale":
1050
+ this.log.child({ op: "getAccessToken" }).warn(() => ({
1051
+ msg: "serving an expired token: the session store is read-only, so it cannot be refreshed"
1052
+ }));
1053
+ return classification.token;
1054
+ case "refresh":
1055
+ return this.refresh(classification);
830
1056
  }
831
- return this.refreshSession(session.refreshToken, setSession);
832
1057
  }
833
- async refreshSession(refreshToken, setSession) {
1058
+ /**
1059
+ * Refreshes through the process-wide coordinator, then acts on the outcome
1060
+ * for this caller's own store. The store is written before anything is
1061
+ * logged or the host is notified, so `onSessionExpired` always finds it
1062
+ * cleared.
1063
+ */
1064
+ async refresh({
1065
+ session,
1066
+ setSession,
1067
+ fallback
1068
+ }) {
834
1069
  const log = this.log.child({ op: "refresh" });
835
- if (!refreshToken) {
836
- log.warn(() => ({
837
- msg: "clearing the session: it carries no refresh token"
838
- }));
839
- await setSession(null);
840
- return null;
1070
+ const key = RefreshKey.of({ clientId: this.config.clientId, session });
1071
+ const { outcome, joined } = this.config.refreshes.refresh(
1072
+ key,
1073
+ () => this.exchange(session.refreshToken)
1074
+ );
1075
+ if (joined) {
1076
+ log.debug(() => ({ msg: "joined a refresh already in flight" }));
1077
+ }
1078
+ const result = await outcome;
1079
+ switch (result.type) {
1080
+ case "renewed":
1081
+ await setSession(result.session);
1082
+ if (!joined) {
1083
+ log.info(() => ({ msg: "session renewed" }));
1084
+ }
1085
+ return result.session.accessToken;
1086
+ case "suspected":
1087
+ if (!joined) {
1088
+ log.warn(() => ({
1089
+ msg: "refresh rejected; not clearing until it is confirmed"
1090
+ }));
1091
+ }
1092
+ return fallback;
1093
+ case "expired":
1094
+ await setSession(null);
1095
+ if (!joined) {
1096
+ log.info(() => ({
1097
+ msg: "session expired",
1098
+ fields: {
1099
+ reason: session.refreshToken === void 0 ? "no-refresh-token" : "refresh-rejected"
1100
+ }
1101
+ }));
1102
+ notifySessionExpired(this.config.onSessionExpired, log);
1103
+ }
1104
+ return null;
1105
+ case "already-expired":
1106
+ await setSession(null);
1107
+ return null;
1108
+ case "unknown":
1109
+ if (fallback === null) {
1110
+ throw result.error;
1111
+ }
1112
+ if (!joined) {
1113
+ log.warning(
1114
+ () => ({ msg: "refresh failed; serving the current token" }),
1115
+ result.error
1116
+ );
1117
+ }
1118
+ return fallback;
1119
+ }
1120
+ }
1121
+ /**
1122
+ * One `POST /oauth/token`, total: every failure comes back as a value, so
1123
+ * the coordinator can judge it once for everyone who joined.
1124
+ *
1125
+ * Never retried. The refresh token is single-use, so a retry of an attempt
1126
+ * that reached the server would present a token already rotated away, and
1127
+ * its `invalid_grant` would be judged as a rejection of the session.
1128
+ */
1129
+ async exchange(refreshToken) {
1130
+ if (refreshToken === void 0) {
1131
+ return { type: "unrenewable" };
841
1132
  }
842
1133
  try {
843
1134
  const sessionDto = await this.api.send({
@@ -848,19 +1139,12 @@ var ServerAuthNamespaceImpl = class {
848
1139
  refresh_token: refreshToken,
849
1140
  client_id: this.config.clientId,
850
1141
  client_secret: this.config.clientSecret
851
- }
1142
+ },
1143
+ attributes: Attributes.notRetryable()
852
1144
  });
853
- const newSession = OAuthSession.fromDto(sessionDto);
854
- await setSession(newSession);
855
- log.info(() => ({ msg: "session renewed" }));
856
- return newSession.accessToken;
1145
+ return { type: "renewed", session: OAuthSession.fromDto(sessionDto) };
857
1146
  } catch (error) {
858
- log.failure(
859
- () => ({ msg: "refresh failed; clearing the session" }),
860
- error
861
- );
862
- await setSession(null);
863
- return null;
1147
+ return isInvalidGrant(error) ? { type: "rejected" } : { type: "unknown", error };
864
1148
  }
865
1149
  }
866
1150
  async clientCredentials() {
@@ -896,28 +1180,102 @@ var ServerAuthNamespaceImpl = class {
896
1180
  }
897
1181
  });
898
1182
  } catch (err) {
899
- if (!(err instanceof HttpError) || this.config.strict) {
1183
+ if (this.config.strict) {
900
1184
  throw err;
901
1185
  }
902
- this.log.child({ op: "logout" }).warn(() => ({
903
- msg: "the token was not revoked upstream; the local session is cleared regardless"
904
- }));
1186
+ this.log.child({ op: "logout" }).warning(
1187
+ () => ({ msg: "revoke failed; local session cleared regardless" }),
1188
+ err
1189
+ );
905
1190
  }
906
1191
  await setSession(null);
907
1192
  }
908
1193
  /**
909
- * Returns the store's `setSession`, or throws — the guard every
1194
+ * Returns the store's `setSession`, or throws - the guard every
910
1195
  * session-writing operation shares.
911
1196
  */
912
1197
  writableSessionStore() {
913
- const sessionStore = this.config.sessionStore;
914
- const setSession = sessionStore.setSession?.bind(sessionStore);
915
- if (!setSession) {
916
- throw new WritableSessionStoreRequiredError();
1198
+ const store = sessionStoreAccess(this.config.sessionStore);
1199
+ switch (store.type) {
1200
+ case "writable":
1201
+ return store.setSession;
1202
+ case "read-only":
1203
+ throw new WritableSessionStoreRequiredError();
917
1204
  }
918
- return setSession;
919
1205
  }
920
1206
  };
1207
+ function classifyAccessToken({
1208
+ session,
1209
+ store,
1210
+ trigger,
1211
+ now,
1212
+ buffer
1213
+ }) {
1214
+ if (session === null) {
1215
+ return { type: "no-session" };
1216
+ }
1217
+ const token = session.accessToken;
1218
+ const stale = isExpired({
1219
+ expiresAt: EpochMillis.fromDate(token.expiresAt),
1220
+ now,
1221
+ buffer
1222
+ });
1223
+ switch (store.type) {
1224
+ case "read-only":
1225
+ return classifyReadOnly(token, trigger, stale);
1226
+ case "writable": {
1227
+ if (!needsRefresh(token, trigger, stale)) {
1228
+ return { type: "serve", token };
1229
+ }
1230
+ const fallback = fallbackToken(token, trigger, now);
1231
+ return session.refreshToken === void 0 && fallback !== null ? { type: "serve", token: fallback } : {
1232
+ type: "refresh",
1233
+ session,
1234
+ setSession: store.setSession,
1235
+ fallback
1236
+ };
1237
+ }
1238
+ }
1239
+ }
1240
+ function classifyReadOnly(token, trigger, stale) {
1241
+ switch (trigger.type) {
1242
+ case "after-401":
1243
+ return isRejectedToken(token, trigger.rejected) ? { type: "refuse" } : { type: "serve", token };
1244
+ case "before-request":
1245
+ return stale ? { type: "serve-stale", token } : { type: "serve", token };
1246
+ }
1247
+ }
1248
+ function sessionStoreAccess(sessionStore) {
1249
+ const setSession = sessionStore.setSession?.bind(sessionStore);
1250
+ return setSession === void 0 ? { type: "read-only" } : { type: "writable", setSession };
1251
+ }
1252
+ function needsRefresh(token, trigger, stale) {
1253
+ switch (trigger.type) {
1254
+ case "before-request":
1255
+ return stale;
1256
+ case "after-401":
1257
+ return trigger.rejected.type === "no-user-token" || isRejectedToken(token, trigger.rejected) || stale;
1258
+ }
1259
+ }
1260
+ function fallbackToken(token, trigger, now) {
1261
+ const unexpired = !isExpired({
1262
+ expiresAt: EpochMillis.fromDate(token.expiresAt),
1263
+ now
1264
+ });
1265
+ switch (trigger.type) {
1266
+ case "after-401":
1267
+ return unexpired && !isRejectedToken(token, trigger.rejected) ? token : null;
1268
+ case "before-request":
1269
+ return unexpired ? token : null;
1270
+ }
1271
+ }
1272
+ function isInvalidGrant(error) {
1273
+ if (!(error instanceof HttpError) || error.response.status !== 400) {
1274
+ return false;
1275
+ }
1276
+ const body = error.response.body;
1277
+ return typeof body === "object" && body !== null && "error" in body && body.error === "invalid_grant";
1278
+ }
921
1279
 
922
1280
  // src/universal/api/pagination.ts
923
1281
  async function fetchAllPages(fetchPage, baseParams) {
@@ -1821,6 +2179,8 @@ function formatRawAmount(amount) {
1821
2179
  return (0, import_viem4.formatUnits)(amount.raw, amount.decimals);
1822
2180
  }
1823
2181
  var NA_AMOUNT_ASSET_UI = FormattedAmountAssetUi("-");
2182
+ var SHARE_MAX_FRACTION_DIGITS = AssetDecimals(6);
2183
+ var SHARE_MIN_FRACTION_DIGITS = AssetDecimals(2);
1824
2184
  var USD_FRACTION_DIGITS = AssetDecimals(2);
1825
2185
 
1826
2186
  // src/universal/core/shared/blockchain/math.ts
@@ -3666,28 +4026,26 @@ var WalletsNamespaceImpl = class {
3666
4026
  };
3667
4027
 
3668
4028
  // src/server/core/coinlist-server.ts
3669
- var ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS = 60;
4029
+ var ACCESS_TOKEN_EXPIRY_BUFFER = Seconds.of(60);
3670
4030
  var CoinListServerImpl = class {
3671
4031
  constructor(_config) {
3672
- this._config = _config;
3673
4032
  const logger = _config.logger ?? null;
3674
4033
  this.api = new ApiClient(
3675
4034
  {
3676
4035
  baseUrl: _config.baseUrl ?? PUBLIC_API_BASE_URL,
3677
4036
  xApiVersion: API_VERSION
3678
4037
  },
3679
- // When refresh=true the renewal middleware has received a 401 and wants a
3680
- // fresh token. A read-only store cannot persist a new session, so return
3681
- // null immediately — this tells the middleware to skip the retry rather
3682
- // than re-sending with the same expired token and wasting a round-trip.
3683
- (refresh) => refresh && !this._config.sessionStore.setSession ? Promise.resolve(null) : this.auth.getAccessToken(),
4038
+ (trigger) => auth.tokenFor(trigger),
3684
4039
  logger
3685
4040
  );
3686
- this.auth = new ServerAuthNamespaceImpl(this.api, {
4041
+ const auth = new ServerAuthNamespaceImpl(this.api, {
3687
4042
  ..._config,
3688
- accessTokenExpiryBufferSeconds: _config.accessTokenExpiryBufferSeconds ?? ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS,
3689
- strict: _config.strict ?? false
4043
+ accessTokenExpiryBufferSeconds: _config.accessTokenExpiryBufferSeconds ?? ACCESS_TOKEN_EXPIRY_BUFFER,
4044
+ strict: _config.strict ?? false,
4045
+ refreshes: processRefreshCoordinator,
4046
+ ...systemTime
3690
4047
  });
4048
+ this.auth = auth;
3691
4049
  const ctx = {
3692
4050
  api: this.api,
3693
4051
  rpc: publicClients(this.api, _config.rpc, _config.logger ?? null),
@@ -3865,9 +4223,13 @@ function pinoServerLogger(options) {
3865
4223
  options.level
3866
4224
  );
3867
4225
  }
4226
+
4227
+ // src/server/core/server-auth-namespace.ts
4228
+ function emptySessionStore() {
4229
+ return { getSession: async () => null };
4230
+ }
3868
4231
  // Annotate the CommonJS export names for ESM import in node:
3869
4232
  0 && (module.exports = {
3870
- ServerAuthNamespaceImpl,
3871
4233
  ServerOffersNamespaceImpl,
3872
4234
  ServerRequirementsNamespaceImpl,
3873
4235
  WritableSessionStoreRequiredError,