@coinbase/cdp-api-client 0.0.119 → 0.0.120

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.
@@ -1,2 +1,2 @@
1
- import { __require as r } from "./index17.js";
1
+ import { __require as r } from "./index12.js";
2
2
  r();
@@ -1,4 +1,4 @@
1
- const o = "0.0.119";
1
+ const o = "0.0.120";
2
2
  export {
3
3
  o as VERSION
4
4
  };
@@ -1,287 +1,302 @@
1
- import { Analytics as i } from "./index2.js";
1
+ import { Analytics as d } from "./index2.js";
2
2
  import "./index3.js";
3
- import p from "axios";
4
- import { ERROR_DOCS_PAGE_URL as h } from "./index9.js";
5
- import { UnknownApiError as g, HttpErrorType as d, isOpenAPIError as y, APIError as l, UnknownError as T } from "./index7.js";
6
- const v = () => typeof window < "u" && typeof document < "u";
7
- let n = p.create({
3
+ import w from "axios";
4
+ import { ERROR_DOCS_PAGE_URL as m } from "./index9.js";
5
+ import { isRefreshTokenUnavailableError as y, UnknownApiError as T, HttpErrorType as p, isOpenAPIError as P, APIError as f, UnknownError as S, RefreshTokenUnavailableError as v } from "./index7.js";
6
+ const k = () => typeof window < "u" && typeof document < "u";
7
+ let n = w.create({
8
8
  baseURL: "https://api.cdp.coinbase.com/platform"
9
- }), c = null;
10
- const N = (t) => {
11
- if (n = p.create({
12
- baseURL: t.basePath || "https://api.cdp.coinbase.com/platform",
9
+ }), h = null;
10
+ const B = (e) => {
11
+ if (n = w.create({
12
+ baseURL: e.basePath || "https://api.cdp.coinbase.com/platform",
13
13
  // Only enable cookies in browser environments where they're supported
14
- withCredentials: v()
14
+ withCredentials: k()
15
15
  }), n.interceptors.request.use(async (r) => {
16
- const e = C(r), s = w(r);
17
- if (!c || _(s.pathname) || A(s.pathname) && !await c.isSignedIn() || !O(s.pathname))
16
+ const t = L(r), s = R(r);
17
+ if (!h || _(s.pathname) || A(s.pathname) && !await h.isSignedIn() || !X(s.pathname))
18
18
  return r;
19
- const a = await c.getXWalletAuth({
20
- requestMethod: e,
19
+ const a = await h.getXWalletAuth({
20
+ requestMethod: t,
21
21
  requestHost: s.host,
22
22
  requestPath: s.pathname,
23
23
  requestData: r.data
24
24
  });
25
25
  return a && (r.headers["X-Wallet-Auth"] = a), r;
26
26
  }), n.interceptors.request.use(async (r) => {
27
- const e = w(r);
28
- if (!c || _(e.pathname) || A(e.pathname) && !await c.isSignedIn())
27
+ const t = R(r);
28
+ if (!h || _(t.pathname) || A(t.pathname) && !await h.isSignedIn())
29
29
  return r;
30
- const s = await c.getToken();
30
+ const s = await h.getToken();
31
31
  return s && s !== "" && (r.headers.Authorization = `Bearer ${s}`), r;
32
- }), !v()) {
33
- const r = t.platform ? `CDP/reactnative/${t.platform}` : "CDP/reactnative";
34
- n.interceptors.request.use(async (e) => {
35
- let s = e.headers["User-Agent"];
36
- return s = s ? `${s} ${r}` : r, e.headers["User-Agent"] = s, e;
32
+ }), !k()) {
33
+ const r = e.platform ? `CDP/reactnative/${e.platform}` : "CDP/reactnative";
34
+ n.interceptors.request.use(async (t) => {
35
+ let s = t.headers["User-Agent"];
36
+ return s = s ? `${s} ${r}` : r, t.headers["User-Agent"] = s, t;
37
37
  });
38
38
  }
39
- t.refreshTokenStorage && U(t.refreshTokenStorage, t.retryTokenReads ?? !1), t.appAttestation && P(t.appAttestation, t.platform), t.debugging && (n.interceptors.request.use((r) => (console.log("Request:", r), r)), n.interceptors.response.use((r) => (console.log("Response:", r), r)));
40
- }, z = (t) => {
41
- c = t;
42
- }, P = (t, r) => {
43
- n.interceptors.request.use(async (e) => {
44
- const s = w(e);
45
- if (!x(s.pathname) || !t.isEnabled())
46
- return e;
39
+ e.refreshTokenStorage && I(
40
+ e.refreshTokenStorage,
41
+ e.retryTokenReads ?? !1,
42
+ e.failClosedOnMissingToken ?? !1
43
+ ), e.appAttestation && b(e.appAttestation, e.platform), e.debugging && (n.interceptors.request.use((r) => (console.log("Request:", r), r)), n.interceptors.response.use((r) => (console.log("Response:", r), r)));
44
+ }, K = (e) => {
45
+ h = e;
46
+ }, b = (e, r) => {
47
+ n.interceptors.request.use(async (t) => {
48
+ const s = R(t);
49
+ if (!D(s.pathname) || !e.isEnabled())
50
+ return t;
47
51
  try {
48
52
  if (r === "ios") {
49
53
  const a = s.pathname.match(/\/projects\/([^/]+)\//);
50
54
  if (!a || !a[1])
51
- return e;
52
- const u = `/v2/embedded-wallet-api/projects/${a[1]}/attestation/challenge`, m = (await n.post(u)).data.challenge;
53
- if (!m)
54
- return console.warn("Failed to fetch assertion challenge"), e;
55
- const f = await t.generateAssertion(m);
56
- if (!f)
57
- return e;
58
- e.headers["X-App-Attestation-Assertion"] = f.assertion, e.headers["X-App-Attestation-Key-ID"] = f.keyId, e.headers["X-App-Attestation-Challenge"] = m;
55
+ return t;
56
+ const l = `/v2/embedded-wallet-api/projects/${a[1]}/attestation/challenge`, u = (await n.post(l)).data.challenge;
57
+ if (!u)
58
+ return console.warn("Failed to fetch assertion challenge"), t;
59
+ const i = await e.generateAssertion(u);
60
+ if (!i)
61
+ return t;
62
+ t.headers["X-App-Attestation-Assertion"] = i.assertion, t.headers["X-App-Attestation-Key-ID"] = i.keyId, t.headers["X-App-Attestation-Challenge"] = u;
59
63
  } else {
60
- const a = await t.generateAssertion(JSON.stringify(e.data));
64
+ const a = await e.generateAssertion(JSON.stringify(t.data));
61
65
  if (!a)
62
- return e;
63
- e.headers["X-Integrity-Token"] = a.assertion, e.headers["X-Package-Name"] = t.appIdentifier;
66
+ return t;
67
+ t.headers["X-Integrity-Token"] = a.assertion, t.headers["X-Package-Name"] = e.appIdentifier;
64
68
  }
65
- return e;
69
+ return t;
66
70
  } catch (a) {
67
71
  throw new Error(
68
72
  `App attestation required but assertion generation failed: ${a instanceof Error ? a.message : String(a)}`
69
73
  );
70
74
  }
71
75
  });
72
- }, S = (t, r) => ({
73
- ...t,
76
+ }, $ = (e, r) => ({
77
+ ...e,
74
78
  headers: {
75
- ...t.headers || {},
79
+ ...e.headers || {},
76
80
  "X-Idempotency-Key": r
77
81
  }
78
- }), H = async (t, r) => {
79
- j(t), r && r !== "" && (t = S(t, r));
82
+ }), G = async (e, r) => {
83
+ x(e), r && r !== "" && (e = $(e, r));
80
84
  try {
81
- return (await n(t)).data;
82
- } catch (e) {
83
- if (p.isAxiosError(e) && !e.response)
84
- throw new g(
85
- d.unknown,
86
- e.cause instanceof Error ? e.cause.message : e.message,
87
- e.cause
85
+ return (await n(e)).data;
86
+ } catch (t) {
87
+ if (y(t))
88
+ throw t;
89
+ if (w.isAxiosError(t) && !t.response)
90
+ throw new T(
91
+ p.unknown,
92
+ t.cause instanceof Error ? t.cause.message : t.message,
93
+ t.cause
88
94
  );
89
- if (p.isAxiosError(e) && e.response) {
90
- if (y(e.response.data))
91
- throw new l(
92
- e.response.status,
93
- e.response.data.errorType,
94
- e.response.data.errorMessage,
95
- e.response.data.correlationId,
96
- e.response.data.errorLink,
97
- e.cause
95
+ if (w.isAxiosError(t) && t.response) {
96
+ if (P(t.response.data))
97
+ throw new f(
98
+ t.response.status,
99
+ t.response.data.errorType,
100
+ t.response.data.errorMessage,
101
+ t.response.data.correlationId,
102
+ t.response.data.errorLink,
103
+ t.cause
98
104
  );
99
105
  {
100
- const s = e.response.status;
106
+ const s = t.response.status;
101
107
  switch (s) {
102
108
  case 401:
103
- throw new l(
109
+ throw new f(
104
110
  s,
105
- d.unauthorized,
111
+ p.unauthorized,
106
112
  "Unauthorized.",
107
113
  void 0,
108
- `${h}#unauthorized`,
109
- e.cause
114
+ `${m}#unauthorized`,
115
+ t.cause
110
116
  );
111
117
  case 404:
112
- throw new l(
118
+ throw new f(
113
119
  s,
114
- d.not_found,
120
+ p.not_found,
115
121
  "API not found.",
116
122
  void 0,
117
- `${h}#not_found`,
118
- e.cause
123
+ `${m}#not_found`,
124
+ t.cause
119
125
  );
120
126
  case 502:
121
- throw new l(
127
+ throw new f(
122
128
  s,
123
- d.bad_gateway,
129
+ p.bad_gateway,
124
130
  "Bad gateway.",
125
131
  void 0,
126
- `${h}`,
127
- e.cause
132
+ `${m}`,
133
+ t.cause
128
134
  );
129
135
  case 503:
130
- throw new l(
136
+ throw new f(
131
137
  s,
132
- d.service_unavailable,
138
+ p.service_unavailable,
133
139
  "Service unavailable. Please try again later.",
134
140
  void 0,
135
- `${h}`,
136
- e.cause
141
+ `${m}`,
142
+ t.cause
137
143
  );
138
144
  default: {
139
145
  let a = "";
140
- if (e.response.data)
146
+ if (t.response.data)
141
147
  try {
142
- a = JSON.stringify(e.response.data);
148
+ a = JSON.stringify(t.response.data);
143
149
  } catch {
144
- a = String(e.response.data);
150
+ a = String(t.response.data);
145
151
  }
146
152
  const o = a ? `An unexpected error occurred: ${a}` : "An unexpected error occurred.";
147
- throw new l(
153
+ throw new f(
148
154
  s,
149
- d.unexpected_error,
155
+ p.unexpected_error,
150
156
  o,
151
157
  void 0,
152
- `${h}`,
153
- e.cause
158
+ `${m}`,
159
+ t.cause
154
160
  );
155
161
  }
156
162
  }
157
163
  }
158
164
  }
159
- throw new T("Something went wrong.", e instanceof Error ? e : void 0);
165
+ throw new S("Something went wrong.", t instanceof Error ? t : void 0);
160
166
  }
161
- }, k = 4, b = [25, 50, 100], E = async (t) => {
162
- if (t.getRefreshTokenWithOutcome) {
163
- const { token: r, outcome: e, errorMessage: s } = await t.getRefreshTokenWithOutcome();
164
- return { token: r, outcome: e, errorMessage: s };
167
+ }, E = 4, U = [25, 50, 100], g = async (e) => {
168
+ if (e.getRefreshTokenWithOutcome) {
169
+ const { token: r, outcome: t, errorMessage: s } = await e.getRefreshTokenWithOutcome();
170
+ return { token: r, outcome: t, errorMessage: s };
165
171
  }
166
- return { token: await t.getRefreshToken() };
167
- }, $ = async (t) => {
168
- let r = null, e, s;
169
- for (let a = 0; a < k; a++) {
170
- const o = await E(t);
171
- if (r = o.token, e = o.outcome, s = o.errorMessage, r)
172
+ return { token: await e.getRefreshToken() };
173
+ }, q = async (e) => {
174
+ let r = null, t, s;
175
+ for (let a = 0; a < E; a++) {
176
+ const o = await g(e);
177
+ if (r = o.token, t = o.outcome, s = o.errorMessage, r)
172
178
  return {
173
179
  token: r,
174
180
  attempt: a,
175
- outcome: e,
181
+ outcome: t,
176
182
  errorMessage: s
177
183
  };
178
- const u = b[a];
179
- u !== void 0 && await new Promise((R) => setTimeout(R, u));
184
+ const l = U[a];
185
+ l !== void 0 && await new Promise((c) => setTimeout(c, l));
180
186
  }
181
187
  return {
182
188
  token: r,
183
- attempt: k - 1,
184
- outcome: e,
189
+ attempt: E - 1,
190
+ outcome: t,
185
191
  errorMessage: s
186
192
  };
187
- }, U = (t, r) => {
193
+ }, I = (e, r, t) => {
188
194
  n.interceptors.response.use(
189
- async (e) => {
195
+ async (s) => {
190
196
  try {
191
- if (q(e)) {
192
- i.sendSessionRefreshEvent({
197
+ if (O(s)) {
198
+ d.sendSessionRefreshEvent({
193
199
  name: "refresh_token_response"
194
200
  });
195
- const s = e.data?.refreshToken;
196
- s && (i.sendSessionRefreshEvent({
201
+ const a = s.data?.refreshToken;
202
+ a && (d.sendSessionRefreshEvent({
197
203
  name: "refresh_token_received"
198
204
  }), await Promise.race([
199
- t.setRefreshToken(s),
205
+ e.setRefreshToken(a),
200
206
  new Promise(
201
- (a, o) => setTimeout(() => o(new Error("setRefreshToken timed out after 5000ms")), 5e3)
207
+ (o, l) => setTimeout(() => l(new Error("setRefreshToken timed out after 5000ms")), 5e3)
202
208
  )
203
- ]), i.sendSessionRefreshEvent({
209
+ ]), d.sendSessionRefreshEvent({
204
210
  name: "refresh_token_stored"
205
211
  }));
206
212
  }
207
- } catch (s) {
208
- const a = s instanceof Error ? s.message : String(s);
209
- i.sendSessionRefreshEvent({
213
+ } catch (a) {
214
+ const o = a instanceof Error ? a.message : String(a);
215
+ d.sendSessionRefreshEvent({
210
216
  name: "refresh_token_store_failed",
211
- error_message: a
212
- }), console.warn("Failed to store refresh token:", s);
217
+ error_message: o
218
+ }), console.warn("Failed to store refresh token:", a);
213
219
  }
214
- return e;
220
+ return s;
215
221
  },
216
- (e) => Promise.reject(e)
217
- ), n.interceptors.request.use(async (e) => {
218
- try {
219
- if (M(e.url)) {
220
- const {
221
- token: s,
222
- attempt: a,
223
- outcome: o,
224
- errorMessage: u
225
- } = r ? await $(t) : { ...await E(t), attempt: 0 };
226
- s ? (i.sendSessionRefreshEvent({
227
- name: "refresh_token_retrieved",
228
- attempt: a,
229
- read_outcome: o,
230
- error_message: u
231
- }), e.data = {
232
- ...e.data,
233
- refreshToken: s
234
- }) : i.sendSessionRefreshEvent({
235
- name: "refresh_token_missing",
236
- attempt: a,
237
- read_outcome: o,
222
+ (s) => Promise.reject(s)
223
+ ), n.interceptors.request.use(async (s) => {
224
+ if (j(s.url)) {
225
+ const a = C(s.url);
226
+ let o = null, l = 0, c, u;
227
+ try {
228
+ const i = r ? await q(e) : { ...await g(e), attempt: 0 };
229
+ o = i.token, l = i.attempt, c = i.outcome, u = i.errorMessage;
230
+ } catch (i) {
231
+ if (u = i instanceof Error ? i.message : String(i), c = "error", d.sendSessionRefreshEvent({
232
+ name: "refresh_token_retrieve_failed",
238
233
  error_message: u
239
- });
234
+ }), console.warn("Failed to retrieve refresh token:", i), a && t)
235
+ throw new v("error", u);
240
236
  }
241
- I(e.url) && (i.sendSessionRefreshEvent({
242
- name: "refresh_token_removing_token"
243
- }), await t.removeRefreshToken());
244
- } catch (s) {
245
- const a = s instanceof Error ? s.message : String(s);
246
- i.sendSessionRefreshEvent({
247
- name: "refresh_token_retrieve_failed",
248
- error_message: a
249
- }), console.warn("Failed to retrieve refresh token:", s);
237
+ if (o)
238
+ d.sendSessionRefreshEvent({
239
+ name: "refresh_token_retrieved",
240
+ attempt: l,
241
+ read_outcome: c,
242
+ error_message: u
243
+ }), s.data = {
244
+ ...s.data,
245
+ refreshToken: o
246
+ };
247
+ else if (d.sendSessionRefreshEvent({
248
+ name: "refresh_token_missing",
249
+ attempt: l,
250
+ read_outcome: c,
251
+ error_message: u
252
+ }), a && t)
253
+ throw new v(
254
+ c === "error" ? "error" : "not_found",
255
+ u
256
+ );
250
257
  }
251
- return e;
258
+ if (M(s.url))
259
+ try {
260
+ d.sendSessionRefreshEvent({
261
+ name: "refresh_token_removing_token"
262
+ }), await e.removeRefreshToken();
263
+ } catch (a) {
264
+ console.warn("Failed to remove refresh token:", a);
265
+ }
266
+ return s;
252
267
  });
253
- }, I = (t) => t ? /^\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/logout$/.test(t) : !1, M = (t) => t ? /^\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(refresh|logout)$/.test(t) : !1, q = (t) => {
254
- const r = t.config.url;
268
+ }, M = (e) => e ? /^\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/logout$/.test(e) : !1, j = (e) => e ? /^\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(refresh|logout)$/.test(e) : !1, C = (e) => e ? /^\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/refresh$/.test(e) : !1, O = (e) => {
269
+ const r = e.config.url;
255
270
  return r ? /^\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(verify\/(email|sms|siwe|oauth\/.*)|refresh)$/.test(
256
271
  r
257
272
  ) : !1;
258
- }, j = (t) => {
273
+ }, x = (e) => {
259
274
  if (!n.getUri() || n.getUri() === "")
260
275
  throw new Error("CDP client URI not configured. Call configure() first.");
261
- if (!t.url || t.url === "")
276
+ if (!e.url || e.url === "")
262
277
  throw new Error("AxiosRequestConfig URL is empty. This should never happen.");
263
- if (!t.method || t.method === "")
278
+ if (!e.method || e.method === "")
264
279
  throw new Error("AxiosRequestConfig method is empty. This should never happen.");
265
- }, C = (t) => t.method?.toString().toUpperCase() || "GET", w = (t) => {
266
- if (!t.url)
280
+ }, L = (e) => e.method?.toString().toUpperCase() || "GET", R = (e) => {
281
+ if (!e.url)
267
282
  throw new Error("URL is required for authentication");
268
- const r = n.getUri() + t.url;
283
+ const r = n.getUri() + e.url;
269
284
  return new URL(r);
270
- }, _ = (t) => {
271
- const r = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(refresh|logout)$/, e = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(mfa)$/, s = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/config$/, a = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/attestation\/(challenge|register)$/;
272
- return r.test(t) || e.test(t) || s.test(t) || a.test(t);
273
- }, O = (t) => !/^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/custom\/authenticate$/.test(t), A = (t) => {
274
- const r = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(init)$/, e = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/verify\//;
275
- return r.test(t) || e.test(t);
276
- }, x = (t) => [
285
+ }, _ = (e) => {
286
+ const r = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(refresh|logout)$/, t = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(mfa)$/, s = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/config$/, a = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/attestation\/(challenge|register)$/;
287
+ return r.test(e) || t.test(e) || s.test(e) || a.test(e);
288
+ }, X = (e) => !/^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/custom\/authenticate$/.test(e), A = (e) => {
289
+ const r = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/(init)$/, t = /^\/platform\/v2\/embedded-wallet-api\/projects\/[^/]+\/auth\/verify\//;
290
+ return r.test(e) || t.test(e);
291
+ }, D = (e) => [
277
292
  // Auth flows: prevent account takeover on mobile clients
278
293
  /\/auth\/verify\//,
279
294
  // VerifyEmailAuthentication, VerifySmsAuthentication, VerifyOAuthCode, VerifyOAuthEndUserIdentity
280
295
  /\/auth\/refresh$/
281
296
  // RefreshAccessToken
282
- ].some((e) => e.test(t));
297
+ ].some((t) => t.test(e));
283
298
  export {
284
- H as cdpApiClient,
285
- N as configureCdpApiClient,
286
- z as setAuthManager
299
+ G as cdpApiClient,
300
+ B as configureCdpApiClient,
301
+ K as setAuthManager
287
302
  };
@@ -22,8 +22,8 @@ class a extends Error {
22
22
  * @param errorLink - URL to documentation about this error
23
23
  * @param cause - The cause of the error
24
24
  */
25
- constructor(e, o, t, n, s, i) {
26
- super(t, { cause: i }), this.name = "APIError", this.statusCode = e, this.errorType = o, this.errorMessage = t, n !== void 0 && (this.correlationId = n), s !== void 0 && (this.errorLink = s);
25
+ constructor(e, o, n, t, s, i) {
26
+ super(n, { cause: i }), this.name = "APIError", this.statusCode = e, this.errorType = o, this.errorMessage = n, t !== void 0 && (this.correlationId = t), s !== void 0 && (this.errorLink = s);
27
27
  }
28
28
  /**
29
29
  * Convert the error to a JSON object, excluding undefined properties
@@ -49,11 +49,11 @@ class d extends a {
49
49
  * @param errorMessage - The error message
50
50
  * @param cause - The cause of the error
51
51
  */
52
- constructor(e, o, t) {
53
- super(0, e, o, void 0, void 0, t), this.name = "UnknownApiError";
52
+ constructor(e, o, n) {
53
+ super(0, e, o, void 0, void 0, n), this.name = "UnknownApiError";
54
54
  }
55
55
  }
56
- class c extends Error {
56
+ class l extends Error {
57
57
  /**
58
58
  * Constructor for the UnknownError class
59
59
  *
@@ -64,13 +64,36 @@ class c extends Error {
64
64
  super(e, { cause: o }), this.name = "UnknownError";
65
65
  }
66
66
  }
67
- function p(r) {
67
+ function E(r) {
68
68
  return r !== null && typeof r == "object" && "errorType" in r && typeof r.errorType == "string" && "errorMessage" in r && typeof r.errorMessage == "string";
69
69
  }
70
+ class h extends Error {
71
+ /** Stable discriminator used for cross-package detection. */
72
+ code = "CDP_REFRESH_TOKEN_UNAVAILABLE";
73
+ /**
74
+ * The read outcome that produced this error:
75
+ * - `"not_found"`: storage returned no token (the user may be genuinely signed out).
76
+ * - `"error"`: the read threw or reported an error (e.g. storage locked / not ready).
77
+ */
78
+ outcome;
79
+ /**
80
+ * Constructor for the RefreshTokenUnavailableError class
81
+ *
82
+ * @param outcome - The read outcome that produced this error.
83
+ * @param message - An optional human-readable message.
84
+ */
85
+ constructor(e, o) {
86
+ super(o ?? `Refresh token unavailable in local storage (${e})`), this.name = "RefreshTokenUnavailableError", this.outcome = e;
87
+ }
88
+ }
89
+ const p = (r) => typeof r == "object" && r !== null && r.code === "CDP_REFRESH_TOKEN_UNAVAILABLE", c = /user interaction is not allowed/i, T = (r) => r.outcome === "error" && c.test(r.message ?? "");
70
90
  export {
71
91
  a as APIError,
72
92
  u as HttpErrorType,
93
+ h as RefreshTokenUnavailableError,
73
94
  d as UnknownApiError,
74
- c as UnknownError,
75
- p as isOpenAPIError
95
+ l as UnknownError,
96
+ E as isOpenAPIError,
97
+ p as isRefreshTokenUnavailableError,
98
+ T as isTransientTokenReadError
76
99
  };
@@ -210,6 +210,15 @@ export declare type CdpOptions = {
210
210
  * reads are synchronous and have no such race. Defaults to false.
211
211
  */
212
212
  retryTokenReads?: boolean;
213
+ /**
214
+ * Whether an unreadable/missing refresh token should fail closed (throw
215
+ * {@link RefreshTokenUnavailableError}) instead of proceeding with a token-less
216
+ * refresh. Enable only where the stored token is the sole transport (native,
217
+ * which has no cookies). On web the httpOnly cookie is the primary transport and
218
+ * storage is a supplemental mirror, so an empty mirror must NOT block the refresh.
219
+ * Defaults to false.
220
+ */
221
+ failClosedOnMissingToken?: boolean;
213
222
  /**
214
223
  * Optional app attestation for native mobile platforms.
215
224
  * When provided, adds assertion headers to sensitive API calls to prove app integrity.
@@ -2152,6 +2161,28 @@ export declare const IosAttestationRegistrationResponsePlatform: {
2152
2161
  */
2153
2162
  export declare function isOpenAPIError(obj: unknown): obj is Error_2;
2154
2163
 
2164
+ /**
2165
+ * Type guard for {@link RefreshTokenUnavailableError} that is robust to the class
2166
+ * being duplicated across bundled package boundaries by checking a stable `code`
2167
+ * discriminator rather than the prototype chain.
2168
+ *
2169
+ * @param error - The value to check
2170
+ * @returns True if the value is a RefreshTokenUnavailableError
2171
+ */
2172
+ export declare const isRefreshTokenUnavailableError: (error: unknown) => error is RefreshTokenUnavailableError;
2173
+
2174
+ /**
2175
+ * Determines whether a {@link RefreshTokenUnavailableError} represents a transient
2176
+ * read failure (the token exists but is momentarily unreadable) rather than a
2177
+ * definitive one. Only a locked secure store qualifies, so a later refresh can
2178
+ * recover; a genuinely absent token or any other read error is definitive and
2179
+ * callers should treat it as a hard sign-out condition.
2180
+ *
2181
+ * @param error - The refresh-token-unavailable error to classify.
2182
+ * @returns True if the read failure is transient and the session should be kept.
2183
+ */
2184
+ export declare const isTransientTokenReadError: (error: RefreshTokenUnavailableError) => boolean;
2185
+
2155
2186
  export declare interface ListResponse {
2156
2187
  /** The token for the next page of items, if any. */
2157
2188
  nextPageToken?: string;
@@ -2568,6 +2599,38 @@ export declare interface RefreshTokenStorage {
2568
2599
  removeRefreshToken(): Promise<void>;
2569
2600
  }
2570
2601
 
2602
+ /**
2603
+ * Error thrown by the request interceptor when a token refresh is attempted but
2604
+ * the refresh token cannot be read from local storage — either genuinely absent
2605
+ * or the read failed (e.g. an OS secure store that is temporarily locked or not
2606
+ * yet ready after an app resume).
2607
+ *
2608
+ * This is deliberately distinct from a server-side rejection of a refresh token
2609
+ * (HTTP 401): when this error is thrown, no request reaches the server. Consumers
2610
+ * should treat it as a recoverable, local condition and avoid destructive actions
2611
+ * (such as signing the user out) on a merely transient read failure.
2612
+ *
2613
+ * Prefer {@link isRefreshTokenUnavailableError} over `instanceof` when detecting
2614
+ * this error, since bundlers can duplicate the class across package boundaries.
2615
+ */
2616
+ export declare class RefreshTokenUnavailableError extends Error {
2617
+ /** Stable discriminator used for cross-package detection. */
2618
+ readonly code = "CDP_REFRESH_TOKEN_UNAVAILABLE";
2619
+ /**
2620
+ * The read outcome that produced this error:
2621
+ * - `"not_found"`: storage returned no token (the user may be genuinely signed out).
2622
+ * - `"error"`: the read threw or reported an error (e.g. storage locked / not ready).
2623
+ */
2624
+ readonly outcome: "not_found" | "error";
2625
+ /**
2626
+ * Constructor for the RefreshTokenUnavailableError class
2627
+ *
2628
+ * @param outcome - The read outcome that produced this error.
2629
+ * @param message - An optional human-readable message.
2630
+ */
2631
+ constructor(outcome: "not_found" | "error", message?: string);
2632
+ }
2633
+
2571
2634
  /**
2572
2635
  * Verifies an iOS App Attest attestation payload and registers the device's public key for future assertion validation. **This endpoint is iOS-only.** Android devices use a different flow (see below).
2573
2636
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cdp-api-client",
3
- "version": "0.0.119",
3
+ "version": "0.0.120",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**",