@coinlist-co/react 0.4.0 → 0.5.1
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-CRACFEJ4.js +17 -0
- package/dist/chunk-CRACFEJ4.js.map +1 -0
- package/dist/{chunk-NC45IO63.js → chunk-N3WBC2VS.js} +20 -325
- package/dist/chunk-N3WBC2VS.js.map +1 -0
- package/dist/chunk-V6WO67RO.js +311 -0
- package/dist/chunk-V6WO67RO.js.map +1 -0
- package/dist/client/index.cjs +1013 -220
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +526 -9
- package/dist/client/index.d.ts +526 -9
- package/dist/client/index.js +1464 -94
- package/dist/client/index.js.map +1 -1
- package/dist/{participation-DMMEDxON.d.cts → requirement-BEO42QOr.d.cts} +204 -2
- package/dist/{participation-E-bT-GXJ.d.ts → requirement-BEO42QOr.d.ts} +204 -2
- package/dist/server/index.cjs +50 -100
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +67 -24
- package/dist/server/index.d.ts +67 -24
- package/dist/server/index.js +54 -87
- package/dist/server/index.js.map +1 -1
- package/dist/shared/index.cjs +375 -0
- package/dist/shared/index.cjs.map +1 -0
- package/dist/shared/index.d.cts +35 -0
- package/dist/shared/index.d.ts +35 -0
- package/dist/{client/core → shared}/index.js +35 -16
- package/dist/shared/index.js.map +1 -0
- package/package.json +7 -22
- package/dist/RequirementItem-B-MvcxSr.d.cts +0 -37
- package/dist/RequirementItem-DcYot2uC.d.ts +0 -37
- package/dist/chunk-EHZ6PIGN.js +0 -414
- package/dist/chunk-EHZ6PIGN.js.map +0 -1
- package/dist/chunk-NC45IO63.js.map +0 -1
- package/dist/chunk-RIFATQB5.js +0 -257
- package/dist/chunk-RIFATQB5.js.map +0 -1
- package/dist/chunk-UGJUTRXC.js +0 -304
- package/dist/chunk-UGJUTRXC.js.map +0 -1
- package/dist/chunk-YJQMJFA7.js +0 -16
- package/dist/chunk-YJQMJFA7.js.map +0 -1
- package/dist/client/components/index.cjs +0 -1108
- package/dist/client/components/index.cjs.map +0 -1
- package/dist/client/components/index.d.cts +0 -139
- package/dist/client/components/index.d.ts +0 -139
- package/dist/client/components/index.js +0 -558
- package/dist/client/components/index.js.map +0 -1
- package/dist/client/core/index.cjs +0 -1047
- package/dist/client/core/index.cjs.map +0 -1
- package/dist/client/core/index.d.cts +0 -23
- package/dist/client/core/index.d.ts +0 -23
- package/dist/client/core/index.js.map +0 -1
- package/dist/client/hooks/index.cjs +0 -307
- package/dist/client/hooks/index.cjs.map +0 -1
- package/dist/client/hooks/index.d.cts +0 -126
- package/dist/client/hooks/index.d.ts +0 -126
- package/dist/client/hooks/index.js +0 -19
- package/dist/client/hooks/index.js.map +0 -1
- package/dist/coinlist-client-BSxpqylo.d.ts +0 -158
- package/dist/coinlist-client-Cah5c_aF.d.cts +0 -158
- package/dist/requirement-XWT0T_zO.d.cts +0 -205
- package/dist/requirement-XWT0T_zO.d.ts +0 -205
- package/dist/useCoinListRequirements-B5-ZPOMz.d.ts +0 -80
- package/dist/useCoinListRequirements-Cuc32Tqw.d.cts +0 -80
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
import { A as AuthorizationCode,
|
|
2
|
-
export { B as Blockchain, C as ClientId, b as CodeChallenge, e as Cursor, f as OAuthRefreshToken, h as PaginatedResponseDto, l as ParticipationStatus, R as RedirectUri, W as WalletAddress, n as fetchAllPages } from '../participation-E-bT-GXJ.js';
|
|
3
|
-
import { O as Offer, b as OfferId, a as OfferDetail, d as OfferOptionId, R as Requirement, h as RequirementStatusInfo } from '../requirement-XWT0T_zO.js';
|
|
4
|
-
export { F as FaqItem, L as Link, M as Milestone, c as OfferOption, e as OfferOptionSlug, f as OfferSlug, g as RequirementId, i as RequirementStatusValue, j as RequirementType, T as TermItem } from '../requirement-XWT0T_zO.js';
|
|
1
|
+
import { A as AuthorizationCode, C as CodeVerifier, n as OAuthSession, O as OAuthAccessToken, b as Offer, P as PaginationParams, c as PaginatedResponse, d as OfferId, e as OfferDetail, f as Participation, g as ParticipationsPaginationParams, h as ParticipationId, i as CreateParticipationParams, j as OfferOptionId, R as Requirement, k as RequirementStatusInfo, a as Config, o as ClientSecret } from '../requirement-BEO42QOr.js';
|
|
5
2
|
|
|
6
3
|
interface SessionStore {
|
|
7
4
|
getSession(): Promise<OAuthSession | null>;
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Persists or clears the OAuth session.
|
|
7
|
+
*
|
|
8
|
+
* **Omit this method to create a read-only store.** When absent, the SDK
|
|
9
|
+
* skips token refresh entirely — no network call is made and no refresh
|
|
10
|
+
* token is consumed. This is the correct approach for contexts that can read
|
|
11
|
+
* the session but cannot write it back, such as Next.js Server Components.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ Do **not** implement this as a no-op (`async () => {}`). If the method
|
|
14
|
+
* is present, the SDK assumes writes succeed: it will fire a token refresh
|
|
15
|
+
* network call, consume the refresh token, then invoke `setSession` — which
|
|
16
|
+
* would silently discard the new session and leave the browser holding an
|
|
17
|
+
* invalidated refresh token. Simply **omit** `setSession` to prevent any
|
|
18
|
+
* refresh from being attempted.
|
|
19
|
+
*
|
|
20
|
+
* {@link CoinListServer.completeOAuth} and {@link CoinListServer.logout}
|
|
21
|
+
* always require a writable store and throw
|
|
22
|
+
* {@link WritableSessionStoreRequiredError} if `setSession` is absent.
|
|
23
|
+
*/
|
|
24
|
+
setSession?(session: OAuthSession | null): Promise<void>;
|
|
9
25
|
}
|
|
10
26
|
interface ServerConfig extends Config {
|
|
11
27
|
readonly clientSecret: ClientSecret;
|
|
@@ -18,19 +34,56 @@ interface ServerConfig extends Config {
|
|
|
18
34
|
*/
|
|
19
35
|
readonly strict?: boolean;
|
|
20
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Server-side CoinList SDK client.
|
|
39
|
+
*
|
|
40
|
+
* Operates in one of two modes depending on whether {@link SessionStore}
|
|
41
|
+
* includes a `setSession` implementation:
|
|
42
|
+
*
|
|
43
|
+
* - **Writable store** (`setSession` provided) — full functionality: token
|
|
44
|
+
* refresh, {@link completeOAuth}, and {@link logout} all work normally.
|
|
45
|
+
*
|
|
46
|
+
* - **Read-only store** (no `setSession`) — token refresh is skipped entirely,
|
|
47
|
+
* meaning no network call is made and no refresh token is consumed.
|
|
48
|
+
* {@link completeOAuth} and {@link logout} throw
|
|
49
|
+
* {@link WritableSessionStoreRequiredError}. {@link accessToken} may return
|
|
50
|
+
* an expired token (see its docs). Use this mode in execution contexts that
|
|
51
|
+
* can read the session but cannot write it back, such as Next.js Server
|
|
52
|
+
* Components.
|
|
53
|
+
*/
|
|
21
54
|
interface CoinListServer {
|
|
55
|
+
/**
|
|
56
|
+
* Exchanges an authorization code for an OAuth session and persists it via
|
|
57
|
+
* {@link SessionStore.setSession}.
|
|
58
|
+
*
|
|
59
|
+
* Throws {@link WritableSessionStoreRequiredError} if the session store does
|
|
60
|
+
* not provide `setSession`.
|
|
61
|
+
*/
|
|
22
62
|
completeOAuth(code: AuthorizationCode, codeVerifier: CodeVerifier): Promise<OAuthSession>;
|
|
23
63
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* 2. Renewing the session if it's expired (or about to expire)
|
|
64
|
+
* Returns a valid access token for the current session, refreshing it if
|
|
65
|
+
* it is expired or near expiry.
|
|
27
66
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
67
|
+
* **Writable store**: if the token is expired, the SDK exchanges the refresh
|
|
68
|
+
* token for a new session, persists it, and returns the fresh access token.
|
|
69
|
+
* Returns `null` if there is no session or the refresh fails.
|
|
70
|
+
*
|
|
71
|
+
* **Read-only store** (no `setSession`): refresh is skipped entirely. The
|
|
72
|
+
* stored token is returned as-is, even if it is expired — a non-null return
|
|
73
|
+
* value does **not** guarantee the token is accepted by the API. Before
|
|
74
|
+
* making API calls, check `token.expiresAt > new Date()`. Use a writable
|
|
75
|
+
* store (e.g. in a Route Handler) when you need the SDK to renew the session
|
|
76
|
+
* automatically.
|
|
77
|
+
*
|
|
78
|
+
* @returns the access token, or `null` if there is no session or the session
|
|
79
|
+
* could not be refreshed.
|
|
30
80
|
*/
|
|
31
81
|
accessToken(): Promise<OAuthAccessToken | null>;
|
|
32
82
|
/**
|
|
33
83
|
* Revokes the current token via POST /oauth/revoke and clears the session.
|
|
84
|
+
*
|
|
85
|
+
* Throws {@link WritableSessionStoreRequiredError} if the session store does
|
|
86
|
+
* not provide `setSession`.
|
|
34
87
|
*/
|
|
35
88
|
logout(): Promise<void>;
|
|
36
89
|
/**
|
|
@@ -38,7 +91,7 @@ interface CoinListServer {
|
|
|
38
91
|
*
|
|
39
92
|
* Throws {@link NotAuthenticatedError} if the user is not authenticated.
|
|
40
93
|
*/
|
|
41
|
-
|
|
94
|
+
fetchOffers(): Promise<Offer[]>;
|
|
42
95
|
/**
|
|
43
96
|
* Fetches a single page of offers.
|
|
44
97
|
*
|
|
@@ -56,7 +109,7 @@ interface CoinListServer {
|
|
|
56
109
|
*
|
|
57
110
|
* Throws {@link NotAuthenticatedError} if the user is not authenticated.
|
|
58
111
|
*/
|
|
59
|
-
|
|
112
|
+
fetchParticipations(offerId?: OfferId): Promise<Participation[]>;
|
|
60
113
|
/**
|
|
61
114
|
* Fetches a single page of participations, optionally filtered by offer.
|
|
62
115
|
*
|
|
@@ -90,18 +143,8 @@ interface CoinListServer {
|
|
|
90
143
|
}
|
|
91
144
|
declare function createCoinListServer(config: ServerConfig): CoinListServer;
|
|
92
145
|
|
|
93
|
-
|
|
94
|
-
* Error thrown when a feature or code path is not yet implemented.
|
|
95
|
-
*/
|
|
96
|
-
declare class NotImplementedError extends Error {
|
|
97
|
-
constructor(message?: string);
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Error thrown when accessing a feature that requires authentication
|
|
101
|
-
* without being authenticated.
|
|
102
|
-
*/
|
|
103
|
-
declare class NotAuthenticatedError extends Error {
|
|
146
|
+
declare class WritableSessionStoreRequiredError extends Error {
|
|
104
147
|
constructor(message?: string);
|
|
105
148
|
}
|
|
106
149
|
|
|
107
|
-
export {
|
|
150
|
+
export { type CoinListServer, type ServerConfig, type SessionStore, WritableSessionStoreRequiredError, createCoinListServer };
|
package/dist/server/index.js
CHANGED
|
@@ -1,54 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
OAuthRefreshToken,
|
|
3
|
-
OAuthSession
|
|
4
|
-
} from "../chunk-5E3P7AMH.js";
|
|
5
1
|
import {
|
|
6
2
|
API_VERSION,
|
|
7
3
|
AuthenticatedApiClient,
|
|
8
|
-
AuthorizationCode,
|
|
9
|
-
Blockchain,
|
|
10
|
-
ClientId,
|
|
11
|
-
ClientSecret,
|
|
12
|
-
CodeChallenge,
|
|
13
|
-
CodeVerifier,
|
|
14
|
-
CreateParticipationParams,
|
|
15
|
-
Cursor,
|
|
16
|
-
FaqItem,
|
|
17
4
|
HttpError,
|
|
18
|
-
Link,
|
|
19
|
-
Milestone,
|
|
20
|
-
NotAuthenticatedError,
|
|
21
|
-
NotImplementedError,
|
|
22
|
-
Offer,
|
|
23
|
-
OfferDetail,
|
|
24
|
-
OfferId,
|
|
25
|
-
OfferOption,
|
|
26
|
-
OfferOptionId,
|
|
27
|
-
OfferOptionSlug,
|
|
28
|
-
OfferSlug,
|
|
29
5
|
PUBLIC_API_BASE_URL,
|
|
30
|
-
PaginatedResponse,
|
|
31
|
-
PaginationParams,
|
|
32
|
-
Participation,
|
|
33
|
-
ParticipationId,
|
|
34
|
-
ParticipationsPaginationParams,
|
|
35
|
-
RedirectUri,
|
|
36
|
-
Requirement,
|
|
37
|
-
RequirementId,
|
|
38
|
-
RequirementStatusInfo,
|
|
39
|
-
TermItem,
|
|
40
|
-
WalletAddress,
|
|
41
6
|
createParticipation,
|
|
42
|
-
fetchAllOffers,
|
|
43
|
-
fetchAllPages,
|
|
44
|
-
fetchAllParticipations,
|
|
45
7
|
fetchOfferDetails,
|
|
46
8
|
fetchOfferRequirements,
|
|
9
|
+
fetchOffers,
|
|
47
10
|
fetchOffersPage,
|
|
48
11
|
fetchParticipation,
|
|
12
|
+
fetchParticipations,
|
|
49
13
|
fetchParticipationsPage,
|
|
50
14
|
fetchRequirementStatuses
|
|
51
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-N3WBC2VS.js";
|
|
16
|
+
import {
|
|
17
|
+
OAuthSession
|
|
18
|
+
} from "../chunk-5E3P7AMH.js";
|
|
19
|
+
import {
|
|
20
|
+
NotAuthenticatedError
|
|
21
|
+
} from "../chunk-V6WO67RO.js";
|
|
52
22
|
|
|
53
23
|
// src/server/api/api.server.ts
|
|
54
24
|
var Api = class {
|
|
@@ -60,6 +30,14 @@ var Api = class {
|
|
|
60
30
|
}
|
|
61
31
|
};
|
|
62
32
|
|
|
33
|
+
// src/server/errors.ts
|
|
34
|
+
var WritableSessionStoreRequiredError = class extends Error {
|
|
35
|
+
constructor(message = "This operation requires a writable SessionStore. Provide a `setSession` implementation in your SessionStore to persist or clear OAuth sessions.") {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "WritableSessionStoreRequiredError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
63
41
|
// src/server/coinlist.server.ts
|
|
64
42
|
var ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS = 60;
|
|
65
43
|
var CoinListServerImpl = class {
|
|
@@ -73,10 +51,19 @@ var CoinListServerImpl = class {
|
|
|
73
51
|
baseUrl: this.baseUrl,
|
|
74
52
|
xApiVersion: API_VERSION
|
|
75
53
|
},
|
|
76
|
-
|
|
54
|
+
// When refresh=true the renewal middleware has received a 401 and wants a
|
|
55
|
+
// fresh token. A read-only store cannot persist a new session, so return
|
|
56
|
+
// null immediately — this tells the middleware to skip the retry rather
|
|
57
|
+
// than re-sending with the same expired token and wasting a round-trip.
|
|
58
|
+
(refresh) => refresh && !this._config.sessionStore.setSession ? Promise.resolve(null) : this.accessToken()
|
|
77
59
|
);
|
|
78
60
|
}
|
|
79
61
|
async completeOAuth(code, codeVerifier) {
|
|
62
|
+
const sessionStore = this._config.sessionStore;
|
|
63
|
+
const setSession = sessionStore.setSession?.bind(sessionStore);
|
|
64
|
+
if (!setSession) {
|
|
65
|
+
throw new WritableSessionStoreRequiredError();
|
|
66
|
+
}
|
|
80
67
|
const sessionDto = await this.api.send({
|
|
81
68
|
method: "POST",
|
|
82
69
|
url: `/oauth/token`,
|
|
@@ -90,11 +77,12 @@ var CoinListServerImpl = class {
|
|
|
90
77
|
}
|
|
91
78
|
});
|
|
92
79
|
const session = OAuthSession.fromDto(sessionDto);
|
|
93
|
-
|
|
80
|
+
await setSession(session);
|
|
94
81
|
return session;
|
|
95
82
|
}
|
|
96
83
|
async accessToken() {
|
|
97
|
-
const
|
|
84
|
+
const sessionStore = this._config.sessionStore;
|
|
85
|
+
const session = await sessionStore.getSession();
|
|
98
86
|
if (session == null) return null;
|
|
99
87
|
const now = Date.now();
|
|
100
88
|
const expiresAt = session.accessToken.expiresAt.getTime();
|
|
@@ -102,9 +90,16 @@ var CoinListServerImpl = class {
|
|
|
102
90
|
if (expiresAt > now + bufferMs) {
|
|
103
91
|
return session.accessToken;
|
|
104
92
|
}
|
|
105
|
-
const
|
|
93
|
+
const setSession = sessionStore.setSession?.bind(sessionStore);
|
|
94
|
+
if (!setSession) {
|
|
95
|
+
return session.accessToken;
|
|
96
|
+
} else {
|
|
97
|
+
return this.refreshSession(session.refreshToken, setSession);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async refreshSession(refreshToken, setSession) {
|
|
106
101
|
if (!refreshToken) {
|
|
107
|
-
await
|
|
102
|
+
await setSession(null);
|
|
108
103
|
return null;
|
|
109
104
|
}
|
|
110
105
|
try {
|
|
@@ -119,15 +114,20 @@ var CoinListServerImpl = class {
|
|
|
119
114
|
}
|
|
120
115
|
});
|
|
121
116
|
const newSession = OAuthSession.fromDto(sessionDto);
|
|
122
|
-
await
|
|
117
|
+
await setSession(newSession);
|
|
123
118
|
return newSession.accessToken;
|
|
124
119
|
} catch {
|
|
125
|
-
await
|
|
120
|
+
await setSession(null);
|
|
126
121
|
return null;
|
|
127
122
|
}
|
|
128
123
|
}
|
|
129
124
|
async logout() {
|
|
130
|
-
const
|
|
125
|
+
const sessionStore = this._config.sessionStore;
|
|
126
|
+
const setSession = sessionStore.setSession?.bind(sessionStore);
|
|
127
|
+
if (!setSession) {
|
|
128
|
+
throw new WritableSessionStoreRequiredError();
|
|
129
|
+
}
|
|
130
|
+
const session = await sessionStore.getSession();
|
|
131
131
|
if (session != null) {
|
|
132
132
|
const tokenToRevoke = session.accessToken.value;
|
|
133
133
|
try {
|
|
@@ -149,7 +149,7 @@ var CoinListServerImpl = class {
|
|
|
149
149
|
throw err;
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
await
|
|
152
|
+
await setSession(null);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
async ensureAuthenticated() {
|
|
@@ -158,9 +158,9 @@ var CoinListServerImpl = class {
|
|
|
158
158
|
throw new NotAuthenticatedError();
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
async
|
|
161
|
+
async fetchOffers() {
|
|
162
162
|
await this.ensureAuthenticated();
|
|
163
|
-
return
|
|
163
|
+
return fetchOffers(this.api);
|
|
164
164
|
}
|
|
165
165
|
async fetchOffersPage(params) {
|
|
166
166
|
await this.ensureAuthenticated();
|
|
@@ -170,9 +170,9 @@ var CoinListServerImpl = class {
|
|
|
170
170
|
await this.ensureAuthenticated();
|
|
171
171
|
return fetchOfferDetails(this.api, id);
|
|
172
172
|
}
|
|
173
|
-
async
|
|
173
|
+
async fetchParticipations(offerId) {
|
|
174
174
|
await this.ensureAuthenticated();
|
|
175
|
-
return
|
|
175
|
+
return fetchParticipations(this.api, offerId);
|
|
176
176
|
}
|
|
177
177
|
async fetchParticipationsPage(params) {
|
|
178
178
|
await this.ensureAuthenticated();
|
|
@@ -199,40 +199,7 @@ function createCoinListServer(config) {
|
|
|
199
199
|
return new CoinListServerImpl(config);
|
|
200
200
|
}
|
|
201
201
|
export {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
ClientId,
|
|
205
|
-
ClientSecret,
|
|
206
|
-
CodeChallenge,
|
|
207
|
-
CodeVerifier,
|
|
208
|
-
CreateParticipationParams,
|
|
209
|
-
Cursor,
|
|
210
|
-
FaqItem,
|
|
211
|
-
Link,
|
|
212
|
-
Milestone,
|
|
213
|
-
NotAuthenticatedError,
|
|
214
|
-
NotImplementedError,
|
|
215
|
-
OAuthRefreshToken,
|
|
216
|
-
OAuthSession,
|
|
217
|
-
Offer,
|
|
218
|
-
OfferDetail,
|
|
219
|
-
OfferId,
|
|
220
|
-
OfferOption,
|
|
221
|
-
OfferOptionId,
|
|
222
|
-
OfferOptionSlug,
|
|
223
|
-
OfferSlug,
|
|
224
|
-
PaginatedResponse,
|
|
225
|
-
PaginationParams,
|
|
226
|
-
Participation,
|
|
227
|
-
ParticipationId,
|
|
228
|
-
ParticipationsPaginationParams,
|
|
229
|
-
RedirectUri,
|
|
230
|
-
Requirement,
|
|
231
|
-
RequirementId,
|
|
232
|
-
RequirementStatusInfo,
|
|
233
|
-
TermItem,
|
|
234
|
-
WalletAddress,
|
|
235
|
-
createCoinListServer,
|
|
236
|
-
fetchAllPages
|
|
202
|
+
WritableSessionStoreRequiredError,
|
|
203
|
+
createCoinListServer
|
|
237
204
|
};
|
|
238
205
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/api/api.server.ts","../../src/server/coinlist.server.ts"],"sourcesContent":["import { AuthenticatedApiClient } from '@/shared/api/authenticated-api-client';\nimport type { HttpClientConfig, HttpRequest } from '@/shared/api/http';\nimport type { OAuthAccessToken } from '@/shared/types/oauth-session';\n\nexport class Api {\n private readonly client: AuthenticatedApiClient;\n\n constructor(\n config: HttpClientConfig,\n fetchAccessToken: (refresh: boolean) => Promise<OAuthAccessToken | null>\n ) {\n this.client = new AuthenticatedApiClient(config, fetchAccessToken);\n }\n\n async send<TResponse>(request: HttpRequest): Promise<TResponse> {\n return this.client.send<TResponse>(request);\n }\n}\n","import { Api } from '@/server/api/api.server';\nimport { API_VERSION, PUBLIC_API_BASE_URL } from '@/shared/api/frontline';\nimport * as offersApi from '@/shared/api/frontline/offers';\nimport * as participationsApi from '@/shared/api/frontline/participations';\nimport * as requirementsApi from '@/shared/api/frontline/requirements';\nimport { HttpError } from '@/shared/api/http';\nimport type {\n PaginatedResponse,\n PaginationParams,\n} from '@/shared/api/pagination';\nimport type { Config } from '@/shared/types/config';\nimport type { OAuthSessionDto } from '@/shared/types/dto/oauth-session';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type {\n AuthorizationCode,\n ClientSecret,\n CodeVerifier,\n} from '@/shared/types/oauth';\nimport {\n type OAuthAccessToken,\n OAuthSession,\n} from '@/shared/types/oauth-session';\nimport type { Offer, OfferId } from '@/shared/types/offer';\nimport type { OfferDetail, OfferOptionId } from '@/shared/types/offer-detail';\nimport type {\n CreateParticipationParams,\n Participation,\n ParticipationId,\n ParticipationsPaginationParams,\n} from '@/shared/types/participation';\nimport type {\n Requirement,\n RequirementStatusInfo,\n} from '@/shared/types/requirement';\n\n/** Buffer in seconds before expiry to consider token expired for refresh. */\nconst ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS = 60;\n\nexport interface SessionStore {\n getSession(): Promise<OAuthSession | null>;\n setSession(session: OAuthSession | null): Promise<void>;\n}\n\nexport interface ServerConfig extends Config {\n readonly clientSecret: ClientSecret;\n readonly sessionStore: SessionStore;\n /** Buffer in seconds before expiry to consider token expired for refresh. */\n readonly accessTokenExpiryBufferSeconds?: number;\n /**\n * Whether SDK will throw an exception in cases it can be silent.\n * For example, if token revokation on logout fails.\n */\n readonly strict?: boolean;\n}\n\nexport interface CoinListServer {\n completeOAuth(\n code: AuthorizationCode,\n codeVerifier: CodeVerifier\n ): Promise<OAuthSession>;\n\n /**\n * Responsible for:\n * 1. Returning a securely stored access token from Cookies\n * 2. Renewing the session if it's expired (or about to expire)\n *\n * @returns a valid access token or null if the user is not authentication.\n * On error rejects the Promise and throws.\n */\n accessToken(): Promise<OAuthAccessToken | null>;\n\n /**\n * Revokes the current token via POST /oauth/revoke and clears the session.\n */\n logout(): Promise<void>;\n\n /**\n * Fetches all offers by iterating through every paginated response.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchAllOffers(): Promise<Offer[]>;\n\n /**\n * Fetches a single page of offers.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOffersPage(params: PaginationParams): Promise<PaginatedResponse<Offer>>;\n\n /**\n * Fetches details for a given offer by its id.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOfferDetails(id: OfferId): Promise<OfferDetail>;\n\n /**\n * Fetches all participations by iterating through every paginated response.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchAllParticipations(offerId?: OfferId): Promise<Participation[]>;\n\n /**\n * Fetches a single page of participations, optionally filtered by offer.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchParticipationsPage(\n params: ParticipationsPaginationParams\n ): Promise<PaginatedResponse<Participation>>;\n\n /**\n * Fetches a participation by id.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchParticipation(id: ParticipationId): Promise<Participation>;\n\n /**\n * Creates a participation.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n createParticipation(\n params: CreateParticipationParams\n ): Promise<Participation>;\n\n /**\n * Fetches the requirements for all options of a given offer, grouped by option ID.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOfferRequirements(\n offerId: OfferId\n ): Promise<Record<OfferOptionId, Requirement[]>>;\n\n /**\n * Fetches the user's requirement statuses for a given offer.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchRequirementStatuses(offerId: OfferId): Promise<RequirementStatusInfo[]>;\n}\n\nclass CoinListServerImpl implements CoinListServer {\n private readonly api: Api;\n private readonly baseUrl: string;\n\n private readonly accessTokenExpiryBufferSeconds: number;\n private readonly strict: boolean;\n\n constructor(private readonly _config: ServerConfig) {\n this.baseUrl = _config.baseUrl ?? PUBLIC_API_BASE_URL;\n this.accessTokenExpiryBufferSeconds =\n _config.accessTokenExpiryBufferSeconds ??\n ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS;\n this.strict = _config.strict ?? false;\n this.api = new Api(\n {\n baseUrl: this.baseUrl,\n xApiVersion: API_VERSION,\n },\n (_refresh) => this.accessToken()\n );\n }\n\n async completeOAuth(\n code: AuthorizationCode,\n codeVerifier: CodeVerifier\n ): Promise<OAuthSession> {\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'authorization_code',\n code,\n redirect_uri: this._config.redirectUri,\n client_id: this._config.clientId,\n client_secret: this._config.clientSecret,\n code_verifier: codeVerifier,\n },\n });\n const session = OAuthSession.fromDto(sessionDto);\n this._config.sessionStore.setSession(session);\n return session;\n }\n\n async accessToken(): Promise<OAuthAccessToken | null> {\n const session = await this._config.sessionStore.getSession();\n if (session == null) return null;\n\n const now = Date.now();\n const expiresAt = session.accessToken.expiresAt.getTime();\n const bufferMs = this.accessTokenExpiryBufferSeconds * 1000;\n if (expiresAt > now + bufferMs) {\n return session.accessToken;\n }\n\n const refreshToken = session?.refreshToken;\n if (!refreshToken) {\n await this._config.sessionStore.setSession(null);\n return null;\n }\n\n try {\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: this._config.clientId,\n client_secret: this._config.clientSecret,\n },\n });\n const newSession = OAuthSession.fromDto(sessionDto);\n await this._config.sessionStore.setSession(newSession);\n return newSession.accessToken;\n } catch {\n await this._config.sessionStore.setSession(null);\n return null;\n }\n }\n\n async logout(): Promise<void> {\n const session = await this._config.sessionStore.getSession();\n if (session != null) {\n const tokenToRevoke = session.accessToken.value;\n try {\n await this.api.send({\n method: 'POST',\n url: `/oauth/revoke`,\n body: {\n token: tokenToRevoke,\n client_id: this._config.clientId,\n client_secret: this._config.clientSecret,\n },\n });\n } catch (err) {\n if (err instanceof HttpError) {\n if (this.strict) {\n throw err;\n }\n } else {\n throw err;\n }\n }\n await this._config.sessionStore.setSession(null);\n }\n }\n\n private async ensureAuthenticated(): Promise<void> {\n const token = await this.accessToken();\n if (token === null) {\n throw new NotAuthenticatedError();\n }\n }\n\n async fetchAllOffers(): Promise<Offer[]> {\n await this.ensureAuthenticated();\n return offersApi.fetchAllOffers(this.api);\n }\n\n async fetchOffersPage(\n params: PaginationParams\n ): Promise<PaginatedResponse<Offer>> {\n await this.ensureAuthenticated();\n return offersApi.fetchOffersPage(this.api, params);\n }\n\n async fetchOfferDetails(id: OfferId): Promise<OfferDetail> {\n await this.ensureAuthenticated();\n return offersApi.fetchOfferDetails(this.api, id);\n }\n\n async fetchAllParticipations(offerId?: OfferId): Promise<Participation[]> {\n await this.ensureAuthenticated();\n return participationsApi.fetchAllParticipations(this.api, offerId);\n }\n\n async fetchParticipationsPage(\n params: ParticipationsPaginationParams\n ): Promise<PaginatedResponse<Participation>> {\n await this.ensureAuthenticated();\n return participationsApi.fetchParticipationsPage(this.api, params);\n }\n\n async fetchParticipation(id: ParticipationId): Promise<Participation> {\n await this.ensureAuthenticated();\n return participationsApi.fetchParticipation(this.api, id);\n }\n\n async createParticipation(\n params: CreateParticipationParams\n ): Promise<Participation> {\n await this.ensureAuthenticated();\n return participationsApi.createParticipation(this.api, params);\n }\n\n async fetchOfferRequirements(\n offerId: OfferId\n ): Promise<Record<OfferOptionId, Requirement[]>> {\n await this.ensureAuthenticated();\n return requirementsApi.fetchOfferRequirements(this.api, offerId);\n }\n\n async fetchRequirementStatuses(\n offerId: OfferId\n ): Promise<RequirementStatusInfo[]> {\n await this.ensureAuthenticated();\n return requirementsApi.fetchRequirementStatuses(this.api, offerId);\n }\n}\n\nexport function createCoinListServer(config: ServerConfig): CoinListServer {\n return new CoinListServerImpl(config);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/api/api.server.ts","../../src/server/errors.ts","../../src/server/coinlist.server.ts"],"sourcesContent":["import { AuthenticatedApiClient } from '@/shared/api/authenticated-api-client';\nimport type { HttpClientConfig, HttpRequest } from '@/shared/api/http';\nimport type { OAuthAccessToken } from '@/shared/types/oauth-session';\n\nexport class Api {\n private readonly client: AuthenticatedApiClient;\n\n constructor(\n config: HttpClientConfig,\n fetchAccessToken: (refresh: boolean) => Promise<OAuthAccessToken | null>\n ) {\n this.client = new AuthenticatedApiClient(config, fetchAccessToken);\n }\n\n async send<TResponse>(request: HttpRequest): Promise<TResponse> {\n return this.client.send<TResponse>(request);\n }\n}\n","export class WritableSessionStoreRequiredError extends Error {\n constructor(\n message = 'This operation requires a writable SessionStore. Provide a `setSession` implementation in your SessionStore to persist or clear OAuth sessions.'\n ) {\n super(message);\n this.name = 'WritableSessionStoreRequiredError';\n }\n}\n","import { Api } from '@/server/api/api.server';\nimport { WritableSessionStoreRequiredError } from '@/server/errors';\nimport {\n API_VERSION,\n PUBLIC_API_BASE_URL,\n} from '@/shared/api/frontline/config';\nimport * as offersApi from '@/shared/api/frontline/offers';\nimport * as participationsApi from '@/shared/api/frontline/participations';\nimport * as requirementsApi from '@/shared/api/frontline/requirements';\nimport { HttpError } from '@/shared/api/http';\nimport type {\n PaginatedResponse,\n PaginationParams,\n} from '@/shared/api/pagination';\nimport type { Config } from '@/shared/types/config';\nimport type { OAuthSessionDto } from '@/shared/types/dto/oauth-session';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type {\n AuthorizationCode,\n ClientSecret,\n CodeVerifier,\n} from '@/shared/types/oauth';\nimport {\n type OAuthAccessToken,\n type OAuthRefreshToken,\n OAuthSession,\n} from '@/shared/types/oauth-session';\nimport type { Offer, OfferId } from '@/shared/types/offer';\nimport type { OfferDetail, OfferOptionId } from '@/shared/types/offer-detail';\nimport type {\n CreateParticipationParams,\n Participation,\n ParticipationId,\n ParticipationsPaginationParams,\n} from '@/shared/types/participation';\nimport type {\n Requirement,\n RequirementStatusInfo,\n} from '@/shared/types/requirement';\n\n/** Buffer in seconds before expiry to consider token expired for refresh. */\nconst ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS = 60;\n\nexport interface SessionStore {\n getSession(): Promise<OAuthSession | null>;\n /**\n * Persists or clears the OAuth session.\n *\n * **Omit this method to create a read-only store.** When absent, the SDK\n * skips token refresh entirely — no network call is made and no refresh\n * token is consumed. This is the correct approach for contexts that can read\n * the session but cannot write it back, such as Next.js Server Components.\n *\n * ⚠️ Do **not** implement this as a no-op (`async () => {}`). If the method\n * is present, the SDK assumes writes succeed: it will fire a token refresh\n * network call, consume the refresh token, then invoke `setSession` — which\n * would silently discard the new session and leave the browser holding an\n * invalidated refresh token. Simply **omit** `setSession` to prevent any\n * refresh from being attempted.\n *\n * {@link CoinListServer.completeOAuth} and {@link CoinListServer.logout}\n * always require a writable store and throw\n * {@link WritableSessionStoreRequiredError} if `setSession` is absent.\n */\n setSession?(session: OAuthSession | null): Promise<void>;\n}\n\nexport interface ServerConfig extends Config {\n readonly clientSecret: ClientSecret;\n readonly sessionStore: SessionStore;\n /** Buffer in seconds before expiry to consider token expired for refresh. */\n readonly accessTokenExpiryBufferSeconds?: number;\n /**\n * Whether SDK will throw an exception in cases it can be silent.\n * For example, if token revokation on logout fails.\n */\n readonly strict?: boolean;\n}\n\n/**\n * Server-side CoinList SDK client.\n *\n * Operates in one of two modes depending on whether {@link SessionStore}\n * includes a `setSession` implementation:\n *\n * - **Writable store** (`setSession` provided) — full functionality: token\n * refresh, {@link completeOAuth}, and {@link logout} all work normally.\n *\n * - **Read-only store** (no `setSession`) — token refresh is skipped entirely,\n * meaning no network call is made and no refresh token is consumed.\n * {@link completeOAuth} and {@link logout} throw\n * {@link WritableSessionStoreRequiredError}. {@link accessToken} may return\n * an expired token (see its docs). Use this mode in execution contexts that\n * can read the session but cannot write it back, such as Next.js Server\n * Components.\n */\nexport interface CoinListServer {\n /**\n * Exchanges an authorization code for an OAuth session and persists it via\n * {@link SessionStore.setSession}.\n *\n * Throws {@link WritableSessionStoreRequiredError} if the session store does\n * not provide `setSession`.\n */\n completeOAuth(\n code: AuthorizationCode,\n codeVerifier: CodeVerifier\n ): Promise<OAuthSession>;\n\n /**\n * Returns a valid access token for the current session, refreshing it if\n * it is expired or near expiry.\n *\n * **Writable store**: if the token is expired, the SDK exchanges the refresh\n * token for a new session, persists it, and returns the fresh access token.\n * Returns `null` if there is no session or the refresh fails.\n *\n * **Read-only store** (no `setSession`): refresh is skipped entirely. The\n * stored token is returned as-is, even if it is expired — a non-null return\n * value does **not** guarantee the token is accepted by the API. Before\n * making API calls, check `token.expiresAt > new Date()`. Use a writable\n * store (e.g. in a Route Handler) when you need the SDK to renew the session\n * automatically.\n *\n * @returns the access token, or `null` if there is no session or the session\n * could not be refreshed.\n */\n accessToken(): Promise<OAuthAccessToken | null>;\n\n /**\n * Revokes the current token via POST /oauth/revoke and clears the session.\n *\n * Throws {@link WritableSessionStoreRequiredError} if the session store does\n * not provide `setSession`.\n */\n logout(): Promise<void>;\n\n /**\n * Fetches all offers by iterating through every paginated response.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOffers(): Promise<Offer[]>;\n\n /**\n * Fetches a single page of offers.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOffersPage(params: PaginationParams): Promise<PaginatedResponse<Offer>>;\n\n /**\n * Fetches details for a given offer by its id.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOfferDetails(id: OfferId): Promise<OfferDetail>;\n\n /**\n * Fetches all participations by iterating through every paginated response.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchParticipations(offerId?: OfferId): Promise<Participation[]>;\n\n /**\n * Fetches a single page of participations, optionally filtered by offer.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchParticipationsPage(\n params: ParticipationsPaginationParams\n ): Promise<PaginatedResponse<Participation>>;\n\n /**\n * Fetches a participation by id.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchParticipation(id: ParticipationId): Promise<Participation>;\n\n /**\n * Creates a participation.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n createParticipation(\n params: CreateParticipationParams\n ): Promise<Participation>;\n\n /**\n * Fetches the requirements for all options of a given offer, grouped by option ID.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchOfferRequirements(\n offerId: OfferId\n ): Promise<Record<OfferOptionId, Requirement[]>>;\n\n /**\n * Fetches the user's requirement statuses for a given offer.\n *\n * Throws {@link NotAuthenticatedError} if the user is not authenticated.\n */\n fetchRequirementStatuses(offerId: OfferId): Promise<RequirementStatusInfo[]>;\n}\n\nclass CoinListServerImpl implements CoinListServer {\n private readonly api: Api;\n private readonly baseUrl: string;\n\n private readonly accessTokenExpiryBufferSeconds: number;\n private readonly strict: boolean;\n\n constructor(private readonly _config: ServerConfig) {\n this.baseUrl = _config.baseUrl ?? PUBLIC_API_BASE_URL;\n this.accessTokenExpiryBufferSeconds =\n _config.accessTokenExpiryBufferSeconds ??\n ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS;\n this.strict = _config.strict ?? false;\n this.api = new Api(\n {\n baseUrl: this.baseUrl,\n xApiVersion: API_VERSION,\n },\n // When refresh=true the renewal middleware has received a 401 and wants a\n // fresh token. A read-only store cannot persist a new session, so return\n // null immediately — this tells the middleware to skip the retry rather\n // than re-sending with the same expired token and wasting a round-trip.\n (refresh) =>\n refresh && !this._config.sessionStore.setSession\n ? Promise.resolve(null)\n : this.accessToken()\n );\n }\n\n async completeOAuth(\n code: AuthorizationCode,\n codeVerifier: CodeVerifier\n ): Promise<OAuthSession> {\n const sessionStore = this._config.sessionStore;\n const setSession = sessionStore.setSession?.bind(sessionStore);\n if (!setSession) {\n throw new WritableSessionStoreRequiredError();\n }\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'authorization_code',\n code,\n redirect_uri: this._config.redirectUri,\n client_id: this._config.clientId,\n client_secret: this._config.clientSecret,\n code_verifier: codeVerifier,\n },\n });\n const session = OAuthSession.fromDto(sessionDto);\n await setSession(session);\n return session;\n }\n\n async accessToken(): Promise<OAuthAccessToken | null> {\n const sessionStore = this._config.sessionStore;\n const session = await sessionStore.getSession();\n if (session == null) return null;\n\n const now = Date.now();\n const expiresAt = session.accessToken.expiresAt.getTime();\n const bufferMs = this.accessTokenExpiryBufferSeconds * 1000;\n if (expiresAt > now + bufferMs) {\n // Valid access token, return it regardless\n return session.accessToken;\n }\n\n const setSession = sessionStore.setSession?.bind(sessionStore);\n if (!setSession) {\n // No write session capabilities => can't refresh!\n // Return the access token as-is\n return session.accessToken;\n } else {\n return this.refreshSession(session.refreshToken, setSession);\n }\n }\n\n private async refreshSession(\n refreshToken: OAuthRefreshToken | undefined,\n setSession: (session: OAuthSession | null) => Promise<void>\n ): Promise<OAuthAccessToken | null> {\n if (!refreshToken) {\n await setSession(null);\n return null;\n }\n\n try {\n const sessionDto = await this.api.send<OAuthSessionDto>({\n method: 'POST',\n url: `/oauth/token`,\n body: {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: this._config.clientId,\n client_secret: this._config.clientSecret,\n },\n });\n const newSession = OAuthSession.fromDto(sessionDto);\n await setSession(newSession);\n return newSession.accessToken;\n } catch {\n await setSession(null);\n return null;\n }\n }\n\n async logout(): Promise<void> {\n const sessionStore = this._config.sessionStore;\n const setSession = sessionStore.setSession?.bind(sessionStore);\n if (!setSession) {\n throw new WritableSessionStoreRequiredError();\n }\n const session = await sessionStore.getSession();\n if (session != null) {\n const tokenToRevoke = session.accessToken.value;\n try {\n await this.api.send({\n method: 'POST',\n url: `/oauth/revoke`,\n body: {\n token: tokenToRevoke,\n client_id: this._config.clientId,\n client_secret: this._config.clientSecret,\n },\n });\n } catch (err) {\n if (err instanceof HttpError) {\n if (this.strict) {\n throw err;\n }\n } else {\n throw err;\n }\n }\n // invalidate the session\n await setSession(null);\n }\n }\n\n private async ensureAuthenticated(): Promise<void> {\n const token = await this.accessToken();\n if (token === null) {\n throw new NotAuthenticatedError();\n }\n }\n\n async fetchOffers(): Promise<Offer[]> {\n await this.ensureAuthenticated();\n return offersApi.fetchOffers(this.api);\n }\n\n async fetchOffersPage(\n params: PaginationParams\n ): Promise<PaginatedResponse<Offer>> {\n await this.ensureAuthenticated();\n return offersApi.fetchOffersPage(this.api, params);\n }\n\n async fetchOfferDetails(id: OfferId): Promise<OfferDetail> {\n await this.ensureAuthenticated();\n return offersApi.fetchOfferDetails(this.api, id);\n }\n\n async fetchParticipations(offerId?: OfferId): Promise<Participation[]> {\n await this.ensureAuthenticated();\n return participationsApi.fetchParticipations(this.api, offerId);\n }\n\n async fetchParticipationsPage(\n params: ParticipationsPaginationParams\n ): Promise<PaginatedResponse<Participation>> {\n await this.ensureAuthenticated();\n return participationsApi.fetchParticipationsPage(this.api, params);\n }\n\n async fetchParticipation(id: ParticipationId): Promise<Participation> {\n await this.ensureAuthenticated();\n return participationsApi.fetchParticipation(this.api, id);\n }\n\n async createParticipation(\n params: CreateParticipationParams\n ): Promise<Participation> {\n await this.ensureAuthenticated();\n return participationsApi.createParticipation(this.api, params);\n }\n\n async fetchOfferRequirements(\n offerId: OfferId\n ): Promise<Record<OfferOptionId, Requirement[]>> {\n await this.ensureAuthenticated();\n return requirementsApi.fetchOfferRequirements(this.api, offerId);\n }\n\n async fetchRequirementStatuses(\n offerId: OfferId\n ): Promise<RequirementStatusInfo[]> {\n await this.ensureAuthenticated();\n return requirementsApi.fetchRequirementStatuses(this.api, offerId);\n }\n}\n\nexport function createCoinListServer(config: ServerConfig): CoinListServer {\n return new CoinListServerImpl(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAIO,IAAM,MAAN,MAAU;AAAA,EAGf,YACE,QACA,kBACA;AACA,SAAK,SAAS,IAAI,uBAAuB,QAAQ,gBAAgB;AAAA,EACnE;AAAA,EAEA,MAAM,KAAgB,SAA0C;AAC9D,WAAO,KAAK,OAAO,KAAgB,OAAO;AAAA,EAC5C;AACF;;;ACjBO,IAAM,oCAAN,cAAgD,MAAM;AAAA,EAC3D,YACE,UAAU,mJACV;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACkCA,IAAM,qCAAqC;AAsK3C,IAAM,qBAAN,MAAmD;AAAA,EAOjD,YAA6B,SAAuB;AAAvB;AAC3B,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,iCACH,QAAQ,kCACR;AACF,SAAK,SAAS,QAAQ,UAAU;AAChC,SAAK,MAAM,IAAI;AAAA,MACb;AAAA,QACE,SAAS,KAAK;AAAA,QACd,aAAa;AAAA,MACf;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,CAAC,YACC,WAAW,CAAC,KAAK,QAAQ,aAAa,aAClC,QAAQ,QAAQ,IAAI,IACpB,KAAK,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAM,cACJ,MACA,cACuB;AACvB,UAAM,eAAe,KAAK,QAAQ;AAClC,UAAM,aAAa,aAAa,YAAY,KAAK,YAAY;AAC7D,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,kCAAkC;AAAA,IAC9C;AACA,UAAM,aAAa,MAAM,KAAK,IAAI,KAAsB;AAAA,MACtD,QAAQ;AAAA,MACR,KAAK;AAAA,MACL,MAAM;AAAA,QACJ,YAAY;AAAA,QACZ;AAAA,QACA,cAAc,KAAK,QAAQ;AAAA,QAC3B,WAAW,KAAK,QAAQ;AAAA,QACxB,eAAe,KAAK,QAAQ;AAAA,QAC5B,eAAe;AAAA,MACjB;AAAA,IACF,CAAC;AACD,UAAM,UAAU,aAAa,QAAQ,UAAU;AAC/C,UAAM,WAAW,OAAO;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAgD;AACpD,UAAM,eAAe,KAAK,QAAQ;AAClC,UAAM,UAAU,MAAM,aAAa,WAAW;AAC9C,QAAI,WAAW,KAAM,QAAO;AAE5B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,QAAQ,YAAY,UAAU,QAAQ;AACxD,UAAM,WAAW,KAAK,iCAAiC;AACvD,QAAI,YAAY,MAAM,UAAU;AAE9B,aAAO,QAAQ;AAAA,IACjB;AAEA,UAAM,aAAa,aAAa,YAAY,KAAK,YAAY;AAC7D,QAAI,CAAC,YAAY;AAGf,aAAO,QAAQ;AAAA,IACjB,OAAO;AACL,aAAO,KAAK,eAAe,QAAQ,cAAc,UAAU;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,MAAc,eACZ,cACA,YACkC;AAClC,QAAI,CAAC,cAAc;AACjB,YAAM,WAAW,IAAI;AACrB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,IAAI,KAAsB;AAAA,QACtD,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,MAAM;AAAA,UACJ,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,WAAW,KAAK,QAAQ;AAAA,UACxB,eAAe,KAAK,QAAQ;AAAA,QAC9B;AAAA,MACF,CAAC;AACD,YAAM,aAAa,aAAa,QAAQ,UAAU;AAClD,YAAM,WAAW,UAAU;AAC3B,aAAO,WAAW;AAAA,IACpB,QAAQ;AACN,YAAM,WAAW,IAAI;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SAAwB;AAC5B,UAAM,eAAe,KAAK,QAAQ;AAClC,UAAM,aAAa,aAAa,YAAY,KAAK,YAAY;AAC7D,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,kCAAkC;AAAA,IAC9C;AACA,UAAM,UAAU,MAAM,aAAa,WAAW;AAC9C,QAAI,WAAW,MAAM;AACnB,YAAM,gBAAgB,QAAQ,YAAY;AAC1C,UAAI;AACF,cAAM,KAAK,IAAI,KAAK;AAAA,UAClB,QAAQ;AAAA,UACR,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,OAAO;AAAA,YACP,WAAW,KAAK,QAAQ;AAAA,YACxB,eAAe,KAAK,QAAQ;AAAA,UAC9B;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,YAAI,eAAe,WAAW;AAC5B,cAAI,KAAK,QAAQ;AACf,kBAAM;AAAA,UACR;AAAA,QACF,OAAO;AACL,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,WAAW,IAAI;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAc,sBAAqC;AACjD,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,QAAI,UAAU,MAAM;AAClB,YAAM,IAAI,sBAAsB;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,MAAM,cAAgC;AACpC,UAAM,KAAK,oBAAoB;AAC/B,WAAiB,YAAY,KAAK,GAAG;AAAA,EACvC;AAAA,EAEA,MAAM,gBACJ,QACmC;AACnC,UAAM,KAAK,oBAAoB;AAC/B,WAAiB,gBAAgB,KAAK,KAAK,MAAM;AAAA,EACnD;AAAA,EAEA,MAAM,kBAAkB,IAAmC;AACzD,UAAM,KAAK,oBAAoB;AAC/B,WAAiB,kBAAkB,KAAK,KAAK,EAAE;AAAA,EACjD;AAAA,EAEA,MAAM,oBAAoB,SAA6C;AACrE,UAAM,KAAK,oBAAoB;AAC/B,WAAyB,oBAAoB,KAAK,KAAK,OAAO;AAAA,EAChE;AAAA,EAEA,MAAM,wBACJ,QAC2C;AAC3C,UAAM,KAAK,oBAAoB;AAC/B,WAAyB,wBAAwB,KAAK,KAAK,MAAM;AAAA,EACnE;AAAA,EAEA,MAAM,mBAAmB,IAA6C;AACpE,UAAM,KAAK,oBAAoB;AAC/B,WAAyB,mBAAmB,KAAK,KAAK,EAAE;AAAA,EAC1D;AAAA,EAEA,MAAM,oBACJ,QACwB;AACxB,UAAM,KAAK,oBAAoB;AAC/B,WAAyB,oBAAoB,KAAK,KAAK,MAAM;AAAA,EAC/D;AAAA,EAEA,MAAM,uBACJ,SAC+C;AAC/C,UAAM,KAAK,oBAAoB;AAC/B,WAAuB,uBAAuB,KAAK,KAAK,OAAO;AAAA,EACjE;AAAA,EAEA,MAAM,yBACJ,SACkC;AAClC,UAAM,KAAK,oBAAoB;AAC/B,WAAuB,yBAAyB,KAAK,KAAK,OAAO;AAAA,EACnE;AACF;AAEO,SAAS,qBAAqB,QAAsC;AACzE,SAAO,IAAI,mBAAmB,MAAM;AACtC;","names":[]}
|