@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 +16 -10
- package/README.md +1 -1
- package/dist/{SubscriptionLink-client-B5Tmyqw7.js → SubscriptionLink-client-VkkcO2Yz.js} +2 -2
- package/dist/{index-client-BY-d8Msy.js → index-client-D55-rzVl.js} +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.js +80 -44
- package/package.json +13 -14
- package/src/api/graphql/links/BatchHttpLink.ts +4 -2
- package/src/api/graphql/links/HttpLink.ts +4 -2
- package/src/api/rest/{AuthAPI.ts → auth.ts} +39 -26
- package/src/api/rest/fetchWithRetry.ts +73 -0
- package/src/api/rest/index.ts +1 -1
- package/src/api/rest/axios.ts +0 -52
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
[](https://badge.fury.io/js/@quiltt%2Fcore)
|
|
4
|
-
[](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-
|
|
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.
|
|
7
|
+
var version$1 = "3.5.6";
|
|
8
8
|
|
|
9
9
|
const QUILTT_API_INSECURE = (()=>{
|
|
10
10
|
try {
|
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 =
|
|
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 =
|
|
405
|
-
type UnprocessableResponse =
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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-
|
|
2
|
-
export { L as LocalStorage, M as MemoryStorage, O as Observable, f as Storage, c as cdnBase, b as endpointWebsockets } from './SubscriptionLink-client-
|
|
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
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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
|
|
265
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
230
266
|
}
|
|
231
267
|
return {
|
|
232
|
-
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.
|
|
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.
|
|
37
|
-
"
|
|
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.
|
|
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.
|
|
45
|
-
"@types/react": "18.2.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2
|
-
import {
|
|
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 =
|
|
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 =
|
|
35
|
-
export type UnprocessableResponse =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
84
|
-
const headers
|
|
85
|
-
|
|
86
|
-
|
|
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
|
|
106
|
+
headers.set('Authorization', `Bearer ${token}`)
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
return {
|
|
94
|
-
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
|
+
}
|
package/src/api/rest/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './auth'
|
package/src/api/rest/axios.ts
DELETED
|
@@ -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
|