@coinlist-co/react 0.2.0 → 0.4.0

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 (67) hide show
  1. package/dist/RequirementItem-B-MvcxSr.d.cts +37 -0
  2. package/dist/RequirementItem-DcYot2uC.d.ts +37 -0
  3. package/dist/chunk-EHZ6PIGN.js +414 -0
  4. package/dist/chunk-EHZ6PIGN.js.map +1 -0
  5. package/dist/chunk-NC45IO63.js +777 -0
  6. package/dist/chunk-NC45IO63.js.map +1 -0
  7. package/dist/chunk-RIFATQB5.js +257 -0
  8. package/dist/chunk-RIFATQB5.js.map +1 -0
  9. package/dist/chunk-UGJUTRXC.js +304 -0
  10. package/dist/chunk-UGJUTRXC.js.map +1 -0
  11. package/dist/client/components/index.cjs +473 -10
  12. package/dist/client/components/index.cjs.map +1 -1
  13. package/dist/client/components/index.d.cts +80 -12
  14. package/dist/client/components/index.d.ts +80 -12
  15. package/dist/client/components/index.js +243 -243
  16. package/dist/client/components/index.js.map +1 -1
  17. package/dist/client/core/index.cjs +349 -202
  18. package/dist/client/core/index.cjs.map +1 -1
  19. package/dist/client/core/index.d.cts +4 -4
  20. package/dist/client/core/index.d.ts +4 -4
  21. package/dist/client/core/index.js +16 -14
  22. package/dist/client/hooks/index.cjs +133 -16
  23. package/dist/client/hooks/index.cjs.map +1 -1
  24. package/dist/client/hooks/index.d.cts +102 -8
  25. package/dist/client/hooks/index.d.ts +102 -8
  26. package/dist/client/hooks/index.js +11 -9
  27. package/dist/client/index.cjs +591 -210
  28. package/dist/client/index.cjs.map +1 -1
  29. package/dist/client/index.d.cts +7 -6
  30. package/dist/client/index.d.ts +7 -6
  31. package/dist/client/index.js +41 -17
  32. package/dist/client/styles.css +1 -1
  33. package/dist/coinlist-client-BSxpqylo.d.ts +158 -0
  34. package/dist/coinlist-client-Cah5c_aF.d.cts +158 -0
  35. package/dist/participation-DMMEDxON.d.cts +185 -0
  36. package/dist/participation-E-bT-GXJ.d.ts +185 -0
  37. package/dist/requirement-XWT0T_zO.d.cts +205 -0
  38. package/dist/requirement-XWT0T_zO.d.ts +205 -0
  39. package/dist/server/index.cjs +519 -14
  40. package/dist/server/index.cjs.map +1 -1
  41. package/dist/server/index.d.cts +73 -4
  42. package/dist/server/index.d.ts +73 -4
  43. package/dist/server/index.js +116 -20
  44. package/dist/server/index.js.map +1 -1
  45. package/dist/useCoinListRequirements-B5-ZPOMz.d.ts +80 -0
  46. package/dist/useCoinListRequirements-Cuc32Tqw.d.cts +80 -0
  47. package/package.json +4 -4
  48. package/dist/chunk-32GOUV3L.js +0 -118
  49. package/dist/chunk-32GOUV3L.js.map +0 -1
  50. package/dist/chunk-G3B4RLXP.js +0 -76
  51. package/dist/chunk-G3B4RLXP.js.map +0 -1
  52. package/dist/chunk-GRVV7NLV.js +0 -282
  53. package/dist/chunk-GRVV7NLV.js.map +0 -1
  54. package/dist/chunk-Z5RFQOMS.js +0 -560
  55. package/dist/chunk-Z5RFQOMS.js.map +0 -1
  56. package/dist/coinlist-client-20v7fJSW.d.ts +0 -394
  57. package/dist/coinlist-client-Ds86ycAg.d.cts +0 -394
  58. package/dist/newtype-yKTvFdg_.d.cts +0 -6
  59. package/dist/newtype-yKTvFdg_.d.ts +0 -6
  60. package/dist/oauth-session-DgItaMKN.d.cts +0 -56
  61. package/dist/oauth-session-DnKDI5ou.d.ts +0 -56
  62. package/dist/offer-D6p_Ijme.d.cts +0 -30
  63. package/dist/offer-DnFEFJw9.d.ts +0 -30
  64. package/dist/useCoinListOffers-DIdOb5hB.d.ts +0 -27
  65. package/dist/useCoinListOffers-DvJxu6qj.d.cts +0 -27
  66. package/dist/useCompleteCoinListOAuth-C359mp_k.d.cts +0 -55
  67. package/dist/useCompleteCoinListOAuth-wZX3o8r2.d.ts +0 -55
@@ -23,7 +23,9 @@ __export(hooks_exports, {
23
23
  useCoinList: () => useCoinList,
24
24
  useCoinListOfferDetails: () => useCoinListOfferDetails,
25
25
  useCoinListOffers: () => useCoinListOffers,
26
- useCompleteCoinListOAuth: () => useCompleteCoinListOAuth
26
+ useCoinListRequirements: () => useCoinListRequirements,
27
+ useCompleteCoinListOAuth: () => useCompleteCoinListOAuth,
28
+ useParticipations: () => useParticipations
27
29
  });
28
30
  module.exports = __toCommonJS(hooks_exports);
29
31
 
