better-supabase 0.2.0 → 0.4.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.
Files changed (190) hide show
  1. package/README.md +37 -33
  2. package/dist/base64-D2gsioCY.js +42 -0
  3. package/dist/claims-BAf6c7is.js +48 -0
  4. package/dist/cli/_format-Cax5EsM9.js +101 -0
  5. package/dist/cli/bin.js +39 -6
  6. package/dist/cli/command-CK14fP91.js +502 -0
  7. package/dist/cli/compare-BmIWVEui.js +9 -0
  8. package/dist/cli/config-D9R0o1LH.js +1895 -0
  9. package/dist/cli/config-SSKSTUl2.js +2 -0
  10. package/dist/cli/db-DOBPt660.js +52 -0
  11. package/dist/cli/dist-B1MvLv-M.js +129 -0
  12. package/dist/cli/dist-CFgGknmZ.js +1112 -0
  13. package/dist/cli/dist-Du7VnEUG.js +621 -0
  14. package/dist/cli/doctor-BOym3mbL.js +2382 -0
  15. package/dist/cli/env-BZ0uy8XK.js +108 -0
  16. package/dist/cli/env-jIFUADLY.js +511 -0
  17. package/dist/cli/errors-4v4DTtda.js +56 -0
  18. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  19. package/dist/cli/gen-Ccn_p90L.js +2008 -0
  20. package/dist/cli/gen-D5cLCULx.js +2 -0
  21. package/dist/cli/index.d.ts +295 -28
  22. package/dist/cli/index.js +184 -3
  23. package/dist/cli/init-ClnxRYBF.js +550 -0
  24. package/dist/cli/introspect-DxqfkVIv.js +81 -0
  25. package/dist/cli/io-k41_z-jT.js +24 -0
  26. package/dist/cli/json5-ChRycquj.js +787 -0
  27. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  28. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  29. package/dist/cli/keys-ByKHnvP9.js +83 -0
  30. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  31. package/dist/cli/openapi-8WHjxiTJ.js +66 -0
  32. package/dist/cli/project-B_GeXyHh.js +70 -0
  33. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  34. package/dist/cli/prompts-BTYQGunc.js +2 -0
  35. package/dist/cli/read-sets-CMa29t9a.js +971 -0
  36. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  37. package/dist/cli/run-CP20LKk_.js +347 -0
  38. package/dist/cli/seed-5J6eu_Xg.js +98 -0
  39. package/dist/cli/skills-LJJIhLFR.js +137 -0
  40. package/dist/cli/sql-D4VbqmXi.js +199 -0
  41. package/dist/cli/style-C0NFdJRL.js +29 -0
  42. package/dist/cli/toml-DB-MNnFs.js +424 -0
  43. package/dist/{version-Cqq1m3UY.js → cli/version-5mW_8sY1.js} +1 -1
  44. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  45. package/dist/client/index.d.ts +2 -2
  46. package/dist/client/index.js +15 -25
  47. package/dist/{compiler-CZ9nIuqy.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
  48. package/dist/config/index.d.ts +4 -3
  49. package/dist/config/index.js +5 -3
  50. package/dist/config-BpxDfnbg.js +129 -0
  51. package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
  52. package/dist/{config-ClDXmVtY.js → config-CbvFmGD8.js} +14 -137
  53. package/dist/{define-BXMYD9AY.js → define-Bh0AqTik.js} +221 -120
  54. package/dist/{define-e3zI4_4g.d.ts → define-CRS2MRV3.d.ts} +36 -31
  55. package/dist/edge/index.d.ts +9 -13
  56. package/dist/edge/index.js +51 -25
  57. package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
  58. package/dist/env/index.js +203 -1
  59. package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
  60. package/dist/events/index.d.ts +1 -1
  61. package/dist/events/index.js +139 -1
  62. package/dist/{events-DOt3nSO-.d.ts → events-B21UOhvP.d.ts} +4 -4
  63. package/dist/{executor-CGdNQ27g.d.ts → executor-B5IoUxLE.d.ts} +2 -2
  64. package/dist/{executor-DeppvirK.js → executor-CVt6dD0B.js} +9 -4
  65. package/dist/{executor-BYXQ9AGn.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
  66. package/dist/hono/index.d.ts +17 -24
  67. package/dist/hono/index.js +11 -11
  68. package/dist/{index-DuP14VHo.d.ts → index-528Ugmyp.d.ts} +30 -10
  69. package/dist/{index-Bw1bwtC0.d.ts → index-BAy4tsAP.d.ts} +28 -15
  70. package/dist/{index-CBDufH1M.d.ts → index-BBdvo-5d.d.ts} +24 -26
  71. package/dist/{index-DbxFr7XG.d.ts → index-BhHEC04r.d.ts} +9 -4
  72. package/dist/index-Cf-D2jk1.d.ts +50 -0
  73. package/dist/{index-Ciu5Xlml.d.ts → index-D5IfUIDh.d.ts} +6 -5
  74. package/dist/{index-K-3WM2Hp.d.ts → index-DFmOpBGC.d.ts} +13 -13
  75. package/dist/{index-D4v7sncu.d.ts → index-RlLXd8dL.d.ts} +13 -13
  76. package/dist/index.d.ts +17 -17
  77. package/dist/index.js +23 -14
  78. package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
  79. package/dist/jobs/index.d.ts +33 -26
  80. package/dist/jobs/index.js +44 -22
  81. package/dist/{json-schema-BpYqkCKf.js → json-schema-DP6V7cbr.js} +8 -3
  82. package/dist/lint/index.js +1 -1
  83. package/dist/list/index.d.ts +2 -2
  84. package/dist/list/index.js +131 -96
  85. package/dist/{live-DJyZMD6w.d.ts → live-BHakSWWo.d.ts} +26 -16
  86. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  87. package/dist/lookup-BaP5bI27.js +18 -0
  88. package/dist/mcp/index.d.ts +34 -27
  89. package/dist/mcp/index.js +59 -25
  90. package/dist/{mfa-TVia554c.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
  91. package/dist/{mfa-CpLfJSde.js → mfa-byfjruR9.js} +190 -46
  92. package/dist/next/image/index.js +6 -3
  93. package/dist/next/index.d.ts +64 -48
  94. package/dist/next/index.js +63 -50
  95. package/dist/openapi/index.d.ts +1 -1
  96. package/dist/openapi/index.js +61 -38
  97. package/dist/orpc/index.d.ts +18 -21
  98. package/dist/orpc/index.js +12 -12
  99. package/dist/otel/index.d.ts +5 -5
  100. package/dist/otel/index.js +84 -63
  101. package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-C4KFzLc9.js} +12 -7
  102. package/dist/permdock-sql-DImJULY-.d.ts +17 -0
  103. package/dist/{plugin-C4TR9J0Z.d.ts → plugin-bNhvJwRh.d.ts} +5 -4
  104. package/dist/plugins/actor/index.d.ts +1 -1
  105. package/dist/plugins/actor/index.js +1 -1
  106. package/dist/plugins/rules/index.d.ts +2 -2
  107. package/dist/plugins/rules/index.js +46 -16
  108. package/dist/plugins/soft-delete/index.d.ts +3 -3
  109. package/dist/plugins/soft-delete/index.js +4 -4
  110. package/dist/plugins/tenant/index.d.ts +3 -3
  111. package/dist/plugins/tenant/index.js +10 -4
  112. package/dist/plugins/timestamps/index.d.ts +1 -1
  113. package/dist/plugins/timestamps/index.js +2 -2
  114. package/dist/plugins/validation/index.d.ts +2 -2
  115. package/dist/plugins/validation/index.js +3 -3
  116. package/dist/pool-DFwEQwmN.d.ts +78 -0
  117. package/dist/postgres/index.d.ts +10 -5
  118. package/dist/postgres/index.js +37 -16
  119. package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
  120. package/dist/{problem-BTkTtRV9.js → problem-BihjPR0t.js} +11 -5
  121. package/dist/{problem-DlgB0lYl.d.ts → problem-U8UEKccv.d.ts} +6 -4
  122. package/dist/query/index.d.ts +2 -2
  123. package/dist/query/index.js +2 -2
  124. package/dist/{query-ChvDbsDE.js → query-BMMSWbI7.js} +19 -19
  125. package/dist/react/index.d.ts +7 -7
  126. package/dist/react/index.js +2 -2
  127. package/dist/react/server.d.ts +6 -6
  128. package/dist/react/server.js +3 -3
  129. package/dist/react/session.d.ts +21 -2
  130. package/dist/react/session.js +19 -1
  131. package/dist/{react-C83BdBGt.js → react-CQHMaESR.js} +59 -55
  132. package/dist/{read-set-4lI3W_gZ.js → read-set-FWK8qQ58.js} +51 -16
  133. package/dist/realtime/index.d.ts +4 -3
  134. package/dist/realtime/index.js +19 -17
  135. package/dist/{index-CG69s15v.d.ts → refresh-BwYkyNOQ.d.ts} +7 -3
  136. package/dist/{resolve-CjHILk2w.d.ts → resolve-BAqQcwMW.d.ts} +17 -11
  137. package/dist/{resource-CgZa3YG_.d.ts → resource-BJcaIfxi.d.ts} +17 -8
  138. package/dist/{resource-BHEWmQJK.js → resource-My_y-Syh.js} +43 -21
  139. package/dist/{respond-Dne1ARSa.d.ts → respond-DgIbdrt8.d.ts} +39 -14
  140. package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
  141. package/dist/{result-CTkCZ6JA.d.ts → result-CqEsbgYs.d.ts} +16 -11
  142. package/dist/server/index.d.ts +38 -13
  143. package/dist/server/index.js +43 -14
  144. package/dist/{respond-DMcbQwfN.js → server-BA75B0mT.js} +151 -96
  145. package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
  146. package/dist/{simplify-bwJpLnaS.js → simplify-Cqvfr9dj.js} +76 -39
  147. package/dist/sql/index.d.ts +139 -0
  148. package/dist/sql/index.js +1983 -0
  149. package/dist/{sql-DTjA1LPj.js → sql-B34HU2DI.js} +74 -17
  150. package/dist/ssr/index.d.ts +4 -4
  151. package/dist/ssr/index.js +2 -2
  152. package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
  153. package/dist/{standard-CyhRzctb.d.ts → standard-BsaCYQJc.d.ts} +1 -1
  154. package/dist/{stats-C5W-rBfR.js → stats-C_YxA2r1.js} +40 -14
  155. package/dist/storage/index.d.ts +4 -3
  156. package/dist/storage/index.js +548 -1
  157. package/dist/{tables-rZxRxNPW.js → tables-Cb-3_VWc.js} +19 -5
  158. package/dist/temporal-BwshQk0W.js +27 -0
  159. package/dist/temporal-required-CBNHT6ni.js +19 -0
  160. package/dist/testing/index.d.ts +25 -24
  161. package/dist/testing/index.js +165 -66
  162. package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
  163. package/dist/version-Bmf7_FzM.js +4 -0
  164. package/dist/{view-D1s0wa0T.d.ts → view-B64o81wp.d.ts} +36 -6
  165. package/dist/{view-aOYizTeE.js → view-BrJI2LKp.js} +11 -2
  166. package/dist/webhooks/index.d.ts +19 -17
  167. package/dist/webhooks/index.js +1 -1
  168. package/dist/{webhooks-DMipjhSP.js → webhooks-Bh_Jf_-l.js} +45 -24
  169. package/package.json +50 -22
  170. package/schemas/config-v1.json +31 -1
  171. package/schemas/doctor-report-v1.json +17 -1
  172. package/schemas/snapshot-v2.json +35 -0
  173. package/skills/better-supabase/SKILL.md +77 -18
  174. package/skills/better-supabase/references/plugins.md +21 -22
  175. package/skills/better-supabase/references/troubleshooting.md +38 -18
  176. package/skills/better-supabase-api/SKILL.md +66 -18
  177. package/skills/better-supabase-api/references/adapters.md +64 -41
  178. package/skills/better-supabase-auth/SKILL.md +146 -0
  179. package/skills/better-supabase-auth/references/permdock.md +55 -0
  180. package/skills/better-supabase-testing/SKILL.md +68 -11
  181. package/dist/casing-da0uRTqt.js +0 -14
  182. package/dist/claims-CoTobNC-.js +0 -25
  183. package/dist/cli-DrSgDOQC.js +0 -6646
  184. package/dist/env-BEToab0I.js +0 -204
  185. package/dist/events-BuzTgn2D.js +0 -135
  186. package/dist/pool-R2m_DkXi.d.ts +0 -37
  187. package/dist/seed-CzecODcJ.js +0 -101
  188. package/dist/session-C3LxnQ0e.d.ts +0 -22
  189. package/dist/session-CqEwzVup.js +0 -20
  190. package/dist/storage-DuGn38fr.js +0 -527
@@ -1,5 +1,6 @@
1
- import { n as dbError } from "./errors-Bl3rkOzo.js";
1
+ import { n as dbError } from "./errors-L0xK41y_.js";
2
2
  import { t as consoleLogger } from "./logger-glM28NVN.js";
3
+ import { n as decodeJwtPayload } from "./base64-D2gsioCY.js";
3
4
  import { DEFAULT_COOKIE_OPTIONS, createChunks, isChunkLike, parseCookieHeader, serializeCookieHeader, stringFromBase64URL, stringToBase64URL } from "@supabase/ssr";
4
5
  import { extractCredentials, verifyCredentials } from "@supabase/server/core";
5
6
  //#region src/auth/impersonation.ts
@@ -22,6 +23,65 @@ function impersonatorOf(claims) {
22
23
  } : { id: sub };
23
24
  }
24
25
  //#endregion
26
+ //#region src/auth/actor.ts
27
+ function isRecord(value) {
28
+ return typeof value === "object" && value !== null && !Array.isArray(value);
29
+ }
30
+ function copyAct(level) {
31
+ const entries = Object.entries(level).map(([key, value]) => [key, key === "act" && isRecord(value) ? copyAct(value) : value]);
32
+ return Object.fromEntries(entries);
33
+ }
34
+ /**
35
+ * Reads only `act` and `client_id`, the way PermDock's `actorOf` does. An
36
+ * `act` that is not a chain of objects each with a non-empty `sub` is
37
+ * `{ ok: false }`: the session must not pass as the user alone.
38
+ */
39
+ function actorOf(claims) {
40
+ const outer = claims["act"];
41
+ if (outer !== void 0) {
42
+ let level = outer;
43
+ while (level !== void 0) {
44
+ if (!isRecord(level)) return {
45
+ ok: false,
46
+ reason: "invalid-chain"
47
+ };
48
+ const sub = level["sub"];
49
+ if (typeof sub !== "string" || sub === "") return {
50
+ ok: false,
51
+ reason: "invalid-chain"
52
+ };
53
+ level = level["act"];
54
+ }
55
+ if (!isRecord(outer) || typeof outer["sub"] !== "string") return {
56
+ ok: false,
57
+ reason: "invalid-chain"
58
+ };
59
+ return {
60
+ ok: true,
61
+ actor: {
62
+ id: outer["sub"],
63
+ kind: "oauth-client",
64
+ chain: copyAct(outer)
65
+ }
66
+ };
67
+ }
68
+ const client = claims["client_id"];
69
+ if (typeof client === "string" && client !== "") return {
70
+ ok: true,
71
+ actor: {
72
+ id: client,
73
+ kind: "oauth-client"
74
+ }
75
+ };
76
+ return { ok: true };
77
+ }
78
+ /** The OAuth `scope` claim (a space-separated string or a list); `undefined` when empty. */
79
+ function delegationOf(claims) {
80
+ const scope = claims["scope"];
81
+ const scopes = typeof scope === "string" ? scope.split(/\s+/u).filter(Boolean) : Array.isArray(scope) ? scope.filter((item) => typeof item === "string") : [];
82
+ return scopes.length > 0 ? { scopes } : void 0;
83
+ }
84
+ //#endregion
25
85
  //#region src/auth/refresh.ts
26
86
  /**
27
87
  * How long a finished refresh is reused for requests still carrying the old
@@ -29,6 +89,7 @@ function impersonatorOf(claims) {
29
89
  * refresh-token reuse interval of Supabase Auth.
30
90
  */
31
91
  const REUSE_MS = 1e4;
92
+ const DEFAULT_TIMEOUT_MS = 5e3;
32
93
  const inflight = /* @__PURE__ */ new Map();
33
94
  const recent = /* @__PURE__ */ new Map();
34
95
  function shared(outcome) {
@@ -38,10 +99,14 @@ function shared(outcome) {
38
99
  } : outcome;
39
100
  }
40
101
  function prune(now) {
41
- for (const [token, entry] of recent) if (entry.until <= now) recent.delete(token);
102
+ for (const [token, entry] of recent) {
103
+ if (entry.until > now) return;
104
+ recent.delete(token);
105
+ }
42
106
  }
43
107
  async function request(refreshToken, options, now) {
44
108
  const doFetch = options.fetch ?? fetch;
109
+ const signal = AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
45
110
  let response;
46
111
  try {
47
112
  response = await doFetch(`${options.url}/auth/v1/token?grant_type=refresh_token`, {
@@ -51,7 +116,8 @@ async function request(refreshToken, options, now) {
51
116
  "content-type": "application/json",
52
117
  ...options.forwardedFor ? { "sb-forwarded-for": options.forwardedFor.ip } : {}
53
118
  },
54
- body: JSON.stringify({ refresh_token: refreshToken })
119
+ body: JSON.stringify({ refresh_token: refreshToken }),
120
+ signal
55
121
  });
56
122
  } catch (cause) {
57
123
  return {
@@ -61,6 +127,11 @@ async function request(refreshToken, options, now) {
61
127
  };
62
128
  }
63
129
  const body = await response.json().catch(() => null);
130
+ if (signal.aborted) return {
131
+ ok: false,
132
+ reason: "network",
133
+ message: "refresh timed out"
134
+ };
64
135
  if (!response.ok || !body || typeof body["access_token"] !== "string" || typeof body["refresh_token"] !== "string") {
65
136
  const message = typeof body?.["msg"] === "string" ? body["msg"] : typeof body?.["error_description"] === "string" ? body["error_description"] : `Refresh failed with ${String(response.status)}`;
66
137
  return {
@@ -94,12 +165,16 @@ function refreshSession(refreshToken, options) {
94
165
  const pending = inflight.get(refreshToken);
95
166
  if (pending) return pending.then(shared);
96
167
  const promise = request(refreshToken, options, now).then((outcome) => {
97
- inflight.delete(refreshToken);
98
- if (outcome.ok) recent.set(refreshToken, {
99
- outcome,
100
- until: now + REUSE_MS
101
- });
168
+ if (outcome.ok || outcome.reason === "rejected") {
169
+ recent.delete(refreshToken);
170
+ recent.set(refreshToken, {
171
+ outcome,
172
+ until: now + REUSE_MS
173
+ });
174
+ }
102
175
  return outcome;
176
+ }).finally(() => {
177
+ inflight.delete(refreshToken);
103
178
  });
104
179
  inflight.set(refreshToken, promise);
105
180
  return promise;
@@ -115,8 +190,15 @@ const AUTH_CACHE_HEADERS = {
115
190
  };
116
191
  /** `sb-<first label of the hostname>-auth-token`, the supabase-js default. */
117
192
  function sessionCookieName(url) {
118
- return `sb-${new URL(url).hostname.split(".")[0] ?? ""}-auth-token`;
193
+ let name = cookieNames.get(url);
194
+ if (name === void 0) {
195
+ name = `sb-${new URL(url).hostname.split(".")[0] ?? ""}-auth-token`;
196
+ if (cookieNames.size < 16) cookieNames.set(url, name);
197
+ }
198
+ return name;
119
199
  }
200
+ /** Project URLs come from configuration, so a handful of entries suffice. */
201
+ const cookieNames = /* @__PURE__ */ new Map();
120
202
  function parseCookies(header) {
121
203
  return header ? parseCookieHeader(header) : [];
122
204
  }
@@ -191,20 +273,9 @@ function applyCookieWrites(cookies, writes) {
191
273
  }
192
274
  //#endregion
193
275
  //#region src/auth/resolve.ts
194
- function decodePayload(token) {
195
- const part = token.split(".")[1];
196
- if (!part) return void 0;
197
- try {
198
- const json = atob(part.replace(/-/g, "+").replace(/_/g, "/"));
199
- const parsed = JSON.parse(json);
200
- return typeof parsed === "object" && parsed !== null ? parsed : void 0;
201
- } catch {
202
- return;
203
- }
204
- }
205
276
  function expiryOf(session) {
206
277
  if (typeof session.expires_at === "number") return session.expires_at;
207
- const exp = decodePayload(session.access_token)?.["exp"];
278
+ const exp = decodeJwtPayload(session.access_token)?.["exp"];
208
279
  return typeof exp === "number" ? exp : void 0;
209
280
  }
210
281
  function serverEnv(options) {
@@ -248,8 +319,31 @@ function remembered(memo, token, now) {
248
319
  memo.set(token, hit);
249
320
  return hit;
250
321
  }
251
- function remember(memo, token, user) {
252
- if (user.expiresAt === null) return;
322
+ const NO_SCHEMA = {};
323
+ const checkedByMemo = /* @__PURE__ */ new WeakMap();
324
+ /**
325
+ * Tokens that already passed (or failed) the claims and `userMetadata`
326
+ * schemas, per verification memo and schema pair, so a memo hit skips
327
+ * validation too.
328
+ */
329
+ function checkedFor(memo, options) {
330
+ const claims = options.claims ?? NO_SCHEMA;
331
+ const metadata = options.userMetadata ?? NO_SCHEMA;
332
+ let byClaims = checkedByMemo.get(memo);
333
+ if (!byClaims) checkedByMemo.set(memo, byClaims = /* @__PURE__ */ new WeakMap());
334
+ let byMetadata = byClaims.get(claims);
335
+ if (!byMetadata) byClaims.set(claims, byMetadata = /* @__PURE__ */ new WeakMap());
336
+ let checked = byMetadata.get(metadata);
337
+ if (!checked) byMetadata.set(metadata, checked = /* @__PURE__ */ new Map());
338
+ return checked;
339
+ }
340
+ function withoutSource(state) {
341
+ if (state.kind !== "user") return state;
342
+ const { source: _source, ...rest } = state;
343
+ return rest;
344
+ }
345
+ function remember(memo, token, user, expiresAt) {
346
+ if (expiresAt === null) return;
253
347
  memo.set(token, user);
254
348
  if (memo.size > MEMO_SIZE) memo.delete(memo.keys().next().value);
255
349
  }
@@ -258,10 +352,20 @@ async function verify(credentials, modes, options, source) {
258
352
  const memo = token ? memoFor(options) : void 0;
259
353
  if (token && memo) {
260
354
  const hit = remembered(memo, token, Math.floor((options.now ?? Date.now)() / 1e3));
261
- if (hit) return checkUser({
262
- ...hit,
263
- source
264
- }, options);
355
+ if (hit) {
356
+ const checked = checkedFor(memo, options);
357
+ const known = checked.get(token);
358
+ if (known) return known.kind === "user" ? {
359
+ ...known,
360
+ source
361
+ } : known;
362
+ const state = await checkUser({
363
+ ...hit,
364
+ source
365
+ }, options);
366
+ remember(checked, token, withoutSource(state), hit.expiresAt);
367
+ return state;
368
+ }
265
369
  }
266
370
  const state = await verifyOnce(credentials, modes, options, source);
267
371
  if (token && memo && state.kind === "user") remember(memo, token, {
@@ -270,12 +374,20 @@ async function verify(credentials, modes, options, source) {
270
374
  claims: state.claims,
271
375
  user: state.user,
272
376
  expiresAt: state.expiresAt
273
- });
274
- return checkUser(state, options);
377
+ }, state.expiresAt);
378
+ const checked = await checkUser(state, options);
379
+ if (token && memo && state.kind === "user") remember(checkedFor(memo, options), token, withoutSource(checked), state.expiresAt);
380
+ return checked;
275
381
  }
276
382
  /** Validates a user's claims against `options.claims`, keeping every JWT claim. */
277
383
  async function checkClaims(state, options) {
278
- if (state.kind !== "user" || !options.claims) return state;
384
+ if (state.kind !== "user") return state;
385
+ if (!actorOf(state.claims).ok) return {
386
+ kind: "invalid",
387
+ reason: "actor",
388
+ error: dbError("unauthorized", "The token's act claim is not a chain of actors with a sub", { code: "ACTOR_INVALID" })
389
+ };
390
+ if (!options.claims) return state;
279
391
  let outcome = options.claims["~standard"].validate(state.claims);
280
392
  if (outcome instanceof Promise) outcome = await outcome;
281
393
  if (outcome.issues) {
@@ -313,16 +425,40 @@ async function checkUser(state, options) {
313
425
  };
314
426
  if (!warnedMetadata.has(schema)) {
315
427
  warnedMetadata.add(schema);
316
- (options.logger ?? consoleLogger).warn("user_metadata does not match sb.userMetadata(schema); session.profile is undefined", { paths: outcome.issues.map((issue) => issue.path?.map((segment) => typeof segment === "object" ? String(segment.key) : String(segment)).join(".") ?? "") });
428
+ (options.logger ?? consoleLogger).warn("user_metadata does not match betterSupabase.userMetadata(schema); session.profile is undefined", { paths: outcome.issues.map((issue) => issue.path?.map((segment) => typeof segment === "object" ? String(segment.key) : String(segment)).join(".") ?? "") });
317
429
  }
318
430
  return user;
319
431
  }
432
+ /** A token whose `kid` matches no key: verifying it loads the JWKS and fails. */
433
+ const PREFETCH_TOKEN = `${base64url(JSON.stringify({
434
+ alg: "ES256",
435
+ kid: "better-supabase-prefetch",
436
+ typ: "JWT"
437
+ }))}.e30.AA`;
438
+ function base64url(text) {
439
+ return btoa(text).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
440
+ }
441
+ /**
442
+ * Loads the remote JWKS into the verifier's cache so the first request
443
+ * doesn't wait for it. Never throws; a failure leaves the first request to
444
+ * fetch it as before.
445
+ */
446
+ function prefetchJwks(options) {
447
+ if (options.jwks !== void 0) return Promise.resolve();
448
+ return verifyCredentials({
449
+ token: PREFETCH_TOKEN,
450
+ apikey: null
451
+ }, {
452
+ auth: ["user"],
453
+ env: serverEnv(options)
454
+ }).then(() => {}, () => {});
455
+ }
320
456
  async function verifyOnce(credentials, modes, options, source) {
321
457
  const { data, error } = await verifyCredentials(credentials, {
322
458
  auth: modes,
323
459
  env: serverEnv(options),
324
- ...options.audience !== void 0 ? { audience: options.audience } : {},
325
- ...options.issuer !== void 0 ? { issuer: options.issuer } : {}
460
+ ...options.audience === void 0 ? {} : { audience: options.audience },
461
+ ...options.issuer === void 0 ? {} : { issuer: options.issuer }
326
462
  });
327
463
  if (error) {
328
464
  const kind = error.code === "JWKS_FETCH_FAILED" ? "network" : error.status >= 500 ? "unexpected" : "unauthorized";
@@ -351,11 +487,16 @@ async function verifyOnce(credentials, modes, options, source) {
351
487
  }
352
488
  function resolution(auth, cookies, writes = []) {
353
489
  const headers = writes.length > 0 ? AUTH_CACHE_HEADERS : {};
490
+ let requestCookies;
354
491
  return {
355
492
  auth,
356
493
  cookies: writes,
357
494
  headers,
358
- requestCookies: writes.length > 0 ? applyCookieWrites(cookies, writes) : cookies,
495
+ /** Parsed on first read: bearer requests rarely need their cookies. */
496
+ get requestCookies() {
497
+ requestCookies ??= writes.length > 0 ? applyCookieWrites(cookies(), writes) : cookies();
498
+ return requestCookies;
499
+ },
359
500
  apply(response) {
360
501
  if (writes.length === 0) return response;
361
502
  let target = response;
@@ -385,7 +526,8 @@ function clientIp(request) {
385
526
  * set, through a single-flight request, and only then are cookies written.
386
527
  */
387
528
  async function resolveAuth(request, options) {
388
- const cookies = parseCookies(request.headers.get("cookie"));
529
+ let parsed;
530
+ const cookies = () => parsed ??= parseCookies(request.headers.get("cookie"));
389
531
  for (const resolver of options.resolvers ?? []) {
390
532
  const state = await resolver.resolve(request);
391
533
  if (!state) continue;
@@ -405,10 +547,10 @@ async function resolveAuth(request, options) {
405
547
  apikey: credentials.apikey
406
548
  }, options.secret === true ? ["secret"] : options.secret.map((name) => `secret:${name}`), options, "bearer"), cookies);
407
549
  const name = options.cookie?.name ?? sessionCookieName(options.env.url);
408
- const session = readSession(cookies, name);
550
+ const session = readSession(cookies(), name);
409
551
  if (!session) {
410
- const stale = cookies.some((cookie) => cookie.name === name || cookie.name.startsWith(`${name}.`));
411
- const writes = stale && options.refresh ? writeSession(cookies, name, null, options.cookie?.options) : [];
552
+ const stale = cookies().some((cookie) => cookie.name === name || cookie.name.startsWith(`${name}.`));
553
+ const writes = stale && options.refresh ? writeSession(cookies(), name, null, options.cookie?.options) : [];
412
554
  return resolution({
413
555
  kind: "anon",
414
556
  reason: stale ? "signed_out" : "none"
@@ -416,12 +558,13 @@ async function resolveAuth(request, options) {
416
558
  }
417
559
  const now = Math.floor((options.now ?? Date.now)() / 1e3);
418
560
  const expiry = expiryOf(session);
419
- if (expiry !== void 0 && expiry - (options.leeway ?? 60) > now) {
561
+ const leeway = options.refresh ? options.leeway ?? 60 : 0;
562
+ if (expiry !== void 0 && expiry - leeway > now) {
420
563
  const state = await verify({
421
564
  token: session.access_token,
422
565
  apikey: null
423
566
  }, ["user"], options, "cookie");
424
- if (state.kind !== "invalid" || state.reason === "claims" || state.error.kind === "network") return resolution(state, cookies);
567
+ if (state.kind !== "invalid" || state.reason !== "token" || state.error.kind === "network") return resolution(state, cookies);
425
568
  if (!options.refresh) return resolution({
426
569
  kind: "anon",
427
570
  reason: "signed_out"
@@ -441,7 +584,8 @@ async function resolveAuth(request, options) {
441
584
  secretKey
442
585
  } } : {},
443
586
  ...options.fetch ? { fetch: options.fetch } : {},
444
- ...options.now ? { now: options.now } : {}
587
+ ...options.now ? { now: options.now } : {},
588
+ ...options.refreshTimeoutMs === void 0 ? {} : { timeoutMs: options.refreshTimeoutMs }
445
589
  });
446
590
  options.onRefresh?.({
447
591
  ok: outcome.ok,
@@ -456,9 +600,9 @@ async function resolveAuth(request, options) {
456
600
  return resolution({
457
601
  kind: "anon",
458
602
  reason: "signed_out"
459
- }, cookies, writeSession(cookies, name, null, options.cookie?.options));
603
+ }, cookies, writeSession(cookies(), name, null, options.cookie?.options));
460
604
  }
461
- const writes = writeSession(cookies, name, outcome.session, options.cookie?.options);
605
+ const writes = writeSession(cookies(), name, outcome.session, options.cookie?.options);
462
606
  return resolution(await verify({
463
607
  token: outcome.session.access_token,
464
608
  apikey: null
@@ -473,8 +617,8 @@ function authContext(auth) {
473
617
  actor: {
474
618
  id: auth.user.id,
475
619
  kind: "user",
476
- ...auth.user.role !== void 0 ? { role: auth.user.role } : {},
477
- ...auth.user.email !== void 0 ? { email: auth.user.email } : {},
620
+ ...auth.user.role === void 0 ? {} : { role: auth.user.role },
621
+ ...auth.user.email === void 0 ? {} : { email: auth.user.email },
478
622
  ...impersonator ? { impersonator: impersonator.id } : {}
479
623
  },
480
624
  claims: auth.claims
@@ -538,4 +682,4 @@ function checkAal(auth, required) {
538
682
  });
539
683
  }
540
684
  //#endregion
541
- export { clientIp as a, applyCookieWrites as c, serializeCookie as d, sessionCookieName as f, impersonatorOf as g, actClaim as h, authContext as i, parseCookies as l, refreshSession as m, amrOf as n, resolveAuth as o, writeSession as p, checkAal as r, AUTH_CACHE_HEADERS as s, aalOf as t, readSession as u };
685
+ export { delegationOf as _, clientIp as a, AUTH_CACHE_HEADERS as c, readSession as d, serializeCookie as f, actorOf as g, refreshSession as h, authContext as i, applyCookieWrites as l, writeSession as m, amrOf as n, prefetchJwks as o, sessionCookieName as p, checkAal as r, resolveAuth as s, aalOf as t, parseCookies as u, actClaim as v, impersonatorOf as y };
@@ -21,13 +21,16 @@ const clamp = (value, min, max) => Math.min(max, Math.max(min, Math.round(value)
21
21
  */
22
22
  function createImageLoader(options) {
23
23
  const storage = `${options.url.replace(/\/+$/, "")}/storage/v1`;
24
- const publicPrefixes = [`${storage}/object/public/`, `${storage}/render/image/public/`];
24
+ const publicPrefixes = [`${storage}/object/public/`, `${storage}/render/image/public/`].map((prefix) => ({
25
+ prefix,
26
+ length: new URL(prefix).pathname.length
27
+ }));
25
28
  const fallback = options.fallback ?? (({ src }) => src);
26
29
  return (props) => {
27
- const prefix = publicPrefixes.find((candidate) => props.src.startsWith(candidate));
30
+ const prefix = publicPrefixes.find((candidate) => props.src.startsWith(candidate.prefix));
28
31
  if (prefix === void 0) return fallback(props);
29
32
  const source = new URL(props.src);
30
- const object = source.pathname.slice(new URL(prefix).pathname.length);
33
+ const object = source.pathname.slice(prefix.length);
31
34
  const target = new URL(`${storage}/render/image/public/${object}`);
32
35
  for (const [key, value] of source.searchParams) target.searchParams.set(key, value);
33
36
  target.searchParams.set("width", String(clamp(props.width, 1, MAX_WIDTH)));
@@ -1,12 +1,12 @@
1
- import { n as AnyModels, t as AnyFunctions } from "../types-C83v5SQz.js";
2
- import { p as DbError } from "../result-CTkCZ6JA.js";
3
- import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-e3zI4_4g.js";
4
- import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-TVia554c.js";
5
- import { r as AuthState } from "../resolve-CjHILk2w.js";
6
- import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions, t as AuthKind } from "../respond-Dne1ARSa.js";
7
- import { n as toSession, t as AuthSession } from "../view-D1s0wa0T.js";
8
- import { r as LiveCountSeed, t as CountRunner } from "../live-DJyZMD6w.js";
9
- import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-Dng-ctuT.js";
1
+ import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
2
+ import { p as DbError } from "../result-CqEsbgYs.js";
3
+ import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-CRS2MRV3.js";
4
+ import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-C7MNQ1Ob.js";
5
+ import { r as AuthState } from "../resolve-BAqQcwMW.js";
6
+ import { d as ContextOptions, f as ServerContext, n as GuardOptions, p as ServerOptions, t as AuthKind, u as BetterServer } from "../respond-DgIbdrt8.js";
7
+ import { n as toSession, t as AuthSession } from "../view-B64o81wp.js";
8
+ import { r as LiveCountSeed, t as CountRunner } from "../live-BHakSWWo.js";
9
+ import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-Dm-FHpNH.js";
10
10
  import { NextRequest } from "next/server.js";
11
11
  import { StandardSchemaV1 } from "@standard-schema/spec";
12
12
  //#region src/next/collector.d.ts
@@ -17,8 +17,8 @@ interface DbBudget {
17
17
  readonly waves?: number;
18
18
  }
19
19
  //#endregion
20
- //#region src/next/index.d.ts
21
- export interface NextOptions extends ServerOptions {
20
+ //#region src/next/create.d.ts
21
+ interface NextOptions extends ServerOptions {
22
22
  /** Invalidate `tagFor(table)` cache tags after mutations. Defaults to true. */
23
23
  readonly cacheTags?: boolean;
24
24
  /** Include internal error messages in Problem Details. Defaults to development only. */
@@ -26,19 +26,19 @@ export interface NextOptions extends ServerOptions {
26
26
  /** Per-request database call counts. See the Budget docs. */
27
27
  readonly debug?: NextDebugOptions;
28
28
  }
29
- export interface NextDebugOptions {
29
+ interface NextDebugOptions {
30
30
  /** Collect outside development too, e.g. for e2e runs against `next start`. */
31
31
  readonly enabled?: boolean;
32
32
  /** Header with `calls;waves;ms` on route handler responses. Defaults to `x-bs-db-calls`. */
33
33
  readonly header?: string;
34
34
  /** Warns in development when one render goes over it. */
35
35
  readonly budget?: DbBudget;
36
- /** Where `next.debugRoute()` is mounted. Defaults to `/api/bs-stats`. */
36
+ /** Where `bs.debugRoute()` is mounted. Defaults to `/api/bs-stats`. */
37
37
  readonly route?: string;
38
38
  }
39
- /** Response header with the URL of the request's totals on `next.debugRoute()`. */
39
+ /** Response header with the URL of the request's totals on `bs.debugRoute()`. */
40
40
  export declare const STATS_URL_HEADER = "x-bs-stats";
41
- export interface ProxyOptions<C = unknown, U = unknown> {
41
+ interface ProxyOptions<C = unknown, P = unknown> {
42
42
  /**
43
43
  * Another middleware to compose with, such as next-intl's. It runs on the
44
44
  * original request, alongside auth; its rewrite, redirect or request
@@ -46,16 +46,16 @@ export interface ProxyOptions<C = unknown, U = unknown> {
46
46
  */
47
47
  readonly before?: (request: NextRequest) => Response | undefined | Promise<Response | undefined>;
48
48
  /** Answer before rendering, e.g. redirect signed-out users. Refreshed cookies are kept. */
49
- readonly protect?: (auth: AuthState<C, U>, request: NextRequest) => Response | undefined | Promise<Response | undefined>;
49
+ readonly protect?: (auth: AuthState<C, P>, request: NextRequest) => Response | undefined | Promise<Response | undefined>;
50
50
  /** Post-processes the final response; return a new one to replace it. */
51
- readonly after?: (response: Response, auth: AuthState<C, U>) => Response | undefined | void | Promise<Response | undefined | void>;
51
+ readonly after?: (response: Response, auth: AuthState<C, P>) => Response | undefined | void | Promise<Response | undefined | void>;
52
52
  /**
53
53
  * Adds `Server-Timing: bs-proxy;dur=..., bs-verify;dur=...` (ms): the whole
54
54
  * proxy, and resolving the session (a local verify, or a refresh).
55
55
  */
56
56
  readonly serverTiming?: boolean;
57
57
  }
58
- export type ActionResult<T> = {
58
+ type ActionResult<T> = {
59
59
  readonly ok: true;
60
60
  readonly data: T;
61
61
  readonly error: null;
@@ -64,7 +64,7 @@ export type ActionResult<T> = {
64
64
  readonly data: null;
65
65
  readonly error: DbError;
66
66
  };
67
- export interface ActionOptions<S extends StandardSchemaV1 | undefined> extends GuardOptions {
67
+ interface ActionOptions<S extends StandardSchemaV1 | undefined> extends GuardOptions {
68
68
  /** Validates the input (plain object or `FormData`) with any Standard Schema. */
69
69
  readonly input?: S;
70
70
  }
@@ -76,57 +76,66 @@ type Unwrapped<T> = T extends {
76
76
  } | {
77
77
  readonly ok: false;
78
78
  } ? D : T;
79
- export interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, U = unknown> extends BetterServer<M, F, E, C, U> {
79
+ interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
80
80
  /** `proxy.ts`: refreshes sessions for page loads and server actions. */
81
- proxy(request: NextRequest, options?: ProxyOptions<C, U>): Promise<Response>;
82
- /** Server Components and actions: the caller's context, memoized per request. */
83
- server(): Promise<ServerContext<M, F, E, C, U>>;
81
+ proxy(request: NextRequest, options?: ProxyOptions<C, P>): Promise<Response>;
82
+ /**
83
+ * The caller's context. Without a request (Server Components, actions) it
84
+ * reads the incoming headers and is memoized per request.
85
+ */
86
+ context(request?: Request, options?: ContextOptions): Promise<ServerContext<M, F, E, C, P>>;
84
87
  /**
85
88
  * The verified caller as serializable data (no token, no clients), memoized
86
89
  * per request. Wrap it in a `'use cache: private'` function with Cache
87
90
  * Components and pass the promise to `<SessionProvider>`.
88
91
  */
89
- session(): Promise<AuthSession<C, U>>;
92
+ session(): Promise<AuthSession<C, P>>;
90
93
  /**
91
94
  * The context for a session and its token, without reading the request.
92
95
  * The token is verified again (memoized, no network call); a token for
93
96
  * another user than `session` gives an `invalid` context.
94
97
  */
95
- serverFor(session: AuthSession<C, U>, options: {
98
+ contextForSession(session: AuthSession<C, P>, options: {
96
99
  readonly token: string | null;
97
- }): Promise<ServerContext<M, F, E, C, U>>;
100
+ }): Promise<ServerContext<M, F, E, C, P>>;
98
101
  /**
99
102
  * First statement of an app-authored `'use cache: private'` function: sets
100
- * `cacheLife` from the session's expiry (`sessionStale`), tags the entry
101
- * `bs:session:<user id>` and returns the caller's context plus `session`.
103
+ * `cacheLife` from the session's expiry (`sessionStale`, capped by
104
+ * `life.stale`), tags the entry `bs:session:<user id>` plus `tags`, and
105
+ * returns the caller's context plus `session`.
102
106
  *
103
107
  * ```ts
104
108
  * async function getCustomers() {
105
109
  * 'use cache: private';
106
- * const { db } = await next.cached();
110
+ * const { db } = await bs.cached();
107
111
  * return db.customers.findMany().orThrow();
108
112
  * }
109
113
  * ```
110
114
  */
111
- cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C, U>>;
112
- /** Drops every `next.cached()` entry of a user, e.g. after a role change. */
113
- invalidateSession(userId: string): void;
115
+ cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C, P>>;
116
+ /**
117
+ * Drops every `bs.cached()` entry of a user, e.g. after a role change,
118
+ * and the entries tagged with `tags` (such as PermDock's `snapshotTag(userId)`).
119
+ */
120
+ invalidateSession(userId: string, options?: {
121
+ readonly tags?: readonly string[];
122
+ }): void;
114
123
  /**
115
124
  * Counts on the server and returns a serializable seed for
116
125
  * `useLiveCount(seed)`, so the badge renders with a number and the client
117
126
  * only refetches on changes. Pass `db` inside `'use cache: private'`
118
- * (from `next.cached()`); otherwise it uses `next.server()`. A failed
127
+ * (from `bs.cached()`); otherwise it uses `bs.context()`. A failed
119
128
  * count gives `count: null` instead of throwing.
120
129
  */
121
130
  liveCount<T extends Extract<keyof M, string>>(spec: QuerySpec<T, "count", number>, db?: CountRunner): Promise<LiveCountSeed<T>>;
122
131
  /** Route handler with auth, Result unwrapping and Problem Details errors. */
123
- route<P = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C, U> & {
124
- readonly params: P;
132
+ route<Params = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C, P> & {
133
+ readonly params: Params;
125
134
  }) => unknown, options?: GuardOptions): (request: NextRequest, segment: {
126
- readonly params: Promise<P>;
135
+ readonly params: Promise<Params>;
127
136
  }) => Promise<Response>;
128
137
  /** Server action returning a serializable `ActionResult`. */
129
- action<S extends StandardSchemaV1 | undefined, T>(options: ActionOptions<S>, fn: (input: ActionParsed<S>, ctx: ServerContext<M, F, E, C, U>) => Promise<T> | T): (input: ActionInput<S>) => Promise<ActionResult<Unwrapped<Awaited<T>>>>;
138
+ action<S extends StandardSchemaV1 | undefined, T>(options: ActionOptions<S>, fn: (input: ActionParsed<S>, ctx: ServerContext<M, F, E, C, P>) => Promise<T> | T): (input: ActionInput<S>) => Promise<ActionResult<Unwrapped<Awaited<T>>>>;
130
139
  /** Tags the current `"use cache"` scope with a table (and row) tag. */
131
140
  cacheTag(table: Extract<keyof M, string>, id?: string | number): void;
132
141
  /**
@@ -141,7 +150,7 @@ export interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C =
141
150
  */
142
151
  debugRoute(): (request: Request) => Promise<Response>;
143
152
  }
144
- export interface SessionStaleOptions {
153
+ interface SessionStaleOptions {
145
154
  /** Below this, the result would not be prefetched anyway. Defaults to 30. */
146
155
  readonly min?: number;
147
156
  /** 300 (5 minutes) joins the route's App Shell. Defaults to 300. */
@@ -152,17 +161,24 @@ export interface SessionStaleOptions {
152
161
  * the token's expiry, and at least `min`.
153
162
  */
154
163
  export declare function sessionStale(session: AuthSession, options?: SessionStaleOptions, now?: number): number;
155
- export interface CachedOptions {
156
- /** `cacheLife` for the entry. `stale` comes from `sessionStale`. */
164
+ interface CachedOptions {
165
+ /**
166
+ * `cacheLife` for the entry. `stale` is `sessionStale`, and never more than
167
+ * `stale` when it is set: pass PermDock's `cacheLifeFor(snapshot)` so the
168
+ * entry goes stale with the snapshot too.
169
+ */
157
170
  readonly life?: SessionStaleOptions & {
171
+ readonly stale?: number;
158
172
  readonly revalidate?: number;
159
173
  readonly expire?: number;
160
174
  };
175
+ /** More `cacheTag`s for the entry, e.g. `snapshotTag(sub)` or `org:<id>`. */
176
+ readonly tags?: readonly string[];
161
177
  }
162
- export type CachedContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, U = unknown> = ServerContext<M, F, E, C, U> & {
163
- readonly session: AuthSession<C, U>;
178
+ type CachedContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> = ServerContext<M, F, E, C, P> & {
179
+ readonly session: AuthSession<C, P>;
164
180
  };
165
- /** The tag `next.cached()` puts on a user's entries. */
181
+ /** The tag `bs.cached()` puts on a user's entries. */
166
182
  export declare function sessionTag(userId: string): string;
167
183
  /** `bs:<table>` or `bs:<table>:<id>`. Mutations invalidate both. */
168
184
  export declare function tagFor(table: string, id?: string | number): string;
@@ -172,7 +188,7 @@ export declare function tagFor(table: string, id?: string | number): string;
172
188
  * `cacheTags: false`.
173
189
  */
174
190
  export declare function nextCache(): CacheAdapter;
175
- export interface RequireAalOptions {
191
+ interface RequireAalOptions {
176
192
  /**
177
193
  * Page that enrolls or challenges a second factor. Signed-in users below
178
194
  * the level are redirected there with `?next=<path>`; without it they get
@@ -189,7 +205,7 @@ export interface RequireAalOptions {
189
205
  * combine it with your sign-in redirect.
190
206
  *
191
207
  * ```ts
192
- * next.proxy(request, {
208
+ * bs.proxy(request, {
193
209
  * protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
194
210
  * });
195
211
  * ```
@@ -202,9 +218,9 @@ export declare function shouldRefresh(request: Request): boolean;
202
218
  * actions and cache tags, on top of `createServer`.
203
219
  *
204
220
  * ```ts
205
- * export const next = createNext(sb);
221
+ * export const bs = createNext(betterSupabase);
206
222
  * ```
207
223
  */
208
- export declare function createNext<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, U = unknown>(sb: BetterSupabase<M, D, F, E, C, U>, options?: NextOptions): BetterNext<M, F, E, C, U>;
224
+ export declare function createNext<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options?: NextOptions): BetterNext<M, F, E, C, P>;
209
225
  //#endregion
210
- export { type Aal, type AmrEntry, type AuthKind, type AuthSession, type DbBudget, type DbStats, type EntitlementKey, type GuardOptions, type Impersonator, type MembershipClaim, hasEntitlement, toSession };
226
+ export { type Aal, type ActionOptions, type ActionResult, type AmrEntry, type AuthKind, type AuthSession, type BetterNext, type CachedContext, type CachedOptions, type DbBudget, type DbStats, type EntitlementKey, type GuardOptions, type Impersonator, type LiveCountSeed, type MembershipClaim, type NextDebugOptions, type NextOptions, type ProxyOptions, type RequireAalOptions, type SessionStaleOptions, hasEntitlement, toSession };