@quiltt/core 4.5.1 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +21 -6
  3. package/dist/api/browser.cjs +14 -0
  4. package/dist/api/browser.d.ts +128 -0
  5. package/dist/api/browser.js +12 -0
  6. package/dist/api/graphql/SubscriptionLink-12s-ufJBKwu1.js +149 -0
  7. package/dist/api/graphql/SubscriptionLink-12s-wjkChfxO.cjs +150 -0
  8. package/dist/api/graphql/index.cjs +218 -0
  9. package/dist/api/graphql/index.d.ts +82 -0
  10. package/dist/api/graphql/index.js +184 -0
  11. package/dist/api/index.cjs +26 -0
  12. package/dist/api/index.d.ts +3 -0
  13. package/dist/api/index.js +3 -0
  14. package/dist/api/rest/index.cjs +225 -0
  15. package/dist/api/rest/index.d.ts +128 -0
  16. package/dist/api/rest/index.js +217 -0
  17. package/dist/auth/index.cjs +21 -0
  18. package/dist/auth/index.d.ts +29 -0
  19. package/dist/auth/index.js +19 -0
  20. package/dist/config/index.cjs +44 -0
  21. package/dist/config/index.d.ts +9 -0
  22. package/dist/config/index.js +36 -0
  23. package/dist/index.cjs +61 -0
  24. package/dist/index.d.ts +8 -500
  25. package/dist/index.js +8 -371
  26. package/dist/observables/index.cjs +30 -0
  27. package/dist/observables/index.d.ts +21 -0
  28. package/dist/observables/index.js +28 -0
  29. package/dist/storage/index.cjs +272 -0
  30. package/dist/storage/index.d.ts +91 -0
  31. package/dist/{SubscriptionLink-12s-B8qxyDXC.js → storage/index.js} +2 -129
  32. package/dist/timing/index.cjs +30 -0
  33. package/dist/timing/index.d.ts +15 -0
  34. package/dist/timing/index.js +28 -0
  35. package/dist/types.cjs +1 -0
  36. package/dist/types.d.ts +28 -0
  37. package/dist/types.js +1 -0
  38. package/dist/utils/index.cjs +61 -0
  39. package/dist/utils/index.d.ts +18 -0
  40. package/dist/utils/index.js +57 -0
  41. package/package.json +69 -13
  42. package/src/api/graphql/client.ts +16 -16
  43. package/src/api/graphql/links/ActionCableLink.ts +34 -17
  44. package/src/api/graphql/links/AuthLink.ts +21 -13
  45. package/src/api/graphql/links/BatchHttpLink.ts +2 -2
  46. package/src/api/graphql/links/ErrorLink.ts +20 -9
  47. package/src/api/graphql/links/ForwardableLink.ts +1 -1
  48. package/src/api/graphql/links/HttpLink.ts +2 -2
  49. package/src/api/graphql/links/RetryLink.ts +6 -2
  50. package/src/api/graphql/links/TerminatingLink.ts +7 -2
  51. package/src/api/graphql/links/VersionLink.ts +17 -12
  52. package/src/api/rest/auth.ts +1 -1
  53. package/src/api/rest/connectors.ts +9 -5
  54. package/src/auth/index.ts +1 -0
  55. package/src/{JsonWebToken.ts → auth/json-web-token.ts} +1 -1
  56. package/src/{configuration.ts → config/configuration.ts} +1 -1
  57. package/src/config/index.ts +1 -0
  58. package/src/index.ts +5 -4
  59. package/src/observables/index.ts +1 -0
  60. package/src/{Observable.ts → observables/observable.ts} +1 -1
  61. package/src/storage/Local.ts +1 -1
  62. package/src/storage/Memory.ts +2 -2
  63. package/src/storage/Storage.ts +1 -1
  64. package/src/timing/index.ts +1 -0
  65. package/src/{Timeoutable.ts → timing/timeoutable.ts} +1 -1
  66. package/src/utils/index.ts +1 -0
  67. package/src/utils/telemetry.ts +71 -0
  68. package/src/utils/token-validation.ts +67 -0
