najm-auth 1.1.23 → 1.1.24

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.
@@ -153,4 +153,87 @@ declare class FetchClient {
153
153
  private parseBody;
154
154
  }
155
155
 
156
- export { AuthError as A, type DecodedToken as D, FetchClient as F, type RetryConfig as R, type SyncPayload as S, type TabSyncMessage as T, type AuthClientConfig as a, type AuthEventMap as b, type AuthState as c, type AuthUser as d, type AuthEvent as e, type AuthEventHandler as f, type ServerResponse as g, type TokenPair as h, type RequestOptions as i };
156
+ interface HydrateSession {
157
+ user: AuthUser | null;
158
+ accessToken?: string | null;
159
+ roles?: string[];
160
+ permissions?: string[];
161
+ }
162
+ declare class NajmAuthClient {
163
+ private config;
164
+ private static readonly MAX_REFRESH_FAILURES;
165
+ private static readonly CIRCUIT_RESET_MS;
166
+ private state;
167
+ private refreshTimer;
168
+ private refreshCircuitTimer;
169
+ private refreshPromise;
170
+ private fetchUserPromise;
171
+ private refreshFailures;
172
+ private _hydrated;
173
+ private listeners;
174
+ private eventListeners;
175
+ private tabSync;
176
+ api: FetchClient;
177
+ private readonly prefix;
178
+ private readonly threshold;
179
+ constructor(config: AuthClientConfig);
180
+ login(credentials: {
181
+ email: string;
182
+ password: string;
183
+ }): Promise<AuthUser>;
184
+ register(data: Record<string, unknown>): Promise<AuthUser>;
185
+ logout(): Promise<void>;
186
+ refresh(): Promise<void>;
187
+ fetchUser(): Promise<AuthUser | null>;
188
+ private _doFetchUser;
189
+ forgotPassword(data: {
190
+ email: string;
191
+ }): Promise<void>;
192
+ changePassword(data: {
193
+ currentPassword: string;
194
+ newPassword: string;
195
+ }): Promise<void>;
196
+ resetPassword(data: {
197
+ token: string;
198
+ newPassword: string;
199
+ }): Promise<void>;
200
+ can(permission: string): boolean;
201
+ hasRole(role: string): boolean;
202
+ hasAnyRole(roles: string[]): boolean;
203
+ hasPermission(permission: string): boolean;
204
+ getUser(): AuthUser | null;
205
+ getAccessToken(): string | null;
206
+ isAuthenticated(): boolean;
207
+ getState(): AuthState;
208
+ /**
209
+ * Hydrate the client with a session resolved server-side.
210
+ * Use to skip the initial loading flicker on SSR-rendered pages.
211
+ * Safe to call multiple times — subsequent calls are no-ops.
212
+ */
213
+ hydrate(session: HydrateSession | null): void;
214
+ isHydrated(): boolean;
215
+ on<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
216
+ off<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
217
+ subscribe(listener: (state: AuthState) => void): () => void;
218
+ destroy(): void;
219
+ private _refreshWithCircuit;
220
+ private _doRefresh;
221
+ private handleUnauthorized;
222
+ private applyTokens;
223
+ private scheduleRefresh;
224
+ private clearRefreshTimer;
225
+ private clearRefreshCircuitTimer;
226
+ private resetRefreshFailures;
227
+ private registerRefreshFailure;
228
+ private resetState;
229
+ private getSyncPayload;
230
+ private handleTabMessage;
231
+ private notify;
232
+ private emit;
233
+ }
234
+ /**
235
+ * Factory function to create an auth client.
236
+ */
237
+ declare function createAuthClient(config: AuthClientConfig): NajmAuthClient;
238
+
239
+ export { AuthError as A, type DecodedToken as D, FetchClient as F, type HydrateSession as H, NajmAuthClient as N, type RetryConfig as R, type SyncPayload as S, type TabSyncMessage as T, type AuthClientConfig as a, type AuthEventMap as b, createAuthClient as c, type AuthState as d, type AuthUser as e, type AuthEvent as f, type AuthEventHandler as g, type ServerResponse as h, type TokenPair as i, type RequestOptions as j };
@@ -1,6 +1,5 @@
1
- export { H as HydrateSession, N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-CZHKl4ri.js';
2
- import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../FetchClient-DadnuVF7.js';
3
- export { a as AuthClientConfig, A as AuthError, e as AuthEvent, f as AuthEventHandler, b as AuthEventMap, c as AuthState, d as AuthUser, F as FetchClient, i as RequestOptions, R as RetryConfig, g as ServerResponse, h as TokenPair } from '../FetchClient-DadnuVF7.js';
1
+ import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../NajmAuthClient-D08--i69.js';
2
+ export { a as AuthClientConfig, A as AuthError, f as AuthEvent, g as AuthEventHandler, b as AuthEventMap, d as AuthState, e as AuthUser, F as FetchClient, H as HydrateSession, N as NajmAuthClient, j as RequestOptions, R as RetryConfig, h as ServerResponse, i as TokenPair, c as createAuthClient } from '../NajmAuthClient-D08--i69.js';
4
3
 
5
4
  /**
6
5
  * Decode a JWT token payload without verification.
@@ -1,8 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, CSSProperties, ReactElement } from 'react';
4
- import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-CZHKl4ri.js';
5
- import { c as AuthState, d as AuthUser, A as AuthError, e as AuthEvent, b as AuthEventMap } from '../../FetchClient-DadnuVF7.js';
4
+ import { N as NajmAuthClient, H as HydrateSession, d as AuthState, e as AuthUser, A as AuthError, f as AuthEvent, b as AuthEventMap } from '../../NajmAuthClient-D08--i69.js';
6
5
 
7
6
  interface AuthProviderProps {
8
7
  client: NajmAuthClient;
@@ -1,4 +1,4 @@
1
- import { d as AuthUser, F as FetchClient } from '../../FetchClient-DadnuVF7.js';
1
+ import { e as AuthUser, F as FetchClient, R as RetryConfig, N as NajmAuthClient } from '../../NajmAuthClient-D08--i69.js';
2
2
  import * as next_server from 'next/server';
3
3
 
4
4
  interface GetServerSessionOptions {
@@ -202,6 +202,18 @@ interface DefineAuthConfig {
202
202
  apiBaseURL?: string;
203
203
  /** Auth prefix appended to apiBaseURL (default: '/auth') */
204
204
  authPrefix?: string;
205
+ /** Refresh token cookie name (default: 'refreshToken') */
206
+ cookieName?: string;
207
+ /** Proactive refresh at this fraction of token lifetime (default: 0.8) */
208
+ refreshThreshold?: number;
209
+ /** Enable multi-tab sync via BroadcastChannel (default: true) */
210
+ tabSync?: boolean;
211
+ /** BroadcastChannel name (default: 'najm-auth') */
212
+ channelName?: string;
213
+ /** Request timeout in milliseconds (default: 30000) */
214
+ timeout?: number;
215
+ /** Network retry configuration */
216
+ retry?: RetryConfig;
205
217
  /** Route to redirect unauthenticated users (default: '/login') */
206
218
  loginRoute?: string;
207
219
  /** Route to redirect after login (default: '/dashboard') */
@@ -212,8 +224,6 @@ interface DefineAuthConfig {
212
224
  protectedRoutes?: string[];
213
225
  /** Routes restricted to specific roles: { '/admin/:path*': ['admin'] } */
214
226
  roleRoutes?: Record<string, string[]>;
215
- /** Refresh token cookie name (default: 'refreshToken') */
216
- cookieName?: string;
217
227
  /** Session cookie name (default: 'najm.session') */
218
228
  sessionCookieName?: string;
219
229
  /** Secret for verifying session cookie HMAC. Falls back to env vars. */
@@ -222,6 +232,15 @@ interface DefineAuthConfig {
222
232
  matcher?: string[];
223
233
  }
224
234
  interface AuthKit {
235
+ /**
236
+ * Browser auth client — lazily instantiated on first access.
237
+ *
238
+ * Safe to touch from server/edge runtimes (constructor is runtime-guarded),
239
+ * but intended for client-side use via `<AuthProvider client={auth.client}>`.
240
+ */
241
+ readonly client: NajmAuthClient;
242
+ /** Shortcut for `client.api` — the underlying FetchClient with auth attached. */
243
+ readonly api: FetchClient;
225
244
  /** Resolve session — reads signed cookie first (instant), falls back to /auth/me */
226
245
  getSession: (opts?: Pick<GetSessionConfig, 'mode'>) => Promise<ServerSession | null>;
227
246
  /** Require session — throws if unauthenticated */
@@ -460,6 +460,421 @@ function withAuth(Page, options = {}) {
460
460
  }
461
461
  __name(withAuth, "withAuth");
462
462
 
463
+ // src/client/tokenDecoder.ts
464
+ function decodeToken(token) {
465
+ try {
466
+ const parts = token.split(".");
467
+ if (parts.length !== 3) return null;
468
+ const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
469
+ const json = typeof atob === "function" ? atob(payload) : Buffer.from(payload, "base64").toString("utf-8");
470
+ return JSON.parse(json);
471
+ } catch {
472
+ return null;
473
+ }
474
+ }
475
+ __name(decodeToken, "decodeToken");
476
+ function getTokenTTL(decoded) {
477
+ if (!decoded.exp) return 0;
478
+ return Math.max(0, decoded.exp - Date.now() / 1e3);
479
+ }
480
+ __name(getTokenTTL, "getTokenTTL");
481
+
482
+ // src/client/NajmAuthClient.ts
483
+ init_permissions();
484
+
485
+ // src/client/tabSync.ts
486
+ var TabSync = class {
487
+ constructor(channelName, onMessage) {
488
+ this.onMessage = onMessage;
489
+ this.channel = new BroadcastChannel(channelName);
490
+ this.channel.onmessage = (e) => this.onMessage(e.data);
491
+ }
492
+ static {
493
+ __name(this, "TabSync");
494
+ }
495
+ channel;
496
+ broadcastSync(state) {
497
+ this.channel.postMessage({ type: "sync", state });
498
+ }
499
+ broadcastLogout() {
500
+ this.channel.postMessage({ type: "logout" });
501
+ }
502
+ destroy() {
503
+ this.channel.close();
504
+ }
505
+ };
506
+
507
+ // src/client/NajmAuthClient.ts
508
+ var INITIAL_STATE = {
509
+ user: null,
510
+ accessToken: null,
511
+ isAuthenticated: false,
512
+ isLoading: false,
513
+ roles: [],
514
+ permissions: []
515
+ };
516
+ var NajmAuthClient = class _NajmAuthClient {
517
+ constructor(config) {
518
+ this.config = config;
519
+ this.prefix = config.authPrefix ?? "/auth";
520
+ this.threshold = config.refreshThreshold ?? 0.8;
521
+ this.api = new FetchClient({
522
+ baseURL: config.baseURL,
523
+ credentials: "include",
524
+ timeout: config.timeout ?? 3e4,
525
+ getToken: /* @__PURE__ */ __name(() => this.state.accessToken, "getToken"),
526
+ onUnauthorized: /* @__PURE__ */ __name(() => this.handleUnauthorized(), "onUnauthorized"),
527
+ retry: config.retry
528
+ });
529
+ if (config.tabSync !== false && typeof BroadcastChannel !== "undefined") {
530
+ const name = config.channelName ?? "najm-auth";
531
+ this.tabSync = new TabSync(name, (msg) => this.handleTabMessage(msg));
532
+ }
533
+ }
534
+ static {
535
+ __name(this, "NajmAuthClient");
536
+ }
537
+ static MAX_REFRESH_FAILURES = 3;
538
+ static CIRCUIT_RESET_MS = 6e4;
539
+ state = { ...INITIAL_STATE };
540
+ refreshTimer = null;
541
+ refreshCircuitTimer = null;
542
+ refreshPromise = null;
543
+ fetchUserPromise = null;
544
+ refreshFailures = 0;
545
+ _hydrated = false;
546
+ // Subscriptions (for React useSyncExternalStore)
547
+ listeners = /* @__PURE__ */ new Set();
548
+ eventListeners = /* @__PURE__ */ new Map();
549
+ // Multi-tab sync
550
+ tabSync = null;
551
+ // Public fetch client
552
+ api;
553
+ prefix;
554
+ threshold;
555
+ // =========================================================================
556
+ // Auth Operations
557
+ // =========================================================================
558
+ async login(credentials) {
559
+ const res = await this.api.post(
560
+ `${this.prefix}/login`,
561
+ { body: credentials, skipAuth: true }
562
+ );
563
+ this.applyTokens(res.data);
564
+ if (res.data.user) {
565
+ this.state = { ...this.state, user: res.data.user };
566
+ this.notify();
567
+ } else {
568
+ await this.fetchUser();
569
+ }
570
+ this.tabSync?.broadcastSync(this.getSyncPayload());
571
+ this.emit("login", this.state.user);
572
+ return this.state.user;
573
+ }
574
+ async register(data) {
575
+ const res = await this.api.post(
576
+ `${this.prefix}/register`,
577
+ { body: data, skipAuth: true }
578
+ );
579
+ return res.data;
580
+ }
581
+ async logout() {
582
+ this.resetState();
583
+ this.tabSync?.broadcastLogout();
584
+ this.emit("logout", null);
585
+ try {
586
+ await this.api.post(`${this.prefix}/logout`);
587
+ } catch (err) {
588
+ this.emit("logoutError", err);
589
+ }
590
+ }
591
+ async refresh() {
592
+ if (this.refreshFailures >= _NajmAuthClient.MAX_REFRESH_FAILURES) {
593
+ throw new Error("Session expired (circuit open)");
594
+ }
595
+ if (!this.refreshPromise) {
596
+ this.refreshPromise = this._refreshWithCircuit().finally(() => {
597
+ this.refreshPromise = null;
598
+ });
599
+ }
600
+ return this.refreshPromise;
601
+ }
602
+ async fetchUser() {
603
+ if (!this.fetchUserPromise) {
604
+ this.fetchUserPromise = this._doFetchUser().finally(() => {
605
+ this.fetchUserPromise = null;
606
+ });
607
+ }
608
+ return this.fetchUserPromise;
609
+ }
610
+ async _doFetchUser() {
611
+ try {
612
+ const res = await this.api.get(`${this.prefix}/me`);
613
+ this.state = { ...this.state, user: res.data };
614
+ this.notify();
615
+ this.emit("userUpdated", res.data);
616
+ return res.data;
617
+ } catch {
618
+ return null;
619
+ }
620
+ }
621
+ async forgotPassword(data) {
622
+ await this.api.post(`${this.prefix}/forgot-password`, { body: data, skipAuth: true });
623
+ }
624
+ async changePassword(data) {
625
+ await this.api.post(`${this.prefix}/change-password`, { body: data });
626
+ this.resetState();
627
+ this.tabSync?.broadcastLogout();
628
+ this.emit("logout", null);
629
+ }
630
+ async resetPassword(data) {
631
+ await this.api.post(`${this.prefix}/reset-password`, { body: data, skipAuth: true });
632
+ }
633
+ // =========================================================================
634
+ // Permissions (decoded from JWT — instant, no round-trip)
635
+ // =========================================================================
636
+ can(permission) {
637
+ return matchPermission(this.state.permissions, permission);
638
+ }
639
+ hasRole(role) {
640
+ return hasRole(this.state.roles, role);
641
+ }
642
+ hasAnyRole(roles) {
643
+ return hasAnyRole(this.state.roles, roles);
644
+ }
645
+ hasPermission(permission) {
646
+ return this.can(permission);
647
+ }
648
+ // =========================================================================
649
+ // State Access
650
+ // =========================================================================
651
+ getUser() {
652
+ return this.state.user;
653
+ }
654
+ getAccessToken() {
655
+ return this.state.accessToken;
656
+ }
657
+ isAuthenticated() {
658
+ return this.state.isAuthenticated;
659
+ }
660
+ getState() {
661
+ return this.state;
662
+ }
663
+ // =========================================================================
664
+ // Hydration (SSR)
665
+ // =========================================================================
666
+ /**
667
+ * Hydrate the client with a session resolved server-side.
668
+ * Use to skip the initial loading flicker on SSR-rendered pages.
669
+ * Safe to call multiple times — subsequent calls are no-ops.
670
+ */
671
+ hydrate(session) {
672
+ if (this._hydrated) return;
673
+ this._hydrated = true;
674
+ this.resetRefreshFailures();
675
+ if (!session || !session.user) {
676
+ this.state = { ...INITIAL_STATE };
677
+ this.notify();
678
+ return;
679
+ }
680
+ this.state = {
681
+ ...this.state,
682
+ user: session.user,
683
+ accessToken: session.accessToken ?? null,
684
+ isAuthenticated: true,
685
+ isLoading: false,
686
+ roles: session.roles ?? (session.user.role ? [session.user.role] : []),
687
+ permissions: session.permissions ?? session.user.permissions ?? []
688
+ };
689
+ if (session.accessToken) {
690
+ const decoded = decodeToken(session.accessToken);
691
+ if (decoded) {
692
+ if (!session.roles && decoded.roles) this.state.roles = decoded.roles;
693
+ if (!session.permissions && decoded.permissions) this.state.permissions = decoded.permissions;
694
+ this.scheduleRefresh(decoded);
695
+ }
696
+ }
697
+ this.notify();
698
+ }
699
+ isHydrated() {
700
+ return this._hydrated;
701
+ }
702
+ // =========================================================================
703
+ // Events
704
+ // =========================================================================
705
+ on(event, handler) {
706
+ if (!this.eventListeners.has(event)) this.eventListeners.set(event, /* @__PURE__ */ new Set());
707
+ this.eventListeners.get(event).add(handler);
708
+ }
709
+ off(event, handler) {
710
+ this.eventListeners.get(event)?.delete(handler);
711
+ }
712
+ // =========================================================================
713
+ // Subscribe (for React useSyncExternalStore)
714
+ // =========================================================================
715
+ subscribe(listener) {
716
+ this.listeners.add(listener);
717
+ return () => this.listeners.delete(listener);
718
+ }
719
+ // =========================================================================
720
+ // Cleanup
721
+ // =========================================================================
722
+ destroy() {
723
+ this.clearRefreshTimer();
724
+ this.clearRefreshCircuitTimer();
725
+ this.tabSync?.destroy();
726
+ this.tabSync = null;
727
+ this.refreshFailures = 0;
728
+ this.state = { ...INITIAL_STATE };
729
+ this.listeners.clear();
730
+ this.eventListeners.clear();
731
+ }
732
+ // =========================================================================
733
+ // Internals
734
+ // =========================================================================
735
+ async _refreshWithCircuit() {
736
+ try {
737
+ await this._doRefresh();
738
+ this.resetRefreshFailures();
739
+ } catch (err) {
740
+ const shouldOpenCircuit = this.registerRefreshFailure(err);
741
+ if (shouldOpenCircuit) {
742
+ this.resetState();
743
+ this.emit("sessionExpired", null);
744
+ if (err instanceof AuthError && err.status === 401) {
745
+ throw new Error("Session expired");
746
+ }
747
+ throw new Error("Session expired (circuit open)");
748
+ }
749
+ throw err;
750
+ }
751
+ }
752
+ async _doRefresh() {
753
+ const res = await this.api.post(
754
+ `${this.prefix}/refresh`,
755
+ { skipAuth: true }
756
+ );
757
+ this.applyTokens(res.data);
758
+ this.tabSync?.broadcastSync(this.getSyncPayload());
759
+ this.emit("tokenRefresh", null);
760
+ }
761
+ async handleUnauthorized() {
762
+ try {
763
+ await this.refresh();
764
+ return this.state.accessToken;
765
+ } catch {
766
+ return null;
767
+ }
768
+ }
769
+ applyTokens(tokens) {
770
+ this.resetRefreshFailures();
771
+ const decoded = decodeToken(tokens.accessToken);
772
+ this.state = {
773
+ ...this.state,
774
+ accessToken: tokens.accessToken,
775
+ isAuthenticated: true,
776
+ isLoading: false,
777
+ roles: decoded?.roles ?? [],
778
+ permissions: decoded?.permissions ?? []
779
+ };
780
+ if (decoded) this.scheduleRefresh(decoded);
781
+ this.notify();
782
+ }
783
+ scheduleRefresh(decoded) {
784
+ this.clearRefreshTimer();
785
+ const ttl = getTokenTTL(decoded);
786
+ if (ttl <= 0) return;
787
+ const delay = ttl * this.threshold * 1e3;
788
+ this.refreshTimer = setTimeout(() => this.refresh().catch(() => {
789
+ }), delay);
790
+ }
791
+ clearRefreshTimer() {
792
+ if (this.refreshTimer) {
793
+ clearTimeout(this.refreshTimer);
794
+ this.refreshTimer = null;
795
+ }
796
+ }
797
+ clearRefreshCircuitTimer() {
798
+ if (this.refreshCircuitTimer) {
799
+ clearTimeout(this.refreshCircuitTimer);
800
+ this.refreshCircuitTimer = null;
801
+ }
802
+ }
803
+ resetRefreshFailures() {
804
+ this.refreshFailures = 0;
805
+ this.clearRefreshCircuitTimer();
806
+ }
807
+ registerRefreshFailure(err) {
808
+ if (err instanceof AuthError && err.status === 401) {
809
+ this.refreshFailures = _NajmAuthClient.MAX_REFRESH_FAILURES;
810
+ } else {
811
+ this.refreshFailures += 1;
812
+ }
813
+ if (this.refreshFailures >= _NajmAuthClient.MAX_REFRESH_FAILURES) {
814
+ this.clearRefreshCircuitTimer();
815
+ this.refreshCircuitTimer = setTimeout(() => {
816
+ this.refreshFailures = 0;
817
+ this.refreshCircuitTimer = null;
818
+ }, _NajmAuthClient.CIRCUIT_RESET_MS);
819
+ return true;
820
+ }
821
+ return false;
822
+ }
823
+ resetState() {
824
+ this.clearRefreshTimer();
825
+ this.state = { ...INITIAL_STATE };
826
+ this.notify();
827
+ }
828
+ getSyncPayload() {
829
+ return {
830
+ accessToken: this.state.accessToken,
831
+ user: this.state.user,
832
+ roles: this.state.roles,
833
+ permissions: this.state.permissions,
834
+ isAuthenticated: this.state.isAuthenticated
835
+ };
836
+ }
837
+ handleTabMessage(msg) {
838
+ switch (msg.type) {
839
+ case "logout":
840
+ this.clearRefreshTimer();
841
+ this.state = { ...INITIAL_STATE };
842
+ this.notify();
843
+ this.emit("logout", null);
844
+ break;
845
+ case "sync":
846
+ this.state = {
847
+ ...this.state,
848
+ accessToken: msg.state.accessToken,
849
+ user: msg.state.user,
850
+ roles: msg.state.roles,
851
+ permissions: msg.state.permissions,
852
+ isAuthenticated: msg.state.isAuthenticated,
853
+ isLoading: false
854
+ };
855
+ if (msg.state.accessToken) {
856
+ const decoded = decodeToken(msg.state.accessToken);
857
+ if (decoded) this.scheduleRefresh(decoded);
858
+ }
859
+ this.notify();
860
+ break;
861
+ }
862
+ }
863
+ notify() {
864
+ this.listeners.forEach((l) => l(this.state));
865
+ }
866
+ emit(event, data) {
867
+ this.eventListeners.get(event)?.forEach((h) => h(data));
868
+ if (event !== "stateChange") {
869
+ this.eventListeners.get("stateChange")?.forEach((h) => h(this.state));
870
+ }
871
+ }
872
+ };
873
+ function createAuthClient(config) {
874
+ return new NajmAuthClient(config);
875
+ }
876
+ __name(createAuthClient, "createAuthClient");
877
+
463
878
  // src/client/server/defineAuth.ts
464
879
  function matchesAny2(pathname, patterns) {
465
880
  return patterns.some((p) => matchPattern2(pathname, p));
@@ -493,7 +908,12 @@ function defineAuth(authConfig = {}) {
493
908
  cookieName = "refreshToken",
494
909
  sessionCookieName = "najm.session",
495
910
  sessionSecret,
496
- matcher = ["/((?!_next/static|_next/image|favicon.ico|api).*)"]
911
+ matcher = ["/((?!_next/static|_next/image|favicon.ico|api).*)"],
912
+ refreshThreshold,
913
+ tabSync,
914
+ channelName,
915
+ timeout,
916
+ retry
497
917
  } = authConfig;
498
918
  const sessionConfig = {
499
919
  baseURL: void 0,
@@ -503,6 +923,20 @@ function defineAuth(authConfig = {}) {
503
923
  sessionCookieName,
504
924
  sessionSecret
505
925
  };
926
+ let _client = null;
927
+ const getClient = /* @__PURE__ */ __name(() => {
928
+ if (_client) return _client;
929
+ _client = createAuthClient({
930
+ baseURL: apiBaseURL,
931
+ authPrefix,
932
+ refreshThreshold,
933
+ tabSync,
934
+ channelName,
935
+ timeout,
936
+ retry
937
+ });
938
+ return _client;
939
+ }, "getClient");
506
940
  const getSession2 = /* @__PURE__ */ __name(async (opts) => {
507
941
  const { getSession: resolveSession } = await Promise.resolve().then(() => (init_getSession(), getSession_exports));
508
942
  return resolveSession({ ...sessionConfig, ...opts });
@@ -596,6 +1030,12 @@ function defineAuth(authConfig = {}) {
596
1030
  }, "ProtectedPage");
597
1031
  }, "protect");
598
1032
  return {
1033
+ get client() {
1034
+ return getClient();
1035
+ },
1036
+ get api() {
1037
+ return getClient().api;
1038
+ },
599
1039
  getSession: getSession2,
600
1040
  requireSession,
601
1041
  middleware,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-auth",
3
- "version": "1.1.23",
3
+ "version": "1.1.24",
4
4
  "description": "Authentication and authorization library for najm framework",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,86 +0,0 @@
1
- import { F as FetchClient, a as AuthClientConfig, d as AuthUser, c as AuthState, e as AuthEvent, f as AuthEventHandler } from './FetchClient-DadnuVF7.js';
2
-
3
- interface HydrateSession {
4
- user: AuthUser | null;
5
- accessToken?: string | null;
6
- roles?: string[];
7
- permissions?: string[];
8
- }
9
- declare class NajmAuthClient {
10
- private config;
11
- private static readonly MAX_REFRESH_FAILURES;
12
- private static readonly CIRCUIT_RESET_MS;
13
- private state;
14
- private refreshTimer;
15
- private refreshCircuitTimer;
16
- private refreshPromise;
17
- private fetchUserPromise;
18
- private refreshFailures;
19
- private _hydrated;
20
- private listeners;
21
- private eventListeners;
22
- private tabSync;
23
- api: FetchClient;
24
- private readonly prefix;
25
- private readonly threshold;
26
- constructor(config: AuthClientConfig);
27
- login(credentials: {
28
- email: string;
29
- password: string;
30
- }): Promise<AuthUser>;
31
- register(data: Record<string, unknown>): Promise<AuthUser>;
32
- logout(): Promise<void>;
33
- refresh(): Promise<void>;
34
- fetchUser(): Promise<AuthUser | null>;
35
- private _doFetchUser;
36
- forgotPassword(data: {
37
- email: string;
38
- }): Promise<void>;
39
- changePassword(data: {
40
- currentPassword: string;
41
- newPassword: string;
42
- }): Promise<void>;
43
- resetPassword(data: {
44
- token: string;
45
- newPassword: string;
46
- }): Promise<void>;
47
- can(permission: string): boolean;
48
- hasRole(role: string): boolean;
49
- hasAnyRole(roles: string[]): boolean;
50
- hasPermission(permission: string): boolean;
51
- getUser(): AuthUser | null;
52
- getAccessToken(): string | null;
53
- isAuthenticated(): boolean;
54
- getState(): AuthState;
55
- /**
56
- * Hydrate the client with a session resolved server-side.
57
- * Use to skip the initial loading flicker on SSR-rendered pages.
58
- * Safe to call multiple times — subsequent calls are no-ops.
59
- */
60
- hydrate(session: HydrateSession | null): void;
61
- isHydrated(): boolean;
62
- on<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
63
- off<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
64
- subscribe(listener: (state: AuthState) => void): () => void;
65
- destroy(): void;
66
- private _refreshWithCircuit;
67
- private _doRefresh;
68
- private handleUnauthorized;
69
- private applyTokens;
70
- private scheduleRefresh;
71
- private clearRefreshTimer;
72
- private clearRefreshCircuitTimer;
73
- private resetRefreshFailures;
74
- private registerRefreshFailure;
75
- private resetState;
76
- private getSyncPayload;
77
- private handleTabMessage;
78
- private notify;
79
- private emit;
80
- }
81
- /**
82
- * Factory function to create an auth client.
83
- */
84
- declare function createAuthClient(config: AuthClientConfig): NajmAuthClient;
85
-
86
- export { type HydrateSession as H, NajmAuthClient as N, createAuthClient as c };