@palbase/web 4.0.2 → 5.0.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/README.md +20 -3
- package/dist/chunk-BNAGRUIQ.js +381 -0
- package/dist/chunk-BNAGRUIQ.js.map +1 -0
- package/dist/chunk-OWLTZG2V.js +24 -0
- package/dist/chunk-OWLTZG2V.js.map +1 -0
- package/dist/{chunk-TW6YN354.js → chunk-QKFPPZLF.js} +13 -373
- package/dist/chunk-QKFPPZLF.js.map +1 -0
- package/dist/gen/cli.cjs +8 -5
- package/dist/gen/cli.cjs.map +1 -1
- package/dist/gen/cli.js +8 -5
- package/dist/gen/cli.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/internal.cjs +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.js +2 -1
- package/dist/next/client.cjs +1 -1
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +2 -1
- package/dist/next/client.js.map +1 -1
- package/dist/next/index.cjs +4 -88
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +2 -62
- package/dist/next/index.d.ts +2 -62
- package/dist/next/index.js +8 -102
- package/dist/next/index.js.map +1 -1
- package/dist/next/middleware.cjs +263 -0
- package/dist/next/middleware.cjs.map +1 -0
- package/dist/next/middleware.d.cts +101 -0
- package/dist/next/middleware.d.ts +101 -0
- package/dist/next/middleware.js +107 -0
- package/dist/next/middleware.js.map +1 -0
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +2 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +11 -1
- package/dist/chunk-TW6YN354.js.map +0 -1
package/dist/next/client.js
CHANGED
package/dist/next/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/client.ts"],"sourcesContent":["/**\n * '@palbase/web/next/client' — the browser half of the Next.js adapter. This entry\n * must NEVER import 'next' (it runs in plain client bundles); the cookie jar\n * is document.cookie itself, written with the shared codec so the server\n * side (pbServer / middleware / callback) reads the same bytes.\n */\nimport { __configure, getRuntime } from '../internal.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookies,\n} from './cookie-codec.js';\n\n/** 30 days — the refresh-token TTL (P3 design contract). */\nconst SESSION_MAX_AGE_S = 2_592_000;\n\n// Secure is unconditional, INCLUDING http://localhost: browsers treat\n// localhost as a potentially-trustworthy origin, so document.cookie accepts\n// Secure cookies there — no dev-mode special case needed. KNOWN LIMITATION:\n// plain-http origins OTHER than localhost — e.g. LAN-IP device testing on\n// http://192.168.x.x — are NOT trustworthy, so the browser silently DROPS\n// these Secure cookie writes and the session won't persist; use https (or a\n// localhost tunnel/port-forward) for on-device testing. NOT HttpOnly by\n// design: the browser SDK must read/write the session (Supabase-paradigm\n// tradeoff, documented in the P3 plan).\nconst WRITE_ATTRS = 'Path=/; SameSite=Lax; Secure';\n\n/** Parse document.cookie (\"a=1; b=2\") into a name → raw-value map. */\nfunction cookieJar(): Map<string, string> {\n const jar = new Map<string, string>();\n for (const part of document.cookie.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (name) jar.set(name, part.slice(eq + 1).trim());\n }\n return jar;\n}\n\nfunction deleteCookie(name: string): void {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;\n}\n\n/**\n * document.cookie-backed SessionStorageAdapter. `load` returns the EXTENDED\n * PersistedSession (access token + expiry ride along) so hydration adopts\n * the full session without a refresh round-trip.\n */\nexport function cookieSessionStorage(environmentRef: string): SessionStorageAdapter {\n return {\n load(): PersistedSession | null {\n if (typeof document === 'undefined') return null;\n const jar = cookieJar();\n const stored = decodeSessionCookies((name) => jar.get(name), environmentRef);\n if (!stored) return null;\n // A refresh-only save round-trips as a:'' / e:0 — normalize back to\n // the legacy shape so hydration takes the expired-trick path cleanly.\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 (typeof document === 'undefined') return;\n const jar = cookieJar();\n // Delete every currently-present session cookie first (stale chunks of\n // a previously-larger session, or a stale base when the new write\n // chunks), then set the new cookie(s).\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n const { set, clear } = encodeSessionCookies(environmentRef, {\n accessToken: session.accessToken ?? '',\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt ?? 0,\n });\n for (const { name, value } of set) {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=${value}; ${WRITE_ATTRS}; Max-Age=${SESSION_MAX_AGE_S}`;\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) deleteCookie(name);\n },\n clear(): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n },\n };\n}\n\n/**\n * One-liner for a client component/provider: re-configure the already-loaded\n * gen config (palbe.gen.ts must be imported first — throws the guided\n * notConfigured error otherwise) with cookie-backed session storage so the\n * browser and the server share the session. Calling it again simply\n * re-configures (safe, e.g. under fast refresh). When Next evaluates the\n * client component module server-side there is no document — no-op.\n */\nexport function setupPalbeNext(): void {\n if (typeof document === 'undefined') return;\n const config = getRuntime().config;\n __configure({\n ...config,\n storage: cookieSessionStorage(config.environmentRef),\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/next/client.ts"],"sourcesContent":["/**\n * '@palbase/web/next/client' — the browser half of the Next.js adapter. This entry\n * must NEVER import 'next' (it runs in plain client bundles); the cookie jar\n * is document.cookie itself, written with the shared codec so the server\n * side (pbServer / middleware / callback) reads the same bytes.\n */\nimport { __configure, getRuntime } from '../internal.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookies,\n} from './cookie-codec.js';\n\n/** 30 days — the refresh-token TTL (P3 design contract). */\nconst SESSION_MAX_AGE_S = 2_592_000;\n\n// Secure is unconditional, INCLUDING http://localhost: browsers treat\n// localhost as a potentially-trustworthy origin, so document.cookie accepts\n// Secure cookies there — no dev-mode special case needed. KNOWN LIMITATION:\n// plain-http origins OTHER than localhost — e.g. LAN-IP device testing on\n// http://192.168.x.x — are NOT trustworthy, so the browser silently DROPS\n// these Secure cookie writes and the session won't persist; use https (or a\n// localhost tunnel/port-forward) for on-device testing. NOT HttpOnly by\n// design: the browser SDK must read/write the session (Supabase-paradigm\n// tradeoff, documented in the P3 plan).\nconst WRITE_ATTRS = 'Path=/; SameSite=Lax; Secure';\n\n/** Parse document.cookie (\"a=1; b=2\") into a name → raw-value map. */\nfunction cookieJar(): Map<string, string> {\n const jar = new Map<string, string>();\n for (const part of document.cookie.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (name) jar.set(name, part.slice(eq + 1).trim());\n }\n return jar;\n}\n\nfunction deleteCookie(name: string): void {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;\n}\n\n/**\n * document.cookie-backed SessionStorageAdapter. `load` returns the EXTENDED\n * PersistedSession (access token + expiry ride along) so hydration adopts\n * the full session without a refresh round-trip.\n */\nexport function cookieSessionStorage(environmentRef: string): SessionStorageAdapter {\n return {\n load(): PersistedSession | null {\n if (typeof document === 'undefined') return null;\n const jar = cookieJar();\n const stored = decodeSessionCookies((name) => jar.get(name), environmentRef);\n if (!stored) return null;\n // A refresh-only save round-trips as a:'' / e:0 — normalize back to\n // the legacy shape so hydration takes the expired-trick path cleanly.\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 (typeof document === 'undefined') return;\n const jar = cookieJar();\n // Delete every currently-present session cookie first (stale chunks of\n // a previously-larger session, or a stale base when the new write\n // chunks), then set the new cookie(s).\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n const { set, clear } = encodeSessionCookies(environmentRef, {\n accessToken: session.accessToken ?? '',\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt ?? 0,\n });\n for (const { name, value } of set) {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=${value}; ${WRITE_ATTRS}; Max-Age=${SESSION_MAX_AGE_S}`;\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) deleteCookie(name);\n },\n clear(): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n },\n };\n}\n\n/**\n * One-liner for a client component/provider: re-configure the already-loaded\n * gen config (palbe.gen.ts must be imported first — throws the guided\n * notConfigured error otherwise) with cookie-backed session storage so the\n * browser and the server share the session. Calling it again simply\n * re-configures (safe, e.g. under fast refresh). When Next evaluates the\n * client component module server-side there is no document — no-op.\n */\nexport function setupPalbeNext(): void {\n if (typeof document === 'undefined') return;\n const config = getRuntime().config;\n __configure({\n ...config,\n storage: cookieSessionStorage(config.environmentRef),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAM,oBAAoB;AAW1B,IAAM,cAAc;AAGpB,SAAS,YAAiC;AACxC,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,QAAQ,SAAS,OAAO,MAAM,GAAG,GAAG;AAC7C,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,OAAO,GAAI;AACf,UAAM,OAAO,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACpC,QAAI,KAAM,KAAI,IAAI,MAAM,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC;AAAA,EACnD;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAoB;AAExC,WAAS,SAAS,GAAG,IAAI,MAAM,WAAW;AAC5C;AAOO,SAAS,qBAAqB,gBAA+C;AAClF,SAAO;AAAA,IACL,OAAgC;AAC9B,UAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,YAAM,MAAM,UAAU;AACtB,YAAM,SAAS,qBAAqB,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,cAAc;AAC3E,UAAI,CAAC,OAAQ,QAAO;AAGpB,aAAO,OAAO,eAAe,OAAO,YAAY,IAC5C;AAAA,QACE,cAAc,OAAO;AAAA,QACrB,aAAa,OAAO;AAAA,QACpB,WAAW,OAAO;AAAA,MACpB,IACA,EAAE,cAAc,OAAO,aAAa;AAAA,IAC1C;AAAA,IACA,KAAK,SAAiC;AACpC,UAAI,OAAO,aAAa,YAAa;AACrC,YAAM,MAAM,UAAU;AAItB,iBAAW,QAAQ,0BAA0B,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG;AAC/E,qBAAa,IAAI;AAAA,MACnB;AACA,YAAM,EAAE,KAAK,MAAM,IAAI,qBAAqB,gBAAgB;AAAA,QAC1D,aAAa,QAAQ,eAAe;AAAA,QACpC,cAAc,QAAQ;AAAA,QACtB,WAAW,QAAQ,aAAa;AAAA,MAClC,CAAC;AACD,iBAAW,EAAE,MAAM,MAAM,KAAK,KAAK;AAEjC,iBAAS,SAAS,GAAG,IAAI,IAAI,KAAK,KAAK,WAAW,aAAa,iBAAiB;AAAA,MAClF;AAGA,iBAAW,QAAQ,MAAO,cAAa,IAAI;AAAA,IAC7C;AAAA,IACA,QAAc;AACZ,UAAI,OAAO,aAAa,YAAa;AACrC,YAAM,MAAM,UAAU;AACtB,iBAAW,QAAQ,0BAA0B,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG;AAC/E,qBAAa,IAAI;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;AAUO,SAAS,iBAAuB;AACrC,MAAI,OAAO,aAAa,YAAa;AACrC,QAAM,SAAS,WAAW,EAAE;AAC5B,cAAY;AAAA,IACV,GAAG;AAAA,IACH,SAAS,qBAAqB,OAAO,cAAc;AAAA,EACrD,CAAC;AACH;","names":[]}
|
package/dist/next/index.cjs
CHANGED
|
@@ -37,7 +37,6 @@ __export(next_exports, {
|
|
|
37
37
|
encodeSessionCookiesDecoded: () => encodeSessionCookiesDecoded,
|
|
38
38
|
environmentRefFromApiKey: () => environmentRefFromApiKey,
|
|
39
39
|
handleAuthCallback: () => handleAuthCallback,
|
|
40
|
-
palbeMiddleware: () => palbeMiddleware,
|
|
41
40
|
pbServer: () => pbServer,
|
|
42
41
|
sessionCookieName: () => sessionCookieName
|
|
43
42
|
});
|
|
@@ -9038,7 +9037,7 @@ function defaultSessionStorage(key) {
|
|
|
9038
9037
|
}
|
|
9039
9038
|
|
|
9040
9039
|
// src/version.ts
|
|
9041
|
-
var VERSION = "
|
|
9040
|
+
var VERSION = "5.0.1";
|
|
9042
9041
|
|
|
9043
9042
|
// src/runtime.ts
|
|
9044
9043
|
function buildRuntime(config) {
|
|
@@ -9418,7 +9417,7 @@ function getRuntime() {
|
|
|
9418
9417
|
return rt;
|
|
9419
9418
|
}
|
|
9420
9419
|
|
|
9421
|
-
// src/next/
|
|
9420
|
+
// src/next/global-config.ts
|
|
9422
9421
|
function requireGlobalConfig(hint) {
|
|
9423
9422
|
try {
|
|
9424
9423
|
return getRuntime().config;
|
|
@@ -9429,6 +9428,8 @@ function requireGlobalConfig(hint) {
|
|
|
9429
9428
|
});
|
|
9430
9429
|
}
|
|
9431
9430
|
}
|
|
9431
|
+
|
|
9432
|
+
// src/next/shared.ts
|
|
9432
9433
|
var nextServerModule;
|
|
9433
9434
|
async function importNextServer(caller) {
|
|
9434
9435
|
nextServerModule ??= import("next/server");
|
|
@@ -9534,90 +9535,6 @@ function handleAuthCallback(opts) {
|
|
|
9534
9535
|
};
|
|
9535
9536
|
}
|
|
9536
9537
|
|
|
9537
|
-
// src/next/middleware.ts
|
|
9538
|
-
var DEFAULT_REFRESH_MARGIN_MS = 6e4;
|
|
9539
|
-
var inflightRefreshes = /* @__PURE__ */ new Map();
|
|
9540
|
-
function refreshSingleFlight(config, refreshToken) {
|
|
9541
|
-
const existing = inflightRefreshes.get(refreshToken);
|
|
9542
|
-
if (existing) return existing;
|
|
9543
|
-
const pending = refresh(config, refreshToken).finally(() => {
|
|
9544
|
-
inflightRefreshes.delete(refreshToken);
|
|
9545
|
-
});
|
|
9546
|
-
inflightRefreshes.set(refreshToken, pending);
|
|
9547
|
-
return pending;
|
|
9548
|
-
}
|
|
9549
|
-
async function refresh(config, refreshToken) {
|
|
9550
|
-
try {
|
|
9551
|
-
const res = await fetch(`${config.url}/auth/token/refresh`, {
|
|
9552
|
-
method: "POST",
|
|
9553
|
-
headers: { apikey: config.apiKey, "content-type": "application/json" },
|
|
9554
|
-
body: JSON.stringify({ refresh_token: refreshToken })
|
|
9555
|
-
});
|
|
9556
|
-
if (res.status === 400 || res.status === 401 || res.status === 403) {
|
|
9557
|
-
return { kind: "terminal" };
|
|
9558
|
-
}
|
|
9559
|
-
if (!res.ok) return { kind: "transient" };
|
|
9560
|
-
const raw = await res.json();
|
|
9561
|
-
if (typeof raw === "object" && raw !== null) {
|
|
9562
|
-
const obj = raw;
|
|
9563
|
-
if (typeof obj.access_token === "string" && typeof obj.refresh_token === "string" && typeof obj.expires_in === "number") {
|
|
9564
|
-
return {
|
|
9565
|
-
kind: "rotated",
|
|
9566
|
-
session: {
|
|
9567
|
-
accessToken: obj.access_token,
|
|
9568
|
-
refreshToken: obj.refresh_token,
|
|
9569
|
-
expiresAt: Date.now() + obj.expires_in * 1e3
|
|
9570
|
-
}
|
|
9571
|
-
};
|
|
9572
|
-
}
|
|
9573
|
-
}
|
|
9574
|
-
return { kind: "transient" };
|
|
9575
|
-
} catch {
|
|
9576
|
-
return { kind: "transient" };
|
|
9577
|
-
}
|
|
9578
|
-
}
|
|
9579
|
-
async function palbeMiddleware(request, opts) {
|
|
9580
|
-
const config = requireGlobalConfig(
|
|
9581
|
-
"import the generated palbe.gen.ts at the top of middleware.ts \u2014 Next bundles middleware as its own module graph, so the layout.tsx import does not reach it."
|
|
9582
|
-
);
|
|
9583
|
-
const { NextResponse } = await importNextServer("palbeMiddleware");
|
|
9584
|
-
const passThrough = () => opts?.response ?? NextResponse.next({ request });
|
|
9585
|
-
const ref = config.environmentRef;
|
|
9586
|
-
const session = decodeSessionCookies((name) => request.cookies.get(name)?.value, ref);
|
|
9587
|
-
if (!session) return passThrough();
|
|
9588
|
-
if (session.expiresAt - Date.now() > (opts?.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS)) {
|
|
9589
|
-
return passThrough();
|
|
9590
|
-
}
|
|
9591
|
-
const outcome = await refreshSingleFlight(config, session.refreshToken);
|
|
9592
|
-
if (outcome.kind === "transient") return passThrough();
|
|
9593
|
-
const staleNames = clearedSessionCookieNames(ref, (name) => request.cookies.has(name));
|
|
9594
|
-
if (outcome.kind === "terminal") {
|
|
9595
|
-
for (const name of staleNames) request.cookies.delete(name);
|
|
9596
|
-
const response2 = opts?.response ?? NextResponse.next({ request });
|
|
9597
|
-
for (const name of staleNames) {
|
|
9598
|
-
response2.cookies.set(name, "", { ...SESSION_COOKIE_ATTRS, maxAge: 0 });
|
|
9599
|
-
}
|
|
9600
|
-
response2.headers.set("cache-control", "private, no-store");
|
|
9601
|
-
return response2;
|
|
9602
|
-
}
|
|
9603
|
-
const write = encodeSessionCookiesDecoded(ref, outcome.session);
|
|
9604
|
-
for (const name of staleNames) request.cookies.delete(name);
|
|
9605
|
-
for (const { name, value } of write.set) request.cookies.set(name, value);
|
|
9606
|
-
for (const name of write.clear) request.cookies.delete(name);
|
|
9607
|
-
const response = opts?.response ?? NextResponse.next({ request });
|
|
9608
|
-
const written = new Set(write.set.map((c) => c.name));
|
|
9609
|
-
for (const { name, value } of write.set) {
|
|
9610
|
-
response.cookies.set(name, value, SESSION_COOKIE_ATTRS);
|
|
9611
|
-
}
|
|
9612
|
-
for (const name of /* @__PURE__ */ new Set([...write.clear, ...staleNames])) {
|
|
9613
|
-
if (!written.has(name)) {
|
|
9614
|
-
response.cookies.set(name, "", { ...SESSION_COOKIE_ATTRS, maxAge: 0 });
|
|
9615
|
-
}
|
|
9616
|
-
}
|
|
9617
|
-
response.headers.set("cache-control", "private, no-store");
|
|
9618
|
-
return response;
|
|
9619
|
-
}
|
|
9620
|
-
|
|
9621
9538
|
// src/next/server.ts
|
|
9622
9539
|
function nextRequired(cause) {
|
|
9623
9540
|
const detail = cause instanceof Error && cause.message ? ` (${cause.message})` : "";
|
|
@@ -9707,7 +9624,6 @@ async function pbServer(opts) {
|
|
|
9707
9624
|
encodeSessionCookiesDecoded,
|
|
9708
9625
|
environmentRefFromApiKey,
|
|
9709
9626
|
handleAuthCallback,
|
|
9710
|
-
palbeMiddleware,
|
|
9711
9627
|
pbServer,
|
|
9712
9628
|
sessionCookieName
|
|
9713
9629
|
});
|