@palbase/web 1.0.1 → 1.1.1
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/dist/{analytics-facade-DV2uAiSg.d.cts → analytics-facade-CAKBIH_U.d.cts} +351 -5
- package/dist/{analytics-facade-CE_vGf1F.d.ts → analytics-facade-DLH-KivI.d.ts} +351 -5
- package/dist/chunk-EMQGOKW6.js +6842 -0
- package/dist/chunk-EMQGOKW6.js.map +1 -0
- package/dist/index.cjs +3616 -187
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +207 -4
- package/dist/index.d.ts +207 -4
- package/dist/index.js +7 -1
- package/dist/internal.cjs +3612 -167
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +4 -3
- package/dist/internal.d.ts +4 -3
- package/dist/internal.js +1 -1
- package/dist/next/client.cjs +3610 -177
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +3615 -170
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +3 -8
- package/dist/next/index.d.ts +3 -8
- package/dist/next/index.js +14 -5
- package/dist/next/index.js.map +1 -1
- package/dist/{pb-EMn_MF9g.d.ts → pb-DioxNuEV.d.cts} +3 -1
- package/dist/{pb-DcknUtCh.d.cts → pb-HegMSSk-.d.ts} +3 -1
- package/dist/pkg/palbe_mls_bg.wasm +0 -0
- package/dist/react/index.cjs +97 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +28 -2
- package/dist/react/index.d.ts +28 -2
- package/dist/react/index.js +86 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +6 -3
- package/dist/chunk-AVEXGXRQ.js +0 -3384
- package/dist/chunk-AVEXGXRQ.js.map +0 -1
package/dist/next/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { P as PB } from '../pb-
|
|
3
|
-
import '../analytics-facade-
|
|
2
|
+
import { P as PB } from '../pb-DioxNuEV.cjs';
|
|
3
|
+
import '../analytics-facade-CAKBIH_U.cjs';
|
|
4
|
+
import 'livekit-client';
|
|
4
5
|
import '../storage-BPaeSG8K.cjs';
|
|
5
6
|
import './pooled-flags-Bwq4usn0.js';
|
|
6
7
|
|
|
@@ -39,12 +40,6 @@ interface HandleAuthCallbackOptions {
|
|
|
39
40
|
*/
|
|
40
41
|
declare function handleAuthCallback(opts?: HandleAuthCallbackOptions): (request: NextRequest) => Promise<Response>;
|
|
41
42
|
|
|
42
|
-
/**
|
|
43
|
-
* `pb_<endpointRef>_<scope><random>` → endpointRef ('' if malformed).
|
|
44
|
-
* Single source of truth — used by runtime storage scoping AND the
|
|
45
|
-
* palbe/next cookie naming; the two MUST agree or browser and server
|
|
46
|
-
* would read different cookies.
|
|
47
|
-
*/
|
|
48
43
|
declare function endpointRefFromApiKey(apiKey: string): string;
|
|
49
44
|
|
|
50
45
|
/**
|
package/dist/next/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { P as PB } from '../pb-
|
|
3
|
-
import '../analytics-facade-
|
|
2
|
+
import { P as PB } from '../pb-HegMSSk-.js';
|
|
3
|
+
import '../analytics-facade-DLH-KivI.js';
|
|
4
|
+
import 'livekit-client';
|
|
4
5
|
import '../storage-BPaeSG8K.js';
|
|
5
6
|
import './pooled-flags-Bwq4usn0.js';
|
|
6
7
|
|
|
@@ -39,12 +40,6 @@ interface HandleAuthCallbackOptions {
|
|
|
39
40
|
*/
|
|
40
41
|
declare function handleAuthCallback(opts?: HandleAuthCallbackOptions): (request: NextRequest) => Promise<Response>;
|
|
41
42
|
|
|
42
|
-
/**
|
|
43
|
-
* `pb_<endpointRef>_<scope><random>` → endpointRef ('' if malformed).
|
|
44
|
-
* Single source of truth — used by runtime storage scoping AND the
|
|
45
|
-
* palbe/next cookie naming; the two MUST agree or browser and server
|
|
46
|
-
* would read different cookies.
|
|
47
|
-
*/
|
|
48
43
|
declare function endpointRefFromApiKey(apiKey: string): string;
|
|
49
44
|
|
|
50
45
|
/**
|
package/dist/next/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
createBoundClient,
|
|
14
14
|
endpointRefFromApiKey,
|
|
15
15
|
getRuntime
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-EMQGOKW6.js";
|
|
17
17
|
|
|
18
18
|
// src/next/shared.ts
|
|
19
19
|
function requireGlobalConfig(hint) {
|
|
@@ -42,9 +42,18 @@ async function importNextServer(caller) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/next/callback.ts
|
|
45
|
-
function safeNextPath(next) {
|
|
46
|
-
if (!next
|
|
47
|
-
if (
|
|
45
|
+
function safeNextPath(next, origin) {
|
|
46
|
+
if (!next) return null;
|
|
47
|
+
if (/[\u0000-\u0020\u007f\s]/.test(next)) return null;
|
|
48
|
+
if (next[0] !== "/" || next[1] === "/" || next[1] === "\\") return null;
|
|
49
|
+
const firstSlash = next.indexOf("/");
|
|
50
|
+
const firstColon = next.indexOf(":");
|
|
51
|
+
if (firstColon !== -1 && firstColon < firstSlash) return null;
|
|
52
|
+
try {
|
|
53
|
+
if (new URL(next, origin).origin !== origin) return null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
48
57
|
return next;
|
|
49
58
|
}
|
|
50
59
|
function wireErrorCode(raw) {
|
|
@@ -106,7 +115,7 @@ function handleAuthCallback(opts) {
|
|
|
106
115
|
}
|
|
107
116
|
const wire = asWireAuthResult(raw);
|
|
108
117
|
if (!wire) return redirect(fallback, { auth_error: "oauth_exchange_failed" });
|
|
109
|
-
const response = redirect(safeNextPath(params.get("next")) ?? fallback);
|
|
118
|
+
const response = redirect(safeNextPath(params.get("next"), request.nextUrl.origin) ?? fallback);
|
|
110
119
|
const write = encodeSessionCookiesDecoded(endpointRefFromApiKey(config.apiKey), {
|
|
111
120
|
accessToken: wire.access_token,
|
|
112
121
|
refreshToken: wire.refresh_token,
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/shared.ts","../../src/next/callback.ts","../../src/next/middleware.ts","../../src/next/server.ts"],"sourcesContent":["/**\n * Cross-surface helpers for the palbe/next server entries (pbServer,\n * palbeMiddleware, handleAuthCallback): global-config lookup with a\n * surface-specific guided error, and the lazy 'next/server' import.\n */\nimport type { PalbeConfig } from '../config.js';\nimport { BackendError } from '../errors.js';\nimport { getRuntime } from '../internal.js';\n\n/**\n * Global config with a surface-specific guided error when absent. Each\n * surface names the import location that configures ITS module graph —\n * Next bundles middleware separately from the server graph, so the hint\n * matters (layout.tsx for Server Components / Route Handlers, middleware.ts\n * for the middleware).\n */\nexport function requireGlobalConfig(hint: string): PalbeConfig {\n try {\n return getRuntime().config;\n } catch {\n throw new BackendError('notConfigured', {\n code: 'not_configured',\n message: `Palbe is not configured in this module graph. Run 'palbase web link' and ${hint}`,\n });\n }\n}\n\nlet nextServerModule: Promise<typeof import('next/server')> | undefined;\n\n/**\n * Lazy 'next/server' import (MODULE-GRAPH RULE: never top-level — importing\n * palbe/next must work without next installed) with a guided failure.\n * The module promise is memoized: bundler dynamic-import machinery can take\n * macrotask-scale hops PER CALL, so without the cache two near-simultaneous\n * middleware invocations could straddle a refresh — every call after the\n * first now resolves in pure microtasks.\n */\nexport async function importNextServer(caller: string): Promise<typeof import('next/server')> {\n nextServerModule ??= import('next/server');\n try {\n return await nextServerModule;\n } catch (cause) {\n nextServerModule = undefined; // never poison later calls with a cached rejection\n const detail = cause instanceof Error && cause.message ? ` (${cause.message})` : '';\n throw new BackendError('validation', {\n code: 'next_required',\n message: `${caller}() requires Next.js — 'next/server' could not be resolved${detail}.`,\n });\n }\n}\n","/**\n * handleAuthCallback — the OAuth completion route handler factory.\n *\n * The app's redirect_uri (registered when calling signInWithOAuth with\n * `redirectTo: '<app>/auth/callback?provider=google&next=/dash'`) lands\n * here with `?code&state` from the provider. The handler exchanges them\n * SERVER-SIDE against palauth (PKCE verifier + state live server-side; the\n * browser never holds them), writes the session cookies onto a redirect\n * response, and sends the user on.\n *\n * ```ts\n * // app/auth/callback/route.ts\n * import { handleAuthCallback } from '@palbase/web/next';\n * export const GET = handleAuthCallback({ defaultNext: '/' });\n * ```\n *\n * MODULE-GRAPH RULE (same as ./index.ts): 'next/server' only lazily; the\n * type-only NextRequest import erases at build time.\n */\nimport type { NextRequest } from 'next/server';\nimport { asWireAuthResult } from '../auth-wire.js';\nimport {\n encodeSessionCookiesDecoded,\n endpointRefFromApiKey,\n SESSION_COOKIE_ATTRS,\n} from './cookie-codec.js';\nimport { importNextServer, requireGlobalConfig } from './shared.js';\n\nexport interface HandleAuthCallbackOptions {\n /** Where to land when the callback carries no (valid) `next` param — and on every error/MFA redirect. Default '/'. */\n defaultNext?: string;\n}\n\n/**\n * Open-redirect guard: `next` rides the OAuth redirect querystring, so it is\n * attacker-influencable — only RELATIVE paths are honored. '//host' is\n * protocol-relative and '/\\\\host' normalizes to it in browsers; anything\n * absolute ('https://…', 'javascript:…') fails the leading-'/' check.\n */\nfunction safeNextPath(next: string | null): string | null {\n if (!next?.startsWith('/')) return null;\n if (next[1] === '/' || next[1] === '\\\\') return null;\n return next;\n}\n\nfunction wireErrorCode(raw: unknown): string {\n if (typeof raw === 'object' && raw !== null) {\n const code = (raw as Record<string, unknown>).error;\n if (typeof code === 'string' && code !== '') return code;\n }\n return 'oauth_exchange_failed';\n}\n\n/**\n * Build the callback route handler. Outcomes (all 307 redirects, all with\n * `Cache-Control: private, no-store` — auth outcomes must never cache):\n * - provider `error` param → `?auth_error=<error>` (+ `auth_error_description`)\n * - missing code/state/provider → `?auth_error=invalid_callback`\n * - exchanged AuthResult → session cookies on the redirect to `next ?? defaultNext ?? '/'`\n * - MFA union → `?mfa_token=…&mfa_factors=a,b` on defaultNext, NO cookies\n * - exchange failure → `?auth_error=<wire code | oauth_exchange_failed>`\n */\nexport function handleAuthCallback(\n opts?: HandleAuthCallbackOptions,\n): (request: NextRequest) => Promise<Response> {\n return async (request) => {\n const config = requireGlobalConfig(\n 'import the generated palbe.gen.ts once in app/layout.tsx — route handlers share the server module graph with the root layout.',\n );\n const { NextResponse } = await importNextServer('handleAuthCallback');\n const fallback = opts?.defaultNext ?? '/';\n\n const redirect = (path: string, query?: Record<string, string>) => {\n const url = new URL(path, request.nextUrl);\n for (const [key, value] of Object.entries(query ?? {})) url.searchParams.set(key, value);\n const response = NextResponse.redirect(url); // 307 — the callback GET stays a GET\n response.headers.set('cache-control', 'private, no-store');\n return response;\n };\n\n const params = request.nextUrl.searchParams;\n const providerError = params.get('error');\n if (providerError) {\n const query: Record<string, string> = { auth_error: providerError };\n const description = params.get('error_description');\n if (description) query.auth_error_description = description;\n return redirect(fallback, query);\n }\n\n const code = params.get('code');\n const state = params.get('state');\n const provider = params.get('provider');\n if (!code || !state || !provider) {\n return redirect(fallback, { auth_error: 'invalid_callback' });\n }\n\n let exchange: Response;\n try {\n exchange = await fetch(\n `${config.url}/auth/oauth/${encodeURIComponent(provider)}/callback` +\n `?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`,\n { headers: { apikey: config.apiKey } },\n );\n } catch {\n return redirect(fallback, { auth_error: 'oauth_exchange_failed' });\n }\n let raw: unknown = null;\n try {\n raw = await exchange.json();\n } catch {\n // malformed body — the shape checks below turn this into auth_error\n }\n if (!exchange.ok) return redirect(fallback, { auth_error: wireErrorCode(raw) });\n\n if (typeof raw === 'object' && raw !== null) {\n const obj = raw as Record<string, unknown>;\n // MFA union: relay the challenge to the app — NO cookies until it completes.\n if (obj.mfa_required === true && typeof obj.mfa_token === 'string') {\n const factors = Array.isArray(obj.mfa_factors)\n ? obj.mfa_factors.filter((f): f is string => typeof f === 'string')\n : [];\n return redirect(fallback, { mfa_token: obj.mfa_token, mfa_factors: factors.join(',') });\n }\n }\n\n // Zombie-session guard (shared with the auth facade): cookies are only\n // ever written for a FULLY-proven AuthResult.\n const wire = asWireAuthResult(raw);\n if (!wire) return redirect(fallback, { auth_error: 'oauth_exchange_failed' });\n\n const response = redirect(safeNextPath(params.get('next')) ?? fallback);\n // DECODED codec values: NextResponse.cookies percent-encodes on write,\n // producing exactly the shared-codec wire bytes (encodeSessionCookiesDecoded).\n const write = encodeSessionCookiesDecoded(endpointRefFromApiKey(config.apiKey), {\n accessToken: wire.access_token,\n refreshToken: wire.refresh_token,\n expiresAt: Date.now() + wire.expires_in * 1000,\n });\n for (const { name, value } of write.set) {\n response.cookies.set(name, value, SESSION_COOKIE_ATTRS);\n }\n // Codec overflow guard: expire one-past-the-end of the written run.\n for (const name of write.clear) {\n response.cookies.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n }\n return response;\n };\n}\n","/**\n * palbeMiddleware — proactive cookie-session refresh in Next.js middleware.\n *\n * REQUIRED — not optional — for session-bearing RSC apps. Server Components\n * cannot write cookies, so pbServer's reactive refresh cannot PERSIST a\n * rotated session there: every RSC render would re-refresh from the SAME\n * cookie refresh token, and once two of those land more than 30s apart\n * (outside palauth's rotation grace) the reuse detector REVOKES THE WHOLE\n * TOKEN FAMILY — the user is force-logged-out everywhere. This middleware\n * refreshes the cookie BEFORE the RSC tree renders, so pbServer hydrates a\n * still-valid session and never burns the token itself.\n *\n * MODULE-GRAPH RULES (same as ./index.ts): 'next/server' is imported only\n * lazily (the type-only NextRequest/NextResponse imports erase at build\n * time), and middleware.ts must import the generated palbe.gen.ts itself —\n * Next bundles middleware as its OWN module graph; the layout.tsx import\n * does not reach it.\n *\n * ```ts\n * // middleware.ts\n * import './palbe.gen';\n * import { palbeMiddleware } from '@palbase/web/next';\n * import type { NextRequest } from 'next/server';\n *\n * export function middleware(request: NextRequest) {\n * return palbeMiddleware(request);\n * }\n * export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };\n * ```\n */\nimport type { NextRequest, NextResponse } from 'next/server';\nimport type { PalbeConfig } from '../config.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookiesDecoded,\n endpointRefFromApiKey,\n SESSION_COOKIE_ATTRS,\n type StoredSession,\n} from './cookie-codec.js';\nimport { importNextServer, requireGlobalConfig } from './shared.js';\n\nconst DEFAULT_REFRESH_MARGIN_MS = 60_000;\n\nexport interface PalbeMiddlewareOptions {\n /** Refresh when the access token expires within this window. Default 60s. */\n refreshMarginMs?: number;\n /**\n * Pre-built response to decorate (e.g. from chained middleware) instead of\n * building `NextResponse.next({ request })`. Pass-through cases return it\n * UNMODIFIED. Caveat: a pre-built response snapshotted the request headers\n * at ITS creation, so a rotated cookie reaches the browser (Set-Cookie)\n * but not the downstream RSC render of THIS request — prefer omitting this\n * and building your response after palbeMiddleware when possible.\n */\n response?: NextResponse;\n}\n\ntype RefreshOutcome =\n | { kind: 'rotated'; session: StoredSession }\n | { kind: 'terminal' } // 400/401/403 — refresh token dead, sign out\n | { kind: 'transient' }; // network / 5xx / malformed — keep the session\n\n/**\n * Per-process single-flight: concurrent middleware invocations holding the\n * SAME refresh token (parallel tabs / prefetches) share one refresh call —\n * a per-token thundering herd would race palauth rotation. Multi-pod\n * concurrency is covered by palauth's 30s rotation grace (concurrent\n * refreshes of one token all succeed); per-process dedup is all that's\n * needed here (Studio precedent — don't over-engineer).\n */\nconst inflightRefreshes = new Map<string, Promise<RefreshOutcome>>();\n\nfunction refreshSingleFlight(config: PalbeConfig, refreshToken: string): Promise<RefreshOutcome> {\n const existing = inflightRefreshes.get(refreshToken);\n if (existing) return existing;\n const pending = refresh(config, refreshToken).finally(() => {\n inflightRefreshes.delete(refreshToken);\n });\n inflightRefreshes.set(refreshToken, pending);\n return pending;\n}\n\n/** Direct palauth refresh call (no runtime: middleware needs only this one wire). */\nasync function refresh(config: PalbeConfig, refreshToken: string): Promise<RefreshOutcome> {\n try {\n const res = await fetch(`${config.url}/auth/token/refresh`, {\n method: 'POST',\n headers: { apikey: config.apiKey, 'content-type': 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n if (res.status === 400 || res.status === 401 || res.status === 403) {\n return { kind: 'terminal' };\n }\n if (!res.ok) return { kind: 'transient' };\n const raw: unknown = await res.json();\n if (typeof raw === 'object' && raw !== null) {\n const obj = raw as Record<string, unknown>;\n if (\n typeof obj.access_token === 'string' &&\n typeof obj.refresh_token === 'string' &&\n typeof obj.expires_in === 'number'\n ) {\n return {\n kind: 'rotated',\n session: {\n accessToken: obj.access_token,\n refreshToken: obj.refresh_token,\n expiresAt: Date.now() + obj.expires_in * 1000,\n },\n };\n }\n }\n return { kind: 'transient' }; // malformed 200 — never destroy a session over it\n } catch {\n return { kind: 'transient' }; // network failure\n }\n}\n\n/**\n * Keep the session cookie fresh for the request. No session / still-fresh\n * session → untouched pass-through (no cache-control changes). Stale or\n * expired → single-flight refresh, then the rotated (or cleared, on a dead\n * token) cookies are written onto BOTH the request (for the downstream RSC\n * render via `NextResponse.next({ request })`) and the response (for the\n * browser), with `Cache-Control: private, no-store` on touched responses.\n *\n * Cookie writes go through Next's cookie APIs with the DECODED codec values:\n * Next percent-encodes on serialization, so the wire bytes come out exactly\n * as the shared codec's (see encodeSessionCookiesDecoded). NextRequest's\n * cookie store is NOT a CookieStoreLike (its set is the 2-arg/RequestCookie\n * form), hence the direct codec calls here instead of the pbServer adapter.\n */\nexport async function palbeMiddleware(\n request: NextRequest,\n opts?: PalbeMiddlewareOptions,\n): Promise<NextResponse> {\n const config = requireGlobalConfig(\n 'import the generated palbe.gen.ts at the top of middleware.ts — Next bundles middleware as its own module graph, so the layout.tsx import does not reach it.',\n );\n const { NextResponse } = await importNextServer('palbeMiddleware');\n const passThrough = (): NextResponse => opts?.response ?? NextResponse.next({ request });\n\n const ref = endpointRefFromApiKey(config.apiKey);\n const session = decodeSessionCookies((name) => request.cookies.get(name)?.value, ref);\n if (!session) return passThrough();\n if (session.expiresAt - Date.now() > (opts?.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS)) {\n return passThrough();\n }\n\n const outcome = await refreshSingleFlight(config, session.refreshToken);\n if (outcome.kind === 'transient') return passThrough(); // keep the session over blips\n\n // Session-cookie names in the incoming jar — the browser's state mirror.\n const staleNames = clearedSessionCookieNames(ref, (name) => request.cookies.has(name));\n\n if (outcome.kind === 'terminal') {\n for (const name of staleNames) request.cookies.delete(name);\n const response = opts?.response ?? NextResponse.next({ request });\n for (const name of staleNames) {\n response.cookies.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n }\n response.headers.set('cache-control', 'private, no-store');\n return response;\n }\n\n const write = encodeSessionCookiesDecoded(ref, outcome.session);\n for (const name of staleNames) request.cookies.delete(name);\n for (const { name, value } of write.set) request.cookies.set(name, value);\n for (const name of write.clear) request.cookies.delete(name);\n // Build AFTER the request-cookie mutation: NextResponse.next({ request })\n // snapshots the request headers for the downstream RSC render.\n const response = opts?.response ?? NextResponse.next({ request });\n const written = new Set(write.set.map((c) => c.name));\n for (const { name, value } of write.set) {\n response.cookies.set(name, value, SESSION_COOKIE_ATTRS);\n }\n // Browser-side cleanup: stale names not part of the new run (a shrunken /\n // grown chunk layout) + the codec's one-past-the-end overflow guard.\n for (const name of new Set([...write.clear, ...staleNames])) {\n if (!written.has(name)) {\n response.cookies.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n }\n }\n response.headers.set('cache-control', 'private, no-store');\n return response;\n}\n","/**\n * pbServer — the per-request server client of the Next.js adapter.\n *\n * Every call builds a FRESH runtime over the request's cookie store and\n * returns a runtime-BOUND `PB` client: typed namespaces, `call`, `upload`\n * and `auth` all resolve that runtime, never the process-global slot — two\n * bound clients serving concurrent requests cannot leak each other's\n * sessions (see createBoundClient). The store itself is read lazily per\n * adapter operation, so nothing is snapshotted beyond the call.\n *\n * MODULE-GRAPH RULE (same as ./index.ts): 'next/headers' is imported ONLY\n * lazily inside the default-store path — importing this module, and using\n * pbServer with an injected store, must work without next installed.\n */\nimport { BackendError } from '../errors.js';\nimport { buildRuntime, createBoundClient } from '../internal.js';\nimport type { PB } from '../pb.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookiesDecoded,\n endpointRefFromApiKey,\n SESSION_COOKIE_ATTRS,\n} from './cookie-codec.js';\nimport { requireGlobalConfig } from './shared.js';\n\n/**\n * Structural slice of Next's request cookie store (`await cookies()`); any\n * object honoring it works — the seam for tests and non-Next servers.\n * `set`/`delete` are optional because RSC-scope stores are read-only.\n */\nexport interface CookieStoreLike {\n get(name: string): { name: string; value: string } | undefined;\n getAll?(): Array<{ name: string; value: string }>;\n set?(name: string, value: string, options?: Record<string, unknown>): void;\n delete?(name: string): void;\n}\n\nfunction nextRequired(cause: unknown): BackendError {\n const detail = cause instanceof Error && cause.message ? ` (${cause.message})` : '';\n return new BackendError('validation', {\n code: 'next_required',\n message:\n 'pbServer() could not resolve a Next.js request cookie store: call it inside a Server Component, Server Action or Route Handler — or pass a store explicitly via pbServer({ cookies })' +\n `${detail}.`,\n });\n}\n\n/**\n * Next CONTROL-FLOW errors must pass through UNWRAPPED. During static\n * prerender (`next build`) `cookies()` throws DynamicServerError (digest\n * 'DYNAMIC_SERVER_USAGE'), which Next catches upstream to mark the route\n * dynamic — wrapping it into a BackendError would turn that bailout signal\n * into a real prerender error and FAIL THE BUILD of every page calling\n * pbServer (caught live in the P3 Next smoke). 'NEXT_'-prefixed digests are\n * the same sentinel family (redirect / not-found / static-gen bailout).\n * Genuine outside-request-scope errors carry no digest and still map to\n * next_required.\n */\nexport function isNextControlFlowError(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false;\n const digest = (err as { digest?: unknown }).digest;\n return (\n typeof digest === 'string' && (digest === 'DYNAMIC_SERVER_USAGE' || digest.startsWith('NEXT_'))\n );\n}\n\n/** Lazy default store: `await (await import('next/headers')).cookies()`. */\nasync function nextCookieStore(): Promise<CookieStoreLike> {\n let cookies: () => Promise<CookieStoreLike>;\n try {\n ({ cookies } = await import('next/headers'));\n } catch (cause) {\n throw nextRequired(cause); // next not installed / not resolvable\n }\n try {\n return await cookies();\n } catch (cause) {\n if (isNextControlFlowError(cause)) throw cause; // static-prerender bailout etc.\n throw nextRequired(cause); // installed, but outside a request scope\n }\n}\n\n/** Delete one cookie: set-with-maxAge-0 when the store can set (parity with the browser side), else delete, else swallow (read-only RSC store). */\nfunction removeCookie(store: CookieStoreLike, name: string): void {\n try {\n if (store.set) store.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n else store.delete?.(name);\n } catch {\n // RSC read-only store — cookie mutation is the middleware's job there\n }\n}\n\n/**\n * SessionStorageAdapter over the request cookie store.\n *\n * SAVE SEMANTICS (decision, documented): only FULL sessions (access token +\n * expiry present) are written; a refresh-only save is SKIPPED with no throw\n * rather than written as {a:'',e:0}. The cookie is SHARED with the browser —\n * degrading it would drop an access token the browser may still be using and\n * force a refresh per page-load. The refresh-only branch is a defensive\n * legacy path anyway: every successful refresh saves a full session.\n *\n * All writes swallow per-cookie failures: in RSC scope Next's store throws\n * on mutation; the request still proceeds with the in-memory session but the\n * rotated cookie is NOT persisted. palbeMiddleware is therefore REQUIRED —\n * not optional — for session-bearing RSC apps: without it every RSC render\n * re-refreshes from the SAME cookie refresh token, and once two such\n * refreshes land more than 30s apart (outside palauth's rotation grace) the\n * reuse detector REVOKES THE WHOLE TOKEN FAMILY — the user is\n * force-logged-out everywhere.\n *\n * Values are written DECODED (encodeSessionCookiesDecoded): Next's real\n * cookies() store percent-encodes on serialization, yielding exactly the\n * shared-codec wire bytes; a raw store (tests, custom servers) persists the\n * bare JSON, which the load path's decode tolerates identically (tokens are\n * %-free base64url, so decodeURIComponent is the identity on them).\n */\nfunction serverCookieAdapter(store: CookieStoreLike, endpointRef: string): SessionStorageAdapter {\n const present = (name: string): boolean => store.get(name) !== undefined;\n return {\n load(): PersistedSession | null {\n const stored = decodeSessionCookies((name) => store.get(name)?.value, endpointRef);\n if (!stored) return null;\n // A browser-side refresh-only write round-trips as a:'' / e:0 —\n // normalize to the legacy shape so hydration takes the expired-trick.\n return stored.accessToken && stored.expiresAt > 0\n ? {\n refreshToken: stored.refreshToken,\n accessToken: stored.accessToken,\n expiresAt: stored.expiresAt,\n }\n : { refreshToken: stored.refreshToken };\n },\n save(session: PersistedSession): void {\n if (!session.accessToken || !session.expiresAt) return; // see SAVE SEMANTICS above\n for (const name of clearedSessionCookieNames(endpointRef, present)) {\n removeCookie(store, name);\n }\n const { set, clear } = encodeSessionCookiesDecoded(endpointRef, {\n accessToken: session.accessToken,\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt,\n });\n for (const { name, value } of set) {\n try {\n store.set?.(name, value, { ...SESSION_COOKIE_ATTRS });\n } catch {\n // RSC read-only store\n }\n }\n // Overflow guard (codec contract): delete one-past-the-end so a stale\n // orphan chunk behind a gap can never join a future chunk run.\n for (const name of clear) removeCookie(store, name);\n },\n clear(): void {\n for (const name of clearedSessionCookieNames(endpointRef, present)) {\n removeCookie(store, name);\n }\n },\n };\n}\n\n/**\n * Build a per-request `PB` client over the request's cookies. Reads the\n * session the browser SDK wrote (shared cookie codec), refreshes pre-flight\n * when expired, and — where the store is writable (Server Actions, Route\n * Handlers) — persists the rotated session back.\n *\n * Each call builds a fresh runtime (a handful of small objects, no I/O) —\n * cheap enough to call per data access; code paths invoking it many times\n * within one request MAY memoize per-request (e.g. React's `cache()`), but\n * don't have to.\n *\n * ```ts\n * const pb = await pbServer();\n * const todos = await pb.todos.list();\n * ```\n */\nexport async function pbServer(opts?: { cookies?: CookieStoreLike }): Promise<PB> {\n const config = requireGlobalConfig(\n 'import the generated palbe.gen.ts once in app/layout.tsx — the root-layout import configures Server Components and Route Handlers too.',\n );\n const store = opts?.cookies ?? (await nextCookieStore());\n const ref = endpointRefFromApiKey(config.apiKey);\n const rt = buildRuntime({ ...config, storage: serverCookieAdapter(store, ref) });\n return createBoundClient(rt);\n}\n"],"mappings":"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","names":["response","response"]}
|
|
1
|
+
{"version":3,"sources":["../../src/next/shared.ts","../../src/next/callback.ts","../../src/next/middleware.ts","../../src/next/server.ts"],"sourcesContent":["/**\n * Cross-surface helpers for the palbe/next server entries (pbServer,\n * palbeMiddleware, handleAuthCallback): global-config lookup with a\n * surface-specific guided error, and the lazy 'next/server' import.\n */\nimport type { PalbeConfig } from '../config.js';\nimport { BackendError } from '../errors.js';\nimport { getRuntime } from '../internal.js';\n\n/**\n * Global config with a surface-specific guided error when absent. Each\n * surface names the import location that configures ITS module graph —\n * Next bundles middleware separately from the server graph, so the hint\n * matters (layout.tsx for Server Components / Route Handlers, middleware.ts\n * for the middleware).\n */\nexport function requireGlobalConfig(hint: string): PalbeConfig {\n try {\n return getRuntime().config;\n } catch {\n throw new BackendError('notConfigured', {\n code: 'not_configured',\n message: `Palbe is not configured in this module graph. Run 'palbase web link' and ${hint}`,\n });\n }\n}\n\nlet nextServerModule: Promise<typeof import('next/server')> | undefined;\n\n/**\n * Lazy 'next/server' import (MODULE-GRAPH RULE: never top-level — importing\n * palbe/next must work without next installed) with a guided failure.\n * The module promise is memoized: bundler dynamic-import machinery can take\n * macrotask-scale hops PER CALL, so without the cache two near-simultaneous\n * middleware invocations could straddle a refresh — every call after the\n * first now resolves in pure microtasks.\n */\nexport async function importNextServer(caller: string): Promise<typeof import('next/server')> {\n nextServerModule ??= import('next/server');\n try {\n return await nextServerModule;\n } catch (cause) {\n nextServerModule = undefined; // never poison later calls with a cached rejection\n const detail = cause instanceof Error && cause.message ? ` (${cause.message})` : '';\n throw new BackendError('validation', {\n code: 'next_required',\n message: `${caller}() requires Next.js — 'next/server' could not be resolved${detail}.`,\n });\n }\n}\n","/**\n * handleAuthCallback — the OAuth completion route handler factory.\n *\n * The app's redirect_uri (registered when calling signInWithOAuth with\n * `redirectTo: '<app>/auth/callback?provider=google&next=/dash'`) lands\n * here with `?code&state` from the provider. The handler exchanges them\n * SERVER-SIDE against palauth (PKCE verifier + state live server-side; the\n * browser never holds them), writes the session cookies onto a redirect\n * response, and sends the user on.\n *\n * ```ts\n * // app/auth/callback/route.ts\n * import { handleAuthCallback } from '@palbase/web/next';\n * export const GET = handleAuthCallback({ defaultNext: '/' });\n * ```\n *\n * MODULE-GRAPH RULE (same as ./index.ts): 'next/server' only lazily; the\n * type-only NextRequest import erases at build time.\n */\nimport type { NextRequest } from 'next/server';\nimport { asWireAuthResult } from '../auth-wire.js';\nimport {\n encodeSessionCookiesDecoded,\n endpointRefFromApiKey,\n SESSION_COOKIE_ATTRS,\n} from './cookie-codec.js';\nimport { importNextServer, requireGlobalConfig } from './shared.js';\n\nexport interface HandleAuthCallbackOptions {\n /** Where to land when the callback carries no (valid) `next` param — and on every error/MFA redirect. Default '/'. */\n defaultNext?: string;\n}\n\n/**\n * Open-redirect guard: `next` rides the OAuth redirect querystring, so it is\n * attacker-influencable — only same-origin, path-absolute relative paths are\n * honored, and the post-auth victim is never sent off-origin.\n *\n * A leading-'/' heuristic is NOT enough. The value is later resolved with\n * `new URL(next, base)`, and the WHATWG URL parser STRIPS ASCII tab (0x09),\n * LF (0x0A) and CR (0x0D) before parsing — so `/<TAB>/evil.com` slips past a\n * \"next[1] is not '/'\" check yet parses to `//evil.com` → `https://evil.com`\n * (CWE-601 open redirect). We defend with TWO independent gates that must\n * BOTH pass, matching OWASP unvalidated-redirect guidance:\n *\n * 1. RAW-STRING gate (reject before normalization can hide the payload):\n * - reject any C0 control char (\\x00-\\x1F, incl. \\t \\n \\r) or other\n * whitespace — these are exactly what URL parsing strips/normalizes\n * away;\n * - require a path-absolute prefix: a single '/' NOT followed by '/' or\n * '\\\\' (blocks '//host' protocol-relative and '/\\\\host' backslash\n * tricks);\n * - reject a scheme: any ':' appearing before the first '/' ('javascript:',\n * 'https:'). Path-absolute inputs start with '/', so this also rejects\n * bare 'foo:bar'.\n * 2. CANONICAL gate: resolve `new URL(next, origin)` and require the result\n * to land on the SAME origin. This is the authoritative check — it sees\n * exactly what the browser will, after normalization.\n *\n * Anything ambiguous returns null so the caller falls back to a safe default.\n */\nfunction safeNextPath(next: string | null, origin: string): string | null {\n if (!next) return null;\n // 1a. No control chars / whitespace — URL parsing would strip these and\n // change what the path means (the \\t \\n \\r open-redirect class).\n // biome-ignore lint/suspicious/noControlCharactersInRegex: rejecting C0 is the point of the guard.\n if (/[\\u0000-\\u0020\\u007f\\s]/.test(next)) return null;\n // 1b. Must be path-absolute: a single leading '/', not '//' or '/\\\\'.\n if (next[0] !== '/' || next[1] === '/' || next[1] === '\\\\') return null;\n // 1c. No scheme before the first path separator (defense in depth; a\n // path-absolute string can't carry one, but reject explicitly).\n const firstSlash = next.indexOf('/');\n const firstColon = next.indexOf(':');\n if (firstColon !== -1 && firstColon < firstSlash) return null;\n // 2. Canonical same-origin check — resolve as the browser/redirect helper\n // will and confirm we never leave this origin.\n try {\n if (new URL(next, origin).origin !== origin) return null;\n } catch {\n return null;\n }\n return next;\n}\n\nfunction wireErrorCode(raw: unknown): string {\n if (typeof raw === 'object' && raw !== null) {\n const code = (raw as Record<string, unknown>).error;\n if (typeof code === 'string' && code !== '') return code;\n }\n return 'oauth_exchange_failed';\n}\n\n/**\n * Build the callback route handler. Outcomes (all 307 redirects, all with\n * `Cache-Control: private, no-store` — auth outcomes must never cache):\n * - provider `error` param → `?auth_error=<error>` (+ `auth_error_description`)\n * - missing code/state/provider → `?auth_error=invalid_callback`\n * - exchanged AuthResult → session cookies on the redirect to `next ?? defaultNext ?? '/'`\n * - MFA union → `?mfa_token=…&mfa_factors=a,b` on defaultNext, NO cookies\n * - exchange failure → `?auth_error=<wire code | oauth_exchange_failed>`\n */\nexport function handleAuthCallback(\n opts?: HandleAuthCallbackOptions,\n): (request: NextRequest) => Promise<Response> {\n return async (request) => {\n const config = requireGlobalConfig(\n 'import the generated palbe.gen.ts once in app/layout.tsx — route handlers share the server module graph with the root layout.',\n );\n const { NextResponse } = await importNextServer('handleAuthCallback');\n const fallback = opts?.defaultNext ?? '/';\n\n const redirect = (path: string, query?: Record<string, string>) => {\n const url = new URL(path, request.nextUrl);\n for (const [key, value] of Object.entries(query ?? {})) url.searchParams.set(key, value);\n const response = NextResponse.redirect(url); // 307 — the callback GET stays a GET\n response.headers.set('cache-control', 'private, no-store');\n return response;\n };\n\n const params = request.nextUrl.searchParams;\n const providerError = params.get('error');\n if (providerError) {\n const query: Record<string, string> = { auth_error: providerError };\n const description = params.get('error_description');\n if (description) query.auth_error_description = description;\n return redirect(fallback, query);\n }\n\n const code = params.get('code');\n const state = params.get('state');\n const provider = params.get('provider');\n if (!code || !state || !provider) {\n return redirect(fallback, { auth_error: 'invalid_callback' });\n }\n\n let exchange: Response;\n try {\n exchange = await fetch(\n `${config.url}/auth/oauth/${encodeURIComponent(provider)}/callback` +\n `?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`,\n { headers: { apikey: config.apiKey } },\n );\n } catch {\n return redirect(fallback, { auth_error: 'oauth_exchange_failed' });\n }\n let raw: unknown = null;\n try {\n raw = await exchange.json();\n } catch {\n // malformed body — the shape checks below turn this into auth_error\n }\n if (!exchange.ok) return redirect(fallback, { auth_error: wireErrorCode(raw) });\n\n if (typeof raw === 'object' && raw !== null) {\n const obj = raw as Record<string, unknown>;\n // MFA union: relay the challenge to the app — NO cookies until it completes.\n if (obj.mfa_required === true && typeof obj.mfa_token === 'string') {\n const factors = Array.isArray(obj.mfa_factors)\n ? obj.mfa_factors.filter((f): f is string => typeof f === 'string')\n : [];\n return redirect(fallback, { mfa_token: obj.mfa_token, mfa_factors: factors.join(',') });\n }\n }\n\n // Zombie-session guard (shared with the auth facade): cookies are only\n // ever written for a FULLY-proven AuthResult.\n const wire = asWireAuthResult(raw);\n if (!wire) return redirect(fallback, { auth_error: 'oauth_exchange_failed' });\n\n const response = redirect(safeNextPath(params.get('next'), request.nextUrl.origin) ?? fallback);\n // DECODED codec values: NextResponse.cookies percent-encodes on write,\n // producing exactly the shared-codec wire bytes (encodeSessionCookiesDecoded).\n const write = encodeSessionCookiesDecoded(endpointRefFromApiKey(config.apiKey), {\n accessToken: wire.access_token,\n refreshToken: wire.refresh_token,\n expiresAt: Date.now() + wire.expires_in * 1000,\n });\n for (const { name, value } of write.set) {\n response.cookies.set(name, value, SESSION_COOKIE_ATTRS);\n }\n // Codec overflow guard: expire one-past-the-end of the written run.\n for (const name of write.clear) {\n response.cookies.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n }\n return response;\n };\n}\n","/**\n * palbeMiddleware — proactive cookie-session refresh in Next.js middleware.\n *\n * REQUIRED — not optional — for session-bearing RSC apps. Server Components\n * cannot write cookies, so pbServer's reactive refresh cannot PERSIST a\n * rotated session there: every RSC render would re-refresh from the SAME\n * cookie refresh token, and once two of those land more than 30s apart\n * (outside palauth's rotation grace) the reuse detector REVOKES THE WHOLE\n * TOKEN FAMILY — the user is force-logged-out everywhere. This middleware\n * refreshes the cookie BEFORE the RSC tree renders, so pbServer hydrates a\n * still-valid session and never burns the token itself.\n *\n * MODULE-GRAPH RULES (same as ./index.ts): 'next/server' is imported only\n * lazily (the type-only NextRequest/NextResponse imports erase at build\n * time), and middleware.ts must import the generated palbe.gen.ts itself —\n * Next bundles middleware as its OWN module graph; the layout.tsx import\n * does not reach it.\n *\n * ```ts\n * // middleware.ts\n * import './palbe.gen';\n * import { palbeMiddleware } from '@palbase/web/next';\n * import type { NextRequest } from 'next/server';\n *\n * export function middleware(request: NextRequest) {\n * return palbeMiddleware(request);\n * }\n * export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };\n * ```\n */\nimport type { NextRequest, NextResponse } from 'next/server';\nimport type { PalbeConfig } from '../config.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookiesDecoded,\n endpointRefFromApiKey,\n SESSION_COOKIE_ATTRS,\n type StoredSession,\n} from './cookie-codec.js';\nimport { importNextServer, requireGlobalConfig } from './shared.js';\n\nconst DEFAULT_REFRESH_MARGIN_MS = 60_000;\n\nexport interface PalbeMiddlewareOptions {\n /** Refresh when the access token expires within this window. Default 60s. */\n refreshMarginMs?: number;\n /**\n * Pre-built response to decorate (e.g. from chained middleware) instead of\n * building `NextResponse.next({ request })`. Pass-through cases return it\n * UNMODIFIED. Caveat: a pre-built response snapshotted the request headers\n * at ITS creation, so a rotated cookie reaches the browser (Set-Cookie)\n * but not the downstream RSC render of THIS request — prefer omitting this\n * and building your response after palbeMiddleware when possible.\n */\n response?: NextResponse;\n}\n\ntype RefreshOutcome =\n | { kind: 'rotated'; session: StoredSession }\n | { kind: 'terminal' } // 400/401/403 — refresh token dead, sign out\n | { kind: 'transient' }; // network / 5xx / malformed — keep the session\n\n/**\n * Per-process single-flight: concurrent middleware invocations holding the\n * SAME refresh token (parallel tabs / prefetches) share one refresh call —\n * a per-token thundering herd would race palauth rotation. Multi-pod\n * concurrency is covered by palauth's 30s rotation grace (concurrent\n * refreshes of one token all succeed); per-process dedup is all that's\n * needed here (Studio precedent — don't over-engineer).\n */\nconst inflightRefreshes = new Map<string, Promise<RefreshOutcome>>();\n\nfunction refreshSingleFlight(config: PalbeConfig, refreshToken: string): Promise<RefreshOutcome> {\n const existing = inflightRefreshes.get(refreshToken);\n if (existing) return existing;\n const pending = refresh(config, refreshToken).finally(() => {\n inflightRefreshes.delete(refreshToken);\n });\n inflightRefreshes.set(refreshToken, pending);\n return pending;\n}\n\n/** Direct palauth refresh call (no runtime: middleware needs only this one wire). */\nasync function refresh(config: PalbeConfig, refreshToken: string): Promise<RefreshOutcome> {\n try {\n const res = await fetch(`${config.url}/auth/token/refresh`, {\n method: 'POST',\n headers: { apikey: config.apiKey, 'content-type': 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n if (res.status === 400 || res.status === 401 || res.status === 403) {\n return { kind: 'terminal' };\n }\n if (!res.ok) return { kind: 'transient' };\n const raw: unknown = await res.json();\n if (typeof raw === 'object' && raw !== null) {\n const obj = raw as Record<string, unknown>;\n if (\n typeof obj.access_token === 'string' &&\n typeof obj.refresh_token === 'string' &&\n typeof obj.expires_in === 'number'\n ) {\n return {\n kind: 'rotated',\n session: {\n accessToken: obj.access_token,\n refreshToken: obj.refresh_token,\n expiresAt: Date.now() + obj.expires_in * 1000,\n },\n };\n }\n }\n return { kind: 'transient' }; // malformed 200 — never destroy a session over it\n } catch {\n return { kind: 'transient' }; // network failure\n }\n}\n\n/**\n * Keep the session cookie fresh for the request. No session / still-fresh\n * session → untouched pass-through (no cache-control changes). Stale or\n * expired → single-flight refresh, then the rotated (or cleared, on a dead\n * token) cookies are written onto BOTH the request (for the downstream RSC\n * render via `NextResponse.next({ request })`) and the response (for the\n * browser), with `Cache-Control: private, no-store` on touched responses.\n *\n * Cookie writes go through Next's cookie APIs with the DECODED codec values:\n * Next percent-encodes on serialization, so the wire bytes come out exactly\n * as the shared codec's (see encodeSessionCookiesDecoded). NextRequest's\n * cookie store is NOT a CookieStoreLike (its set is the 2-arg/RequestCookie\n * form), hence the direct codec calls here instead of the pbServer adapter.\n */\nexport async function palbeMiddleware(\n request: NextRequest,\n opts?: PalbeMiddlewareOptions,\n): Promise<NextResponse> {\n const config = requireGlobalConfig(\n 'import the generated palbe.gen.ts at the top of middleware.ts — Next bundles middleware as its own module graph, so the layout.tsx import does not reach it.',\n );\n const { NextResponse } = await importNextServer('palbeMiddleware');\n const passThrough = (): NextResponse => opts?.response ?? NextResponse.next({ request });\n\n const ref = endpointRefFromApiKey(config.apiKey);\n const session = decodeSessionCookies((name) => request.cookies.get(name)?.value, ref);\n if (!session) return passThrough();\n if (session.expiresAt - Date.now() > (opts?.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS)) {\n return passThrough();\n }\n\n const outcome = await refreshSingleFlight(config, session.refreshToken);\n if (outcome.kind === 'transient') return passThrough(); // keep the session over blips\n\n // Session-cookie names in the incoming jar — the browser's state mirror.\n const staleNames = clearedSessionCookieNames(ref, (name) => request.cookies.has(name));\n\n if (outcome.kind === 'terminal') {\n for (const name of staleNames) request.cookies.delete(name);\n const response = opts?.response ?? NextResponse.next({ request });\n for (const name of staleNames) {\n response.cookies.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n }\n response.headers.set('cache-control', 'private, no-store');\n return response;\n }\n\n const write = encodeSessionCookiesDecoded(ref, outcome.session);\n for (const name of staleNames) request.cookies.delete(name);\n for (const { name, value } of write.set) request.cookies.set(name, value);\n for (const name of write.clear) request.cookies.delete(name);\n // Build AFTER the request-cookie mutation: NextResponse.next({ request })\n // snapshots the request headers for the downstream RSC render.\n const response = opts?.response ?? NextResponse.next({ request });\n const written = new Set(write.set.map((c) => c.name));\n for (const { name, value } of write.set) {\n response.cookies.set(name, value, SESSION_COOKIE_ATTRS);\n }\n // Browser-side cleanup: stale names not part of the new run (a shrunken /\n // grown chunk layout) + the codec's one-past-the-end overflow guard.\n for (const name of new Set([...write.clear, ...staleNames])) {\n if (!written.has(name)) {\n response.cookies.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n }\n }\n response.headers.set('cache-control', 'private, no-store');\n return response;\n}\n","/**\n * pbServer — the per-request server client of the Next.js adapter.\n *\n * Every call builds a FRESH runtime over the request's cookie store and\n * returns a runtime-BOUND `PB` client: typed namespaces, `call`, `upload`\n * and `auth` all resolve that runtime, never the process-global slot — two\n * bound clients serving concurrent requests cannot leak each other's\n * sessions (see createBoundClient). The store itself is read lazily per\n * adapter operation, so nothing is snapshotted beyond the call.\n *\n * MODULE-GRAPH RULE (same as ./index.ts): 'next/headers' is imported ONLY\n * lazily inside the default-store path — importing this module, and using\n * pbServer with an injected store, must work without next installed.\n */\nimport { BackendError } from '../errors.js';\nimport { buildRuntime, createBoundClient } from '../internal.js';\nimport type { PB } from '../pb.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookiesDecoded,\n endpointRefFromApiKey,\n SESSION_COOKIE_ATTRS,\n} from './cookie-codec.js';\nimport { requireGlobalConfig } from './shared.js';\n\n/**\n * Structural slice of Next's request cookie store (`await cookies()`); any\n * object honoring it works — the seam for tests and non-Next servers.\n * `set`/`delete` are optional because RSC-scope stores are read-only.\n */\nexport interface CookieStoreLike {\n get(name: string): { name: string; value: string } | undefined;\n getAll?(): Array<{ name: string; value: string }>;\n set?(name: string, value: string, options?: Record<string, unknown>): void;\n delete?(name: string): void;\n}\n\nfunction nextRequired(cause: unknown): BackendError {\n const detail = cause instanceof Error && cause.message ? ` (${cause.message})` : '';\n return new BackendError('validation', {\n code: 'next_required',\n message:\n 'pbServer() could not resolve a Next.js request cookie store: call it inside a Server Component, Server Action or Route Handler — or pass a store explicitly via pbServer({ cookies })' +\n `${detail}.`,\n });\n}\n\n/**\n * Next CONTROL-FLOW errors must pass through UNWRAPPED. During static\n * prerender (`next build`) `cookies()` throws DynamicServerError (digest\n * 'DYNAMIC_SERVER_USAGE'), which Next catches upstream to mark the route\n * dynamic — wrapping it into a BackendError would turn that bailout signal\n * into a real prerender error and FAIL THE BUILD of every page calling\n * pbServer (caught live in the P3 Next smoke). 'NEXT_'-prefixed digests are\n * the same sentinel family (redirect / not-found / static-gen bailout).\n * Genuine outside-request-scope errors carry no digest and still map to\n * next_required.\n */\nexport function isNextControlFlowError(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false;\n const digest = (err as { digest?: unknown }).digest;\n return (\n typeof digest === 'string' && (digest === 'DYNAMIC_SERVER_USAGE' || digest.startsWith('NEXT_'))\n );\n}\n\n/** Lazy default store: `await (await import('next/headers')).cookies()`. */\nasync function nextCookieStore(): Promise<CookieStoreLike> {\n let cookies: () => Promise<CookieStoreLike>;\n try {\n ({ cookies } = await import('next/headers'));\n } catch (cause) {\n throw nextRequired(cause); // next not installed / not resolvable\n }\n try {\n return await cookies();\n } catch (cause) {\n if (isNextControlFlowError(cause)) throw cause; // static-prerender bailout etc.\n throw nextRequired(cause); // installed, but outside a request scope\n }\n}\n\n/** Delete one cookie: set-with-maxAge-0 when the store can set (parity with the browser side), else delete, else swallow (read-only RSC store). */\nfunction removeCookie(store: CookieStoreLike, name: string): void {\n try {\n if (store.set) store.set(name, '', { ...SESSION_COOKIE_ATTRS, maxAge: 0 });\n else store.delete?.(name);\n } catch {\n // RSC read-only store — cookie mutation is the middleware's job there\n }\n}\n\n/**\n * SessionStorageAdapter over the request cookie store.\n *\n * SAVE SEMANTICS (decision, documented): only FULL sessions (access token +\n * expiry present) are written; a refresh-only save is SKIPPED with no throw\n * rather than written as {a:'',e:0}. The cookie is SHARED with the browser —\n * degrading it would drop an access token the browser may still be using and\n * force a refresh per page-load. The refresh-only branch is a defensive\n * legacy path anyway: every successful refresh saves a full session.\n *\n * All writes swallow per-cookie failures: in RSC scope Next's store throws\n * on mutation; the request still proceeds with the in-memory session but the\n * rotated cookie is NOT persisted. palbeMiddleware is therefore REQUIRED —\n * not optional — for session-bearing RSC apps: without it every RSC render\n * re-refreshes from the SAME cookie refresh token, and once two such\n * refreshes land more than 30s apart (outside palauth's rotation grace) the\n * reuse detector REVOKES THE WHOLE TOKEN FAMILY — the user is\n * force-logged-out everywhere.\n *\n * Values are written DECODED (encodeSessionCookiesDecoded): Next's real\n * cookies() store percent-encodes on serialization, yielding exactly the\n * shared-codec wire bytes; a raw store (tests, custom servers) persists the\n * bare JSON, which the load path's decode tolerates identically (tokens are\n * %-free base64url, so decodeURIComponent is the identity on them).\n */\nfunction serverCookieAdapter(store: CookieStoreLike, endpointRef: string): SessionStorageAdapter {\n const present = (name: string): boolean => store.get(name) !== undefined;\n return {\n load(): PersistedSession | null {\n const stored = decodeSessionCookies((name) => store.get(name)?.value, endpointRef);\n if (!stored) return null;\n // A browser-side refresh-only write round-trips as a:'' / e:0 —\n // normalize to the legacy shape so hydration takes the expired-trick.\n return stored.accessToken && stored.expiresAt > 0\n ? {\n refreshToken: stored.refreshToken,\n accessToken: stored.accessToken,\n expiresAt: stored.expiresAt,\n }\n : { refreshToken: stored.refreshToken };\n },\n save(session: PersistedSession): void {\n if (!session.accessToken || !session.expiresAt) return; // see SAVE SEMANTICS above\n for (const name of clearedSessionCookieNames(endpointRef, present)) {\n removeCookie(store, name);\n }\n const { set, clear } = encodeSessionCookiesDecoded(endpointRef, {\n accessToken: session.accessToken,\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt,\n });\n for (const { name, value } of set) {\n try {\n store.set?.(name, value, { ...SESSION_COOKIE_ATTRS });\n } catch {\n // RSC read-only store\n }\n }\n // Overflow guard (codec contract): delete one-past-the-end so a stale\n // orphan chunk behind a gap can never join a future chunk run.\n for (const name of clear) removeCookie(store, name);\n },\n clear(): void {\n for (const name of clearedSessionCookieNames(endpointRef, present)) {\n removeCookie(store, name);\n }\n },\n };\n}\n\n/**\n * Build a per-request `PB` client over the request's cookies. Reads the\n * session the browser SDK wrote (shared cookie codec), refreshes pre-flight\n * when expired, and — where the store is writable (Server Actions, Route\n * Handlers) — persists the rotated session back.\n *\n * Each call builds a fresh runtime (a handful of small objects, no I/O) —\n * cheap enough to call per data access; code paths invoking it many times\n * within one request MAY memoize per-request (e.g. React's `cache()`), but\n * don't have to.\n *\n * ```ts\n * const pb = await pbServer();\n * const todos = await pb.todos.list();\n * ```\n */\nexport async function pbServer(opts?: { cookies?: CookieStoreLike }): Promise<PB> {\n const config = requireGlobalConfig(\n 'import the generated palbe.gen.ts once in app/layout.tsx — the root-layout import configures Server Components and Route Handlers too.',\n );\n const store = opts?.cookies ?? (await nextCookieStore());\n const ref = endpointRefFromApiKey(config.apiKey);\n const rt = buildRuntime({ ...config, storage: serverCookieAdapter(store, ref) });\n return createBoundClient(rt);\n}\n"],"mappings":"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","names":["response","response"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { s as PalbeAuth, v as PalbeFlags, y as PalbeRealtime, P as PalbeAnalytics, t as PalbeCalls, w as PalbeMessaging, J as PalbeRuntime } from './analytics-facade-CAKBIH_U.cjs';
|
|
2
2
|
|
|
3
3
|
interface CallOptions {
|
|
4
4
|
headers?: Record<string, string>;
|
|
@@ -40,6 +40,8 @@ interface PB {
|
|
|
40
40
|
readonly flags: PalbeFlags;
|
|
41
41
|
readonly realtime: PalbeRealtime;
|
|
42
42
|
readonly analytics: PalbeAnalytics;
|
|
43
|
+
readonly calls: PalbeCalls;
|
|
44
|
+
readonly messaging: PalbeMessaging;
|
|
43
45
|
}
|
|
44
46
|
declare const pb: PB;
|
|
45
47
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { s as PalbeAuth, v as PalbeFlags, y as PalbeRealtime, P as PalbeAnalytics, t as PalbeCalls, w as PalbeMessaging, J as PalbeRuntime } from './analytics-facade-DLH-KivI.js';
|
|
2
2
|
|
|
3
3
|
interface CallOptions {
|
|
4
4
|
headers?: Record<string, string>;
|
|
@@ -40,6 +40,8 @@ interface PB {
|
|
|
40
40
|
readonly flags: PalbeFlags;
|
|
41
41
|
readonly realtime: PalbeRealtime;
|
|
42
42
|
readonly analytics: PalbeAnalytics;
|
|
43
|
+
readonly calls: PalbeCalls;
|
|
44
|
+
readonly messaging: PalbeMessaging;
|
|
43
45
|
}
|
|
44
46
|
declare const pb: PB;
|
|
45
47
|
/**
|
|
Binary file
|
package/dist/react/index.cjs
CHANGED
|
@@ -22,9 +22,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var react_exports = {};
|
|
23
23
|
__export(react_exports, {
|
|
24
24
|
useChannel: () => useChannel,
|
|
25
|
+
useChat: () => useChat,
|
|
26
|
+
useChatMembers: () => useChatMembers,
|
|
27
|
+
useChats: () => useChats,
|
|
25
28
|
useFlag: () => useFlag,
|
|
26
29
|
useFlags: () => useFlags,
|
|
30
|
+
useMessages: () => useMessages,
|
|
27
31
|
useSession: () => useSession,
|
|
32
|
+
useTyping: () => useTyping,
|
|
28
33
|
useUser: () => useUser
|
|
29
34
|
});
|
|
30
35
|
module.exports = __toCommonJS(react_exports);
|
|
@@ -292,7 +297,7 @@ function getNamespace(name) {
|
|
|
292
297
|
}
|
|
293
298
|
|
|
294
299
|
// src/version.ts
|
|
295
|
-
var VERSION = "1.
|
|
300
|
+
var VERSION = "1.1.1";
|
|
296
301
|
|
|
297
302
|
// src/call.ts
|
|
298
303
|
async function callEndpoint(resolveRt, name, input, options) {
|
|
@@ -465,6 +470,12 @@ function createClientProxy(resolveRt, nsAccessor) {
|
|
|
465
470
|
},
|
|
466
471
|
get analytics() {
|
|
467
472
|
return resolveRt().analytics;
|
|
473
|
+
},
|
|
474
|
+
get calls() {
|
|
475
|
+
return resolveRt().calls;
|
|
476
|
+
},
|
|
477
|
+
get messaging() {
|
|
478
|
+
return resolveRt().messaging;
|
|
468
479
|
}
|
|
469
480
|
};
|
|
470
481
|
return new Proxy(base, {
|
|
@@ -638,12 +649,97 @@ function useChannel(name, event, handler) {
|
|
|
638
649
|
}, [name, event, configEpoch]);
|
|
639
650
|
return { status };
|
|
640
651
|
}
|
|
652
|
+
var EMPTY_CHATS = Object.freeze([]);
|
|
653
|
+
var EMPTY_MESSAGES = Object.freeze([]);
|
|
654
|
+
var EMPTY_MEMBERS = Object.freeze([]);
|
|
655
|
+
var EMPTY_TYPING = Object.freeze([]);
|
|
656
|
+
function useChats() {
|
|
657
|
+
const lastRef = (0, import_react.useRef)(EMPTY_CHATS);
|
|
658
|
+
const getSnapshot = (0, import_react.useCallback)(() => {
|
|
659
|
+
try {
|
|
660
|
+
const next = pb.messaging.chats;
|
|
661
|
+
if (next.length !== lastRef.current.length || next.some((c, i) => c !== lastRef.current[i])) {
|
|
662
|
+
lastRef.current = next.slice();
|
|
663
|
+
}
|
|
664
|
+
return lastRef.current;
|
|
665
|
+
} catch {
|
|
666
|
+
return EMPTY_CHATS;
|
|
667
|
+
}
|
|
668
|
+
}, []);
|
|
669
|
+
return (0, import_react.useSyncExternalStore)(subscribeChatsWithConfig, getSnapshot, getEmptyChats);
|
|
670
|
+
}
|
|
671
|
+
var subscribeChatsWithConfig = subscribeWithConfig(
|
|
672
|
+
(onStoreChange) => pb.messaging.onChatsChange(onStoreChange)
|
|
673
|
+
);
|
|
674
|
+
function getEmptyChats() {
|
|
675
|
+
return EMPTY_CHATS;
|
|
676
|
+
}
|
|
677
|
+
function useChat(chat) {
|
|
678
|
+
const subscribe = (0, import_react.useCallback)(
|
|
679
|
+
(onStoreChange) => chat.onChange(onStoreChange),
|
|
680
|
+
[chat]
|
|
681
|
+
);
|
|
682
|
+
const getSnapshot = (0, import_react.useCallback)(() => chat, [chat]);
|
|
683
|
+
const [, force] = (0, import_react.useState)(0);
|
|
684
|
+
(0, import_react.useEffect)(() => chat.onChange(() => force((n) => n + 1)), [chat]);
|
|
685
|
+
return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
686
|
+
}
|
|
687
|
+
function useMessages(chat) {
|
|
688
|
+
const lastRef = (0, import_react.useRef)(EMPTY_MESSAGES);
|
|
689
|
+
const subscribe = (0, import_react.useCallback)(
|
|
690
|
+
(onStoreChange) => chat.onChange(onStoreChange),
|
|
691
|
+
[chat]
|
|
692
|
+
);
|
|
693
|
+
const getSnapshot = (0, import_react.useCallback)(() => {
|
|
694
|
+
const next = chat.messages;
|
|
695
|
+
if (next.length !== lastRef.current.length || next.some((m, i) => m !== lastRef.current[i])) {
|
|
696
|
+
lastRef.current = next.slice();
|
|
697
|
+
}
|
|
698
|
+
return lastRef.current;
|
|
699
|
+
}, [chat]);
|
|
700
|
+
return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, () => EMPTY_MESSAGES);
|
|
701
|
+
}
|
|
702
|
+
function useChatMembers(chat) {
|
|
703
|
+
const lastRef = (0, import_react.useRef)(EMPTY_MEMBERS);
|
|
704
|
+
const subscribe = (0, import_react.useCallback)(
|
|
705
|
+
(onStoreChange) => chat.onChange(onStoreChange),
|
|
706
|
+
[chat]
|
|
707
|
+
);
|
|
708
|
+
const getSnapshot = (0, import_react.useCallback)(() => {
|
|
709
|
+
const next = chat.members;
|
|
710
|
+
if (next.length !== lastRef.current.length || next.some((m, i) => m !== lastRef.current[i])) {
|
|
711
|
+
lastRef.current = next.slice();
|
|
712
|
+
}
|
|
713
|
+
return lastRef.current;
|
|
714
|
+
}, [chat]);
|
|
715
|
+
return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, () => EMPTY_MEMBERS);
|
|
716
|
+
}
|
|
717
|
+
function useTyping(chat) {
|
|
718
|
+
const lastRef = (0, import_react.useRef)(EMPTY_TYPING);
|
|
719
|
+
const subscribe = (0, import_react.useCallback)(
|
|
720
|
+
(onStoreChange) => chat.onChange(onStoreChange),
|
|
721
|
+
[chat]
|
|
722
|
+
);
|
|
723
|
+
const getSnapshot = (0, import_react.useCallback)(() => {
|
|
724
|
+
const next = chat.typing;
|
|
725
|
+
if (next.length !== lastRef.current.length || next.some((m, i) => m !== lastRef.current[i])) {
|
|
726
|
+
lastRef.current = next.slice();
|
|
727
|
+
}
|
|
728
|
+
return lastRef.current;
|
|
729
|
+
}, [chat]);
|
|
730
|
+
return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, () => EMPTY_TYPING);
|
|
731
|
+
}
|
|
641
732
|
// Annotate the CommonJS export names for ESM import in node:
|
|
642
733
|
0 && (module.exports = {
|
|
643
734
|
useChannel,
|
|
735
|
+
useChat,
|
|
736
|
+
useChatMembers,
|
|
737
|
+
useChats,
|
|
644
738
|
useFlag,
|
|
645
739
|
useFlags,
|
|
740
|
+
useMessages,
|
|
646
741
|
useSession,
|
|
742
|
+
useTyping,
|
|
647
743
|
useUser
|
|
648
744
|
});
|
|
649
745
|
//# sourceMappingURL=index.cjs.map
|