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
@@ -1,17 +1,18 @@
1
- import { n as dbError } from "../errors-Bl3rkOzo.js";
2
- import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-D78UQ8Yu.js";
3
- import { o as isReadSet } from "../read-set-D0qBcboM.js";
4
- import { t as validate } from "../standard-FUi3Z3-7.js";
5
- import { a as problemResponse } from "../problem-CGt1ADix.js";
1
+ import { n as dbError } from "../errors-DtwXa6b8.js";
2
+ import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-Bqob3WqT.js";
3
+ import { o as isReadSet } from "../read-set-DQFt1N_Y.js";
4
+ import { t as validate } from "../standard-D7r6dMUu.js";
5
+ import { a as problemResponse } from "../problem-BW1KhzSG.js";
6
6
  import { t as isList } from "../guards-BnzDvFrz.js";
7
+ import { t as supportOf } from "../support-view-g4YLBeqX.js";
7
8
  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-C44EZAJo.js";
9
- import { r as checkAal } from "../mfa-BxoR1RkN.js";
10
- import { t as toSession } from "../view-wZ-9qIUz.js";
9
+ import { a as withExtra, b as withPrimaryPin, d as guard, m as settle, n as createServer, p as respond, r as extendServer, u as defaultExpose, v as PRIMARY_COOKIE, y as primaryCookieOptions } from "../server-Csx2sRjg.js";
10
+ import { a as checkAal, n as toSession } from "../view-Bq64ki8-.js";
11
+ import { t as supportSessions } from "../support-Dk6vdmEw.js";
11
12
  import { cache } from "react";
12
13
  import { cacheLife, cacheTag, revalidateTag, updateTag } from "next/cache.js";
13
14
  import { cookies, headers } from "next/headers.js";
14
- import { NextResponse } from "next/server.js";
15
+ import { NextResponse, after } from "next/server.js";
15
16
  //#region src/next/collector.ts
16
17
  /** Forwarded by the proxy so every scope of one render shares an id. */
17
18
  const REQUEST_ID_HEADER = "x-bs-request-id";
@@ -143,8 +144,12 @@ function serverTimingValue(metrics) {
143
144
  }
144
145
  //#endregion
145
146
  //#region src/next/create.ts
146
- /** Response header with the URL of the request's totals on `next.debugRoute()`. */
147
+ /** Response header with the URL of the request's totals on `bs.debugRoute()`. */
147
148
  const STATS_URL_HEADER = "x-bs-stats";
149
+ /** The tag `bs.cached()` adds to entries rendered in a support session. */
150
+ function supportTag(sessionId) {
151
+ return `bs:support:${sessionId}`;
152
+ }
148
153
  /**
149
154
  * `cacheLife` `stale` seconds for a view of `session`: `max`, but never past
150
155
  * the token's expiry, and at least `min`.
@@ -156,7 +161,7 @@ function sessionStale(session, options = {}, now = Date.now()) {
156
161
  const remaining = session.expiresAt - Math.floor(now / 1e3);
157
162
  return Math.min(max, Math.max(min, remaining));
158
163
  }
159
- /** The tag `next.cached()` puts on a user's entries. */
164
+ /** The tag `bs.cached()` puts on a user's entries. */
160
165
  function sessionTag(userId) {
161
166
  return `bs:session:${userId}`;
162
167
  }
