@quiltt/core 5.0.3 → 5.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @quiltt/core
2
2
 
3
+ ## 5.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#419](https://github.com/quiltt/quiltt-js/pull/419) [`5f5846a`](https://github.com/quiltt/quiltt-js/commit/5f5846a1d807f517c156bbebe60d639256a7db1e) Thanks [@rubendinho](https://github.com/rubendinho)! - Update telemetry headers
8
+
9
+ - [#421](https://github.com/quiltt/quiltt-js/pull/421) [`0756cb5`](https://github.com/quiltt/quiltt-js/commit/0756cb59d3fb55208cc2d1bdc10a2a8d5b66b595) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Ensure custom Headers are passed to all API calls
10
+
11
+ ## 5.1.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [#406](https://github.com/quiltt/quiltt-js/pull/406) [`177a4fc`](https://github.com/quiltt/quiltt-js/commit/177a4fc3d70fa3f313f6586396719dae9763cf4b) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Add OAuth redirect URL support to React SDK
16
+
17
+ ### Patch Changes
18
+
19
+ - [#418](https://github.com/quiltt/quiltt-js/pull/418) [`2d918a0`](https://github.com/quiltt/quiltt-js/commit/2d918a089f8546bb7d20db1eefd958beca296ee0) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Internal: Add `headers` prop to QuilttProvider
20
+
21
+ For Quiltt internal usage. Not intended for public use.
22
+
3
23
  ## 5.0.3
4
24
 
5
25
  ### Patch Changes
@@ -101,6 +101,8 @@ type ConnectorSDKCallbackMetadata = {
101
101
  type ConnectorSDKConnectOptions = ConnectorSDKCallbacks & {
102
102
  /** The Institution ID or search term to connect */
103
103
  institution?: string;
104
+ /** The OAuth redirect URL for mobile or embedded webview flows */
105
+ oauthRedirectUrl?: string;
104
106
  };
105
107
  /**
106
108
  * Options for the Reconnect flow
@@ -109,6 +111,8 @@ type ConnectorSDKConnectOptions = ConnectorSDKCallbacks & {
109
111
  type ConnectorSDKReconnectOptions = ConnectorSDKCallbacks & {
110
112
  /** The ID of the Connection to reconnect */
111
113
  connectionId: string;
114
+ /** The OAuth redirect URL for mobile or embedded webview flows */
115
+ oauthRedirectUrl?: string;
112
116
  };
113
117
  /** Options to initialize Connector
114
118
  *
@@ -122,6 +126,8 @@ type ConnectorSDKConnectorOptions = ConnectorSDKCallbacks & {
122
126
  connectionId?: string;
123
127
  /** The nonce to use for the script tag */
124
128
  nonce?: string;
129
+ /** The OAuth redirect URL for mobile or embedded webview flows */
130
+ oauthRedirectUrl?: string;
125
131
  };
126
132
 
127
133
  export { ConnectorSDKEventType };
@@ -99,6 +99,7 @@ class ActionCableLink extends core.ApolloLink {
99
99
  this.cables[token] = actioncable.createConsumer(index_cjs$1.endpointWebsockets + (token ? `?token=${token}` : ''));
100
100
  }
101
101
  return new rxjs.Observable((observer)=>{
102
+ let cancelled = false;
102
103
  const channelId = Math.round(Date.now() + Math.random() * 100000).toString(16);
103
104
  const actionName = this.actionName;
104
105
  const connectionParams = typeof this.connectionParams === 'function' ? this.connectionParams(operation) : this.connectionParams;
@@ -108,6 +109,7 @@ class ActionCableLink extends core.ApolloLink {
108
109
  channelId: channelId
109
110
  }, connectionParams), {
110
111
  connected: (args)=>{
112
+ if (cancelled) return;
111
113
  channel.perform(actionName, {
112
114
  query: operation.query ? graphql.print(operation.query) : null,
113
115
  variables: operation.variables,
@@ -118,6 +120,7 @@ class ActionCableLink extends core.ApolloLink {
118
120
  callbacks.connected?.(args);
119
121
  },
120
122
  received: (payload)=>{
123
+ if (cancelled) return;
121
124
  if (payload?.result?.data || payload?.result?.errors) {
122
125
  observer.next(payload.result);
123
126
  }
@@ -126,14 +129,18 @@ class ActionCableLink extends core.ApolloLink {
126
129
  }
127
130
  callbacks.received?.(payload);
128
131
  },
132
+ // Intentionally not guarded by `cancelled` - disconnected represents
133
+ // the WebSocket connection state which users may want to know about
134
+ // for cleanup/status purposes, even after the observable completes.
129
135
  disconnected: ()=>{
130
136
  callbacks.disconnected?.();
131
137
  }
132
138
  });
133
- // Make the ActionCable subscription behave like an Apollo subscription
134
- return Object.assign(channel, {
135
- closed: false
136
- });
139
+ // Return teardown logic
140
+ return ()=>{
141
+ cancelled = true;
142
+ channel.unsubscribe();
143
+ };
137
144
  });
138
145
  }
139
146
  }
@@ -99,6 +99,7 @@ class ActionCableLink extends ApolloLink {
99
99
  this.cables[token] = createConsumer(endpointWebsockets + (token ? `?token=${token}` : ''));
100
100
  }
101
101
  return new Observable((observer)=>{
102
+ let cancelled = false;
102
103
  const channelId = Math.round(Date.now() + Math.random() * 100000).toString(16);
103
104
  const actionName = this.actionName;
104
105
  const connectionParams = typeof this.connectionParams === 'function' ? this.connectionParams(operation) : this.connectionParams;
@@ -108,6 +109,7 @@ class ActionCableLink extends ApolloLink {
108
109
  channelId: channelId
109
110
  }, connectionParams), {
110
111
  connected: (args)=>{
112
+ if (cancelled) return;
111
113
  channel.perform(actionName, {
112
114
  query: operation.query ? print(operation.query) : null,
113
115
  variables: operation.variables,
@@ -118,6 +120,7 @@ class ActionCableLink extends ApolloLink {
118
120
  callbacks.connected?.(args);
119
121
  },
120
122
  received: (payload)=>{
123
+ if (cancelled) return;
121
124
  if (payload?.result?.data || payload?.result?.errors) {
122
125
  observer.next(payload.result);
123
126
  }
@@ -126,14 +129,18 @@ class ActionCableLink extends ApolloLink {
126
129
  }
127
130
  callbacks.received?.(payload);
128
131
  },
132
+ // Intentionally not guarded by `cancelled` - disconnected represents
133
+ // the WebSocket connection state which users may want to know about
134
+ // for cleanup/status purposes, even after the observable completes.
129
135
  disconnected: ()=>{
130
136
  callbacks.disconnected?.();
131
137
  }
132
138
  });
133
- // Make the ActionCable subscription behave like an Apollo subscription
134
- return Object.assign(channel, {
135
- closed: false
136
- });
139
+ // Return teardown logic
140
+ return ()=>{
141
+ cancelled = true;
142
+ channel.unsubscribe();
143
+ };
137
144
  });
138
145
  }
