@stackwright-pro/auth-nextjs 0.2.0-alpha.7 → 0.3.0-alpha.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/README.md CHANGED
@@ -10,10 +10,35 @@ pnpm add @stackwright-pro/auth-nextjs
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Middleware (Route Protection)
13
+ ### Proxy (Next.js >=16 — Recommended)
14
+
15
+ Next.js 16 deprecated the `middleware.ts` convention in favor of `proxy.ts`. Use `createAuthProxy` for new projects:
14
16
 
15
17
  ```typescript
16
- // middleware.ts
18
+ // proxy.ts (project root)
19
+ import { createAuthProxy } from '@stackwright-pro/auth-nextjs';
20
+ import { sessionManager, rbacEngine, authConfig } from './lib/auth';
21
+
22
+ export default createAuthProxy({
23
+ authConfig,
24
+ sessionManager,
25
+ rbacEngine,
26
+ cookieName: 'my_session',
27
+ loginUrl: '/login',
28
+ unauthorizedUrl: '/403',
29
+ });
30
+
31
+ export const config = {
32
+ matcher: ['/((?!_next|api/auth|login).*)'],
33
+ };
34
+ ```
35
+
36
+ ### Middleware (Next.js 14–15 — Legacy)
37
+
38
+ > **Deprecated**: For Next.js >=16, use `createAuthProxy` above instead.
39
+
40
+ ```typescript
41
+ // middleware.ts (project root)
17
42
  import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';
18
43
  import { sessionManager, rbacEngine, authConfig } from './lib/auth';
19
44
 
@@ -59,11 +84,18 @@ import { setSessionCookie } from '@stackwright-pro/auth-nextjs';
59
84
  export default async function handler(req, res) {
60
85
  const session = await authenticateUser(req.body);
61
86
  const jwt = await sessionManager.serialize(session);
62
-
87
+
63
88
  setSessionCookie(res, jwt, {
64
89
  maxAge: 900, // 15 minutes
65
90
  });
66
-
91
+
67
92
  res.json({ success: true });
68
93
  }
69
94
  ```
95
+
96
+ ## Subpath Exports
97
+
98
+ | Import path | Convention | Next.js version |
99
+ | ------------------------------------ | --------------------------- | --------------- |
100
+ | `@stackwright-pro/auth-nextjs` | Both exports available | All |
101
+ | `@stackwright-pro/auth-nextjs/proxy` | Proxy only (tree-shakeable) | >=16 |
@@ -0,0 +1,58 @@
1
+ // src/auth-handler.ts
2
+ import { NextResponse } from "next/server";
3
+ function createAuthHandler(config) {
4
+ const {
5
+ sessionManager,
6
+ rbacEngine,
7
+ cookieName = "stackwright_session",
8
+ loginUrl = "/login",
9
+ unauthorizedUrl = "/unauthorized"
10
+ } = config;
11
+ return async function authHandler(request) {
12
+ const { pathname } = request.nextUrl;
13
+ if (rbacEngine.isPublicRoute(pathname)) {
14
+ return void 0;
15
+ }
16
+ const sessionCookie = request.cookies.get(cookieName);
17
+ let session = null;
18
+ if (sessionCookie) {
19
+ session = await sessionManager.deserialize(sessionCookie.value);
20
+ }
21
+ if (!session || sessionManager.isExpired(session)) {
22
+ const url = request.nextUrl.clone();
23
+ url.pathname = loginUrl;
24
+ url.searchParams.set("redirect", pathname);
25
+ return NextResponse.redirect(url);
26
+ }
27
+ if (!rbacEngine.canAccessRoute(session.user, pathname)) {
28
+ const url = request.nextUrl.clone();
29
+ url.pathname = unauthorizedUrl;
30
+ return NextResponse.redirect(url);
31
+ }
32
+ if (sessionManager.shouldRefresh(session)) {
33
+ const refreshed = await sessionManager.refreshSession(session);
34
+ const newJwt = await sessionManager.serialize(refreshed);
35
+ const response = NextResponse.next();
36
+ response.cookies.set(cookieName, newJwt, {
37
+ httpOnly: true,
38
+ secure: true,
39
+ sameSite: "lax",
40
+ maxAge: 900
41
+ // 15 minutes
42
+ });
43
+ return response;
44
+ }
45
+ return void 0;
46
+ };
47
+ }
48
+
49
+ // src/proxy.ts
50
+ function createAuthProxy(config) {
51
+ return createAuthHandler(config);
52
+ }
53
+
54
+ export {
55
+ createAuthHandler,
56
+ createAuthProxy
57
+ };
58
+ //# sourceMappingURL=chunk-MFZSH4YL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth-handler.ts","../src/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface AuthHandlerConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Core auth handler shared by both the middleware (Next.js <16) and\n * proxy (Next.js >=16) conventions.\n *\n * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.\n */\nexport function createAuthHandler(config: AuthHandlerConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authHandler(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next handler\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js proxy-based auth (Next.js >=16).\n */\nexport type ProxyConfig = AuthHandlerConfig;\n\n/**\n * Create a Next.js proxy handler for authentication and authorization.\n *\n * For Next.js >=16 projects that use the `proxy.ts` file convention,\n * replacing the deprecated `middleware.ts` convention.\n *\n * @example\n * ```typescript\n * // proxy.ts (Next.js >=16)\n * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthProxy({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthProxy(config: ProxyConfig) {\n return createAuthHandler(config);\n}\n"],"mappings":";AAAA,SAA2B,oBAAoB;AA8CxC,SAAS,kBAAkB,QAA2B;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB;AAAA,EACpB,IAAI;AAEJ,SAAO,eAAe,YAAY,SAAyD;AACzF,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,WAAW,cAAc,QAAQ,GAAG;AACtC,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU;AACpD,QAAI,UAA8B;AAElC,QAAI,eAAe;AACjB,gBAAU,MAAM,eAAe,YAAY,cAAc,KAAK;AAAA,IAChE;AAGA,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AAEjD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,UAAI,aAAa,IAAI,YAAY,QAAQ;AACzC,aAAO,aAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,CAAC,WAAW,eAAe,QAAQ,MAAM,QAAQ,GAAG;AAEtD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,aAAO,aAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,eAAe,cAAc,OAAO,GAAG;AACzC,YAAM,YAAY,MAAM,eAAe,eAAe,OAAO;AAC7D,YAAM,SAAS,MAAM,eAAe,UAAU,SAAS;AAEvD,YAAM,WAAW,aAAa,KAAK;AACnC,eAAS,QAAQ,IAAI,YAAY,QAAQ;AAAA,QACvC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA,MACV,CAAC;AAED,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AACF;;;AC9EO,SAAS,gBAAgB,QAAqB;AACnD,SAAO,kBAAkB,MAAM;AACjC;","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,40 +1,29 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { AuthConfig, SessionManager, RBACEngine, AuthSession, CookieOptions } from '@stackwright-pro/auth';
3
- export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
1
+ import * as next_server_js from 'next/server.js';
2
+ import { A as AuthHandlerConfig } from './proxy-Blj2kIJp.mjs';
3
+ export { P as ProxyConfig, c as createAuthHandler, a as createAuthProxy } from './proxy-Blj2kIJp.mjs';
4
4
  import { NextApiRequest, NextApiResponse } from 'next';
