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,13 +1,13 @@
1
- import { n as dbError } from "../errors-Bl3rkOzo.js";
2
- import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-C5W-rBfR.js";
3
- import { o as isReadSet } from "../read-set-4lI3W_gZ.js";
4
- import { t as validate } from "../standard-FUi3Z3-7.js";
5
- import { a as problemResponse } from "../problem-BTkTtRV9.js";
1
+ import { n as dbError } from "../errors-L0xK41y_.js";
2
+ import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-C_YxA2r1.js";
3
+ import { o as isReadSet } from "../read-set-FWK8qQ58.js";
4
+ import { t as validate } from "../standard-BRyBGTtp.js";
5
+ import { a as problemResponse } from "../problem-BihjPR0t.js";
6
6
  import { t as isList } from "../guards-BnzDvFrz.js";
7
7
  import { t as hasEntitlement } from "../entitlements-CTEBm7IB.js";
8
- import { a as settle, c as withExtra, d as primaryCookie, i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose, u as PRIMARY_COOKIE } from "../respond-DMcbQwfN.js";
9
- import { r as checkAal } from "../mfa-CpLfJSde.js";
10
- import { t as toSession } from "../view-aOYizTeE.js";
8
+ import { a as guard, c as settle, d as primaryCookie, i as defaultExpose, n as extendServer, r as withExtra, s as respond, t as createServer, u as PRIMARY_COOKIE } from "../server-BA75B0mT.js";
9
+ import { r as checkAal } from "../mfa-byfjruR9.js";
10
+ import { t as toSession } from "../view-BrJI2LKp.js";
11
11
  import { cache } from "react";
12
12
  import { cacheLife, cacheTag, revalidateTag, updateTag } from "next/cache.js";
13
13
  import { cookies, headers } from "next/headers.js";
