@logickernel/bridge 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +188 -262
  2. package/package.json +1 -2
package/README.md CHANGED
@@ -1,83 +1,171 @@
1
- # Bridge
1
+ # @logickernel/bridge
2
2
 
3
- Framework-agnostic micro-frontend authentication library for AuthJS/NextAuth JWT tokens.
3
+ Framework-agnostic micro-frontend authentication library for AuthJS/NextAuth JWT tokens with built-in Next.js components.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
8
  npm install @logickernel/bridge
9
- # or
10
- pnpm add @logickernel/bridge
11
- # or
12
- yarn add @logickernel/bridge
13
9
  ```
14
10
 
15
- ## Features
16
-
17
- - **Framework-agnostic core** - JWT decoding and validation works anywhere
18
- - **Next.js adapter** - First-class support for Next.js App Router
19
- - **Role-based access control** - Check user roles against organization permissions
20
- - **TypeScript-first** - Full type safety with detailed type exports
21
- - **Dual ESM/CJS** - Works in any JavaScript environment
11
+ ## Quick Start (Next.js)
22
12
 
23
- ## Quick Start
13
+ ### 1. Environment Variables
24
14
 
25
- ### Environment Variables
15
+ Add these to the `.env` file of your application:
26
16
 
27
17
  ```bash
28
18
  AUTH_SECRET=your-nextauth-secret # Required: Same secret used by your kernel/auth server
29
- AUTH_URL=http://localhost:3000 # Required: Used internally for API calls to the kernel
30
- BASE_URL=http://localhost:7001 # Optional: Facade URL for user-facing redirects (used by getBaseUrl())
31
- AUTH_COOKIE=authjs.session-token # Optional: Cookie name for session token (defaults to authjs.session-token)
19
+ AUTH_URL=http://localhost:3000 # Required: Base URL of your kernel/auth server
20
+ BASE_URL=http://localhost:7001 # Optional: Your facade URL for user-facing redirects
32
21
  ```
33
22
 
34
- ### Core Usage (Framework-Agnostic)
23
+ ### 2. Configure Next.js
24
+
25
+ In your `next.config.ts`, add the bridge package to `transpilePackages`:
35
26
 
36
27
  ```typescript
37
- import {
38
- decodeSessionToken,
39
- extractSessionCookie,
40
- fetchUserRoles,
41
- hasAnyRole,
42
- } from "@logickernel/bridge"
28
+ import type { NextConfig } from "next"
43
29
 