5
+ import { AuthSession, SessionManager, RBACEngine, CookieOptions } from '@stackwright-pro/auth';
6
+ export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
7
+ import { NextRequest, NextResponse } from 'next/server';
5
8
 
6
- interface MiddlewareConfig {
7
- /**
8
- * Auth configuration
9
- */
10
- authConfig: AuthConfig;
11
- /**
12
- * Session manager instance
13
- */
14
- sessionManager: SessionManager;
15
- /**
16
- * RBAC engine instance
17
- */
18
- rbacEngine: RBACEngine;
19
- /**
20
- * Cookie name for session (default: 'stackwright_session')
21
- */
22
- cookieName?: string;
23
- /**
24
- * URL to redirect to when authentication fails (default: '/login')
25
- */
26
- loginUrl?: string;
27
- /**
28
- * URL to redirect to when authorization fails (default: '/unauthorized')
29
- */
30
- unauthorizedUrl?: string;
31
- }
32
9
  /**
33
- * Create Next.js middleware for authentication and authorization
10
+ * Configuration for Next.js middleware-based auth (Next.js <16).
11
+ *
12
+ * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.
13
+ * The `middleware.ts` file convention is deprecated in Next.js 16.
14
+ */
15
+ type MiddlewareConfig = AuthHandlerConfig;
16
+ /**
17
+ * Create Next.js middleware for authentication and authorization.
18
+ *
19
+ * For Next.js 14–15 projects that use the `middleware.ts` file convention.
20
+ *
21
+ * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.
22
+ * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.
34
23
  *
35
24
  * @example
36
25
  * ```typescript
37
- * // middleware.ts
26
+ * // middleware.ts (Next.js 14–15)
38
27
  * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';
39
28
  *
40
29
  * export default createAuthMiddleware({
@@ -48,7 +37,7 @@ interface MiddlewareConfig {
48
37
  * };
49
38
  * ```
50
39
  */
