@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.
Files changed (61) hide show
  1. package/dist/chunk-CRACFEJ4.js +17 -0
  2. package/dist/chunk-CRACFEJ4.js.map +1 -0
  3. package/dist/{chunk-NC45IO63.js → chunk-N3WBC2VS.js} +20 -325
  4. package/dist/chunk-N3WBC2VS.js.map +1 -0
  5. package/dist/chunk-V6WO67RO.js +311 -0
  6. package/dist/chunk-V6WO67RO.js.map +1 -0
  7. package/dist/client/index.cjs +1013 -220
  8. package/dist/client/index.cjs.map +1 -1
  9. package/dist/client/index.d.cts +526 -9
  10. package/dist/client/index.d.ts +526 -9
  11. package/dist/client/index.js +1464 -94
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/{participation-DMMEDxON.d.cts → requirement-BEO42QOr.d.cts} +204 -2
  14. package/dist/{participation-E-bT-GXJ.d.ts → requirement-BEO42QOr.d.ts} +204 -2
  15. package/dist/server/index.cjs +50 -100
  16. package/dist/server/index.cjs.map +1 -1
  17. package/dist/server/index.d.cts +67 -24
  18. package/dist/server/index.d.ts +67 -24
  19. package/dist/server/index.js +54 -87
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/shared/index.cjs +375 -0
  22. package/dist/shared/index.cjs.map +1 -0
  23. package/dist/shared/index.d.cts +35 -0
  24. package/dist/shared/index.d.ts +35 -0
  25. package/dist/{client/core → shared}/index.js +35 -16
  26. package/dist/shared/index.js.map +1 -0
  27. package/package.json +7 -22
  28. package/dist/RequirementItem-B-MvcxSr.d.cts +0 -37
  29. package/dist/RequirementItem-DcYot2uC.d.ts +0 -37
  30. package/dist/chunk-EHZ6PIGN.js +0 -414
  31. package/dist/chunk-EHZ6PIGN.js.map +0 -1
  32. package/dist/chunk-NC45IO63.js.map +0 -1
  33. package/dist/chunk-RIFATQB5.js +0 -257
  34. package/dist/chunk-RIFATQB5.js.map +0 -1
  35. package/dist/chunk-UGJUTRXC.js +0 -304
  36. package/dist/chunk-UGJUTRXC.js.map +0 -1
  37. package/dist/chunk-YJQMJFA7.js +0 -16
  38. package/dist/chunk-YJQMJFA7.js.map +0 -1
  39. package/dist/client/components/index.cjs +0 -1108
  40. package/dist/client/components/index.cjs.map +0 -1
  41. package/dist/client/components/index.d.cts +0 -139
  42. package/dist/client/components/index.d.ts +0 -139
  43. package/dist/client/components/index.js +0 -558
  44. package/dist/client/components/index.js.map +0 -1
  45. package/dist/client/core/index.cjs +0 -1047
  46. package/dist/client/core/index.cjs.map +0 -1
  47. package/dist/client/core/index.d.cts +0 -23
  48. package/dist/client/core/index.d.ts +0 -23
  49. package/dist/client/core/index.js.map +0 -1
  50. package/dist/client/hooks/index.cjs +0 -307
  51. package/dist/client/hooks/index.cjs.map +0 -1
  52. package/dist/client/hooks/index.d.cts +0 -126
  53. package/dist/client/hooks/index.d.ts +0 -126
  54. package/dist/client/hooks/index.js +0 -19
  55. package/dist/client/hooks/index.js.map +0 -1
  56. package/dist/coinlist-client-BSxpqylo.d.ts +0 -158
  57. package/dist/coinlist-client-Cah5c_aF.d.cts +0 -158
  58. package/dist/requirement-XWT0T_zO.d.cts +0 -205
  59. package/dist/requirement-XWT0T_zO.d.ts +0 -205
  60. package/dist/useCoinListRequirements-B5-ZPOMz.d.ts +0 -80
  61. package/dist/useCoinListRequirements-Cuc32Tqw.d.cts +0 -80