44
- // Decode a JWT session token
45
- const result = await decodeSessionToken(token, secret, isSecure)
46
- if (result.success) {
47
- console.log(result.session.user.email)
48
- console.log(result.session.user.id)
49
- } else {
50
- console.error(result.error) // "missing_token" | "missing_secret" | "decode_error" | "expired"
30
+ const nextConfig: NextConfig = {
31
+ transpilePackages: ["@logickernel/bridge"],
32
+ // ... other config
51
33
  }
52
34
 
53
- // Check user roles (uses AUTH_URL environment variable internally)
54
- // User ID is determined from the session token
55
- const roles = await fetchUserRoles({
56
- organizationId: "org-456",
57
- sessionToken: token,
58
- })
35
+ export default nextConfig
36
+ ```
37
+
38
+ **Why `transpilePackages`?** This tells Next.js to transpile the bridge library's TypeScript/ES modules during the build process. This is necessary because the library uses modern JavaScript features and needs to be processed by Next.js's bundler (Turbopack or webpack) to work correctly with your application.
39
+
40
+ ### 3. Use the Layout Component
41
+
42
+ The library provides a ready-to-use layout component with sidebar navigation. You need a thin wrapper component in your app to establish the client/server boundary.
43
+
44
+ **Step 1: Create a wrapper component**
45
+
46
+ Create `src/components/app-layout-wrapper.tsx`:
59
47
 
60
- if (roles.success && hasAnyRole(roles.roles, ["organization.owner"])) {
61
- // User is an owner
48
+ ```typescript
49
+ "use client"
50
+
51
+ /**
52
+ * Client boundary wrapper for AppLayout.
53
+ *
54
+ * This wrapper is required because "use client" directives are not preserved
55
+ * in bundled library code. Next.js needs this directive in your source code
56
+ * to properly handle the client/server boundary.
57
+ */
58
+ import { AppLayout } from "@logickernel/bridge/next-components"
59
+
60
+ export function AppLayoutWrapper({
61
+ children,
62
+ organizationId,
63
+ apiBaseUrl,
64
+ }: {
65
+ children: React.ReactNode
66
+ organizationId?: string
67
+ apiBaseUrl?: string
68
+ }) {
69
+ return (
70
+ <AppLayout
71
+ organizationId={organizationId}
72
+ apiBaseUrl={apiBaseUrl}
73
+ >
74
+ {children}
75
+ </AppLayout>
76
+ )
62
77
  }
63
78
  ```
64
79
 
65
- ### Next.js Usage
80
+ **Step 2: Use in your layout**
81
+
82
+ In your `app/app/layout.tsx` (or wherever your app layout is):
66
83
 
67
84
  ```typescript
68
- import {
69
- getSession,
70
- getUserRoles,
71
- withAuth,
72
- checkPageAuth,
73
- createProxyHandler,
74
- } from "@logickernel/bridge/next"
85
+ import { redirect } from "next/navigation"
86
+ import { auth } from "@/lib/next-auth" // or your auth setup
87
+ import { AppLayoutWrapper } from "@/components/app-layout-wrapper"
88
+
89
+ export default async function Layout({
90
+ children,
91
+ }: {
92
+ children: React.ReactNode
93
+ }) {
94
+ const session = await auth()
95
+
96
+ if (!session?.user) {
97
+ redirect("/auth/signin?callbackUrl=/app")
98
+ }
99
+
100
+ return (
101
+ <AppLayoutWrapper>
102
+ {children}
103
+ </AppLayoutWrapper>
104
+ )
105
+ }
106
+ ```
107
+
108
+ The `user` prop is **not needed** - the layout automatically fetches user information from the navigation API endpoint.
109
+
110
+ ### 4. Navigation API Endpoint
111
+
112
+ The layout component automatically loads navigation items from your API. The **Kernel Core application** provides this endpoint at `/api/navigation/[organization_id]`.
113
+
114
+ **Required Endpoint:** `GET /api/navigation/[organization_id]`
115
+
116
+ **Response Payload:**
117
+
118
+ ```typescript
119
+ {
120
+ items: NavigationItem[]
121
+ organizationId: string
122
+ organizations: NavigationOrganization[]
123
+ user: {
124
+ id: string
125
+ name: string | null
126
+ email: string
127
+ image: string | null
128
+ }
129
+ }
130
+
131
+ interface NavigationItem {
132
+ title: string
133
+ url?: string // If missing, item is treated as a section label
134
+ icon?: string // Lucide icon name (e.g., "LayoutDashboard", "Users")
135
+ isActive?: boolean // Whether the item should be highlighted
136
+ items?: { // Sub-items for collapsible navigation
137
+ title: string
138
+ url: string
139
+ }[]
140
+ }
141
+
142
+ interface NavigationOrganization {
143
+ id: string
144
+ name: string
145
+ logo?: string // Lucide icon name for the organization logo
146
+ plan?: string // Optional plan/badge text
147
+ }
75
148
  ```
76
149
 
77
- #### Server Components / Pages
150
+ **Note:** The endpoint should be protected and only return data the authenticated user has access to. The `{organizationId}` placeholder in item URLs will be automatically replaced with the current organization ID.
151
+
152
+ ## Features
153
+
154
+ The `AppLayout` component provides:
155
+
156
+ - ✅ **Built-in sidebar navigation** with collapsible sections
157
+ - ✅ **Organization switcher** - Switch between organizations the user belongs to
158
+ - ✅ **User menu** with profile information and sign-out
159
+ - ✅ **Responsive design** - Works on mobile and desktop
160
+ - ✅ **Automatic navigation loading** - Fetches from your API endpoint
161
+ - ✅ **Consistent UI** - Same look and feel across all microfrontends using the library
162
+ - ✅ **No extra components required** - All UI components are included in the library
163
+
164
+ ## Next.js Utilities
165
+
166
+ ### Server Components / Pages
78
167
 
79
168
  ```typescript
80
- // app/dashboard/page.tsx
81
169
  import { getSession } from "@logickernel/bridge/next"
82
170
  import { redirect } from "next/navigation"
83
171
 
@@ -92,42 +180,39 @@ export default async function DashboardPage() {
92
180
  }
93
181
  ```
94
182
 
95
- #### Role-Based Page Protection
183
+ ### Role-Based Page Protection
96
184
 
97
185
  ```typescript
98
- // app/[org_id]/admin/page.tsx
99
186
  import { checkPageAuth } from "@logickernel/bridge/next"
100
187
  import { redirect, notFound } from "next/navigation"
101
188
 
102
189
  export default async function AdminPage({
103
190
  params,
104
191
  }: {
105
- params: Promise<{ org_id: string }>
192
+ params: Promise<{ organization_id: string }>
106
193
  }) {
107
- const { org_id } = await params
194
+ const { organization_id } = await params
108
195
 
109
196
  const auth = await checkPageAuth({
110
197
  requiredRoles: ["organization.owner", "organization.editor"],
111
- organizationId: org_id,
198
+ organizationId: organization_id,
112
199
  })
113
200
 
114
201
  if (!auth.authenticated) {
115
202
  redirect(auth.redirectUrl)
116
203
  }
117
204
 
118
- // User is authenticated but may not have required roles
119
- if (!auth.roles.some(r => ["organization.owner", "organization.editor"].includes(r))) {
205
+ if (!auth.hasRequiredRole) {
120
206
  notFound()
121
207
  }
122
208
 
123
- return <div>Admin Panel for {session.user.email}</div>
209
+ return <div>Admin Panel for {auth.session.user.email}</div>
124
210
  }
125
211
  ```
126
212
 
127
- #### API Routes
213
+ ### API Routes
128
214
 
129
215
  ```typescript
130
- // app/api/data/route.ts
131
216
  import { withAuth } from "@logickernel/bridge/next"
132
217
  import { NextResponse } from "next/server"
133
218
 
@@ -152,10 +237,10 @@ export const DELETE = withAuth(
152
237
  )
153
238
  ```
154
239
 
155
- #### Middleware / Proxy
240
+ ### Middleware / Proxy
156
241
 
157
242
  ```typescript
158
- // src/proxy.ts (or middleware.ts)
243
+ // middleware.ts or proxy.ts
159
244
  import { createProxyHandler, type ProxyOptions } from "@logickernel/bridge/next"
160
245
 
161
246
  const config: ProxyOptions = {
@@ -166,7 +251,7 @@ const config: ProxyOptions = {
166
251
  },
167
252
  dynamicRoutes: [
168
253
  {
169
- pattern: "/[organization_id]/admin",
254
+ pattern: "/app/[organization_id]/admin",
170
255
  requiredRoles: ["organization.owner"],
171
256
  },
172
257
  ],
@@ -174,174 +259,27 @@ const config: ProxyOptions = {
174
259
 
175
260
  export const proxy = createProxyHandler(config)
176
261
 
177
- // Static config for Next.js (must be in the same file)
178
- export const middlewareConfig = {
262
+ // Static config for Next.js middleware
263
+ export const config = {
179
264
  matcher: ["/((?!_next/static|_next/image|favicon.ico|public).*)"],
180
265
  }
181
266
  ```
182
267
 
183
- #### Layout Components
184
-
185
- The bridge library provides ready-to-use layout components with built-in navigation connected to the Core of the Kernel.
186
-
187
- ```typescript
188
- // app/app/layout.tsx (or your app layout)
189
- "use client"
190
-
191
- import { AppLayout, type User } from "@logickernel/bridge/next/components"
192
- import { useSession } from "next-auth/react"
193
-
194
- export default function Layout({ children }: { children: React.ReactNode }) {
195
- const { data: session } = useSession()
196
-
197
- if (!session?.user) {
198
- return <>{children}</>
199
- }
200
-
201
- const user: User = {
202
- name: session.user.name || null,
203
- email: session.user.email || "",
204
- image: session.user.image || null,
205
- }
206
-
207
- return (
208
- <AppLayout
209
- user={user}
210
- organizationId={organizationId} // Optional: Current organization ID
211
- apiBaseUrl={apiBaseUrl} // Optional: API base URL for navigation
212
- >
213
- {children}
214
- </AppLayout>
215
- )
216
- }
217
- ```
218
-
219
- **For Server Components:**
268
+ ## Import Paths
220
269
 
221
- ```typescript
222
- // app/app/layout.tsx
223
- import { AppLayout, type User } from "@logickernel/bridge/next/components"
224
- import { redirect } from "next/navigation"
225
- import { auth } from "@/lib/next-auth" // or your NextAuth setup
270
+ The library uses top-level exports (no subpath exports) for better bundler compatibility:
226
271
 
227
- export default async function Layout({
228
- children,
229
- params,
230
- }: {
231
- children: React.ReactNode
232
- params: Promise<{ organization_id?: string }>
233
- }) {
234
- const session = await auth()
235
-
236
- if (!session?.user) {
237
- redirect("/auth/signin")
238
- }
239
-
240
- const { organization_id } = await params
241
-
242
- const user: User = {
243
- name: session.user.name || null,
244
- email: session.user.email || "",
245
- image: session.user.image || null,
246
- }
247
-
248
- return (
249
- <AppLayout
250
- user={user}
251
- organizationId={organization_id}
252
- apiBaseUrl={process.env.AUTH_URL}
253
- >
254
- {children}
255
- </AppLayout>
256
- )
257
- }
258
- ```
259
-
260
- **Note:** You can also use `getSession()` from `@logickernel/bridge/next` instead of NextAuth's `auth()` function. Both will work, but NextAuth's `auth()` is recommended if you're using NextAuth v5.
261
-
262
- **Features:**
263
- - ✅ Built-in sidebar navigation with organization switcher
264
- - ✅ User menu with sign-out
265
- - ✅ Responsive design (mobile/desktop)
266
- - ✅ Automatic navigation loading from your API
267
- - ✅ No component passing required - UI components are included in the library
268
- - ✅ Consistent UI across all microfrontends using the bridge
269
-
270
- The `AppLayout` component automatically:
271
- - Loads navigation items from `/api/navigation/[organization_id]` endpoint
272
- - Displays organizations the user belongs to
273
- - Handles organization switching
274
- - Provides user profile menu with sign-out
275
-
276
- **Required API Endpoint:**
277
-
278
- The navigation requires an endpoint at `/api/navigation/[organization_id]` that returns:
279
- ```typescript
280
- {
281
- items: NavigationItem[],
282
- organizations: NavigationOrganization[]
283
- }
284
- ```
285
-
286
- **Type Definitions:**
287
272
  ```typescript
288
- interface NavigationItem {
289
- title: string
290
- url?: string // If missing, item is treated as a section label
291
- icon?: string // Icon name (mapped using getIconComponent)
292
- isActive?: boolean // Whether the item should be highlighted
293
- items?: { // Sub-items for collapsible navigation
294
- title: string
295
- url: string
296
- }[]
297
- }
273
+ // Core utilities (framework-agnostic)
274
+ import { decodeSessionToken, fetchUserRoles } from "@logickernel/bridge"
298
275
 
299
- interface NavigationOrganization {
300
- id: string
301
- name: string
302
- logo?: string // Icon name for the organization logo
303
- plan?: string // Optional plan/badge text
304
- }
305
- ```
276
+ // Next.js utilities (server components, API routes, middleware)
277
+ import { getSession, withAuth, checkPageAuth } from "@logickernel/bridge/next"
306
278
 
307
- **Example Response:**
308
- ```json
309
- {
310
- "items": [
311
- {
312
- "title": "Dashboard",
313
- "url": "/app/{organizationId}/home",
314
- "icon": "LayoutDashboard"
315
- },
316
- {
317
- "title": "Team",
318
- "icon": "Users" // Section label (no URL)
319
- },
320
- {
321
- "title": "Members",
322
- "url": "/app/{organizationId}/members",
323
- "icon": "User",
324
- "items": [
325
- {
326
- "title": "All Members",
327
- "url": "/app/{organizationId}/members"
328
- }
329
- ]
330
- }
331
- ],
332
- "organizations": [
333
- {
334
- "id": "org-123",
335
- "name": "Acme Corp",
336
- "logo": "Building",
337
- "plan": "Pro"
338
- }
339
- ]
340
- }
279
+ // Layout components (client components)
280
+ import { AppLayout, type User } from "@logickernel/bridge/next-components"
341
281
  ```
342
282
 
343
- **Note:** The `{organizationId}` placeholder in URLs will be automatically replaced with the current organization ID. The endpoint should be protected and only return data the current user has access to.
344
-
345
283
  ## API Reference
346
284
 
347
285
  ### Core Exports (`@logickernel/bridge`)
@@ -350,13 +288,10 @@ interface NavigationOrganization {
350
288
  |--------|-------------|
351
289
  | `decodeSessionToken(token, secret, isSecure)` | Decode and validate a JWT session token |
352
290
  | `extractSessionCookie(cookies)` | Extract session cookie from a cookies object |
353
- | `fetchUserRoles(options)` | Fetch user roles from the kernel API (uses AUTH_URL internally) |
291
+ | `fetchUserRoles(options)` | Fetch user roles from the kernel API |
354
292
  | `hasAnyRole(userRoles, requiredRoles)` | Check if user has at least one required role |
355
293
  | `hasAllRoles(userRoles, requiredRoles)` | Check if user has all required roles |
356
294
  | `hasRole(userRoles, role)` | Check if user has a specific role |
357
- | `matchRoute(pathname, config)` | Match a pathname against route configuration |
358
- | `buildSignInUrl(callbackUrl?)` | Build a sign-in redirect URL (uses AUTH_URL internally) |
359
- | `buildSignOutUrl(callbackUrl?)` | Build a sign-out redirect URL (uses AUTH_URL internally) |
360
295
 
361
296
  ### Next.js Exports (`@logickernel/bridge/next`)
362
297
 
@@ -364,63 +299,57 @@ interface NavigationOrganization {
364
299
  |--------|-------------|
365
300
  | `getSession()` | Get the current session from cookies |
366
301
  | `getSessionToken()` | Get the raw session token value |
367
- | `getUserRoles(orgId)` | Get user roles in an organization (user ID determined from session token) |
302
+ | `getUserRoles(orgId)` | Get user roles in an organization |
368
303
  | `withAuth(handler, options?)` | Wrap an API route with authentication |
369
- | `getSessionFromRequest(request)` | Get session from a NextRequest |
370
304
  | `checkPageAuth(options?)` | Check authentication for a page |
371
305
  | `requireAuth()` | Require authentication (throws if not authenticated) |
372
- | `hasRequiredRole(orgId, roles)` | Check if user has required roles (user ID determined from session token) |
306
+ | `hasRequiredRole(orgId, roles)` | Check if user has required roles |
373
307
  | `createProxyHandler(options)` | Create a middleware/proxy handler |
374
308
 
375
- ### Layout Components (`@logickernel/bridge/next/components`)
309
+ ### Layout Components (`@logickernel/bridge/next-components`)
376
310
 
377
311
  | Export | Description |
378
312
  |--------|-------------|
379
- | `AppLayout` | Main layout component with sidebar navigation |
380
- | `AppSidebar` | Sidebar component (used internally by AppLayout) |
381
- | `NavMain` | Main navigation component (used internally) |
382
- | `NavUser` | User menu component (used internally) |
383
- | `TeamSwitcher` | Organization switcher component (used internally) |
313
+ | `AppLayout` | Main layout component with sidebar navigation (wrap in a local component with `"use client"` for server components) |
384
314
  | `useNavigation` | Hook to fetch navigation items from API |
385
315
  | `getIconComponent` | Utility to get icon component from icon name |
386
316
 
387
317
  **AppLayout Props:**
318
+
388
319
  ```typescript
389
320
  interface AppLayoutProps {
390
- user: User // User information (name, email, image)
391
- organizationId?: string // Optional: Current organization ID
392
- apiBaseUrl?: string // Optional: API base URL for navigation
393
- children: React.ReactNode // Page content
321
+ user?: User // Optional: User information (auto-fetched if not provided)
322
+ organizationId?: string // Optional: Current organization ID
323
+ apiBaseUrl?: string // Optional: API base URL (defaults to "/api")
324
+ children: React.ReactNode // Page content
394
325
  }
395
326
  ```
396
327
 
397
- ### Types
328
+ ## TypeScript Types
398
329
 
399
330
  ```typescript
331
+ // Core types
400
332
  import type {
401
333
  Session,
402
334
  SessionUser,
403
335
  DecodeResult,
404
- RouteConfig,
405
- RouteMatch,
406
- MicroFrontendConfig,
407
336
  } from "@logickernel/bridge"
408
337
 
338
+ // Next.js types
409
339
  import type {
410
340
  ProxyOptions,
411
- DynamicRoutePattern,
412
341
  AuthContext,
413
342
  AuthenticatedHandler,
414
- WithAuthOptions,
415
343
  PageAuthOptions,
416
344
  PageAuthResult,
417
345
  } from "@logickernel/bridge/next"
418
346
 
347
+ // Component types
419
348
  import type {
420
349
  User,
421
350
  NavigationItem,
422
351
  NavigationOrganization,
423
- } from "@logickernel/bridge/next/components"
352
+ } from "@logickernel/bridge/next-components"
424
353
  ```
425
354
 
426
355
  ## Architecture
@@ -428,26 +357,23 @@ import type {
428
357
  ```
429
358
  bridge/
430
359
  ├── src/
431
- │ ├── index.ts # Core exports
432
- │ ├── types.ts # Core type definitions
433
- │ ├── jwt.ts # JWT decoding utilities
434
- │ ├── roles.ts # Role checking utilities
435
- │ ├── routes.ts # Route matching utilities
360
+ │ ├── index.ts # Core exports (framework-agnostic)
361
+ │ ├── jwt.ts # JWT decoding utilities
362
+ │ ├── roles.ts # Role checking utilities
436
363
  │ └── next/
437
- │ ├── index.ts # Next.js adapter exports
438
- │ ├── types.ts # Next.js-specific types
439
- │ ├── session.ts # Session utilities
440
- │ ├── api.ts # API route utilities
441
- │ ├── page.ts # Page utilities
442
- │ ├── proxy.ts # Middleware/proxy utilities
364
+ │ ├── index.ts # Next.js adapter exports
365
+ │ ├── session.ts # Session utilities
366
+ │ ├── api.ts # API route utilities
367
+ │ ├── page.ts # Page utilities
368
+ │ ├── proxy.ts # Middleware/proxy utilities
443
369
  │ └── components/
444
- │ ├── app-layout.tsx # Main layout component
445
- │ ├── app-sidebar.tsx # Sidebar component
446
- │ ├── nav-main.tsx # Navigation component
447
- │ ├── nav-user.tsx # User menu component
448
- │ ├── team-switcher.tsx # Organization switcher
449
- │ ├── use-navigation.ts # Navigation hook
450
- │ └── ui/ # Internal UI components (shadcn)
370
+ │ ├── app-layout.tsx # Main layout component
371
+ │ ├── app-sidebar.tsx # Sidebar component
372
+ │ ├── nav-main.tsx # Navigation component
373
+ │ ├── nav-user.tsx # User menu component
374
+ │ ├── team-switcher.tsx # Organization switcher
375
+ │ ├── use-navigation.ts # Navigation hook
376
+ │ └── ui/ # Internal UI components (shadcn)
451
377
  ```
452
378
 
453
379
  ## Development
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logickernel/bridge",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Framework-agnostic micro-frontend authentication library for AuthJS/NextAuth JWT tokens",
5
5
  "author": "Victor",
6
6
  "license": "MIT",
@@ -101,4 +101,3 @@
101
101
  "node": ">=18"
102
102
  }
103
103
  }
104
-