@palbase/web 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/analytics-facade-DkOwkEpi.d.ts +454 -0
- package/dist/analytics-facade-t6UrFdn7.d.cts +454 -0
- package/dist/chunk-JVT65V4E.js +3384 -0
- package/dist/chunk-JVT65V4E.js.map +1 -0
- package/dist/chunk-VJXFABBW.js +94 -0
- package/dist/chunk-VJXFABBW.js.map +1 -0
- package/dist/errors-fDoNdTrJ.d.cts +35 -0
- package/dist/errors-fDoNdTrJ.d.ts +35 -0
- package/dist/index.cjs +2394 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.cjs +3403 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +49 -0
- package/dist/internal.d.ts +49 -0
- package/dist/internal.js +19 -0
- package/dist/internal.js.map +1 -0
- package/dist/next/client.cjs +3131 -0
- package/dist/next/client.cjs.map +1 -0
- package/dist/next/client.d.cts +19 -0
- package/dist/next/client.d.ts +19 -0
- package/dist/next/client.js +75 -0
- package/dist/next/client.js.map +1 -0
- package/dist/next/index.cjs +3680 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +238 -0
- package/dist/next/index.d.ts +238 -0
- package/dist/next/index.js +301 -0
- package/dist/next/index.js.map +1 -0
- package/dist/pb-BmgkAe97.d.ts +54 -0
- package/dist/pb-Cudze7Kb.d.cts +54 -0
- package/dist/react/index.cjs +649 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +86 -0
- package/dist/react/index.d.ts +86 -0
- package/dist/react/index.js +156 -0
- package/dist/react/index.js.map +1 -0
- package/dist/storage-BPaeSG8K.d.cts +21 -0
- package/dist/storage-BPaeSG8K.d.ts +21 -0
- package/package.json +123 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { P as PB } from '../pb-Cudze7Kb.cjs';
|
|
3
|
+
import '../analytics-facade-t6UrFdn7.cjs';
|
|
4
|
+
import '@palbase/core';
|
|
5
|
+
import '@palbase/auth';
|
|
6
|
+
import '../storage-BPaeSG8K.cjs';
|
|
7
|
+
import '@palbase/flags';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* handleAuthCallback — the OAuth completion route handler factory.
|
|
11
|
+
*
|
|
12
|
+
* The app's redirect_uri (registered when calling signInWithOAuth with
|
|
13
|
+
* `redirectTo: '<app>/auth/callback?provider=google&next=/dash'`) lands
|
|
14
|
+
* here with `?code&state` from the provider. The handler exchanges them
|
|
15
|
+
* SERVER-SIDE against palauth (PKCE verifier + state live server-side; the
|
|
16
|
+
* browser never holds them), writes the session cookies onto a redirect
|
|
17
|
+
* response, and sends the user on.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* // app/auth/callback/route.ts
|
|
21
|
+
* import { handleAuthCallback } from '@palbase/web/next';
|
|
22
|
+
* export const GET = handleAuthCallback({ defaultNext: '/' });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* MODULE-GRAPH RULE (same as ./index.ts): 'next/server' only lazily; the
|
|
26
|
+
* type-only NextRequest import erases at build time.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
interface HandleAuthCallbackOptions {
|
|
30
|
+
/** Where to land when the callback carries no (valid) `next` param — and on every error/MFA redirect. Default '/'. */
|
|
31
|
+
defaultNext?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the callback route handler. Outcomes (all 307 redirects, all with
|
|
35
|
+
* `Cache-Control: private, no-store` — auth outcomes must never cache):
|
|
36
|
+
* - provider `error` param → `?auth_error=<error>` (+ `auth_error_description`)
|
|
37
|
+
* - missing code/state/provider → `?auth_error=invalid_callback`
|
|
38
|
+
* - exchanged AuthResult → session cookies on the redirect to `next ?? defaultNext ?? '/'`
|
|
39
|
+
* - MFA union → `?mfa_token=…&mfa_factors=a,b` on defaultNext, NO cookies
|
|
40
|
+
* - exchange failure → `?auth_error=<wire code | oauth_exchange_failed>`
|
|
41
|
+
*/
|
|
42
|
+
declare function handleAuthCallback(opts?: HandleAuthCallbackOptions): (request: NextRequest) => Promise<Response>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* `pb_<endpointRef>_<scope><random>` → endpointRef ('' if malformed).
|
|
46
|
+
* Single source of truth — used by runtime storage scoping AND the
|
|
47
|
+
* palbe/next cookie naming; the two MUST agree or browser and server
|
|
48
|
+
* would read different cookies.
|
|
49
|
+
*/
|
|
50
|
+
declare function endpointRefFromApiKey(apiKey: string): string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Session-cookie codec shared by EVERY palbe/next surface (browser storage
|
|
54
|
+
* adapter, pbServer cookie adapter, middleware, auth callback). Pure
|
|
55
|
+
* functions — no 'next' imports, no palbe runtime imports — so both sides
|
|
56
|
+
* provably read and write the SAME bytes.
|
|
57
|
+
*
|
|
58
|
+
* Format (P3 design contract):
|
|
59
|
+
* - name: `palbe-session-<endpointRef>`; overflow chunk names append `.0`,
|
|
60
|
+
* `.1`, … (cookie names otherwise avoid dots — tooling friendliness).
|
|
61
|
+
* - value: `encodeURIComponent(JSON.stringify({ a, r, e }))` with a=access
|
|
62
|
+
* token, r=refresh token, e=expiry (ms epoch).
|
|
63
|
+
* - one cookie when the encoded value fits MAX_COOKIE_VALUE chars, else
|
|
64
|
+
* chunks ONLY (the base name must not coexist with chunks — callers clear
|
|
65
|
+
* stale names via clearedSessionCookieNames before writing, then honor the
|
|
66
|
+
* encode result's `clear` overflow guard after writing).
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
interface StoredSession {
|
|
70
|
+
accessToken: string;
|
|
71
|
+
refreshToken: string;
|
|
72
|
+
/** ms epoch */
|
|
73
|
+
expiresAt: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The P3 cookie contract attributes, shared by every server-side writer
|
|
77
|
+
* (pbServer adapter, middleware, callback); the browser writer mirrors them
|
|
78
|
+
* in document.cookie string form (next/client.ts). NOT HttpOnly by design —
|
|
79
|
+
* the browser SDK must read/write the session (Supabase-paradigm tradeoff);
|
|
80
|
+
* Max-Age = 30d = the refresh-token TTL.
|
|
81
|
+
*/
|
|
82
|
+
declare const SESSION_COOKIE_ATTRS: {
|
|
83
|
+
readonly path: "/";
|
|
84
|
+
readonly sameSite: "lax";
|
|
85
|
+
readonly secure: true;
|
|
86
|
+
readonly maxAge: 2592000;
|
|
87
|
+
};
|
|
88
|
+
declare function sessionCookieName(endpointRef: string): string;
|
|
89
|
+
interface SessionCookieWrite {
|
|
90
|
+
/** Cookies to SET, in order. */
|
|
91
|
+
set: Array<{
|
|
92
|
+
name: string;
|
|
93
|
+
value: string;
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Names to DELETE alongside the set: the one-past-the-end OVERFLOW GUARD
|
|
97
|
+
* (base write → `.0`; chunks `.0..n` → `.(n+1)`). Decode stops at the
|
|
98
|
+
* first missing chunk, so deleting exactly one-past-the-end guarantees a
|
|
99
|
+
* stale orphan chunk (left behind a gap, where presence-probing cleanup
|
|
100
|
+
* cannot see it) can never concatenate into the freshly written run.
|
|
101
|
+
* Writers MUST honor both lists.
|
|
102
|
+
*/
|
|
103
|
+
clear: string[];
|
|
104
|
+
}
|
|
105
|
+
declare function encodeSessionCookies(endpointRef: string, session: StoredSession): SessionCookieWrite;
|
|
106
|
+
/**
|
|
107
|
+
* encodeSessionCookies for percent-ENCODING cookie stores. Next's request
|
|
108
|
+
* and response cookie APIs apply encodeURIComponent to values when
|
|
109
|
+
* serializing (and decodeURIComponent on read) — handing them the codec's
|
|
110
|
+
* already-encoded values would DOUBLE-encode the wire bytes and break the
|
|
111
|
+
* browser-side reader. This variant returns the same cookie run with
|
|
112
|
+
* DECODED values: `encodeURIComponent(value)` reproduces the
|
|
113
|
+
* encodeSessionCookies bytes exactly (chunk boundaries never split an
|
|
114
|
+
* escape), so an encoding store writes byte-identical cookies to a raw
|
|
115
|
+
* writer like document.cookie.
|
|
116
|
+
*/
|
|
117
|
+
declare function encodeSessionCookiesDecoded(endpointRef: string, session: StoredSession): SessionCookieWrite;
|
|
118
|
+
/**
|
|
119
|
+
* Read the session back from a cookie getter: the base name wins; otherwise
|
|
120
|
+
* `.0`, `.1`, … are concatenated until the first missing chunk. Tolerant:
|
|
121
|
+
* malformed URI escapes / JSON / shape → null (treated as "no session").
|
|
122
|
+
*
|
|
123
|
+
* Accepts BOTH value forms. Next's cookie stores percent-DECODE values once
|
|
124
|
+
* on read, so server-side callers (pbServer adapter, middleware) hand us the
|
|
125
|
+
* already-decoded JSON; raw readers (document.cookie) hand us the encoded
|
|
126
|
+
* form. The raw (already-decoded) parse runs FIRST because it is
|
|
127
|
+
* unambiguous — an encoded value can never raw-parse as JSON
|
|
128
|
+
* (encodeURIComponent escapes `{` itself, so it starts with `%7B`) — whereas
|
|
129
|
+
* decode-first would silently corrupt a literal `%XX` run inside a token
|
|
130
|
+
* (decodeURIComponent('%41') → 'A', no throw) or throw on an invalid escape.
|
|
131
|
+
*/
|
|
132
|
+
declare function decodeSessionCookies(get: (name: string) => string | undefined, endpointRef: string): StoredSession | null;
|
|
133
|
+
/**
|
|
134
|
+
* Names a writer must delete before/while writing: the base name (when
|
|
135
|
+
* present) plus every existing `.N` chunk, probed in order until the first
|
|
136
|
+
* missing one (writes are always a contiguous `.0..n` run, so a gap means
|
|
137
|
+
* the end of palbe-owned chunks).
|
|
138
|
+
*/
|
|
139
|
+
declare function clearedSessionCookieNames(endpointRef: string, present: (name: string) => boolean): string[];
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* palbeMiddleware — proactive cookie-session refresh in Next.js middleware.
|
|
143
|
+
*
|
|
144
|
+
* REQUIRED — not optional — for session-bearing RSC apps. Server Components
|
|
145
|
+
* cannot write cookies, so pbServer's reactive refresh cannot PERSIST a
|
|
146
|
+
* rotated session there: every RSC render would re-refresh from the SAME
|
|
147
|
+
* cookie refresh token, and once two of those land more than 30s apart
|
|
148
|
+
* (outside palauth's rotation grace) the reuse detector REVOKES THE WHOLE
|
|
149
|
+
* TOKEN FAMILY — the user is force-logged-out everywhere. This middleware
|
|
150
|
+
* refreshes the cookie BEFORE the RSC tree renders, so pbServer hydrates a
|
|
151
|
+
* still-valid session and never burns the token itself.
|
|
152
|
+
*
|
|
153
|
+
* MODULE-GRAPH RULES (same as ./index.ts): 'next/server' is imported only
|
|
154
|
+
* lazily (the type-only NextRequest/NextResponse imports erase at build
|
|
155
|
+
* time), and middleware.ts must import the generated palbe.gen.ts itself —
|
|
156
|
+
* Next bundles middleware as its OWN module graph; the layout.tsx import
|
|
157
|
+
* does not reach it.
|
|
158
|
+
*
|
|
159
|
+
* ```ts
|
|
160
|
+
* // middleware.ts
|
|
161
|
+
* import './palbe.gen';
|
|
162
|
+
* import { palbeMiddleware } from '@palbase/web/next';
|
|
163
|
+
* import type { NextRequest } from 'next/server';
|
|
164
|
+
*
|
|
165
|
+
* export function middleware(request: NextRequest) {
|
|
166
|
+
* return palbeMiddleware(request);
|
|
167
|
+
* }
|
|
168
|
+
* export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
interface PalbeMiddlewareOptions {
|
|
173
|
+
/** Refresh when the access token expires within this window. Default 60s. */
|
|
174
|
+
refreshMarginMs?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Pre-built response to decorate (e.g. from chained middleware) instead of
|
|
177
|
+
* building `NextResponse.next({ request })`. Pass-through cases return it
|
|
178
|
+
* UNMODIFIED. Caveat: a pre-built response snapshotted the request headers
|
|
179
|
+
* at ITS creation, so a rotated cookie reaches the browser (Set-Cookie)
|
|
180
|
+
* but not the downstream RSC render of THIS request — prefer omitting this
|
|
181
|
+
* and building your response after palbeMiddleware when possible.
|
|
182
|
+
*/
|
|
183
|
+
response?: NextResponse;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Keep the session cookie fresh for the request. No session / still-fresh
|
|
187
|
+
* session → untouched pass-through (no cache-control changes). Stale or
|
|
188
|
+
* expired → single-flight refresh, then the rotated (or cleared, on a dead
|
|
189
|
+
* token) cookies are written onto BOTH the request (for the downstream RSC
|
|
190
|
+
* render via `NextResponse.next({ request })`) and the response (for the
|
|
191
|
+
* browser), with `Cache-Control: private, no-store` on touched responses.
|
|
192
|
+
*
|
|
193
|
+
* Cookie writes go through Next's cookie APIs with the DECODED codec values:
|
|
194
|
+
* Next percent-encodes on serialization, so the wire bytes come out exactly
|
|
195
|
+
* as the shared codec's (see encodeSessionCookiesDecoded). NextRequest's
|
|
196
|
+
* cookie store is NOT a CookieStoreLike (its set is the 2-arg/RequestCookie
|
|
197
|
+
* form), hence the direct codec calls here instead of the pbServer adapter.
|
|
198
|
+
*/
|
|
199
|
+
declare function palbeMiddleware(request: NextRequest, opts?: PalbeMiddlewareOptions): Promise<NextResponse>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Structural slice of Next's request cookie store (`await cookies()`); any
|
|
203
|
+
* object honoring it works — the seam for tests and non-Next servers.
|
|
204
|
+
* `set`/`delete` are optional because RSC-scope stores are read-only.
|
|
205
|
+
*/
|
|
206
|
+
interface CookieStoreLike {
|
|
207
|
+
get(name: string): {
|
|
208
|
+
name: string;
|
|
209
|
+
value: string;
|
|
210
|
+
} | undefined;
|
|
211
|
+
getAll?(): Array<{
|
|
212
|
+
name: string;
|
|
213
|
+
value: string;
|
|
214
|
+
}>;
|
|
215
|
+
set?(name: string, value: string, options?: Record<string, unknown>): void;
|
|
216
|
+
delete?(name: string): void;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Build a per-request `PB` client over the request's cookies. Reads the
|
|
220
|
+
* session the browser SDK wrote (shared cookie codec), refreshes pre-flight
|
|
221
|
+
* when expired, and — where the store is writable (Server Actions, Route
|
|
222
|
+
* Handlers) — persists the rotated session back.
|
|
223
|
+
*
|
|
224
|
+
* Each call builds a fresh runtime (a handful of small objects, no I/O) —
|
|
225
|
+
* cheap enough to call per data access; code paths invoking it many times
|
|
226
|
+
* within one request MAY memoize per-request (e.g. React's `cache()`), but
|
|
227
|
+
* don't have to.
|
|
228
|
+
*
|
|
229
|
+
* ```ts
|
|
230
|
+
* const pb = await pbServer();
|
|
231
|
+
* const todos = await pb.todos.list();
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
declare function pbServer(opts?: {
|
|
235
|
+
cookies?: CookieStoreLike;
|
|
236
|
+
}): Promise<PB>;
|
|
237
|
+
|
|
238
|
+
export { type CookieStoreLike, type HandleAuthCallbackOptions, type PalbeMiddlewareOptions, SESSION_COOKIE_ATTRS, type SessionCookieWrite, type StoredSession, clearedSessionCookieNames, decodeSessionCookies, encodeSessionCookies, encodeSessionCookiesDecoded, endpointRefFromApiKey, handleAuthCallback, palbeMiddleware, pbServer, sessionCookieName };
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { P as PB } from '../pb-BmgkAe97.js';
|
|
3
|
+
import '../analytics-facade-DkOwkEpi.js';
|
|
4
|
+
import '@palbase/core';
|
|
5
|
+
import '@palbase/auth';
|
|
6
|
+
import '../storage-BPaeSG8K.js';
|
|
7
|
+
import '@palbase/flags';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* handleAuthCallback — the OAuth completion route handler factory.
|
|
11
|
+
*
|
|
12
|
+
* The app's redirect_uri (registered when calling signInWithOAuth with
|
|
13
|
+
* `redirectTo: '<app>/auth/callback?provider=google&next=/dash'`) lands
|
|
14
|
+
* here with `?code&state` from the provider. The handler exchanges them
|
|
15
|
+
* SERVER-SIDE against palauth (PKCE verifier + state live server-side; the
|
|
16
|
+
* browser never holds them), writes the session cookies onto a redirect
|
|
17
|
+
* response, and sends the user on.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* // app/auth/callback/route.ts
|
|
21
|
+
* import { handleAuthCallback } from '@palbase/web/next';
|
|
22
|
+
* export const GET = handleAuthCallback({ defaultNext: '/' });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* MODULE-GRAPH RULE (same as ./index.ts): 'next/server' only lazily; the
|
|
26
|
+
* type-only NextRequest import erases at build time.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
interface HandleAuthCallbackOptions {
|
|
30
|
+
/** Where to land when the callback carries no (valid) `next` param — and on every error/MFA redirect. Default '/'. */
|
|
31
|
+
defaultNext?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the callback route handler. Outcomes (all 307 redirects, all with
|
|
35
|
+
* `Cache-Control: private, no-store` — auth outcomes must never cache):
|
|
36
|
+
* - provider `error` param → `?auth_error=<error>` (+ `auth_error_description`)
|
|
37
|
+
* - missing code/state/provider → `?auth_error=invalid_callback`
|
|
38
|
+
* - exchanged AuthResult → session cookies on the redirect to `next ?? defaultNext ?? '/'`
|
|
39
|
+
* - MFA union → `?mfa_token=…&mfa_factors=a,b` on defaultNext, NO cookies
|
|
40
|
+
* - exchange failure → `?auth_error=<wire code | oauth_exchange_failed>`
|
|
41
|
+
*/
|
|
42
|
+
declare function handleAuthCallback(opts?: HandleAuthCallbackOptions): (request: NextRequest) => Promise<Response>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* `pb_<endpointRef>_<scope><random>` → endpointRef ('' if malformed).
|
|
46
|
+
* Single source of truth — used by runtime storage scoping AND the
|
|
47
|
+
* palbe/next cookie naming; the two MUST agree or browser and server
|
|
48
|
+
* would read different cookies.
|
|
49
|
+
*/
|
|
50
|
+
declare function endpointRefFromApiKey(apiKey: string): string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Session-cookie codec shared by EVERY palbe/next surface (browser storage
|
|
54
|
+
* adapter, pbServer cookie adapter, middleware, auth callback). Pure
|
|
55
|
+
* functions — no 'next' imports, no palbe runtime imports — so both sides
|
|
56
|
+
* provably read and write the SAME bytes.
|
|
57
|
+
*
|
|
58
|
+
* Format (P3 design contract):
|
|
59
|
+
* - name: `palbe-session-<endpointRef>`; overflow chunk names append `.0`,
|
|
60
|
+
* `.1`, … (cookie names otherwise avoid dots — tooling friendliness).
|
|
61
|
+
* - value: `encodeURIComponent(JSON.stringify({ a, r, e }))` with a=access
|
|
62
|
+
* token, r=refresh token, e=expiry (ms epoch).
|
|
63
|
+
* - one cookie when the encoded value fits MAX_COOKIE_VALUE chars, else
|
|
64
|
+
* chunks ONLY (the base name must not coexist with chunks — callers clear
|
|
65
|
+
* stale names via clearedSessionCookieNames before writing, then honor the
|
|
66
|
+
* encode result's `clear` overflow guard after writing).
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
interface StoredSession {
|
|
70
|
+
accessToken: string;
|
|
71
|
+
refreshToken: string;
|
|
72
|
+
/** ms epoch */
|
|
73
|
+
expiresAt: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The P3 cookie contract attributes, shared by every server-side writer
|
|
77
|
+
* (pbServer adapter, middleware, callback); the browser writer mirrors them
|
|
78
|
+
* in document.cookie string form (next/client.ts). NOT HttpOnly by design —
|
|
79
|
+
* the browser SDK must read/write the session (Supabase-paradigm tradeoff);
|
|
80
|
+
* Max-Age = 30d = the refresh-token TTL.
|
|
81
|
+
*/
|
|
82
|
+
declare const SESSION_COOKIE_ATTRS: {
|
|
83
|
+
readonly path: "/";
|
|
84
|
+
readonly sameSite: "lax";
|
|
85
|
+
readonly secure: true;
|
|
86
|
+
readonly maxAge: 2592000;
|
|
87
|
+
};
|
|
88
|
+
declare function sessionCookieName(endpointRef: string): string;
|
|
89
|
+
interface SessionCookieWrite {
|
|
90
|
+
/** Cookies to SET, in order. */
|
|
91
|
+
set: Array<{
|
|
92
|
+
name: string;
|
|
93
|
+
value: string;
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Names to DELETE alongside the set: the one-past-the-end OVERFLOW GUARD
|
|
97
|
+
* (base write → `.0`; chunks `.0..n` → `.(n+1)`). Decode stops at the
|
|
98
|
+
* first missing chunk, so deleting exactly one-past-the-end guarantees a
|
|
99
|
+
* stale orphan chunk (left behind a gap, where presence-probing cleanup
|
|
100
|
+
* cannot see it) can never concatenate into the freshly written run.
|
|
101
|
+
* Writers MUST honor both lists.
|
|
102
|
+
*/
|
|
103
|
+
clear: string[];
|
|
104
|
+
}
|
|
105
|
+
declare function encodeSessionCookies(endpointRef: string, session: StoredSession): SessionCookieWrite;
|
|
106
|
+
/**
|
|
107
|
+
* encodeSessionCookies for percent-ENCODING cookie stores. Next's request
|
|
108
|
+
* and response cookie APIs apply encodeURIComponent to values when
|
|
109
|
+
* serializing (and decodeURIComponent on read) — handing them the codec's
|
|
110
|
+
* already-encoded values would DOUBLE-encode the wire bytes and break the
|
|
111
|
+
* browser-side reader. This variant returns the same cookie run with
|
|
112
|
+
* DECODED values: `encodeURIComponent(value)` reproduces the
|
|
113
|
+
* encodeSessionCookies bytes exactly (chunk boundaries never split an
|
|
114
|
+
* escape), so an encoding store writes byte-identical cookies to a raw
|
|
115
|
+
* writer like document.cookie.
|
|
116
|
+
*/
|
|
117
|
+
declare function encodeSessionCookiesDecoded(endpointRef: string, session: StoredSession): SessionCookieWrite;
|
|
118
|
+
/**
|
|
119
|
+
* Read the session back from a cookie getter: the base name wins; otherwise
|
|
120
|
+
* `.0`, `.1`, … are concatenated until the first missing chunk. Tolerant:
|
|
121
|
+
* malformed URI escapes / JSON / shape → null (treated as "no session").
|
|
122
|
+
*
|
|
123
|
+
* Accepts BOTH value forms. Next's cookie stores percent-DECODE values once
|
|
124
|
+
* on read, so server-side callers (pbServer adapter, middleware) hand us the
|
|
125
|
+
* already-decoded JSON; raw readers (document.cookie) hand us the encoded
|
|
126
|
+
* form. The raw (already-decoded) parse runs FIRST because it is
|
|
127
|
+
* unambiguous — an encoded value can never raw-parse as JSON
|
|
128
|
+
* (encodeURIComponent escapes `{` itself, so it starts with `%7B`) — whereas
|
|
129
|
+
* decode-first would silently corrupt a literal `%XX` run inside a token
|
|
130
|
+
* (decodeURIComponent('%41') → 'A', no throw) or throw on an invalid escape.
|
|
131
|
+
*/
|
|
132
|
+
declare function decodeSessionCookies(get: (name: string) => string | undefined, endpointRef: string): StoredSession | null;
|
|
133
|
+
/**
|
|
134
|
+
* Names a writer must delete before/while writing: the base name (when
|
|
135
|
+
* present) plus every existing `.N` chunk, probed in order until the first
|
|
136
|
+
* missing one (writes are always a contiguous `.0..n` run, so a gap means
|
|
137
|
+
* the end of palbe-owned chunks).
|
|
138
|
+
*/
|
|
139
|
+
declare function clearedSessionCookieNames(endpointRef: string, present: (name: string) => boolean): string[];
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* palbeMiddleware — proactive cookie-session refresh in Next.js middleware.
|
|
143
|
+
*
|
|
144
|
+
* REQUIRED — not optional — for session-bearing RSC apps. Server Components
|
|
145
|
+
* cannot write cookies, so pbServer's reactive refresh cannot PERSIST a
|
|
146
|
+
* rotated session there: every RSC render would re-refresh from the SAME
|
|
147
|
+
* cookie refresh token, and once two of those land more than 30s apart
|
|
148
|
+
* (outside palauth's rotation grace) the reuse detector REVOKES THE WHOLE
|
|
149
|
+
* TOKEN FAMILY — the user is force-logged-out everywhere. This middleware
|
|
150
|
+
* refreshes the cookie BEFORE the RSC tree renders, so pbServer hydrates a
|
|
151
|
+
* still-valid session and never burns the token itself.
|
|
152
|
+
*
|
|
153
|
+
* MODULE-GRAPH RULES (same as ./index.ts): 'next/server' is imported only
|
|
154
|
+
* lazily (the type-only NextRequest/NextResponse imports erase at build
|
|
155
|
+
* time), and middleware.ts must import the generated palbe.gen.ts itself —
|
|
156
|
+
* Next bundles middleware as its OWN module graph; the layout.tsx import
|
|
157
|
+
* does not reach it.
|
|
158
|
+
*
|
|
159
|
+
* ```ts
|
|
160
|
+
* // middleware.ts
|
|
161
|
+
* import './palbe.gen';
|
|
162
|
+
* import { palbeMiddleware } from '@palbase/web/next';
|
|
163
|
+
* import type { NextRequest } from 'next/server';
|
|
164
|
+
*
|
|
165
|
+
* export function middleware(request: NextRequest) {
|
|
166
|
+
* return palbeMiddleware(request);
|
|
167
|
+
* }
|
|
168
|
+
* export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
interface PalbeMiddlewareOptions {
|
|
173
|
+
/** Refresh when the access token expires within this window. Default 60s. */
|
|
174
|
+
refreshMarginMs?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Pre-built response to decorate (e.g. from chained middleware) instead of
|
|
177
|
+
* building `NextResponse.next({ request })`. Pass-through cases return it
|
|
178
|
+
* UNMODIFIED. Caveat: a pre-built response snapshotted the request headers
|
|
179
|
+
* at ITS creation, so a rotated cookie reaches the browser (Set-Cookie)
|
|
180
|
+
* but not the downstream RSC render of THIS request — prefer omitting this
|
|
181
|
+
* and building your response after palbeMiddleware when possible.
|
|
182
|
+
*/
|
|
183
|
+
response?: NextResponse;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Keep the session cookie fresh for the request. No session / still-fresh
|
|
187
|
+
* session → untouched pass-through (no cache-control changes). Stale or
|
|
188
|
+
* expired → single-flight refresh, then the rotated (or cleared, on a dead
|
|
189
|
+
* token) cookies are written onto BOTH the request (for the downstream RSC
|
|
190
|
+
* render via `NextResponse.next({ request })`) and the response (for the
|
|
191
|
+
* browser), with `Cache-Control: private, no-store` on touched responses.
|
|
192
|
+
*
|
|
193
|
+
* Cookie writes go through Next's cookie APIs with the DECODED codec values:
|
|
194
|
+
* Next percent-encodes on serialization, so the wire bytes come out exactly
|
|
195
|
+
* as the shared codec's (see encodeSessionCookiesDecoded). NextRequest's
|
|
196
|
+
* cookie store is NOT a CookieStoreLike (its set is the 2-arg/RequestCookie
|
|
197
|
+
* form), hence the direct codec calls here instead of the pbServer adapter.
|
|
198
|
+
*/
|
|
199
|
+
declare function palbeMiddleware(request: NextRequest, opts?: PalbeMiddlewareOptions): Promise<NextResponse>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Structural slice of Next's request cookie store (`await cookies()`); any
|
|
203
|
+
* object honoring it works — the seam for tests and non-Next servers.
|
|
204
|
+
* `set`/`delete` are optional because RSC-scope stores are read-only.
|
|
205
|
+
*/
|
|
206
|
+
interface CookieStoreLike {
|
|
207
|
+
get(name: string): {
|
|
208
|
+
name: string;
|
|
209
|
+
value: string;
|
|
210
|
+
} | undefined;
|
|
211
|
+
getAll?(): Array<{
|
|
212
|
+
name: string;
|
|
213
|
+
value: string;
|
|
214
|
+
}>;
|
|
215
|
+
set?(name: string, value: string, options?: Record<string, unknown>): void;
|
|
216
|
+
delete?(name: string): void;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Build a per-request `PB` client over the request's cookies. Reads the
|
|
220
|
+
* session the browser SDK wrote (shared cookie codec), refreshes pre-flight
|
|
221
|
+
* when expired, and — where the store is writable (Server Actions, Route
|
|
222
|
+
* Handlers) — persists the rotated session back.
|
|
223
|
+
*
|
|
224
|
+
* Each call builds a fresh runtime (a handful of small objects, no I/O) —
|
|
225
|
+
* cheap enough to call per data access; code paths invoking it many times
|
|
226
|
+
* within one request MAY memoize per-request (e.g. React's `cache()`), but
|
|
227
|
+
* don't have to.
|
|
228
|
+
*
|
|
229
|
+
* ```ts
|
|
230
|
+
* const pb = await pbServer();
|
|
231
|
+
* const todos = await pb.todos.list();
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
declare function pbServer(opts?: {
|
|
235
|
+
cookies?: CookieStoreLike;
|
|
236
|
+
}): Promise<PB>;
|
|
237
|
+
|
|
238
|
+
export { type CookieStoreLike, type HandleAuthCallbackOptions, type PalbeMiddlewareOptions, SESSION_COOKIE_ATTRS, type SessionCookieWrite, type StoredSession, clearedSessionCookieNames, decodeSessionCookies, encodeSessionCookies, encodeSessionCookiesDecoded, endpointRefFromApiKey, handleAuthCallback, palbeMiddleware, pbServer, sessionCookieName };
|