@gram-ai/elements 1.27.1 → 1.27.3

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.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Extracts the `exp` claim from a JWT token without verifying the signature.
3
+ * Returns the expiry as a Unix timestamp (seconds), or null if the token
4
+ * is not a valid JWT or has no `exp` claim.
5
+ */
6
+ export function getTokenExpiry(token: string): number | null {
7
+ try {
8
+ const parts = token.split('.')
9
+ if (parts.length !== 3) return null
10
+
11
+ // base64url → base64 → decode
12
+ let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/')
13
+ while (payload.length % 4) payload += '='
14
+
15
+ const json = atob(payload)
16
+ const parsed = JSON.parse(json)
17
+
18
+ if (typeof parsed.exp === 'number') {
19
+ return parsed.exp
20
+ }
21
+ return null
22
+ } catch {
23
+ return null
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Returns true when the token is expired or within `bufferMs` milliseconds
29
+ * of expiry. Fails open (returns false) for non-JWT tokens or tokens
30
+ * without an `exp` claim so they pass through unchanged.
31
+ */
32
+ export function isTokenExpired(
33
+ token: string,
34
+ bufferMs: number = 30_000
35
+ ): boolean {
36
+ const exp = getTokenExpiry(token)
37
+ if (exp === null) return false // fail-open for non-JWT tokens
38
+ return Date.now() >= exp * 1000 - bufferMs
39
+ }