@logickernel/bridge 0.8.1 → 0.8.2
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 +170 -258
- package/dist/next-components.cjs +15 -0
- package/dist/next-components.cjs.map +1 -1
- package/dist/next-components.d.cts +8 -1
- package/dist/next-components.d.ts +8 -1
- package/dist/next-components.js +15 -1
- package/dist/next-components.js.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
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
|
|
|
@@ -12,72 +12,135 @@ pnpm add @logickernel/bridge
|
|
|
12
12
|
yarn add @logickernel/bridge
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
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
|
|
15
|
+
## Quick Start (Next.js)
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
### 1. Environment Variables
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
Add these to your `.env` file:
|
|
26
20
|
|
|
27
21
|
```bash
|
|
28
22
|
AUTH_SECRET=your-nextauth-secret # Required: Same secret used by your kernel/auth server
|
|
29
|
-
AUTH_URL=http://localhost:3000 # Required:
|
|
30
|
-
BASE_URL=http://localhost:7001 # Optional:
|
|
31
|
-
AUTH_COOKIE=authjs.session-token # Optional: Cookie name for session token (defaults to authjs.session-token)
|
|
23
|
+
AUTH_URL=http://localhost:3000 # Required: Base URL of your kernel/auth server
|
|
24
|
+
BASE_URL=http://localhost:7001 # Optional: Your facade URL for user-facing redirects
|
|
32
25
|
```
|
|
33
26
|
|
|
34
|
-
###
|
|
27
|
+
### 2. Configure Next.js
|
|
28
|
+
|
|
29
|
+
In your `next.config.ts`, add the bridge package to `transpilePackages`:
|
|
35
30
|
|
|
36
31
|
```typescript
|
|
37
|
-
import {
|
|
38
|
-
decodeSessionToken,
|
|
39
|
-
extractSessionCookie,
|
|
40
|
-
fetchUserRoles,
|
|
41
|
-
hasAnyRole,
|
|
42
|
-
} from "@logickernel/bridge"
|
|
32
|
+
import type { NextConfig } from "next"
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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"
|
|
34
|
+
const nextConfig: NextConfig = {
|
|
35
|
+
transpilePackages: ["@logickernel/bridge"],
|
|
36
|
+
// ... other config
|
|
51
37
|
}
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
39
|
+
export default nextConfig
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Use the Layout Component
|
|
43
|
+
|
|
44
|
+
The library provides a ready-to-use layout component with sidebar navigation. Import `AppLayoutWrapper` directly from the library:
|
|
45
|
+
|
|
46
|
+
In your `app/app/layout.tsx` (or wherever your app layout is):
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { redirect } from "next/navigation"
|
|
50
|
+
import { auth } from "@/lib/next-auth" // or your auth setup
|
|
51
|
+
import { AppLayoutWrapper } from "@logickernel/bridge/next-components"
|
|
52
|
+
|
|
53
|
+
export default async function Layout({
|
|
54
|
+
children,
|
|
55
|
+
}: {
|
|
56
|
+
children: React.ReactNode
|
|
57
|
+
}) {
|
|
58
|
+
const session = await auth()
|
|
59
59
|
|
|
60
|
-
if (
|
|
61
|
-
|
|
60
|
+
if (!session?.user) {
|
|
61
|
+
redirect("/auth/signin?callbackUrl=/app")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<AppLayoutWrapper>
|
|
66
|
+
{children}
|
|
67
|
+
</AppLayoutWrapper>
|
|
68
|
+
)
|
|
62
69
|
}
|
|
63
70
|
```
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
**Optional: Pass organization ID and API base URL**
|
|
66
73
|
|
|
67
74
|
```typescript
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} from "@logickernel/bridge/next"
|
|
75
|
+
<AppLayoutWrapper
|
|
76
|
+
organizationId={organizationId}
|
|
77
|
+
apiBaseUrl={apiBaseUrl}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</AppLayoutWrapper>
|
|
75
81
|
```
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
The `user` prop is **not needed** - the layout automatically fetches user information from the navigation API endpoint.
|
|
84
|
+
|
|
85
|
+
### 4. Navigation API Endpoint
|
|
86
|
+
|
|
87
|
+
The layout component automatically loads navigation items from your API. The **Kernel Core application** provides this endpoint at `/api/navigation/[organization_id]`.
|
|
88
|
+
|
|
89
|
+
**Required Endpoint:** `GET /api/navigation/[organization_id]`
|
|
90
|
+
|
|
91
|
+
**Response Payload:**
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
{
|
|
95
|
+
items: NavigationItem[]
|
|
96
|
+
organizationId: string
|
|
97
|
+
organizations: NavigationOrganization[]
|
|
98
|
+
user: {
|
|
99
|
+
id: string
|
|
100
|
+
name: string | null
|
|
101
|
+
email: string
|
|
102
|
+
image: string | null
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface NavigationItem {
|
|
107
|
+
title: string
|
|
108
|
+
url?: string // If missing, item is treated as a section label
|
|
109
|
+
icon?: string // Lucide icon name (e.g., "LayoutDashboard", "Users")
|
|
110
|
+
isActive?: boolean // Whether the item should be highlighted
|
|
111
|
+
items?: { // Sub-items for collapsible navigation
|
|
112
|
+
title: string
|
|
113
|
+
url: string
|
|
114
|
+
}[]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface NavigationOrganization {
|
|
118
|
+
id: string
|
|
119
|
+
name: string
|
|
120
|
+
logo?: string // Lucide icon name for the organization logo
|
|
121
|
+
plan?: string // Optional plan/badge text
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**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.
|
|
126
|
+
|
|
127
|
+
## Features
|
|
128
|
+
|
|
129
|
+
The `AppLayout` component provides:
|
|
130
|
+
|
|
131
|
+
- ✅ **Built-in sidebar navigation** with collapsible sections
|
|
132
|
+
- ✅ **Organization switcher** - Switch between organizations the user belongs to
|
|
133
|
+
- ✅ **User menu** with profile information and sign-out
|
|
134
|
+
- ✅ **Responsive design** - Works on mobile and desktop
|
|
135
|
+
- ✅ **Automatic navigation loading** - Fetches from your API endpoint
|
|
136
|
+
- ✅ **Consistent UI** - Same look and feel across all microfrontends using the library
|
|
137
|
+
- ✅ **No extra components required** - All UI components are included in the library
|
|
138
|
+
|
|
139
|
+
## Next.js Utilities
|
|
140
|
+
|
|
141
|
+
### Server Components / Pages
|
|
78
142
|
|
|
79
143
|
```typescript
|
|
80
|
-
// app/dashboard/page.tsx
|
|
81
144
|
import { getSession } from "@logickernel/bridge/next"
|
|
82
145
|
import { redirect } from "next/navigation"
|
|
83
146
|
|
|
@@ -92,42 +155,39 @@ export default async function DashboardPage() {
|
|
|
92
155
|
}
|
|
93
156
|
```
|
|
94
157
|
|
|
95
|
-
|
|
158
|
+
### Role-Based Page Protection
|
|
96
159
|
|
|
97
160
|
```typescript
|
|
98
|
-
// app/[org_id]/admin/page.tsx
|
|
99
161
|
import { checkPageAuth } from "@logickernel/bridge/next"
|
|
100
162
|
import { redirect, notFound } from "next/navigation"
|
|
101
163
|
|
|
102
164
|
export default async function AdminPage({
|
|
103
165
|
params,
|
|
104
166
|
}: {
|
|
105
|
-
params: Promise<{
|
|
167
|
+
params: Promise<{ organization_id: string }>
|
|
106
168
|
}) {
|
|
107
|
-
const {
|
|
169
|
+
const { organization_id } = await params
|
|
108
170
|
|
|
109
171
|
const auth = await checkPageAuth({
|
|
110
172
|
requiredRoles: ["organization.owner", "organization.editor"],
|
|
111
|
-
organizationId:
|
|
173
|
+
organizationId: organization_id,
|
|
112
174
|
})
|
|
113
175
|
|
|
114
176
|
if (!auth.authenticated) {
|
|
115
177
|
redirect(auth.redirectUrl)
|
|
116
178
|
}
|
|
117
179
|
|
|
118
|
-
|
|
119
|
-
if (!auth.roles.some(r => ["organization.owner", "organization.editor"].includes(r))) {
|
|
180
|
+
if (!auth.hasRequiredRole) {
|
|
120
181
|
notFound()
|
|
121
182
|
}
|
|
122
183
|
|
|
123
|
-
return <div>Admin Panel for {session.user.email}</div>
|
|
184
|
+
return <div>Admin Panel for {auth.session.user.email}</div>
|
|
124
185
|
}
|
|
125
186
|
```
|
|
126
187
|
|
|
127
|
-
|
|
188
|
+
### API Routes
|
|
128
189
|
|
|
129
190
|
```typescript
|
|
130
|
-
// app/api/data/route.ts
|
|
131
191
|
import { withAuth } from "@logickernel/bridge/next"
|
|
132
192
|
import { NextResponse } from "next/server"
|
|
133
193
|
|
|
@@ -152,10 +212,10 @@ export const DELETE = withAuth(
|
|
|
152
212
|
)
|
|
153
213
|
```
|
|
154
214
|
|
|
155
|
-
|
|
215
|
+
### Middleware / Proxy
|
|
156
216
|
|
|
157
217
|
```typescript
|
|
158
|
-
//
|
|
218
|
+
// middleware.ts or proxy.ts
|
|
159
219
|
import { createProxyHandler, type ProxyOptions } from "@logickernel/bridge/next"
|
|
160
220
|
|
|
161
221
|
const config: ProxyOptions = {
|
|
@@ -166,7 +226,7 @@ const config: ProxyOptions = {
|
|
|
166
226
|
},
|
|
167
227
|
dynamicRoutes: [
|
|
168
228
|
{
|
|
169
|
-
pattern: "/[organization_id]/admin",
|
|
229
|
+
pattern: "/app/[organization_id]/admin",
|
|
170
230
|
requiredRoles: ["organization.owner"],
|
|
171
231
|
},
|
|
172
232
|
],
|
|
@@ -174,174 +234,27 @@ const config: ProxyOptions = {
|
|
|
174
234
|
|
|
175
235
|
export const proxy = createProxyHandler(config)
|
|
176
236
|
|
|
177
|
-
// Static config for Next.js
|
|
178
|
-
export const
|
|
237
|
+
// Static config for Next.js middleware
|
|
238
|
+
export const config = {
|
|
179
239
|
matcher: ["/((?!_next/static|_next/image|favicon.ico|public).*)"],
|
|
180
240
|
}
|
|
181
241
|
```
|
|
182
242
|
|
|
183
|
-
|
|
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:**
|
|
220
|
-
|
|
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
|
|
226
|
-
|
|
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
|
|
243
|
+
## Import Paths
|
|
269
244
|
|
|
270
|
-
The
|
|
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
|
|
245
|
+
The library uses top-level exports (no subpath exports) for better bundler compatibility:
|
|
275
246
|
|
|
276
|
-
**Required API Endpoint:**
|
|
277
|
-
|
|
278
|
-
The navigation requires an endpoint at `/api/navigation/[organization_id]` that returns:
|
|
279
247
|
```typescript
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
organizations: NavigationOrganization[]
|
|
283
|
-
}
|
|
284
|
-
```
|
|
248
|
+
// Core utilities (framework-agnostic)
|
|
249
|
+
import { decodeSessionToken, fetchUserRoles } from "@logickernel/bridge"
|
|
285
250
|
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
}
|
|
251
|
+
// Next.js utilities (server components, API routes, middleware)
|
|
252
|
+
import { getSession, withAuth, checkPageAuth } from "@logickernel/bridge/next"
|
|
298
253
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
name: string
|
|
302
|
-
logo?: string // Icon name for the organization logo
|
|
303
|
-
plan?: string // Optional plan/badge text
|
|
304
|
-
}
|
|
254
|
+
// Layout components (client components)
|
|
255
|
+
import { AppLayout, type User } from "@logickernel/bridge/next-components"
|
|
305
256
|
```
|
|
306
257
|
|
|
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
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
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
258
|
## API Reference
|
|
346
259
|
|
|
347
260
|
### Core Exports (`@logickernel/bridge`)
|
|
@@ -350,13 +263,10 @@ interface NavigationOrganization {
|
|
|
350
263
|
|--------|-------------|
|
|
351
264
|
| `decodeSessionToken(token, secret, isSecure)` | Decode and validate a JWT session token |
|
|
352
265
|
| `extractSessionCookie(cookies)` | Extract session cookie from a cookies object |
|
|
353
|
-
| `fetchUserRoles(options)` | Fetch user roles from the kernel API
|
|
266
|
+
| `fetchUserRoles(options)` | Fetch user roles from the kernel API |
|
|
354
267
|
| `hasAnyRole(userRoles, requiredRoles)` | Check if user has at least one required role |
|
|
355
268
|
| `hasAllRoles(userRoles, requiredRoles)` | Check if user has all required roles |
|
|
356
269
|
| `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
270
|
|
|
361
271
|
### Next.js Exports (`@logickernel/bridge/next`)
|
|
362
272
|
|
|
@@ -364,63 +274,68 @@ interface NavigationOrganization {
|
|
|
364
274
|
|--------|-------------|
|
|
365
275
|
| `getSession()` | Get the current session from cookies |
|
|
366
276
|
| `getSessionToken()` | Get the raw session token value |
|
|
367
|
-
| `getUserRoles(orgId)` | Get user roles in an organization
|
|
277
|
+
| `getUserRoles(orgId)` | Get user roles in an organization |
|
|
368
278
|
| `withAuth(handler, options?)` | Wrap an API route with authentication |
|
|
369
|
-
| `getSessionFromRequest(request)` | Get session from a NextRequest |
|
|
370
279
|
| `checkPageAuth(options?)` | Check authentication for a page |
|
|
371
280
|
| `requireAuth()` | Require authentication (throws if not authenticated) |
|
|
372
|
-
| `hasRequiredRole(orgId, roles)` | Check if user has required roles
|
|
281
|
+
| `hasRequiredRole(orgId, roles)` | Check if user has required roles |
|
|
373
282
|
| `createProxyHandler(options)` | Create a middleware/proxy handler |
|
|
374
283
|
|
|
375
|
-
### Layout Components (`@logickernel/bridge/next
|
|
284
|
+
### Layout Components (`@logickernel/bridge/next-components`)
|
|
376
285
|
|
|
377
286
|
| Export | Description |
|
|
378
287
|
|--------|-------------|
|
|
379
|
-
| `AppLayout` | Main layout component with sidebar navigation |
|
|
380
|
-
| `
|
|
381
|
-
| `NavMain` | Main navigation component (used internally) |
|
|
382
|
-
| `NavUser` | User menu component (used internally) |
|
|
383
|
-
| `TeamSwitcher` | Organization switcher component (used internally) |
|
|
288
|
+
| `AppLayout` | Main layout component with sidebar navigation (use `AppLayoutWrapper` in server components) |
|
|
289
|
+
| `AppLayoutWrapper` | Wrapper component for use in server components (recommended) |
|
|
384
290
|
| `useNavigation` | Hook to fetch navigation items from API |
|
|
385
291
|
| `getIconComponent` | Utility to get icon component from icon name |
|
|
386
292
|
|
|
387
293
|
**AppLayout Props:**
|
|
294
|
+
|
|
388
295
|
```typescript
|
|
389
296
|
interface AppLayoutProps {
|
|
390
|
-
user
|
|
391
|
-
organizationId?: string
|
|
392
|
-
apiBaseUrl?: string
|
|
393
|
-
children: React.ReactNode
|
|
297
|
+
user?: User // Optional: User information (auto-fetched if not provided)
|
|
298
|
+
organizationId?: string // Optional: Current organization ID
|
|
299
|
+
apiBaseUrl?: string // Optional: API base URL (defaults to "/api")
|
|
300
|
+
children: React.ReactNode // Page content
|
|
394
301
|
}
|
|
395
302
|
```
|
|
396
303
|
|
|
397
|
-
|
|
304
|
+
## Why AppLayoutWrapper?
|
|
305
|
+
|
|
306
|
+
The library provides `AppLayoutWrapper` as a convenience wrapper for `AppLayout` because:
|
|
307
|
+
|
|
308
|
+
1. **React Context Requirement**: `AppLayout` uses React Context internally (for sidebar state)
|
|
309
|
+
2. **SSR Build Compatibility**: Next.js requires a clean client/server boundary when client components with Context are used in server components
|
|
310
|
+
3. **Simplified Usage**: Using `AppLayoutWrapper` directly avoids React resolution issues during build-time analysis
|
|
311
|
+
|
|
312
|
+
You can use `AppLayoutWrapper` directly in server components without creating your own wrapper.
|
|
313
|
+
|
|
314
|
+
## TypeScript Types
|
|
398
315
|
|
|
399
316
|
```typescript
|
|
317
|
+
// Core types
|
|
400
318
|
import type {
|
|
401
319
|
Session,
|
|
402
320
|
SessionUser,
|
|
403
321
|
DecodeResult,
|
|
404
|
-
RouteConfig,
|
|
405
|
-
RouteMatch,
|
|
406
|
-
MicroFrontendConfig,
|
|
407
322
|
} from "@logickernel/bridge"
|
|
408
323
|
|
|
324
|
+
// Next.js types
|
|
409
325
|
import type {
|
|
410
326
|
ProxyOptions,
|
|
411
|
-
DynamicRoutePattern,
|
|
412
327
|
AuthContext,
|
|
413
328
|
AuthenticatedHandler,
|
|
414
|
-
WithAuthOptions,
|
|
415
329
|
PageAuthOptions,
|
|
416
330
|
PageAuthResult,
|
|
417
331
|
} from "@logickernel/bridge/next"
|
|
418
332
|
|
|
333
|
+
// Component types
|
|
419
334
|
import type {
|
|
420
335
|
User,
|
|
421
336
|
NavigationItem,
|
|
422
337
|
NavigationOrganization,
|
|
423
|
-
} from "@logickernel/bridge/next
|
|
338
|
+
} from "@logickernel/bridge/next-components"
|
|
424
339
|
```
|
|
425
340
|
|
|
426
341
|
## Architecture
|
|
@@ -428,26 +343,23 @@ import type {
|
|
|
428
343
|
```
|
|
429
344
|
bridge/
|
|
430
345
|
├── src/
|
|
431
|
-
│ ├── index.ts
|
|
432
|
-
│ ├──
|
|
433
|
-
│ ├──
|
|
434
|
-
│ ├── roles.ts # Role checking utilities
|
|
435
|
-
│ ├── routes.ts # Route matching utilities
|
|
346
|
+
│ ├── index.ts # Core exports (framework-agnostic)
|
|
347
|
+
│ ├── jwt.ts # JWT decoding utilities
|
|
348
|
+
│ ├── roles.ts # Role checking utilities
|
|
436
349
|
│ └── next/
|
|
437
|
-
│ ├── index.ts
|
|
438
|
-
│ ├──
|
|
439
|
-
│ ├──
|
|
440
|
-
│ ├──
|
|
441
|
-
│ ├──
|
|
442
|
-
│ ├── proxy.ts # Middleware/proxy utilities
|
|
350
|
+
│ ├── index.ts # Next.js adapter exports
|
|
351
|
+
│ ├── session.ts # Session utilities
|
|
352
|
+
│ ├── api.ts # API route utilities
|
|
353
|
+
│ ├── page.ts # Page utilities
|
|
354
|
+
│ ├── proxy.ts # Middleware/proxy utilities
|
|
443
355
|
│ └── components/
|
|
444
|
-
│ ├── app-layout.tsx
|
|
445
|
-
│ ├── app-sidebar.tsx
|
|
446
|
-
│ ├── nav-main.tsx
|
|
447
|
-
│ ├── nav-user.tsx
|
|
448
|
-
│ ├── team-switcher.tsx
|
|
449
|
-
│ ├── use-navigation.ts
|
|
450
|
-
│ └── ui/
|
|
356
|
+
│ ├── app-layout.tsx # Main layout component
|
|
357
|
+
│ ├── app-sidebar.tsx # Sidebar component
|
|
358
|
+
│ ├── nav-main.tsx # Navigation component
|
|
359
|
+
│ ├── nav-user.tsx # User menu component
|
|
360
|
+
│ ├── team-switcher.tsx # Organization switcher
|
|
361
|
+
│ ├── use-navigation.ts # Navigation hook
|
|
362
|
+
│ └── ui/ # Internal UI components (shadcn)
|
|
451
363
|
```
|
|
452
364
|
|
|
453
365
|
## Development
|
package/dist/next-components.cjs
CHANGED
|
@@ -1335,8 +1335,23 @@ function AppLayout({
|
|
|
1335
1335
|
] })
|
|
1336
1336
|
] });
|
|
1337
1337
|
}
|
|
1338
|
+
function AppLayoutWrapper({
|
|
1339
|
+
organizationId,
|
|
1340
|
+
apiBaseUrl,
|
|
1341
|
+
children
|
|
1342
|
+
}) {
|
|
1343
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1344
|
+
AppLayout,
|
|
1345
|
+
{
|
|
1346
|
+
organizationId,
|
|
1347
|
+
apiBaseUrl,
|
|
1348
|
+
children
|
|
1349
|
+
}
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1338
1352
|
|
|
1339
1353
|
exports.AppLayout = AppLayout;
|
|
1354
|
+
exports.AppLayoutWrapper = AppLayoutWrapper;
|
|
1340
1355
|
exports.AppSidebar = AppSidebar;
|
|
1341
1356
|
exports.NavMain = NavMain;
|
|
1342
1357
|
exports.NavUser = NavUser;
|