package/dist/index.js CHANGED
@@ -1,371 +1,8 @@
1
- import { ApolloLink, ApolloClient } from '@apollo/client/core/index.js';
2
- export { gql } from '@apollo/client/core/index.js';
3
- import { G as GlobalStorage, e as endpointGraphQL, v as version, d as debugging, S as SubscriptionLink, a as endpointAuth, b as endpointRest } from './SubscriptionLink-12s-B8qxyDXC.js';
4
- export { L as LocalStorage, M as MemoryStorage, O as Observable, g as Storage, c as cdnBase, f as endpointWebsockets } from './SubscriptionLink-12s-B8qxyDXC.js';
5
- import { BatchHttpLink as BatchHttpLink$1 } from '@apollo/client/link/batch-http/index.js';
6
- import crossfetch from 'cross-fetch';
7
- import { onError } from '@apollo/client/link/error/index.js';
8
- import { HttpLink as HttpLink$1 } from '@apollo/client/link/http/index.js';
9
- import { RetryLink as RetryLink$1 } from '@apollo/client/link/retry/index.js';
10
- export { InMemoryCache } from '@apollo/client/cache/index.js';
11
- export { useMutation, useQuery, useSubscription } from '@apollo/client/react/hooks/index.js';
12
-
13
- /**
14
- * Enum representing the different types of events emitted by the Connector.
15
- */ var ConnectorSDKEventType = /*#__PURE__*/ function(ConnectorSDKEventType) {
16
- /** The Connector modal has been opened */ ConnectorSDKEventType["Open"] = "opened";
17
- /** The Connector has loaded successfully */ ConnectorSDKEventType["Load"] = "loaded";
18
- /** The end-user successfully completed the flow */ ConnectorSDKEventType["ExitSuccess"] = "exited.successful";
19
- /** The end-user exited the Connector before completing the flow */ ConnectorSDKEventType["ExitAbort"] = "exited.aborted";
20
- /** The end-user experienced an error during the flow */ ConnectorSDKEventType["ExitError"] = "exited.errored";
21
- return ConnectorSDKEventType;
22
- }({});
23
-
24
- /**
25
- * unauthorizedCallback only triggers in the event the token is present, and
26
- * returns the token; This allows sessions to be forgotten without race conditions
27
- * causing null sessions to kill valid sessions, or invalid sessions for killing
28
- * valid sessions during rotation and networking weirdness.
29
- */ class AuthLink extends ApolloLink {
30
- request(operation, forward) {
31
- const token = GlobalStorage.get('session');
32
- if (!token) {
33
- console.warn('QuilttLink attempted to send an unauthenticated Query');
34
- return null;
35
- }
36
- operation.setContext(({ headers = {} })=>({
37
- headers: {
38
- ...headers,
39
- authorization: `Bearer ${token}`
40
- }
41
- }));
42
- return forward(operation);
43
- }
44
- }
45
-
46
- // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
47
- const effectiveFetch$2 = typeof fetch === 'undefined' ? crossfetch : fetch;
48
- const BatchHttpLink = new BatchHttpLink$1({
49
- uri: endpointGraphQL,
50
- fetch: effectiveFetch$2
51
- });
52
-
53
- const ErrorLink = onError(({ graphQLErrors, networkError })=>{
54
- if (graphQLErrors) {
55
- graphQLErrors.forEach(({ message, path, extensions })=>{
56
- const formattedPath = Array.isArray(path) ? path.join('.') : path ?? 'N/A';
57
- const parts = [
58
- `[Quiltt][GraphQL Error]: ${message}`,
59
- `Path: ${formattedPath}`
60
- ];
61
- if (extensions) {
62
- if (extensions.code) parts.push(`Code: ${extensions.code}`);
63
- if (extensions.errorId) parts.push(`Error ID: ${extensions.errorId}`);
64
- }
65
- console.warn(parts.join(' | '));
66
- });
67
- }
68
- if (networkError) {
69
- if (networkError.statusCode === 401) {
70
- console.warn('[Quiltt][Authentication Error]:', networkError);
71
- GlobalStorage.set('session', null);
72
- } else {
73
- console.warn('[Quiltt][Network Error]:', networkError);
74
- }
75
- }
76
- });
77
-
78
- const ForwardableLink = new ApolloLink((operation, forward)=>forward(operation));
79
-
80
- // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
81
- const effectiveFetch$1 = typeof fetch === 'undefined' ? crossfetch : fetch;
82
- const HttpLink = new HttpLink$1({
83
- uri: endpointGraphQL,
84
- fetch: effectiveFetch$1
85
- });
86
-
87
- const RetryLink = new RetryLink$1({
88
- attempts: {
89
- retryIf: (error, _operation)=>!!error && (!error.statusCode || error.statusCode >= 500)
90
- }
91
- });
92
-
93
- const TerminatingLink = new ApolloLink(()=>null);
94
-
95
- const VersionLink = new ApolloLink((operation, forward)=>{
96
- operation.setContext(({ headers = {} })=>({
97
- headers: {
98
- ...headers,
99
- 'Quiltt-Client-Version': version
100
- }
101
- }));
102
- return forward(operation);
103
- });
104
-
105
- class QuilttClient extends ApolloClient {
106
- constructor(options){
107
- const finalOptions = {
108
- ...options,
109
- devtools: {
110
- enabled: options.devtools?.enabled ?? debugging
111
- }
112
- };
113
- const initialLinks = options.customLinks ? [
114
- ...options.customLinks
115
- ] : [];
116
- const isOperationDefinition = (def)=>def.kind === 'OperationDefinition';
117
- const isSubscriptionOperation = (operation)=>{
118
- return operation.query.definitions.some((definition)=>isOperationDefinition(definition) && definition.operation === 'subscription');
119
- };
120
- const isBatchable = (operation)=>{
121
- return operation.getContext().batchable ?? true;
122
- };
123
- const authLink = new AuthLink();
124
- const subscriptionsLink = new SubscriptionLink();
125
- const quilttLink = ApolloLink.from([
126
- ...initialLinks,
127
- VersionLink,
128
- authLink,
129
- ErrorLink,
130
- RetryLink
131
- ]).split(isSubscriptionOperation, subscriptionsLink, ForwardableLink).split(isBatchable, BatchHttpLink, HttpLink);
132
- super({
133
- link: quilttLink,
134
- ...finalOptions
135
- });
136
- }
137
- }
138
-
139
- // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
140
- const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch;
141
- const RETRY_DELAY = 150 // ms
142
- ;
143
- const RETRIES = 10 // 150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500 = 8.250s
144
- ;
145
- /**
146
- * A wrapper around the native `fetch` function that adds automatic retries on failure, including network errors and HTTP 429 responses.
147
- * Now treats any response with status < 500 as valid.
148
- */ const fetchWithRetry = async (url, options = {
149
- retry: false
150
- })=>{
151
- const { retry, retriesRemaining, validateStatus = (status)=>status >= 200 && status < 300, ...fetchOptions } = options;
152
- try {
153
- const response = await effectiveFetch(url, fetchOptions);
154
- const isResponseOk = validateStatus(response.status);
155
- if (isResponseOk) {
156
- return {
157
- data: await response.json().catch(()=>null),
158
- status: response.status,
159
- statusText: response.statusText,
160
- headers: response.headers,
161
- ok: isResponseOk
162
- };
163
- }
164
- // If validateStatus fails, and retry is enabled, prepare to retry for eligible status codes
165
- if (retry && (response.status >= 500 || response.status === 429)) {
166
- throw new Error('Retryable failure');
167
- }
168
- throw new Error(`HTTP error with status ${response.status}`);
169
- } catch (error) {
170
- if (retry) {
171
- const currentRetriesRemaining = retriesRemaining !== undefined ? retriesRemaining : RETRIES;
172
- if (currentRetriesRemaining > 0) {
173
- const delayTime = RETRY_DELAY * (RETRIES - currentRetriesRemaining);
174
- await new Promise((resolve)=>setTimeout(resolve, delayTime));
175
- return fetchWithRetry(url, {
176
- ...options,
177
- retriesRemaining: currentRetriesRemaining - 1
178
- });
179
- }
180
- }
181
- return Promise.reject(error);
182
- }
183
- };
184
-
185
- var AuthStrategies = /*#__PURE__*/ function(AuthStrategies) {
186
- AuthStrategies["Email"] = "email";
187
- AuthStrategies["Phone"] = "phone";
188
- return AuthStrategies;
189
- }({});
190
- // https://www.quiltt.dev/api-reference/auth
191
- class AuthAPI {
192
- constructor(clientId){
193
- /**
194
- * Response Statuses:
195
- * - 200: OK -> Session is Valid
196
- * - 401: Unauthorized -> Session is Invalid
197
- */ this.ping = async (token)=>{
198
- const response = await fetchWithRetry(endpointAuth, {
199
- method: 'GET',
200
- ...this.config(token)
201
- });
202
- return response;
203
- };
204
- /**
205
- * Response Statuses:
206
- * - 201: Created -> Profile Created, New Session Returned
207
- * - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
208
- * - 422: Unprocessable Entity -> Invalid Payload
209
- */ this.identify = async (payload)=>{
210
- const response = await fetchWithRetry(endpointAuth, {
211
- method: 'POST',
212
- body: JSON.stringify(this.body(payload)),
213
- ...this.config()
214
- });
215
- return response;
216
- };
217
- /**
218
- * Response Statuses:
219
- * - 201: Created -> MFA Validated, New Session Returned
220
- * - 401: Unauthorized -> MFA Invalid
221
- * - 422: Unprocessable Entity -> Invalid Payload
222
- */ this.authenticate = async (payload)=>{
223
- const response = await fetchWithRetry(endpointAuth, {
224
- method: 'PUT',
225
- body: JSON.stringify(this.body(payload)),
226
- ...this.config()
227
- });
228
- return response;
229
- };
230
- /**
231
- * Response Statuses:
232
- * - 204: No Content -> Session Revoked
233
- * - 401: Unauthorized -> Session Not Found
234
- */ this.revoke = async (token)=>{
235
- const response = await fetchWithRetry(endpointAuth, {
236
- method: 'DELETE',
237
- ...this.config(token)
238
- });
239
- return response;
240
- };
241
- this.config = (token)=>{
242
- const headers = new Headers();
243
- headers.set('Content-Type', 'application/json');
244
- headers.set('Accept', 'application/json');
245
- if (token) {
246
- headers.set('Authorization', `Bearer ${token}`);
247
- }
248
- return {
249
- headers,
250
- validateStatus: this.validateStatus,
251
- retry: true
252
- };
253
- };
254
- this.validateStatus = (status)=>status < 500 && status !== 429;
255
- this.body = (payload)=>{
256
- if (!this.clientId) {
257
- console.error('Quiltt Client ID is not set. Unable to identify & authenticate');
258
- }
259
- return {
260
- session: {
261
- clientId: this.clientId,
262
- ...payload
263
- }
264
- };
265
- };
266
- this.clientId = clientId;
267
- }
268
- }
269
-
270
- class ConnectorsAPI {
271
- constructor(clientId, agent = 'web'){
272
- /**
273
- * Response Statuses:
274
- * - 200: OK -> Institutions Found
275
- * - 401: Unauthorized -> Invalid Token
276
- * - 403: Forbidden -> Unsupported SDK
277
- * - 400: Bad Request -> Invalid Request
278
- */ this.searchInstitutions = async (token, connectorId, term, signal)=>{
279
- const params = new URLSearchParams();
280
- params.append('term', term);
281
- const response = await fetchWithRetry(`${endpointRest}/sdk/connectors/${connectorId}/institutions?${params}`, {
282
- method: 'GET',
283
- signal,
284
- ...this.config(token)
285
- });
286
- return response;
287
- };
288
- /**
289
- * Response Statuses:
290
- * - 200: OK -> Provider API ID is resolvable or not
291
- * - 401: Unauthorized -> Invalid Token
292
- * - 403: Forbidden -> Unsupported SDK
293
- * - 400: Bad Request -> Missing provider API ID parameter
294
- * - 404: Not Found -> Connector not found
295
- */ this.checkResolvable = async (token, connectorId, providerId, signal)=>{
296
- const params = new URLSearchParams();
297
- const providerKey = Object.keys(providerId)[0];
298
- if (providerKey && providerId[providerKey]) {
299
- params.append(providerKey, providerId[providerKey]);
300
- }
301
- const response = await fetchWithRetry(`${endpointRest}/sdk/connectors/${connectorId}/resolvable?${params}`, {
302
- method: 'GET',
303
- signal,
304
- ...this.config(token)
305
- });
306
- return response;
307
- };
308
- this.config = (token)=>{
309
- const headers = new Headers();
310
- headers.set('Content-Type', 'application/json');
311
- headers.set('Accept', 'application/json');
312
- headers.set('Quiltt-SDK-Agent', this.agent);
313
- headers.set('Authorization', `Bearer ${token}`);
314
- return {
315
- headers,
316
- validateStatus: this.validateStatus,
317
- retry: true
318
- };
319
- };
320
- this.validateStatus = (status)=>status < 500 && status !== 429;
321
- this.clientId = clientId;
322
- this.agent = agent;
323
- }
324
- }
325
-
326
- const MATCHER = /^(?:[\w-]+\.){2}[\w-]+$/;
327
- const JsonWebTokenParse = (token)=>{
328
- if (typeof token === 'undefined' || token === null) return token;
329
- if (!MATCHER.test(token)) {
330
- console.error(`Invalid Session Token: ${token}`);
331
- return;
332
- }
333
- const [_header, payload, _signature] = token.split('.');
334
- try {
335
- return {
336
- token,
337
- claims: JSON.parse(atob(payload))
338
- };
339
- } catch (error) {
340
- console.error(`Invalid Session Token: ${error}`);
341
- }
342
- };
343
-
344
- /**
345
- * This is designed to support singletons to timeouts that can broadcast
346
- * to any observers, preventing race conditions with multiple timeouts.
347
- */ class Timeoutable {
348
- constructor(){
349
- this.observers = [];
350
- this.set = (callback, delay)=>{
351
- if (this.timeout) {
352
- clearTimeout(this.timeout);
353
- }
354
- // Replace all observers with the new one
355
- this.observers = [
356
- callback
357
- ];
358
- this.timeout = setTimeout(this.broadcast.bind(this), delay);
359
- };
360
- this.clear = (observer)=>{
361
- this.observers = this.observers.filter((callback)=>callback !== observer);
362
- };
363
- // Only sends to the 1st listener, but ensures that someone is notified
364
- this.broadcast = ()=>{
365
- if (this.observers.length === 0) return;
366
- this.observers[0](undefined);
367
- };
368
- }
369
- }
370
-
371
- export { AuthAPI, AuthLink, AuthStrategies, BatchHttpLink, ConnectorSDKEventType, ConnectorsAPI, ErrorLink, ForwardableLink, GlobalStorage, HttpLink, JsonWebTokenParse, QuilttClient, RetryLink, SubscriptionLink, TerminatingLink, Timeoutable, VersionLink, debugging, endpointAuth, endpointGraphQL, endpointRest, version };
1
+ export * from './api/index.js';
2
+ export * from './auth/index.js';
3
+ export * from './config/index.js';
4
+ export * from './observables/index.js';
5
+ export * from './storage/index.js';
6
+ export * from './timing/index.js';
7
+ export * from './types.js';
8
+ export * from './utils/index.js';
@@ -0,0 +1,30 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ /**
4
+ * This is designed to support singletons to share the memory states across all
5
+ * instance of hooks to ensure that updates only process once, by storing a value
6
+ * then notifying all subscribers when it's updated.
7
+ */ class Observable {
8
+ constructor(initialState){
9
+ this.observers = [];
10
+ this.get = ()=>{
11
+ return this.state;
12
+ };
13
+ this.set = (nextState)=>{
14
+ if (this.state === nextState) return;
15
+ this.state = nextState;
16
+ this.observers.forEach((update)=>{
17
+ update(nextState);
18
+ });
19
+ };
20
+ this.subscribe = (observer)=>{
21
+ this.observers.push(observer);
22
+ };
23
+ this.unsubscribe = (observer)=>{
24
+ this.observers = this.observers.filter((update)=>update !== observer);
25
+ };
26
+ this.state = initialState;
27
+ }
28
+ }
29
+
30
+ exports.Observable = Observable;
@@ -0,0 +1,21 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ import { Maybe } from '../types.js';
3
+
4
+ type Observer<T> = Dispatch<SetStateAction<Maybe<T> | undefined>>;
5
+ /**
6
+ * This is designed to support singletons to share the memory states across all
7
+ * instance of hooks to ensure that updates only process once, by storing a value
8
+ * then notifying all subscribers when it's updated.
9
+ */
10
+ declare class Observable<T> {
11
+ private state?;
12
+ private observers;
13
+ constructor(initialState?: Maybe<T>);
14
+ get: () => Maybe<T> | undefined;
15
+ set: (nextState: Maybe<T> | undefined) => void;
16
+ subscribe: (observer: Observer<T>) => void;
17
+ unsubscribe: (observer: Observer<T>) => void;
18
+ }
19
+
20
+ export { Observable };
21
+ export type { Observer };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * This is designed to support singletons to share the memory states across all
3
+ * instance of hooks to ensure that updates only process once, by storing a value
4
+ * then notifying all subscribers when it's updated.
5
+ */ class Observable {
6
+ constructor(initialState){
7
+ this.observers = [];
8
+ this.get = ()=>{
9
+ return this.state;
10
+ };
11
+ this.set = (nextState)=>{
12
+ if (this.state === nextState) return;
13
+ this.state = nextState;
14
+ this.observers.forEach((update)=>{
15
+ update(nextState);
16
+ });
17
+ };
18
+ this.subscribe = (observer)=>{
19
+ this.observers.push(observer);
20
+ };
21
+ this.unsubscribe = (observer)=>{
22
+ this.observers = this.observers.filter((update)=>update !== observer);
23
+ };
24
+ this.state = initialState;
25
+ }
26
+ }
27
+
28
+ export { Observable };