@@ -66,13 +68,23 @@ var CoinListClientInitializationError = class extends Error {
66
68
  // src/client/hooks/useCoinListOfferDetails.ts
67
69
  var import_react3 = require("react");
68
70
  var LOADING_STATE = { type: "LOADING" };
69
- function useCoinListOfferDetails(offerId) {
71
+ function useCoinListOfferDetails(offerId, options = {}) {
70
72
  const { coinlist, isReady } = useCoinList();
71
- const [offerDetailsState, setOfferDetailsState] = (0, import_react3.useState)(LOADING_STATE);
73
+ const { serverData } = options;
74
+ const [offerDetailsState, setOfferDetailsState] = (0, import_react3.useState)(
75
+ serverData !== void 0 ? { type: "CONTENT", offerDetail: serverData } : LOADING_STATE
76
+ );
72
77
  (0, import_react3.useEffect)(() => {
73
78
  let isCancelled = false;
74
79
  if (!isReady) {
75
- setOfferDetailsState(LOADING_STATE);
80
+ if (serverData === void 0) {
81
+ setOfferDetailsState(LOADING_STATE);
82
+ }
83
+ return () => {
84
+ isCancelled = true;
85
+ };
86
+ }
87
+ if (serverData !== void 0) {
76
88
  return () => {
77
89
  isCancelled = true;
78
90
  };
@@ -91,20 +103,30 @@ function useCoinListOfferDetails(offerId) {
91
103
  return () => {
92
104
  isCancelled = true;
93
105
  };
94
- }, [coinlist, isReady, offerId]);
106
+ }, [coinlist, isReady, offerId, serverData]);
95
107
  return { offerDetailsState };
96
108
  }
97
109
 
98
110
  // src/client/hooks/useCoinListOffers.ts
99
111
  var import_react4 = require("react");
100
112
  var LOADING_STATE2 = { type: "LOADING" };
101
- function useCoinListOffers() {
113
+ function useCoinListOffers(options = {}) {
102
114
  const { coinlist, isReady } = useCoinList();
103
- const [offersState, setOffersState] = (0, import_react4.useState)(LOADING_STATE2);
115
+ const { serverOffers } = options;
116
+ const [offersState, setOffersState] = (0, import_react4.useState)(
117
+ serverOffers !== void 0 ? { type: "CONTENT", offers: serverOffers } : LOADING_STATE2
118
+ );
104
119
  (0, import_react4.useEffect)(() => {
105
120
  let isCancelled = false;
106
121
  if (!isReady) {
107
- setOffersState(LOADING_STATE2);
122
+ if (serverOffers === void 0) {
123
+ setOffersState(LOADING_STATE2);
124
+ }
125
+ return () => {
126
+ isCancelled = true;
127
+ };
128
+ }
129
+ if (serverOffers !== void 0) {
108
130
  return () => {
109
131
  isCancelled = true;
110
132
  };
@@ -123,22 +145,73 @@ function useCoinListOffers() {
123
145
  return () => {
124
146
  isCancelled = true;
125
147
  };
126
- }, [coinlist, isReady]);
148
+ }, [coinlist, isReady, serverOffers]);
127
149
  return { offersState };
128
150
  }
129
151
 
130
- // src/client/hooks/useCompleteCoinListOAuth.ts
152
+ // src/client/hooks/useCoinListRequirements.ts
131
153
  var import_react5 = require("react");
154
+ var LOADING_STATE3 = { type: "LOADING" };
155
+ function useCoinListRequirements(offerId, options = {}) {
156
+ const { coinlist, isReady } = useCoinList();
157
+ const { serverData } = options;
158
+ const [requirementsState, setRequirementsState] = (0, import_react5.useState)(
159
+ serverData !== void 0 ? { type: "CONTENT", ...serverData } : LOADING_STATE3
160
+ );
161
+ const [fetchKey, setFetchKey] = (0, import_react5.useState)(0);
162
+ const refetch = (0, import_react5.useCallback)(() => setFetchKey((k) => k + 1), []);
163
+ (0, import_react5.useEffect)(() => {
164
+ let isCancelled = false;
165
+ if (!isReady) {
166
+ if (serverData === void 0) {
167
+ setRequirementsState(LOADING_STATE3);
168
+ }
169
+ return () => {
170
+ isCancelled = true;
171
+ };
172
+ }
173
+ if (serverData !== void 0 && fetchKey === 0) {
174
+ return () => {
175
+ isCancelled = true;
176
+ };
177
+ }
178
+ setRequirementsState(LOADING_STATE3);
179
+ Promise.all([
180
+ coinlist.fetchOfferRequirements(offerId),
181
+ coinlist.fetchRequirementStatuses(offerId)
182
+ ]).then(([requirementsByOptionId, statuses]) => {
183
+ if (!isCancelled) {
184
+ setRequirementsState({
185
+ type: "CONTENT",
186
+ requirementsByOptionId,
187
+ statuses
188
+ });
189
+ }
190
+ }).catch((error) => {
191
+ if (!isCancelled) {
192
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
193
+ setRequirementsState({ type: "ERROR", reason });
194
+ }
195
+ });
196
+ return () => {
197
+ isCancelled = true;
198
+ };
199
+ }, [coinlist, isReady, offerId, fetchKey]);
200
+ return { requirementsState, refetch };
201
+ }
202
+
203
+ // src/client/hooks/useCompleteCoinListOAuth.ts
204
+ var import_react6 = require("react");
132
205
  function useCompleteCoinListOAuth(options) {
133
206
  const { coinlist } = useCoinList();
134
- const postOAuthCompleteRef = (0, import_react5.useRef)(options.postOAuthComplete);
135
- const onFailureRef = (0, import_react5.useRef)(options.onFailure);
136
- const onSuccessRef = (0, import_react5.useRef)(options.onSuccess);
207
+ const postOAuthCompleteRef = (0, import_react6.useRef)(options.postOAuthComplete);
208
+ const onFailureRef = (0, import_react6.useRef)(options.onFailure);
209
+ const onSuccessRef = (0, import_react6.useRef)(options.onSuccess);
137
210
  postOAuthCompleteRef.current = options.postOAuthComplete;
138
211
  onFailureRef.current = options.onFailure;
139
212
  onSuccessRef.current = options.onSuccess;
140
- const hasStartedExchangeRef = (0, import_react5.useRef)(false);
141
- (0, import_react5.useEffect)(() => {
213
+ const hasStartedExchangeRef = (0, import_react6.useRef)(false);
214
+ (0, import_react6.useEffect)(() => {
142
215
  if (typeof window === "undefined") return;
143
216
  const params = new URLSearchParams(window.location.search);
144
217
  if (!params.has("code") && !params.has("error")) return;
@@ -180,11 +253,55 @@ function useCompleteCoinListOAuth(options) {
180
253
  };
181
254
  }, [coinlist]);
182
255
  }
256
+
257
+ // src/client/hooks/useParticipations.ts
258
+ var import_react7 = require("react");
259
+ var LOADING_STATE4 = { type: "LOADING" };
260
+ function useParticipations(offerId, options = {}) {
261
+ const { coinlist, isReady } = useCoinList();
262
+ const { serverData } = options;
263
+ const [participationsState, setParticipationsState] = (0, import_react7.useState)(
264
+ serverData !== void 0 ? { type: "CONTENT", participations: serverData } : LOADING_STATE4
265
+ );
266
+ (0, import_react7.useEffect)(() => {
267
+ let isCancelled = false;
268
+ if (!isReady) {
269
+ if (serverData === void 0) {
270
+ setParticipationsState(LOADING_STATE4);
271
+ }
272
+ return () => {
273
+ isCancelled = true;
274
+ };
275
+ }
276
+ if (serverData !== void 0) {
277
+ return () => {
278
+ isCancelled = true;
279
+ };
280
+ }
281
+ setParticipationsState(LOADING_STATE4);
282
+ coinlist.fetchAllParticipations(offerId).then((participations) => {
283
+ if (!isCancelled) {
284
+ setParticipationsState({ type: "CONTENT", participations });
285
+ }
286
+ }).catch((error) => {
287
+ if (!isCancelled) {
288
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
289
+ setParticipationsState({ type: "ERROR", reason });
290
+ }
291
+ });
292
+ return () => {
293
+ isCancelled = true;
294
+ };
295
+ }, [coinlist, isReady, offerId, serverData]);
296
+ return { participationsState };
297
+ }
183
298
  // Annotate the CommonJS export names for ESM import in node:
184
299
  0 && (module.exports = {
185
300
  useCoinList,
186
301
  useCoinListOfferDetails,
187
302
  useCoinListOffers,
188
- useCompleteCoinListOAuth
303
+ useCoinListRequirements,
304
+ useCompleteCoinListOAuth,
305
+ useParticipations
189
306
  });
190
307
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/client/hooks/index.ts","../../../src/client/hooks/useCoinList.ts","../../../src/client/CoinListProvider.tsx","../../../src/shared/types/errors.ts","../../../src/client/hooks/useCoinListOfferDetails.ts","../../../src/client/hooks/useCoinListOffers.ts","../../../src/client/hooks/useCompleteCoinListOAuth.ts"],"sourcesContent":["export { useCoinList } from '@/client/hooks/useCoinList';\nexport { useCoinListOfferDetails } from '@/client/hooks/useCoinListOfferDetails';\nexport { useCoinListOffers } from '@/client/hooks/useCoinListOffers';\nexport {\n type CompleteCoinListOAuthFailureReason,\n type UseCompleteCoinListOAuthOptions,\n useCompleteCoinListOAuth,\n} from '@/client/hooks/useCompleteCoinListOAuth';\n","import { useContext } from 'react';\nimport { CoinListContext } from '@/client/CoinListProvider';\nimport type { CoinListClient } from '@/client/core/coinlist-client';\n\nexport interface UseCoinListResult {\n /**\n * Indicates whether the CoinList client has been initialized and ready to use.\n * While isReady is false, then the current user state will be `unknown`.\n */\n isReady: boolean;\n coinlist: CoinListClient;\n}\n\nexport function useCoinList(): UseCoinListResult {\n const context = useContext(CoinListContext);\n if (context === null) {\n throw new CoinListClientInitializationError();\n }\n\n return {\n isReady: context.isReady,\n coinlist: context.coinlist,\n };\n}\n\nexport class CoinListClientInitializationError extends Error {\n constructor(\n message = 'useCoinList() must be used within CoinListProvider. Wrap your app with <CoinListProvider config={...}>.'\n ) {\n super(message);\n this.name = 'CoinListClientInitializationError';\n }\n}\n","/**\n * Global CoinList initialization for React apps.\n * Wrap your app once at the root; use useCoinList() anywhere below.\n */\n\nimport {\n createContext,\n type ReactNode,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n type ClientConfig,\n type CoinListClient,\n createCoinListClient,\n} from '@/client/core/coinlist-client';\n\nexport type CoinListContextValue = {\n coinlist: CoinListClient;\n isReady: boolean;\n};\n\nexport const CoinListContext = createContext<CoinListContextValue | null>(null);\n\nexport type CoinListProviderProps = {\n config: ClientConfig;\n children: ReactNode;\n};\n\n/**\n * Provide CoinList (api + config) to the tree. Initialize once at app root with your config.\n */\nexport function CoinListProvider({ config, children }: CoinListProviderProps) {\n const [isReady, setIsReady] = useState(false);\n const coinlist = useMemo(() => createCoinListClient(config), [config]);\n\n useEffect(() => {\n let isCancelled = false;\n if (coinlist.getAuthState() === 'unknown') {\n coinlist\n .init()\n .then(() => {\n if (!isCancelled) {\n setIsReady(true);\n }\n })\n .catch(() => {\n if (!isCancelled) {\n setIsReady(true);\n }\n });\n } else {\n setIsReady(true);\n }\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist]);\n\n const value = useMemo(() => ({ coinlist, isReady }), [coinlist, isReady]);\n\n return (\n <CoinListContext.Provider value={value}>\n {children}\n </CoinListContext.Provider>\n );\n}\n","/**\n * Error thrown when a feature or code path is not yet implemented.\n */\nexport class NotImplementedError extends Error {\n constructor(message = 'Not implemented yet') {\n super(message);\n this.name = 'NotImplementedError';\n }\n}\n\n/**\n * Error thrown when accessing a feature that requires authentication\n * without being authenticated.\n */\nexport class NotAuthenticatedError extends Error {\n constructor(\n message = 'The user is not authenticated. Go through the OAuth flow first!'\n ) {\n super(message);\n this.name = 'NotAuthenticatedError';\n }\n}\n","import { useEffect, useState } from 'react';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type { OfferId } from '@/shared/types/offer';\nimport type { OfferDetail } from '@/shared/types/offer-detail';\n\nexport type LoadOfferDetailsReason = 'not-authenticated' | 'generic-error';\n\nexport type LoadOfferDetailsState =\n | {\n type: 'LOADING';\n }\n | {\n type: 'ERROR';\n reason: LoadOfferDetailsReason;\n }\n | {\n type: 'CONTENT';\n offerDetail: OfferDetail;\n };\n\nexport interface UseCoinListOfferDetailsResult {\n offerDetailsState: LoadOfferDetailsState;\n}\n\nconst LOADING_STATE: LoadOfferDetailsState = { type: 'LOADING' };\n\n/**\n * Loads CoinList offer details for a given offer id and exposes a UI-friendly state.\n *\n * Returns `LOADING` while CoinList is initializing or while details are being fetched.\n * Returns `CONTENT` with the offer when `fetchOfferDetails()` succeeds.\n * Returns `ERROR` with:\n * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}\n * - `generic-error` for any other failure\n */\nexport function useCoinListOfferDetails(\n offerId: OfferId\n): UseCoinListOfferDetailsResult {\n const { coinlist, isReady } = useCoinList();\n const [offerDetailsState, setOfferDetailsState] =\n useState<LoadOfferDetailsState>(LOADING_STATE);\n\n useEffect(() => {\n let isCancelled = false;\n\n if (!isReady) {\n setOfferDetailsState(LOADING_STATE);\n return () => {\n isCancelled = true;\n };\n }\n\n setOfferDetailsState(LOADING_STATE);\n coinlist\n .fetchOfferDetails(offerId)\n .then((offerDetail) => {\n if (!isCancelled) {\n setOfferDetailsState({ type: 'CONTENT', offerDetail });\n }\n })\n .catch((error: unknown) => {\n if (!isCancelled) {\n const reason: LoadOfferDetailsReason =\n error instanceof NotAuthenticatedError\n ? 'not-authenticated'\n : 'generic-error';\n setOfferDetailsState({ type: 'ERROR', reason });\n }\n });\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist, isReady, offerId]);\n\n return { offerDetailsState };\n}\n","import { useEffect, useState } from 'react';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type { Offer } from '@/shared/types/offer';\n\nexport type LoadOffersReason = 'not-authenticated' | 'generic-error';\n\nexport type LoadOffersState =\n | {\n type: 'LOADING';\n }\n | {\n type: 'ERROR';\n reason: LoadOffersReason;\n }\n | {\n type: 'CONTENT';\n offers: Offer[];\n };\n\nexport interface UseCoinListOffersResult {\n offersState: LoadOffersState;\n}\n\nconst LOADING_STATE: LoadOffersState = { type: 'LOADING' };\n\n/**\n * Loads all CoinList offers for the current user and exposes a UI-friendly state.\n *\n * Returns `LOADING` while CoinList is initializing or while offers are being fetched.\n * Returns `CONTENT` with all offers when `fetchAllOffers()` succeeds.\n * Returns `ERROR` with:\n * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}\n * - `generic-error` for any other failure\n */\nexport function useCoinListOffers(): UseCoinListOffersResult {\n const { coinlist, isReady } = useCoinList();\n const [offersState, setOffersState] =\n useState<LoadOffersState>(LOADING_STATE);\n\n useEffect(() => {\n let isCancelled = false;\n\n // Wait until CoinListProvider finishes auth initialization.\n if (!isReady) {\n setOffersState(LOADING_STATE);\n return () => {\n isCancelled = true;\n };\n }\n\n setOffersState(LOADING_STATE);\n coinlist\n .fetchAllOffers()\n .then((offers) => {\n if (!isCancelled) {\n setOffersState({ type: 'CONTENT', offers });\n }\n })\n .catch((error: unknown) => {\n if (!isCancelled) {\n const reason: LoadOffersReason =\n error instanceof NotAuthenticatedError\n ? 'not-authenticated'\n : 'generic-error';\n setOffersState({ type: 'ERROR', reason });\n }\n });\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist, isReady]);\n\n return { offersState };\n}\n","import { useEffect, useRef } from 'react';\nimport type { OauthClientErrorReason } from '@/client/core/types/client-types';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport type { AuthorizationCode, CodeVerifier } from '@/shared/types/oauth';\n\n/**\n * Reasons passed to {@link UseCompleteCoinListOAuthOptions.onFailure} when the\n * OAuth callback cannot be completed. Includes PKCE / redirect validation\n * errors from {@link CoinListClient.completeOAuth} and a dedicated value when\n * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails or rejects.\n */\nexport type CompleteCoinListOAuthFailureReason =\n | OauthClientErrorReason\n | 'complete_request_failed';\n\nexport interface UseCompleteCoinListOAuthOptions {\n /**\n * Exchange the authorization code on your backend (e.g. POST to your API).\n * Return `true` when the session was established successfully, `false` when\n * the request completed but signing in failed. Rejections are treated like\n * `false` and reported as {@link CompleteCoinListOAuthFailureReason}\n * `\"complete_request_failed\"`.\n */\n postOAuthComplete: (payload: {\n code: AuthorizationCode;\n codeVerifier: CodeVerifier;\n }) => Promise<boolean>;\n /**\n * Called when the user denied access (`error` query parameter), when\n * {@link CoinListClient.completeOAuth} fails PKCE/state validation, or when\n * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails.\n */\n onFailure: (\n reason: CompleteCoinListOAuthFailureReason\n ) => void | Promise<void>;\n /** Invoked after {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} succeeds and {@link CoinListClient.init} has refreshed the client session. */\n onSuccess: () => void | Promise<void>;\n}\n\n/**\n * Runs the CoinList OAuth callback once on mount: validates the redirect URL via\n * {@link CoinListClient.completeOAuth}, exchanges the code via {@link UseCompleteCoinListOAuthOptions.postOAuthComplete},\n * refreshes tokens with {@link CoinListClient.init}, then calls {@link UseCompleteCoinListOAuthOptions.onSuccess}.\n *\n * Framework-agnostic: reads `window.location` like {@link CoinListClient.completeOAuth}. Supply\n * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete}, {@link UseCompleteCoinListOAuthOptions.onFailure},\n * and {@link UseCompleteCoinListOAuthOptions.onSuccess} to perform redirects or routing in your app.\n *\n * Must be used within {@link CoinListProvider}.\n */\nexport function useCompleteCoinListOAuth(\n options: UseCompleteCoinListOAuthOptions\n): void {\n const { coinlist } = useCoinList();\n\n const postOAuthCompleteRef = useRef(options.postOAuthComplete);\n const onFailureRef = useRef(options.onFailure);\n const onSuccessRef = useRef(options.onSuccess);\n\n postOAuthCompleteRef.current = options.postOAuthComplete;\n onFailureRef.current = options.onFailure;\n onSuccessRef.current = options.onSuccess;\n\n const hasStartedExchangeRef = useRef(false);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const params = new URLSearchParams(window.location.search);\n if (!params.has('code') && !params.has('error')) return;\n\n if (hasStartedExchangeRef.current) return;\n\n const oauthResult = coinlist.completeOAuth();\n if (oauthResult.type === 'error') {\n void Promise.resolve(onFailureRef.current(oauthResult.reason)).catch(\n () => {}\n );\n return;\n }\n\n hasStartedExchangeRef.current = true;\n let cancelled = false;\n\n void (async () => {\n try {\n const ok = await postOAuthCompleteRef.current({\n code: oauthResult.code,\n codeVerifier: oauthResult.codeVerifier,\n });\n\n if (cancelled) return;\n\n if (!ok) {\n hasStartedExchangeRef.current = false;\n await onFailureRef.current('complete_request_failed');\n return;\n }\n\n await coinlist.init();\n\n if (cancelled) return;\n\n await onSuccessRef.current();\n } catch {\n if (cancelled) return;\n hasStartedExchangeRef.current = false;\n await onFailureRef.current('complete_request_failed');\n }\n })();\n\n return () => {\n cancelled = true;\n hasStartedExchangeRef.current = false;\n };\n }, [coinlist]);\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","LOADING_STATE","import_react"]}
1
+ {"version":3,"sources":["../../../src/client/hooks/index.ts","../../../src/client/hooks/useCoinList.ts","../../../src/client/CoinListProvider.tsx","../../../src/shared/types/errors.ts","../../../src/client/hooks/useCoinListOfferDetails.ts","../../../src/client/hooks/useCoinListOffers.ts","../../../src/client/hooks/useCoinListRequirements.ts","../../../src/client/hooks/useCompleteCoinListOAuth.ts","../../../src/client/hooks/useParticipations.ts"],"sourcesContent":["export { useCoinList } from '@/client/hooks/useCoinList';\nexport {\n type LoadOfferDetailsReason,\n type LoadOfferDetailsState,\n type UseCoinListOfferDetailsOptions,\n type UseCoinListOfferDetailsResult,\n useCoinListOfferDetails,\n} from '@/client/hooks/useCoinListOfferDetails';\nexport {\n type LoadOffersReason,\n type LoadOffersState,\n type UseCoinListOffersOptions,\n type UseCoinListOffersResult,\n useCoinListOffers,\n} from '@/client/hooks/useCoinListOffers';\nexport {\n type LoadRequirementsReason,\n type LoadRequirementsState,\n type UseCoinListRequirementsOptions,\n type UseCoinListRequirementsResult,\n useCoinListRequirements,\n} from '@/client/hooks/useCoinListRequirements';\nexport {\n type CompleteCoinListOAuthFailureReason,\n type UseCompleteCoinListOAuthOptions,\n useCompleteCoinListOAuth,\n} from '@/client/hooks/useCompleteCoinListOAuth';\nexport {\n type LoadParticipationsReason,\n type LoadParticipationsState,\n type UseParticipationsOptions,\n type UseParticipationsResult,\n useParticipations,\n} from '@/client/hooks/useParticipations';\n","import { useContext } from 'react';\nimport { CoinListContext } from '@/client/CoinListProvider';\nimport type { CoinListClient } from '@/client/core/coinlist-client';\n\nexport interface UseCoinListResult {\n /**\n * Indicates whether the CoinList client has been initialized and ready to use.\n * While isReady is false, then the current user state will be `unknown`.\n */\n isReady: boolean;\n coinlist: CoinListClient;\n}\n\nexport function useCoinList(): UseCoinListResult {\n const context = useContext(CoinListContext);\n if (context === null) {\n throw new CoinListClientInitializationError();\n }\n\n return {\n isReady: context.isReady,\n coinlist: context.coinlist,\n };\n}\n\nexport class CoinListClientInitializationError extends Error {\n constructor(\n message = 'useCoinList() must be used within CoinListProvider. Wrap your app with <CoinListProvider config={...}>.'\n ) {\n super(message);\n this.name = 'CoinListClientInitializationError';\n }\n}\n","/**\n * Global CoinList initialization for React apps.\n * Wrap your app once at the root; use useCoinList() anywhere below.\n */\n\nimport {\n createContext,\n type ReactNode,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n type ClientConfig,\n type CoinListClient,\n createCoinListClient,\n} from '@/client/core/coinlist-client';\n\nexport type CoinListContextValue = {\n coinlist: CoinListClient;\n isReady: boolean;\n};\n\nexport const CoinListContext = createContext<CoinListContextValue | null>(null);\n\nexport type CoinListProviderProps = {\n config: ClientConfig;\n children: ReactNode;\n};\n\n/**\n * Provide CoinList (api + config) to the tree. Initialize once at app root with your config.\n */\nexport function CoinListProvider({ config, children }: CoinListProviderProps) {\n const [isReady, setIsReady] = useState(false);\n const coinlist = useMemo(() => createCoinListClient(config), [config]);\n\n useEffect(() => {\n let isCancelled = false;\n if (coinlist.getAuthState() === 'unknown') {\n coinlist\n .init()\n .then(() => {\n if (!isCancelled) {\n setIsReady(true);\n }\n })\n .catch(() => {\n if (!isCancelled) {\n setIsReady(true);\n }\n });\n } else {\n setIsReady(true);\n }\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist]);\n\n const value = useMemo(() => ({ coinlist, isReady }), [coinlist, isReady]);\n\n return (\n <CoinListContext.Provider value={value}>\n {children}\n </CoinListContext.Provider>\n );\n}\n","/**\n * Error thrown when a feature or code path is not yet implemented.\n */\nexport class NotImplementedError extends Error {\n constructor(message = 'Not implemented yet') {\n super(message);\n this.name = 'NotImplementedError';\n }\n}\n\n/**\n * Error thrown when accessing a feature that requires authentication\n * without being authenticated.\n */\nexport class NotAuthenticatedError extends Error {\n constructor(\n message = 'The user is not authenticated. Go through the OAuth flow first!'\n ) {\n super(message);\n this.name = 'NotAuthenticatedError';\n }\n}\n","import { useEffect, useState } from 'react';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type { OfferId } from '@/shared/types/offer';\nimport type { OfferDetail } from '@/shared/types/offer-detail';\n\nexport type LoadOfferDetailsReason = 'not-authenticated' | 'generic-error';\n\nexport type LoadOfferDetailsState =\n | {\n type: 'LOADING';\n }\n | {\n type: 'ERROR';\n reason: LoadOfferDetailsReason;\n }\n | {\n type: 'CONTENT';\n offerDetail: OfferDetail;\n };\n\nexport interface UseCoinListOfferDetailsOptions {\n /**\n * Offer detail pre-fetched on the server (e.g. via `CoinListServer.fetchOfferDetails()`).\n * When provided, the hook uses this data as-is and skips the client-side fetch entirely.\n */\n serverData?: OfferDetail;\n}\n\nexport interface UseCoinListOfferDetailsResult {\n offerDetailsState: LoadOfferDetailsState;\n}\n\nconst LOADING_STATE: LoadOfferDetailsState = { type: 'LOADING' };\n\n/**\n * Loads CoinList offer details for a given offer id and exposes a UI-friendly state.\n *\n * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip\n * the client-side fetch entirely.\n *\n * Returns `LOADING` while CoinList is initializing or while details are being fetched.\n * Returns `CONTENT` with the offer when `fetchOfferDetails()` succeeds.\n * Returns `ERROR` with:\n * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}\n * - `generic-error` for any other failure\n */\nexport function useCoinListOfferDetails(\n offerId: OfferId,\n options: UseCoinListOfferDetailsOptions = {}\n): UseCoinListOfferDetailsResult {\n const { coinlist, isReady } = useCoinList();\n const { serverData } = options;\n const [offerDetailsState, setOfferDetailsState] =\n useState<LoadOfferDetailsState>(\n serverData !== undefined\n ? { type: 'CONTENT', offerDetail: serverData }\n : LOADING_STATE\n );\n\n useEffect(() => {\n let isCancelled = false;\n\n if (!isReady) {\n if (serverData === undefined) {\n setOfferDetailsState(LOADING_STATE);\n }\n return () => {\n isCancelled = true;\n };\n }\n\n if (serverData !== undefined) {\n return () => {\n isCancelled = true;\n };\n }\n\n setOfferDetailsState(LOADING_STATE);\n coinlist\n .fetchOfferDetails(offerId)\n .then((offerDetail) => {\n if (!isCancelled) {\n setOfferDetailsState({ type: 'CONTENT', offerDetail });\n }\n })\n .catch((error: unknown) => {\n if (!isCancelled) {\n const reason: LoadOfferDetailsReason =\n error instanceof NotAuthenticatedError\n ? 'not-authenticated'\n : 'generic-error';\n setOfferDetailsState({ type: 'ERROR', reason });\n }\n });\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist, isReady, offerId, serverData]);\n\n return { offerDetailsState };\n}\n","import { useEffect, useState } from 'react';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type { Offer } from '@/shared/types/offer';\n\nexport type LoadOffersReason = 'not-authenticated' | 'generic-error';\n\nexport type LoadOffersState =\n | {\n type: 'LOADING';\n }\n | {\n type: 'ERROR';\n reason: LoadOffersReason;\n }\n | {\n type: 'CONTENT';\n offers: Offer[];\n };\n\nexport interface UseCoinListOffersOptions {\n /**\n * Offers pre-fetched on the server (e.g. via `CoinListServer.fetchAllOffers()`).\n * When provided, the hook uses this data as-is and skips the client-side fetch entirely.\n */\n serverOffers?: Offer[];\n}\n\nexport interface UseCoinListOffersResult {\n offersState: LoadOffersState;\n}\n\nconst LOADING_STATE: LoadOffersState = { type: 'LOADING' };\n\n/**\n * Loads all CoinList offers for the current user and exposes a UI-friendly state.\n *\n * Pass `serverOffers` (pre-fetched server-side) to use that data as-is and skip\n * the client-side fetch entirely.\n *\n * Returns `LOADING` while CoinList is initializing or while offers are being fetched.\n * Returns `CONTENT` with all offers when `fetchAllOffers()` succeeds.\n * Returns `ERROR` with:\n * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}\n * - `generic-error` for any other failure\n */\nexport function useCoinListOffers(\n options: UseCoinListOffersOptions = {}\n): UseCoinListOffersResult {\n const { coinlist, isReady } = useCoinList();\n const { serverOffers } = options;\n const [offersState, setOffersState] = useState<LoadOffersState>(\n serverOffers !== undefined\n ? { type: 'CONTENT', offers: serverOffers }\n : LOADING_STATE\n );\n\n useEffect(() => {\n let isCancelled = false;\n\n // Wait until CoinListProvider finishes auth initialization.\n if (!isReady) {\n if (serverOffers === undefined) {\n setOffersState(LOADING_STATE);\n }\n return () => {\n isCancelled = true;\n };\n }\n\n if (serverOffers !== undefined) {\n return () => {\n isCancelled = true;\n };\n }\n\n setOffersState(LOADING_STATE);\n coinlist\n .fetchAllOffers()\n .then((offers) => {\n if (!isCancelled) {\n setOffersState({ type: 'CONTENT', offers });\n }\n })\n .catch((error: unknown) => {\n if (!isCancelled) {\n const reason: LoadOffersReason =\n error instanceof NotAuthenticatedError\n ? 'not-authenticated'\n : 'generic-error';\n setOffersState({ type: 'ERROR', reason });\n }\n });\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist, isReady, serverOffers]);\n\n return { offersState };\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type { OfferId } from '@/shared/types/offer';\nimport type { OfferOptionId } from '@/shared/types/offer-detail';\nimport type {\n Requirement,\n RequirementStatusInfo,\n} from '@/shared/types/requirement';\n\nexport type LoadRequirementsReason = 'not-authenticated' | 'generic-error';\n\nexport type LoadRequirementsState =\n | {\n type: 'LOADING';\n }\n | {\n type: 'ERROR';\n reason: LoadRequirementsReason;\n }\n | {\n type: 'CONTENT';\n requirementsByOptionId: Record<OfferOptionId, Requirement[]>;\n statuses: RequirementStatusInfo[];\n };\n\nexport interface UseCoinListRequirementsOptions {\n /**\n * Requirements data pre-fetched on the server (e.g. via\n * `CoinListServer.fetchOfferRequirements()` + `fetchRequirementStatuses()`).\n * When provided, the hook uses this data as-is and skips the initial client-side fetch.\n * Calling `refetch()` will still trigger a fresh fetch.\n */\n serverData?: {\n requirementsByOptionId: Record<OfferOptionId, Requirement[]>;\n statuses: RequirementStatusInfo[];\n };\n}\n\nexport interface UseCoinListRequirementsResult {\n requirementsState: LoadRequirementsState;\n /** Triggers a re-fetch of requirements and statuses. */\n refetch: () => void;\n}\n\nconst LOADING_STATE: LoadRequirementsState = { type: 'LOADING' };\n\n/**\n * Loads requirements (grouped by option ID) and requirement statuses for a\n * given offer, then exposes them via a state machine.\n *\n * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip\n * the initial client-side fetch. Calling `refetch()` will still trigger a fresh fetch.\n *\n * Returns `LOADING` while CoinList is initializing or while data is being fetched.\n * Returns `CONTENT` with requirementsByOptionId and statuses on success.\n * Returns `ERROR` with:\n * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}\n * - `generic-error` for any other failure\n */\nexport function useCoinListRequirements(\n offerId: OfferId,\n options: UseCoinListRequirementsOptions = {}\n): UseCoinListRequirementsResult {\n const { coinlist, isReady } = useCoinList();\n const { serverData } = options;\n const [requirementsState, setRequirementsState] =\n useState<LoadRequirementsState>(\n serverData !== undefined\n ? { type: 'CONTENT', ...serverData }\n : LOADING_STATE\n );\n const [fetchKey, setFetchKey] = useState(0);\n const refetch = useCallback(() => setFetchKey((k) => k + 1), []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: fetchKey is an intentional refetch trigger\n useEffect(() => {\n let isCancelled = false;\n\n if (!isReady) {\n if (serverData === undefined) {\n setRequirementsState(LOADING_STATE);\n }\n return () => {\n isCancelled = true;\n };\n }\n\n if (serverData !== undefined && fetchKey === 0) {\n return () => {\n isCancelled = true;\n };\n }\n\n setRequirementsState(LOADING_STATE);\n Promise.all([\n coinlist.fetchOfferRequirements(offerId),\n coinlist.fetchRequirementStatuses(offerId),\n ])\n .then(([requirementsByOptionId, statuses]) => {\n if (!isCancelled) {\n setRequirementsState({\n type: 'CONTENT',\n requirementsByOptionId,\n statuses,\n });\n }\n })\n .catch((error: unknown) => {\n if (!isCancelled) {\n const reason: LoadRequirementsReason =\n error instanceof NotAuthenticatedError\n ? 'not-authenticated'\n : 'generic-error';\n setRequirementsState({ type: 'ERROR', reason });\n }\n });\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist, isReady, offerId, fetchKey]);\n\n return { requirementsState, refetch };\n}\n","import { useEffect, useRef } from 'react';\nimport type { OauthClientErrorReason } from '@/client/core/types/client-types';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport type { AuthorizationCode, CodeVerifier } from '@/shared/types/oauth';\n\n/**\n * Reasons passed to {@link UseCompleteCoinListOAuthOptions.onFailure} when the\n * OAuth callback cannot be completed. Includes PKCE / redirect validation\n * errors from {@link CoinListClient.completeOAuth} and a dedicated value when\n * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails or rejects.\n */\nexport type CompleteCoinListOAuthFailureReason =\n | OauthClientErrorReason\n | 'complete_request_failed';\n\nexport interface UseCompleteCoinListOAuthOptions {\n /**\n * Exchange the authorization code on your backend (e.g. POST to your API).\n * Return `true` when the session was established successfully, `false` when\n * the request completed but signing in failed. Rejections are treated like\n * `false` and reported as {@link CompleteCoinListOAuthFailureReason}\n * `\"complete_request_failed\"`.\n */\n postOAuthComplete: (payload: {\n code: AuthorizationCode;\n codeVerifier: CodeVerifier;\n }) => Promise<boolean>;\n /**\n * Called when the user denied access (`error` query parameter), when\n * {@link CoinListClient.completeOAuth} fails PKCE/state validation, or when\n * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails.\n */\n onFailure: (\n reason: CompleteCoinListOAuthFailureReason\n ) => void | Promise<void>;\n /** Invoked after {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} succeeds and {@link CoinListClient.init} has refreshed the client session. */\n onSuccess: () => void | Promise<void>;\n}\n\n/**\n * Runs the CoinList OAuth callback once on mount: validates the redirect URL via\n * {@link CoinListClient.completeOAuth}, exchanges the code via {@link UseCompleteCoinListOAuthOptions.postOAuthComplete},\n * refreshes tokens with {@link CoinListClient.init}, then calls {@link UseCompleteCoinListOAuthOptions.onSuccess}.\n *\n * Framework-agnostic: reads `window.location` like {@link CoinListClient.completeOAuth}. Supply\n * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete}, {@link UseCompleteCoinListOAuthOptions.onFailure},\n * and {@link UseCompleteCoinListOAuthOptions.onSuccess} to perform redirects or routing in your app.\n *\n * Must be used within {@link CoinListProvider}.\n */\nexport function useCompleteCoinListOAuth(\n options: UseCompleteCoinListOAuthOptions\n): void {\n const { coinlist } = useCoinList();\n\n const postOAuthCompleteRef = useRef(options.postOAuthComplete);\n const onFailureRef = useRef(options.onFailure);\n const onSuccessRef = useRef(options.onSuccess);\n\n postOAuthCompleteRef.current = options.postOAuthComplete;\n onFailureRef.current = options.onFailure;\n onSuccessRef.current = options.onSuccess;\n\n const hasStartedExchangeRef = useRef(false);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const params = new URLSearchParams(window.location.search);\n if (!params.has('code') && !params.has('error')) return;\n\n if (hasStartedExchangeRef.current) return;\n\n const oauthResult = coinlist.completeOAuth();\n if (oauthResult.type === 'error') {\n void Promise.resolve(onFailureRef.current(oauthResult.reason)).catch(\n () => {}\n );\n return;\n }\n\n hasStartedExchangeRef.current = true;\n let cancelled = false;\n\n void (async () => {\n try {\n const ok = await postOAuthCompleteRef.current({\n code: oauthResult.code,\n codeVerifier: oauthResult.codeVerifier,\n });\n\n if (cancelled) return;\n\n if (!ok) {\n hasStartedExchangeRef.current = false;\n await onFailureRef.current('complete_request_failed');\n return;\n }\n\n await coinlist.init();\n\n if (cancelled) return;\n\n await onSuccessRef.current();\n } catch {\n if (cancelled) return;\n hasStartedExchangeRef.current = false;\n await onFailureRef.current('complete_request_failed');\n }\n })();\n\n return () => {\n cancelled = true;\n hasStartedExchangeRef.current = false;\n };\n }, [coinlist]);\n}\n","import { useEffect, useState } from 'react';\nimport { useCoinList } from '@/client/hooks/useCoinList';\nimport { NotAuthenticatedError } from '@/shared/types/errors';\nimport type { OfferId } from '@/shared/types/offer';\nimport type { Participation } from '@/shared/types/participation';\n\nexport type LoadParticipationsReason = 'not-authenticated' | 'generic-error';\n\nexport type LoadParticipationsState =\n | {\n type: 'LOADING';\n }\n | {\n type: 'ERROR';\n reason: LoadParticipationsReason;\n }\n | {\n type: 'CONTENT';\n participations: Participation[];\n };\n\nexport interface UseParticipationsOptions {\n /**\n * Participations pre-fetched on the server (e.g. via `CoinListServer.fetchAllParticipations()`).\n * When provided, the hook uses this data as-is and skips the client-side fetch entirely.\n */\n serverData?: Participation[];\n}\n\nexport interface UseParticipationsResult {\n participationsState: LoadParticipationsState;\n}\n\nconst LOADING_STATE: LoadParticipationsState = { type: 'LOADING' };\n\n/**\n * Loads all CoinList participations for the current user and exposes a UI-friendly state.\n * Optionally filters by offer when `offerId` is provided.\n *\n * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip\n * the client-side fetch entirely.\n *\n * Returns `LOADING` while CoinList is initializing or while participations are being fetched.\n * Returns `CONTENT` with all participations when `fetchAllParticipations()` succeeds.\n * Returns `ERROR` with:\n * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}\n * - `generic-error` for any other failure\n */\nexport function useParticipations(\n offerId?: OfferId,\n options: UseParticipationsOptions = {}\n): UseParticipationsResult {\n const { coinlist, isReady } = useCoinList();\n const { serverData } = options;\n const [participationsState, setParticipationsState] =\n useState<LoadParticipationsState>(\n serverData !== undefined\n ? { type: 'CONTENT', participations: serverData }\n : LOADING_STATE\n );\n\n useEffect(() => {\n let isCancelled = false;\n\n // Wait until CoinListProvider finishes auth initialization.\n if (!isReady) {\n if (serverData === undefined) {\n setParticipationsState(LOADING_STATE);\n }\n return () => {\n isCancelled = true;\n };\n }\n\n if (serverData !== undefined) {\n return () => {\n isCancelled = true;\n };\n }\n\n setParticipationsState(LOADING_STATE);\n coinlist\n .fetchAllParticipations(offerId)\n .then((participations) => {\n if (!isCancelled) {\n setParticipationsState({ type: 'CONTENT', participations });\n }\n })\n .catch((error: unknown) => {\n if (!isCancelled) {\n const reason: LoadParticipationsReason =\n error instanceof NotAuthenticatedError\n ? 'not-authenticated'\n : 'generic-error';\n setParticipationsState({ type: 'ERROR', reason });\n }\n });\n\n return () => {\n isCancelled = true;\n };\n }, [coinlist, isReady, offerId, serverData]);\n\n return { participationsState };\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","LOADING_STATE","import_react","LOADING_STATE","import_react","import_react","LOADING_STATE"]}
@@ -1,9 +1,17 @@
1
- export { C as CompleteCoinListOAuthFailureReason, U as UseCompleteCoinListOAuthOptions, u as useCoinList, a as useCompleteCoinListOAuth } from '../../useCompleteCoinListOAuth-C359mp_k.cjs';
2
- import { a as OfferId } from '../../offer-D6p_Ijme.cjs';
3
- import { e as OfferDetail } from '../../coinlist-client-Ds86ycAg.cjs';
4
- export { u as useCoinListOffers } from '../../useCoinListOffers-DvJxu6qj.cjs';
5
- import '../../oauth-session-DgItaMKN.cjs';
6
- import '../../newtype-yKTvFdg_.cjs';
1
+ import { a as CoinListClient, O as OauthClientErrorReason } from '../../coinlist-client-Cah5c_aF.cjs';
2
+ import { a as OfferDetail, b as OfferId } from '../../requirement-XWT0T_zO.cjs';
3
+ export { L as LoadOffersReason, a as LoadOffersState, b as LoadRequirementsReason, c as LoadRequirementsState, U as UseCoinListOffersOptions, d as UseCoinListOffersResult, e as UseCoinListRequirementsOptions, f as UseCoinListRequirementsResult, u as useCoinListOffers, g as useCoinListRequirements } from '../../useCoinListRequirements-Cuc32Tqw.cjs';
4
+ import { A as AuthorizationCode, c as CodeVerifier, j as Participation } from '../../participation-DMMEDxON.cjs';
5
+
6
+ interface UseCoinListResult {
7
+ /**
8
+ * Indicates whether the CoinList client has been initialized and ready to use.
9
+ * While isReady is false, then the current user state will be `unknown`.
10
+ */
11
+ isReady: boolean;
12
+ coinlist: CoinListClient;
13
+ }
14
+ declare function useCoinList(): UseCoinListResult;
7
15
 
8
16
  type LoadOfferDetailsReason = 'not-authenticated' | 'generic-error';
9
17
  type LoadOfferDetailsState = {
@@ -15,18 +23,104 @@ type LoadOfferDetailsState = {
15
23
  type: 'CONTENT';
16
24
  offerDetail: OfferDetail;
17
25
  };
26
+ interface UseCoinListOfferDetailsOptions {
27
+ /**
28
+ * Offer detail pre-fetched on the server (e.g. via `CoinListServer.fetchOfferDetails()`).
29
+ * When provided, the hook uses this data as-is and skips the client-side fetch entirely.
30
+ */
31
+ serverData?: OfferDetail;
32
+ }
18
33
  interface UseCoinListOfferDetailsResult {
19
34
  offerDetailsState: LoadOfferDetailsState;
20
35
  }
21
36
  /**
22
37
  * Loads CoinList offer details for a given offer id and exposes a UI-friendly state.
23
38
  *
39
+ * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip
40
+ * the client-side fetch entirely.
41
+ *
24
42
  * Returns `LOADING` while CoinList is initializing or while details are being fetched.
25
43
  * Returns `CONTENT` with the offer when `fetchOfferDetails()` succeeds.
26
44
  * Returns `ERROR` with:
27
45
  * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
28
46
  * - `generic-error` for any other failure
29
47
  */
30
- declare function useCoinListOfferDetails(offerId: OfferId): UseCoinListOfferDetailsResult;
48
+ declare function useCoinListOfferDetails(offerId: OfferId, options?: UseCoinListOfferDetailsOptions): UseCoinListOfferDetailsResult;
49
+
50
+ /**
51
+ * Reasons passed to {@link UseCompleteCoinListOAuthOptions.onFailure} when the
52
+ * OAuth callback cannot be completed. Includes PKCE / redirect validation
53
+ * errors from {@link CoinListClient.completeOAuth} and a dedicated value when
54
+ * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails or rejects.
55
+ */
56
+ type CompleteCoinListOAuthFailureReason = OauthClientErrorReason | 'complete_request_failed';
57
+ interface UseCompleteCoinListOAuthOptions {
58
+ /**
59
+ * Exchange the authorization code on your backend (e.g. POST to your API).
60
+ * Return `true` when the session was established successfully, `false` when
61
+ * the request completed but signing in failed. Rejections are treated like
62
+ * `false` and reported as {@link CompleteCoinListOAuthFailureReason}
63
+ * `"complete_request_failed"`.
64
+ */
65
+ postOAuthComplete: (payload: {
66
+ code: AuthorizationCode;
67
+ codeVerifier: CodeVerifier;
68
+ }) => Promise<boolean>;
69
+ /**
70
+ * Called when the user denied access (`error` query parameter), when
71
+ * {@link CoinListClient.completeOAuth} fails PKCE/state validation, or when
72
+ * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails.
73
+ */
74
+ onFailure: (reason: CompleteCoinListOAuthFailureReason) => void | Promise<void>;
75
+ /** Invoked after {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} succeeds and {@link CoinListClient.init} has refreshed the client session. */
76
+ onSuccess: () => void | Promise<void>;
77
+ }
78
+ /**
79
+ * Runs the CoinList OAuth callback once on mount: validates the redirect URL via
80
+ * {@link CoinListClient.completeOAuth}, exchanges the code via {@link UseCompleteCoinListOAuthOptions.postOAuthComplete},
81
+ * refreshes tokens with {@link CoinListClient.init}, then calls {@link UseCompleteCoinListOAuthOptions.onSuccess}.
82
+ *
83
+ * Framework-agnostic: reads `window.location` like {@link CoinListClient.completeOAuth}. Supply
84
+ * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete}, {@link UseCompleteCoinListOAuthOptions.onFailure},
85
+ * and {@link UseCompleteCoinListOAuthOptions.onSuccess} to perform redirects or routing in your app.
86
+ *
87
+ * Must be used within {@link CoinListProvider}.
88
+ */
89
+ declare function useCompleteCoinListOAuth(options: UseCompleteCoinListOAuthOptions): void;
90
+
91
+ type LoadParticipationsReason = 'not-authenticated' | 'generic-error';
92
+ type LoadParticipationsState = {
93
+ type: 'LOADING';
94
+ } | {
95
+ type: 'ERROR';
96
+ reason: LoadParticipationsReason;
97
+ } | {
98
+ type: 'CONTENT';
99
+ participations: Participation[];
100
+ };
101
+ interface UseParticipationsOptions {
102
+ /**
103
+ * Participations pre-fetched on the server (e.g. via `CoinListServer.fetchAllParticipations()`).
104
+ * When provided, the hook uses this data as-is and skips the client-side fetch entirely.
105
+ */
106
+ serverData?: Participation[];
107
+ }
108
+ interface UseParticipationsResult {
109
+ participationsState: LoadParticipationsState;
110
+ }
111
+ /**
112
+ * Loads all CoinList participations for the current user and exposes a UI-friendly state.
113
+ * Optionally filters by offer when `offerId` is provided.
114
+ *
115
+ * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip
116
+ * the client-side fetch entirely.
117
+ *
118
+ * Returns `LOADING` while CoinList is initializing or while participations are being fetched.
119
+ * Returns `CONTENT` with all participations when `fetchAllParticipations()` succeeds.
120
+ * Returns `ERROR` with:
121
+ * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
122
+ * - `generic-error` for any other failure
123
+ */
124
+ declare function useParticipations(offerId?: OfferId, options?: UseParticipationsOptions): UseParticipationsResult;
31
125
 
32
- export { useCoinListOfferDetails };
126
+ export { type CompleteCoinListOAuthFailureReason, type LoadOfferDetailsReason, type LoadOfferDetailsState, type LoadParticipationsReason, type LoadParticipationsState, type UseCoinListOfferDetailsOptions, type UseCoinListOfferDetailsResult, type UseCompleteCoinListOAuthOptions, type UseParticipationsOptions, type UseParticipationsResult, useCoinList, useCoinListOfferDetails, useCompleteCoinListOAuth, useParticipations };
@@ -1,9 +1,17 @@
1
- export { C as CompleteCoinListOAuthFailureReason, U as UseCompleteCoinListOAuthOptions, u as useCoinList, a as useCompleteCoinListOAuth } from '../../useCompleteCoinListOAuth-wZX3o8r2.js';
2
- import { a as OfferId } from '../../offer-DnFEFJw9.js';
3
- import { e as OfferDetail } from '../../coinlist-client-20v7fJSW.js';
4
- export { u as useCoinListOffers } from '../../useCoinListOffers-DIdOb5hB.js';
5
- import '../../oauth-session-DnKDI5ou.js';
6
- import '../../newtype-yKTvFdg_.js';
1
+ import { a as CoinListClient, O as OauthClientErrorReason } from '../../coinlist-client-BSxpqylo.js';
2
+ import { a as OfferDetail, b as OfferId } from '../../requirement-XWT0T_zO.js';
3
+ export { L as LoadOffersReason, a as LoadOffersState, b as LoadRequirementsReason, c as LoadRequirementsState, U as UseCoinListOffersOptions, d as UseCoinListOffersResult, e as UseCoinListRequirementsOptions, f as UseCoinListRequirementsResult, u as useCoinListOffers, g as useCoinListRequirements } from '../../useCoinListRequirements-B5-ZPOMz.js';
4
+ import { A as AuthorizationCode, c as CodeVerifier, j as Participation } from '../../participation-E-bT-GXJ.js';
5
+
6
+ interface UseCoinListResult {
7
+ /**
8
+ * Indicates whether the CoinList client has been initialized and ready to use.
9
+ * While isReady is false, then the current user state will be `unknown`.
10
+ */
11
+ isReady: boolean;
12
+ coinlist: CoinListClient;
13
+ }
14
+ declare function useCoinList(): UseCoinListResult;
7
15
 
8
16
  type LoadOfferDetailsReason = 'not-authenticated' | 'generic-error';
9
17
  type LoadOfferDetailsState = {
@@ -15,18 +23,104 @@ type LoadOfferDetailsState = {
15
23
  type: 'CONTENT';
16
24
  offerDetail: OfferDetail;
17
25
  };
26
+ interface UseCoinListOfferDetailsOptions {
27
+ /**
28
+ * Offer detail pre-fetched on the server (e.g. via `CoinListServer.fetchOfferDetails()`).
29
+ * When provided, the hook uses this data as-is and skips the client-side fetch entirely.
30
+ */
31
+ serverData?: OfferDetail;
32
+ }
18
33
  interface UseCoinListOfferDetailsResult {
19
34
  offerDetailsState: LoadOfferDetailsState;
20
35
  }
21
36
  /**
22
37
  * Loads CoinList offer details for a given offer id and exposes a UI-friendly state.
23
38
  *
39
+ * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip
40
+ * the client-side fetch entirely.
41
+ *
24
42
  * Returns `LOADING` while CoinList is initializing or while details are being fetched.
25
43
  * Returns `CONTENT` with the offer when `fetchOfferDetails()` succeeds.
26
44
  * Returns `ERROR` with:
27
45
  * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
28
46
  * - `generic-error` for any other failure
29
47
  */
30
- declare function useCoinListOfferDetails(offerId: OfferId): UseCoinListOfferDetailsResult;
48
+ declare function useCoinListOfferDetails(offerId: OfferId, options?: UseCoinListOfferDetailsOptions): UseCoinListOfferDetailsResult;
49
+
50
+ /**
51
+ * Reasons passed to {@link UseCompleteCoinListOAuthOptions.onFailure} when the
52
+ * OAuth callback cannot be completed. Includes PKCE / redirect validation
53
+ * errors from {@link CoinListClient.completeOAuth} and a dedicated value when
54
+ * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails or rejects.
55
+ */
56
+ type CompleteCoinListOAuthFailureReason = OauthClientErrorReason | 'complete_request_failed';
57
+ interface UseCompleteCoinListOAuthOptions {
58
+ /**
59
+ * Exchange the authorization code on your backend (e.g. POST to your API).
60
+ * Return `true` when the session was established successfully, `false` when
61
+ * the request completed but signing in failed. Rejections are treated like
62
+ * `false` and reported as {@link CompleteCoinListOAuthFailureReason}
63
+ * `"complete_request_failed"`.
64
+ */
65
+ postOAuthComplete: (payload: {
66
+ code: AuthorizationCode;
67
+ codeVerifier: CodeVerifier;
68
+ }) => Promise<boolean>;
69
+ /**
70
+ * Called when the user denied access (`error` query parameter), when
71
+ * {@link CoinListClient.completeOAuth} fails PKCE/state validation, or when
72
+ * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} fails.
73
+ */
74
+ onFailure: (reason: CompleteCoinListOAuthFailureReason) => void | Promise<void>;
75
+ /** Invoked after {@link UseCompleteCoinListOAuthOptions.postOAuthComplete} succeeds and {@link CoinListClient.init} has refreshed the client session. */
76
+ onSuccess: () => void | Promise<void>;
77
+ }
78
+ /**
79
+ * Runs the CoinList OAuth callback once on mount: validates the redirect URL via
80
+ * {@link CoinListClient.completeOAuth}, exchanges the code via {@link UseCompleteCoinListOAuthOptions.postOAuthComplete},
81
+ * refreshes tokens with {@link CoinListClient.init}, then calls {@link UseCompleteCoinListOAuthOptions.onSuccess}.
82
+ *
83
+ * Framework-agnostic: reads `window.location` like {@link CoinListClient.completeOAuth}. Supply
84
+ * {@link UseCompleteCoinListOAuthOptions.postOAuthComplete}, {@link UseCompleteCoinListOAuthOptions.onFailure},
85
+ * and {@link UseCompleteCoinListOAuthOptions.onSuccess} to perform redirects or routing in your app.
86
+ *
87
+ * Must be used within {@link CoinListProvider}.
88
+ */
89
+ declare function useCompleteCoinListOAuth(options: UseCompleteCoinListOAuthOptions): void;
90
+
91
+ type LoadParticipationsReason = 'not-authenticated' | 'generic-error';
92
+ type LoadParticipationsState = {
93
+ type: 'LOADING';
94
+ } | {
95
+ type: 'ERROR';
96
+ reason: LoadParticipationsReason;
97
+ } | {
98
+ type: 'CONTENT';
99
+ participations: Participation[];
100
+ };
101
+ interface UseParticipationsOptions {
102
+ /**
103
+ * Participations pre-fetched on the server (e.g. via `CoinListServer.fetchAllParticipations()`).
104
+ * When provided, the hook uses this data as-is and skips the client-side fetch entirely.
105
+ */
106
+ serverData?: Participation[];
107
+ }
108
+ interface UseParticipationsResult {
109
+ participationsState: LoadParticipationsState;
110
+ }
111
+ /**
112
+ * Loads all CoinList participations for the current user and exposes a UI-friendly state.
113
+ * Optionally filters by offer when `offerId` is provided.
114
+ *
115
+ * Pass `serverData` (pre-fetched server-side) to use that data as-is and skip
116
+ * the client-side fetch entirely.
117
+ *
118
+ * Returns `LOADING` while CoinList is initializing or while participations are being fetched.
119
+ * Returns `CONTENT` with all participations when `fetchAllParticipations()` succeeds.
120
+ * Returns `ERROR` with:
121
+ * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
122
+ * - `generic-error` for any other failure
123
+ */
124
+ declare function useParticipations(offerId?: OfferId, options?: UseParticipationsOptions): UseParticipationsResult;
31
125
 
32
- export { useCoinListOfferDetails };
126
+ export { type CompleteCoinListOAuthFailureReason, type LoadOfferDetailsReason, type LoadOfferDetailsState, type LoadParticipationsReason, type LoadParticipationsState, type UseCoinListOfferDetailsOptions, type UseCoinListOfferDetailsResult, type UseCompleteCoinListOAuthOptions, type UseParticipationsOptions, type UseParticipationsResult, useCoinList, useCoinListOfferDetails, useCompleteCoinListOAuth, useParticipations };
@@ -1,17 +1,19 @@
1
- import {
2
- useCoinListOfferDetails,
3
- useCoinListOffers
4
- } from "../../chunk-G3B4RLXP.js";
5
1
  import {
6
2
  useCoinList,
7
- useCompleteCoinListOAuth
8
- } from "../../chunk-32GOUV3L.js";
9
- import "../../chunk-Z5RFQOMS.js";
10
- import "../../chunk-GRVV7NLV.js";
3
+ useCoinListOfferDetails,
4
+ useCoinListOffers,
5
+ useCoinListRequirements,
6
+ useCompleteCoinListOAuth,
7
+ useParticipations
8
+ } from "../../chunk-UGJUTRXC.js";
9
+ import "../../chunk-RIFATQB5.js";
10
+ import "../../chunk-NC45IO63.js";
11
11
  export {
12
12
  useCoinList,
13
13
  useCoinListOfferDetails,
14
14
  useCoinListOffers,
15
- useCompleteCoinListOAuth
15
+ useCoinListRequirements,
16
+ useCompleteCoinListOAuth,
17
+ useParticipations
16
18
  };
17
19
  //# sourceMappingURL=index.js.map