better-supabase 0.3.0 → 0.5.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 (211) hide show
  1. package/README.md +9 -6
  2. package/bin/better-supabase.js +4 -0
  3. package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
  4. package/dist/channel-FQW-CnGR.d.ts +70 -0
  5. package/dist/claims-BAf6c7is.js +48 -0
  6. package/dist/cli/_format-Cax5EsM9.js +101 -0
  7. package/dist/cli/bin.d.ts +1 -0
  8. package/dist/cli/bin.js +48 -0
  9. package/dist/cli/codemod-DqV7612m.js +388 -0
  10. package/dist/cli/command-CK14fP91.js +502 -0
  11. package/dist/cli/compare-BmIWVEui.js +9 -0
  12. package/dist/cli/config-CPVpXP5I.js +1965 -0
  13. package/dist/cli/config-dtoekM0v.js +2 -0
  14. package/dist/cli/db-DOBPt660.js +52 -0
  15. package/dist/cli/diff-SjkO5rNr.js +752 -0
  16. package/dist/cli/dist-DBPdlPYK.js +1112 -0
  17. package/dist/cli/dist-DreZ24Sz.js +129 -0
  18. package/dist/cli/dist-xn4wlboc.js +732 -0
  19. package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
  20. package/dist/cli/env-DmKv_G4A.js +108 -0
  21. package/dist/cli/env-jIFUADLY.js +511 -0
  22. package/dist/cli/errors-4v4DTtda.js +56 -0
  23. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  24. package/dist/cli/gen-BJc9vdyu.js +2 -0
  25. package/dist/cli/gen-C2osRcr5.js +2204 -0
  26. package/dist/cli/index.d.ts +493 -0
  27. package/dist/cli/index.js +184 -0
  28. package/dist/cli/init-Saf5-R9T.js +550 -0
  29. package/dist/cli/introspect--H7oGmcd.js +81 -0
  30. package/dist/cli/io-k41_z-jT.js +24 -0
  31. package/dist/cli/json5-ChRycquj.js +787 -0
  32. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  33. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  34. package/dist/cli/keys-ByKHnvP9.js +83 -0
  35. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  36. package/dist/cli/openapi-Cp5Fyta-.js +66 -0
  37. package/dist/cli/project-B_GeXyHh.js +70 -0
  38. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  39. package/dist/cli/prompts-BTYQGunc.js +2 -0
  40. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  41. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  42. package/dist/cli/run-BKR2W_79.js +351 -0
  43. package/dist/cli/seed-CUlqWh-8.js +98 -0
  44. package/dist/cli/skills-LJJIhLFR.js +137 -0
  45. package/dist/cli/sql-DzPbfngx.js +348 -0
  46. package/dist/cli/style-C0NFdJRL.js +29 -0
  47. package/dist/cli/toml-DB-MNnFs.js +424 -0
  48. package/dist/cli/version-BDJuO0UR.js +4 -0
  49. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  50. package/dist/client/index.d.ts +2 -2
  51. package/dist/client/index.js +15 -22
  52. package/dist/clone-BfgmLU90.js +21 -0
  53. package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  54. package/dist/config/index.d.ts +4 -4
  55. package/dist/config/index.js +5 -5
  56. package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
  57. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  58. package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
  59. package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
  60. package/dist/edge/index.d.ts +20 -13
  61. package/dist/edge/index.js +55 -26
  62. package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
  63. package/dist/env/index.d.ts +2 -2
  64. package/dist/env/index.js +34 -1
  65. package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
  66. package/dist/events/index.d.ts +3 -2
  67. package/dist/events/index.js +69 -16
  68. package/dist/events-aIO7vsIk.d.ts +230 -0
  69. package/dist/executor-BunPOO-k.d.ts +402 -0
  70. package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  71. package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
  72. package/dist/hono/index.d.ts +27 -23
  73. package/dist/hono/index.js +46 -11
  74. package/dist/impersonation-VfaGc4Q_.js +41 -0
  75. package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
  76. package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
  77. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  78. package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
  79. package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
  80. package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
  81. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  82. package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
  83. package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
  84. package/dist/index.d.ts +34 -17
  85. package/dist/index.js +15 -14
  86. package/dist/jobs/index.d.ts +147 -95
  87. package/dist/jobs/index.js +773 -250
  88. package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
  89. package/dist/kit-events-CTFDfSwb.js +60 -0
  90. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  91. package/dist/kit-transport-DJlHQUBU.js +55 -0
  92. package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
  93. package/dist/list/index.d.ts +1 -1
  94. package/dist/list/index.js +3 -3
  95. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  96. package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
  97. package/dist/lookup-BaP5bI27.js +18 -0
  98. package/dist/mcp/index.d.ts +39 -22
  99. package/dist/mcp/index.js +39 -25
  100. package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  101. package/dist/next/image/index.js +6 -3
  102. package/dist/next/index.d.ts +60 -36
  103. package/dist/next/index.js +167 -50
  104. package/dist/notifications/index.d.ts +162 -0
  105. package/dist/notifications/index.js +264 -0
  106. package/dist/openapi/index.d.ts +1 -1
  107. package/dist/openapi/index.js +6 -6
  108. package/dist/orgs/index.d.ts +132 -0
  109. package/dist/orgs/index.js +245 -0
  110. package/dist/orpc/index.d.ts +37 -20
  111. package/dist/orpc/index.js +24 -11
  112. package/dist/otel/index.d.ts +24 -5
  113. package/dist/otel/index.js +124 -64
  114. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  115. package/dist/permdock-sql-DgOfeixi.js +51 -0
  116. package/dist/plugin-DwARMuQp.js +59 -0
  117. package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
  118. package/dist/plugins/actor/index.d.ts +5 -2
  119. package/dist/plugins/actor/index.js +22 -8
  120. package/dist/plugins/rules/index.d.ts +2 -2
  121. package/dist/plugins/rules/index.js +52 -17
  122. package/dist/plugins/soft-delete/index.d.ts +3 -3
  123. package/dist/plugins/soft-delete/index.js +14 -5
  124. package/dist/plugins/tenant/index.d.ts +3 -3
  125. package/dist/plugins/tenant/index.js +45 -21
  126. package/dist/plugins/timestamps/index.d.ts +1 -1
  127. package/dist/plugins/timestamps/index.js +4 -3
  128. package/dist/plugins/validation/index.d.ts +2 -2
  129. package/dist/plugins/validation/index.js +26 -6
  130. package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
  131. package/dist/postgres/index.d.ts +5 -5
  132. package/dist/postgres/index.js +20 -10
  133. package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  134. package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
  135. package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
  136. package/dist/query/index.d.ts +2 -2
  137. package/dist/query/index.js +3 -3
  138. package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
  139. package/dist/queue-BYUCjr59.d.ts +252 -0
  140. package/dist/react/index.d.ts +8 -7
  141. package/dist/react/index.js +4 -3
  142. package/dist/react/server.d.ts +16 -5
  143. package/dist/react/server.js +12 -4
  144. package/dist/react/session.d.ts +10 -4
  145. package/dist/react/session.js +11 -3
  146. package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
  147. package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
  148. package/dist/realtime/index.d.ts +3 -3
  149. package/dist/realtime/index.js +14 -12
  150. package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
  151. package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
  152. package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
  153. package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
  154. package/dist/respond-D2OQl62-.d.ts +371 -0
  155. package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
  156. package/dist/server/index.d.ts +17 -15
  157. package/dist/server/index.js +16 -22
  158. package/dist/server-Csx2sRjg.js +655 -0
  159. package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
  160. package/dist/signers-Cc6r98SW.d.ts +162 -0
  161. package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
  162. package/dist/sql/index.d.ts +247 -10
  163. package/dist/sql/index.js +8591 -780
  164. package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
  165. package/dist/ssr/index.d.ts +4 -4
  166. package/dist/ssr/index.js +1 -2
  167. package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
  168. package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  169. package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
  170. package/dist/storage/index.d.ts +4 -4
  171. package/dist/storage/index.js +174 -27
  172. package/dist/support-DT-OhSxD.d.ts +88 -0
  173. package/dist/support-Dk6vdmEw.js +291 -0
  174. package/dist/support-view-Da5peKFg.d.ts +17 -0
  175. package/dist/support-view-g4YLBeqX.js +18 -0
  176. package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
  177. package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
  178. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  179. package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
  180. package/dist/testing/index.d.ts +113 -24
  181. package/dist/testing/index.js +375 -47
  182. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  183. package/dist/user-change-CHFEUro1.js +49 -0
  184. package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
  185. package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
  186. package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
  187. package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
  188. package/dist/webhooks/index.d.ts +136 -5
  189. package/dist/webhooks/index.js +588 -2
  190. package/dist/wire-D2dT86vi.js +35 -0
  191. package/package.json +50 -10
  192. package/schemas/config-v1.json +131 -1
  193. package/schemas/doctor-report-v1.json +10 -0
  194. package/skills/better-supabase/SKILL.md +60 -10
  195. package/skills/better-supabase/references/plugins.md +13 -14
  196. package/skills/better-supabase/references/troubleshooting.md +25 -4
  197. package/skills/better-supabase-api/SKILL.md +74 -19
  198. package/skills/better-supabase-api/references/adapters.md +48 -27
  199. package/skills/better-supabase-auth/SKILL.md +149 -0
  200. package/skills/better-supabase-auth/references/permdock.md +100 -0
  201. package/skills/better-supabase-testing/SKILL.md +58 -4
  202. package/dist/claims-CoTobNC-.js +0 -25
  203. package/dist/events-Do8jXh4D.d.ts +0 -84
  204. package/dist/executor-DynmIWhq.d.ts +0 -195
  205. package/dist/invalidate-DbxuL2jL.js +0 -18
  206. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  207. package/dist/plugin-BqR6wKMB.js +0 -23
  208. package/dist/respond-C44EZAJo.js +0 -399
  209. package/dist/respond-DAkPIdTH.d.ts +0 -175
  210. package/dist/result-c2OYgCV2.d.ts +0 -203
  211. package/dist/view-wZ-9qIUz.js +0 -45