@@ -202,7 +207,7 @@ function formDataObject(form) {
202
207
  * combine it with your sign-in redirect.
203
208
  *
204
209
  * ```ts
205
- * next.proxy(request, {
210
+ * bs.proxy(request, {
206
211
  * protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
207
212
  * });
208
213
  * ```
@@ -235,20 +240,20 @@ function shouldRefresh(request) {
235
240
  * actions and cache tags, on top of `createServer`.
236
241
  *
237
242
  * ```ts
238
- * export const next = createNext(sb);
243
+ * export const bs = createNext(betterSupabase);
239
244
  * ```
240
245
  */
241
- function createNext(sb, options = {}) {
242
- const base = createServer(sb, options);
246
+ function createNext(betterSupabase, options = {}) {
247
+ const base = createServer(betterSupabase, options);
243
248
  const pinMs = options.replicas?.pinMs ?? 5e3;
244
249
  const expose = options.exposeErrors ?? defaultExpose();
245
- if (options.cacheTags !== false) sb.cache(nextCache());
250
+ if (options.cacheTags !== false) betterSupabase.cache(nextCache());
246
251
  const debug = options.debug;
247
252
  const statsHeader = debug?.header ?? "x-bs-db-calls";
248
253
  const statsRoute = debug?.route ?? "/api/bs-stats";
249
254
  const development = process.env["NODE_ENV"] === "development";
250
255
  const collector = debug?.enabled ? sharedCollector({
251
- logger: sb.events.logger,
256
+ logger: betterSupabase.events.logger,
252
257
  warn: development,
253
258
  ...debug.budget ? { budget: debug.budget } : {}
254
259
  }) : void 0;
@@ -256,35 +261,66 @@ function createNext(sb, options = {}) {
256
261
  const id = collector && request.headers.get("x-bs-request-id");
257
262
  return id ? { stats: collector.recorderFor(id) } : {};
258
263
  };
264
+ /** Keeps the function alive for event sink sends the handler started. */
265
+ const flushAfter = () => {
266
+ const { events } = betterSupabase;
267
+ if (!events.pending) return;
268
+ try {
269
+ after(() => events.settled());
270
+ } catch (cause) {
271
+ events.logger.warn("after() is unavailable; event sends may be cut off", { cause });
272
+ }
273
+ };
259
274
  const incomingRequest = async () => new Request("http://next.local/", { headers: new Headers(await headers()) });
260
- const server = cache(async () => {
275
+ /** One token verification per render scope backs `context()`, `session()` and `cached()`. */
276
+ const incoming = cache(async () => {
261
277
  const request = await incomingRequest();
262
- return base.context(request, statsFor(request));
278
+ return {
279
+ request,
280
+ resolution: await base.resolve(request, { refresh: false })
281
+ };
263
282
  });
264
- const session = cache(async () => {
265
- const resolution = await base.resolve(await incomingRequest(), { refresh: false });
266
- return toSession(resolution.auth);
283
+ const current = cache(async () => {
284
+ const { request, resolution } = await incoming();
285
+ const [tenant, support] = await Promise.all([options.tenant?.(request, resolution.auth), base.support.current(request, resolution.auth)]);
286
+ return base.contextFromResolution(resolution, request, {
287
+ ...statsFor(request),
288
+ ...tenant === void 0 ? {} : { tenant },
289
+ ...support ? { support } : {}
290
+ });
267
291
  });
268
- const serverFor = async (view, { token }) => {
292
+ const context = (request, contextOptions) => request ? base.context(request, contextOptions) : current();
293
+ /** In a support session, the target's view: `impersonator` names the admin. */
294
+ const session = cache(async () => toSession(options.support ? (await current()).auth : (await incoming()).resolution.auth));
295
+ const supportCookieName = options.support?.options.cookie?.name ?? "bs-support";
296
+ const supportCookieOptions = {
297
+ httpOnly: true,
298
+ sameSite: "lax",
299
+ path: options.support?.options.cookie?.path ?? "/",
300
+ secure: options.support?.options.cookie?.secure ?? true
301
+ };
302
+ const contextForSession = async (view, { token }) => {
269
303
  const request = new Request("http://next.local/", { headers: token ? { authorization: `Bearer ${token}` } : {} });
270
304
  const ctx = await base.context(request);
271
- if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind !== "user") return ctx;
305
+ if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind === ctx.auth.kind) return ctx;
272
306
  return base.contextFor({
273
307
  kind: "invalid",
274
308
  reason: "token",
275
- error: dbError("unauthorized", "The token does not belong to the session passed to serverFor()")
309
+ error: dbError("unauthorized", "The token does not belong to the session passed to contextForSession()")
276
310
  });
277
311
  };
278
- const withAccounts = withExtra(base, { deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
279
- invalidate(sessionTag(userId));
280
- return result;
281
- })) });
312
+ const withAccounts = withExtra(base, {
313
+ context,
314
+ deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
315
+ invalidate(sessionTag(userId));
316
+ return result;
317
+ }))
318
+ });
282
319
  return extendServer(withAccounts, {
283
- server,
284
320
  session,
285
- serverFor,
321
+ contextForSession,
286
322
  async cached(cachedOptions = {}) {
287
- const [view, ctx] = await Promise.all([session(), server()]);
323
+ const [view, ctx] = await Promise.all([session(), current()]);
288
324
  const life = cachedOptions.life;
289
325
  const stale = sessionStale(view, life);
290
326
  cacheLife({
@@ -292,7 +328,11 @@ function createNext(sb, options = {}) {
292
328
  ...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
293
329
  ...life?.expire === void 0 ? {} : { expire: life.expire }
294
330
  });
295
- const tags = [...view.kind === "user" ? [sessionTag(view.user.id)] : [], ...cachedOptions.tags ?? []];
331
+ const tags = [
332
+ ...view.kind === "user" ? [sessionTag(view.user.id)] : [],
333
+ ...ctx.support ? [supportTag(ctx.support.session.id)] : [],
334
+ ...cachedOptions.tags ?? []
335
+ ];
296
336
  if (tags.length > 0) cacheTag(...tags);
297
337
  return withExtra(ctx, { session: view });
298
338
  },
@@ -301,7 +341,7 @@ function createNext(sb, options = {}) {
301
341
  for (const tag of invalidateOptions.tags ?? []) invalidate(tag);
302
342
  },
303
343
  async liveCount(spec, db) {
304
- const result = await (db ?? (await server()).db).$run(spec);
344
+ const result = await (db ?? (await current()).db).$run(spec);
305
345
  return {
306
346
  spec,
307
347
  count: result.ok && typeof result.data === "number" ? result.data : null
@@ -349,13 +389,21 @@ function createNext(sb, options = {}) {
349
389
  expose
350
390
  });
351
391
  const params = await segment.params;
352
- const response = await respond(() => handler(request, withExtra(ctx, { params })), {
353
- instance,
354
- expose
355
- });
356
- if (ctx.replica?.wrote) try {
357
- response.headers.append("set-cookie", primaryCookie(pinMs));
358
- } catch {}
392
+ let response;
393
+ try {
394
+ response = await respond(() => handler(request, withExtra(ctx, { params })), {
395
+ instance,
396
+ expose
397
+ });
398
+ } catch (cause) {
399
+ rethrowNextControlFlow(cause);
400
+ response = problemResponse(dbError("unexpected", expose && cause instanceof Error ? cause.message : "Internal server error"), {
401
+ instance,
402
+ expose
403
+ });
404
+ }
405
+ response = withPrimaryPin(response, ctx.replica, pinMs);
406
+ flushAfter();
359
407
  if (collector) try {
360
408
  response.headers.set(statsHeader, formatStats(ctx.stats()));
361
409
  } catch {}
@@ -378,7 +426,7 @@ function createNext(sb, options = {}) {
378
426
  },
379
427
  action(actionOptions, fn) {
380
428
  return async (input) => {
381
- const ctx = await server();
429
+ const ctx = await current();
382
430
  const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal, actionOptions.scopes);
383
431
  if (denied) return {
384
432
  ok: false,
@@ -396,12 +444,8 @@ function createNext(sb, options = {}) {
396
444
  parsed = checked.data;
397
445
  }
398
446
  const settled = await settle(() => fn(parsed, ctx));
399
- if (ctx.replica?.wrote) (await cookies()).set(PRIMARY_COOKIE, String(Date.now() + pinMs), {
400
- path: "/",
401
- maxAge: Math.max(1, Math.ceil(pinMs / 1e3)),
402
- httpOnly: true,
403
- sameSite: "lax"
404
- });
447
+ flushAfter();
448
+ if (ctx.replica?.wrote) (await cookies()).set(PRIMARY_COOKIE, String(Date.now() + pinMs), primaryCookieOptions(pinMs));
405
449
  return settled.ok ? {
406
450
  ok: true,
407
451
  data: settled.data,
@@ -413,6 +457,59 @@ function createNext(sb, options = {}) {
413
457
  };
414
458
  };
415
459
  },
460
+ async startSupport(request) {
461
+ const { resolution } = await incoming();
462
+ const started = await base.support.start(resolution.auth, request);
463
+ flushAfter();
464
+ if (!started.ok) return {
465
+ ok: false,
466
+ data: null,
467
+ error: started.error
468
+ };
469
+ const { session: support } = started.data;
470
+ (await cookies()).set(supportCookieName, support.id, {
471
+ ...supportCookieOptions,
472
+ maxAge: Math.max(0, Math.floor((support.expiresAt.epochMilliseconds - Date.now()) / 1e3))
473
+ });
474
+ if (resolution.auth.kind === "user") invalidate(sessionTag(resolution.auth.user.id));
475
+ return {
476
+ ok: true,
477
+ data: {
478
+ sessionId: support.id,
479
+ targetUserId: support.targetUserId,
480
+ readOnly: support.readOnly,
481
+ expiresAt: support.expiresAt.toString()
482
+ },
483
+ error: null
484
+ };
485
+ },
486
+ async stopSupport() {
487
+ const { request, resolution } = await incoming();
488
+ const jar = await cookies();
489
+ const id = base.support.sessionIdOf(request);
490
+ jar.delete({
491
+ name: supportCookieName,
492
+ path: supportCookieOptions.path
493
+ });
494
+ if (!id) return {
495
+ ok: true,
496
+ data: { ended: false },
497
+ error: null
498
+ };
499
+ const stopped = await base.support.stop(resolution.auth, id);
500
+ flushAfter();
501
+ invalidate(supportTag(id));
502
+ if (resolution.auth.kind === "user") invalidate(sessionTag(resolution.auth.user.id));
503
+ return stopped.ok ? {
504
+ ok: true,
505
+ data: { ended: stopped.data.ended },
506
+ error: null
507
+ } : {
508
+ ok: false,
509
+ data: null,
510
+ error: stopped.error
511
+ };
512
+ },
416
513
  cacheTag(table, id) {
417
514
  cacheTag(tagFor(table), ...id === void 0 ? [] : [tagFor(table, id)]);
418
515
  },
@@ -420,7 +517,7 @@ function createNext(sb, options = {}) {
420
517
  const specs = isReadSet(target) ? Object.values(target.specs) : isList(target) ? target : [target];
421
518
  const tags = /* @__PURE__ */ new Set();
422
519
  for (const spec of specs) {
423
- for (const table of sb.tablesOf(spec)) tags.add(tagFor(table));
520
+ for (const table of betterSupabase.tablesOf(spec)) tags.add(tagFor(table));
424
521
  const [id] = spec.args;
425
522
  if (spec.method === "findById" && (typeof id === "string" || typeof id === "number")) tags.add(tagFor(spec.table, id));
426
523
  }
@@ -428,5 +525,25 @@ function createNext(sb, options = {}) {
428
525
  }
429
526
  });
430
527
  }
528
+ const NEXT_DIGESTS = /* @__PURE__ */ new Set([
529
+ "NEXT_REDIRECT",
530
+ "NEXT_HTTP_ERROR_FALLBACK",
531
+ "DYNAMIC_SERVER_USAGE",
532
+ "BAILOUT_TO_CLIENT_SIDE_RENDERING",
533
+ "HANGING_PROMISE_REJECTION",
534
+ "NEXT_PRERENDER_INTERRUPTED"
535
+ ]);
536
+ const REACT_POSTPONE = Symbol.for("react.postpone");
537
+ /**
538
+ * `unstable_rethrow` without `next/navigation`: that entry loads the client
539
+ * router context, which Turbopack can't resolve inside route handlers. The
540
+ * digests are the ones Next 16 checks.
541
+ */
542
+ function rethrowNextControlFlow(error) {
543
+ if (typeof error !== "object" || error === null) return;
544
+ const digest = "digest" in error ? error.digest : void 0;
545
+ if (typeof digest === "string" && NEXT_DIGESTS.has(digest.split(";", 1)[0] ?? "") || "$$typeof" in error && error.$$typeof === REACT_POSTPONE || error instanceof Error && error.message.includes("needs to bail out of prerendering")) throw error;
546
+ if ("cause" in error) rethrowNextControlFlow(error.cause);
547
+ }
431
548
  //#endregion
432
- export { REQUEST_ID_HEADER, STATS_URL_HEADER, createNext, hasEntitlement, nextCache, requireAal, sessionStale, sessionTag, shouldRefresh, tagFor, toSession };
549
+ export { REQUEST_ID_HEADER, STATS_URL_HEADER, createNext, hasEntitlement, nextCache, requireAal, sessionStale, sessionTag, shouldRefresh, supportOf, supportSessions, supportTag, tagFor, toSession };
@@ -0,0 +1,162 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { S as ErrorMapper, a as AsyncResult } from "../executor-BunPOO-k.js";
3
+ import { a as EventHub } from "../events-aIO7vsIk.js";
4
+ import { g as RequestContext } from "../plugin-o144f322.js";
5
+ import { i as sqlTransport, n as RpcClient, r as rpcTransport, t as KitTransport } from "../kit-transport-B82k1djG.js";
6
+ import { a as NotificationSubject, i as NotificationItem, n as NotificationMessage, o as RenderedText, r as NotificationSendResult, t as NotificationChannel } from "../channel-FQW-CnGR.js";
7
+ import { StandardSchemaV1 } from "@standard-schema/spec";
8
+ //#region src/notifications/notifications.d.ts
9
+ /** Notification type to the Standard Schema of its `data`. */
10
+ type NotificationTypes = Readonly<Record<string, StandardSchemaV1>>;
11
+ type TypeName<K extends NotificationTypes> = keyof K & string;
12
+ interface SendInput<D = unknown> {
13
+ /** Who gets it. Watchers of `subject` and the `notification_audience` hook add more. */
14
+ readonly recipients?: readonly string[];
15
+ /** The organization; required when an authenticated user sends. */
16
+ readonly tenant?: string;
17
+ readonly subject?: NotificationSubject;
18
+ readonly summary?: string;
19
+ readonly actionPath?: string;
20
+ /** One of `kits.notifications.options.priorities`. Defaults to `normal`. */
21
+ readonly priority?: string;
22
+ readonly data: D;
23
+ /** With a key, sending again (per tenant) returns the first notification. */
24
+ readonly key?: string;
25
+ /** Defaults to `kits.notifications.options.channels`. */
26
+ readonly channels?: readonly string[];
27
+ /** Also notify the user who caused it. Defaults to `false`. */
28
+ readonly includeActor?: boolean;
29
+ /**
30
+ * `participating` (default) reaches subject watchers at both levels;
31
+ * `all` is routine activity that only `all` watchers get.
32
+ */
33
+ readonly activity?: "participating" | "all";
34
+ /** Create it already resolved, e.g. for a record of a finished approval. */
35
+ readonly resolved?: boolean;
36
+ /** The actor, when the service sends on a user's behalf. */
37
+ readonly actorId?: string;
38
+ }
39
+ interface ListOptions<K extends string = string> {
40
+ readonly tenant?: string;
41
+ readonly status?: "all" | "unread" | "read" | "unresolved";
42
+ readonly types?: readonly K[];
43
+ /**
44
+ * Page: only notifications older than this. Pass the last item of the
45
+ * previous page; a bare instant skips items created at that same instant.
46
+ */
47
+ readonly before?: Temporal.Instant | Pick<NotificationItem, "createdAt" | "id">;
48
+ /** Up to 200. Defaults to 50. */
49
+ readonly limit?: number;
50
+ /** Passed to `render`. */
51
+ readonly locale?: string;
52
+ }
53
+ interface NotificationCounts {
54
+ readonly unread: number;
55
+ /** Unresolved notifications of the `actionable` types. */
56
+ readonly actionable: number;
57
+ }
58
+ type SubscriptionLevel = "participating" | "all" | "ignore";
59
+ interface DeliverOptions {
60
+ /** Channel names to work on. Defaults to every channel in `channels`. */
61
+ readonly channels?: readonly string[];
62
+ /** Deliveries per claim. Defaults to 50. */
63
+ readonly batch?: number;
64
+ /** How long a claimed delivery waits before another worker retries it. */
65
+ readonly lease?: string;
66
+ /**
67
+ * Tries before a delivery is `failed`. Defaults to 5. Retries wait a random
68
+ * time up to 30 seconds, doubling per attempt to an hour.
69
+ */
70
+ readonly maxAttempts?: number;
71
+ readonly budgetMs?: number;
72
+ }
73
+ interface DeliverResult {
74
+ readonly sent: number;
75
+ readonly skipped: number;
76
+ /** Deliveries that threw; retried later unless they ran out of attempts. */
77
+ readonly failed: number;
78
+ }
79
+ interface Rendered<K extends string = string, D = unknown> extends NotificationItem<K, D> {
80
+ /** What `render` returned. Absent without a `render` option. */
81
+ readonly text?: RenderedText;
82
+ }
83
+ interface NotificationsOptions<K extends NotificationTypes> {
84
+ /** `sqlTransport(postgres.asUser(claims))`, or over `postgres.admin` for the service. */
85
+ readonly transport: KitTransport;
86
+ /** Each type's `data` schema: `send` validates against it. */
87
+ readonly types: K;
88
+ /** `kits.notifications.schema`. Defaults to `better_supabase`. */
89
+ readonly schema?: string;
90
+ /** Turns a notification into text at read time, in the reader's locale. */
91
+ readonly render?: (item: NotificationItem, context: {
92
+ readonly locale?: string;
93
+ }) => RenderedText;
94
+ /** Types that wait for an action; `counts().actionable` counts them. */
95
+ readonly actionable?: readonly TypeName<K>[];
96
+ /** Senders for the non-`in_app` channels, used by `deliver()`. */
97
+ readonly channels?: readonly NotificationChannel[];
98
+ /** Runs after a notification is stored, e.g. to call Next's `updateTag`. */
99
+ readonly onSent?: (sent: {
100
+ readonly id: string;
101
+ readonly type: string;
102
+ readonly tenant: string | null;
103
+ readonly recipients: readonly string[];
104
+ }) => void | Promise<void>;
105
+ /** `betterSupabase.events`, for `notification.*` kit events. */
106
+ readonly events?: EventHub;
107
+ readonly actorId?: string;
108
+ readonly context?: RequestContext;
109
+ readonly errorMappers?: readonly ErrorMapper[];
110
+ }
111
+ /**
112
+ * The `notifications` SQL kit module as typed calls. `send` validates the
113
+ * data against its type's schema; the database checks the send permission,
114
+ * leaves out the actor and non-members and applies subscriptions and
115
+ * preferences.
116
+ */
117
+ interface Notifications<K extends NotificationTypes> {
118
+ /** The notification id, or `null` when nobody was left to notify. */
119
+ send<N extends TypeName<K>>(type: N, input: SendInput<StandardSchemaV1.InferInput<K[N]>>): AsyncResult<string | null>;
120
+ list(options?: ListOptions<TypeName<K>>): AsyncResult<readonly Rendered<TypeName<K>>[]>;
121
+ counts(options?: {
122
+ readonly tenant?: string;
123
+ }): AsyncResult<NotificationCounts>;
124
+ /** Marks the given notifications, or all of them, read. */
125
+ markRead(options?: {
126
+ readonly ids?: readonly string[];
127
+ readonly tenant?: string;
128
+ }): AsyncResult<number>;
129
+ dismiss(ids: readonly string[]): AsyncResult<number>;
130
+ /** Resolves every recipient's notification of a type about a subject. */
131
+ resolve(input: {
132
+ readonly type: TypeName<K>;
133
+ readonly subject: NotificationSubject;
134
+ readonly tenant?: string;
135
+ }): AsyncResult<number>;
136
+ /** Sets the user's level for a subject; `null` removes it. */
137
+ subscribe(input: {
138
+ readonly subject: NotificationSubject;
139
+ readonly level: SubscriptionLevel | null;
140
+ readonly tenant?: string;
141
+ /** Another member; only the service can set it. */
142
+ readonly userId?: string;
143
+ }): AsyncResult<void>;
144
+ /** Turns a type (`*` for all) on or off on a channel; `null` removes it. */
145
+ setPreference(input: {
146
+ readonly type: TypeName<K> | "*";
147
+ readonly channel: string;
148
+ readonly enabled: boolean | null;
149
+ /** One organization, or everywhere when omitted. */
150
+ readonly tenant?: string;
151
+ }): AsyncResult<void>;
152
+ /** Sends pending deliveries through `channels`. Run it from a cron or a job. */
153
+ deliver(options?: DeliverOptions): Promise<DeliverResult>;
154
+ /**
155
+ * Deletes up to `batch` (10,000) notifications older than `olderThan`
156
+ * (`90 days`) with their recipients and deliveries. Service only.
157
+ */
158
+ purge(olderThan?: string, batch?: number): AsyncResult<number>;
159
+ }
160
+ export declare function createNotifications<const K extends NotificationTypes>(options: NotificationsOptions<K>): Notifications<K>;
161
+ //#endregion
162
+ export { type DeliverOptions, type DeliverResult, type KitTransport, type ListOptions, type NotificationChannel, type NotificationCounts, type NotificationItem, type NotificationMessage, type NotificationSendResult, type NotificationSubject, type NotificationTypes, type Notifications, type NotificationsOptions, type Rendered, type RenderedText, type RpcClient, type SendInput, type SubscriptionLevel, rpcTransport, sqlTransport };