@quiltt/core 3.5.5 → 3.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @quiltt/core
2
2
 
3
+ ## 3.5.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#223](https://github.com/quiltt/quiltt-js/pull/223) [`8c5041c`](https://github.com/quiltt/quiltt-js/commit/8c5041c1670fd7dbfed06355c06888256ce84b08) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Remove axios and replace with native fetch
8
+
3
9
  ## 3.5.5
4
10
 
5
11
  ### Patch Changes
@@ -24,57 +30,57 @@
24
30
 
25
31
  ### Patch Changes
26
32
 
27
- - [#204](https://github.com/quiltt/quiltt-public/pull/204) [`ee42bf1`](https://github.com/quiltt/quiltt-public/commit/ee42bf137db1029807df49f66ff7e57117e8ace9) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Add missing type to QuilttButton
33
+ - [#204](https://github.com/quiltt/quiltt-js/pull/204) [`ee42bf1`](https://github.com/quiltt/quiltt-js/commit/ee42bf137db1029807df49f66ff7e57117e8ace9) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Add missing type to QuilttButton
28
34
 
29
- - [#197](https://github.com/quiltt/quiltt-public/pull/197) [`8d9f24c`](https://github.com/quiltt/quiltt-public/commit/8d9f24c59102db5dc665195a6145cfac2c80e2c0) Thanks [@rubendinho](https://github.com/rubendinho)! - Update docs, and fix typo in types
35
+ - [#197](https://github.com/quiltt/quiltt-js/pull/197) [`8d9f24c`](https://github.com/quiltt/quiltt-js/commit/8d9f24c59102db5dc665195a6145cfac2c80e2c0) Thanks [@rubendinho](https://github.com/rubendinho)! - Update docs, and fix typo in types
30
36
 
31
37
  ## 3.5.2
32
38
 
33
39
  ### Patch Changes
34
40
 
35
- - [#202](https://github.com/quiltt/quiltt-public/pull/202) [`42705f0`](https://github.com/quiltt/quiltt-public/commit/42705f0e01b0adb35ab627697169433e1065a8f0) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Expose Institutions to React API
41
+ - [#202](https://github.com/quiltt/quiltt-js/pull/202) [`42705f0`](https://github.com/quiltt/quiltt-js/commit/42705f0e01b0adb35ab627697169433e1065a8f0) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Expose Institutions to React API
36
42
 
37
43
  ## 3.5.1
38
44
 
39
45
  ### Patch Changes
40
46
 
41
- - [#200](https://github.com/quiltt/quiltt-public/pull/200) [`0a07431`](https://github.com/quiltt/quiltt-public/commit/0a07431ff936e6cd4fd3aeee66bba1fec21f6624) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Fix release
47
+ - [#200](https://github.com/quiltt/quiltt-js/pull/200) [`0a07431`](https://github.com/quiltt/quiltt-js/commit/0a07431ff936e6cd4fd3aeee66bba1fec21f6624) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Fix release
42
48
 
43
49
  ## 3.5.0
44
50
 
45
51
  ### Minor Changes
46
52
 
47
- - [#198](https://github.com/quiltt/quiltt-public/pull/198) [`c65d87a`](https://github.com/quiltt/quiltt-public/commit/c65d87a8316dbec82635a0c4108714de7bbd082b) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Add Institutions to the Connect API
53
+ - [#198](https://github.com/quiltt/quiltt-js/pull/198) [`c65d87a`](https://github.com/quiltt/quiltt-js/commit/c65d87a8316dbec82635a0c4108714de7bbd082b) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Add Institutions to the Connect API
48
54
 
49
55
  ## 3.4.1
50
56
 
51
57
  ### Patch Changes
52
58
 
53
- - [#195](https://github.com/quiltt/quiltt-public/pull/195) [`6c36908`](https://github.com/quiltt/quiltt-public/commit/6c36908678cb46d5f6a0c7438e0ed48889cabf79) Thanks [@tom-quiltt](https://github.com/tom-quiltt)! - Report preflight error before sending connectorUrl to webview
59
+ - [#195](https://github.com/quiltt/quiltt-js/pull/195) [`6c36908`](https://github.com/quiltt/quiltt-js/commit/6c36908678cb46d5f6a0c7438e0ed48889cabf79) Thanks [@tom-quiltt](https://github.com/tom-quiltt)! - Report preflight error before sending connectorUrl to webview
54
60
 
55
61
  ## 3.4.0
56
62
 
57
63
  ### Minor Changes
58
64
 
59
- - [#191](https://github.com/quiltt/quiltt-public/pull/191) [`58c8f0c`](https://github.com/quiltt/quiltt-public/commit/58c8f0c5265dfa379263225baafb4552067514c6) Thanks [@tom-quiltt](https://github.com/tom-quiltt)! - Add agent QSP for analytic
65
+ - [#191](https://github.com/quiltt/quiltt-js/pull/191) [`58c8f0c`](https://github.com/quiltt/quiltt-js/commit/58c8f0c5265dfa379263225baafb4552067514c6) Thanks [@tom-quiltt](https://github.com/tom-quiltt)! - Add agent QSP for analytic
60
66
 
61
67
  ## 3.3.10
62
68
 
63
69
  ### Patch Changes
64
70
 
65
- - [#190](https://github.com/quiltt/quiltt-public/pull/190) [`21ead66`](https://github.com/quiltt/quiltt-public/commit/21ead662e7626f906562f952c9d1c0bc2c859985) Thanks [@tom-quiltt](https://github.com/tom-quiltt)! - Fix Android App with Chase app installed not able to launch Chase app
71
+ - [#190](https://github.com/quiltt/quiltt-js/pull/190) [`21ead66`](https://github.com/quiltt/quiltt-js/commit/21ead662e7626f906562f952c9d1c0bc2c859985) Thanks [@tom-quiltt](https://github.com/tom-quiltt)! - Fix Android App with Chase app installed not able to launch Chase app
66
72
 
67
73
  ## 3.3.9
68
74
 
69
75
  ### Patch Changes
70
76
 
71
- - [#187](https://github.com/quiltt/quiltt-public/pull/187) [`02f37cd`](https://github.com/quiltt/quiltt-public/commit/02f37cda97f501f2d77601d0fd6b7fbbd1c71431) Thanks [@rubendinho](https://github.com/rubendinho)! - Export Quiltt config
77
+ - [#187](https://github.com/quiltt/quiltt-js/pull/187) [`02f37cd`](https://github.com/quiltt/quiltt-js/commit/02f37cda97f501f2d77601d0fd6b7fbbd1c71431) Thanks [@rubendinho](https://github.com/rubendinho)! - Export Quiltt config
72
78
 
73
79
  ## 3.3.8
74
80
 
75
81
  ### Patch Changes
76
82
 
77
- - [#185](https://github.com/quiltt/quiltt-public/pull/185) [`a3452da`](https://github.com/quiltt/quiltt-public/commit/a3452da3c7604902b5917e1f838e2dced42b708c) Thanks [@rubendinho](https://github.com/rubendinho)! - Fix Vite build error
83
+ - [#185](https://github.com/quiltt/quiltt-js/pull/185) [`a3452da`](https://github.com/quiltt/quiltt-js/commit/a3452da3c7604902b5917e1f838e2dced42b708c) Thanks [@rubendinho](https://github.com/rubendinho)! - Fix Vite build error
78
84
 
79
85
  ## 3.3.7
80
86
 
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @quiltt/core
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@quiltt%2Fcore.svg)](https://badge.fury.io/js/@quiltt%2Fcore)
4
- [![CI](https://github.com/quiltt/quiltt-public/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/quiltt/quiltt-public/actions/workflows/ci.yml)
4
+ [![CI](https://github.com/quiltt/quiltt-js/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/quiltt/quiltt-js/actions/workflows/ci.yml)
5
5
 
6
6
  `@quiltt/core` provides essential functionality for building Javascript-based applications with Quiltt. It provides an Auth API client and modules for handling JSON Web Tokens (JWT), observables, storage management, timeouts, API handling, and Typescript types.
7
7
 
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
  import { ApolloLink, Observable as Observable$1 } from '@apollo/client/core/index.js';
3
3
  import { print } from 'graphql';
4
- import { c as createConsumer } from './index-client-BY-d8Msy.js';
4
+ import { c as createConsumer } from './index-client-D55-rzVl.js';
5
5
 
6
6
  var name = "@quiltt/core";
7
- var version$1 = "3.5.5";
7
+ var version$1 = "3.5.6";
8
8
 
9
9
  const QUILTT_API_INSECURE = (()=>{
10
10
  try {
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { d as debugging } from './SubscriptionLink-client-B5Tmyqw7.js';
2
+ import { d as debugging } from './SubscriptionLink-client-VkkcO2Yz.js';
3
3
 
4
4
  var adapters = {
5
5
  logger: typeof globalThis !== 'undefined' ? globalThis.console : undefined,
package/dist/index.d.ts CHANGED
@@ -6,7 +6,6 @@ import * as _apollo_client from '@apollo/client';
6
6
  import { HttpLink as HttpLink$1 } from '@apollo/client/link/http/index.js';
7
7
  import { RetryLink as RetryLink$1 } from '@apollo/client/link/retry/index.js';
8
8
  import { ApolloLink as ApolloLink$1, Operation as Operation$1, NextLink as NextLink$1, Observable as Observable$2, FetchResult as FetchResult$1 } from '@apollo/client/core/index.js';
9
- import { AxiosResponse } from 'axios';
10
9
 
11
10
  declare const debugging: boolean;
12
11
  declare const version: string;
@@ -370,6 +369,14 @@ declare class QuilttClient<T> extends ApolloClient<T> {
370
369
  constructor(options: QuilttClientOptions<T>);
371
370
  }
372
371
 
372
+ type FetchResponse<T> = {
373
+ data: T;
374
+ status: number;
375
+ statusText: string;
376
+ headers: Headers;
377
+ ok: boolean;
378
+ };
379
+
373
380
  declare enum AuthStrategies {
374
381
  Email = "email",
375
382
  Phone = "phone"
@@ -389,7 +396,7 @@ type PasscodePayload = UsernamePayload & {
389
396
  type SessionData = {
390
397
  token: string;
391
398
  };
392
- type NoContentData = void;
399
+ type NoContentData = null;
393
400
  type UnauthorizedData = {
394
401
  message: string;
395
402
  instruction: string;
@@ -401,8 +408,8 @@ type Ping = SessionData | UnauthorizedData;
401
408
  type Identify = SessionData | NoContentData | UnprocessableData;
402
409
  type Authenticate = SessionData | UnauthorizedData | UnprocessableData;
403
410
  type Revoke = NoContentData | UnauthorizedData;
404
- type SessionResponse = AxiosResponse<SessionData>;
405
- type UnprocessableResponse = AxiosResponse<UnprocessableData>;
411
+ type SessionResponse = FetchResponse<SessionData>;
412
+ type UnprocessableResponse = FetchResponse<UnprocessableData>;
406
413
  declare class AuthAPI {
407
414
  clientId: string | undefined;
408
415
  constructor(clientId?: string | undefined);
@@ -411,27 +418,27 @@ declare class AuthAPI {
411
418
  * - 200: OK -> Session is Valid
412
419
  * - 401: Unauthorized -> Session is Invalid
413
420
  */
414
- ping: (token: string) => Promise<AxiosResponse<Ping, any>>;
421
+ ping: (token: string) => Promise<FetchResponse<Ping>>;
415
422
  /**
416
423
  * Response Statuses:
417
424
  * - 201: Created -> Profile Created, New Session Returned
418
425
  * - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
419
426
  * - 422: Unprocessable Entity -> Invalid Payload
420
427
  */
421
- identify: (payload: UsernamePayload) => Promise<AxiosResponse<Identify, any>>;
428
+ identify: (payload: UsernamePayload) => Promise<FetchResponse<Identify>>;
422
429
  /**
423
430
  * Response Statuses:
424
431
  * - 201: Created -> MFA Validated, New Session Returned
425
432
  * - 401: Unauthorized -> MFA Invalid
426
433
  * - 422: Unprocessable Entity -> Invalid Payload
427
434
  */
428
- authenticate: (payload: PasscodePayload) => Promise<AxiosResponse<Authenticate, any>>;
435
+ authenticate: (payload: PasscodePayload) => Promise<FetchResponse<Authenticate>>;
429
436
  /**
430
437
  * Response Statuses:
431
438
  * - 204: No Content -> Session Revoked
432
439
  * - 401: Unauthorized -> Session Not Found
433
440
  */
434
- revoke: (token: string) => Promise<AxiosResponse<Revoke, any>>;
441
+ revoke: (token: string) => Promise<FetchResponse<Revoke>>;
435
442
  private config;
436
443
  private validateStatus;
437
444
  private body;
package/dist/index.js CHANGED
@@ -1,13 +1,12 @@
1
- import { G as GlobalStorage, e as endpointGraphQL, v as version, d as debugging, S as SubscriptionLink, a as endpointAuth } from './SubscriptionLink-client-B5Tmyqw7.js';
2
- export { L as LocalStorage, M as MemoryStorage, O as Observable, f as Storage, c as cdnBase, b as endpointWebsockets } from './SubscriptionLink-client-B5Tmyqw7.js';
1
+ import { G as GlobalStorage, e as endpointGraphQL, v as version, d as debugging, S as SubscriptionLink, a as endpointAuth } from './SubscriptionLink-client-VkkcO2Yz.js';
2
+ export { L as LocalStorage, M as MemoryStorage, O as Observable, f as Storage, c as cdnBase, b as endpointWebsockets } from './SubscriptionLink-client-VkkcO2Yz.js';
3
3
  import { ApolloLink, ApolloClient } from '@apollo/client/index.js';
4
4
  export { InMemoryCache, gql, useMutation, useQuery, useSubscription } from '@apollo/client/index.js';
5
5
  import { BatchHttpLink as BatchHttpLink$1 } from '@apollo/client/link/batch-http/index.js';
6
- import fetch from 'cross-fetch';
6
+ import crossfetch from 'cross-fetch';
7
7
  import { onError } from '@apollo/client/link/error/index.js';
8
8
  import { HttpLink as HttpLink$1 } from '@apollo/client/link/http/index.js';
9
9
  import { RetryLink as RetryLink$1 } from '@apollo/client/link/retry/index.js';
10
- import Axios from 'axios';
11
10
 
12
11
  const MATCHER = /^(?:[\w-]+\.){2}[\w-]+$/;
13
12
  const JsonWebTokenParse = (token)=>{
@@ -81,9 +80,11 @@ var ConnectorSDKEventType;
81
80
  }
82
81
  }
83
82
 
83
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
84
+ const effectiveFetch$2 = typeof fetch === 'undefined' ? crossfetch : fetch;
84
85
  const BatchHttpLink = new BatchHttpLink$1({
85
86
  uri: endpointGraphQL,
86
- fetch
87
+ fetch: effectiveFetch$2
87
88
  });
88
89
 
89
90
  const ErrorLink = onError(({ graphQLErrors, networkError })=>{
@@ -104,9 +105,11 @@ const ErrorLink = onError(({ graphQLErrors, networkError })=>{
104
105
 
105
106
  const ForwardableLink = new ApolloLink((operation, forward)=>forward(operation));
106
107
 
108
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
109
+ const effectiveFetch$1 = typeof fetch === 'undefined' ? crossfetch : fetch;
107
110
  const HttpLink = new HttpLink$1({
108
111
  uri: endpointGraphQL,
109
- fetch
112
+ fetch: effectiveFetch$1
110
113
  });
111
114
 
112
115
  const RetryLink = new RetryLink$1({
@@ -152,35 +155,51 @@ class QuilttClient extends ApolloClient {
152
155
  }
153
156
  }
154
157
 
158
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
159
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch;
155
160
  const RETRY_DELAY = 150 // ms
156
161
  ;
157
162
  const RETRIES = 10 // 150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500 = 8.250s
158
163
  ;
159
- // Create an axios singleton for Quiltt, to prevent mutating other instances
160
- const axios = Axios.create();
161
- // Example: axios.get(url, { retry: true })
162
- axios.interceptors.response.use(undefined, (error)=>{
163
- const { config, message, response } = error;
164
- const messageLower = message.toLowerCase();
165
- if (!config || !config.retry) {
166
- return Promise.reject(error);
167
- }
168
- // Retry Network timeout, Network errors, and Too Many Requests
169
- if (!(messageLower.includes('timeout') || messageLower.includes('network error') || response?.status === 429)) {
170
- return Promise.reject(error);
171
- }
172
- if (config.retriesRemaining === undefined) {
173
- config.retriesRemaining = RETRIES - 1;
174
- } else if (config.retriesRemaining === 1) {
164
+ /**
165
+ * A wrapper around the native `fetch` function that adds automatic retries on failure, including network errors and HTTP 429 responses.
166
+ * Now treats any response with status < 500 as valid.
167
+ */ const fetchWithRetry = async (url, options = {
168
+ retry: false
169
+ })=>{
170
+ const { retry, retriesRemaining, validateStatus = (status)=>status >= 200 && status < 300, ...fetchOptions } = options;
171
+ try {
172
+ const response = await effectiveFetch(url, fetchOptions);
173
+ const isResponseOk = validateStatus(response.status);
174
+ if (isResponseOk) {
175
+ return {
176
+ data: await response.json().catch(()=>null),
177
+ status: response.status,
178
+ statusText: response.statusText,
179
+ headers: response.headers,
180
+ ok: isResponseOk
181
+ };
182
+ }
183
+ // If validateStatus fails, and retry is enabled, prepare to retry for eligible status codes
184
+ if (retry && (response.status >= 500 || response.status === 429)) {
185
+ throw new Error('Retryable failure');
186
+ }
187
+ throw new Error('HTTP error with status ' + response.status);
188
+ } catch (error) {
189
+ if (retry) {
190
+ const currentRetriesRemaining = retriesRemaining !== undefined ? retriesRemaining : RETRIES;
191
+ if (currentRetriesRemaining > 0) {
192
+ const delayTime = RETRY_DELAY * (RETRIES - currentRetriesRemaining);
193
+ await new Promise((resolve)=>setTimeout(resolve, delayTime));
194
+ return fetchWithRetry(url, {
195
+ ...options,
196
+ retriesRemaining: currentRetriesRemaining - 1
197
+ });
198
+ }
199
+ }
175
200
  return Promise.reject(error);
176
- } else {
177
- config.retriesRemaining -= 1;
178
201
  }
179
- const delay = new Promise((resolve)=>{
180
- setTimeout(()=>resolve(), RETRY_DELAY * (RETRIES - config.retriesRemaining));
181
- });
182
- return delay.then(()=>axios(config));
183
- });
202
+ };
184
203
 
185
204
  var AuthStrategies;
186
205
  (function(AuthStrategies) {
@@ -194,47 +213,64 @@ class AuthAPI {
194
213
  * Response Statuses:
195
214
  * - 200: OK -> Session is Valid
196
215
  * - 401: Unauthorized -> Session is Invalid
197
- */ this.ping = (token)=>{
198
- return axios.get(endpointAuth, this.config(token));
216
+ */ this.ping = async (token)=>{
217
+ const response = await fetchWithRetry(endpointAuth, {
218
+ method: 'GET',
219
+ ...this.config(token)
220
+ });
221
+ return response;
199
222
  };
200
223
  /**
201
224
  * Response Statuses:
202
225
  * - 201: Created -> Profile Created, New Session Returned
203
226
  * - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
204
227
  * - 422: Unprocessable Entity -> Invalid Payload
205
- */ this.identify = (payload)=>{
206
- return axios.post(endpointAuth, this.body(payload), this.config());
228
+ */ this.identify = async (payload)=>{
229
+ const response = await fetchWithRetry(endpointAuth, {
230
+ method: 'POST',
231
+ body: JSON.stringify(this.body(payload)),
232
+ ...this.config()
233
+ });
234
+ return response;
207
235
  };
208
236
  /**
209
237
  * Response Statuses:
210
238
  * - 201: Created -> MFA Validated, New Session Returned
211
239
  * - 401: Unauthorized -> MFA Invalid
212
240
  * - 422: Unprocessable Entity -> Invalid Payload
213
- */ this.authenticate = (payload)=>{
214
- return axios.put(endpointAuth, this.body(payload), this.config());
241
+ */ this.authenticate = async (payload)=>{
242
+ const response = await fetchWithRetry(endpointAuth, {
243
+ method: 'PUT',
244
+ body: JSON.stringify(this.body(payload)),
245
+ ...this.config()
246
+ });
247
+ return response;
215
248
  };
216
249
  /**
217
250
  * Response Statuses:
218
251
  * - 204: No Content -> Session Revoked
219
252
  * - 401: Unauthorized -> Session Not Found
220
- */ this.revoke = (token)=>{
221
- return axios.delete(endpointAuth, this.config(token));
253
+ */ this.revoke = async (token)=>{
254
+ const response = await fetchWithRetry(endpointAuth, {
255
+ method: 'DELETE',
256
+ ...this.config(token)
257
+ });
258
+ return response;
222
259
  };
223
260
  this.config = (token)=>{
224
- const headers = {
225
- 'Content-Type': 'application/json',
226
- Accept: 'application/json'
227
- };
261
+ const headers = new Headers();
262
+ headers.set('Content-Type', 'application/json');
263
+ headers.set('Accept', 'application/json');
228
264
  if (token) {
229
- headers.Authorization = `Bearer ${token}`;
265
+ headers.set('Authorization', `Bearer ${token}`);
230
266
  }
231
267
  return {
232
- headers: headers,
268
+ headers,
233
269
  validateStatus: this.validateStatus,
234
270
  retry: true
235
271
  };
236
272
  };
237
- this.validateStatus = (status)=>status < 500;
273
+ this.validateStatus = (status)=>status < 500 && status !== 429;
238
274
  this.body = (payload)=>{
239
275
  if (!this.clientId) {
240
276
  console.error('Quiltt Client ID is not set. Unable to identify & authenticate');
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@quiltt/core",
3
- "version": "3.5.5",
3
+ "version": "3.5.6",
4
4
  "description": "Javascript API client and utilities for Quiltt",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/quiltt/quiltt-public.git",
8
- "directory": "packages/core"
9
- },
10
- "homepage": "https://github.com/quiltt/quiltt-public/tree/main/packages/core#readme",
11
5
  "keywords": [
12
6
  "quiltt",
13
7
  "typescript"
14
8
  ],
9
+ "homepage": "https://github.com/quiltt/quiltt-js/tree/main/packages/core#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/quiltt/quiltt-js.git",
13
+ "directory": "packages/core"
14
+ },
15
15
  "license": "MIT",
16
16
  "sideEffects": [
17
17
  "./src/Storage/Local.ts",
@@ -33,16 +33,15 @@
33
33
  "CHANGELOG.md"
34
34
  ],
35
35
  "dependencies": {
36
- "@apollo/client": "^3.7.16",
37
- "axios": "^1.6.0",
38
- "cross-fetch": "^3.1.8",
36
+ "@apollo/client": "^3.9.9",
37
+ "cross-fetch": "^4.0.0",
39
38
  "graphql": "^16.8.1",
40
- "graphql-ruby-client": "^1.11.8"
39
+ "graphql-ruby-client": "^1.13.3"
41
40
  },
42
41
  "devDependencies": {
43
42
  "@trivago/prettier-plugin-sort-imports": "4.1.1",
44
- "@types/node": "20.11.25",
45
- "@types/react": "18.2.64",
43
+ "@types/node": "20.12.2",
44
+ "@types/react": "18.2.73",
46
45
  "@typescript-eslint/eslint-plugin": "5.60.1",
47
46
  "@typescript-eslint/parser": "5.60.1",
48
47
  "bunchee": "4.4.8",
@@ -51,7 +50,7 @@
51
50
  "eslint-plugin-prettier": "4.2.1",
52
51
  "prettier": "2.8.8",
53
52
  "rimraf": "5.0.5",
54
- "typescript": "5.4.2"
53
+ "typescript": "5.4.3"
55
54
  },
56
55
  "publishConfig": {
57
56
  "access": "public"
@@ -1,12 +1,14 @@
1
1
  import { BatchHttpLink as ApolloHttpLink } from '@apollo/client/link/batch-http/index.js'
2
+ import crossfetch from 'cross-fetch'
2
3
 
3
- import fetch from 'cross-fetch'
4
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
5
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
4
6
 
5
7
  import { endpointGraphQL } from '../../../configuration'
6
8
 
7
9
  export const BatchHttpLink = new ApolloHttpLink({
8
10
  uri: endpointGraphQL,
9
- fetch,
11
+ fetch: effectiveFetch,
10
12
  })
11
13
 
12
14
  export default BatchHttpLink
@@ -1,12 +1,14 @@
1
1
  import { HttpLink as ApolloHttpLink } from '@apollo/client/link/http/index.js'
2
+ import crossfetch from 'cross-fetch'
2
3
 
3
- import fetch from 'cross-fetch'
4
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
5
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
4
6
 
5
7
  import { endpointGraphQL } from '../../../configuration'
6
8
 
7
9
  export const HttpLink = new ApolloHttpLink({
8
10
  uri: endpointGraphQL,
9
- fetch,
11
+ fetch: effectiveFetch,
10
12
  })
11
13
 
12
14
  export default HttpLink
@@ -1,7 +1,6 @@
1
- import type { AxiosRequestConfig, AxiosResponse } from './axios'
2
- import { axios } from './axios'
3
-
4
- import { endpointAuth } from '../../configuration'
1
+ import { endpointAuth } from '@/configuration'
2
+ import type { FetchResponse } from './fetchWithRetry'
3
+ import { fetchWithRetry } from './fetchWithRetry'
5
4
 
6
5
  export enum AuthStrategies {
7
6
  Email = 'email',
@@ -22,7 +21,7 @@ export type UsernamePayload = EmailInput | PhoneInput
22
21
  export type PasscodePayload = UsernamePayload & { passcode: string }
23
22
 
24
23
  type SessionData = { token: string }
25
- type NoContentData = void
24
+ type NoContentData = null
26
25
  type UnauthorizedData = { message: string; instruction: string }
27
26
  export type UnprocessableData = { [attribute: string]: Array<string> }
28
27
 
@@ -31,8 +30,8 @@ type Identify = SessionData | NoContentData | UnprocessableData
31
30
  type Authenticate = SessionData | UnauthorizedData | UnprocessableData
32
31
  type Revoke = NoContentData | UnauthorizedData
33
32
 
34
- export type SessionResponse = AxiosResponse<SessionData>
35
- export type UnprocessableResponse = AxiosResponse<UnprocessableData>
33
+ export type SessionResponse = FetchResponse<SessionData>
34
+ export type UnprocessableResponse = FetchResponse<UnprocessableData>
36
35
 
37
36
  // https://www.quiltt.dev/api-reference/rest/auth#
38
37
  export class AuthAPI {
@@ -47,8 +46,12 @@ export class AuthAPI {
47
46
  * - 200: OK -> Session is Valid
48
47
  * - 401: Unauthorized -> Session is Invalid
49
48
  */
50
- ping = (token: string) => {
51
- return axios.get<Ping>(endpointAuth, this.config(token))
49
+ ping = async (token: string) => {
50
+ const response = await fetchWithRetry<Ping>(endpointAuth, {
51
+ method: 'GET',
52
+ ...this.config(token),
53
+ })
54
+ return response
52
55
  }
53
56
 
54
57
  /**
@@ -57,8 +60,13 @@ export class AuthAPI {
57
60
  * - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
58
61
  * - 422: Unprocessable Entity -> Invalid Payload
59
62
  */
60
- identify = (payload: UsernamePayload) => {
61
- return axios.post<Identify>(endpointAuth, this.body(payload), this.config())
63
+ identify = async (payload: UsernamePayload) => {
64
+ const response = await fetchWithRetry<Identify>(endpointAuth, {
65
+ method: 'POST',
66
+ body: JSON.stringify(this.body(payload)),
67
+ ...this.config(),
68
+ })
69
+ return response
62
70
  }
63
71
 
64
72
  /**
@@ -67,8 +75,13 @@ export class AuthAPI {
67
75
  * - 401: Unauthorized -> MFA Invalid
68
76
  * - 422: Unprocessable Entity -> Invalid Payload
69
77
  */
70
- authenticate = (payload: PasscodePayload) => {
71
- return axios.put<Authenticate>(endpointAuth, this.body(payload), this.config())
78
+ authenticate = async (payload: PasscodePayload): Promise<FetchResponse<Authenticate>> => {
79
+ const response = await fetchWithRetry<Authenticate>(endpointAuth, {
80
+ method: 'PUT',
81
+ body: JSON.stringify(this.body(payload)),
82
+ ...this.config(),
83
+ })
84
+ return response
72
85
  }
73
86
 
74
87
  /**
@@ -76,28 +89,30 @@ export class AuthAPI {
76
89
  * - 204: No Content -> Session Revoked
77
90
  * - 401: Unauthorized -> Session Not Found
78
91
  */
79
- revoke = (token: string) => {
80
- return axios.delete<Revoke>(endpointAuth, this.config(token))
92
+ revoke = async (token: string): Promise<FetchResponse<Revoke>> => {
93
+ const response = await fetchWithRetry<Revoke>(endpointAuth, {
94
+ method: 'DELETE',
95
+ ...this.config(token),
96
+ })
97
+ return response
81
98
  }
82
99
 
83
- private config = (token?: string): AxiosRequestConfig => {
84
- const headers: { [id: string]: string } = {
85
- 'Content-Type': 'application/json',
86
- Accept: 'application/json',
87
- }
100
+ private config = (token?: string) => {
101
+ const headers = new Headers()
102
+ headers.set('Content-Type', 'application/json')
103
+ headers.set('Accept', 'application/json')
88
104
 
89
105
  if (token) {
90
- headers.Authorization = `Bearer ${token}`
106
+ headers.set('Authorization', `Bearer ${token}`)
91
107
  }
92
108
 
93
109
  return {
94
- headers: headers,
110
+ headers,
95
111
  validateStatus: this.validateStatus,
96
112
  retry: true,
97
113
  }
98
114
  }
99
-
100
- private validateStatus = (status: number) => status < 500
115
+ private validateStatus = (status: number) => status < 500 && status !== 429
101
116
 
102
117
  private body = (payload: any) => {
103
118
  if (!this.clientId) {
@@ -112,5 +127,3 @@ export class AuthAPI {
112
127
  }
113
128
  }
114
129
  }
115
-
116
- export default AuthAPI
@@ -0,0 +1,73 @@
1
+ import crossfetch from 'cross-fetch'
2
+
3
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
4
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
5
+
6
+ type FetchWithRetryOptions = RequestInit & {
7
+ retry?: boolean
8
+ retriesRemaining?: number
9
+ validateStatus?: (status: number) => boolean
10
+ }
11
+
12
+ export type FetchResponse<T> = {
13
+ data: T
14
+ status: number
15
+ statusText: string
16
+ headers: Headers
17
+ ok: boolean
18
+ }
19
+
20
+ const RETRY_DELAY = 150 // ms
21
+ const RETRIES = 10 // 150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500 = 8.250s
22
+
23
+ /**
24
+ * A wrapper around the native `fetch` function that adds automatic retries on failure, including network errors and HTTP 429 responses.
25
+ * Now treats any response with status < 500 as valid.
26
+ */
27
+ export const fetchWithRetry = async <T>(
28
+ url: RequestInfo,
29
+ options: FetchWithRetryOptions = { retry: false }
30
+ ): Promise<FetchResponse<T>> => {
31
+ const {
32
+ retry,
33
+ retriesRemaining,
34
+ validateStatus = (status) => status >= 200 && status < 300, // Default to success for 2xx responses
35
+ ...fetchOptions
36
+ } = options
37
+
38
+ try {
39
+ const response = await effectiveFetch(url, fetchOptions)
40
+
41
+ const isResponseOk = validateStatus(response.status)
42
+
43
+ if (isResponseOk) {
44
+ return {
45
+ data: await response.json().catch(() => null),
46
+ status: response.status,
47
+ statusText: response.statusText,
48
+ headers: response.headers,
49
+ ok: isResponseOk,
50
+ }
51
+ }
52
+
53
+ // If validateStatus fails, and retry is enabled, prepare to retry for eligible status codes
54
+ if (retry && (response.status >= 500 || response.status === 429)) {
55
+ throw new Error('Retryable failure')
56
+ }
57
+
58
+ throw new Error('HTTP error with status ' + response.status)
59
+ } catch (error) {
60
+ if (retry) {
61
+ const currentRetriesRemaining = retriesRemaining !== undefined ? retriesRemaining : RETRIES
62
+ if (currentRetriesRemaining > 0) {
63
+ const delayTime = RETRY_DELAY * (RETRIES - currentRetriesRemaining)
64
+ await new Promise((resolve) => setTimeout(resolve, delayTime))
65
+ return fetchWithRetry(url, {
66
+ ...options,
67
+ retriesRemaining: currentRetriesRemaining - 1,
68
+ })
69
+ }
70
+ }
71
+ return Promise.reject(error)
72
+ }
73
+ }
@@ -1 +1 @@
1
- export * from './AuthAPI'
1
+ export * from './auth'
@@ -1,52 +0,0 @@
1
- import type { AxiosRequestConfig as RequestConfig } from 'axios'
2
- import Axios from 'axios'
3
-
4
- export type { AxiosResponse } from 'axios'
5
- export type AxiosRequestConfig<D = any> = RequestConfig<D> & {
6
- retry: boolean
7
- }
8
-
9
- const RETRY_DELAY = 150 // ms
10
- const RETRIES = 10 // 150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500 = 8.250s
11
-
12
- // Create an axios singleton for Quiltt, to prevent mutating other instances
13
- const axios = Axios.create()
14
-
15
- // Example: axios.get(url, { retry: true })
16
- axios.interceptors.response.use(undefined, (error) => {
17
- const { config, message, response } = error
18
- const messageLower = message.toLowerCase()
19
-
20
- if (!config || !config.retry) {
21
- return Promise.reject(error)
22
- }
23
-
24
- // Retry Network timeout, Network errors, and Too Many Requests
25
- if (
26
- !(
27
- messageLower.includes('timeout') ||
28
- messageLower.includes('network error') ||
29
- response?.status === 429
30
- )
31
- ) {
32
- return Promise.reject(error)
33
- }
34
-
35
- if (config.retriesRemaining === undefined) {
36
- config.retriesRemaining = RETRIES - 1
37
- } else if (config.retriesRemaining === 1) {
38
- return Promise.reject(error)
39
- } else {
40
- config.retriesRemaining -= 1
41
- }
42
-
43
- const delay = new Promise<void>((resolve) => {
44
- setTimeout(() => resolve(), RETRY_DELAY * (RETRIES - config.retriesRemaining))
45
- })
46
-
47
- return delay.then(() => axios(config))
48
- })
49
-
50
- export { axios }
51
-
52
- export default axios