@@ -1,11 +1,27 @@
1
- import { A as AuthorizationCode, c as CodeVerifier, g as OAuthSession, O as OAuthAccessToken, i as PaginationParams, P as PaginatedResponse, j as Participation, m as ParticipationsPaginationParams, k as ParticipationId, d as CreateParticipationParams, o as Config, a as ClientSecret } from '../participation-E-bT-GXJ.js';
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
- setSession(session: OAuthSession | null): Promise<void>;
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
- * Responsible for:
25
- * 1. Returning a securely stored access token from Cookies
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
- * @returns a valid access token or null if the user is not authentication.
29
- * On error rejects the Promise and throws.
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
- fetchAllOffers(): Promise<Offer[]>;
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
- fetchAllParticipations(offerId?: OfferId): Promise<Participation[]>;
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 { AuthorizationCode, ClientSecret, CodeVerifier, type CoinListServer, Config, CreateParticipationParams, NotAuthenticatedError, NotImplementedError, OAuthAccessToken, OAuthSession, Offer, OfferDetail, OfferId, OfferOptionId, PaginatedResponse, PaginationParams, Participation, ParticipationId, ParticipationsPaginationParams, Requirement, RequirementStatusInfo, type ServerConfig, type SessionStore, createCoinListServer };
150
+ export { type CoinListServer, type ServerConfig, type SessionStore, WritableSessionStoreRequiredError, createCoinListServer };
@@ -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-NC45IO63.js";
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
- (_refresh) => this.accessToken()
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
- this._config.sessionStore.setSession(session);
80
+ await setSession(session);
94
81
  return session;
95
82
  }
96
83
  async accessToken() {
97
- const session = await this._config.sessionStore.getSession();
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 refreshToken = session?.refreshToken;
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 this._config.sessionStore.setSession(null);
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 this._config.sessionStore.setSession(newSession);
117
+ await setSession(newSession);
123
118
  return newSession.accessToken;
124
119
  } catch {
125
- await this._config.sessionStore.setSession(null);
120
+ await setSession(null);
126
121
  return null;
127
122
  }
128
123
  }
129
124
  async logout() {
130
- const session = await this._config.sessionStore.getSession();
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 this._config.sessionStore.setSession(null);
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 fetchAllOffers() {
161
+ async fetchOffers() {
162
162
  await this.ensureAuthenticated();
163
- return fetchAllOffers(this.api);
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 fetchAllParticipations(offerId) {
173
+ async fetchParticipations(offerId) {
174
174
  await this.ensureAuthenticated();
175
- return fetchAllParticipations(this.api, offerId);
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
- AuthorizationCode,
203
- Blockchain,
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
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;;;ACmBA,IAAM,qCAAqC;AA8G3C,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,MACA,CAAC,aAAa,KAAK,YAAY;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAM,cACJ,MACA,cACuB;AACvB,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,SAAK,QAAQ,aAAa,WAAW,OAAO;AAC5C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAgD;AACpD,UAAM,UAAU,MAAM,KAAK,QAAQ,aAAa,WAAW;AAC3D,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;AAC9B,aAAO,QAAQ;AAAA,IACjB;AAEA,UAAM,eAAe,SAAS;AAC9B,QAAI,CAAC,cAAc;AACjB,YAAM,KAAK,QAAQ,aAAa,WAAW,IAAI;AAC/C,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,KAAK,QAAQ,aAAa,WAAW,UAAU;AACrD,aAAO,WAAW;AAAA,IACpB,QAAQ;AACN,YAAM,KAAK,QAAQ,aAAa,WAAW,IAAI;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SAAwB;AAC5B,UAAM,UAAU,MAAM,KAAK,QAAQ,aAAa,WAAW;AAC3D,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;AACA,YAAM,KAAK,QAAQ,aAAa,WAAW,IAAI;AAAA,IACjD;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,iBAAmC;AACvC,UAAM,KAAK,oBAAoB;AAC/B,WAAiB,eAAe,KAAK,GAAG;AAAA,EAC1C;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,uBAAuB,SAA6C;AACxE,UAAM,KAAK,oBAAoB;AAC/B,WAAyB,uBAAuB,KAAK,KAAK,OAAO;AAAA,EACnE;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":[]}
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":"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","names":[]}