package/dist/mcp/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { n as dbError } from "../errors-Bl3rkOzo.js";
2
- import { t as base64ToText } from "../base64-CQeB1eSf.js";
3
- import { t as validate } from "../standard-FUi3Z3-7.js";
4
- import { o as toProblem } from "../problem-CGt1ADix.js";
1
+ import { n as dbError } from "../errors-DtwXa6b8.js";
2
+ import { t as base64ToText } from "../base64-D2gsioCY.js";
3
+ import { t as validate } from "../standard-D7r6dMUu.js";
4
+ import { a as problemResponse, o as toProblem } from "../problem-BW1KhzSG.js";
5
5
  import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
6
- import { t as buildJsonSchema } from "../json-schema--aOXAGfU.js";
7
- import { a as settle, c as withExtra, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-C44EZAJo.js";
8
- import { t as defineResource } from "../resource-D30rp0GN.js";
6
+ import { t as buildJsonSchema } from "../json-schema-m1fiwKgN.js";
7
+ import { a as withExtra, d as guard, g as jsonResponse, h as jsonReplacer, m as settle, n as createServer, r as extendServer, u as defaultExpose } from "../server-Csx2sRjg.js";
8
+ import { t as defineResource } from "../resource-CRxxwkQq.js";
9
9
  import { fromSupabaseUrl, unauthorizedResponse } from "@supabase/server/oauth-protected-resource";
10
10
  //#region src/mcp/mcp.ts
11
11
  const MCP_PROTOCOL_VERSION = SPEC_PINS.mcp;
@@ -215,7 +215,7 @@ function textResult(value, isError = false) {
215
215
  return {
216
216
  content: [{
217
217
  type: "text",
218
- text: typeof value === "string" ? value : JSON.stringify(value ?? null)
218
+ text: typeof value === "string" ? value : JSON.stringify(value ?? null, jsonReplacer)
219
219
  }],
220
220
  ...structured ? { structuredContent: structured } : {},
221
221
  ...isError ? { isError: true } : {}
@@ -231,17 +231,19 @@ function canonical(request) {
231
231
  * through RLS. Answers 401 with RFC 9728 metadata so clients can sign in with
232
232
  * Supabase Auth's OAuth server.
233
233
  */
234
- function createMcp(sb, options) {
235
- const server = createServer(sb, options);
234
+ function createMcp(betterSupabase, options) {
235
+ const server = createServer(betterSupabase, options);
236
236
  const expose = options.exposeErrors ?? defaultExpose();
237
237
  const defs = buildJsonSchema({
238
- meta: sb.meta,
238
+ meta: betterSupabase.meta,
239
239
  config: { json: options.json ?? {} }
240
240
  })["$defs"];
241
241
  const registry = /* @__PURE__ */ new Map();
242
+ let allTools;
242
243
  const register = (entry) => {
243
244
  if (registry.has(entry.info.name)) throw new TypeError(`Duplicate MCP tool "${entry.info.name}"`);
244
245
  registry.set(entry.info.name, entry);
246
+ allTools = void 0;
245
247
  };
246
248
  const failure = (error) => textResult(toProblem(error, { expose }), true);
247
249
  const refused = async (tool, args, ctx) => {
@@ -256,11 +258,14 @@ function createMcp(sb, options) {
256
258
  const isVisible = async (tool, ctx) => !options.visible || options.visible(ctx, tool);
257
259
  for (const [table, raw] of Object.entries(options.resources ?? {})) {
258
260
  if (!raw) continue;
259
- const resource = defineResource(sb, table, raw === true ? {} : raw);
261
+ const key = table;
262
+ const entry = raw;
263
+ const { meta, ...route } = entry === true ? {} : entry;
264
+ const resource = defineResource(betterSupabase, key, route);
260
265
  for (const { info, operation } of tableTools(resource, defs)) {
261
266
  const ref = {
262
267
  info,
263
- meta: void 0
268
+ meta: meta?.[operation]
264
269
  };
265
270
  register({
266
271
  ...ref,
@@ -302,8 +307,11 @@ function createMcp(sb, options) {
302
307
  });
303
308
  };
304
309
  for (const tool of options.tools ?? []) addTool(tool);
305
- const listTools = () => [...registry.values()].map((entry) => entry.info);
306
- const visibleTools = async (ctx) => {
310
+ const listTools = () => allTools ??= [...registry.values()].map((entry) => entry.info);
311
+ /** Without a `visible` hook every caller sees the same list, built once. */
312
+ const visibleTools = async (context) => {
313
+ if (!options.visible) return listTools();
314
+ const ctx = context();
307
315
  const entries = [...registry.values()];
308
316
  const shown = await Promise.all(entries.map((entry) => isVisible(entry, ctx)));
309
317
  return entries.filter((_, index) => shown[index]).map((e) => e.info);
@@ -333,7 +341,7 @@ function createMcp(sb, options) {
333
341
  const outcome = await invoke(name, args, ctx);
334
342
  return isRefusal(outcome) ? failure(dbError("forbidden", outcome.refusal)) : outcome;
335
343
  };
336
- const scopes = (options.scopes ?? []).filter((scope) => scope !== "offline_access");
344
+ const scopes = (options.advertisedScopes ?? options.scopes ?? []).filter((scope) => scope !== "offline_access");
337
345
  const serverInfo = {
338
346
  name: options.name,
339
347
  version: options.version,
@@ -357,6 +365,12 @@ function createMcp(sb, options) {
357
365
  response.headers.set("www-authenticate", challenge);
358
366
  return response;
359
367
  };
368
+ /** A missing second factor is no scope problem, and an outage is not the caller's fault. */
369
+ const refuse = (request, denied) => {
370
+ if (denied.kind === "unauthorized") return unauthorized(request);
371
+ if (denied.kind !== "forbidden") return problemResponse(denied, { expose });
372
+ return denied.code === "INSUFFICIENT_AAL" ? rpcError(null, INVALID_REQUEST, denied.message, 403) : forbidden(request, denied.message, denied.scopes);
373
+ };
360
374
  const unsupportedVersion = (id, requested) => rpcError(id, UNSUPPORTED_PROTOCOL_VERSION, "Unsupported protocol version", 400, {
361
375
  supported: [...SUPPORTED_VERSIONS],
362
376
  requested
@@ -376,7 +390,7 @@ function createMcp(sb, options) {
376
390
  if (sent === null || headerName(sent) !== name) return mismatch(`Mcp-Name must be ${name}`);
377
391
  }
378
392
  };
379
- const handler = async (request) => {
393
+ const endpoint = async (request) => {
380
394
  const origin = request.headers.get("origin");
381
395
  if (origin && options.allowedOrigins && !options.allowedOrigins.includes(origin)) return rpcError(null, INVALID_REQUEST, "Origin not allowed", 403);
382
396
  if (options.allowedHosts && !hostAllowed(request, options.allowedHosts)) return rpcError(null, INVALID_REQUEST, "Host not allowed", 403);
@@ -386,9 +400,9 @@ function createMcp(sb, options) {
386
400
  });
387
401
  const header = request.headers.get("mcp-protocol-version");
388
402
  if (header && !SUPPORTED_VERSIONS.includes(header)) return unsupportedVersion(null, header);
389
- const ctx = await server.context(request);
390
- const denied = guard(ctx.auth, options.allow, options.aal);
391
- if (denied) return denied.kind === "unauthorized" ? unauthorized(request) : forbidden(request, denied.message);
403
+ const ctx = await server.context(request, { cookies: false });
404
+ const denied = guard(ctx.auth, options.allow, options.aal, options.requiredScopes);
405
+ if (denied) return refuse(request, denied);
392
406
  let message;
393
407
  try {
394
408
  message = await request.json();
@@ -407,7 +421,7 @@ function createMcp(sb, options) {
407
421
  request,
408
422
  signal: request.signal
409
423
  });
410
- const reply = (result) => Response.json({
424
+ const reply = (result) => jsonResponse({
411
425
  jsonrpc: "2.0",
412
426
  id: message.id,
413
427
  result: modern ? {
@@ -437,7 +451,7 @@ function createMcp(sb, options) {
437
451
  }
438
452
  case "ping": return modern ? notFound() : reply({});
439
453
  case "tools/list": return reply({
440
- tools: await visibleTools(toolContext()),
454
+ tools: await visibleTools(toolContext),
441
455
  ...modern ? {
442
456
  ttlMs: LIST_TTL_MS,
443
457
  cacheScope: "private"
@@ -465,14 +479,14 @@ function createMcp(sb, options) {
465
479
  }, { headers: { "access-control-allow-origin": "*" } });
466
480
  const mcp = extendServer(server, {
467
481
  get tools() {
468
- return listTools();
482
+ return [...listTools()];
469
483
  },
470
484
  tool(definition) {
471
485
  addTool(defineTool(definition));
472
486
  return mcp;
473
487
  },
474
488
  call,
475
- handler,
489
+ endpoint,
476
490
  metadata,
477
491
  fetch(request) {
478
492
  const { pathname } = new URL(request.url);
@@ -483,7 +497,7 @@ function createMcp(sb, options) {
483
497
  "access-control-allow-methods": "GET"
484
498
  }
485
499
  }) : metadata(request));
486
- return handler(request);
500
+ return endpoint(request);
487
501
  }
488
502
  });
489
503
  return mcp;
@@ -1,9 +1,13 @@
1
- import { p as DbError } from "./result-c2OYgCV2.js";
1
+ import { _ as DbError } from "./executor-BunPOO-k.js";
2
2
  //#region src/auth/impersonation.d.ts
3
3
  /** The admin acting as the user, from the RFC 8693 `act` claim. */
4
4
  interface Impersonator {
5
5
  readonly id: string;
6
6
  readonly reason?: string;
7
+ /** The support session, when the admin started one (`act.session_id`). */
8
+ readonly sessionId?: string;
9
+ /** Whether the support session blocks writes (`act.read_only`). */
10
+ readonly readOnly?: boolean;
7
11
  }
8
12
  /** `actingAs(userId, claims, { actor, reason })`: who acts, and why (recorded by the audit module). */
9
13
  interface ImpersonationOptions {
@@ -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,13 @@
1
- import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
2
- import { p as DbError } from "../result-c2OYgCV2.js";
3
- import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-DUbydmJa.js";
4
- import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-x3V3vGh-.js";
5
- import { r as AuthState } from "../resolve-BRciX2jK.js";
6
- import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions, t as AuthKind } from "../respond-DAkPIdTH.js";
7
- import { n as toSession, t as AuthSession } from "../view-3Po_PVlr.js";
8
- import { r as LiveCountSeed, t as CountRunner } from "../live-QE3UPbGm.js";
9
- import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-DvJXoI02.js";
1
+ import { n as AnyFunctions, r as AnyModels } from "../types-Dnf_wRDz.js";
2
+ import { _ as DbError } from "../executor-BunPOO-k.js";
3
+ import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-Bp_BZa5h.js";
4
+ import { r as AuthState } from "../resolve-C15TJq77.js";
5
+ import { C as SupportOptions, D as supportSessions, E as SupportStartRequest, T as SupportSessions, b as ActiveSupport, d as ContextOptions, f as ServerContext, n as GuardOptions, p as ServerOptions, t as AuthKind, u as BetterServer, w as SupportPolicy } from "../respond-D2OQl62-.js";
6
+ import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-CusDgPaD.js";
7
+ import { n as toSession, t as AuthSession } from "../view-BEf0diC5.js";
8
+ import { r as LiveCountSeed, t as CountRunner } from "../live-D9W2M5aO.js";
9
+ import { n as supportOf, t as SupportView } from "../support-view-Da5peKFg.js";
10
+ import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-DUxU4oHe.js";
10
11
  import { NextRequest } from "next/server.js";
11
12
  import { StandardSchemaV1 } from "@standard-schema/spec";
12
13
  //#region src/next/collector.d.ts
@@ -33,12 +34,12 @@ interface NextDebugOptions {
33
34
  readonly header?: string;
34
35
  /** Warns in development when one render goes over it. */
35
36
  readonly budget?: DbBudget;
36
- /** Where `next.debugRoute()` is mounted. Defaults to `/api/bs-stats`. */
37
+ /** Where `bs.debugRoute()` is mounted. Defaults to `/api/bs-stats`. */
37
38
  readonly route?: string;
38
39
  }
39
- /** Response header with the URL of the request's totals on `next.debugRoute()`. */
40
+ /** Response header with the URL of the request's totals on `bs.debugRoute()`. */
40
41
  export declare const STATS_URL_HEADER = "x-bs-stats";
41
- interface ProxyOptions<C = unknown, U = unknown> {
42
+ interface ProxyOptions<C = unknown, P = unknown> {
42
43
  /**
43
44
  * Another middleware to compose with, such as next-intl's. It runs on the
44
45
  * original request, alongside auth; its rewrite, redirect or request
@@ -46,9 +47,9 @@ interface ProxyOptions<C = unknown, U = unknown> {
46
47
  */
47
48
  readonly before?: (request: NextRequest) => Response | undefined | Promise<Response | undefined>;
48
49
  /** 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>;
50
+ readonly protect?: (auth: AuthState<C, P>, request: NextRequest) => Response | undefined | Promise<Response | undefined>;
50
51
  /** 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>;
52
+ readonly after?: (response: Response, auth: AuthState<C, P>) => Response | undefined | void | Promise<Response | undefined | void>;
52
53
  /**
53
54
  * Adds `Server-Timing: bs-proxy;dur=..., bs-verify;dur=...` (ms): the whole
54
55
  * proxy, and resolving the session (a local verify, or a refresh).
@@ -76,25 +77,28 @@ type Unwrapped<T> = T extends {
76
77
  } | {
77
78
  readonly ok: false;
78
79
  } ? D : T;
79
- interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, U = unknown> extends BetterServer<M, F, E, C, U> {
80
+ interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
80
81
  /** `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>>;
82
+ proxy(request: NextRequest, options?: ProxyOptions<C, P>): Promise<Response>;
83
+ /**
84
+ * The caller's context. Without a request (Server Components, actions) it
85
+ * reads the incoming headers and is memoized per request.
86
+ */
87
+ context(request?: Request, options?: ContextOptions): Promise<ServerContext<M, F, E, C, P>>;
84
88
  /**
85
89
  * The verified caller as serializable data (no token, no clients), memoized
86
90
  * per request. Wrap it in a `'use cache: private'` function with Cache
87
91
  * Components and pass the promise to `<SessionProvider>`.
88
92
  */
89
- session(): Promise<AuthSession<C, U>>;
93
+ session(): Promise<AuthSession<C, P>>;
90
94
  /**
91
95
  * The context for a session and its token, without reading the request.
92
96
  * The token is verified again (memoized, no network call); a token for
93
97
  * another user than `session` gives an `invalid` context.
94
98
  */
95
- serverFor(session: AuthSession<C, U>, options: {
99
+ contextForSession(session: AuthSession<C, P>, options: {
96
100
  readonly token: string | null;
97
- }): Promise<ServerContext<M, F, E, C, U>>;
101
+ }): Promise<ServerContext<M, F, E, C, P>>;
98
102
  /**
99
103
  * First statement of an app-authored `'use cache: private'` function: sets
100
104
  * `cacheLife` from the session's expiry (`sessionStale`, capped by
@@ -104,14 +108,14 @@ interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown
104
108
  * ```ts
105
109
  * async function getCustomers() {
106
110
  * 'use cache: private';
107
- * const { db } = await next.cached();
111
+ * const { db } = await bs.cached();
108
112
  * return db.customers.findMany().orThrow();
109
113
  * }
110
114
  * ```
111
115
  */
112
- cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C, U>>;
116
+ cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C, P>>;
113
117
  /**
114
- * Drops every `next.cached()` entry of a user, e.g. after a role change,
118
+ * Drops every `bs.cached()` entry of a user, e.g. after a role change,
115
119
  * and the entries tagged with `tags` (such as PermDock's `snapshotTag(userId)`).
116
120
  */
117
121
  invalidateSession(userId: string, options?: {
@@ -121,18 +125,18 @@ interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown
121
125
  * Counts on the server and returns a serializable seed for
122
126
  * `useLiveCount(seed)`, so the badge renders with a number and the client
123
127
  * only refetches on changes. Pass `db` inside `'use cache: private'`
124
- * (from `next.cached()`); otherwise it uses `next.server()`. A failed
128
+ * (from `bs.cached()`); otherwise it uses `bs.context()`. A failed
125
129
  * count gives `count: null` instead of throwing.
126
130
  */
127
131
  liveCount<T extends Extract<keyof M, string>>(spec: QuerySpec<T, "count", number>, db?: CountRunner): Promise<LiveCountSeed<T>>;
128
132
  /** Route handler with auth, Result unwrapping and Problem Details errors. */
129
- route<P = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C, U> & {
130
- readonly params: P;
133
+ route<Params = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C, P> & {
134
+ readonly params: Params;
131
135
  }) => unknown, options?: GuardOptions): (request: NextRequest, segment: {
132
- readonly params: Promise<P>;
136
+ readonly params: Promise<Params>;
133
137
  }) => Promise<Response>;
134
138
  /** Server action returning a serializable `ActionResult`. */
135
- 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>>>>;
139
+ 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>>>>;
136
140
  /** Tags the current `"use cache"` scope with a table (and row) tag. */
137
141
  cacheTag(table: Extract<keyof M, string>, id?: string | number): void;
138
142
  /**
@@ -146,7 +150,27 @@ interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown
146
150
  * `expectDbBudget`. Answers 404 unless `debug` is on.
147
151
  */
148
152
  debugRoute(): (request: Request) => Promise<Response>;
153
+ /**
154
+ * Starts a support session for the signed-in admin and sets its cookie,
155
+ * so the admin's next requests render as the target. Call it from a
156
+ * server action.
157
+ */
158
+ startSupport(request: SupportStartRequest): Promise<ActionResult<SupportStarted>>;
159
+ /** Ends the admin's support session and clears its cookie. Call it from a server action. */
160
+ stopSupport(): Promise<ActionResult<{
161
+ readonly ended: boolean;
162
+ }>>;
163
+ }
164
+ /** What `startSupport` returns: plain data, safe across the action boundary. */
165
+ interface SupportStarted {
166
+ readonly sessionId: string;
167
+ readonly targetUserId: string;
168
+ readonly readOnly: boolean;
169
+ /** ISO 8601. */
170
+ readonly expiresAt: string;
149
171
  }
172
+ /** The tag `bs.cached()` adds to entries rendered in a support session. */
173
+ export declare function supportTag(sessionId: string): string;
150
174
  interface SessionStaleOptions {
151
175
  /** Below this, the result would not be prefetched anyway. Defaults to 30. */
152
176
  readonly min?: number;
@@ -172,10 +196,10 @@ interface CachedOptions {
172
196
  /** More `cacheTag`s for the entry, e.g. `snapshotTag(sub)` or `org:<id>`. */
173
197
  readonly tags?: readonly string[];
174
198
  }
175
- type CachedContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, U = unknown> = ServerContext<M, F, E, C, U> & {
176
- readonly session: AuthSession<C, U>;
199
+ type CachedContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> = ServerContext<M, F, E, C, P> & {
200
+ readonly session: AuthSession<C, P>;
177
201
  };
178
- /** The tag `next.cached()` puts on a user's entries. */
202
+ /** The tag `bs.cached()` puts on a user's entries. */
179
203
  export declare function sessionTag(userId: string): string;
180
204
  /** `bs:<table>` or `bs:<table>:<id>`. Mutations invalidate both. */
181
205
  export declare function tagFor(table: string, id?: string | number): string;
@@ -202,7 +226,7 @@ interface RequireAalOptions {
202
226
  * combine it with your sign-in redirect.
203
227
  *
204
228
  * ```ts
205
- * next.proxy(request, {
229
+ * bs.proxy(request, {
206
230
  * protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
207
231
  * });
208
232
  * ```
@@ -215,9 +239,9 @@ export declare function shouldRefresh(request: Request): boolean;
215
239
  * actions and cache tags, on top of `createServer`.
216
240
  *
217
241
  * ```ts
218
- * export const next = createNext(sb);
242
+ * export const bs = createNext(betterSupabase);
219
243
  * ```
220
244
  */
221
- 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>;
245
+ 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>;
222
246
  //#endregion
223
- 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 MembershipClaim, type NextDebugOptions, type NextOptions, type ProxyOptions, type RequireAalOptions, type SessionStaleOptions, hasEntitlement, toSession };
247
+ export { type Aal, type ActionOptions, type ActionResult, type ActiveSupport, 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, type SupportOptions, type SupportPolicy, type SupportSessions, type SupportStartRequest, type SupportStarted, type SupportView, hasEntitlement, supportOf, supportSessions, toSession };