@insforge/nextjs 0.11.2 → 0.11.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.
package/README.md
CHANGED
|
@@ -29,7 +29,6 @@ Wrap your app with `InsforgeProvider` in the root layout:
|
|
|
29
29
|
```tsx
|
|
30
30
|
// app/layout.tsx
|
|
31
31
|
import { InsforgeProvider } from '@insforge/nextjs';
|
|
32
|
-
import '@insforge/nextjs/styles.css';
|
|
33
32
|
|
|
34
33
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
35
34
|
return (
|
|
@@ -47,7 +46,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
47
46
|
}
|
|
48
47
|
```
|
|
49
48
|
|
|
50
|
-
> **
|
|
49
|
+
> **Zero Configuration**: Styles are automatically injected via Emotion CSS-in-JS. No CSS imports needed!
|
|
51
50
|
|
|
52
51
|
### 2. Create API Route
|
|
53
52
|
|
|
@@ -3,7 +3,8 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import {
|
|
4
4
|
InsforgeProviderCore,
|
|
5
5
|
useInsforge as useReactInsforge,
|
|
6
|
-
NavigationProvider
|
|
6
|
+
NavigationProvider,
|
|
7
|
+
StyleProvider
|
|
7
8
|
} from "@insforge/react";
|
|
8
9
|
import { NavigationAdapter } from "../navigation";
|
|
9
10
|
async function handleSignIn(token) {
|
|
@@ -48,7 +49,7 @@ function ClientInsforgeProvider({
|
|
|
48
49
|
onSignOut: handleSignOut,
|
|
49
50
|
initialState
|
|
50
51
|
};
|
|
51
|
-
return /* @__PURE__ */ jsx(NavigationProvider, { adapter: NavigationAdapter, children: /* @__PURE__ */ jsx(InsforgeProviderCore, { ...providerProps, children }) });
|
|
52
|
+
return /* @__PURE__ */ jsx(StyleProvider, { children: /* @__PURE__ */ jsx(NavigationProvider, { adapter: NavigationAdapter, children: /* @__PURE__ */ jsx(InsforgeProviderCore, { ...providerProps, children }) }) });
|
|
52
53
|
}
|
|
53
54
|
const useInsforge = useReactInsforge;
|
|
54
55
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/client/provider.tsx"],"sourcesContent":["'use client';\n\nimport {\n InsforgeProviderCore,\n type InsforgeProviderProps,\n type InitialAuthState,\n useInsforge as useReactInsforge,\n NavigationProvider,\n} from '@insforge/react';\nimport { NavigationAdapter } from '../navigation';\n\n// Extended interface for Next.js provider\ninterface ExtendedProviderProps extends InsforgeProviderProps {\n afterSignInUrl?: string;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n /**\n * Initial auth state from server (for SSR hydration)\n * @internal\n */\n initialState?: InitialAuthState;\n}\n\n// Sync token to server-side cookie on sign in\nasync function handleSignIn(token: string): Promise<void> {\n try {\n const response = await fetch('/api/auth', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n action: 'sync-token',\n token,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error('[Insforge Client Provider] sync-token failed:', errorText);\n }\n } catch (err) {\n console.error('[Insforge Client Provider] Failed to sync token to cookie:', err);\n }\n}\n\n// Clear HTTP-only cookie on sign out\nasync function handleSignOut(): Promise<void> {\n try {\n await fetch('/api/auth', { method: 'DELETE' });\n } catch {\n // API route doesn't exist - ignore\n }\n}\n\n/**\n * Client Insforge Provider for Next.js\n *\n * This is a Client Component ('use client') that:\n * 1. Receives initialState from Server Provider\n * 2. Manages authentication state on the client\n * 3. Syncs tokens to HTTP-only cookies via /api/auth\n *\n * @internal - Not intended for direct use. Use the Server Provider instead.\n */\nexport function ClientInsforgeProvider({\n children,\n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n initialState,\n}: ExtendedProviderProps) {\n const providerProps: ExtendedProviderProps = {\n children,\n baseUrl,\n afterSignInUrl,\n onAuthChange,\n onSignIn: handleSignIn,\n onSignOut: handleSignOut,\n initialState,\n };\n\n return (\n <NavigationProvider adapter={NavigationAdapter}>\n
|
|
1
|
+
{"version":3,"sources":["../../../src/client/provider.tsx"],"sourcesContent":["'use client';\n\nimport {\n InsforgeProviderCore,\n type InsforgeProviderProps,\n type InitialAuthState,\n useInsforge as useReactInsforge,\n NavigationProvider,\n StyleProvider,\n} from '@insforge/react';\nimport { NavigationAdapter } from '../navigation';\n\n// Extended interface for Next.js provider\ninterface ExtendedProviderProps extends InsforgeProviderProps {\n afterSignInUrl?: string;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n /**\n * Initial auth state from server (for SSR hydration)\n * @internal\n */\n initialState?: InitialAuthState;\n}\n\n// Sync token to server-side cookie on sign in\nasync function handleSignIn(token: string): Promise<void> {\n try {\n const response = await fetch('/api/auth', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n action: 'sync-token',\n token,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error('[Insforge Client Provider] sync-token failed:', errorText);\n }\n } catch (err) {\n console.error('[Insforge Client Provider] Failed to sync token to cookie:', err);\n }\n}\n\n// Clear HTTP-only cookie on sign out\nasync function handleSignOut(): Promise<void> {\n try {\n await fetch('/api/auth', { method: 'DELETE' });\n } catch {\n // API route doesn't exist - ignore\n }\n}\n\n/**\n * Client Insforge Provider for Next.js\n *\n * This is a Client Component ('use client') that:\n * 1. Receives initialState from Server Provider\n * 2. Manages authentication state on the client\n * 3. Syncs tokens to HTTP-only cookies via /api/auth\n *\n * @internal - Not intended for direct use. Use the Server Provider instead.\n */\nexport function ClientInsforgeProvider({\n children,\n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n initialState,\n}: ExtendedProviderProps) {\n const providerProps: ExtendedProviderProps = {\n children,\n baseUrl,\n afterSignInUrl,\n onAuthChange,\n onSignIn: handleSignIn,\n onSignOut: handleSignOut,\n initialState,\n };\n\n return (\n <StyleProvider>\n <NavigationProvider adapter={NavigationAdapter}>\n <InsforgeProviderCore {...providerProps}>{children}</InsforgeProviderCore>\n </NavigationProvider>\n </StyleProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * Re-exports the hook from @insforge/react for convenience.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n *\n * if (!isSignedIn) return <SignIn />;\n *\n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useInsforge = useReactInsforge;\n"],"mappings":";AAsFQ;AApFR;AAAA,EACE;AAAA,EAGA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAelC,eAAe,aAAa,OAA8B;AACxD,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,aAAa;AAAA,MACxC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAQ,MAAM,iDAAiD,SAAS;AAAA,IAC1E;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,8DAA8D,GAAG;AAAA,EACjF;AACF;AAGA,eAAe,gBAA+B;AAC5C,MAAI;AACF,UAAM,MAAM,aAAa,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC/C,QAAQ;AAAA,EAER;AACF;AAYO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,gBAAuC;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,IACX;AAAA,EACF;AAEA,SACE,oBAAC,iBACC,8BAAC,sBAAmB,SAAS,mBAC3B,8BAAC,wBAAsB,GAAG,eAAgB,UAAS,GACrD,GACF;AAEJ;AAuBO,MAAM,cAAc;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/server/InsforgeProvider.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { ClientInsforgeProvider } from '../client/provider';\nimport type { InsforgeProviderProps } from '@insforge/react';\nimport { cookies } from 'next/headers';\nimport type { InitialAuthState } from '@insforge/react';\n\nexport interface InsforgeProviderServerProps extends Omit<InsforgeProviderProps, 'initialState'> {\n children: ReactNode;\n /**\n * Opt into dynamic rendering to read auth state from cookies during SSR.\n *\n * - When true: Reads cookies on server, prevents content flashing, opts into dynamic rendering\n * - When false: Static rendering, may see brief content flash during hydration\n *\n * Following Clerk's pattern: dynamic prop controls server-side auth state access\n *\n * @default false\n *\n * @example\n * ```tsx\n * <InsforgeProvider baseUrl={...} dynamic>\n * {children}\n * </InsforgeProvider>\n * ```\n */\n dynamic?: boolean;\n}\n\n/**\n * Get initial auth state from cookies (server-side)\n *\n * This helper reads the authentication state from cookies set by the SDK.\n * Use this in Server Components or Server Actions to get the initial auth state\n * and pass it to the InsforgeProvider via initialState prop.\n *\n * This prevents hydration mismatches by ensuring SSR and client render have the same state.\n *\n * @example\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { InsforgeProvider } from '@insforge/nextjs';\n * import { getAuthFromCookies } from '@insforge/nextjs/api/auth-helpers';\n
|
|
1
|
+
{"version":3,"sources":["../../../src/server/InsforgeProvider.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { ClientInsforgeProvider } from '../client/provider';\nimport type { InsforgeProviderProps } from '@insforge/react';\nimport { cookies } from 'next/headers';\nimport type { InitialAuthState } from '@insforge/react';\n\nexport interface InsforgeProviderServerProps extends Omit<InsforgeProviderProps, 'initialState'> {\n children: ReactNode;\n /**\n * Opt into dynamic rendering to read auth state from cookies during SSR.\n *\n * - When true: Reads cookies on server, prevents content flashing, opts into dynamic rendering\n * - When false: Static rendering, may see brief content flash during hydration\n *\n * Following Clerk's pattern: dynamic prop controls server-side auth state access\n *\n * @default false\n *\n * @example\n * ```tsx\n * <InsforgeProvider baseUrl={...} dynamic>\n * {children}\n * </InsforgeProvider>\n * ```\n */\n dynamic?: boolean;\n}\n\n/**\n * Get initial auth state from cookies (server-side)\n *\n * This helper reads the authentication state from cookies set by the SDK.\n * Use this in Server Components or Server Actions to get the initial auth state\n * and pass it to the InsforgeProvider via initialState prop.\n *\n * This prevents hydration mismatches by ensuring SSR and client render have the same state.\n *\n * @example\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { InsforgeProvider } from '@insforge/nextjs';\n * import { getAuthFromCookies } from '@insforge/nextjs/api/auth-helpers';\n *\n * export default async function RootLayout({ children }) {\n * const initialAuth = await getAuthFromCookies();\n *\n * return (\n * <html>\n * <body>\n * <InsforgeProvider\n * baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}\n * initialState={initialAuth}\n * >\n * {children}\n * </InsforgeProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * @returns Initial auth state with user and userId, or empty object if not authenticated\n */\nexport async function getAuthFromCookies(): Promise<InitialAuthState> {\n try {\n const cookieStore = await cookies();\n\n // Read session token from cookie (set by middleware or /api/auth route)\n const sessionToken = cookieStore.get('insforge-session')?.value;\n\n if (!sessionToken) {\n // Not authenticated - return null to indicate signed out\n return {\n user: null,\n userId: null,\n };\n }\n\n // Read user identity from cookie (set by middleware or /api/auth route)\n const userCookie = cookieStore.get('insforge-user')?.value;\n\n if (!userCookie) {\n // Have session token but no user info - this shouldn't happen\n // but return null to be safe\n return {\n user: null,\n userId: null,\n };\n }\n\n try {\n const user = JSON.parse(userCookie) as { id: string; email: string; name?: string };\n\n // Return basic user info from cookie\n // This is lightweight data for SSR hydration only\n // Full profile will be loaded by SDK's getCurrentUser() after hydration\n return {\n user: {\n id: user.id,\n email: user.email,\n name: user.name || '',\n },\n userId: user.id,\n };\n } catch {\n // Invalid user data in cookie\n return {\n user: null,\n userId: null,\n };\n }\n } catch (error) {\n // Error reading cookies (might be in middleware or edge runtime)\n console.error('[Insforge] Error reading cookies:', error);\n return {\n user: null,\n userId: null,\n };\n }\n}\n\n/**\n * Synchronous version for use in middleware or edge runtime\n *\n * @param request - Next.js request object\n * @returns Initial auth state\n */\nexport function getAuthFromRequest(request: Request): InitialAuthState {\n try {\n // Parse cookies from request headers\n const cookieHeader = request.headers.get('cookie') || '';\n const cookies = Object.fromEntries(\n cookieHeader.split('; ').map((c) => {\n const [key, ...v] = c.split('=');\n return [key, v.join('=')];\n })\n );\n\n const sessionToken = cookies['insforge-session'];\n\n if (!sessionToken) {\n // Not authenticated\n return {\n user: null,\n userId: null,\n };\n }\n\n const userCookie = cookies['insforge-user'];\n\n if (!userCookie) {\n // Have session but no user info\n return {\n user: null,\n userId: null,\n };\n }\n\n try {\n const user = JSON.parse(decodeURIComponent(userCookie)) as {\n id: string;\n email: string;\n name?: string;\n };\n\n // Return basic user info from cookie\n // This is lightweight data for SSR hydration only\n return {\n user: {\n id: user.id,\n email: user.email,\n name: user.name || '',\n },\n userId: user.id,\n };\n } catch {\n return {\n user: null,\n userId: null,\n };\n }\n } catch (error) {\n console.error('[Insforge] Error reading cookies from request:', error);\n return {\n user: null,\n userId: null,\n };\n }\n}\n\n/**\n * Server Component Provider for Insforge\n *\n * This is a Server Component (no 'use client') that:\n * 1. Optionally reads auth state from cookies on the server (when dynamic=true)\n * 2. Passes initialState to the Client Provider\n * 3. Ensures SSR and client hydration match perfectly\n *\n * Following Clerk's pattern: packages/nextjs/src/app-router/server/ClerkProvider.tsx\n *\n * @example\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { InsforgeProvider } from '@insforge/nextjs';\n *\n * export default async function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <InsforgeProvider\n * baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}\n * dynamic // 👈 Enable dynamic rendering for SSR auth\n * >\n * {children}\n * </InsforgeProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport async function InsforgeProvider(props: InsforgeProviderServerProps) {\n const { children, dynamic = true, ...restProps } = props;\n\n // Only read cookies if dynamic=true (opts into dynamic rendering)\n // When dynamic=false, Next.js can statically generate the page\n const initialAuth = dynamic ? await getAuthFromCookies() : { user: null, userId: null };\n\n // Pass initialState to Client Provider\n return (\n <ClientInsforgeProvider {...restProps} initialState={initialAuth}>\n {children}\n </ClientInsforgeProvider>\n );\n}\n"],"mappings":"AAsOI;AArOJ,SAAS,8BAA8B;AAEvC,SAAS,eAAe;AA4DxB,eAAsB,qBAAgD;AACpE,MAAI;AACF,UAAM,cAAc,MAAM,QAAQ;AAGlC,UAAM,eAAe,YAAY,IAAI,kBAAkB,GAAG;AAE1D,QAAI,CAAC,cAAc;AAEjB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAGA,UAAM,aAAa,YAAY,IAAI,eAAe,GAAG;AAErD,QAAI,CAAC,YAAY;AAGf,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,UAAU;AAKlC,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,IAAI,KAAK;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,QACA,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,QAAQ;AAEN,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AAEd,YAAQ,MAAM,qCAAqC,KAAK;AACxD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAQO,SAAS,mBAAmB,SAAoC;AACrE,MAAI;AAEF,UAAM,eAAe,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AACtD,UAAMA,WAAU,OAAO;AAAA,MACrB,aAAa,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM;AAClC,cAAM,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG;AAC/B,eAAO,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC;AAAA,MAC1B,CAAC;AAAA,IACH;AAEA,UAAM,eAAeA,SAAQ,kBAAkB;AAE/C,QAAI,CAAC,cAAc;AAEjB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,UAAM,aAAaA,SAAQ,eAAe;AAE1C,QAAI,CAAC,YAAY;AAEf,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,mBAAmB,UAAU,CAAC;AAQtD,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,IAAI,KAAK;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,QACA,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,kDAAkD,KAAK;AACrE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAiCA,eAAsB,iBAAiB,OAAoC;AACzE,QAAM,EAAE,UAAU,UAAU,MAAM,GAAG,UAAU,IAAI;AAInD,QAAM,cAAc,UAAU,MAAM,mBAAmB,IAAI,EAAE,MAAM,MAAM,QAAQ,KAAK;AAGtF,SACE,oBAAC,0BAAwB,GAAG,WAAW,cAAc,aAClD,UACH;AAEJ;","names":["cookies"]}
|
|
@@ -37,7 +37,6 @@ interface InsforgeProviderServerProps extends Omit<InsforgeProviderProps, 'initi
|
|
|
37
37
|
* // app/layout.tsx (Server Component)
|
|
38
38
|
* import { InsforgeProvider } from '@insforge/nextjs';
|
|
39
39
|
* import { getAuthFromCookies } from '@insforge/nextjs/api/auth-helpers';
|
|
40
|
-
* import '@insforge/nextjs/styles.css';
|
|
41
40
|
*
|
|
42
41
|
* export default async function RootLayout({ children }) {
|
|
43
42
|
* const initialAuth = await getAuthFromCookies();
|
|
@@ -81,7 +80,6 @@ declare function getAuthFromRequest(request: Request): InitialAuthState;
|
|
|
81
80
|
* ```tsx
|
|
82
81
|
* // app/layout.tsx (Server Component)
|
|
83
82
|
* import { InsforgeProvider } from '@insforge/nextjs';
|
|
84
|
-
* import '@insforge/nextjs/styles.css';
|
|
85
83
|
*
|
|
86
84
|
* export default async function RootLayout({ children }) {
|
|
87
85
|
* return (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@insforge/nextjs",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "Pre-built authentication UI components for Next.js with Insforge backend - zero configuration required",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"author": "Insforge",
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@insforge/react": "^0.6.
|
|
48
|
+
"@insforge/react": "^0.6.10",
|
|
49
49
|
"@insforge/sdk": "^0.0.58",
|
|
50
50
|
"@insforge/shared": "^0.1.5",
|
|
51
51
|
"@insforge/shared-schemas": "^1.1.19"
|