react-token-manager 1.0.0

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/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "react-token-manager",
3
+ "version": "1.0.0",
4
+ "description": "A simple React library to manage JWT tokens",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsup src/index.ts --format esm,cjs --dts",
10
+ "dev": "tsup src/index.ts --watch"
11
+ },
12
+ "author": "",
13
+ "license": "ISC",
14
+ "dependencies": {
15
+ "js-cookie": "^3.0.5",
16
+ "jwt-decode": "^4.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/js-cookie": "^3.0.6",
20
+ "@types/react": "^19.2.14",
21
+ "react": "^19.2.4",
22
+ "tsup": "^8.5.1",
23
+ "typescript": "^5.9.3"
24
+ }
25
+ }
package/src/core.ts ADDED
@@ -0,0 +1,75 @@
1
+ import type { JwtPayload } from 'jwt-decode'
2
+ import * as jwtDecodeModule from 'jwt-decode'
3
+
4
+ // Properly type the callable function
5
+ const jwtDecode: <T = unknown>(token: string) => T =
6
+ (jwtDecodeModule as any).default || jwtDecodeModule
7
+
8
+
9
+ export type StorageType = 'localStorage' | 'sessionStorage' | 'cookie'
10
+
11
+ export interface TokenManagerOptions {
12
+ storage?: StorageType
13
+ tokenKey?: string
14
+ }
15
+
16
+ // ---------------- Global configuration ----------------
17
+ let globalOptions: TokenManagerOptions = {
18
+ storage: 'localStorage',
19
+ tokenKey: 'access_token',
20
+ }
21
+
22
+ export const configureTokenManager = (options: TokenManagerOptions) => {
23
+ globalOptions = { ...globalOptions, ...options }
24
+ }
25
+
26
+ // ---------------- TokenManager Class ----------------
27
+ export class TokenManager {
28
+ private storage: StorageType
29
+ private tokenKey: string
30
+
31
+ constructor(options?: TokenManagerOptions) {
32
+ const opts = options || globalOptions
33
+ this.storage = opts.storage || 'localStorage'
34
+ this.tokenKey = opts.tokenKey || 'access_token'
35
+ }
36
+
37
+ set(token: string) {
38
+ if (this.storage === 'localStorage') localStorage.setItem(this.tokenKey, token)
39
+ else if (this.storage === 'sessionStorage') sessionStorage.setItem(this.tokenKey, token)
40
+ else document.cookie = `${this.tokenKey}=${token}; path=/`
41
+ }
42
+
43
+ get(): string | null {
44
+ if (this.storage === 'localStorage') return localStorage.getItem(this.tokenKey)
45
+ if (this.storage === 'sessionStorage') return sessionStorage.getItem(this.tokenKey)
46
+ const match = document.cookie.match(new RegExp('(^| )' + this.tokenKey + '=([^;]+)'))
47
+ return match ? match[2] : null
48
+ }
49
+
50
+ remove() {
51
+ if (this.storage === 'localStorage') localStorage.removeItem(this.tokenKey)
52
+ else if (this.storage === 'sessionStorage') sessionStorage.removeItem(this.tokenKey)
53
+ else document.cookie = `${this.tokenKey}=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`
54
+ }
55
+
56
+ decode<T = unknown>(): T | null {
57
+ const token = this.get()
58
+ if (!token) return null
59
+ try {
60
+ return jwtDecode<T>(token) // Now callable
61
+ } catch {
62
+ return null
63
+ }
64
+ }
65
+
66
+ isExpired(): boolean {
67
+ const decoded = this.decode<JwtPayload>()
68
+ if (!decoded || !decoded.exp) return true
69
+ return Date.now() >= decoded.exp * 1000
70
+ }
71
+
72
+ getValid(): string | null {
73
+ return this.isExpired() ? null : this.get()
74
+ }
75
+ }
package/src/hook.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { TokenManager, TokenManagerOptions } from './core'
3
+
4
+ export interface UseTokenManagerReturn {
5
+ token: string | null
6
+ setToken: (value: string) => void
7
+ removeToken: () => void
8
+ isExpired: () => boolean
9
+ decode: <T = unknown>() => T | null
10
+ }
11
+
12
+ export const useTokenManager = (
13
+ options?: TokenManagerOptions
14
+ ): UseTokenManagerReturn => {
15
+ const manager = new TokenManager(options)
16
+
17
+ const [token, setTokenState] = useState<string | null>(null)
18
+
19
+ useEffect(() => {
20
+ setTokenState(manager.getValid())
21
+ }, [])
22
+
23
+ const setToken = (value: string) => {
24
+ manager.set(value)
25
+ setTokenState(value)
26
+ }
27
+
28
+ const removeToken = () => {
29
+ manager.remove()
30
+ setTokenState(null)
31
+ }
32
+
33
+ return {
34
+ token,
35
+ setToken,
36
+ removeToken,
37
+ isExpired: () => manager.isExpired(),
38
+ decode: <T = unknown>() => manager.decode<T>(),
39
+ }
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './core'
2
+ export * from './hook'
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState, useMemo } from 'react'
2
+ import { TokenManager } from './core'
3
+
4
+ export interface UseTokenManagerReturn {
5
+ token: string | null
6
+ setToken: (value: string) => void
7
+ removeToken: () => void
8
+ isExpired: () => boolean
9
+ decode: <T = unknown>() => T | null
10
+ }
11
+
12
+ export const useTokenManager = (): UseTokenManagerReturn => {
13
+ const manager = useMemo(() => new TokenManager(), [])
14
+
15
+ const [token, setTokenState] = useState<string | null>(null)
16
+
17
+ useEffect(() => {
18
+ setTokenState(manager.getValid())
19
+ }, [manager])
20
+
21
+ const setToken = (value: string) => {
22
+ manager.set(value)
23
+ setTokenState(value)
24
+ }
25
+
26
+ const removeToken = () => {
27
+ manager.remove()
28
+ setTokenState(null)
29
+ }
30
+
31
+ return {
32
+ token,
33
+ setToken,
34
+ removeToken,
35
+ isExpired: () => manager.isExpired(),
36
+ decode: <T = unknown>() => manager.decode<T>(),
37
+ }
38
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2019",
4
+ "module": "ESNext",
5
+ "declaration": true,
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "moduleResolution": "Node",
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src"]
13
+ }
14
+