51
- declare function createAuthMiddleware(config: MiddlewareConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
40
+ declare function createAuthMiddleware(config: MiddlewareConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
52
41
 
53
42
  interface ProtectedRouteConfig {
54
43
  /**
@@ -204,4 +193,4 @@ declare function clearSessionCookieOnResponse(response: NextResponse, options?:
204
193
  cookieName?: string;
205
194
  }): NextResponse;
206
195
 
207
- export { type AuthenticatedHandler, type AuthenticatedRouteHandler, type MiddlewareConfig, type ProtectedRouteConfig, type RouteHandlerConfig, clearSessionCookie, clearSessionCookieOnResponse, createAuthMiddleware, protectedRoute, protectedRouteHandler, setSessionCookie, setSessionCookieOnResponse };
196
+ export { AuthHandlerConfig, type AuthenticatedHandler, type AuthenticatedRouteHandler, type MiddlewareConfig, type ProtectedRouteConfig, type RouteHandlerConfig, clearSessionCookie, clearSessionCookieOnResponse, createAuthMiddleware, protectedRoute, protectedRouteHandler, setSessionCookie, setSessionCookieOnResponse };
package/dist/index.d.ts CHANGED
@@ -1,40 +1,29 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { AuthConfig, SessionManager, RBACEngine, AuthSession, CookieOptions } from '@stackwright-pro/auth';
3
- export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
1
+ import * as next_server_js from 'next/server.js';
2
+ import { A as AuthHandlerConfig } from './proxy-Blj2kIJp.js';
3
+ export { P as ProxyConfig, c as createAuthHandler, a as createAuthProxy } from './proxy-Blj2kIJp.js';
4
4
  import { NextApiRequest, NextApiResponse } from 'next';
5
+ import { AuthSession, SessionManager, RBACEngine, CookieOptions } from '@stackwright-pro/auth';
6
+ export { AuthConfig, AuthContext, AuthContextValue, AuthProvider, AuthProviderProps, AuthSession, AuthUser, RBACEngine, useAuth, useRequireAuth } from '@stackwright-pro/auth';
7
+ import { NextRequest, NextResponse } from 'next/server';
5
8
 
6
- interface MiddlewareConfig {
7
- /**
8
- * Auth configuration
9
- */
10
- authConfig: AuthConfig;
11
- /**
12
- * Session manager instance
13
- */
14
- sessionManager: SessionManager;
15
- /**
16
- * RBAC engine instance
17
- */
18
- rbacEngine: RBACEngine;
19
- /**
20
- * Cookie name for session (default: 'stackwright_session')
21
- */
22
- cookieName?: string;
23
- /**
24
- * URL to redirect to when authentication fails (default: '/login')
25
- */
26
- loginUrl?: string;
27
- /**
28
- * URL to redirect to when authorization fails (default: '/unauthorized')
29
- */
30
- unauthorizedUrl?: string;
31
- }
32
9
  /**
33
- * Create Next.js middleware for authentication and authorization
10
+ * Configuration for Next.js middleware-based auth (Next.js <16).
11
+ *
12
+ * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.
13
+ * The `middleware.ts` file convention is deprecated in Next.js 16.
14
+ */
15
+ type MiddlewareConfig = AuthHandlerConfig;
16
+ /**
17
+ * Create Next.js middleware for authentication and authorization.
18
+ *
19
+ * For Next.js 14–15 projects that use the `middleware.ts` file convention.
20
+ *
21
+ * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.
22
+ * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.
34
23
  *
35
24
  * @example
36
25
  * ```typescript
37
- * // middleware.ts
26
+ * // middleware.ts (Next.js 14–15)
38
27
  * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';
39
28
  *
40
29
  * export default createAuthMiddleware({
@@ -48,7 +37,7 @@ interface MiddlewareConfig {
48
37
  * };
49
38
  * ```
50
39
  */
51
- declare function createAuthMiddleware(config: MiddlewareConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
40
+ declare function createAuthMiddleware(config: MiddlewareConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
52
41
 
53
42
  interface ProtectedRouteConfig {
54
43
  /**
@@ -204,4 +193,4 @@ declare function clearSessionCookieOnResponse(response: NextResponse, options?:
204
193
  cookieName?: string;
205
194
  }): NextResponse;
206
195
 
207
- export { type AuthenticatedHandler, type AuthenticatedRouteHandler, type MiddlewareConfig, type ProtectedRouteConfig, type RouteHandlerConfig, clearSessionCookie, clearSessionCookieOnResponse, createAuthMiddleware, protectedRoute, protectedRouteHandler, setSessionCookie, setSessionCookieOnResponse };
196
+ export { AuthHandlerConfig, type AuthenticatedHandler, type AuthenticatedRouteHandler, type MiddlewareConfig, type ProtectedRouteConfig, type RouteHandlerConfig, clearSessionCookie, clearSessionCookieOnResponse, createAuthMiddleware, protectedRoute, protectedRouteHandler, setSessionCookie, setSessionCookieOnResponse };
package/dist/index.js CHANGED
@@ -25,7 +25,9 @@ __export(index_exports, {
25
25
  RBACEngine: () => import_auth2.RBACEngine,
26
26
  clearSessionCookie: () => clearSessionCookie,
27
27
  clearSessionCookieOnResponse: () => clearSessionCookieOnResponse,
28
+ createAuthHandler: () => createAuthHandler,
28
29
  createAuthMiddleware: () => createAuthMiddleware,
30
+ createAuthProxy: () => createAuthProxy,
29
31
  protectedRoute: () => protectedRoute,
30
32
  protectedRouteHandler: () => protectedRouteHandler,
31
33
  setSessionCookie: () => setSessionCookie,
@@ -35,9 +37,9 @@ __export(index_exports, {
35
37
  });
36
38
  module.exports = __toCommonJS(index_exports);
37
39
 
38
- // src/middleware.ts
40
+ // src/auth-handler.ts
39
41
  var import_server = require("next/server");
40
- function createAuthMiddleware(config) {
42
+ function createAuthHandler(config) {
41
43
  const {
42
44
  sessionManager,
43
45
  rbacEngine,
@@ -45,7 +47,7 @@ function createAuthMiddleware(config) {
45
47
  loginUrl = "/login",
46
48
  unauthorizedUrl = "/unauthorized"
47
49
  } = config;
48
- return async function authMiddleware(request) {
50
+ return async function authHandler(request) {
49
51
  const { pathname } = request.nextUrl;
50
52
  if (rbacEngine.isPublicRoute(pathname)) {
51
53
  return void 0;
@@ -83,6 +85,16 @@ function createAuthMiddleware(config) {
83
85
  };
84
86
  }
85
87
 
88
+ // src/middleware.ts
89
+ function createAuthMiddleware(config) {
90
+ return createAuthHandler(config);
91
+ }
92
+
93
+ // src/proxy.ts
94
+ function createAuthProxy(config) {
95
+ return createAuthHandler(config);
96
+ }
97
+
86
98
  // src/api-helpers.ts
87
99
  function protectedRoute(handler, config) {
88
100
  const { sessionManager, roles, permissions, cookieName = "stackwright_session" } = config;
@@ -224,7 +236,9 @@ function clearSessionCookieOnResponse(response, options = {}) {
224
236
  RBACEngine,
225
237
  clearSessionCookie,
226
238
  clearSessionCookieOnResponse,
239
+ createAuthHandler,
227
240
  createAuthMiddleware,
241
+ createAuthProxy,
228
242
  protectedRoute,
229
243
  protectedRouteHandler,
230
244
  setSessionCookie,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/route-handlers.ts"],"sourcesContent":["export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n\n// App Router Route Handler helpers (replaces api-helpers for App Router users)\nexport {\n protectedRouteHandler,\n setSessionCookieOnResponse,\n clearSessionCookieOnResponse,\n type RouteHandlerConfig,\n type AuthenticatedRouteHandler,\n} from './route-handlers.js';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface MiddlewareConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Create Next.js middleware for authentication and authorization\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authMiddleware(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next middleware\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport { SessionManager, RBACEngine, type AuthSession } from '@stackwright-pro/auth';\n\nexport interface RouteHandlerConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session — App Router Route Handler signature.\n */\nexport type AuthenticatedRouteHandler = (\n request: NextRequest,\n context: { params: Record<string, string> },\n session: AuthSession\n) => Promise<Response> | Response;\n\n/**\n * Wrap an App Router Route Handler with authentication/authorization.\n *\n * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.\n *\n * @example\n * ```typescript\n * // app/api/equipment/[id]/route.ts\n * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';\n *\n * export const GET = protectedRouteHandler(\n * async (request, { params }, session) => {\n * const equipment = await getEquipment(params.id);\n * return NextResponse.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRouteHandler(\n handler: AuthenticatedRouteHandler,\n config: RouteHandlerConfig\n): (request: NextRequest, context: { params: Record<string, string> }) => Promise<Response> {\n const {\n sessionManager,\n roles,\n permissions,\n cookieName = 'stackwright_session',\n rbacEngine,\n } = config;\n\n return async (\n request: NextRequest,\n context: { params: Record<string, string> }\n ): Promise<Response> => {\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n\n if (!sessionCookie) {\n return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie.value);\n\n if (!session || sessionManager.isExpired(session)) {\n return NextResponse.json({ error: 'Session expired' }, { status: 401 });\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAnyRole(session.user, roles)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAllPermissions(session.user, permissions)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));\n if (!hasAllPermissions) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n return handler(request, context, session);\n };\n}\n\n/**\n * Set the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/login/route.ts\n * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST(request: NextRequest) {\n * const session = await createSession(request);\n * const jwt = await sessionManager.serialize(session);\n * const response = NextResponse.json({ ok: true });\n * return setSessionCookieOnResponse(response, jwt);\n * }\n * ```\n */\nexport function setSessionCookieOnResponse(\n response: NextResponse,\n sessionJwt: string,\n options: {\n cookieName?: string;\n maxAge?: number;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n const maxAge = options.maxAge ?? 900; // 15 minutes\n\n response.cookies.set(cookieName, sessionJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge,\n });\n\n return response;\n}\n\n/**\n * Clear the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/logout/route.ts\n * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST() {\n * const response = NextResponse.json({ ok: true });\n * return clearSessionCookieOnResponse(response);\n * }\n * ```\n */\nexport function clearSessionCookieOnResponse(\n response: NextResponse,\n options: {\n cookieName?: string;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n response.cookies.delete(cookieName);\n return response;\n}\n"],"mappings":"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","names":["import_auth","import_server"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/auth-handler.ts","../src/middleware.ts","../src/proxy.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/route-handlers.ts"],"sourcesContent":["export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport { createAuthProxy, type ProxyConfig } from './proxy.js';\nexport { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n\n// App Router Route Handler helpers (replaces api-helpers for App Router users)\nexport {\n protectedRouteHandler,\n setSessionCookieOnResponse,\n clearSessionCookieOnResponse,\n type RouteHandlerConfig,\n type AuthenticatedRouteHandler,\n} from './route-handlers.js';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface AuthHandlerConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Core auth handler shared by both the middleware (Next.js <16) and\n * proxy (Next.js >=16) conventions.\n *\n * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.\n */\nexport function createAuthHandler(config: AuthHandlerConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authHandler(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next handler\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js middleware-based auth (Next.js <16).\n *\n * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16.\n */\nexport type MiddlewareConfig = AuthHandlerConfig;\n\n/**\n * Create Next.js middleware for authentication and authorization.\n *\n * For Next.js 14–15 projects that use the `middleware.ts` file convention.\n *\n * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.\n *\n * @example\n * ```typescript\n * // middleware.ts (Next.js 14–15)\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n return createAuthHandler(config);\n}\n","import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js proxy-based auth (Next.js >=16).\n */\nexport type ProxyConfig = AuthHandlerConfig;\n\n/**\n * Create a Next.js proxy handler for authentication and authorization.\n *\n * For Next.js >=16 projects that use the `proxy.ts` file convention,\n * replacing the deprecated `middleware.ts` convention.\n *\n * @example\n * ```typescript\n * // proxy.ts (Next.js >=16)\n * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthProxy({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthProxy(config: ProxyConfig) {\n return createAuthHandler(config);\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport { SessionManager, RBACEngine, type AuthSession } from '@stackwright-pro/auth';\n\nexport interface RouteHandlerConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session — App Router Route Handler signature.\n */\nexport type AuthenticatedRouteHandler = (\n request: NextRequest,\n context: { params: Record<string, string> },\n session: AuthSession\n) => Promise<Response> | Response;\n\n/**\n * Wrap an App Router Route Handler with authentication/authorization.\n *\n * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.\n *\n * @example\n * ```typescript\n * // app/api/equipment/[id]/route.ts\n * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';\n *\n * export const GET = protectedRouteHandler(\n * async (request, { params }, session) => {\n * const equipment = await getEquipment(params.id);\n * return NextResponse.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRouteHandler(\n handler: AuthenticatedRouteHandler,\n config: RouteHandlerConfig\n): (request: NextRequest, context: { params: Record<string, string> }) => Promise<Response> {\n const {\n sessionManager,\n roles,\n permissions,\n cookieName = 'stackwright_session',\n rbacEngine,\n } = config;\n\n return async (\n request: NextRequest,\n context: { params: Record<string, string> }\n ): Promise<Response> => {\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n\n if (!sessionCookie) {\n return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie.value);\n\n if (!session || sessionManager.isExpired(session)) {\n return NextResponse.json({ error: 'Session expired' }, { status: 401 });\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAnyRole(session.user, roles)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAllPermissions(session.user, permissions)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));\n if (!hasAllPermissions) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n return handler(request, context, session);\n };\n}\n\n/**\n * Set the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/login/route.ts\n * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST(request: NextRequest) {\n * const session = await createSession(request);\n * const jwt = await sessionManager.serialize(session);\n * const response = NextResponse.json({ ok: true });\n * return setSessionCookieOnResponse(response, jwt);\n * }\n * ```\n */\nexport function setSessionCookieOnResponse(\n response: NextResponse,\n sessionJwt: string,\n options: {\n cookieName?: string;\n maxAge?: number;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n const maxAge = options.maxAge ?? 900; // 15 minutes\n\n response.cookies.set(cookieName, sessionJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge,\n });\n\n return response;\n}\n\n/**\n * Clear the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/logout/route.ts\n * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST() {\n * const response = NextResponse.json({ ok: true });\n * return clearSessionCookieOnResponse(response);\n * }\n * ```\n */\nexport function clearSessionCookieOnResponse(\n response: NextResponse,\n options: {\n cookieName?: string;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n response.cookies.delete(cookieName);\n return response;\n}\n"],"mappings":"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","names":["import_auth","import_server"]}
package/dist/index.mjs CHANGED
@@ -1,49 +1,11 @@
1
+ import {
2
+ createAuthHandler,
3
+ createAuthProxy
4
+ } from "./chunk-MFZSH4YL.mjs";
5
+
1
6
  // src/middleware.ts
2
- import { NextResponse } from "next/server";
3
7
  function createAuthMiddleware(config) {
4
- const {
5
- sessionManager,
6
- rbacEngine,
7
- cookieName = "stackwright_session",
8
- loginUrl = "/login",
9
- unauthorizedUrl = "/unauthorized"
10
- } = config;
11
- return async function authMiddleware(request) {
12
- const { pathname } = request.nextUrl;
13
- if (rbacEngine.isPublicRoute(pathname)) {
14
- return void 0;
15
- }
16
- const sessionCookie = request.cookies.get(cookieName);
17
- let session = null;
18
- if (sessionCookie) {
19
- session = await sessionManager.deserialize(sessionCookie.value);
20
- }
21
- if (!session || sessionManager.isExpired(session)) {
22
- const url = request.nextUrl.clone();
23
- url.pathname = loginUrl;
24
- url.searchParams.set("redirect", pathname);
25
- return NextResponse.redirect(url);
26
- }
27
- if (!rbacEngine.canAccessRoute(session.user, pathname)) {
28
- const url = request.nextUrl.clone();
29
- url.pathname = unauthorizedUrl;
30
- return NextResponse.redirect(url);
31
- }
32
- if (sessionManager.shouldRefresh(session)) {
33
- const refreshed = await sessionManager.refreshSession(session);
34
- const newJwt = await sessionManager.serialize(refreshed);
35
- const response = NextResponse.next();
36
- response.cookies.set(cookieName, newJwt, {
37
- httpOnly: true,
38
- secure: true,
39
- sameSite: "lax",
40
- maxAge: 900
41
- // 15 minutes
42
- });
43
- return response;
44
- }
45
- return void 0;
46
- };
8
+ return createAuthHandler(config);
47
9
  }
48
10
 
49
11
  // src/api-helpers.ts
@@ -119,7 +81,7 @@ import { RBACEngine } from "@stackwright-pro/auth";
119
81
  import { AuthProvider, AuthContext, useAuth, useRequireAuth } from "@stackwright-pro/auth";
120
82
 
121
83
  // src/route-handlers.ts
122
- import { NextResponse as NextResponse2 } from "next/server";
84
+ import { NextResponse } from "next/server";
123
85
  function protectedRouteHandler(handler, config) {
124
86
  const {
125
87
  sessionManager,
@@ -131,33 +93,33 @@ function protectedRouteHandler(handler, config) {
131
93
  return async (request, context) => {
132
94
  const sessionCookie = request.cookies.get(cookieName);
133
95
  if (!sessionCookie) {
134
- return NextResponse2.json({ error: "Not authenticated" }, { status: 401 });
96
+ return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
135
97
  }
136
98
  const session = await sessionManager.deserialize(sessionCookie.value);
137
99
  if (!session || sessionManager.isExpired(session)) {
138
- return NextResponse2.json({ error: "Session expired" }, { status: 401 });
100
+ return NextResponse.json({ error: "Session expired" }, { status: 401 });
139
101
  }
140
102
  if (roles && roles.length > 0) {
141
103
  if (rbacEngine) {
142
104
  if (!rbacEngine.hasAnyRole(session.user, roles)) {
143
- return NextResponse2.json({ error: "Insufficient permissions" }, { status: 403 });
105
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
144
106
  }
145
107
  } else {
146
108
  const hasRole = roles.some((role) => session.user.roles.includes(role));
147
109
  if (!hasRole) {
148
- return NextResponse2.json({ error: "Insufficient permissions" }, { status: 403 });
110
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
149
111
  }
150
112
  }
151
113
  }
152
114
  if (permissions && permissions.length > 0) {
153
115
  if (rbacEngine) {
154
116
  if (!rbacEngine.hasAllPermissions(session.user, permissions)) {
155
- return NextResponse2.json({ error: "Insufficient permissions" }, { status: 403 });
117
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
156
118
  }
157
119
  } else {
158
120
  const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));
159
121
  if (!hasAllPermissions) {
160
- return NextResponse2.json({ error: "Insufficient permissions" }, { status: 403 });
122
+ return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 });
161
123
  }
162
124
  }
163
125
  }
@@ -186,7 +148,9 @@ export {
186
148
  RBACEngine,
187
149
  clearSessionCookie,
188
150
  clearSessionCookieOnResponse,
151
+ createAuthHandler,
189
152
  createAuthMiddleware,
153
+ createAuthProxy,
190
154
  protectedRoute,
191
155
  protectedRouteHandler,
192
156
  setSessionCookie,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/index.ts","../src/route-handlers.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface MiddlewareConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Create Next.js middleware for authentication and authorization\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authMiddleware(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next middleware\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n\n// App Router Route Handler helpers (replaces api-helpers for App Router users)\nexport {\n protectedRouteHandler,\n setSessionCookieOnResponse,\n clearSessionCookieOnResponse,\n type RouteHandlerConfig,\n type AuthenticatedRouteHandler,\n} from './route-handlers.js';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport { SessionManager, RBACEngine, type AuthSession } from '@stackwright-pro/auth';\n\nexport interface RouteHandlerConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session — App Router Route Handler signature.\n */\nexport type AuthenticatedRouteHandler = (\n request: NextRequest,\n context: { params: Record<string, string> },\n session: AuthSession\n) => Promise<Response> | Response;\n\n/**\n * Wrap an App Router Route Handler with authentication/authorization.\n *\n * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.\n *\n * @example\n * ```typescript\n * // app/api/equipment/[id]/route.ts\n * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';\n *\n * export const GET = protectedRouteHandler(\n * async (request, { params }, session) => {\n * const equipment = await getEquipment(params.id);\n * return NextResponse.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRouteHandler(\n handler: AuthenticatedRouteHandler,\n config: RouteHandlerConfig\n): (request: NextRequest, context: { params: Record<string, string> }) => Promise<Response> {\n const {\n sessionManager,\n roles,\n permissions,\n cookieName = 'stackwright_session',\n rbacEngine,\n } = config;\n\n return async (\n request: NextRequest,\n context: { params: Record<string, string> }\n ): Promise<Response> => {\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n\n if (!sessionCookie) {\n return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie.value);\n\n if (!session || sessionManager.isExpired(session)) {\n return NextResponse.json({ error: 'Session expired' }, { status: 401 });\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAnyRole(session.user, roles)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAllPermissions(session.user, permissions)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));\n if (!hasAllPermissions) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n return handler(request, context, session);\n };\n}\n\n/**\n * Set the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/login/route.ts\n * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST(request: NextRequest) {\n * const session = await createSession(request);\n * const jwt = await sessionManager.serialize(session);\n * const response = NextResponse.json({ ok: true });\n * return setSessionCookieOnResponse(response, jwt);\n * }\n * ```\n */\nexport function setSessionCookieOnResponse(\n response: NextResponse,\n sessionJwt: string,\n options: {\n cookieName?: string;\n maxAge?: number;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n const maxAge = options.maxAge ?? 900; // 15 minutes\n\n response.cookies.set(cookieName, sessionJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge,\n });\n\n return response;\n}\n\n/**\n * Clear the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/logout/route.ts\n * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST() {\n * const response = NextResponse.json({ ok: true });\n * return clearSessionCookieOnResponse(response);\n * }\n * ```\n */\nexport function clearSessionCookieOnResponse(\n response: NextResponse,\n options: {\n cookieName?: string;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n response.cookies.delete(cookieName);\n return response;\n}\n"],"mappings":"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","names":["NextResponse"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/api-helpers.ts","../src/cookie-utils.ts","../src/index.ts","../src/route-handlers.ts"],"sourcesContent":["import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js middleware-based auth (Next.js <16).\n *\n * @deprecated For Next.js >=16, use {@link ProxyConfig} with {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16.\n */\nexport type MiddlewareConfig = AuthHandlerConfig;\n\n/**\n * Create Next.js middleware for authentication and authorization.\n *\n * For Next.js 14–15 projects that use the `middleware.ts` file convention.\n *\n * @deprecated For Next.js >=16, use {@link createAuthProxy} instead.\n * The `middleware.ts` file convention is deprecated in Next.js 16 in favor of `proxy.ts`.\n *\n * @example\n * ```typescript\n * // middleware.ts (Next.js 14–15)\n * import { createAuthMiddleware } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthMiddleware({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthMiddleware(config: MiddlewareConfig) {\n return createAuthHandler(config);\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { SessionManager, RBACEngine, type AuthSession, type AuthUser } from '@stackwright-pro/auth';\n\nexport interface ProtectedRouteConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session\n */\nexport type AuthenticatedHandler<T = any> = (\n req: NextApiRequest,\n res: NextApiResponse<T>,\n session: AuthSession\n) => void | Promise<void>;\n\n/**\n * Wrap API route with authentication/authorization\n *\n * @example\n * ```typescript\n * // pages/api/equipment/[id].ts\n * export default protectedRoute(\n * async (req, res, session) => {\n * const equipment = await getEquipment(req.query.id);\n * res.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRoute<T = any>(\n handler: AuthenticatedHandler<T>,\n config: ProtectedRouteConfig\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n const { sessionManager, roles, permissions, cookieName = 'stackwright_session' } = config;\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // Extract session from cookie\n const sessionCookie = req.cookies[cookieName];\n\n if (!sessionCookie) {\n res.status(401).json({ error: 'Not authenticated' } as any);\n return;\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie);\n\n if (!session || sessionManager.isExpired(session)) {\n res.status(401).json({ error: 'Session expired' } as any);\n return;\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for consistent wildcard-aware checking\n if (!config.rbacEngine.hasAnyRole(session.user, roles)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (config.rbacEngine) {\n // Use RBAC engine for wildcard-aware permission checking\n if (!config.rbacEngine.hasAllPermissions(session.user, permissions)) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n } else {\n // Fallback: direct check (no wildcard support)\n const hasAllPermissions = permissions.every((permission) =>\n session.user.permissions?.includes(permission)\n );\n if (!hasAllPermissions) {\n res.status(403).json({ error: 'Insufficient permissions' } as any);\n return;\n }\n }\n }\n\n // Call handler with session\n await handler(req, res, session);\n };\n}\n","import type { NextApiResponse } from 'next';\nimport { serializeCookie, clearCookie, type CookieOptions } from '@stackwright-pro/auth';\n\n/**\n * Set session cookie in Next.js API response\n */\nexport function setSessionCookie(\n res: NextApiResponse,\n sessionJwt: string,\n options: CookieOptions = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n const maxAge = options.maxAge || 900; // 15 minutes default\n\n const cookie = serializeCookie(cookieName, sessionJwt, {\n ...options,\n maxAge,\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n });\n\n res.setHeader('Set-Cookie', cookie);\n}\n\n/**\n * Clear session cookie in Next.js API response\n */\nexport function clearSessionCookie(\n res: NextApiResponse,\n options: Pick<CookieOptions, 'name' | 'domain' | 'path'> = {}\n): void {\n const cookieName = options.name || 'stackwright_session';\n\n const cookie = clearCookie(cookieName, options);\n\n res.setHeader('Set-Cookie', cookie);\n}\n","export { createAuthMiddleware, type MiddlewareConfig } from './middleware.js';\nexport { createAuthProxy, type ProxyConfig } from './proxy.js';\nexport { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\nexport {\n protectedRoute,\n type ProtectedRouteConfig,\n type AuthenticatedHandler,\n} from './api-helpers.js';\nexport { setSessionCookie, clearSessionCookie } from './cookie-utils.js';\n\n// Re-export commonly used types/classes from core\nexport type { AuthUser, AuthSession, AuthConfig } from '@stackwright-pro/auth';\n\n// Re-export RBAC engine\nexport { RBACEngine } from '@stackwright-pro/auth';\n\n// Re-export React context components for Next.js apps\nexport { AuthProvider, AuthContext, useAuth, useRequireAuth } from '@stackwright-pro/auth';\nexport type { AuthProviderProps, AuthContextValue } from '@stackwright-pro/auth';\n\n// App Router Route Handler helpers (replaces api-helpers for App Router users)\nexport {\n protectedRouteHandler,\n setSessionCookieOnResponse,\n clearSessionCookieOnResponse,\n type RouteHandlerConfig,\n type AuthenticatedRouteHandler,\n} from './route-handlers.js';\n","import { type NextRequest, NextResponse } from 'next/server';\nimport { SessionManager, RBACEngine, type AuthSession } from '@stackwright-pro/auth';\n\nexport interface RouteHandlerConfig {\n /**\n * Session manager for validating sessions\n */\n sessionManager: SessionManager;\n\n /**\n * Required roles (user must have ANY of these)\n */\n roles?: string[];\n\n /**\n * Required permissions (user must have ALL of these)\n */\n permissions?: string[];\n\n /**\n * Cookie name (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * Optional RBAC engine for wildcard-aware role/permission checking.\n * When provided, authorization decisions use RBACEngine (consistent\n * with middleware). Without it, falls back to direct array checks.\n */\n rbacEngine?: RBACEngine;\n}\n\n/**\n * Handler function with authenticated session — App Router Route Handler signature.\n */\nexport type AuthenticatedRouteHandler = (\n request: NextRequest,\n context: { params: Record<string, string> },\n session: AuthSession\n) => Promise<Response> | Response;\n\n/**\n * Wrap an App Router Route Handler with authentication/authorization.\n *\n * This is the App Router equivalent of `protectedRoute()` from `api-helpers.ts`.\n *\n * @example\n * ```typescript\n * // app/api/equipment/[id]/route.ts\n * import { protectedRouteHandler } from '@stackwright-pro/auth-nextjs';\n *\n * export const GET = protectedRouteHandler(\n * async (request, { params }, session) => {\n * const equipment = await getEquipment(params.id);\n * return NextResponse.json(equipment);\n * },\n * {\n * sessionManager,\n * roles: ['ANALYST', 'ADMIN'],\n * }\n * );\n * ```\n */\nexport function protectedRouteHandler(\n handler: AuthenticatedRouteHandler,\n config: RouteHandlerConfig\n): (request: NextRequest, context: { params: Record<string, string> }) => Promise<Response> {\n const {\n sessionManager,\n roles,\n permissions,\n cookieName = 'stackwright_session',\n rbacEngine,\n } = config;\n\n return async (\n request: NextRequest,\n context: { params: Record<string, string> }\n ): Promise<Response> => {\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n\n if (!sessionCookie) {\n return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });\n }\n\n // Verify session\n const session = await sessionManager.deserialize(sessionCookie.value);\n\n if (!session || sessionManager.isExpired(session)) {\n return NextResponse.json({ error: 'Session expired' }, { status: 401 });\n }\n\n // Check roles\n if (roles && roles.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAnyRole(session.user, roles)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasRole = roles.some((role) => session.user.roles.includes(role));\n if (!hasRole) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n // Check permissions\n if (permissions && permissions.length > 0) {\n if (rbacEngine) {\n if (!rbacEngine.hasAllPermissions(session.user, permissions)) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n } else {\n const hasAllPermissions = permissions.every((p) => session.user.permissions?.includes(p));\n if (!hasAllPermissions) {\n return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 });\n }\n }\n }\n\n return handler(request, context, session);\n };\n}\n\n/**\n * Set the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `setSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/login/route.ts\n * import { setSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST(request: NextRequest) {\n * const session = await createSession(request);\n * const jwt = await sessionManager.serialize(session);\n * const response = NextResponse.json({ ok: true });\n * return setSessionCookieOnResponse(response, jwt);\n * }\n * ```\n */\nexport function setSessionCookieOnResponse(\n response: NextResponse,\n sessionJwt: string,\n options: {\n cookieName?: string;\n maxAge?: number;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n const maxAge = options.maxAge ?? 900; // 15 minutes\n\n response.cookies.set(cookieName, sessionJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge,\n });\n\n return response;\n}\n\n/**\n * Clear the session cookie on a `NextResponse` (App Router).\n *\n * This is the App Router equivalent of `clearSessionCookie()` from `cookie-utils.ts`.\n *\n * @example\n * ```typescript\n * // app/api/auth/logout/route.ts\n * import { clearSessionCookieOnResponse } from '@stackwright-pro/auth-nextjs';\n *\n * export async function POST() {\n * const response = NextResponse.json({ ok: true });\n * return clearSessionCookieOnResponse(response);\n * }\n * ```\n */\nexport function clearSessionCookieOnResponse(\n response: NextResponse,\n options: {\n cookieName?: string;\n } = {}\n): NextResponse {\n const cookieName = options.cookieName ?? 'stackwright_session';\n response.cookies.delete(cookieName);\n return response;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,67 @@
1
+ import * as next_server_js from 'next/server.js';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { AuthConfig, SessionManager, RBACEngine } from '@stackwright-pro/auth';
4
+
5
+ interface AuthHandlerConfig {
6
+ /**
7
+ * Auth configuration
8
+ */
9
+ authConfig: AuthConfig;
10
+ /**
11
+ * Session manager instance
12
+ */
13
+ sessionManager: SessionManager;
14
+ /**
15
+ * RBAC engine instance
16
+ */
17
+ rbacEngine: RBACEngine;
18
+ /**
19
+ * Cookie name for session (default: 'stackwright_session')
20
+ */
21
+ cookieName?: string;
22
+ /**
23
+ * URL to redirect to when authentication fails (default: '/login')
24
+ */
25
+ loginUrl?: string;
26
+ /**
27
+ * URL to redirect to when authorization fails (default: '/unauthorized')
28
+ */
29
+ unauthorizedUrl?: string;
30
+ }
31
+ /**
32
+ * Core auth handler shared by both the middleware (Next.js <16) and
33
+ * proxy (Next.js >=16) conventions.
34
+ *
35
+ * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.
36
+ */
37
+ declare function createAuthHandler(config: AuthHandlerConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
38
+
39
+ /**
40
+ * Configuration for Next.js proxy-based auth (Next.js >=16).
41
+ */
42
+ type ProxyConfig = AuthHandlerConfig;
43
+ /**
44
+ * Create a Next.js proxy handler for authentication and authorization.
45
+ *
46
+ * For Next.js >=16 projects that use the `proxy.ts` file convention,
47
+ * replacing the deprecated `middleware.ts` convention.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // proxy.ts (Next.js >=16)
52
+ * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';
53
+ *
54
+ * export default createAuthProxy({
55
+ * authConfig,
56
+ * sessionManager,
57
+ * rbacEngine,
58
+ * });
59
+ *
60
+ * export const config = {
61
+ * matcher: ['/((?!_next|api/auth).*)'],
62
+ * };
63
+ * ```
64
+ */
65
+ declare function createAuthProxy(config: ProxyConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
66
+
67
+ export { type AuthHandlerConfig as A, type ProxyConfig as P, createAuthProxy as a, createAuthHandler as c };
@@ -0,0 +1,67 @@
1
+ import * as next_server_js from 'next/server.js';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { AuthConfig, SessionManager, RBACEngine } from '@stackwright-pro/auth';
4
+
5
+ interface AuthHandlerConfig {
6
+ /**
7
+ * Auth configuration
8
+ */
9
+ authConfig: AuthConfig;
10
+ /**
11
+ * Session manager instance
12
+ */
13
+ sessionManager: SessionManager;
14
+ /**
15
+ * RBAC engine instance
16
+ */
17
+ rbacEngine: RBACEngine;
18
+ /**
19
+ * Cookie name for session (default: 'stackwright_session')
20
+ */
21
+ cookieName?: string;
22
+ /**
23
+ * URL to redirect to when authentication fails (default: '/login')
24
+ */
25
+ loginUrl?: string;
26
+ /**
27
+ * URL to redirect to when authorization fails (default: '/unauthorized')
28
+ */
29
+ unauthorizedUrl?: string;
30
+ }
31
+ /**
32
+ * Core auth handler shared by both the middleware (Next.js <16) and
33
+ * proxy (Next.js >=16) conventions.
34
+ *
35
+ * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.
36
+ */
37
+ declare function createAuthHandler(config: AuthHandlerConfig): (request: NextRequest) => Promise<NextResponse | undefined>;
38
+
39
+ /**
40
+ * Configuration for Next.js proxy-based auth (Next.js >=16).
41
+ */
42
+ type ProxyConfig = AuthHandlerConfig;
43
+ /**
44
+ * Create a Next.js proxy handler for authentication and authorization.
45
+ *
46
+ * For Next.js >=16 projects that use the `proxy.ts` file convention,
47
+ * replacing the deprecated `middleware.ts` convention.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // proxy.ts (Next.js >=16)
52
+ * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';
53
+ *
54
+ * export default createAuthProxy({
55
+ * authConfig,
56
+ * sessionManager,
57
+ * rbacEngine,
58
+ * });
59
+ *
60
+ * export const config = {
61
+ * matcher: ['/((?!_next|api/auth).*)'],
62
+ * };
63
+ * ```
64
+ */
65
+ declare function createAuthProxy(config: ProxyConfig): (request: next_server_js.NextRequest) => Promise<next_server_js.NextResponse | undefined>;
66
+
67
+ export { type AuthHandlerConfig as A, type ProxyConfig as P, createAuthProxy as a, createAuthHandler as c };
@@ -0,0 +1,4 @@
1
+ import 'next/server.js';
2
+ export { P as ProxyConfig, a as createAuthProxy } from './proxy-Blj2kIJp.mjs';
3
+ import 'next/server';
4
+ import '@stackwright-pro/auth';
@@ -0,0 +1,4 @@
1
+ import 'next/server.js';
2
+ export { P as ProxyConfig, a as createAuthProxy } from './proxy-Blj2kIJp.js';
3
+ import 'next/server';
4
+ import '@stackwright-pro/auth';
package/dist/proxy.js ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/proxy.ts
21
+ var proxy_exports = {};
22
+ __export(proxy_exports, {
23
+ createAuthProxy: () => createAuthProxy
24
+ });
25
+ module.exports = __toCommonJS(proxy_exports);
26
+
27
+ // src/auth-handler.ts
28
+ var import_server = require("next/server");
29
+ function createAuthHandler(config) {
30
+ const {
31
+ sessionManager,
32
+ rbacEngine,
33
+ cookieName = "stackwright_session",
34
+ loginUrl = "/login",
35
+ unauthorizedUrl = "/unauthorized"
36
+ } = config;
37
+ return async function authHandler(request) {
38
+ const { pathname } = request.nextUrl;
39
+ if (rbacEngine.isPublicRoute(pathname)) {
40
+ return void 0;
41
+ }
42
+ const sessionCookie = request.cookies.get(cookieName);
43
+ let session = null;
44
+ if (sessionCookie) {
45
+ session = await sessionManager.deserialize(sessionCookie.value);
46
+ }
47
+ if (!session || sessionManager.isExpired(session)) {
48
+ const url = request.nextUrl.clone();
49
+ url.pathname = loginUrl;
50
+ url.searchParams.set("redirect", pathname);
51
+ return import_server.NextResponse.redirect(url);
52
+ }
53
+ if (!rbacEngine.canAccessRoute(session.user, pathname)) {
54
+ const url = request.nextUrl.clone();
55
+ url.pathname = unauthorizedUrl;
56
+ return import_server.NextResponse.redirect(url);
57
+ }
58
+ if (sessionManager.shouldRefresh(session)) {
59
+ const refreshed = await sessionManager.refreshSession(session);
60
+ const newJwt = await sessionManager.serialize(refreshed);
61
+ const response = import_server.NextResponse.next();
62
+ response.cookies.set(cookieName, newJwt, {
63
+ httpOnly: true,
64
+ secure: true,
65
+ sameSite: "lax",
66
+ maxAge: 900
67
+ // 15 minutes
68
+ });
69
+ return response;
70
+ }
71
+ return void 0;
72
+ };
73
+ }
74
+
75
+ // src/proxy.ts
76
+ function createAuthProxy(config) {
77
+ return createAuthHandler(config);
78
+ }
79
+ // Annotate the CommonJS export names for ESM import in node:
80
+ 0 && (module.exports = {
81
+ createAuthProxy
82
+ });
83
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/proxy.ts","../src/auth-handler.ts"],"sourcesContent":["import { createAuthHandler, type AuthHandlerConfig } from './auth-handler.js';\n\n/**\n * Configuration for Next.js proxy-based auth (Next.js >=16).\n */\nexport type ProxyConfig = AuthHandlerConfig;\n\n/**\n * Create a Next.js proxy handler for authentication and authorization.\n *\n * For Next.js >=16 projects that use the `proxy.ts` file convention,\n * replacing the deprecated `middleware.ts` convention.\n *\n * @example\n * ```typescript\n * // proxy.ts (Next.js >=16)\n * import { createAuthProxy } from '@stackwright-pro/auth-nextjs';\n *\n * export default createAuthProxy({\n * authConfig,\n * sessionManager,\n * rbacEngine,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next|api/auth).*)'],\n * };\n * ```\n */\nexport function createAuthProxy(config: ProxyConfig) {\n return createAuthHandler(config);\n}\n","import { type NextRequest, NextResponse } from 'next/server';\nimport {\n SessionManager,\n RBACEngine,\n type AuthConfig,\n type AuthSession,\n} from '@stackwright-pro/auth';\n\nexport interface AuthHandlerConfig {\n /**\n * Auth configuration\n */\n authConfig: AuthConfig;\n\n /**\n * Session manager instance\n */\n sessionManager: SessionManager;\n\n /**\n * RBAC engine instance\n */\n rbacEngine: RBACEngine;\n\n /**\n * Cookie name for session (default: 'stackwright_session')\n */\n cookieName?: string;\n\n /**\n * URL to redirect to when authentication fails (default: '/login')\n */\n loginUrl?: string;\n\n /**\n * URL to redirect to when authorization fails (default: '/unauthorized')\n */\n unauthorizedUrl?: string;\n}\n\n/**\n * Core auth handler shared by both the middleware (Next.js <16) and\n * proxy (Next.js >=16) conventions.\n *\n * @internal — consumers should use `createAuthMiddleware` or `createAuthProxy`.\n */\nexport function createAuthHandler(config: AuthHandlerConfig) {\n const {\n sessionManager,\n rbacEngine,\n cookieName = 'stackwright_session',\n loginUrl = '/login',\n unauthorizedUrl = '/unauthorized',\n } = config;\n\n return async function authHandler(request: NextRequest): Promise<NextResponse | undefined> {\n const { pathname } = request.nextUrl;\n\n // Check if route is public\n if (rbacEngine.isPublicRoute(pathname)) {\n return undefined; // Continue to next handler\n }\n\n // Extract session from cookie\n const sessionCookie = request.cookies.get(cookieName);\n let session: AuthSession | null = null;\n\n if (sessionCookie) {\n session = await sessionManager.deserialize(sessionCookie.value);\n }\n\n // Check if session is valid\n if (!session || sessionManager.isExpired(session)) {\n // Redirect to login\n const url = request.nextUrl.clone();\n url.pathname = loginUrl;\n url.searchParams.set('redirect', pathname);\n return NextResponse.redirect(url);\n }\n\n // Check if user can access route\n if (!rbacEngine.canAccessRoute(session.user, pathname)) {\n // Redirect to unauthorized\n const url = request.nextUrl.clone();\n url.pathname = unauthorizedUrl;\n return NextResponse.redirect(url);\n }\n\n // Refresh session if needed\n if (sessionManager.shouldRefresh(session)) {\n const refreshed = await sessionManager.refreshSession(session);\n const newJwt = await sessionManager.serialize(refreshed);\n\n const response = NextResponse.next();\n response.cookies.set(cookieName, newJwt, {\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n maxAge: 900, // 15 minutes\n });\n\n return response;\n }\n\n // Allow request to continue\n return undefined;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA+C;AA8CxC,SAAS,kBAAkB,QAA2B;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB;AAAA,EACpB,IAAI;AAEJ,SAAO,eAAe,YAAY,SAAyD;AACzF,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,WAAW,cAAc,QAAQ,GAAG;AACtC,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU;AACpD,QAAI,UAA8B;AAElC,QAAI,eAAe;AACjB,gBAAU,MAAM,eAAe,YAAY,cAAc,KAAK;AAAA,IAChE;AAGA,QAAI,CAAC,WAAW,eAAe,UAAU,OAAO,GAAG;AAEjD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,UAAI,aAAa,IAAI,YAAY,QAAQ;AACzC,aAAO,2BAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,CAAC,WAAW,eAAe,QAAQ,MAAM,QAAQ,GAAG;AAEtD,YAAM,MAAM,QAAQ,QAAQ,MAAM;AAClC,UAAI,WAAW;AACf,aAAO,2BAAa,SAAS,GAAG;AAAA,IAClC;AAGA,QAAI,eAAe,cAAc,OAAO,GAAG;AACzC,YAAM,YAAY,MAAM,eAAe,eAAe,OAAO;AAC7D,YAAM,SAAS,MAAM,eAAe,UAAU,SAAS;AAEvD,YAAM,WAAW,2BAAa,KAAK;AACnC,eAAS,QAAQ,IAAI,YAAY,QAAQ;AAAA,QACvC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA,MACV,CAAC;AAED,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AACF;;;AD9EO,SAAS,gBAAgB,QAAqB;AACnD,SAAO,kBAAkB,MAAM;AACjC;","names":[]}
package/dist/proxy.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ createAuthProxy
3
+ } from "./chunk-MFZSH4YL.mjs";
4
+ export {
5
+ createAuthProxy
6
+ };
7
+ //# sourceMappingURL=proxy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackwright-pro/auth-nextjs",
3
- "version": "0.2.0-alpha.7",
3
+ "version": "0.3.0-alpha.0",
4
4
  "description": "Next.js adapter for Stackwright Pro authentication",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "main": "./dist/index.js",
@@ -11,19 +11,25 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.mjs",
13
13
  "require": "./dist/index.js"
14
+ },
15
+ "./proxy": {
16
+ "types": "./dist/proxy.d.ts",
17
+ "import": "./dist/proxy.mjs",
18
+ "require": "./dist/proxy.js"
14
19
  }
15
20
  },
16
21
  "files": [
17
22
  "dist"
18
23
  ],
19
24
  "dependencies": {
20
- "@stackwright-pro/auth": "0.2.0-alpha.5"
25
+ "@stackwright-pro/auth": "0.2.0-alpha.11"
21
26
  },
22
27
  "devDependencies": {
23
- "@types/node": "^25.4.0",
28
+ "@types/node": "^25.9.2",
29
+ "reflect-metadata": "^0.2.2",
24
30
  "tsup": "^8.5.1",
25
31
  "typescript": "^6.0.3",
26
- "vitest": "^4.0.18"
32
+ "vitest": "^4.1.8"
27
33
  },
28
34
  "peerDependencies": {
29
35
  "next": ">=14.0.0 <16.0.0-beta.0 || >=16.2.3",