@@ -43,7 +43,9 @@ var StatsCollector = class {
43
43
  this.#prune();
44
44
  const entry = {
45
45
  created: Date.now(),
46
- recorder: new CollectedRecorder(() => this.#schedule(id))
46
+ recorder: new CollectedRecorder(() => {
47
+ this.#schedule(id);
48
+ })
47
49
  };
48
50
  this.#entries.set(id, entry);
49
51
  return entry.recorder;
@@ -140,8 +142,8 @@ function serverTimingValue(metrics) {
140
142
  return Object.entries(metrics).map(([name, ms]) => `${name};dur=${ms.toFixed(1)}`).join(", ");
141
143
  }
142
144
  //#endregion
143
- //#region src/next/index.ts
144
- /** Response header with the URL of the request's totals on `next.debugRoute()`. */
145
+ //#region src/next/create.ts
146
+ /** Response header with the URL of the request's totals on `bs.debugRoute()`. */
145
147
  const STATS_URL_HEADER = "x-bs-stats";
146
148
  /**
147
149
  * `cacheLife` `stale` seconds for a view of `session`: `max`, but never past
@@ -154,7 +156,7 @@ function sessionStale(session, options = {}, now = Date.now()) {
154
156
  const remaining = session.expiresAt - Math.floor(now / 1e3);
155
157
  return Math.min(max, Math.max(min, remaining));
156
158
  }
157
- /** The tag `next.cached()` puts on a user's entries. */
159
+ /** The tag `bs.cached()` puts on a user's entries. */
158
160
  function sessionTag(userId) {
159
161
  return `bs:session:${userId}`;
160
162
  }
@@ -200,7 +202,7 @@ function formDataObject(form) {
200
202
  * combine it with your sign-in redirect.
201
203
  *
202
204
  * ```ts
203
- * next.proxy(request, {
205
+ * bs.proxy(request, {
204
206
  * protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
205
207
  * });
206
208
  * ```
@@ -208,10 +210,10 @@ function formDataObject(form) {
208
210
  function requireAal(level, options = {}) {
209
211
  return (auth, request) => {
210
212
  const { pathname, search } = request.nextUrl;
211
- if (options.redirect !== void 0 && pathname === options.redirect) return void 0;
212
- if (options.match && !options.match(pathname)) return void 0;
213
+ if (options.redirect !== void 0 && pathname === options.redirect) return;
214
+ if (options.match && !options.match(pathname)) return;
213
215
  const denied = checkAal(auth, level);
214
- if (!denied) return void 0;
216
+ if (!denied) return;
215
217
  if (options.redirect === void 0) return problemResponse(denied);
216
218
  const target = new URL(options.redirect, request.url);
217
219
  target.searchParams.set("next", `${pathname}${search}`);
@@ -233,20 +235,20 @@ function shouldRefresh(request) {
233
235
  * actions and cache tags, on top of `createServer`.
234
236
  *
235
237
  * ```ts
236
- * export const next = createNext(sb);
238
+ * export const bs = createNext(betterSupabase);
237
239
  * ```
238
240
  */
239
- function createNext(sb, options = {}) {
240
- const base = createServer(sb, options);
241
+ function createNext(betterSupabase, options = {}) {
242
+ const base = createServer(betterSupabase, options);
241
243
  const pinMs = options.replicas?.pinMs ?? 5e3;
242
244
  const expose = options.exposeErrors ?? defaultExpose();
243
- if (options.cacheTags !== false) sb.cache(nextCache());
245
+ if (options.cacheTags !== false) betterSupabase.cache(nextCache());
244
246
  const debug = options.debug;
245
247
  const statsHeader = debug?.header ?? "x-bs-db-calls";
246
248
  const statsRoute = debug?.route ?? "/api/bs-stats";
247
- const development = typeof process !== "undefined" && process.env["NODE_ENV"] === "development";
248
- const collector = debug && (debug.enabled ?? development) ? sharedCollector({
249
- logger: sb.events.logger,
249
+ const development = process.env["NODE_ENV"] === "development";
250
+ const collector = debug?.enabled ? sharedCollector({
251
+ logger: betterSupabase.events.logger,
250
252
  warn: development,
251
253
  ...debug.budget ? { budget: debug.budget } : {}
252
254
  }) : void 0;
@@ -255,48 +257,59 @@ function createNext(sb, options = {}) {
255
257
  return id ? { stats: collector.recorderFor(id) } : {};
256
258
  };
257
259
  const incomingRequest = async () => new Request("http://next.local/", { headers: new Headers(await headers()) });
258
- const server = cache(async () => {
260
+ /** One token verification per render scope backs `context()`, `session()` and `cached()`. */
261
+ const incoming = cache(async () => {
259
262
  const request = await incomingRequest();
260
- return base.context(request, statsFor(request));
263
+ return {
264
+ request,
265
+ resolution: await base.resolve(request, { refresh: false })
266
+ };
261
267
  });
262
- const session = cache(async () => {
263
- const resolution = await base.resolve(await incomingRequest(), { refresh: false });
264
- return toSession(resolution.auth);
268
+ const current = cache(async () => {
269
+ const { request, resolution } = await incoming();
270
+ return base.contextFromResolution(resolution, request, statsFor(request));
265
271
  });
266
- const serverFor = async (view, { token }) => {
272
+ const context = (request, contextOptions) => request ? base.context(request, contextOptions) : current();
273
+ const session = cache(async () => toSession((await incoming()).resolution.auth));
274
+ const contextForSession = async (view, { token }) => {
267
275
  const request = new Request("http://next.local/", { headers: token ? { authorization: `Bearer ${token}` } : {} });
268
276
  const ctx = await base.context(request);
269
277
  if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind !== "user") return ctx;
270
278
  return base.contextFor({
271
279
  kind: "invalid",
272
280
  reason: "token",
273
- error: dbError("unauthorized", "The token does not belong to the session passed to serverFor()")
281
+ error: dbError("unauthorized", "The token does not belong to the session passed to contextForSession()")
274
282
  });
275
283
  };
276
- const withAccounts = withExtra(base, { deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
277
- invalidate(sessionTag(userId));
278
- return result;
279
- })) });
284
+ const withAccounts = withExtra(base, {
285
+ context,
286
+ deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
287
+ invalidate(sessionTag(userId));
288
+ return result;
289
+ }))
290
+ });
280
291
  return extendServer(withAccounts, {
281
- server,
282
292
  session,
283
- serverFor,
293
+ contextForSession,
284
294
  async cached(cachedOptions = {}) {
285
- const [view, ctx] = await Promise.all([session(), server()]);
295
+ const [view, ctx] = await Promise.all([session(), current()]);
286
296
  const life = cachedOptions.life;
297
+ const stale = sessionStale(view, life);
287
298
  cacheLife({
288
- stale: sessionStale(view, life),
299
+ stale: life?.stale === void 0 ? stale : Math.min(stale, life.stale),
289
300
  ...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
290
301
  ...life?.expire === void 0 ? {} : { expire: life.expire }
291
302
  });
292
- if (view.kind === "user") cacheTag(sessionTag(view.user.id));
303
+ const tags = [...view.kind === "user" ? [sessionTag(view.user.id)] : [], ...cachedOptions.tags ?? []];
304
+ if (tags.length > 0) cacheTag(...tags);
293
305
  return withExtra(ctx, { session: view });
294
306
  },
295
- invalidateSession(userId) {
307
+ invalidateSession(userId, invalidateOptions = {}) {
296
308
  invalidate(sessionTag(userId));
309
+ for (const tag of invalidateOptions.tags ?? []) invalidate(tag);
297
310
  },
298
311
  async liveCount(spec, db) {
299
- const result = await (db ?? (await server()).db).$run(spec);
312
+ const result = await (db ?? (await current()).db).$run(spec);
300
313
  return {
301
314
  spec,
302
315
  count: result.ok && typeof result.data === "number" ? result.data : null
@@ -337,7 +350,7 @@ function createNext(sb, options = {}) {
337
350
  route(handler, guardOptions = {}) {
338
351
  return async (request, segment) => {
339
352
  const ctx = await base.context(request, statsFor(request));
340
- const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal);
353
+ const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal, guardOptions.scopes);
341
354
  const instance = request.nextUrl.pathname;
342
355
  if (denied) return problemResponse(denied, {
343
356
  instance,
@@ -358,23 +371,23 @@ function createNext(sb, options = {}) {
358
371
  };
359
372
  },
360
373
  debugRoute() {
361
- return async (request) => {
374
+ return (request) => {
362
375
  const id = new URL(request.url).searchParams.get("id");
363
- if (!collector || !id) return Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
376
+ if (!collector || !id) return Promise.resolve(Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
364
377
  status: 404,
365
378
  headers: { "cache-control": "no-store" }
366
- });
379
+ }));
367
380
  const stats = collector.get(id) ?? EMPTY_STATS;
368
- return Response.json(stats, { headers: {
381
+ return Promise.resolve(Response.json(stats, { headers: {
369
382
  [statsHeader]: formatStats(stats),
370
383
  "cache-control": "no-store"
371
- } });
384
+ } }));
372
385
  };
373
386
  },
374
387
  action(actionOptions, fn) {
375
388
  return async (input) => {
376
- const ctx = await server();
377
- const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal);
389
+ const ctx = await current();
390
+ const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal, actionOptions.scopes);
378
391
  if (denied) return {
379
392
  ok: false,
380
393
  data: null,
@@ -415,7 +428,7 @@ function createNext(sb, options = {}) {
415
428
  const specs = isReadSet(target) ? Object.values(target.specs) : isList(target) ? target : [target];
416
429
  const tags = /* @__PURE__ */ new Set();
417
430
  for (const spec of specs) {
418
- for (const table of sb.tablesOf(spec)) tags.add(tagFor(table));
431
+ for (const table of betterSupabase.tablesOf(spec)) tags.add(tagFor(table));
419
432
  const [id] = spec.args;
420
433
  if (spec.method === "findById" && (typeof id === "string" || typeof id === "number")) tags.add(tagFor(spec.table, id));
421
434
  }
@@ -1,2 +1,2 @@
1
- import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-DbxFr7XG.js";
1
+ import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-BhHEC04r.js";
2
2
  export { OpenApiDocument, OpenApiOptions, PROBLEM_SCHEMA, ResourceOperation, ResourceOptions, createOpenApi };
@@ -1,6 +1,6 @@
1
- import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-BTkTtRV9.js";
1
+ import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-BihjPR0t.js";
2
2
  import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
3
- import { t as buildJsonSchema } from "../json-schema-BpYqkCKf.js";
3
+ import { t as buildJsonSchema } from "../json-schema-DP6V7cbr.js";
4
4
  //#region src/openapi/index.ts
5
5
  /** JSON Schema for RFC 9457 Problem Details as better-supabase sends them. */
6
6
  const PROBLEM_SCHEMA = {
@@ -81,7 +81,7 @@ const ERRORS = [
81
81
  "Validation or a check constraint failed."
82
82
  ]
83
83
  ];
84
- const pascal = (value) => value.replace(/(^|[_-])(\w)/g, (_, _sep, char) => char.toUpperCase());
84
+ const pascal = (value) => value.replaceAll(/(^|[_-])(\w)/g, (_, _sep, char) => char.toUpperCase());
85
85
  const ref = (name) => ({ $ref: `#/components/schemas/${name}` });
86
86
  const response = (name) => ({ $ref: `#/components/responses/${name}` });
87
87
  function errorResponses(codes) {
@@ -95,8 +95,14 @@ function jsonBody(schema, description) {
95
95
  content: { "application/json": { schema } }
96
96
  };
97
97
  }
98
- function pageParameters() {
99
- return [{
98
+ function pageParameters(cursor, maxPageSize) {
99
+ return [cursor ? {
100
+ name: "after",
101
+ in: "query",
102
+ required: false,
103
+ description: "`nextCursor` of the previous page. Leave it out for the first page.",
104
+ schema: { type: "string" }
105
+ } : {
100
106
  name: "page",
101
107
  in: "query",
102
108
  required: false,
@@ -114,26 +120,68 @@ function pageParameters() {
114
120
  schema: {
115
121
  type: "integer",
116
122
  minimum: 1,
117
- maximum: 200,
123
+ maximum: maxPageSize,
118
124
  default: 50
119
125
  }
120
126
  }];
121
127
  }
128
+ function pageSchema(row, cursor) {
129
+ const items = {
130
+ type: "array",
131
+ items: row
132
+ };
133
+ return cursor ? {
134
+ type: "object",
135
+ required: [
136
+ "items",
137
+ "nextCursor",
138
+ "hasMore"
139
+ ],
140
+ properties: {
141
+ items,
142
+ nextCursor: { type: ["string", "null"] },
143
+ hasMore: { type: "boolean" }
144
+ }
145
+ } : {
146
+ type: "object",
147
+ required: ["items", "page"],
148
+ properties: {
149
+ items,
150
+ page: {
151
+ type: "object",
152
+ required: [
153
+ "number",
154
+ "size",
155
+ "total",
156
+ "pages",
157
+ "hasMore"
158
+ ],
159
+ properties: {
160
+ number: { type: "integer" },
161
+ size: { type: "integer" },
162
+ total: { type: ["integer", "null"] },
163
+ pages: { type: ["integer", "null"] },
164
+ hasMore: { type: "boolean" }
165
+ }
166
+ }
167
+ }
168
+ };
169
+ }
122
170
  /**
123
171
  * An OpenAPI 3.1 document for REST resources over your tables, with schemas
124
172
  * from the generated metadata, list-query parameters, Supabase security
125
173
  * schemes and Problem Details errors.
126
174
  *
127
175
  * ```ts
128
- * const doc = createOpenApi(sb, {
176
+ * const doc = createOpenApi(betterSupabase, {
129
177
  * info: { title: 'CRM API', version: '1.0.0' },
130
178
  * resources: { customers: { list: customerList }, notes: { operations: ['list', 'get'] } },
131
179
  * });
132
180
  * ```
133
181
  */
134
- function createOpenApi(sb, options) {
182
+ function createOpenApi(betterSupabase, options) {
135
183
  const defs = buildJsonSchema({
136
- meta: sb.meta,
184
+ meta: betterSupabase.meta,
137
185
  config: { json: options.json ?? {} }
138
186
  })["$defs"];
139
187
  const schemas = { Problem: PROBLEM_SCHEMA };
@@ -142,7 +190,7 @@ function createOpenApi(sb, options) {
142
190
  const base = (options.basePath ?? "").replace(/\/$/, "");
143
191
  const security = options.security ?? ["bearer"];
144
192
  for (const [key, raw] of Object.entries(options.resources)) {
145
- const table = sb.meta.tables[key];
193
+ const table = betterSupabase.meta.tables[key];
146
194
  if (!table) throw new TypeError(`createOpenApi: unknown table "${key}"`);
147
195
  const resource = raw === true ? {} : raw;
148
196
  const operations = resource.operations ?? (table.kind === "view" ? ["list", "get"] : [
@@ -167,33 +215,8 @@ function createOpenApi(sb, options) {
167
215
  const schema = defs[`${key}${variant}`];
168
216
  if (schema) schemas[`${name}${variant}`] = schema;
169
217
  }
170
- schemas[`${name}Page`] = {
171
- type: "object",
172
- required: ["items", "page"],
173
- properties: {
174
- items: {
175
- type: "array",
176
- items: ref(`${name}Row`)
177
- },
178
- page: {
179
- type: "object",
180
- required: [
181
- "number",
182
- "size",
183
- "total",
184
- "pages",
185
- "hasMore"
186
- ],
187
- properties: {
188
- number: { type: "integer" },
189
- size: { type: "integer" },
190
- total: { type: ["integer", "null"] },
191
- pages: { type: ["integer", "null"] },
192
- hasMore: { type: "boolean" }
193
- }
194
- }
195
- }
196
- };
218
+ const cursor = (resource.list ? resource.list.pagination : resource.pagination) === "cursor";
219
+ schemas[`${name}Page`] = pageSchema(ref(`${name}Row`), cursor);
197
220
  const collection = `${base}${resource.path ?? `/${key}`}`;
198
221
  const keyColumn = table.primaryKey.length === 1 ? table.primaryKey[0] : void 0;
199
222
  const idSchema = keyColumn ? (defs[`${key}Row`]?.["properties"])?.[keyColumn] ?? { type: "string" } : void 0;
@@ -215,7 +238,7 @@ function createOpenApi(sb, options) {
215
238
  for (const operation of operations) switch (operation) {
216
239
  case "list":
217
240
  (paths[collection] ??= {})["get"] = op("list", `List ${key}`, {
218
- parameters: resource.list?.openapi ?? pageParameters(),
241
+ parameters: resource.list?.openapi ?? pageParameters(cursor, resource.maxPageSize ?? 200),
219
242
  responses: {
220
243
  "200": jsonBody(ref(`${name}Page`), `A page of ${key}.`),
221
244
  ...errorResponses([
@@ -1,9 +1,9 @@
1
- import { n as AnyModels, t as AnyFunctions } from "../types-C83v5SQz.js";
2
- import { o as Result, p as DbError } from "../result-CTkCZ6JA.js";
3
- import { t as BetterSupabase } from "../define-e3zI4_4g.js";
4
- import { r as AuthState } from "../resolve-CjHILk2w.js";
5
- import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-Dne1ARSa.js";
6
- import { r as ProblemDetails } from "../problem-DlgB0lYl.js";
1
+ import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
2
+ import { o as Result, p as DbError } from "../result-CqEsbgYs.js";
3
+ import { t as BetterSupabase } from "../define-CRS2MRV3.js";
4
+ import { r as AuthState } from "../resolve-BAqQcwMW.js";
5
+ import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-DgIbdrt8.js";
6
+ import { r as ProblemDetails } from "../problem-U8UEKccv.js";
7
7
  import { DecoratedMiddleware, ORPCError } from "@orpc/server";
8
8
  //#region src/orpc/index.d.ts
9
9
  /** Initial context: pass `{ context: { request } }` to the oRPC handler. */
@@ -11,36 +11,33 @@ export interface OrpcRequestContext {
11
11
  readonly request: Request;
12
12
  }
13
13
  /** What `middleware()` adds to `context`. */
14
- export interface OrpcContext<M extends AnyModels, F extends AnyFunctions, E> {
15
- readonly bs: ServerContext<M, F, E>;
16
- readonly db: ServerContext<M, F, E>["db"];
17
- readonly auth: AuthState;
14
+ export interface OrpcContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
15
+ readonly bs: ServerContext<M, F, E, C, P>;
16
+ readonly db: ServerContext<M, F, E, C, P>["db"];
17
+ readonly auth: AuthState<C, P>;
18
18
  }
19
19
  export interface OrpcOptions extends ServerOptions {
20
20
  /** Include internal error messages. Defaults to `NODE_ENV === 'development'`. */
21
21
  readonly exposeErrors?: boolean;
22
22
  }
23
- export interface MiddlewareOptions extends GuardOptions {
24
- /** Refresh an expired cookie session. Pair with `ctx.bs.resolution.apply` in the handler adapter. */
25
- readonly refresh?: boolean;
26
- }
27
- export type OrpcMiddleware<M extends AnyModels, F extends AnyFunctions, E> = DecoratedMiddleware<OrpcRequestContext, OrpcContext<M, F, E>, unknown, unknown, Record<never, never>>;
28
- export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
23
+ export type OrpcMiddleware<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> = DecoratedMiddleware<OrpcRequestContext, OrpcContext<M, F, E, C, P>, unknown, unknown, Record<never, never>>;
24
+ export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
29
25
  /**
30
26
  * Resolves the caller, enforces `allow` and adds `context.db`, `context.auth`
31
27
  * and `context.bs`. Thrown `DbException`s, and errors caused by a
32
- * `DbError`, become `ORPCError`s.
28
+ * `DbError`, become `ORPCError`s. With `refresh`, pair it with
29
+ * `ctx.bs.resolution.apply` in the handler adapter.
33
30
  */
34
- middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E>;
31
+ middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E, C, P>;
35
32
  /** A `Result` (or `AsyncResult`) as data, throwing an `ORPCError` on failure. */
36
33
  unwrap<T>(value: Result<T> | PromiseLike<Result<T>>): Promise<T>;
37
34
  /** Plain values pass through; thrown `DbException`s become `ORPCError`s. */
38
35
  unwrap<T>(value: T | PromiseLike<T>): Promise<T>;
39
- toORPCError(error: DbError): ORPCError<string, ProblemDetails>;
36
+ toOrpcError(error: DbError): ORPCError<string, ProblemDetails>;
40
37
  }
41
38
  /** oRPC code for a `DbError`, from its HTTP status. */
42
39
  export declare function orpcCode(error: DbError): string;
43
40
  /** oRPC integration: middleware adding the caller's repositories to `context`. */
44
- export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E>(sb: BetterSupabase<M, D, F, E>, options?: OrpcOptions): BetterOrpc<M, F, E>;
41
+ export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options?: OrpcOptions): BetterOrpc<M, F, E, C, P>;
45
42
  //#endregion
46
- export type { GuardOptions };
43
+ export type { GuardOptions, MiddlewareOptions };
@@ -1,18 +1,18 @@
1
- import { r as dbErrorOf } from "../errors-Bl3rkOzo.js";
2
- import { o as toProblem } from "../problem-BTkTtRV9.js";
3
- import { a as settle, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-DMcbQwfN.js";
1
+ import { r as dbErrorOf } from "../errors-L0xK41y_.js";
2
+ import { o as toProblem } from "../problem-BihjPR0t.js";
3
+ import { a as guard, c as settle, i as defaultExpose, n as extendServer, t as createServer } from "../server-BA75B0mT.js";
4
4
  import { COMMON_ERROR_STATUS_MAP, ORPCError, os } from "@orpc/server";
5
5
  //#region src/orpc/index.ts
6
- const CODES = new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
6
+ const CODES = /* @__PURE__ */ new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
7
7
  /** oRPC code for a `DbError`, from its HTTP status. */
8
8
  function orpcCode(error) {
9
9
  return CODES.get(error.status) ?? (error.status >= 500 ? "INTERNAL_SERVER_ERROR" : "BAD_REQUEST");
10
10
  }
11
11
  /** oRPC integration: middleware adding the caller's repositories to `context`. */
12
- function createOrpc(sb, options = {}) {
13
- const server = createServer(sb, options);
12
+ function createOrpc(betterSupabase, options = {}) {
13
+ const server = createServer(betterSupabase, options);
14
14
  const expose = options.exposeErrors ?? defaultExpose();
15
- const toORPCError = (error) => {
15
+ const toOrpcError = (error) => {
16
16
  const problem = toProblem(error, { expose });
17
17
  return new ORPCError(orpcCode(error), {
18
18
  message: problem.detail ?? problem.title,
@@ -20,12 +20,12 @@ function createOrpc(sb, options = {}) {
20
20
  });
21
21
  };
22
22
  return extendServer(server, {
23
- toORPCError,
23
+ toOrpcError,
24
24
  middleware(middlewareOptions = {}) {
25
25
  return os.$context().middleware(async ({ context, next }) => {
26
26
  const ctx = await server.context(context.request, { refresh: middlewareOptions.refresh ?? false });
27
- const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal);
28
- if (denied) throw toORPCError(denied);
27
+ const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
28
+ if (denied) throw toOrpcError(denied);
29
29
  try {
30
30
  return await next({ context: {
31
31
  bs: ctx,
@@ -34,14 +34,14 @@ function createOrpc(sb, options = {}) {
34
34
  } });
35
35
  } catch (cause) {
36
36
  const thrown = dbErrorOf(cause);
37
- if (thrown) throw toORPCError(thrown);
37
+ if (thrown) throw toOrpcError(thrown);
38
38
  throw cause;
39
39
  }
40
40
  });
41
41
  },
42
42
  async unwrap(value) {
43
43
  const settled = await settle(() => value);
44
- if (!settled.ok) throw toORPCError(settled.error);
44
+ if (!settled.ok) throw toOrpcError(settled.error);
45
45
  return settled.data;
46
46
  }
47
47
  });
@@ -1,5 +1,5 @@
1
- import { a as EventHub } from "../events-DOt3nSO-.js";
2
- import { f as Plugin } from "../plugin-C4TR9J0Z.js";
1
+ import { a as EventHub } from "../events-B21UOhvP.js";
2
+ import { f as Plugin } from "../plugin-bNhvJwRh.js";
3
3
  import { t as SPEC_PINS } from "../spec-pins-CqorWDnO.js";
4
4
  import { Attributes, Meter, Tracer } from "@opentelemetry/api";
5
5
  //#region src/otel/index.d.ts
@@ -20,15 +20,15 @@ export interface OtelOptions {
20
20
  * never recorded; `db.query.summary` is `SELECT customers`.
21
21
  *
22
22
  * ```ts
23
- * const sb = defineSupabase(schema).use(otel());
23
+ * const betterSupabase = defineSupabase(schema).use(otel());
24
24
  * ```
25
25
  */
26
26
  export declare function otel(options?: OtelOptions): Plugin<"otel">;
27
27
  /**
28
- * Spans for auth resolution and session refreshes, from `sb.events`.
28
+ * Spans for auth resolution and session refreshes, from `betterSupabase.events`.
29
29
  * Returns a function that stops listening.
30
30
  */
31
- export declare function traceAuth(sb: {
31
+ export declare function traceAuth(betterSupabase: {
32
32
  readonly events: EventHub;
33
33
  }, options?: Pick<OtelOptions, "tracer" | "attributes">): () => void;
34
34
  /**