139
146
  }
@@ -3,7 +3,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
3
3
  var core = require('@apollo/client/core');
4
4
  var index_cjs = require('../../config/index.cjs');
5
5
  var rxjs = require('rxjs');
6
- var SubscriptionLink12s = require('./SubscriptionLink-12s-wjkChfxO.cjs');
6
+ var SubscriptionLink12s = require('./SubscriptionLink-12s-DUvHs5WL.cjs');
7
7
  var batchHttp = require('@apollo/client/link/batch-http');
8
8
  var crossfetch = require('cross-fetch');
9
9
  var error = require('@apollo/client/link/error');
@@ -88,6 +88,62 @@ const ErrorLink = new error.ErrorLink(({ error, result })=>{
88
88
 
89
89
  const ForwardableLink = new core.ApolloLink((operation, forward)=>forward(operation));
90
90
 
91
+ /**
92
+ * Apollo Link that adds custom headers to GraphQL requests.
93
+ *
94
+ * Headers can be provided statically or dynamically via a function.
95
+ * These headers are added before the AuthLink, so they will be preserved
96
+ * alongside the authorization header.
97
+ */ class HeadersLink extends core.ApolloLink {
98
+ constructor(options = {}){
99
+ super();
100
+ this.headers = options.headers ?? {};
101
+ this.getHeaders = options.getHeaders;
102
+ }
103
+ request(operation, forward) {
104
+ // If we have a dynamic headers function, handle it
105
+ if (this.getHeaders) {
106
+ return new rxjs.Observable((observer)=>{
107
+ let innerSubscription;
108
+ let cancelled = false;
109
+ Promise.resolve(this.getHeaders()).then((dynamicHeaders)=>{
110
+ // Guard against late resolution after unsubscribe
111
+ if (cancelled) return;
112
+ operation.setContext(({ headers = {} })=>({
113
+ headers: {
114
+ ...headers,
115
+ ...this.headers,
116
+ ...dynamicHeaders
117
+ }
118
+ }));
119
+ innerSubscription = forward(operation).subscribe({
120
+ next: (value)=>!cancelled && observer.next(value),
121
+ error: (err)=>!cancelled && observer.error(err),
122
+ complete: ()=>!cancelled && observer.complete()
123
+ });
124
+ }).catch((error)=>{
125
+ if (!cancelled) {
126
+ observer.error(error);
127
+ }
128
+ });
129
+ // Return teardown logic
130
+ return ()=>{
131
+ cancelled = true;
132
+ innerSubscription?.unsubscribe();
133
+ };
134
+ });
135
+ }
136
+ // Static headers only
137
+ operation.setContext(({ headers = {} })=>({
138
+ headers: {
139
+ ...headers,
140
+ ...this.headers
141
+ }
142
+ }));
143
+ return forward(operation);
144
+ }
145
+ }
146
+
91
147
  // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
92
148
  const effectiveFetch = typeof fetch === 'undefined' ? crossfetch__default.default : fetch;
93
149
  const HttpLink = new http.HttpLink({
@@ -131,22 +187,21 @@ const TerminatingLink = new core.ApolloLink(()=>{
131
187
  return `${major}.${minor}.${patch}`;
132
188
  };
133
189
  /**
134
- * Generates a User-Agent string following standard format
190
+ * Generates a custom SDK Agent string following standard format
135
191
  * Format: Quiltt/<version> (<platform-info>)
136
- */ const getUserAgent = (sdkVersion, platformInfo)=>{
192
+ */ const getSDKAgent = (sdkVersion, platformInfo)=>{
137
193
  return `Quiltt/${sdkVersion} (${platformInfo})`;
138
194
  };
139
195
 
140
196
  const createVersionLink = (platformInfo)=>{
141
197
  const versionNumber = extractVersionNumber(index_cjs.version);
142
- const userAgent = getUserAgent(versionNumber, platformInfo);
198
+ const sdkAgent = getSDKAgent(versionNumber, platformInfo);
143
199
  return new core.ApolloLink((operation, forward)=>{
144
200
  operation.setContext(({ headers = {} })=>({
145
201
  headers: {
146
202
  ...headers,
147
203
  'Quiltt-Client-Version': index_cjs.version,
148
- 'Quiltt-SDK-Agent': userAgent,
149
- 'User-Agent': userAgent
204
+ 'Quiltt-SDK-Agent': sdkAgent
150
205
  }
151
206
  }));
152
207
  return forward(operation);
@@ -212,6 +267,7 @@ exports.AuthLink = AuthLink;
212
267
  exports.BatchHttpLink = BatchHttpLink;
213
268
  exports.ErrorLink = ErrorLink;
214
269
  exports.ForwardableLink = ForwardableLink;
270
+ exports.HeadersLink = HeadersLink;
215
271
  exports.HttpLink = HttpLink;
216
272
  exports.QuilttClient = QuilttClient;
217
273
  exports.RetryLink = RetryLink;
@@ -38,6 +38,26 @@ declare const ErrorLink: ErrorLink$1;
38
38
 
39
39
  declare const ForwardableLink: ApolloLink;
40
40
 
41
+ type HeadersLinkOptions = {
42
+ /** Static headers to add to every request */
43
+ headers?: Record<string, string>;
44
+ /** Dynamic headers function called on each request */
45
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
46
+ };
47
+ /**
48
+ * Apollo Link that adds custom headers to GraphQL requests.
49
+ *
50
+ * Headers can be provided statically or dynamically via a function.
51
+ * These headers are added before the AuthLink, so they will be preserved
52
+ * alongside the authorization header.
53
+ */
54
+ declare class HeadersLink extends ApolloLink {
55
+ private headers;
56
+ private getHeaders?;
57
+ constructor(options?: HeadersLinkOptions);
58
+ request(operation: ApolloLink.Operation, forward: ApolloLink.ForwardFunction): Observable<ApolloLink.Result>;
59
+ }
60
+
41
61
  declare const HttpLink: HttpLink$1;
42
62
 
43
63
  declare const RetryLink: RetryLink$1;
@@ -78,5 +98,5 @@ declare const TerminatingLink: ApolloLink;
78
98
 
79
99
  declare const createVersionLink: (platformInfo: string) => ApolloLink;
80
100
 
81
- export { AuthLink, BatchHttpLink, ErrorLink, ForwardableLink, HttpLink, QuilttClient, RetryLink, SubscriptionLink, TerminatingLink, createVersionLink };
82
- export type { QuilttClientOptions };
101
+ export { AuthLink, BatchHttpLink, ErrorLink, ForwardableLink, HeadersLink, HttpLink, QuilttClient, RetryLink, SubscriptionLink, TerminatingLink, createVersionLink };
102
+ export type { HeadersLinkOptions, QuilttClientOptions };
@@ -2,7 +2,7 @@ import { ApolloLink, ApolloClient } from '@apollo/client/core';
2
2
  export { gql } from '@apollo/client/core';
3
3
  import { endpointGraphQL, version, debugging } from '../../config/index.js';
4
4
  import { Observable } from 'rxjs';
5
- import { v as validateSessionToken, S as SubscriptionLink } from './SubscriptionLink-12s-ufJBKwu1.js';
5
+ import { v as validateSessionToken, S as SubscriptionLink } from './SubscriptionLink-12s-UDBUsy8w.js';
6
6
  import { BatchHttpLink as BatchHttpLink$1 } from '@apollo/client/link/batch-http';
7
7
  import crossfetch from 'cross-fetch';
8
8
  import { ErrorLink as ErrorLink$1 } from '@apollo/client/link/error';
@@ -83,6 +83,62 @@ const ErrorLink = new ErrorLink$1(({ error, result })=>{
83
83
 
84
84
  const ForwardableLink = new ApolloLink((operation, forward)=>forward(operation));
85
85
 
86
+ /**
87
+ * Apollo Link that adds custom headers to GraphQL requests.
88
+ *
89
+ * Headers can be provided statically or dynamically via a function.
90
+ * These headers are added before the AuthLink, so they will be preserved
91
+ * alongside the authorization header.
92
+ */ class HeadersLink extends ApolloLink {
93
+ constructor(options = {}){
94
+ super();
95
+ this.headers = options.headers ?? {};
96
+ this.getHeaders = options.getHeaders;
97
+ }
98
+ request(operation, forward) {
99
+ // If we have a dynamic headers function, handle it
100
+ if (this.getHeaders) {
101
+ return new Observable((observer)=>{
102
+ let innerSubscription;
103
+ let cancelled = false;
104
+ Promise.resolve(this.getHeaders()).then((dynamicHeaders)=>{
105
+ // Guard against late resolution after unsubscribe
106
+ if (cancelled) return;
107
+ operation.setContext(({ headers = {} })=>({
108
+ headers: {
109
+ ...headers,
110
+ ...this.headers,
111
+ ...dynamicHeaders
112
+ }
113
+ }));
114
+ innerSubscription = forward(operation).subscribe({
115
+ next: (value)=>!cancelled && observer.next(value),
116
+ error: (err)=>!cancelled && observer.error(err),
117
+ complete: ()=>!cancelled && observer.complete()
118
+ });
119
+ }).catch((error)=>{
120
+ if (!cancelled) {
121
+ observer.error(error);
122
+ }
123
+ });
124
+ // Return teardown logic
125
+ return ()=>{
126
+ cancelled = true;
127
+ innerSubscription?.unsubscribe();
128
+ };
129
+ });
130
+ }
131
+ // Static headers only
132
+ operation.setContext(({ headers = {} })=>({
133
+ headers: {
134
+ ...headers,
135
+ ...this.headers
136
+ }
137
+ }));
138
+ return forward(operation);
139
+ }
140
+ }
141
+
86
142
  // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
87
143
  const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch;
88
144
  const HttpLink = new HttpLink$1({
@@ -126,22 +182,21 @@ const TerminatingLink = new ApolloLink(()=>{
126
182
  return `${major}.${minor}.${patch}`;
127
183
  };
128
184
  /**
129
- * Generates a User-Agent string following standard format
185
+ * Generates a custom SDK Agent string following standard format
130
186
  * Format: Quiltt/<version> (<platform-info>)
131
- */ const getUserAgent = (sdkVersion, platformInfo)=>{
187
+ */ const getSDKAgent = (sdkVersion, platformInfo)=>{
132
188
  return `Quiltt/${sdkVersion} (${platformInfo})`;
133
189
  };
134
190
 
135
191
  const createVersionLink = (platformInfo)=>{
136
192
  const versionNumber = extractVersionNumber(version);
137
- const userAgent = getUserAgent(versionNumber, platformInfo);
193
+ const sdkAgent = getSDKAgent(versionNumber, platformInfo);
138
194
  return new ApolloLink((operation, forward)=>{
139
195
  operation.setContext(({ headers = {} })=>({
140
196
  headers: {
141
197
  ...headers,
142
198
  'Quiltt-Client-Version': version,
143
- 'Quiltt-SDK-Agent': userAgent,
144
- 'User-Agent': userAgent
199
+ 'Quiltt-SDK-Agent': sdkAgent
145
200
  }
146
201
  }));
147
202
  return forward(operation);
@@ -182,4 +237,4 @@ class QuilttClient extends ApolloClient {
182
237
  }
183
238
  }
184
239
 
185
- export { AuthLink, BatchHttpLink, ErrorLink, ForwardableLink, HttpLink, QuilttClient, RetryLink, SubscriptionLink, TerminatingLink, createVersionLink };
240
+ export { AuthLink, BatchHttpLink, ErrorLink, ForwardableLink, HeadersLink, HttpLink, QuilttClient, RetryLink, SubscriptionLink, TerminatingLink, createVersionLink };
@@ -7,6 +7,32 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
7
 
8
8
  var crossfetch__default = /*#__PURE__*/_interopDefault(crossfetch);
9
9
 
10
+ /**
11
+ * Extracts version number from formatted version string
12
+ * @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
13
+ * @returns Version number like "4.5.1" or "unknown" if not found
14
+ */ const extractVersionNumber = (formattedVersion)=>{
15
+ // Find the 'v' prefix and extract version after it
16
+ const vIndex = formattedVersion.indexOf('v');
17
+ if (vIndex === -1) return 'unknown';
18
+ const versionPart = formattedVersion.substring(vIndex + 1);
19
+ const parts = versionPart.split('.');
20
+ // Validate we have at least major.minor.patch
21
+ if (parts.length < 3) return 'unknown';
22
+ // Extract numeric parts (handles cases like "4.5.1-beta")
23
+ const major = parts[0].match(/^\d+/)?.[0];
24
+ const minor = parts[1].match(/^\d+/)?.[0];
25
+ const patch = parts[2].match(/^\d+/)?.[0];
26
+ if (!major || !minor || !patch) return 'unknown';
27
+ return `${major}.${minor}.${patch}`;
28
+ };
29
+ /**
30
+ * Generates a custom SDK Agent string following standard format
31
+ * Format: Quiltt/<version> (<platform-info>)
32
+ */ const getSDKAgent = (sdkVersion, platformInfo)=>{
33
+ return `Quiltt/${sdkVersion} (${platformInfo})`;
34
+ };
35
+
10
36
  // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
11
37
  const effectiveFetch = typeof fetch === 'undefined' ? crossfetch__default.default : fetch;
12
38
  const RETRY_DELAY = 150 // ms
@@ -60,8 +86,7 @@ var AuthStrategies = /*#__PURE__*/ function(AuthStrategies) {
60
86
  }({});
61
87
  // https://www.quiltt.dev/api-reference/auth
62
88
  class AuthAPI {
63
- // @todo userAgent: string | undefined - allow SDKs and callers to set a userAgent
64
- constructor(clientId){
89
+ constructor(clientId, customHeaders, sdkAgent = getSDKAgent(extractVersionNumber(index_cjs.version), 'Unknown')){
65
90
  /**
66
91
  * Response Statuses:
67
92
  * - 200: OK -> Session is Valid
@@ -114,6 +139,13 @@ class AuthAPI {
114
139
  const headers = new Headers();
115
140
  headers.set('Content-Type', 'application/json');
116
141
  headers.set('Accept', 'application/json');
142
+ headers.set('Quiltt-SDK-Agent', this.sdkAgent);
143
+ // Apply custom headers
144
+ if (this.customHeaders) {
145
+ Object.entries(this.customHeaders).forEach(([key, value])=>{
146
+ headers.set(key, value);
147
+ });
148
+ }
117
149
  if (token) {
118
150
  headers.set('Authorization', `Bearer ${token}`);
119
151
  }
@@ -136,37 +168,13 @@ class AuthAPI {
136
168
  };
137
169
  };
138
170
  this.clientId = clientId;
171
+ this.customHeaders = customHeaders;
172
+ this.sdkAgent = sdkAgent;
139
173
  }
140
174
  }
141
175
 
142
- /**
143
- * Extracts version number from formatted version string
144
- * @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
145
- * @returns Version number like "4.5.1" or "unknown" if not found
146
- */ const extractVersionNumber = (formattedVersion)=>{
147
- // Find the 'v' prefix and extract version after it
148
- const vIndex = formattedVersion.indexOf('v');
149
- if (vIndex === -1) return 'unknown';
150
- const versionPart = formattedVersion.substring(vIndex + 1);
151
- const parts = versionPart.split('.');
152
- // Validate we have at least major.minor.patch
153
- if (parts.length < 3) return 'unknown';
154
- // Extract numeric parts (handles cases like "4.5.1-beta")
155
- const major = parts[0].match(/^\d+/)?.[0];
156
- const minor = parts[1].match(/^\d+/)?.[0];
157
- const patch = parts[2].match(/^\d+/)?.[0];
158
- if (!major || !minor || !patch) return 'unknown';
159
- return `${major}.${minor}.${patch}`;
160
- };
161
- /**
162
- * Generates a User-Agent string following standard format
163
- * Format: Quiltt/<version> (<platform-info>)
164
- */ const getUserAgent = (sdkVersion, platformInfo)=>{
165
- return `Quiltt/${sdkVersion} (${platformInfo})`;
166
- };
167
-
168
176
  class ConnectorsAPI {
169
- constructor(clientId, userAgent = getUserAgent(extractVersionNumber(index_cjs.version), 'Unknown')){
177
+ constructor(clientId, sdkAgent = getSDKAgent(extractVersionNumber(index_cjs.version), 'Unknown'), customHeaders){
170
178
  /**
171
179
  * Response Statuses:
172
180
  * - 200: OK -> Institutions Found
@@ -207,9 +215,16 @@ class ConnectorsAPI {
207
215
  const headers = new Headers();
208
216
  headers.set('Content-Type', 'application/json');
209
217
  headers.set('Accept', 'application/json');
210
- headers.set('User-Agent', this.userAgent);
211
- headers.set('Quiltt-SDK-Agent', this.userAgent);
212
- headers.set('Authorization', `Bearer ${token}`);
218
+ headers.set('Quiltt-SDK-Agent', this.sdkAgent);
219
+ // Apply custom headers
220
+ if (this.customHeaders) {
221
+ Object.entries(this.customHeaders).forEach(([key, value])=>{
222
+ headers.set(key, value);
223
+ });
224
+ }
225
+ if (token) {
226
+ headers.set('Authorization', `Bearer ${token}`);
227
+ }
213
228
  return {
214
229
  headers,
215
230
  validateStatus: this.validateStatus,
@@ -218,7 +233,8 @@ class ConnectorsAPI {
218
233
  };
219
234
  this.validateStatus = (status)=>status < 500 && status !== 429;
220
235
  this.clientId = clientId;
221
- this.userAgent = userAgent;
236
+ this.sdkAgent = sdkAgent;
237
+ this.customHeaders = customHeaders;
222
238
  }
223
239
  }
224
240
 
@@ -50,7 +50,15 @@ type SessionResponse = FetchResponse<SessionData>;
50
50
  declare class AuthAPI {
51
51
  /** The Connector ID, required for identify & authenticate calls */
52
52
  clientId: string | undefined;
53
- constructor(clientId?: string | undefined);
53
+ /** The SDK Agent string for telemetry */
54
+ sdkAgent: string;
55
+ /**
56
+ * Custom headers to include with every request.
57
+ * For Quiltt internal usage. Not intended for public use.
58
+ * @internal
59
+ */
60
+ customHeaders: Record<string, string> | undefined;
61
+ constructor(clientId?: string | undefined, customHeaders?: Record<string, string>, sdkAgent?: string);
54
62
  /**
55
63
  * Response Statuses:
56
64
  * - 200: OK -> Session is Valid
@@ -96,8 +104,14 @@ type SearchResponse = FetchResponse<InstitutionsData>;
96
104
  type ResolvableResponse = FetchResponse<ResolvableData>;
97
105
  declare class ConnectorsAPI {
98
106
  clientId: string;
99
- userAgent: string;
100
- constructor(clientId: string, userAgent?: string);
107
+ sdkAgent: string;
108
+ /**
109
+ * Custom headers to include with every request.
110
+ * For Quiltt internal usage. Not intended for public use.
111
+ * @internal
112
+ */
113
+ customHeaders: Record<string, string> | undefined;
114
+ constructor(clientId: string, sdkAgent?: string, customHeaders?: Record<string, string>);
101
115
  /**
102
116
  * Response Statuses:
103
117
  * - 200: OK -> Institutions Found
@@ -1,6 +1,32 @@
1
- import { endpointAuth, endpointRest, version } from '../../config/index.js';
1
+ import { endpointAuth, version, endpointRest } from '../../config/index.js';
2
2
  import crossfetch from 'cross-fetch';
3
3
 
4
+ /**
5
+ * Extracts version number from formatted version string
6
+ * @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
7
+ * @returns Version number like "4.5.1" or "unknown" if not found
8
+ */ const extractVersionNumber = (formattedVersion)=>{
9
+ // Find the 'v' prefix and extract version after it
10
+ const vIndex = formattedVersion.indexOf('v');
11
+ if (vIndex === -1) return 'unknown';
12
+ const versionPart = formattedVersion.substring(vIndex + 1);
13
+ const parts = versionPart.split('.');
14
+ // Validate we have at least major.minor.patch
15
+ if (parts.length < 3) return 'unknown';
16
+ // Extract numeric parts (handles cases like "4.5.1-beta")
17
+ const major = parts[0].match(/^\d+/)?.[0];
18
+ const minor = parts[1].match(/^\d+/)?.[0];
19
+ const patch = parts[2].match(/^\d+/)?.[0];
20
+ if (!major || !minor || !patch) return 'unknown';
21
+ return `${major}.${minor}.${patch}`;
22
+ };
23
+ /**
24
+ * Generates a custom SDK Agent string following standard format
25
+ * Format: Quiltt/<version> (<platform-info>)
26
+ */ const getSDKAgent = (sdkVersion, platformInfo)=>{
27
+ return `Quiltt/${sdkVersion} (${platformInfo})`;
28
+ };
29
+
4
30
  // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
5
31
  const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch;
6
32
  const RETRY_DELAY = 150 // ms
@@ -54,8 +80,7 @@ var AuthStrategies = /*#__PURE__*/ function(AuthStrategies) {
54
80
  }({});
55
81
  // https://www.quiltt.dev/api-reference/auth
56
82
  class AuthAPI {
57
- // @todo userAgent: string | undefined - allow SDKs and callers to set a userAgent
58
- constructor(clientId){
83
+ constructor(clientId, customHeaders, sdkAgent = getSDKAgent(extractVersionNumber(version), 'Unknown')){
59
84
  /**
60
85
  * Response Statuses:
61
86
  * - 200: OK -> Session is Valid
@@ -108,6 +133,13 @@ class AuthAPI {
108
133
  const headers = new Headers();
109
134
  headers.set('Content-Type', 'application/json');
110
135
  headers.set('Accept', 'application/json');
136
+ headers.set('Quiltt-SDK-Agent', this.sdkAgent);
137
+ // Apply custom headers
138
+ if (this.customHeaders) {
139
+ Object.entries(this.customHeaders).forEach(([key, value])=>{
140
+ headers.set(key, value);
141
+ });
142
+ }
111
143
  if (token) {
112
144
  headers.set('Authorization', `Bearer ${token}`);
113
145
  }
@@ -130,37 +162,13 @@ class AuthAPI {
130
162
  };
131
163
  };
132
164
  this.clientId = clientId;
165
+ this.customHeaders = customHeaders;
166
+ this.sdkAgent = sdkAgent;
133
167
  }
134
168
  }
135
169
 
136
- /**
137
- * Extracts version number from formatted version string
138
- * @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
139
- * @returns Version number like "4.5.1" or "unknown" if not found
140
- */ const extractVersionNumber = (formattedVersion)=>{
141
- // Find the 'v' prefix and extract version after it
142
- const vIndex = formattedVersion.indexOf('v');
143
- if (vIndex === -1) return 'unknown';
144
- const versionPart = formattedVersion.substring(vIndex + 1);
145
- const parts = versionPart.split('.');
146
- // Validate we have at least major.minor.patch
147
- if (parts.length < 3) return 'unknown';
148
- // Extract numeric parts (handles cases like "4.5.1-beta")
149
- const major = parts[0].match(/^\d+/)?.[0];
150
- const minor = parts[1].match(/^\d+/)?.[0];
151
- const patch = parts[2].match(/^\d+/)?.[0];
152
- if (!major || !minor || !patch) return 'unknown';
153
- return `${major}.${minor}.${patch}`;
154
- };
155
- /**
156
- * Generates a User-Agent string following standard format
157
- * Format: Quiltt/<version> (<platform-info>)
158
- */ const getUserAgent = (sdkVersion, platformInfo)=>{
159
- return `Quiltt/${sdkVersion} (${platformInfo})`;
160
- };
161
-
162
170
  class ConnectorsAPI {
163
- constructor(clientId, userAgent = getUserAgent(extractVersionNumber(version), 'Unknown')){
171
+ constructor(clientId, sdkAgent = getSDKAgent(extractVersionNumber(version), 'Unknown'), customHeaders){
164
172
  /**
165
173
  * Response Statuses:
166
174
  * - 200: OK -> Institutions Found
@@ -201,9 +209,16 @@ class ConnectorsAPI {
201
209
  const headers = new Headers();
202
210
  headers.set('Content-Type', 'application/json');
203
211
  headers.set('Accept', 'application/json');
204
- headers.set('User-Agent', this.userAgent);
205
- headers.set('Quiltt-SDK-Agent', this.userAgent);
206
- headers.set('Authorization', `Bearer ${token}`);
212
+ headers.set('Quiltt-SDK-Agent', this.sdkAgent);
213
+ // Apply custom headers
214
+ if (this.customHeaders) {
215
+ Object.entries(this.customHeaders).forEach(([key, value])=>{
216
+ headers.set(key, value);
217
+ });
218
+ }
219
+ if (token) {
220
+ headers.set('Authorization', `Bearer ${token}`);
221
+ }
207
222
  return {
208
223
  headers,
209
224
  validateStatus: this.validateStatus,
@@ -212,7 +227,8 @@ class ConnectorsAPI {
212
227
  };
213
228
  this.validateStatus = (status)=>status < 500 && status !== 429;
214
229
  this.clientId = clientId;
215
- this.userAgent = userAgent;
230
+ this.sdkAgent = sdkAgent;
231
+ this.customHeaders = customHeaders;
216
232
  }
217
233
  }
218
234
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
3
  var name = "@quiltt/core";
4
- var version$1 = "5.0.3";
4
+ var version$1 = "5.1.1";
5
5
 
6
6
  const QUILTT_API_INSECURE = (()=>{
7
7
  try {
@@ -1,5 +1,5 @@
1
1
  var name = "@quiltt/core";
2
- var version$1 = "5.0.3";
2
+ var version$1 = "5.1.1";
3
3
 
4
4
  const QUILTT_API_INSECURE = (()=>{
5
5
  try {
@@ -20,13 +20,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
20
20
  return `${major}.${minor}.${patch}`;
21
21
  };
22
22
  /**
23
- * Generates a User-Agent string following standard format
23
+ * Generates a custom SDK Agent string following standard format
24
24
  * Format: Quiltt/<version> (<platform-info>)
25
- */ const getUserAgent = (sdkVersion, platformInfo)=>{
25
+ */ const getSDKAgent = (sdkVersion, platformInfo)=>{
26
26
  return `Quiltt/${sdkVersion} (${platformInfo})`;
27
27
  };
28
28
  /**
29
- * Detects browser information from user agent string
29
+ * Detects browser information from Browser's user agent string
30
30
  * Returns browser name and version, or 'Unknown' if not detected
31
31
  */ const getBrowserInfo = ()=>{
32
32
  if (typeof navigator === 'undefined' || !navigator.userAgent) {
@@ -58,4 +58,4 @@ Object.defineProperty(exports, '__esModule', { value: true });
58
58
 
59
59
  exports.extractVersionNumber = extractVersionNumber;
60
60
  exports.getBrowserInfo = getBrowserInfo;
61
- exports.getUserAgent = getUserAgent;
61
+ exports.getSDKAgent = getSDKAgent;
@@ -5,14 +5,14 @@
5
5
  */
6
6
  declare const extractVersionNumber: (formattedVersion: string) => string;
7
7
  /**
8
- * Generates a User-Agent string following standard format
8
+ * Generates a custom SDK Agent string following standard format
9
9
  * Format: Quiltt/<version> (<platform-info>)
10
10
  */
11
- declare const getUserAgent: (sdkVersion: string, platformInfo: string) => string;
11
+ declare const getSDKAgent: (sdkVersion: string, platformInfo: string) => string;
12
12
  /**
13
- * Detects browser information from user agent string
13
+ * Detects browser information from Browser's user agent string
14
14
  * Returns browser name and version, or 'Unknown' if not detected
15
15
  */
16
16
  declare const getBrowserInfo: () => string;
17
17
 
18
- export { extractVersionNumber, getBrowserInfo, getUserAgent };
18
+ export { extractVersionNumber, getBrowserInfo, getSDKAgent };
@@ -18,13 +18,13 @@
18
18
  return `${major}.${minor}.${patch}`;
19
19
  };
20
20
  /**
21
- * Generates a User-Agent string following standard format
21
+ * Generates a custom SDK Agent string following standard format
22
22
  * Format: Quiltt/<version> (<platform-info>)
23
- */ const getUserAgent = (sdkVersion, platformInfo)=>{
23
+ */ const getSDKAgent = (sdkVersion, platformInfo)=>{
24
24
  return `Quiltt/${sdkVersion} (${platformInfo})`;
25
25
  };
26
26
  /**
27
- * Detects browser information from user agent string
27
+ * Detects browser information from Browser's user agent string
28
28
  * Returns browser name and version, or 'Unknown' if not detected
29
29
  */ const getBrowserInfo = ()=>{
30
30
  if (typeof navigator === 'undefined' || !navigator.userAgent) {
@@ -54,4 +54,4 @@
54
54
  return 'Unknown';
55
55
  };
56
56
 
57
- export { extractVersionNumber, getBrowserInfo, getUserAgent };
57
+ export { extractVersionNumber, getBrowserInfo, getSDKAgent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiltt/core",
3
- "version": "5.0.3",
3
+ "version": "5.1.1",
4
4
  "description": "Javascript API client and utilities for Quiltt",
5
5
  "keywords": [
6
6
  "quiltt",
@@ -125,6 +125,8 @@ export type ConnectorSDKCallbackMetadata = {
125
125
  export type ConnectorSDKConnectOptions = ConnectorSDKCallbacks & {
126
126
  /** The Institution ID or search term to connect */
127
127
  institution?: string
128
+ /** The OAuth redirect URL for mobile or embedded webview flows */
129
+ oauthRedirectUrl?: string
128
130
  }
129
131
 
130
132
  /**
@@ -134,6 +136,8 @@ export type ConnectorSDKConnectOptions = ConnectorSDKCallbacks & {
134
136
  export type ConnectorSDKReconnectOptions = ConnectorSDKCallbacks & {
135
137
  /** The ID of the Connection to reconnect */
136
138
  connectionId: string
139
+ /** The OAuth redirect URL for mobile or embedded webview flows */
140
+ oauthRedirectUrl?: string
137
141
  }
138
142
 
139
143
  /** Options to initialize Connector
@@ -148,4 +152,6 @@ export type ConnectorSDKConnectorOptions = ConnectorSDKCallbacks & {
148
152
  connectionId?: string
149
153
  /** The nonce to use for the script tag */
150
154
  nonce?: string
155
+ /** The OAuth redirect URL for mobile or embedded webview flows */
156
+ oauthRedirectUrl?: string
151
157
  }
@@ -39,7 +39,7 @@ class ActionCableLink extends ApolloLink {
39
39
 
40
40
  // Interestingly, this link does _not_ call through to `next` because
41
41
  // instead, it sends the request to ActionCable.
42
- request(
42
+ override request(
43
43
  operation: ApolloLink.Operation,
44
44
  _next: ApolloLink.ForwardFunction
45
45
  ): Observable<RequestResult> {
@@ -58,6 +58,7 @@ class ActionCableLink extends ApolloLink {
58
58
  }
59
59
 
60
60
  return new Observable((observer) => {
61
+ let cancelled = false
61
62
  const channelId = Math.round(Date.now() + Math.random() * 100000).toString(16)
62
63
  const actionName = this.actionName
63
64
  const connectionParams =
@@ -77,6 +78,7 @@ class ActionCableLink extends ApolloLink {
77
78
  ),
78
79
  {
79
80
  connected: (args?: { reconnected: boolean }) => {
81
+ if (cancelled) return
80
82
  channel.perform(actionName, {
81
83
  query: operation.query ? print(operation.query) : null,
82
84
  variables: operation.variables,
@@ -88,6 +90,8 @@ class ActionCableLink extends ApolloLink {
88
90
  },
89
91
 
90
92
  received: (payload: { result: RequestResult; more: any }) => {
93
+ if (cancelled) return
94
+
91
95
  if (payload?.result?.data || payload?.result?.errors) {
92
96
  observer.next(payload.result)
93
97
  }
@@ -98,13 +102,20 @@ class ActionCableLink extends ApolloLink {
98
102
 
99
103
  callbacks.received?.(payload)
100
104
  },
105
+ // Intentionally not guarded by `cancelled` - disconnected represents
106
+ // the WebSocket connection state which users may want to know about
107
+ // for cleanup/status purposes, even after the observable completes.
101
108
  disconnected: () => {
102
109
  callbacks.disconnected?.()
103
110
  },
104
111
  }
105
112
  )
106
- // Make the ActionCable subscription behave like an Apollo subscription
107
- return Object.assign(channel, { closed: false })
113
+
114
+ // Return teardown logic
115
+ return () => {
116
+ cancelled = true
117
+ channel.unsubscribe()
118
+ }
108
119
  })
109
120
  }
110
121
  }
@@ -13,7 +13,7 @@ import { validateSessionToken } from '@/utils/token-validation'
13
13
  * - Emits GraphQL errors for consistent Apollo error handling
14
14
  */
15
15
  export class AuthLink extends ApolloLink {
16
- request(
16
+ override request(
17
17
  operation: ApolloLink.Operation,
18
18
  forward: ApolloLink.ForwardFunction
19
19
  ): Observable<ApolloLink.Result> {
@@ -0,0 +1,84 @@
1
+ import { ApolloLink } from '@apollo/client/core'
2
+ import type { Subscription } from 'rxjs'
3
+ import { Observable } from 'rxjs'
4
+
5
+ export type HeadersLinkOptions = {
6
+ /** Static headers to add to every request */
7
+ headers?: Record<string, string>
8
+ /** Dynamic headers function called on each request */
9
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>
10
+ }
11
+
12
+ /**
13
+ * Apollo Link that adds custom headers to GraphQL requests.
14
+ *
15
+ * Headers can be provided statically or dynamically via a function.
16
+ * These headers are added before the AuthLink, so they will be preserved
17
+ * alongside the authorization header.
18
+ */
19
+ export class HeadersLink extends ApolloLink {
20
+ private headers: Record<string, string>
21
+ private getHeaders?: () => Record<string, string> | Promise<Record<string, string>>
22
+
23
+ constructor(options: HeadersLinkOptions = {}) {
24
+ super()
25
+ this.headers = options.headers ?? {}
26
+ this.getHeaders = options.getHeaders
27
+ }
28
+
29
+ override request(
30
+ operation: ApolloLink.Operation,
31
+ forward: ApolloLink.ForwardFunction
32
+ ): Observable<ApolloLink.Result> {
33
+ // If we have a dynamic headers function, handle it
34
+ if (this.getHeaders) {
35
+ return new Observable((observer) => {
36
+ let innerSubscription: Subscription | undefined
37
+ let cancelled = false
38
+
39
+ Promise.resolve(this.getHeaders!())
40
+ .then((dynamicHeaders) => {
41
+ // Guard against late resolution after unsubscribe
42
+ if (cancelled) return
43
+
44
+ operation.setContext(({ headers = {} }) => ({
45
+ headers: {
46
+ ...headers,
47
+ ...this.headers,
48
+ ...dynamicHeaders,
49
+ },
50
+ }))
51
+
52
+ innerSubscription = forward(operation).subscribe({
53
+ next: (value) => !cancelled && observer.next(value),
54
+ error: (err) => !cancelled && observer.error(err),
55
+ complete: () => !cancelled && observer.complete(),
56
+ })
57
+ })
58
+ .catch((error) => {
59
+ if (!cancelled) {
60
+ observer.error(error)
61
+ }
62
+ })
63
+
64
+ // Return teardown logic
65
+ return () => {
66
+ cancelled = true
67
+ innerSubscription?.unsubscribe()
68
+ }
69
+ })
70
+ }
71
+
72
+ // Static headers only
73
+ operation.setContext(({ headers = {} }) => ({
74
+ headers: {
75
+ ...headers,
76
+ ...this.headers,
77
+ },
78
+ }))
79
+
80
+ return forward(operation)
81
+ }
82
+ }
83
+
84
+ export default HeadersLink
@@ -1,19 +1,18 @@
1
1
  import { ApolloLink } from '@apollo/client/core'
2
2
 
3
3
  import { version } from '@/config'
4
- import { extractVersionNumber, getUserAgent } from '@/utils/telemetry'
4
+ import { extractVersionNumber, getSDKAgent } from '@/utils/telemetry'
5
5
 
6
6
  export const createVersionLink = (platformInfo: string) => {
7
7
  const versionNumber = extractVersionNumber(version)
8
- const userAgent = getUserAgent(versionNumber, platformInfo)
8
+ const sdkAgent = getSDKAgent(versionNumber, platformInfo)
9
9
 
10
10
  return new ApolloLink((operation, forward) => {
11
11
  operation.setContext(({ headers = {} }) => ({
12
12
  headers: {
13
13
  ...headers,
14
14
  'Quiltt-Client-Version': version,
15
- 'Quiltt-SDK-Agent': userAgent,
16
- 'User-Agent': userAgent,
15
+ 'Quiltt-SDK-Agent': sdkAgent,
17
16
  },
18
17
  }))
19
18
  return forward(operation)
@@ -2,6 +2,7 @@ export * from './AuthLink'
2
2
  export * from './BatchHttpLink'
3
3
  export * from './ErrorLink'
4
4
  export * from './ForwardableLink'
5
+ export * from './HeadersLink'
5
6
  export * from './HttpLink'
6
7
  export * from './RetryLink'
7
8
  export * from './SubscriptionLink'
@@ -1,4 +1,5 @@
1
- import { endpointAuth } from '@/config'
1
+ import { endpointAuth, version } from '@/config'
2
+ import { extractVersionNumber, getSDKAgent } from '@/utils/telemetry'
2
3
 
3
4
  import type { FetchResponse } from './fetchWithRetry'
4
5
  import { fetchWithRetry } from './fetchWithRetry'
@@ -35,11 +36,23 @@ export type SessionResponse = FetchResponse<SessionData>
35
36
  export class AuthAPI {
36
37
  /** The Connector ID, required for identify & authenticate calls */
37
38
  clientId: string | undefined
39
+ /** The SDK Agent string for telemetry */
40
+ sdkAgent: string
41
+ /**
42
+ * Custom headers to include with every request.
43
+ * For Quiltt internal usage. Not intended for public use.
44
+ * @internal
45
+ */
46
+ customHeaders: Record<string, string> | undefined
38
47
 
39
- // @todo userAgent: string | undefined - allow SDKs and callers to set a userAgent
40
-
41
- constructor(clientId?: string | undefined) {
48
+ constructor(
49
+ clientId?: string | undefined,
50
+ customHeaders?: Record<string, string>,
51
+ sdkAgent: string = getSDKAgent(extractVersionNumber(version), 'Unknown')
52
+ ) {
42
53
  this.clientId = clientId
54
+ this.customHeaders = customHeaders
55
+ this.sdkAgent = sdkAgent
43
56
  }
44
57
 
45
58
  /**
@@ -102,6 +115,14 @@ export class AuthAPI {
102
115
  const headers = new Headers()
103
116
  headers.set('Content-Type', 'application/json')
104
117
  headers.set('Accept', 'application/json')
118
+ headers.set('Quiltt-SDK-Agent', this.sdkAgent)
119
+
120
+ // Apply custom headers
121
+ if (this.customHeaders) {
122
+ Object.entries(this.customHeaders).forEach(([key, value]) => {
123
+ headers.set(key, value)
124
+ })
125
+ }
105
126
 
106
127
  if (token) {
107
128
  headers.set('Authorization', `Bearer ${token}`)
@@ -1,5 +1,5 @@
1
1
  import { endpointRest, version } from '@/config'
2
- import { extractVersionNumber, getUserAgent } from '@/utils/telemetry'
2
+ import { extractVersionNumber, getSDKAgent } from '@/utils/telemetry'
3
3
 
4
4
  import type { FetchResponse } from './fetchWithRetry'
5
5
  import { fetchWithRetry } from './fetchWithRetry'
@@ -18,14 +18,22 @@ export type ResolvableResponse = FetchResponse<ResolvableData>
18
18
 
19
19
  export class ConnectorsAPI {
20
20
  clientId: string
21
- userAgent: string
21
+ sdkAgent: string
22
+ /**
23
+ * Custom headers to include with every request.
24
+ * For Quiltt internal usage. Not intended for public use.
25
+ * @internal
26
+ */
27
+ customHeaders: Record<string, string> | undefined
22
28
 
23
29
  constructor(
24
30
  clientId: string,
25
- userAgent: string = getUserAgent(extractVersionNumber(version), 'Unknown')
31
+ sdkAgent: string = getSDKAgent(extractVersionNumber(version), 'Unknown'),
32
+ customHeaders?: Record<string, string>
26
33
  ) {
27
34
  this.clientId = clientId
28
- this.userAgent = userAgent
35
+ this.sdkAgent = sdkAgent
36
+ this.customHeaders = customHeaders
29
37
  }
30
38
 
31
39
  /**
@@ -91,9 +99,18 @@ export class ConnectorsAPI {
91
99
  const headers = new Headers()
92
100
  headers.set('Content-Type', 'application/json')
93
101
  headers.set('Accept', 'application/json')
94
- headers.set('User-Agent', this.userAgent)
95
- headers.set('Quiltt-SDK-Agent', this.userAgent)
96
- headers.set('Authorization', `Bearer ${token}`)
102
+ headers.set('Quiltt-SDK-Agent', this.sdkAgent)
103
+
104
+ // Apply custom headers
105
+ if (this.customHeaders) {
106
+ Object.entries(this.customHeaders).forEach(([key, value]) => {
107
+ headers.set(key, value)
108
+ })
109
+ }
110
+
111
+ if (token) {
112
+ headers.set('Authorization', `Bearer ${token}`)
113
+ }
97
114
 
98
115
  return {
99
116
  headers,
@@ -1,4 +1,4 @@
1
- import { name as packageName, version as packageVersion } from '../../package.json'
1
+ import { name as PACKAGE_NAME, version as PACKAGE_VERSION } from '../../package.json'
2
2
 
3
3
  const QUILTT_API_INSECURE = (() => {
4
4
  try {
@@ -29,7 +29,7 @@ const protocolHttp = `http${QUILTT_API_INSECURE ? '' : 's'}`
29
29
  const protocolWebsockets = `ws${QUILTT_API_INSECURE ? '' : 's'}`
30
30
 
31
31
  export const debugging = QUILTT_DEBUG
32
- export const version = `${packageName}: v${packageVersion}`
32
+ export const version = `${PACKAGE_NAME}: v${PACKAGE_VERSION}`
33
33
 
34
34
  export const cdnBase = `${protocolHttp}://cdn.${domain}`
35
35
  export const endpointAuth = `${protocolHttp}://auth.${domain}/v1/users/session`
@@ -25,15 +25,15 @@ export const extractVersionNumber = (formattedVersion: string): string => {
25
25
  }
26
26
 
27
27
  /**
28
- * Generates a User-Agent string following standard format
28
+ * Generates a custom SDK Agent string following standard format
29
29
  * Format: Quiltt/<version> (<platform-info>)
30
30
  */
31
- export const getUserAgent = (sdkVersion: string, platformInfo: string): string => {
31
+ export const getSDKAgent = (sdkVersion: string, platformInfo: string): string => {
32
32
  return `Quiltt/${sdkVersion} (${platformInfo})`
33
33
  }
34
34
 
35
35
  /**
36
- * Detects browser information from user agent string
36
+ * Detects browser information from Browser's user agent string
37
37
  * Returns browser name and version, or 'Unknown' if not detected
38
38
  */
39
39
  export const getBrowserInfo = (): string => {