@coinlist-co/react 0.12.2-rc.7b430dd → 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.
- package/dist/{chunk-MOE5S6CE.js → chunk-BNMF2HMW.js} +114 -4
- package/dist/chunk-BNMF2HMW.js.map +1 -0
- package/dist/{chunk-VH34YC3Q.js → chunk-KRA6EM2L.js} +2 -2
- package/dist/{chunk-DCW7NQXH.js → chunk-STAHYDLY.js} +52 -19
- package/dist/chunk-STAHYDLY.js.map +1 -0
- package/dist/client/index.cjs +322 -96
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +26 -10
- package/dist/client/index.d.ts +26 -10
- package/dist/client/index.js +225 -102
- package/dist/client/index.js.map +1 -1
- package/dist/server/index.cjs +437 -76
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +47 -38
- package/dist/server/index.d.ts +47 -38
- package/dist/server/index.js +316 -61
- package/dist/server/index.js.map +1 -1
- package/dist/time-B5YOQxuh.d.ts +50 -0
- package/dist/time-BY3apBpc.d.cts +50 -0
- package/dist/{token-registry-snapshot-BNBNuDu1.d.ts → token-registry-snapshot-C1NxdBmF.d.ts} +1 -1
- package/dist/{token-registry-snapshot-CqI6dXM_.d.cts → token-registry-snapshot-DyjrA32p.d.cts} +1 -1
- package/dist/{tokens-namespace-CIWBtgt2.d.cts → tokens-namespace-BJfFeowE.d.cts} +53 -1
- package/dist/{tokens-namespace-CIWBtgt2.d.ts → tokens-namespace-BJfFeowE.d.ts} +53 -1
- package/dist/universal/index.cjs +100 -3
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.d.cts +23 -5
- package/dist/universal/index.d.ts +23 -5
- package/dist/universal/index.js +12 -2
- package/package.json +1 -1
- package/dist/chunk-DCW7NQXH.js.map +0 -1
- package/dist/chunk-MOE5S6CE.js.map +0 -1
- /package/dist/{chunk-VH34YC3Q.js.map → chunk-KRA6EM2L.js.map} +0 -0
package/dist/server/index.cjs
CHANGED
|
@@ -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) =>
|
|
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
|
-
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
596
|
-
|
|
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(
|
|
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
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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
|
-
|
|
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
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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() {
|
|
@@ -906,18 +1190,91 @@ var ServerAuthNamespaceImpl = class {
|
|
|
906
1190
|
await setSession(null);
|
|
907
1191
|
}
|
|
908
1192
|
/**
|
|
909
|
-
* Returns the store's `setSession`, or throws
|
|
1193
|
+
* Returns the store's `setSession`, or throws - the guard every
|
|
910
1194
|
* session-writing operation shares.
|
|
911
1195
|
*/
|
|
912
1196
|
writableSessionStore() {
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1197
|
+
const store = sessionStoreAccess(this.config.sessionStore);
|
|
1198
|
+
switch (store.type) {
|
|
1199
|
+
case "writable":
|
|
1200
|
+
return store.setSession;
|
|
1201
|
+
case "read-only":
|
|
1202
|
+
throw new WritableSessionStoreRequiredError();
|
|
917
1203
|
}
|
|
918
|
-
return setSession;
|
|
919
1204
|
}
|
|
920
1205
|
};
|
|
1206
|
+
function classifyAccessToken({
|
|
1207
|
+
session,
|
|
1208
|
+
store,
|
|
1209
|
+
trigger,
|
|
1210
|
+
now,
|
|
1211
|
+
buffer
|
|
1212
|
+
}) {
|
|
1213
|
+
if (session === null) {
|
|
1214
|
+
return { type: "no-session" };
|
|
1215
|
+
}
|
|
1216
|
+
const token = session.accessToken;
|
|
1217
|
+
const stale = isExpired({
|
|
1218
|
+
expiresAt: EpochMillis.fromDate(token.expiresAt),
|
|
1219
|
+
now,
|
|
1220
|
+
buffer
|
|
1221
|
+
});
|
|
1222
|
+
switch (store.type) {
|
|
1223
|
+
case "read-only":
|
|
1224
|
+
return classifyReadOnly(token, trigger, stale);
|
|
1225
|
+
case "writable": {
|
|
1226
|
+
if (!needsRefresh(token, trigger, stale)) {
|
|
1227
|
+
return { type: "serve", token };
|
|
1228
|
+
}
|
|
1229
|
+
const fallback = fallbackToken(token, trigger, now);
|
|
1230
|
+
return session.refreshToken === void 0 && fallback !== null ? { type: "serve", token: fallback } : {
|
|
1231
|
+
type: "refresh",
|
|
1232
|
+
session,
|
|
1233
|
+
setSession: store.setSession,
|
|
1234
|
+
fallback
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
function classifyReadOnly(token, trigger, stale) {
|
|
1240
|
+
switch (trigger.type) {
|
|
1241
|
+
case "after-401":
|
|
1242
|
+
return isRejectedToken(token, trigger.rejected) ? { type: "refuse" } : { type: "serve", token };
|
|
1243
|
+
case "before-request":
|
|
1244
|
+
return stale ? { type: "serve-stale", token } : { type: "serve", token };
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
function sessionStoreAccess(sessionStore) {
|
|
1248
|
+
const setSession = sessionStore.setSession?.bind(sessionStore);
|
|
1249
|
+
return setSession === void 0 ? { type: "read-only" } : { type: "writable", setSession };
|
|
1250
|
+
}
|
|
1251
|
+
function needsRefresh(token, trigger, stale) {
|
|
1252
|
+
switch (trigger.type) {
|
|
1253
|
+
case "before-request":
|
|
1254
|
+
return stale;
|
|
1255
|
+
case "after-401":
|
|
1256
|
+
return trigger.rejected.type === "no-user-token" || isRejectedToken(token, trigger.rejected) || stale;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
function fallbackToken(token, trigger, now) {
|
|
1260
|
+
const unexpired = !isExpired({
|
|
1261
|
+
expiresAt: EpochMillis.fromDate(token.expiresAt),
|
|
1262
|
+
now
|
|
1263
|
+
});
|
|
1264
|
+
switch (trigger.type) {
|
|
1265
|
+
case "after-401":
|
|
1266
|
+
return unexpired && !isRejectedToken(token, trigger.rejected) ? token : null;
|
|
1267
|
+
case "before-request":
|
|
1268
|
+
return unexpired ? token : null;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
function isInvalidGrant(error) {
|
|
1272
|
+
if (!(error instanceof HttpError) || error.response.status !== 400) {
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
const body = error.response.body;
|
|
1276
|
+
return typeof body === "object" && body !== null && "error" in body && body.error === "invalid_grant";
|
|
1277
|
+
}
|
|
921
1278
|
|
|
922
1279
|
// src/universal/api/pagination.ts
|
|
923
1280
|
async function fetchAllPages(fetchPage, baseParams) {
|
|
@@ -1821,6 +2178,8 @@ function formatRawAmount(amount) {
|
|
|
1821
2178
|
return (0, import_viem4.formatUnits)(amount.raw, amount.decimals);
|
|
1822
2179
|
}
|
|
1823
2180
|
var NA_AMOUNT_ASSET_UI = FormattedAmountAssetUi("-");
|
|
2181
|
+
var SHARE_MAX_FRACTION_DIGITS = AssetDecimals(6);
|
|
2182
|
+
var SHARE_MIN_FRACTION_DIGITS = AssetDecimals(2);
|
|
1824
2183
|
var USD_FRACTION_DIGITS = AssetDecimals(2);
|
|
1825
2184
|
|
|
1826
2185
|
// src/universal/core/shared/blockchain/math.ts
|
|
@@ -3666,28 +4025,26 @@ var WalletsNamespaceImpl = class {
|
|
|
3666
4025
|
};
|
|
3667
4026
|
|
|
3668
4027
|
// src/server/core/coinlist-server.ts
|
|
3669
|
-
var
|
|
4028
|
+
var ACCESS_TOKEN_EXPIRY_BUFFER = Seconds.of(60);
|
|
3670
4029
|
var CoinListServerImpl = class {
|
|
3671
4030
|
constructor(_config) {
|
|
3672
|
-
this._config = _config;
|
|
3673
4031
|
const logger = _config.logger ?? null;
|
|
3674
4032
|
this.api = new ApiClient(
|
|
3675
4033
|
{
|
|
3676
4034
|
baseUrl: _config.baseUrl ?? PUBLIC_API_BASE_URL,
|
|
3677
4035
|
xApiVersion: API_VERSION
|
|
3678
4036
|
},
|
|
3679
|
-
|
|
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(),
|
|
4037
|
+
(trigger) => auth.tokenFor(trigger),
|
|
3684
4038
|
logger
|
|
3685
4039
|
);
|
|
3686
|
-
|
|
4040
|
+
const auth = new ServerAuthNamespaceImpl(this.api, {
|
|
3687
4041
|
..._config,
|
|
3688
|
-
accessTokenExpiryBufferSeconds: _config.accessTokenExpiryBufferSeconds ??
|
|
3689
|
-
strict: _config.strict ?? false
|
|
4042
|
+
accessTokenExpiryBufferSeconds: _config.accessTokenExpiryBufferSeconds ?? ACCESS_TOKEN_EXPIRY_BUFFER,
|
|
4043
|
+
strict: _config.strict ?? false,
|
|
4044
|
+
refreshes: processRefreshCoordinator,
|
|
4045
|
+
...systemTime
|
|
3690
4046
|
});
|
|
4047
|
+
this.auth = auth;
|
|
3691
4048
|
const ctx = {
|
|
3692
4049
|
api: this.api,
|
|
3693
4050
|
rpc: publicClients(this.api, _config.rpc, _config.logger ?? null),
|
|
@@ -3865,9 +4222,13 @@ function pinoServerLogger(options) {
|
|
|
3865
4222
|
options.level
|
|
3866
4223
|
);
|
|
3867
4224
|
}
|
|
4225
|
+
|
|
4226
|
+
// src/server/core/server-auth-namespace.ts
|
|
4227
|
+
function emptySessionStore() {
|
|
4228
|
+
return { getSession: async () => null };
|
|
4229
|
+
}
|
|
3868
4230
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3869
4231
|
0 && (module.exports = {
|
|
3870
|
-
ServerAuthNamespaceImpl,
|
|
3871
4232
|
ServerOffersNamespaceImpl,
|
|
3872
4233
|
ServerRequirementsNamespaceImpl,
|
|
3873
4234
|
WritableSessionStoreRequiredError,
|