@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
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Extracts version number from formatted version string
3
+ * @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
4
+ * @returns Version number like "4.5.1" or "unknown" if not found
5
+ */
6
+ declare const extractVersionNumber: (formattedVersion: string) => string;
7
+ /**
8
+ * Generates a User-Agent string following standard format
9
+ * Format: Quiltt/<version> (<platform-info>)
10
+ */
11
+ declare const getUserAgent: (sdkVersion: string, platformInfo: string) => string;
12
+ /**
13
+ * Detects browser information from user agent string
14
+ * Returns browser name and version, or 'Unknown' if not detected
15
+ */
16
+ declare const getBrowserInfo: () => string;
17
+
18
+ export { extractVersionNumber, getBrowserInfo, getUserAgent };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Extracts version number from formatted version string
3
+ * @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
4
+ * @returns Version number like "4.5.1" or "unknown" if not found
5
+ */ const extractVersionNumber = (formattedVersion)=>{
6
+ // Find the 'v' prefix and extract version after it
7
+ const vIndex = formattedVersion.indexOf('v');
8
+ if (vIndex === -1) return 'unknown';
9
+ const versionPart = formattedVersion.substring(vIndex + 1);
10
+ const parts = versionPart.split('.');
11
+ // Validate we have at least major.minor.patch
12
+ if (parts.length < 3) return 'unknown';
13
+ // Extract numeric parts (handles cases like "4.5.1-beta")
14
+ const major = parts[0].match(/^\d+/)?.[0];
15
+ const minor = parts[1].match(/^\d+/)?.[0];
16
+ const patch = parts[2].match(/^\d+/)?.[0];
17
+ if (!major || !minor || !patch) return 'unknown';
18
+ return `${major}.${minor}.${patch}`;
19
+ };
20
+ /**
21
+ * Generates a User-Agent string following standard format
22
+ * Format: Quiltt/<version> (<platform-info>)
23
+ */ const getUserAgent = (sdkVersion, platformInfo)=>{
24
+ return `Quiltt/${sdkVersion} (${platformInfo})`;
25
+ };
26
+ /**
27
+ * Detects browser information from user agent string
28
+ * Returns browser name and version, or 'Unknown' if not detected
29
+ */ const getBrowserInfo = ()=>{
30
+ if (typeof navigator === 'undefined' || !navigator.userAgent) {
31
+ return 'Unknown';
32
+ }
33
+ const ua = navigator.userAgent;
34
+ // Edge (must be checked before Chrome)
35
+ if (ua.includes('Edg/')) {
36
+ const version = ua.match(/Edg\/(\d+)/)?.[1] || 'Unknown';
37
+ return `Edge/${version}`;
38
+ }
39
+ // Chrome
40
+ if (ua.includes('Chrome/') && !ua.includes('Edg/')) {
41
+ const version = ua.match(/Chrome\/(\d+)/)?.[1] || 'Unknown';
42
+ return `Chrome/${version}`;
43
+ }
44
+ // Safari (must be checked after Chrome)
45
+ if (ua.includes('Safari/') && !ua.includes('Chrome/')) {
46
+ const version = ua.match(/Version\/(\d+)/)?.[1] || 'Unknown';
47
+ return `Safari/${version}`;
48
+ }
49
+ // Firefox
50
+ if (ua.includes('Firefox/')) {
51
+ const version = ua.match(/Firefox\/(\d+)/)?.[1] || 'Unknown';
52
+ return `Firefox/${version}`;
53
+ }
54
+ return 'Unknown';
55
+ };
56
+
57
+ export { extractVersionNumber, getBrowserInfo, getUserAgent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiltt/core",
3
- "version": "4.5.1",
3
+ "version": "5.0.1",
4
4
  "description": "Javascript API client and utilities for Quiltt",
5
5
  "keywords": [
6
6
  "quiltt",
@@ -20,8 +20,64 @@
20
20
  "type": "module",
21
21
  "exports": {
22
22
  ".": {
23
- "import": "./dist/index.js",
24
- "types": "./dist/index.d.ts"
23
+ "types": "./dist/index.d.ts",
24
+ "require": "./dist/index.cjs",
25
+ "import": "./dist/index.js"
26
+ },
27
+ "./api": {
28
+ "types": "./dist/api/index.d.ts",
29
+ "require": "./dist/api/index.cjs",
30
+ "import": "./dist/api/index.js"
31
+ },
32
+ "./api/browser": {
33
+ "types": "./dist/api/browser.d.ts",
34
+ "require": "./dist/api/browser.cjs",
35
+ "import": "./dist/api/browser.js"
36
+ },
37
+ "./api/graphql": {
38
+ "types": "./dist/api/graphql/index.d.ts",
39
+ "require": "./dist/api/graphql/index.cjs",
40
+ "import": "./dist/api/graphql/index.js"
41
+ },
42
+ "./api/rest": {
43
+ "types": "./dist/api/rest/index.d.ts",
44
+ "require": "./dist/api/rest/index.cjs",
45
+ "import": "./dist/api/rest/index.js"
46
+ },
47
+ "./auth": {
48
+ "types": "./dist/auth/index.d.ts",
49
+ "require": "./dist/auth/index.cjs",
50
+ "import": "./dist/auth/index.js"
51
+ },
52
+ "./config": {
53
+ "types": "./dist/config/index.d.ts",
54
+ "require": "./dist/config/index.cjs",
55
+ "import": "./dist/config/index.js"
56
+ },
57
+ "./observables": {
58
+ "types": "./dist/observables/index.d.ts",
59
+ "require": "./dist/observables/index.cjs",
60
+ "import": "./dist/observables/index.js"
61
+ },
62
+ "./storage": {
63
+ "types": "./dist/storage/index.d.ts",
64
+ "require": "./dist/storage/index.cjs",
65
+ "import": "./dist/storage/index.js"
66
+ },
67
+ "./timing": {
68
+ "types": "./dist/timing/index.d.ts",
69
+ "require": "./dist/timing/index.cjs",
70
+ "import": "./dist/timing/index.js"
71
+ },
72
+ "./types": {
73
+ "types": "./dist/types.d.ts",
74
+ "require": "./dist/types.cjs",
75
+ "import": "./dist/types.js"
76
+ },
77
+ "./utils": {
78
+ "types": "./dist/utils/index.d.ts",
79
+ "require": "./dist/utils/index.cjs",
80
+ "import": "./dist/utils/index.js"
25
81
  }
26
82
  },
27
83
  "types": "./dist/index.d.ts",
@@ -32,19 +88,19 @@
32
88
  ],
33
89
  "main": "dist/index.js",
34
90
  "dependencies": {
35
- "@apollo/client": "^3.14.0",
36
- "@rails/actioncable": "^8.0.300",
91
+ "@apollo/client": "^4.1.3",
92
+ "@rails/actioncable": "^8.1.200",
37
93
  "braces": "^3.0.3",
38
- "cross-fetch": "^4.0.0",
39
- "graphql": "^16.10.0",
40
- "graphql-ruby-client": "^1.14.5"
94
+ "cross-fetch": "^4.1.0",
95
+ "graphql": "^16.12.0",
96
+ "rxjs": "^7.8.2"
41
97
  },
42
98
  "devDependencies": {
43
- "@biomejs/biome": "2.3.8",
44
- "@types/node": "22.19.2",
45
- "@types/rails__actioncable": "6.1.11",
46
- "@types/react": "19.2.7",
47
- "bunchee": "6.6.2",
99
+ "@biomejs/biome": "2.3.14",
100
+ "@types/node": "24.10.10",
101
+ "@types/rails__actioncable": "8.0.3",
102
+ "@types/react": "19.2.11",
103
+ "bunchee": "6.9.4",
48
104
  "rimraf": "6.1.2",
49
105
  "typescript": "5.9.3"
50
106
  },
@@ -1,8 +1,7 @@
1
- import type { ApolloClientOptions, NormalizedCacheObject, Operation } from '@apollo/client/core'
2
- import { ApolloClient, ApolloLink } from '@apollo/client/core/index.js'
1
+ import { ApolloClient, ApolloLink } from '@apollo/client/core'
3
2
  import type { DefinitionNode, OperationDefinitionNode } from 'graphql'
4
3
 
5
- import { debugging } from '@/configuration'
4
+ import { debugging } from '@/config'
6
5
 
7
6
  import {
8
7
  AuthLink,
@@ -12,16 +11,17 @@ import {
12
11
  HttpLink,
13
12
  RetryLink,
14
13
  SubscriptionLink,
15
- VersionLink,
16
14
  } from './links'
17
15
 
18
- export type QuilttClientOptions<T> = Omit<ApolloClientOptions<T>, 'link'> & {
16
+ export type QuilttClientOptions = Omit<ApolloClient.Options, 'link'> & {
19
17
  /** An array of initial links to inject before the default Quiltt Links */
20
18
  customLinks?: ApolloLink[]
19
+ /** Platform-specific version link (required) */
20
+ versionLink: ApolloLink
21
21
  }
22
22
 
23
- export class QuilttClient extends ApolloClient<NormalizedCacheObject> {
24
- constructor(options: QuilttClientOptions<NormalizedCacheObject>) {
23
+ export class QuilttClient extends ApolloClient {
24
+ constructor(options: QuilttClientOptions) {
25
25
  const finalOptions = {
26
26
  ...options,
27
27
  devtools: {
@@ -34,13 +34,13 @@ export class QuilttClient extends ApolloClient<NormalizedCacheObject> {
34
34
  const isOperationDefinition = (def: DefinitionNode): def is OperationDefinitionNode =>
35
35
  def.kind === 'OperationDefinition'
36
36
 
37
- const isSubscriptionOperation = (operation: Operation) => {
37
+ const isSubscriptionOperation = (operation: ApolloLink.Operation) => {
38
38
  return operation.query.definitions.some(
39
39
  (definition) => isOperationDefinition(definition) && definition.operation === 'subscription'
40
40
  )
41
41
  }
42
42
 
43
- const isBatchable = (operation: Operation) => {
43
+ const isBatchable = (operation: ApolloLink.Operation) => {
44
44
  return operation.getContext().batchable ?? true
45
45
  }
46
46
 
@@ -49,12 +49,12 @@ export class QuilttClient extends ApolloClient<NormalizedCacheObject> {
49
49
 
50
50
  const quilttLink = ApolloLink.from([
51
51
  ...initialLinks,
52
- VersionLink,
52
+ options.versionLink,
53
53
  authLink,
54
54
  ErrorLink,
55
55
  RetryLink,
56
- ])
57
- .split(isSubscriptionOperation, subscriptionsLink, ForwardableLink)
56
+ ] as ApolloLink[])
57
+ .split(isSubscriptionOperation, subscriptionsLink as ApolloLink, ForwardableLink)
58
58
  .split(isBatchable, BatchHttpLink, HttpLink)
59
59
 
60
60
  super({
@@ -71,8 +71,8 @@ export class QuilttClient extends ApolloClient<NormalizedCacheObject> {
71
71
 
72
72
  /** Client and Tooling */
73
73
  export type { NormalizedCacheObject } from '@apollo/client/cache'
74
- export { InMemoryCache } from '@apollo/client/cache/index.js'
75
- export type { ApolloError, OperationVariables } from '@apollo/client/core'
76
- export { gql } from '@apollo/client/core/index.js'
74
+ export { InMemoryCache } from '@apollo/client/cache'
75
+ export type { OperationVariables } from '@apollo/client/core'
76
+ export { gql } from '@apollo/client/core'
77
77
  /** React hooks used by @quiltt/react-native and @quiltt/react */
78
- export { useMutation, useQuery, useSubscription } from '@apollo/client/react/hooks/index.js'
78
+ export { useMutation, useQuery, useSubscription } from '@apollo/client/react'
@@ -1,48 +1,58 @@
1
- import type { FetchResult, NextLink, Operation } from '@apollo/client/core'
2
- import { ApolloLink } from '@apollo/client/core/index.js'
3
- import { Observable } from '@apollo/client/utilities/index.js'
1
+ // Adapted from https://github.com/rmosolgo/graphql-ruby/blob/master/javascript_client/src/subscriptions/ActionCableLink.ts
2
+ import { ApolloLink } from '@apollo/client/core'
4
3
  import type { Consumer } from '@rails/actioncable'
5
4
  import { createConsumer } from '@rails/actioncable'
6
5
  import { print } from 'graphql'
6
+ import { Observable } from 'rxjs'
7
7
 
8
- import { endpointWebsockets } from '@/configuration'
9
- import { GlobalStorage } from '@/storage'
8
+ import { endpointWebsockets } from '@/config'
9
+ import { validateSessionToken } from '@/utils/token-validation'
10
10
 
11
- type RequestResult = FetchResult<
12
- { [key: string]: unknown },
13
- Record<string, unknown>,
14
- Record<string, unknown>
15
- >
16
- type ConnectionParams = object | ((operation: Operation) => object)
11
+ type RequestResult = ApolloLink.Result<{ [key: string]: unknown }>
12
+ type ConnectionParams = object | ((operation: ApolloLink.Operation) => object)
13
+ type SubscriptionCallbacks = {
14
+ connected?: (args?: { reconnected: boolean }) => void
15
+ disconnected?: () => void
16
+ received?: (payload: unknown) => void
17
+ }
17
18
 
18
19
  class ActionCableLink extends ApolloLink {
19
20
  cables: { [id: string]: Consumer }
20
21
  channelName: string
21
22
  actionName: string
22
23
  connectionParams: ConnectionParams
24
+ callbacks: SubscriptionCallbacks
23
25
 
24
26
  constructor(options: {
25
27
  channelName?: string
26
28
  actionName?: string
27
29
  connectionParams?: ConnectionParams
30
+ callbacks?: SubscriptionCallbacks
28
31
  }) {
29
32
  super()
30
33
  this.cables = {}
31
34
  this.channelName = options.channelName || 'GraphqlChannel'
32
35
  this.actionName = options.actionName || 'execute'
33
36
  this.connectionParams = options.connectionParams || {}
37
+ this.callbacks = options.callbacks || {}
34
38
  }
35
39
 
36
40
  // Interestingly, this link does _not_ call through to `next` because
37
41
  // instead, it sends the request to ActionCable.
38
- request(operation: Operation, _next: NextLink): Observable<RequestResult> | null {
39
- const token = GlobalStorage.get('session')
42
+ request(
43
+ operation: ApolloLink.Operation,
44
+ _next: ApolloLink.ForwardFunction
45
+ ): Observable<RequestResult> {
46
+ const validation = validateSessionToken('for subscription')
40
47
 
41
- if (!token) {
42
- console.warn('QuilttClient attempted to send an unauthenticated Subscription')
43
- return null
48
+ if (!validation.valid) {
49
+ return new Observable((observer) => {
50
+ observer.error(validation.error)
51
+ })
44
52
  }
45
53
 
54
+ const { token } = validation
55
+
46
56
  if (!this.cables[token]) {
47
57
  this.cables[token] = createConsumer(endpointWebsockets + (token ? `?token=${token}` : ''))
48
58
  }
@@ -55,6 +65,7 @@ class ActionCableLink extends ApolloLink {
55
65
  ? this.connectionParams(operation)
56
66
  : this.connectionParams
57
67
 
68
+ const callbacks = this.callbacks
58
69
  const channel = this.cables[token].subscriptions.create(
59
70
  Object.assign(
60
71
  {},
@@ -65,7 +76,7 @@ class ActionCableLink extends ApolloLink {
65
76
  connectionParams
66
77
  ),
67
78
  {
68
- connected: () => {
79
+ connected: (args?: { reconnected: boolean }) => {
69
80
  channel.perform(actionName, {
70
81
  query: operation.query ? print(operation.query) : null,
71
82
  variables: operation.variables,
@@ -73,6 +84,7 @@ class ActionCableLink extends ApolloLink {
73
84
  operationId: (operation as { operationId?: string }).operationId,
74
85
  operationName: operation.operationName,
75
86
  })
87
+ callbacks.connected?.(args)
76
88
  },
77
89
 
78
90
  received: (payload: { result: RequestResult; more: any }) => {
@@ -83,6 +95,11 @@ class ActionCableLink extends ApolloLink {
83
95
  if (!payload.more) {
84
96
  observer.complete()
85
97
  }
98
+
99
+ callbacks.received?.(payload)
100
+ },
101
+ disconnected: () => {
102
+ callbacks.disconnected?.()
86
103
  },
87
104
  }
88
105
  )
@@ -1,24 +1,32 @@
1
- import type { FetchResult, NextLink, Operation } from '@apollo/client/core'
2
- import { ApolloLink } from '@apollo/client/core/index.js'
3
- import type { Observable } from '@apollo/client/utilities'
1
+ import { ApolloLink } from '@apollo/client/core'
2
+ import { Observable } from 'rxjs'
4
3
 
5
- import { GlobalStorage } from '@/storage'
4
+ import { validateSessionToken } from '@/utils/token-validation'
6
5
 
7
6
  /**
8
- * unauthorizedCallback only triggers in the event the token is present, and
9
- * returns the token; This allows sessions to be forgotten without race conditions
10
- * causing null sessions to kill valid sessions, or invalid sessions for killing
11
- * valid sessions during rotation and networking weirdness.
7
+ * Apollo Link that handles authentication and session expiration for GraphQL requests.
8
+ *
9
+ * Features:
10
+ * - Automatically adds Bearer token to request headers
11
+ * - Detects expired tokens and triggers proper error handling
12
+ * - Clears expired sessions from storage (triggers React re-renders via observers)
13
+ * - Emits GraphQL errors for consistent Apollo error handling
12
14
  */
13
15
  export class AuthLink extends ApolloLink {
14
- request(operation: Operation, forward: NextLink): Observable<FetchResult> | null {
15
- const token = GlobalStorage.get('session')
16
+ request(
17
+ operation: ApolloLink.Operation,
18
+ forward: ApolloLink.ForwardFunction
19
+ ): Observable<ApolloLink.Result> {
20
+ const validation = validateSessionToken()
16
21
 
17
- if (!token) {
18
- console.warn('QuilttLink attempted to send an unauthenticated Query')
19
- return null
22
+ if (!validation.valid) {
23
+ return new Observable((observer) => {
24
+ observer.error(validation.error)
25
+ })
20
26
  }
21
27
 
28
+ const { token } = validation
29
+
22
30
  operation.setContext(({ headers = {} }) => ({
23
31
  headers: {
24
32
  ...headers,
@@ -1,7 +1,7 @@
1
- import { BatchHttpLink as ApolloBatchHttpLink } from '@apollo/client/link/batch-http/index.js'
1
+ import { BatchHttpLink as ApolloBatchHttpLink } from '@apollo/client/link/batch-http'
2
2
  import crossfetch from 'cross-fetch'
3
3
 
4
- import { endpointGraphQL } from '@/configuration'
4
+ import { endpointGraphQL } from '@/config'
5
5
 
6
6
  // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
7
7
  const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
@@ -1,29 +1,40 @@
1
- import type { ServerError } from '@apollo/client/core'
2
- import { onError } from '@apollo/client/link/error/index.js'
1
+ import { ErrorLink as ApolloErrorLink } from '@apollo/client/link/error'
2
+ import type { GraphQLFormattedError } from 'graphql'
3
3
 
4
4
  import { GlobalStorage } from '@/storage'
5
5
 
6
- export const ErrorLink = onError(({ graphQLErrors, networkError }) => {
7
- if (graphQLErrors) {
8
- graphQLErrors.forEach(({ message, path, extensions }) => {
6
+ export const ErrorLink = new ApolloErrorLink(({ error, result }) => {
7
+ // In Apollo Client 4, errors are consolidated to the 'error' and 'result' properties
8
+
9
+ // Handle GraphQL errors from result
10
+ if (result?.errors) {
11
+ result.errors.forEach((graphQLError: GraphQLFormattedError) => {
12
+ const { message, path, extensions } = graphQLError
9
13
  const formattedPath = Array.isArray(path) ? path.join('.') : (path ?? 'N/A')
10
14
  const parts = [`[Quiltt][GraphQL Error]: ${message}`, `Path: ${formattedPath}`]
11
15
 
12
16
  if (extensions) {
13
17
  if (extensions.code) parts.push(`Code: ${extensions.code}`)
14
18
  if (extensions.errorId) parts.push(`Error ID: ${extensions.errorId}`)
19
+ if (extensions.instruction) parts.push(`Instruction: ${extensions.instruction}`)
20
+ if (extensions.documentationUrl) {
21
+ parts.push(`Docs: ${extensions.documentationUrl}`)
22
+ }
15
23
  }
16
24
 
17
25
  console.warn(parts.join(' | '))
18
26
  })
19
27
  }
20
28
 
21
- if (networkError) {
22
- if ((networkError as ServerError).statusCode === 401) {
23
- console.warn('[Quiltt][Authentication Error]:', networkError)
29
+ // Handle network/server errors
30
+ if (error) {
31
+ if ('statusCode' in error && error.statusCode === 401) {
32
+ console.warn('[Quiltt][Authentication Error]:', error)
24
33
  GlobalStorage.set('session', null)
34
+ } else if ('statusCode' in error) {
35
+ console.warn('[Quiltt][Server Error]:', error)
25
36
  } else {
26
- console.warn('[Quiltt][Network Error]:', networkError)
37
+ console.warn('[Quiltt][Network Error]:', error)
27
38
  }
28
39
  }
29
40
  })
@@ -1,4 +1,4 @@
1
- import { ApolloLink } from '@apollo/client/core/index.js'
1
+ import { ApolloLink } from '@apollo/client/core'
2
2
 
3
3
  export const ForwardableLink = new ApolloLink((operation, forward) => forward(operation))
4
4
 
@@ -1,10 +1,10 @@
1
- import { HttpLink as ApolloHttpLink } from '@apollo/client/link/http/index.js'
1
+ import { HttpLink as ApolloHttpLink } from '@apollo/client/link/http'
2
2
  import crossfetch from 'cross-fetch'
3
3
 
4
4
  // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
5
5
  const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
6
6
 
7
- import { endpointGraphQL } from '@/configuration'
7
+ import { endpointGraphQL } from '@/config'
8
8
 
9
9
  export const HttpLink = new ApolloHttpLink({
10
10
  uri: endpointGraphQL,
@@ -1,8 +1,12 @@
1
- import { RetryLink as ApolloRetryLink } from '@apollo/client/link/retry/index.js'
1
+ import { RetryLink as ApolloRetryLink } from '@apollo/client/link/retry'
2
2
 
3
3
  export const RetryLink = new ApolloRetryLink({
4
4
  attempts: {
5
- retryIf: (error, _operation) => !!error && (!error.statusCode || error.statusCode >= 500),
5
+ retryIf: (error, _operation) => {
6
+ if (!error) return false
7
+ const statusCode = 'statusCode' in error ? (error as any).statusCode : undefined
8
+ return !statusCode || statusCode >= 500
9
+ },
6
10
  },
7
11
  })
8
12
 
@@ -1,5 +1,10 @@
1
- import { ApolloLink } from '@apollo/client/core/index.js'
1
+ import { ApolloLink } from '@apollo/client/core'
2
+ import { Observable } from 'rxjs'
2
3
 
3
- export const TerminatingLink = new ApolloLink(() => null)
4
+ export const TerminatingLink = new ApolloLink(() => {
5
+ return new Observable((observer) => {
6
+ observer.complete()
7
+ })
8
+ })
4
9
 
5
10
  export default TerminatingLink
@@ -1,15 +1,20 @@
1
- import { ApolloLink } from '@apollo/client/core/index.js'
1
+ import { ApolloLink } from '@apollo/client/core'
2
2
 
3
- import { version } from '@/configuration'
3
+ import { version } from '@/config'
4
+ import { extractVersionNumber, getUserAgent } from '@/utils/telemetry'
4
5
 
5
- export const VersionLink = new ApolloLink((operation, forward) => {
6
- operation.setContext(({ headers = {} }) => ({
7
- headers: {
8
- ...headers,
9
- 'Quiltt-Client-Version': version,
10
- },
11
- }))
12
- return forward(operation)
13
- })
6
+ export const createVersionLink = (platformInfo: string) => {
7
+ const versionNumber = extractVersionNumber(version)
8
+ const userAgent = getUserAgent(versionNumber, platformInfo)
14
9
 
15
- export default VersionLink
10
+ return new ApolloLink((operation, forward) => {
11
+ operation.setContext(({ headers = {} }) => ({
12
+ headers: {
13
+ ...headers,
14
+ 'Quiltt-Client-Version': version,
15
+ 'User-Agent': userAgent,
16
+ },
17
+ }))
18
+ return forward(operation)
19
+ })
20
+ }
@@ -1,4 +1,4 @@
1
- import { endpointAuth } from '@/configuration'
1
+ import { endpointAuth } from '@/config'
2
2
 
3
3
  import type { FetchResponse } from './fetchWithRetry'
4
4
  import { fetchWithRetry } from './fetchWithRetry'
@@ -1,4 +1,5 @@
1
- import { endpointRest } from '@/configuration'
1
+ import { endpointRest, version } from '@/config'
2
+ import { extractVersionNumber, getUserAgent } from '@/utils/telemetry'
2
3
 
3
4
  import type { FetchResponse } from './fetchWithRetry'
4
5
  import { fetchWithRetry } from './fetchWithRetry'
@@ -17,11 +18,14 @@ export type ResolvableResponse = FetchResponse<ResolvableData>
17
18
 
18
19
  export class ConnectorsAPI {
19
20
  clientId: string
20
- agent: string
21
+ userAgent: string
21
22
 
22
- constructor(clientId: string, agent = 'web') {
23
+ constructor(
24
+ clientId: string,
25
+ userAgent: string = getUserAgent(extractVersionNumber(version), 'Unknown')
26
+ ) {
23
27
  this.clientId = clientId
24
- this.agent = agent
28
+ this.userAgent = userAgent
25
29
  }
26
30
 
27
31
  /**
@@ -87,7 +91,7 @@ export class ConnectorsAPI {
87
91
  const headers = new Headers()
88
92
  headers.set('Content-Type', 'application/json')
89
93
  headers.set('Accept', 'application/json')
90
- headers.set('Quiltt-SDK-Agent', this.agent)
94
+ headers.set('User-Agent', this.userAgent)
91
95
  headers.set('Authorization', `Bearer ${token}`)
92
96
 
93
97
  return {
@@ -0,0 +1 @@
1
+ export * from './json-web-token'
@@ -1,4 +1,4 @@
1
- import type { Maybe } from './types'
1
+ import type { Maybe } from '../types'
2
2
 
3
3
  export type RegisteredClaims = {
4
4
  iss: string // (issuer): Issuer of the JWT
@@ -1,4 +1,4 @@
1
- import { name as packageName, version as packageVersion } from '../package.json'
1
+ import { name as packageName, version as packageVersion } from '../../package.json'
2
2
 
3
3
  const QUILTT_API_INSECURE = (() => {
4
4
  try {
@@ -0,0 +1 @@
1
+ export * from './configuration'
package/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from './api'
2
- export * from './configuration'
3
- export * from './JsonWebToken'
4
- export * from './Observable'
2
+ export * from './auth'
3
+ export * from './config'
4
+ export * from './observables'
5
5
  export * from './storage'
6
- export * from './Timeoutable'
6
+ export * from './timing'
7
7
  export * from './types'
8
+ export * from './utils'
@@ -0,0 +1 @@
1
+ export * from './observable'
@@ -1,6 +1,6 @@
1
1
  import type { Dispatch, SetStateAction } from 'react'
2
2
 
3
- import type { Maybe } from './types'
3
+ import type { Maybe } from '../types'
4
4
 
5
5
  export type Observer<T> = Dispatch<SetStateAction<Maybe<T> | undefined>>
6
6