@coinlist-co/react 0.12.2-rc.7b430dd → 0.12.2-rc.a59b344
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-DCW7NQXH.js → chunk-4IZ2F732.js} +52 -19
- package/dist/chunk-4IZ2F732.js.map +1 -0
- package/dist/{chunk-MOE5S6CE.js → chunk-NBO5E2RO.js} +152 -6
- package/dist/chunk-NBO5E2RO.js.map +1 -0
- package/dist/{chunk-VH34YC3Q.js → chunk-UJYSXXJF.js} +168 -3
- package/dist/chunk-UJYSXXJF.js.map +1 -0
- package/dist/client/index.cjs +469 -145
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +114 -35
- package/dist/client/index.d.ts +114 -35
- package/dist/client/index.js +297 -114
- package/dist/client/index.js.map +1 -1
- package/dist/server/index.cjs +507 -116
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +65 -41
- package/dist/server/index.d.ts +65 -41
- package/dist/server/index.js +321 -65
- package/dist/server/index.js.map +1 -1
- package/dist/time-CFocgYyl.d.cts +50 -0
- package/dist/time-DvgN98Q9.d.ts +50 -0
- package/dist/{token-registry-snapshot-BNBNuDu1.d.ts → token-registry-snapshot-BsAfBL_b.d.ts} +9 -7
- package/dist/{token-registry-snapshot-CqI6dXM_.d.cts → token-registry-snapshot-Cc_eA5zM.d.cts} +9 -7
- package/dist/{tokens-namespace-CIWBtgt2.d.cts → tokens-namespace-D_XSpiS-.d.cts} +154 -19
- package/dist/{tokens-namespace-CIWBtgt2.d.ts → tokens-namespace-D_XSpiS-.d.ts} +154 -19
- package/dist/universal/index.cjs +333 -40
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.d.cts +358 -5
- package/dist/universal/index.d.ts +358 -5
- package/dist/universal/index.js +28 -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 +0 -1
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() {
|
|
@@ -896,28 +1180,102 @@ var ServerAuthNamespaceImpl = class {
|
|
|
896
1180
|
}
|
|
897
1181
|
});
|
|
898
1182
|
} catch (err) {
|
|
899
|
-
if (
|
|
1183
|
+
if (this.config.strict) {
|
|
900
1184
|
throw err;
|
|
901
1185
|
}
|
|
902
|
-
this.log.child({ op: "logout" }).
|
|
903
|
-
msg: "
|
|
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
|
|
1194
|
+
* Returns the store's `setSession`, or throws - the guard every
|
|
910
1195
|
* session-writing operation shares.
|
|
911
1196
|
*/
|
|
912
1197
|
writableSessionStore() {
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
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) {
|
|
@@ -935,40 +1293,6 @@ async function fetchAllPages(fetchPage, baseParams) {
|
|
|
935
1293
|
return items;
|
|
936
1294
|
}
|
|
937
1295
|
|
|
938
|
-
// src/universal/core/shared/utils/wire.ts
|
|
939
|
-
function unknownTypeError(dto, model) {
|
|
940
|
-
const type = dto.type;
|
|
941
|
-
throw new ValidationError(
|
|
942
|
-
`${model}.type: unknown type ${JSON.stringify(type)}`
|
|
943
|
-
);
|
|
944
|
-
}
|
|
945
|
-
function requiredString(value, field) {
|
|
946
|
-
const text = notBlankStringOrNull(value);
|
|
947
|
-
if (text === null) {
|
|
948
|
-
throw new ValidationError(
|
|
949
|
-
`${field}: expected a non-blank string, got ${typeof value === "string" ? "a blank string" : String(value)}`
|
|
950
|
-
);
|
|
951
|
-
}
|
|
952
|
-
return text;
|
|
953
|
-
}
|
|
954
|
-
function requiredDate(value, field) {
|
|
955
|
-
const date = typeof value === "string" ? new Date(value) : null;
|
|
956
|
-
if (date === null || Number.isNaN(date.getTime())) {
|
|
957
|
-
throw new ValidationError(
|
|
958
|
-
`${field}: expected a date, got ${JSON.stringify(value)}`
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
return date;
|
|
962
|
-
}
|
|
963
|
-
function requiredArray(value, field) {
|
|
964
|
-
if (!Array.isArray(value)) {
|
|
965
|
-
throw new ValidationError(
|
|
966
|
-
`${field}: expected an array, got ${typeof value}`
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
return value;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
1296
|
// src/universal/types/blockchain/core.ts
|
|
973
1297
|
var ETHEREUM_CHAINS = {
|
|
974
1298
|
ethereum_mainnet: true,
|
|
@@ -1057,13 +1381,51 @@ function combineAmounts(a, b, op) {
|
|
|
1057
1381
|
}
|
|
1058
1382
|
var AssetSymbol = (value) => value;
|
|
1059
1383
|
|
|
1384
|
+
// src/universal/core/shared/utils/wire.ts
|
|
1385
|
+
function unknownTypeError(dto, model) {
|
|
1386
|
+
const type = dto.type;
|
|
1387
|
+
throw new ValidationError(
|
|
1388
|
+
`${model}.type: unknown type ${JSON.stringify(type)}`
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
function requiredString(value, field) {
|
|
1392
|
+
const text = notBlankStringOrNull(value);
|
|
1393
|
+
if (text === null) {
|
|
1394
|
+
throw new ValidationError(
|
|
1395
|
+
`${field}: expected a non-blank string, got ${typeof value === "string" ? "a blank string" : String(value)}`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
return text;
|
|
1399
|
+
}
|
|
1400
|
+
function requiredDate(value, field) {
|
|
1401
|
+
const date = typeof value === "string" ? new Date(value) : null;
|
|
1402
|
+
if (date === null || Number.isNaN(date.getTime())) {
|
|
1403
|
+
throw new ValidationError(
|
|
1404
|
+
`${field}: expected a date, got ${JSON.stringify(value)}`
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
return date;
|
|
1408
|
+
}
|
|
1409
|
+
function decimalOrNull(value) {
|
|
1410
|
+
return value === null ? null : DecimalString(value);
|
|
1411
|
+
}
|
|
1412
|
+
function requiredArray(value, field) {
|
|
1413
|
+
if (!Array.isArray(value)) {
|
|
1414
|
+
throw new ValidationError(
|
|
1415
|
+
`${field}: expected an array, got ${typeof value}`
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
return value;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1060
1421
|
// src/universal/types/offer.ts
|
|
1061
1422
|
var OfferId = (value) => value;
|
|
1062
1423
|
var OfferSlug = (value) => value;
|
|
1063
1424
|
var OFFER_TYPES = {
|
|
1064
1425
|
"coinlist::token_sale": true,
|
|
1065
1426
|
"superstate::swap": true,
|
|
1066
|
-
"ondo::swap": true
|
|
1427
|
+
"ondo::swap": true,
|
|
1428
|
+
"centrifuge::vault": true
|
|
1067
1429
|
};
|
|
1068
1430
|
var isOfferType = (value) => Object.keys(OFFER_TYPES).includes(value);
|
|
1069
1431
|
var OfferEditorial = {
|
|
@@ -1105,6 +1467,7 @@ var Offer = {
|
|
|
1105
1467
|
// on the wire: copying them would hand every consumer a `startsAt` that
|
|
1106
1468
|
// is 1970 for one variant and a date for the others.
|
|
1107
1469
|
case "ondo::swap":
|
|
1470
|
+
case "centrifuge::vault":
|
|
1108
1471
|
return {
|
|
1109
1472
|
...base2,
|
|
1110
1473
|
type: dto.type,
|
|
@@ -1169,6 +1532,19 @@ var OfferSwapContract = {
|
|
|
1169
1532
|
address: EvmContractAddress(dto.address)
|
|
1170
1533
|
})
|
|
1171
1534
|
};
|
|
1535
|
+
var OfferVaultContract = {
|
|
1536
|
+
fromDto: (dto) => ({
|
|
1537
|
+
chain: Chain(dto.chain),
|
|
1538
|
+
address: EvmContractAddress(dto.address)
|
|
1539
|
+
}),
|
|
1540
|
+
/**
|
|
1541
|
+
* Drops every proxy on a chain this SDK does not name, as
|
|
1542
|
+
* `OfferToken.listFromDto` does: frontline publishes the full offer-token
|
|
1543
|
+
* chain list, which runs ahead of {@link Chain}, and one unmodelled chain
|
|
1544
|
+
* should not throw away the proxies the SDK can use.
|
|
1545
|
+
*/
|
|
1546
|
+
listFromDto: (dtos) => dtos.filter((dto) => isChain(dto.chain)).map(OfferVaultContract.fromDto)
|
|
1547
|
+
};
|
|
1172
1548
|
var OfferDetail = {
|
|
1173
1549
|
fromDto: (dto) => {
|
|
1174
1550
|
const fundingAssets = requiredArray(
|
|
@@ -1231,6 +1607,18 @@ var OfferDetail = {
|
|
|
1231
1607
|
"OfferDetail.swap_contracts"
|
|
1232
1608
|
).map(OfferSwapContract.fromDto)
|
|
1233
1609
|
};
|
|
1610
|
+
// No editorial, as for Ondo. `[]` is fine and a missing key is not, for
|
|
1611
|
+
// the same reason as `swap_contracts`: this is the spender a user
|
|
1612
|
+
// approves.
|
|
1613
|
+
case "centrifuge::vault":
|
|
1614
|
+
return {
|
|
1615
|
+
...base2,
|
|
1616
|
+
type: dto.type,
|
|
1617
|
+
logoUrl: notBlankStringOrNull(dto.logo_url),
|
|
1618
|
+
vaultContracts: OfferVaultContract.listFromDto(
|
|
1619
|
+
requiredArray(dto.vault_contracts, "OfferDetail.vault_contracts")
|
|
1620
|
+
)
|
|
1621
|
+
};
|
|
1234
1622
|
default:
|
|
1235
1623
|
return unknownTypeError(dto, "OfferDetail");
|
|
1236
1624
|
}
|
|
@@ -1821,6 +2209,8 @@ function formatRawAmount(amount) {
|
|
|
1821
2209
|
return (0, import_viem4.formatUnits)(amount.raw, amount.decimals);
|
|
1822
2210
|
}
|
|
1823
2211
|
var NA_AMOUNT_ASSET_UI = FormattedAmountAssetUi("-");
|
|
2212
|
+
var SHARE_MAX_FRACTION_DIGITS = AssetDecimals(6);
|
|
2213
|
+
var SHARE_MIN_FRACTION_DIGITS = AssetDecimals(2);
|
|
1824
2214
|
var USD_FRACTION_DIGITS = AssetDecimals(2);
|
|
1825
2215
|
|
|
1826
2216
|
// src/universal/core/shared/blockchain/math.ts
|
|
@@ -1862,7 +2252,6 @@ var OndoTradingStatus = {
|
|
|
1862
2252
|
};
|
|
1863
2253
|
}
|
|
1864
2254
|
};
|
|
1865
|
-
var decimalOrNull = (value) => value === null ? null : DecimalString(value);
|
|
1866
2255
|
var OndoQuote = {
|
|
1867
2256
|
fromDto: (dto) => {
|
|
1868
2257
|
const assetDecimals = AssetDecimals(dto.asset_decimals);
|
|
@@ -3666,28 +4055,26 @@ var WalletsNamespaceImpl = class {
|
|
|
3666
4055
|
};
|
|
3667
4056
|
|
|
3668
4057
|
// src/server/core/coinlist-server.ts
|
|
3669
|
-
var
|
|
4058
|
+
var ACCESS_TOKEN_EXPIRY_BUFFER = Seconds.of(60);
|
|
3670
4059
|
var CoinListServerImpl = class {
|
|
3671
4060
|
constructor(_config) {
|
|
3672
|
-
this._config = _config;
|
|
3673
4061
|
const logger = _config.logger ?? null;
|
|
3674
4062
|
this.api = new ApiClient(
|
|
3675
4063
|
{
|
|
3676
4064
|
baseUrl: _config.baseUrl ?? PUBLIC_API_BASE_URL,
|
|
3677
4065
|
xApiVersion: API_VERSION
|
|
3678
4066
|
},
|
|
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(),
|
|
4067
|
+
(trigger) => auth.tokenFor(trigger),
|
|
3684
4068
|
logger
|
|
3685
4069
|
);
|
|
3686
|
-
|
|
4070
|
+
const auth = new ServerAuthNamespaceImpl(this.api, {
|
|
3687
4071
|
..._config,
|
|
3688
|
-
accessTokenExpiryBufferSeconds: _config.accessTokenExpiryBufferSeconds ??
|
|
3689
|
-
strict: _config.strict ?? false
|
|
4072
|
+
accessTokenExpiryBufferSeconds: _config.accessTokenExpiryBufferSeconds ?? ACCESS_TOKEN_EXPIRY_BUFFER,
|
|
4073
|
+
strict: _config.strict ?? false,
|
|
4074
|
+
refreshes: processRefreshCoordinator,
|
|
4075
|
+
...systemTime
|
|
3690
4076
|
});
|
|
4077
|
+
this.auth = auth;
|
|
3691
4078
|
const ctx = {
|
|
3692
4079
|
api: this.api,
|
|
3693
4080
|
rpc: publicClients(this.api, _config.rpc, _config.logger ?? null),
|
|
@@ -3865,9 +4252,13 @@ function pinoServerLogger(options) {
|
|
|
3865
4252
|
options.level
|
|
3866
4253
|
);
|
|
3867
4254
|
}
|
|
4255
|
+
|
|
4256
|
+
// src/server/core/server-auth-namespace.ts
|
|
4257
|
+
function emptySessionStore() {
|
|
4258
|
+
return { getSession: async () => null };
|
|
4259
|
+
}
|
|
3868
4260
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3869
4261
|
0 && (module.exports = {
|
|
3870
|
-
ServerAuthNamespaceImpl,
|
|
3871
4262
|
ServerOffersNamespaceImpl,
|
|
3872
4263
|
ServerRequirementsNamespaceImpl,
|
|
3873
4264
|
WritableSessionStoreRequiredError,
|