@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.
Files changed (2) hide show
  1. package/README.md +199 -20
  2. 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 (only React as a peer dependency)
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 (All Approaches)
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
- ### App Router (`app/layout.js`)
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
- export default function RootLayout({ children }) {
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: `${process.env.NEXT_PUBLIC_API_BASE_URL}${process.env.NEXT_PUBLIC_BOOTSTRAP_PATH}`,
313
- tokenCookieName: process.env.NEXT_PUBLIC_TOKEN_COOKIE_NAME,
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>{children}</body>
367
+ <body>
368
+ <SessionProvider>{children}</SessionProvider>
369
+ </body>
321
370
  </html>
322
371
  );
323
372
  }
324
373
  ```
325
374
 
326
- ### Pages Router (`pages/_app.js`)
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: `${process.env.NEXT_PUBLIC_API_BASE_URL}${process.env.NEXT_PUBLIC_BOOTSTRAP_PATH}`,
337
- tokenCookieName: process.env.NEXT_PUBLIC_TOKEN_COOKIE_NAME,
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 (manual token mode or HTTP protocol).
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.6",
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": {