@mapnests/gateway-web-sdk 1.0.6 → 1.0.7
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/README.md +199 -20
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A lightweight, production-ready session token management SDK for React and Next.
|
|
|
8
8
|
- HttpOnly cookie support for secure token storage
|
|
9
9
|
- React hook (`useSession`) for seamless integration
|
|
10
10
|
- Singleton pattern ensuring a single session instance across your app
|
|
11
|
-
- Zero dependencies (
|
|
11
|
+
- Zero runtime dependencies (`react` and `react-dom` as peer dependencies)
|
|
12
12
|
- Next.js compatible (SSR-safe)
|
|
13
13
|
- Built-in Fetch and Axios interceptors for automatic 401 handling
|
|
14
14
|
- Full TypeScript definitions included
|
|
@@ -25,9 +25,11 @@ npm install @mapnests/gateway-web-sdk
|
|
|
25
25
|
|
|
26
26
|
Choose one of the three approaches below based on your preferred HTTP client. All three approaches share the same **Step 1** (environment setup) and **Step 2** (session initialization).
|
|
27
27
|
|
|
28
|
+
> **Next.js users:** Skip the Common Setup below and go directly to the [Next.js Integration](#nextjs-integration) section, which provides its own Steps 1–2. Then return here for Approach A, B, or C.
|
|
29
|
+
|
|
28
30
|
---
|
|
29
31
|
|
|
30
|
-
### Common Setup (
|
|
32
|
+
### Common Setup (Vite / CRA / React Apps)
|
|
31
33
|
|
|
32
34
|
#### Step 1 — Environment Variables
|
|
33
35
|
|
|
@@ -45,8 +47,6 @@ VITE_TOKEN_COOKIE_NAME=token
|
|
|
45
47
|
| `VITE_BOOTSTRAP_PATH` | Path to the session bootstrap endpoint |
|
|
46
48
|
| `VITE_TOKEN_COOKIE_NAME` | Name of the token cookie set by your server |
|
|
47
49
|
|
|
48
|
-
> If you are using Next.js, prefix with `NEXT_PUBLIC_` instead of `VITE_`.
|
|
49
|
-
|
|
50
50
|
Then create a config helper to read these values:
|
|
51
51
|
|
|
52
52
|
```js
|
|
@@ -297,46 +297,148 @@ export default function Dashboard() {
|
|
|
297
297
|
|
|
298
298
|
## Next.js Integration
|
|
299
299
|
|
|
300
|
-
|
|
300
|
+
> **Note:** For Next.js, use `NEXT_PUBLIC_` prefixed environment variables instead of `VITE_`, and replace the common **Step 1** config helper and **Step 2** initialization with the Next.js-specific setup below.
|
|
301
|
+
|
|
302
|
+
### Step 1 — Environment Variables
|
|
303
|
+
|
|
304
|
+
```env
|
|
305
|
+
NEXT_PUBLIC_API_BASE_URL=https://your-gateway.example.com
|
|
306
|
+
NEXT_PUBLIC_BOOTSTRAP_PATH=/api/session/bootstrap
|
|
307
|
+
NEXT_PUBLIC_TOKEN_COOKIE_NAME=token
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Step 2 — Config Helper
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
// src/config.js
|
|
314
|
+
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
|
|
315
|
+
const BOOTSTRAP_PATH = process.env.NEXT_PUBLIC_BOOTSTRAP_PATH;
|
|
316
|
+
const TOKEN_COOKIE_NAME = process.env.NEXT_PUBLIC_TOKEN_COOKIE_NAME;
|
|
317
|
+
|
|
318
|
+
if (!API_BASE_URL) throw new Error('NEXT_PUBLIC_API_BASE_URL is not defined');
|
|
319
|
+
if (!BOOTSTRAP_PATH) throw new Error('NEXT_PUBLIC_BOOTSTRAP_PATH is not defined');
|
|
320
|
+
if (!TOKEN_COOKIE_NAME) throw new Error('NEXT_PUBLIC_TOKEN_COOKIE_NAME is not defined');
|
|
321
|
+
|
|
322
|
+
export { API_BASE_URL, BOOTSTRAP_PATH, TOKEN_COOKIE_NAME };
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### App Router
|
|
326
|
+
|
|
327
|
+
#### Step 3 — Create a Session Provider
|
|
328
|
+
|
|
329
|
+
Create a client component that initializes the session. This keeps the root layout as a Server Component, preserving the benefits of React Server Components.
|
|
301
330
|
|
|
302
331
|
```jsx
|
|
332
|
+
// app/providers/SessionProvider.jsx
|
|
303
333
|
'use client';
|
|
304
334
|
|
|
305
335
|
import { useEffect } from 'react';
|
|
306
336
|
import { SessionManager } from '@mapnests/gateway-web-sdk';
|
|
337
|
+
import { API_BASE_URL, BOOTSTRAP_PATH, TOKEN_COOKIE_NAME } from '@/src/config';
|
|
307
338
|
|
|
308
|
-
|
|
339
|
+
const sessionManager = SessionManager.getInstance();
|
|
340
|
+
|
|
341
|
+
export default function SessionProvider({ children }) {
|
|
309
342
|
useEffect(() => {
|
|
310
|
-
const sessionManager = SessionManager.getInstance();
|
|
311
343
|
sessionManager.configure({
|
|
312
|
-
bootstrapUrl: `${
|
|
313
|
-
tokenCookieName:
|
|
344
|
+
bootstrapUrl: `${API_BASE_URL}${BOOTSTRAP_PATH}`,
|
|
345
|
+
tokenCookieName: TOKEN_COOKIE_NAME,
|
|
314
346
|
});
|
|
315
|
-
sessionManager.initialize()
|
|
347
|
+
sessionManager.initialize().catch(err =>
|
|
348
|
+
console.error('Failed to initialize session:', err)
|
|
349
|
+
);
|
|
316
350
|
}, []);
|
|
317
351
|
|
|
352
|
+
return children;
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### Step 4 — Add the Provider to the Root Layout
|
|
357
|
+
|
|
358
|
+
The root layout stays as a Server Component — do **not** add `'use client'` here.
|
|
359
|
+
|
|
360
|
+
```jsx
|
|
361
|
+
// app/layout.js
|
|
362
|
+
import SessionProvider from './providers/SessionProvider';
|
|
363
|
+
|
|
364
|
+
export default function RootLayout({ children }) {
|
|
318
365
|
return (
|
|
319
366
|
<html lang="en">
|
|
320
|
-
<body>
|
|
367
|
+
<body>
|
|
368
|
+
<SessionProvider>{children}</SessionProvider>
|
|
369
|
+
</body>
|
|
321
370
|
</html>
|
|
322
371
|
);
|
|
323
372
|
}
|
|
324
373
|
```
|
|
325
374
|
|
|
326
|
-
|
|
375
|
+
#### Step 5 — Create an API Layer and Use in a Page
|
|
376
|
+
|
|
377
|
+
After your router setup is complete, create an API layer using any of **Approach A / B / C** from the [Implementation Guide](#implementation-guide) above. The only change needed is to replace `import.meta.env.VITE_*` references with imports from your `src/config.js`.
|
|
378
|
+
|
|
379
|
+
For example, using Approach A (Fetch Interceptor):
|
|
380
|
+
|
|
381
|
+
```js
|
|
382
|
+
// src/api.js
|
|
383
|
+
import { fetchInterceptor } from '@mapnests/gateway-web-sdk';
|
|
384
|
+
import { API_BASE_URL } from './config';
|
|
385
|
+
|
|
386
|
+
export const getUser = () =>
|
|
387
|
+
fetchInterceptor(`${API_BASE_URL}/api/user`);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Then use it in a page component. Page components that use hooks must be client components.
|
|
391
|
+
|
|
392
|
+
```jsx
|
|
393
|
+
// app/dashboard/page.jsx
|
|
394
|
+
'use client';
|
|
395
|
+
|
|
396
|
+
import { useEffect, useState } from 'react';
|
|
397
|
+
import { useSession } from '@mapnests/gateway-web-sdk';
|
|
398
|
+
import { getUser } from '@/src/api';
|
|
399
|
+
|
|
400
|
+
export default function DashboardPage() {
|
|
401
|
+
const { isInitialized, isLoading, error } = useSession();
|
|
402
|
+
const [user, setUser] = useState(null);
|
|
403
|
+
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
if (!isInitialized) return;
|
|
406
|
+
|
|
407
|
+
getUser()
|
|
408
|
+
.then(res => res.json())
|
|
409
|
+
.then(setUser)
|
|
410
|
+
.catch(err => console.error('Failed to fetch user:', err));
|
|
411
|
+
}, [isInitialized]);
|
|
412
|
+
|
|
413
|
+
if (isLoading) return <p>Loading session...</p>;
|
|
414
|
+
if (error) return <p>Session error: {error}</p>;
|
|
415
|
+
if (!user) return <p>Loading data...</p>;
|
|
416
|
+
|
|
417
|
+
return <pre>{JSON.stringify(user, null, 2)}</pre>;
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Pages Router
|
|
422
|
+
|
|
423
|
+
#### Step 3 — Initialize in `_app.js`
|
|
327
424
|
|
|
328
425
|
```jsx
|
|
426
|
+
// pages/_app.js
|
|
329
427
|
import { useEffect } from 'react';
|
|
330
428
|
import { SessionManager } from '@mapnests/gateway-web-sdk';
|
|
429
|
+
import { API_BASE_URL, BOOTSTRAP_PATH, TOKEN_COOKIE_NAME } from '../src/config';
|
|
430
|
+
|
|
431
|
+
const sessionManager = SessionManager.getInstance();
|
|
331
432
|
|
|
332
433
|
function MyApp({ Component, pageProps }) {
|
|
333
434
|
useEffect(() => {
|
|
334
|
-
const sessionManager = SessionManager.getInstance();
|
|
335
435
|
sessionManager.configure({
|
|
336
|
-
bootstrapUrl: `${
|
|
337
|
-
tokenCookieName:
|
|
436
|
+
bootstrapUrl: `${API_BASE_URL}${BOOTSTRAP_PATH}`,
|
|
437
|
+
tokenCookieName: TOKEN_COOKIE_NAME,
|
|
338
438
|
});
|
|
339
|
-
sessionManager.initialize()
|
|
439
|
+
sessionManager.initialize().catch(err =>
|
|
440
|
+
console.error('Failed to initialize session:', err)
|
|
441
|
+
);
|
|
340
442
|
}, []);
|
|
341
443
|
|
|
342
444
|
return <Component {...pageProps} />;
|
|
@@ -345,6 +447,37 @@ function MyApp({ Component, pageProps }) {
|
|
|
345
447
|
export default MyApp;
|
|
346
448
|
```
|
|
347
449
|
|
|
450
|
+
#### Step 4 — Create an API Layer and Use in a Page
|
|
451
|
+
|
|
452
|
+
Same as the App Router — create an `src/api.js` using any of **Approach A / B / C**, then use it in your page:
|
|
453
|
+
|
|
454
|
+
```jsx
|
|
455
|
+
// pages/dashboard.jsx
|
|
456
|
+
import { useEffect, useState } from 'react';
|
|
457
|
+
import { useSession } from '@mapnests/gateway-web-sdk';
|
|
458
|
+
import { getUser } from '../src/api';
|
|
459
|
+
|
|
460
|
+
export default function Dashboard() {
|
|
461
|
+
const { isInitialized, isLoading, error } = useSession();
|
|
462
|
+
const [user, setUser] = useState(null);
|
|
463
|
+
|
|
464
|
+
useEffect(() => {
|
|
465
|
+
if (!isInitialized) return;
|
|
466
|
+
|
|
467
|
+
getUser()
|
|
468
|
+
.then(res => res.json())
|
|
469
|
+
.then(setUser)
|
|
470
|
+
.catch(err => console.error('Failed to fetch user:', err));
|
|
471
|
+
}, [isInitialized]);
|
|
472
|
+
|
|
473
|
+
if (isLoading) return <p>Loading session...</p>;
|
|
474
|
+
if (error) return <p>Session error: {error}</p>;
|
|
475
|
+
if (!user) return <p>Loading data...</p>;
|
|
476
|
+
|
|
477
|
+
return <pre>{JSON.stringify(user, null, 2)}</pre>;
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
348
481
|
---
|
|
349
482
|
|
|
350
483
|
## API Reference
|
|
@@ -366,9 +499,10 @@ React hook for session management.
|
|
|
366
499
|
isInitialized: boolean;
|
|
367
500
|
isLoading: boolean;
|
|
368
501
|
error: string | null;
|
|
369
|
-
lastRefreshTime: number;
|
|
370
|
-
nextRefreshTime: number;
|
|
371
|
-
timeUntilRefresh: number;
|
|
502
|
+
lastRefreshTime: number | null;
|
|
503
|
+
nextRefreshTime: number | null;
|
|
504
|
+
timeUntilRefresh: number | null;
|
|
505
|
+
initializationFailed: boolean;
|
|
372
506
|
refresh: () => Promise<void>;
|
|
373
507
|
initialize: () => Promise<void>;
|
|
374
508
|
}
|
|
@@ -423,7 +557,7 @@ Returns the token value from the named cookie, or `null`.
|
|
|
423
557
|
|
|
424
558
|
#### `shouldUseTokenHeader()`
|
|
425
559
|
|
|
426
|
-
Returns `true` if the token should be sent as a request header (
|
|
560
|
+
Returns `true` if the token should be sent as a request header (i.e. when the app is served over HTTP, not HTTPS).
|
|
427
561
|
|
|
428
562
|
#### `isRefreshing()`
|
|
429
563
|
|
|
@@ -437,6 +571,22 @@ Returns a promise that resolves when the in-progress refresh completes.
|
|
|
437
571
|
|
|
438
572
|
Returns the current session state object.
|
|
439
573
|
|
|
574
|
+
```typescript
|
|
575
|
+
{
|
|
576
|
+
isInitialized: boolean;
|
|
577
|
+
isLoading: boolean;
|
|
578
|
+
lastRefreshTime: number | null;
|
|
579
|
+
nextRefreshTime: number | null;
|
|
580
|
+
tokenExpiry: number | null;
|
|
581
|
+
error: string | null;
|
|
582
|
+
errorCode: string | null;
|
|
583
|
+
initializationFailed: boolean;
|
|
584
|
+
timeUntilRefresh: number | null;
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
> **Note:** `getSessionStatus()` returns `tokenExpiry` and `errorCode` which are not exposed by the `useSession` hook. Use this method directly if you need those fields.
|
|
589
|
+
|
|
440
590
|
#### `subscribe(listener)`
|
|
441
591
|
|
|
442
592
|
Subscribe to session state changes. Returns an unsubscribe function.
|
|
@@ -470,6 +620,35 @@ import { setupAxiosInterceptor } from '@mapnests/gateway-web-sdk';
|
|
|
470
620
|
const api = setupAxiosInterceptor(axios.create({ baseURL: '/api' }));
|
|
471
621
|
```
|
|
472
622
|
|
|
623
|
+
### Error Classes
|
|
624
|
+
|
|
625
|
+
The SDK exports custom error classes for typed error handling:
|
|
626
|
+
|
|
627
|
+
```javascript
|
|
628
|
+
import {
|
|
629
|
+
SessionError, // Base error class (code, details)
|
|
630
|
+
ConfigurationError, // Invalid configuration (code: 'CONFIGURATION_ERROR')
|
|
631
|
+
BootstrapError, // Bootstrap API failure (code: 'BOOTSTRAP_ERROR')
|
|
632
|
+
NetworkError, // Network-level failure (code: 'NETWORK_ERROR')
|
|
633
|
+
SSRError, // Called in non-browser environment (code: 'SSR_ERROR')
|
|
634
|
+
} from '@mapnests/gateway-web-sdk';
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
All errors extend `SessionError`, which provides `code` (string) and `details` (object) properties.
|
|
638
|
+
|
|
639
|
+
### `logger` and `LOG_LEVELS`
|
|
640
|
+
|
|
641
|
+
The SDK's internal logger is exported for advanced use (e.g. setting log level independently of `configure()`):
|
|
642
|
+
|
|
643
|
+
```javascript
|
|
644
|
+
import { logger, LOG_LEVELS } from '@mapnests/gateway-web-sdk';
|
|
645
|
+
|
|
646
|
+
logger.setLevel('DEBUG'); // or logger.setLevel(LOG_LEVELS.DEBUG)
|
|
647
|
+
logger.info('Custom log'); // [SessionManager] Custom log
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
Available levels: `NONE` (0), `ERROR` (1), `WARN` (2, default), `INFO` (3), `DEBUG` (4).
|
|
651
|
+
|
|
473
652
|
---
|
|
474
653
|
|
|
475
654
|
## Security Notice
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapnests/gateway-web-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Session token management SDK with automatic refresh for React/Next.js applications",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"react": ">=16.8.0",
|
|
46
|
-
"react-dom": ">=16.8.0"
|
|
46
|
+
"react-dom": ">=16.8.0",
|
|
47
|
+
"axios": ">=0.21.0"
|
|
47
48
|
},
|
|
48
49
|
"peerDependenciesMeta": {
|
|
49
50
|
"axios": {
|