@quiltt/core 5.0.0 → 5.0.2
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 +20 -0
- package/README.md +19 -12
- package/dist/api/browser.cjs +14 -0
- package/dist/api/browser.d.ts +128 -0
- package/dist/api/browser.js +12 -0
- package/dist/api/graphql/SubscriptionLink-12s-ufJBKwu1.js +149 -0
- package/dist/api/graphql/SubscriptionLink-12s-wjkChfxO.cjs +150 -0
- package/dist/api/graphql/index.cjs +218 -0
- package/dist/api/graphql/index.d.ts +82 -0
- package/dist/api/graphql/index.js +184 -0
- package/dist/api/index.cjs +26 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +3 -0
- package/dist/api/rest/index.cjs +225 -0
- package/dist/api/rest/index.d.ts +128 -0
- package/dist/api/rest/index.js +217 -0
- package/dist/auth/index.cjs +21 -0
- package/dist/auth/index.d.ts +29 -0
- package/dist/auth/index.js +19 -0
- package/dist/config/index.cjs +44 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.js +36 -0
- package/dist/index.cjs +61 -0
- package/dist/index.d.ts +8 -524
- package/dist/index.js +8 -449
- package/dist/observables/index.cjs +30 -0
- package/dist/observables/index.d.ts +21 -0
- package/dist/observables/index.js +28 -0
- package/dist/storage/index.cjs +272 -0
- package/dist/storage/index.d.ts +91 -0
- package/dist/{SubscriptionLink-12s-C2VbF8Tf.js → storage/index.js} +2 -139
- package/dist/timing/index.cjs +30 -0
- package/dist/timing/index.d.ts +15 -0
- package/dist/timing/index.js +28 -0
- package/dist/types.cjs +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.js +1 -0
- package/dist/utils/index.cjs +61 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.js +57 -0
- package/package.json +62 -6
- package/src/api/graphql/client.ts +1 -1
- package/src/api/graphql/links/ActionCableLink.ts +7 -6
- package/src/api/graphql/links/AuthLink.ts +13 -9
- package/src/api/graphql/links/BatchHttpLink.ts +1 -1
- package/src/api/graphql/links/ErrorLink.ts +4 -0
- package/src/api/graphql/links/HttpLink.ts +1 -1
- package/src/api/graphql/links/VersionLink.ts +1 -1
- package/src/api/rest/auth.ts +1 -1
- package/src/api/rest/connectors.ts +1 -1
- package/src/auth/index.ts +1 -0
- package/src/{JsonWebToken.ts → auth/json-web-token.ts} +1 -1
- package/src/{configuration.ts → config/configuration.ts} +1 -1
- package/src/config/index.ts +1 -0
- package/src/index.ts +5 -5
- package/src/observables/index.ts +1 -0
- package/src/{Observable.ts → observables/observable.ts} +1 -1
- package/src/storage/Local.ts +1 -1
- package/src/storage/Memory.ts +2 -2
- package/src/storage/Storage.ts +1 -1
- package/src/timing/index.ts +1 -0
- package/src/{Timeoutable.ts → timing/timeoutable.ts} +1 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/token-validation.ts +67 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts version number from formatted version string
|
|
5
|
+
* @param formattedVersion - Formatted version like "@quiltt/core: v4.5.1"
|
|
6
|
+
* @returns Version number like "4.5.1" or "unknown" if not found
|
|
7
|
+
*/ const extractVersionNumber = (formattedVersion)=>{
|
|
8
|
+
// Find the 'v' prefix and extract version after it
|
|
9
|
+
const vIndex = formattedVersion.indexOf('v');
|
|
10
|
+
if (vIndex === -1) return 'unknown';
|
|
11
|
+
const versionPart = formattedVersion.substring(vIndex + 1);
|
|
12
|
+
const parts = versionPart.split('.');
|
|
13
|
+
// Validate we have at least major.minor.patch
|
|
14
|
+
if (parts.length < 3) return 'unknown';
|
|
15
|
+
// Extract numeric parts (handles cases like "4.5.1-beta")
|
|
16
|
+
const major = parts[0].match(/^\d+/)?.[0];
|
|
17
|
+
const minor = parts[1].match(/^\d+/)?.[0];
|
|
18
|
+
const patch = parts[2].match(/^\d+/)?.[0];
|
|
19
|
+
if (!major || !minor || !patch) return 'unknown';
|
|
20
|
+
return `${major}.${minor}.${patch}`;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Generates a User-Agent string following standard format
|
|
24
|
+
* Format: Quiltt/<version> (<platform-info>)
|
|
25
|
+
*/ const getUserAgent = (sdkVersion, platformInfo)=>{
|
|
26
|
+
return `Quiltt/${sdkVersion} (${platformInfo})`;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Detects browser information from user agent string
|
|
30
|
+
* Returns browser name and version, or 'Unknown' if not detected
|
|
31
|
+
*/ const getBrowserInfo = ()=>{
|
|
32
|
+
if (typeof navigator === 'undefined' || !navigator.userAgent) {
|
|
33
|
+
return 'Unknown';
|
|
34
|
+
}
|
|
35
|
+
const ua = navigator.userAgent;
|
|
36
|
+
// Edge (must be checked before Chrome)
|
|
37
|
+
if (ua.includes('Edg/')) {
|
|
38
|
+
const version = ua.match(/Edg\/(\d+)/)?.[1] || 'Unknown';
|
|
39
|
+
return `Edge/${version}`;
|
|
40
|
+
}
|
|
41
|
+
// Chrome
|
|
42
|
+
if (ua.includes('Chrome/') && !ua.includes('Edg/')) {
|
|
43
|
+
const version = ua.match(/Chrome\/(\d+)/)?.[1] || 'Unknown';
|
|
44
|
+
return `Chrome/${version}`;
|
|
45
|
+
}
|
|
46
|
+
// Safari (must be checked after Chrome)
|
|
47
|
+
if (ua.includes('Safari/') && !ua.includes('Chrome/')) {
|
|
48
|
+
const version = ua.match(/Version\/(\d+)/)?.[1] || 'Unknown';
|
|
49
|
+
return `Safari/${version}`;
|
|
50
|
+
}
|
|
51
|
+
// Firefox
|
|
52
|
+
if (ua.includes('Firefox/')) {
|
|
53
|
+
const version = ua.match(/Firefox\/(\d+)/)?.[1] || 'Unknown';
|
|
54
|
+
return `Firefox/${version}`;
|
|
55
|
+
}
|
|
56
|
+
return 'Unknown';
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
exports.extractVersionNumber = extractVersionNumber;
|
|
60
|
+
exports.getBrowserInfo = getBrowserInfo;
|
|
61
|
+
exports.getUserAgent = getUserAgent;
|
|
@@ -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": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
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
|
-
"
|
|
24
|
-
"
|
|
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",
|
|
@@ -40,10 +96,10 @@
|
|
|
40
96
|
"rxjs": "^7.8.2"
|
|
41
97
|
},
|
|
42
98
|
"devDependencies": {
|
|
43
|
-
"@biomejs/biome": "2.3.
|
|
44
|
-
"@types/node": "24.10.
|
|
99
|
+
"@biomejs/biome": "2.3.14",
|
|
100
|
+
"@types/node": "24.10.10",
|
|
45
101
|
"@types/rails__actioncable": "8.0.3",
|
|
46
|
-
"@types/react": "19.2.
|
|
102
|
+
"@types/react": "19.2.11",
|
|
47
103
|
"bunchee": "6.9.4",
|
|
48
104
|
"rimraf": "6.1.2",
|
|
49
105
|
"typescript": "5.9.3"
|
|
@@ -5,8 +5,8 @@ import { createConsumer } from '@rails/actioncable'
|
|
|
5
5
|
import { print } from 'graphql'
|
|
6
6
|
import { Observable } from 'rxjs'
|
|
7
7
|
|
|
8
|
-
import { endpointWebsockets } from '@/
|
|
9
|
-
import {
|
|
8
|
+
import { endpointWebsockets } from '@/config'
|
|
9
|
+
import { validateSessionToken } from '@/utils/token-validation'
|
|
10
10
|
|
|
11
11
|
type RequestResult = ApolloLink.Result<{ [key: string]: unknown }>
|
|
12
12
|
type ConnectionParams = object | ((operation: ApolloLink.Operation) => object)
|
|
@@ -43,15 +43,16 @@ class ActionCableLink extends ApolloLink {
|
|
|
43
43
|
operation: ApolloLink.Operation,
|
|
44
44
|
_next: ApolloLink.ForwardFunction
|
|
45
45
|
): Observable<RequestResult> {
|
|
46
|
-
const
|
|
46
|
+
const validation = validateSessionToken('for subscription')
|
|
47
47
|
|
|
48
|
-
if (!
|
|
49
|
-
console.warn('QuilttClient attempted to send an unauthenticated Subscription')
|
|
48
|
+
if (!validation.valid) {
|
|
50
49
|
return new Observable((observer) => {
|
|
51
|
-
observer.error(
|
|
50
|
+
observer.error(validation.error)
|
|
52
51
|
})
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
const { token } = validation
|
|
55
|
+
|
|
55
56
|
if (!this.cables[token]) {
|
|
56
57
|
this.cables[token] = createConsumer(endpointWebsockets + (token ? `?token=${token}` : ''))
|
|
57
58
|
}
|
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import { ApolloLink } from '@apollo/client/core'
|
|
2
2
|
import { Observable } from 'rxjs'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { validateSessionToken } from '@/utils/token-validation'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
|
11
14
|
*/
|
|
12
15
|
export class AuthLink extends ApolloLink {
|
|
13
16
|
request(
|
|
14
17
|
operation: ApolloLink.Operation,
|
|
15
18
|
forward: ApolloLink.ForwardFunction
|
|
16
19
|
): Observable<ApolloLink.Result> {
|
|
17
|
-
const
|
|
20
|
+
const validation = validateSessionToken()
|
|
18
21
|
|
|
19
|
-
if (!
|
|
20
|
-
console.warn('QuilttLink attempted to send an unauthenticated Query')
|
|
22
|
+
if (!validation.valid) {
|
|
21
23
|
return new Observable((observer) => {
|
|
22
|
-
observer.error(
|
|
24
|
+
observer.error(validation.error)
|
|
23
25
|
})
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
const { token } = validation
|
|
29
|
+
|
|
26
30
|
operation.setContext(({ headers = {} }) => ({
|
|
27
31
|
headers: {
|
|
28
32
|
...headers,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BatchHttpLink as ApolloBatchHttpLink } from '@apollo/client/link/batch-http'
|
|
2
2
|
import crossfetch from 'cross-fetch'
|
|
3
3
|
|
|
4
|
-
import { endpointGraphQL } from '@/
|
|
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
|
|
@@ -16,6 +16,10 @@ export const ErrorLink = new ApolloErrorLink(({ error, result }) => {
|
|
|
16
16
|
if (extensions) {
|
|
17
17
|
if (extensions.code) parts.push(`Code: ${extensions.code}`)
|
|
18
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
|
+
}
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
console.warn(parts.join(' | '))
|
|
@@ -4,7 +4,7 @@ import crossfetch from 'cross-fetch'
|
|
|
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 '@/
|
|
7
|
+
import { endpointGraphQL } from '@/config'
|
|
8
8
|
|
|
9
9
|
export const HttpLink = new ApolloHttpLink({
|
|
10
10
|
uri: endpointGraphQL,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ApolloLink } from '@apollo/client/core'
|
|
2
2
|
|
|
3
|
-
import { version } from '@/
|
|
3
|
+
import { version } from '@/config'
|
|
4
4
|
import { extractVersionNumber, getUserAgent } from '@/utils/telemetry'
|
|
5
5
|
|
|
6
6
|
export const createVersionLink = (platformInfo: string) => {
|
package/src/api/rest/auth.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './json-web-token'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './configuration'
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from './api'
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
2
|
+
export * from './auth'
|
|
3
|
+
export * from './config'
|
|
4
|
+
export * from './observables'
|
|
5
5
|
export * from './storage'
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './timing'
|
|
7
7
|
export * from './types'
|
|
8
|
-
export * from './utils
|
|
8
|
+
export * from './utils'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './observable'
|
package/src/storage/Local.ts
CHANGED
package/src/storage/Memory.ts
CHANGED
package/src/storage/Storage.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './timeoutable'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './telemetry'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql'
|
|
2
|
+
|
|
3
|
+
import { JsonWebTokenParse } from '@/auth/json-web-token'
|
|
4
|
+
import { GlobalStorage } from '@/storage'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Result of token validation
|
|
8
|
+
*/
|
|
9
|
+
export type TokenValidationResult =
|
|
10
|
+
| { valid: true; token: string }
|
|
11
|
+
| { valid: false; error: GraphQLError }
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validates the session token from GlobalStorage.
|
|
15
|
+
*
|
|
16
|
+
* This function:
|
|
17
|
+
* - Checks if a session token exists
|
|
18
|
+
* - Validates token expiration
|
|
19
|
+
* - Clears expired tokens from storage (triggers observers and React re-renders)
|
|
20
|
+
* - Returns appropriate GraphQL errors for authentication failures
|
|
21
|
+
*
|
|
22
|
+
* @param errorMessagePrefix - Optional prefix for error messages (e.g., "for subscription")
|
|
23
|
+
* @returns TokenValidationResult indicating whether the token is valid or providing an error
|
|
24
|
+
*/
|
|
25
|
+
export function validateSessionToken(errorMessagePrefix = ''): TokenValidationResult {
|
|
26
|
+
const token = GlobalStorage.get('session')
|
|
27
|
+
|
|
28
|
+
if (!token) {
|
|
29
|
+
return {
|
|
30
|
+
valid: false,
|
|
31
|
+
error: new GraphQLError(
|
|
32
|
+
`No session token available${errorMessagePrefix ? ` ${errorMessagePrefix}` : ''}`,
|
|
33
|
+
{
|
|
34
|
+
extensions: {
|
|
35
|
+
code: 'UNAUTHENTICATED',
|
|
36
|
+
reason: 'NO_TOKEN',
|
|
37
|
+
documentationUrl: 'https://www.quiltt.dev/authentication#session-tokens',
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if token is expired
|
|
45
|
+
const jwt = JsonWebTokenParse(token)
|
|
46
|
+
if (jwt?.claims.exp) {
|
|
47
|
+
const nowInSeconds = Math.floor(Date.now() / 1000)
|
|
48
|
+
if (jwt.claims.exp < nowInSeconds) {
|
|
49
|
+
// Clear expired token - this triggers observers and React re-renders
|
|
50
|
+
GlobalStorage.set('session', null)
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
valid: false,
|
|
54
|
+
error: new GraphQLError('Session token has expired', {
|
|
55
|
+
extensions: {
|
|
56
|
+
code: 'UNAUTHENTICATED',
|
|
57
|
+
reason: 'TOKEN_EXPIRED',
|
|
58
|
+
expiredAt: jwt.claims.exp,
|
|
59
|
+
documentationUrl: 'https://www.quiltt.dev/authentication#session-tokens',
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { valid: true, token }
|
|
67
|
+
}
|