better-supabase 0.15.1 → 0.16.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 (272) hide show
  1. package/dist/{ai-schedules-D_Hz-ik5.d.ts → ai-schedules-BmkARmQ9.d.ts} +1 -1
  2. package/dist/{api-DxAPqMrE.d.ts → api-hHwieEfz.d.ts} +1 -1
  3. package/dist/{app-tables-CbCFSXLG.d.ts → app-tables-yMQ4ubyY.d.ts} +1 -1
  4. package/dist/astro/index.d.ts +4 -4
  5. package/dist/{auth-module-BEFESDC4.js → auth-module-BbmX-N4L.js} +12 -4
  6. package/dist/better-result/index.d.ts +2 -2
  7. package/dist/{bind-h5jre2K0.d.ts → bind-JGCPGUJh.d.ts} +4 -4
  8. package/dist/{block-env-BrfRvPrj.d.ts → block-env-y65GF_px.d.ts} +4 -4
  9. package/dist/blocks/access-requests/index.d.ts +1 -1
  10. package/dist/blocks/activities/index.d.ts +1 -1
  11. package/dist/blocks/agents/index.d.ts +1 -1
  12. package/dist/blocks/ai-cache/index.d.ts +1 -1
  13. package/dist/blocks/ai-chat/index.d.ts +4 -4
  14. package/dist/blocks/ai-files/index.d.ts +1 -1
  15. package/dist/blocks/ai-keys/index.d.ts +1 -1
  16. package/dist/blocks/ai-schedules/index.d.ts +2 -2
  17. package/dist/blocks/announcements/index.d.ts +4 -4
  18. package/dist/blocks/api-keys/index.d.ts +2 -2
  19. package/dist/blocks/approvals/index.d.ts +1 -1
  20. package/dist/blocks/assets/index.d.ts +1 -1
  21. package/dist/blocks/assignment/index.d.ts +1 -1
  22. package/dist/blocks/attachments/index.d.ts +2 -2
  23. package/dist/blocks/audit/index.d.ts +5 -5
  24. package/dist/blocks/automations/index.d.ts +3 -3
  25. package/dist/blocks/billable-time/index.d.ts +1 -1
  26. package/dist/blocks/billing/index.d.ts +3 -3
  27. package/dist/blocks/bookings/index.d.ts +2 -2
  28. package/dist/blocks/bulk/index.d.ts +1 -1
  29. package/dist/blocks/calendar/index.d.ts +2 -2
  30. package/dist/blocks/catalog/index.d.ts +1 -1
  31. package/dist/blocks/channels/index.d.ts +1 -1
  32. package/dist/blocks/collections/index.d.ts +1 -1
  33. package/dist/blocks/comments/index.d.ts +4 -4
  34. package/dist/blocks/compliance/index.d.ts +1 -1
  35. package/dist/blocks/connectors/index.d.ts +1 -1
  36. package/dist/blocks/contacts/index.d.ts +1 -1
  37. package/dist/blocks/content/index.d.ts +1 -1
  38. package/dist/blocks/contracts/index.d.ts +1 -1
  39. package/dist/blocks/courses/index.d.ts +1 -1
  40. package/dist/blocks/created-by/index.d.ts +1 -1
  41. package/dist/blocks/custom-fields/index.d.ts +1 -1
  42. package/dist/blocks/data-lifecycle/index.d.ts +1 -1
  43. package/dist/blocks/deals/index.d.ts +1 -1
  44. package/dist/blocks/dispatch/index.d.ts +1 -1
  45. package/dist/blocks/documents/index.d.ts +1 -1
  46. package/dist/blocks/domains/index.d.ts +1 -1
  47. package/dist/blocks/donations/index.d.ts +1 -1
  48. package/dist/blocks/drive/index.d.ts +1 -1
  49. package/dist/blocks/employees/index.d.ts +2 -2
  50. package/dist/blocks/enrichment/index.d.ts +1 -1
  51. package/dist/blocks/enrollments/index.d.ts +1 -1
  52. package/dist/blocks/entitlements/index.d.ts +3 -3
  53. package/dist/blocks/expenses/index.d.ts +1 -1
  54. package/dist/blocks/field-history/index.d.ts +1 -1
  55. package/dist/blocks/flags/index.d.ts +1 -1
  56. package/dist/blocks/forecasts/index.d.ts +1 -1
  57. package/dist/blocks/forms/index.d.ts +2 -2
  58. package/dist/blocks/goals/index.d.ts +1 -1
  59. package/dist/blocks/guests/index.d.ts +1 -1
  60. package/dist/blocks/hiring/index.d.ts +1 -1
  61. package/dist/blocks/identities/index.d.ts +1 -1
  62. package/dist/blocks/imports/index.d.ts +1 -1
  63. package/dist/blocks/inbox/index.d.ts +5 -5
  64. package/dist/blocks/incidents/index.d.ts +1 -1
  65. package/dist/blocks/index.d.ts +2 -2
  66. package/dist/blocks/inventory/index.d.ts +1 -1
  67. package/dist/blocks/invoices/index.d.ts +1 -1
  68. package/dist/blocks/knowledge/index.d.ts +1 -1
  69. package/dist/blocks/lead-scoring/index.d.ts +1 -1
  70. package/dist/blocks/leases/index.d.ts +1 -1
  71. package/dist/blocks/leave/index.d.ts +1 -1
  72. package/dist/blocks/ledger/index.d.ts +1 -1
  73. package/dist/blocks/mail-sync/index.d.ts +1 -1
  74. package/dist/blocks/memberships/index.d.ts +1 -1
  75. package/dist/blocks/memory/index.d.ts +1 -1
  76. package/dist/blocks/messaging/index.d.ts +4 -4
  77. package/dist/blocks/metrics/index.d.ts +1 -1
  78. package/dist/blocks/mfa/index.d.ts +2 -2
  79. package/dist/blocks/moderation/index.d.ts +1 -1
  80. package/dist/blocks/notifications/index.d.ts +4 -4
  81. package/dist/blocks/numbering/index.d.ts +1 -1
  82. package/dist/blocks/oauth-server/index.d.ts +1 -1
  83. package/dist/blocks/onboarding/index.d.ts +2 -2
  84. package/dist/blocks/orders/index.d.ts +2 -2
  85. package/dist/blocks/organizations/index.d.ts +2 -2
  86. package/dist/blocks/outbox/index.d.ts +1 -1
  87. package/dist/blocks/passes/index.d.ts +1 -1
  88. package/dist/blocks/permissions/index.d.ts +3 -3
  89. package/dist/blocks/pipelines/index.d.ts +1 -1
  90. package/dist/blocks/posts/index.d.ts +1 -1
  91. package/dist/blocks/profiles/index.d.ts +1 -1
  92. package/dist/blocks/promotions/index.d.ts +1 -1
  93. package/dist/blocks/push/index.d.ts +2 -2
  94. package/dist/blocks/queue/index.d.ts +29 -2
  95. package/dist/blocks/queue/index.js +1 -1
  96. package/dist/blocks/quotes/index.d.ts +1 -1
  97. package/dist/blocks/rank/index.d.ts +1 -1
  98. package/dist/blocks/reactions/index.d.ts +4 -4
  99. package/dist/blocks/relations/index.d.ts +1 -1
  100. package/dist/blocks/reminders/index.d.ts +1 -1
  101. package/dist/blocks/reviews/index.d.ts +1 -1
  102. package/dist/blocks/saved-views/index.d.ts +3 -3
  103. package/dist/blocks/scores/index.d.ts +1 -1
  104. package/dist/blocks/search/index.d.ts +3 -3
  105. package/dist/blocks/security-events/index.d.ts +1 -1
  106. package/dist/blocks/segments/index.d.ts +1 -1
  107. package/dist/blocks/sequences/index.d.ts +1 -1
  108. package/dist/blocks/service-accounts/index.d.ts +1 -1
  109. package/dist/blocks/sessions/index.d.ts +2 -2
  110. package/dist/blocks/settings/index.d.ts +2 -2
  111. package/dist/blocks/share-links/index.d.ts +1 -1
  112. package/dist/blocks/shipments/index.d.ts +1 -1
  113. package/dist/blocks/signatures/index.d.ts +1 -1
  114. package/dist/blocks/signing-keys/index.d.ts +1 -1
  115. package/dist/blocks/sla/index.d.ts +1 -1
  116. package/dist/blocks/sso/index.d.ts +1 -1
  117. package/dist/blocks/state-machine/index.d.ts +1 -1
  118. package/dist/blocks/subjects/index.d.ts +2 -2
  119. package/dist/blocks/tags/index.d.ts +2 -2
  120. package/dist/blocks/tasks/index.d.ts +2 -2
  121. package/dist/blocks/teams/index.d.ts +1 -1
  122. package/dist/blocks/templates/index.d.ts +1 -1
  123. package/dist/blocks/time-tracking/index.d.ts +1 -1
  124. package/dist/blocks/timeline/index.d.ts +3 -3
  125. package/dist/blocks/trash/index.d.ts +1 -1
  126. package/dist/blocks/undo/index.d.ts +2 -2
  127. package/dist/blocks/updated-at/index.d.ts +1 -1
  128. package/dist/blocks/usage/index.d.ts +2 -2
  129. package/dist/blocks/vercel-marketplace/index.d.ts +1 -1
  130. package/dist/blocks/versions/index.d.ts +1 -1
  131. package/dist/blocks/waitlist/index.d.ts +1 -1
  132. package/dist/blocks/webhooks/index.d.ts +2 -2
  133. package/dist/blocks/webhooks/index.js +1 -1
  134. package/dist/blocks/workflow-editor/index.d.ts +4 -4
  135. package/dist/blocks/workflows/index.d.ts +4 -4
  136. package/dist/{bucket-CiNi8sIw.d.ts → bucket-vc-QRrva.d.ts} +40 -1
  137. package/dist/cli/add-Bfb_ISrO.js +1 -0
  138. package/dist/cli/{add-DNOL9PDu.js → add-D7TkPdco.js} +9 -9
  139. package/dist/cli/{add-C04vkBk9.js → add-I8OW-qJH.js} +2 -2
  140. package/dist/cli/{backfill-VFzf-TJP.js → backfill-DIz5DnlQ.js} +1 -1
  141. package/dist/cli/bin.js +1 -1
  142. package/dist/cli/{blocks-Bn7WUca5.js → blocks-Bp5RPoft.js} +1 -1
  143. package/dist/cli/{blocks-Cb7mILFU.js → blocks-xk-SE8gS.js} +1 -1
  144. package/dist/cli/{cache-DkplpIGe.js → cache-AznIssjI.js} +1 -1
  145. package/dist/cli/{codemod-BbaE5dL_.js → codemod-Aewxza6i.js} +1 -1
  146. package/dist/cli/{content-mapper-Cdx6arxu.js → content-mapper-DCrz6OIv.js} +2 -2
  147. package/dist/cli/{doctor-C-VO-QQ2.js → doctor-DkOSLPEQ.js} +13 -13
  148. package/dist/cli/{gen-yRHOHW1b.js → gen-CFkYUnWq.js} +10 -10
  149. package/dist/cli/index.d.ts +1 -1
  150. package/dist/cli/index.js +6 -6
  151. package/dist/cli/{init-BOw0SX65.js → init-CHj9ad4S.js} +5 -5
  152. package/dist/cli/{live-Lmg-hYyO.js → live-DGdLR41j.js} +2 -2
  153. package/dist/cli/{mcp-DQ1zR2eA.js → mcp-BZno503V.js} +2 -2
  154. package/dist/cli/{metadata-CZaxkv21.js → metadata-BfgHMe4q.js} +3 -3
  155. package/dist/cli/{migration-safety-BbhLu2fo.js → migration-safety-BnnXPAro.js} +1 -1
  156. package/dist/cli/{module-migration-DOn6EmNz.js → module-migration-O_IxewFs.js} +1 -1
  157. package/dist/cli/{print-D_xf8PPj.js → print-DosQIFSy.js} +1 -1
  158. package/dist/cli/{remove-BY_TOTRj.js → remove-DK2G_KHm.js} +5 -5
  159. package/dist/cli/{render-lJmHr0y9.js → render-5y5a1QZr.js} +3 -3
  160. package/dist/cli/{run-BJ6IMs_H.js → run-Bs4OKdDI.js} +7 -7
  161. package/dist/cli/{run-DkB0fhc9.js → run-MRET30vh.js} +20 -20
  162. package/dist/cli/{shared-Bh-6IqJS.js → shared-BLqbvY2x.js} +1 -1
  163. package/dist/cli/{snapshot-Bzcigm7T.js → snapshot-uTUlEzY2.js} +2 -2
  164. package/dist/cli/{source-WoLdcQzN.js → source-D2gJHKwC.js} +1 -1
  165. package/dist/cli/{spec-CZMiLhv_.js → spec-Cm2einTO.js} +2 -2
  166. package/dist/cli/{spec-BF_dpwzW.js → spec-DUjOtciN.js} +1 -1
  167. package/dist/cli/{sql-layout-rFP0oq1N.js → sql-layout-lUWenjJw.js} +2 -2
  168. package/dist/cli/{sync-mANmYYoL.js → sync-CZMRB3aU.js} +2 -2
  169. package/dist/cli/{upgrade-B0Q9wMs-.js → upgrade-BFEIMC7U.js} +3 -3
  170. package/dist/cli/{upgrade-BrNLt92u.js → upgrade-fVsJniXm.js} +2 -2
  171. package/dist/cli/{version-CyavsyGA.js → version-B2Dr6VmJ.js} +1 -1
  172. package/dist/client/index.d.ts +5 -5
  173. package/dist/client/index.js +1 -1
  174. package/dist/client/native/index.d.ts +5 -5
  175. package/dist/client/native/index.js +1 -1
  176. package/dist/{client-BAEbEA7t.js → client-C_mlji5R.js} +6 -5
  177. package/dist/{composite-BZYXlVBN.d.ts → composite-CfYyU3vs.d.ts} +4 -3
  178. package/dist/config/index.d.ts +1 -1
  179. package/dist/{context-TGX2eDS5.d.ts → context-8kb8m5uO.d.ts} +1 -1
  180. package/dist/{controller-DyPPMFSw.d.ts → controller-BW-5Opig.d.ts} +1 -1
  181. package/dist/{conversion-O-kyhXcC.d.ts → conversion-3iv2r7YC.d.ts} +1 -1
  182. package/dist/create/index.d.ts +2 -2
  183. package/dist/create/index.js +1 -1
  184. package/dist/{define-DFBnQyYg.d.ts → define-C5AcEkEJ.d.ts} +1 -1
  185. package/dist/{define-block-DvvcM54N.d.ts → define-block-C1RHXz7L.d.ts} +2 -2
  186. package/dist/edge/index.d.ts +5 -5
  187. package/dist/{entitlements-BnhDBxLW.d.ts → entitlements-sO977DO-.d.ts} +1 -1
  188. package/dist/{executor-CVIn6JHa.d.ts → executor-BcNWcD-4.d.ts} +2 -2
  189. package/dist/expo/index.d.ts +2 -2
  190. package/dist/{field-errors-DYtg1mwv.d.ts → field-errors-Dt86dCKk.d.ts} +12 -12
  191. package/dist/{framework-h5lP_5yj.d.ts → framework-BGdZVyPL.d.ts} +3 -3
  192. package/dist/hono/index.d.ts +4 -4
  193. package/dist/{index-BAHGLvGD.d.ts → index-CENWq3zb.d.ts} +1 -1
  194. package/dist/{index-Do2ChN_y.d.ts → index-CQhPHHDF.d.ts} +20 -2
  195. package/dist/{index-DN9mfmhA.d.ts → index-CTu-3fJB.d.ts} +51 -3
  196. package/dist/{index-BfK6olKm.d.ts → index-Cc4mIQS8.d.ts} +2 -2
  197. package/dist/{index-BfkltrGJ.d.ts → index-CcAmFNVi.d.ts} +2 -2
  198. package/dist/{index-Ab4UeEn_.d.ts → index-Craf712U.d.ts} +44 -12
  199. package/dist/{index-B4gcStOz.d.ts → index-DZKCc2WM.d.ts} +3 -3
  200. package/dist/index.d.ts +2 -2
  201. package/dist/list/index.d.ts +1 -1
  202. package/dist/{list-query-BDVutLFr.d.ts → list-query-DYhgRTd5.d.ts} +1 -1
  203. package/dist/mapper/bin.js +5 -5
  204. package/dist/mcp/index.d.ts +3 -3
  205. package/dist/mcp/sdk/index.d.ts +2 -2
  206. package/dist/next/client/index.d.ts +81 -1
  207. package/dist/next/client/index.js +178 -2
  208. package/dist/next/index.d.ts +15 -14
  209. package/dist/orpc/index.d.ts +4 -4
  210. package/dist/otel/index.js +4 -4
  211. package/dist/{ports-DOhtuw3c.js → ports-CaVRXjxq.js} +1 -1
  212. package/dist/{ports-CpdiQv1D.d.ts → ports-mObLgH1z.d.ts} +1 -1
  213. package/dist/powersync/index.d.ts +2 -2
  214. package/dist/preset/index.d.ts +3 -3
  215. package/dist/{push-token-B3m3pWAv.d.ts → push-token-BSSvsWXZ.d.ts} +1 -1
  216. package/dist/query/index.d.ts +1 -1
  217. package/dist/{rate-limit-o44SYkVY.js → rate-limit-Dt9vAY4E.js} +53 -0
  218. package/dist/react/index.d.ts +6 -6
  219. package/dist/react/index.js +3 -3
  220. package/dist/react/native/index.d.ts +1 -1
  221. package/dist/react/native/index.js +1 -1
  222. package/dist/react/server.d.ts +12 -7
  223. package/dist/react/server.js +8 -3
  224. package/dist/react/session.d.ts +5 -0
  225. package/dist/react/session.js +9 -1
  226. package/dist/{react-BuDYI62C.js → react-CZUZYn9E.js} +41 -4
  227. package/dist/react-router/index.d.ts +3 -3
  228. package/dist/realtime/index.d.ts +5 -5
  229. package/dist/realtime/index.js +2 -1
  230. package/dist/{record-form-DB_5Ue_B.d.ts → record-form-D9ILuVtA.d.ts} +1 -1
  231. package/dist/records/index.d.ts +7 -7
  232. package/dist/relay-gKvKJ3xf.js +208 -0
  233. package/dist/{resource-execute-CzHuDsil.d.ts → resource-execute-Cpnob1Vj.d.ts} +1 -1
  234. package/dist/{resource-types-DnKiH4G2.d.ts → resource-types-pMeLs68X.d.ts} +1 -1
  235. package/dist/{resources-B9cQxhjw.d.ts → resources-7iq2-5_A.d.ts} +1 -1
  236. package/dist/server/index.d.ts +147 -9
  237. package/dist/server/index.js +396 -1
  238. package/dist/{server-types-7QPzW2bF.d.ts → server-types-CK8mUIUT.d.ts} +5 -354
  239. package/dist/{session-CfBCJGgc.d.ts → session-DWmFv3vB.d.ts} +2 -1
  240. package/dist/session-status-qQLm7jzh.d.ts +82 -0
  241. package/dist/{sign-in-BsIaNx2C.js → sign-in-CmPK1AgM.js} +20 -10
  242. package/dist/solid/index.d.ts +7 -7
  243. package/dist/solid/index.js +1 -1
  244. package/dist/solid-start/index.d.ts +3 -3
  245. package/dist/spec/index.d.ts +1 -1
  246. package/dist/sql/index.d.ts +3 -3
  247. package/dist/sql/index.js +1 -1
  248. package/dist/{sql-OLVW4t08.js → sql-Dl3Alk43.js} +127 -0
  249. package/dist/sqlite/index.d.ts +1 -1
  250. package/dist/ssr/index.d.ts +1 -1
  251. package/dist/storage/index.d.ts +3 -3
  252. package/dist/storage/index.js +54 -1
  253. package/dist/support-B4ZBVxt3.d.ts +357 -0
  254. package/dist/svelte/index.d.ts +7 -7
  255. package/dist/svelte/index.js +1 -1
  256. package/dist/sveltekit/index.d.ts +4 -4
  257. package/dist/{tags-BjHflcHF.d.ts → tags-qvVk79pp.d.ts} +1 -1
  258. package/dist/tanstack-db/index.d.ts +1 -1
  259. package/dist/tanstack-start/index.d.ts +3 -3
  260. package/dist/{tenant-DzFsg0Pc.d.ts → tenant-DOKIJI62.d.ts} +1 -1
  261. package/dist/testing/index.d.ts +8 -7
  262. package/dist/{timeline-WrFMgd3U.d.ts → timeline-CvTuXYvn.d.ts} +3 -3
  263. package/dist/{topic-DNFEFGKI.d.ts → topic-CZXHhZye.d.ts} +2 -2
  264. package/dist/{verify-CMou8dWZ.d.ts → verify-CjvNLY7N.d.ts} +1 -1
  265. package/dist/vue/index.d.ts +7 -7
  266. package/dist/vue/index.js +1 -1
  267. package/dist/{watch-topic-Bo9S2R4P.d.ts → watch-topic-BAxoPKZx.d.ts} +1 -1
  268. package/dist/yjs/index.d.ts +1 -1
  269. package/package.json +1 -1
  270. package/skills/better-supabase-api/SKILL.md +3 -1
  271. package/skills/better-supabase-auth/SKILL.md +7 -0
  272. package/dist/cli/add-CUH0rORo.js +0 -1
@@ -1,10 +1,12 @@
1
1
  import { n as dbError } from "../errors-BVFgt8BZ.js";
2
2
  import { n as isRecord } from "../guards-6F2bCtQR.js";
3
3
  import { t as base64ToText } from "../base64-B9Wlsdj7.js";
4
+ import { n as validate } from "../standard-BA3mLh6w.js";
4
5
  import { c as toProblem, i as isProblem, n as PROBLEM_TYPE_BASE, o as problemResponse, r as fromProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-Bf0cJ7tk.js";
5
6
  import { i as delegationOf, n as actorOf, r as agentClaims, t as IDENTITY_SCOPES } from "../actor-go7p9-CJ.js";
6
7
  import { i as userContext, r as impersonatorOf } from "../impersonation-DM3oWNoD.js";
7
8
  import { t as supportOf } from "../support-view-g4YLBeqX.js";
9
+ import { t as joinTopic } from "../channels-CfcFPuo_.js";
8
10
  import { r as toCsv, t as csvColumns } from "../csv-2rXGPNtP.js";
9
11
  import { A as suspendAccount, C as shouldRefresh, D as SERVER_EVENT_SOURCE, E as defineAuthorizationHooks, O as endSessions, S as shouldCheckSession, T as serverCore, a as unexpectedResponse, b as isPrefetch, c as extendServer, d as clearSupportCookie, f as supportCookie, h as TENANT_HEADER, i as resolveToken, j as deleteAccount, k as lastSignIns, l as supportStorage, m as supportCookieValue, n as flushEvents, o as withFlush, r as handle, s as createServer, u as SUPPORT_COOKIE, x as refreshFor, y as withSession } from "../adapter-Dyks1lX1.js";
10
12
  import { n as PRIMARY_COOKIE } from "../replicas-Dyoh3CEg.js";
@@ -57,6 +59,399 @@ const impersonationEvents = defineEvents({
57
59
  "support.denied": event()
58
60
  });
59
61
  //#endregion
62
+ //#region src/server/session-status.ts
63
+ const VOLATILE_CLAIMS = [
64
+ "exp",
65
+ "iat",
66
+ "nbf",
67
+ "jti"
68
+ ];
69
+ const HEADERS = {
70
+ "content-type": "application/json",
71
+ "cache-control": "no-store, private"
72
+ };
73
+ /** JSON with object keys sorted at every level, so equal claims hash alike. */
74
+ function canonical(value) {
75
+ if (value === void 0) return "null";
76
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
77
+ if (value !== null && typeof value === "object") return `{${Object.entries(value).filter(([, each]) => each !== void 0).toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, each]) => `${JSON.stringify(key)}:${canonical(each)}`).join(",")}}`;
78
+ return JSON.stringify(value);
79
+ }
80
+ async function sha256Hex(text) {
81
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
82
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
83
+ }
84
+ /**
85
+ * The fingerprint `sessionStatusRoute` reports for a user's claims and support
86
+ * session: a SHA-256 hex digest that changes when a claim that decides access
87
+ * changes (role, tenant, memberships, `aal`) or the support target does.
88
+ */
89
+ async function claimsFingerprint(claims, support, ignore = []) {
90
+ const skip = /* @__PURE__ */ new Set([...VOLATILE_CLAIMS, ...ignore]);
91
+ return sha256Hex(canonical({
92
+ claims: Object.fromEntries(Object.entries(claims).filter(([key]) => !skip.has(key))),
93
+ support: support ? {
94
+ session: support.session.id,
95
+ target: support.session.targetUserId,
96
+ readOnly: support.session.readOnly
97
+ } : null
98
+ }));
99
+ }
100
+ const statusOf = (resolution) => {
101
+ if (resolution.sessionEnded) return resolution.sessionEndedReason ?? "ended";
102
+ const { auth } = resolution;
103
+ if (auth.kind === "user") return "active";
104
+ return auth.kind === "anon" && auth.reason === "expired" ? "expired" : "signed_out";
105
+ };
106
+ /**
107
+ * A `GET` handler that tells a browser whether its session still holds, for
108
+ * apps that keep the session in httpOnly cookies and have no browser Supabase
109
+ * client: `useSessionWatch()` from `better-supabase/next/client` polls it. It
110
+ * answers `{ status, userId, fingerprint, expiresAt, support }` with
111
+ * `Cache-Control: no-store`.
112
+ *
113
+ * ```ts
114
+ * // app/api/session-status/route.ts
115
+ * export const GET = sessionStatusRoute(bsb);
116
+ * ```
117
+ */
118
+ function sessionStatusRoute(server, options = {}) {
119
+ const checkSession = options.checkSession ?? true;
120
+ const refresh = options.refresh ?? false;
121
+ return async (request) => {
122
+ if (request.method !== "GET" && request.method !== "HEAD") return new Response(null, {
123
+ status: 405,
124
+ headers: { allow: "GET, HEAD" }
125
+ });
126
+ const resolution = await server.resolve(request, {
127
+ checkSession,
128
+ refresh
129
+ });
130
+ const status = statusOf(resolution);
131
+ const { auth } = resolution;
132
+ const user = auth.kind === "user" ? auth : void 0;
133
+ const support = user && status === "active" ? await server.support.current(request, user) : void 0;
134
+ const body = {
135
+ status,
136
+ userId: user?.user.id ?? null,
137
+ fingerprint: user && status === "active" ? await claimsFingerprint(user.claims, support, options.ignoreClaims) : null,
138
+ expiresAt: user?.expiresAt ?? null,
139
+ support: support ? {
140
+ sessionId: support.session.id,
141
+ targetUserId: support.session.targetUserId,
142
+ readOnly: support.session.readOnly
143
+ } : null
144
+ };
145
+ return resolution.apply(new Response(request.method === "HEAD" ? null : JSON.stringify(body), {
146
+ status: 200,
147
+ headers: HEADERS
148
+ }));
149
+ };
150
+ }
151
+ //#endregion
152
+ //#region src/server/realtime-relay.ts
153
+ /** The channels `liveQuery` joins: `bsb:t:<schema>.<table>[:...]`, private. */
154
+ const liveRelayTopic = {
155
+ private: true,
156
+ match: (topic) => /^bsb:t:[^:]+\.[^:]+(?::.+)?$/.test(topic) ? {} : null
157
+ };
158
+ var Verdict = class extends Error {
159
+ allowed;
160
+ constructor(allowed) {
161
+ super(allowed ? "allowed" : "denied");
162
+ this.allowed = allowed;
163
+ }
164
+ };
165
+ const RLS_DENIED = "42501";
166
+ function codeOf(cause) {
167
+ return typeof cause === "object" && cause !== null && "code" in cause ? cause.code : void 0;
168
+ }
169
+ /**
170
+ * Whether `claims` may receive or send on `topic`, by the `realtime.messages`
171
+ * policies, checked the way Realtime checks a join: a probe message is
172
+ * inserted, then read (receive) or written (send) as the caller, and the
173
+ * transaction rolls back.
174
+ */
175
+ async function realtimeTopicAccess(postgres, input) {
176
+ const role = input.claims["role"] === "anon" ? "anon" : "authenticated";
177
+ const sub = input.claims["sub"];
178
+ const extension = input.extension ?? "broadcast";
179
+ const insert = "insert into realtime.messages (topic, extension, event, payload, private) values ($1, $2, 'bsb_relay_probe', '{}'::jsonb, true)";
180
+ const assume = async (sql) => {
181
+ await sql.queryRaw("select set_config('request.jwt.claims', $1, true), set_config('request.jwt.claim.sub', $2, true), set_config('request.jwt.claim.role', $3, true), set_config('realtime.topic', $4, true), set_config('role', $3, true)", [
182
+ JSON.stringify({
183
+ ...input.claims,
184
+ role
185
+ }),
186
+ typeof sub === "string" ? sub : "",
187
+ role,
188
+ input.topic
189
+ ]);
190
+ };
191
+ try {
192
+ await postgres.transaction(async (sql) => {
193
+ if (input.action === "receive") {
194
+ const [probe] = await sql.queryRaw(`${insert} returning id::text as id`, [input.topic, extension]);
195
+ await assume(sql);
196
+ const [row] = await sql.queryRaw("select exists (select 1 from realtime.messages m where m.id::text = $1) as allowed", [probe?.id ?? ""]);
197
+ throw new Verdict(row?.allowed === true);
198
+ }
199
+ await assume(sql);
200
+ try {
201
+ await sql.queryRaw(insert, [input.topic, extension]);
202
+ } catch (cause) {
203
+ if (codeOf(cause) === RLS_DENIED) throw new Verdict(false);
204
+ throw cause;
205
+ }
206
+ throw new Verdict(true);
207
+ });
208
+ } catch (cause) {
209
+ if (cause instanceof Verdict) return cause.allowed;
210
+ throw cause;
211
+ }
212
+ return false;
213
+ }
214
+ const encoder = new TextEncoder();
215
+ const frame = (event, data) => encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
216
+ const PING = encoder.encode(": ping\n\n");
217
+ const STREAM_HEADERS = {
218
+ "content-type": "text/event-stream; charset=utf-8",
219
+ "cache-control": "no-store, no-transform",
220
+ "x-accel-buffering": "no"
221
+ };
222
+ function readOnlyError() {
223
+ return {
224
+ ...dbError("forbidden", "The support session is read-only"),
225
+ code: "SUPPORT_READ_ONLY"
226
+ };
227
+ }
228
+ function isSendBody(value) {
229
+ if (typeof value !== "object" || value === null) return false;
230
+ const body = value;
231
+ return typeof body.topic === "string" && typeof body.event === "string" && body.event !== "";
232
+ }
233
+ /**
234
+ * Realtime for callers without a browser token of their own: a support session
235
+ * ("view as user"), or an app that keeps sessions in httpOnly cookies. `GET
236
+ * ?topic=<name>` resolves the caller, checks the topic's access as that caller
237
+ * (the viewed user in a support session), joins upstream with the secret key
238
+ * and streams the topic's broadcasts and presence as server-sent events. `POST
239
+ * { topic, event, payload }` broadcasts for the caller after the same check on
240
+ * the send policy; read-only support sessions can't send.
241
+ * `relayRealtimeClient(url)` from `better-supabase/realtime` is the client.
242
+ *
243
+ * ```ts
244
+ * // app/api/realtime/route.ts
245
+ * export const { GET, POST } = realtimeRelay(bsb, {
246
+ * topics: [notifications, liveRelayTopic],
247
+ * postgres,
248
+ * });
249
+ * ```
250
+ */
251
+ function realtimeRelay(server, options) {
252
+ const { postgres, authorize } = options;
253
+ if (!postgres && !authorize) throw new TypeError("realtimeRelay needs `postgres` to check the realtime.messages policies, or `authorize`");
254
+ const heartbeatMs = options.heartbeatMs ?? 25e3;
255
+ const maxDurationMs = options.maxDurationMs ?? 24e4;
256
+ const maxQueued = Math.max(1, options.maxQueued ?? 256);
257
+ const retryMs = options.retryMs ?? 1e3;
258
+ const refresh = options.refresh ?? false;
259
+ let upstream = options.upstream;
260
+ const upstreamClient = () => {
261
+ upstream ??= server.admin().$client;
262
+ return upstream;
263
+ };
264
+ const problem = (error) => problemResponse(error, { format: options.problem });
265
+ const topicOf = (name) => options.topics.find((each) => each.match(name) !== null);
266
+ const allowed = async (input) => {
267
+ try {
268
+ if (authorize) return await authorize(input);
269
+ if (input.auth.kind === "service") return true;
270
+ return postgres ? await realtimeTopicAccess(postgres, {
271
+ claims: input.claims,
272
+ topic: input.topic,
273
+ action: input.action
274
+ }) : false;
275
+ } catch {
276
+ return false;
277
+ }
278
+ };
279
+ const claimsOf = (auth, support) => {
280
+ if (support) return support.claims;
281
+ switch (auth.kind) {
282
+ case "user": return auth.claims;
283
+ case "apiKey": return auth.claims;
284
+ case "anon": return { role: "anon" };
285
+ case "service": return { role: "service_role" };
286
+ case "invalid": return { role: "anon" };
287
+ default: return auth;
288
+ }
289
+ };
290
+ /** Resolves the caller and checks `action` on `name`; a Response refuses. */
291
+ const admit = async (request, name, action) => {
292
+ const topic = topicOf(name);
293
+ if (!topic) return { refusal: problem(dbError("not_found", `No relayed topic "${name}"`)) };
294
+ const ctx = await server.context(request, { refresh });
295
+ const { auth, support } = ctx;
296
+ if (auth.kind === "invalid") return { refusal: ctx.apply(problem(auth.error)) };
297
+ if (!(auth.kind !== "anon") && (topic.private || action === "send")) return { refusal: ctx.apply(problem(dbError("unauthorized", "Sign in to join this topic"))) };
298
+ if (action === "send" && support?.session.readOnly === true) return { refusal: ctx.apply(problem(readOnlyError())) };
299
+ if (!(!topic.private && action === "receive" ? true : await allowed({
300
+ topic: name,
301
+ action,
302
+ auth,
303
+ support,
304
+ claims: claimsOf(auth, support)
305
+ }))) return { refusal: ctx.apply(problem(dbError("forbidden", `Not allowed to ${action} on "${name}"`))) };
306
+ return {
307
+ topic,
308
+ ctx
309
+ };
310
+ };
311
+ const expiryOf = (auth, support) => {
312
+ const times = [];
313
+ if (auth.kind === "user" && auth.expiresAt !== null) times.push(auth.expiresAt * 1e3);
314
+ if (support) times.push(support.session.expiresAt.epochMilliseconds);
315
+ return times.length === 0 ? void 0 : Math.min(...times);
316
+ };
317
+ const GET = async (request) => {
318
+ const name = new URL(request.url).searchParams.get("topic");
319
+ if (!name) return problem(dbError("invalid_request", "Pass ?topic=<name>"));
320
+ const admitted = await admit(request, name, "receive");
321
+ if ("refusal" in admitted) return admitted.refusal;
322
+ const { topic, ctx } = admitted;
323
+ const expiresAt = expiryOf(ctx.resolution.auth, ctx.support);
324
+ const client = upstreamClient();
325
+ let finish = () => void 0;
326
+ const stream = new ReadableStream({
327
+ start(controller) {
328
+ let closed = false;
329
+ let subscribed = false;
330
+ let leave;
331
+ const timers = [];
332
+ const enqueue = (chunk) => {
333
+ try {
334
+ controller.enqueue(chunk);
335
+ } catch {
336
+ finish(null);
337
+ }
338
+ };
339
+ const push = (chunk) => {
340
+ if (closed) return;
341
+ if ((controller.desiredSize ?? 1) <= 0) {
342
+ finish("overflow");
343
+ return;
344
+ }
345
+ enqueue(chunk);
346
+ };
347
+ finish = (reason) => {
348
+ if (closed) return;
349
+ closed = true;
350
+ for (const timer of timers) {
351
+ clearTimeout(timer);
352
+ clearInterval(timer);
353
+ }
354
+ request.signal.removeEventListener("abort", aborted);
355
+ leave?.();
356
+ if (reason !== null) enqueue(frame("end", { reason }));
357
+ try {
358
+ controller.close();
359
+ } catch {}
360
+ };
361
+ const aborted = () => {
362
+ finish(null);
363
+ };
364
+ const onStatus = (status) => {
365
+ switch (status) {
366
+ case "joining": return;
367
+ case "subscribed":
368
+ push(frame("status", {
369
+ status,
370
+ rejoin: subscribed
371
+ }));
372
+ subscribed = true;
373
+ return;
374
+ case "error":
375
+ if (subscribed) push(frame("status", { status }));
376
+ else finish("upstream_error");
377
+ return;
378
+ case "closed":
379
+ finish("closed");
380
+ return;
381
+ default: finish(status);
382
+ }
383
+ };
384
+ const subscriber = {
385
+ onStatus,
386
+ receive: (message) => {
387
+ push(frame("broadcast", {
388
+ event: message.event,
389
+ payload: message.payload
390
+ }));
391
+ },
392
+ ...topic.presence ? { sync: (state) => {
393
+ push(frame("presence", state));
394
+ } } : {}
395
+ };
396
+ enqueue(encoder.encode(`retry: ${String(retryMs)}\n\n`));
397
+ if (request.signal.aborted) {
398
+ finish(null);
399
+ return;
400
+ }
401
+ request.signal.addEventListener("abort", aborted);
402
+ timers.push(setInterval(() => {
403
+ push(PING);
404
+ }, heartbeatMs), setTimeout(() => {
405
+ finish("max_duration");
406
+ }, maxDurationMs));
407
+ if (expiresAt !== void 0) timers.push(setTimeout(() => {
408
+ finish("expired");
409
+ }, Math.max(0, expiresAt - Date.now())));
410
+ try {
411
+ leave = joinTopic(client, name, topic.private, false, topic.presence === true, subscriber).leave;
412
+ } catch {
413
+ finish("upstream_error");
414
+ }
415
+ },
416
+ cancel() {
417
+ finish(null);
418
+ }
419
+ }, new CountQueuingStrategy({ highWaterMark: maxQueued }));
420
+ return ctx.apply(new Response(stream, {
421
+ status: 200,
422
+ headers: STREAM_HEADERS
423
+ }));
424
+ };
425
+ const POST = async (request) => {
426
+ const body = await request.json().catch(() => void 0);
427
+ if (!isSendBody(body)) return problem(dbError("invalid_request", "Send a JSON body with `topic`, `event` and `payload`"));
428
+ const known = topicOf(body.topic);
429
+ if (known && !known.clientSend) return problem(dbError("forbidden", `"${body.topic}" takes no client broadcasts`));
430
+ const admitted = await admit(request, body.topic, "send");
431
+ if ("refusal" in admitted) return admitted.refusal;
432
+ const { topic, ctx } = admitted;
433
+ const schema = topic.events?.[body.event];
434
+ const checked = schema ? await validate(schema, body.payload, "payload") : {
435
+ ok: true,
436
+ data: body.payload
437
+ };
438
+ if (!checked.ok) return ctx.apply(problem(checked.error));
439
+ const client = upstreamClient();
440
+ const open = client.getChannels().some((existing) => existing.topic === `realtime:${body.topic}`);
441
+ const channel = client.channel(body.topic, { config: { private: topic.private } });
442
+ try {
443
+ const sent = await channel.httpSend(body.event, checked.data);
444
+ return ctx.apply(sent.success ? new Response(null, { status: 202 }) : problem(dbError("network", sent.error)));
445
+ } finally {
446
+ if (!open) await client.removeChannel(channel);
447
+ }
448
+ };
449
+ return {
450
+ GET,
451
+ POST
452
+ };
453
+ }
454
+ //#endregion
60
455
  //#region src/server/entries/upstream.ts
61
456
  /** Repository context from a `@supabase/server` context. */
62
457
  function contextFromSupabase(ctx) {
@@ -452,4 +847,4 @@ function subscribeAuthzVersions(options) {
452
847
  return subscription;
453
848
  }
454
849
  //#endregion
455
- export { AUTH_CACHE_HEADERS, DB_CALLS_HEADER, IDENTITY_SCOPES, PERMISSION_CAP_CLAIM, PRIMARY_COOKIE, PROBLEM_CONTENT_TYPE, PROBLEM_TYPE_BASE, SERVER_EVENT_SOURCE, SUPPORT_COOKIE, TENANT_HEADER, aalOf, accessTokenResolver, actorOf, agentClaims, amrOf, authContext, authModeOf, authzVersionOf, bindAuthorizer, cacheTagsOf, checkAal, checkSession, clearSessionCookies, clearSupportCookie, clientIp, contextFromSupabase, createServer, csvColumns, csvResponse, defineAuthorizationHooks, defineAuthorizer, defineResource, delegationOf, deleteAccount, endSessions, extendServer, flushEvents, fromProblem, guard, handle, hasRole, impersonationEvents, impersonatorOf, isPrefetch, isProblem, isRateLimitPolicyName, isResult, isSessionSnapshot, issuedAtOf, jwtClaimsOf, lastSignIns, parseDbStats, passwordGrant, periodSeconds, problemResponse, rateLimitHeader, rateLimitPolicyHeader, readSession, refreshFor, refreshSession, requestMemo, resolveAuth, resolveToken, respond, rolesAt, safeRedirectPath, serverCore, sessionCacheKey, sessionCookieName, sessionMemo, sessionStatus, settle, shouldCheckSession, shouldRefresh, snapshotOf, sqlSupportStore, subjectReaders, subscribeAuthzVersions, supportClaims, supportCookie, supportCookieValue, supportOf, supportSessions, supportStorage, suspendAccount, tagCache, tagFor, toCsv, toProblem, toSession, unexpectedResponse, userClaimsOf, withBetterDb, withBetterPostgres, withBetterSupabase, withBlock, withDbStats, withFlush, withGuard, withServerTiming, withSession, writeSession };
850
+ export { AUTH_CACHE_HEADERS, DB_CALLS_HEADER, IDENTITY_SCOPES, PERMISSION_CAP_CLAIM, PRIMARY_COOKIE, PROBLEM_CONTENT_TYPE, PROBLEM_TYPE_BASE, SERVER_EVENT_SOURCE, SUPPORT_COOKIE, TENANT_HEADER, aalOf, accessTokenResolver, actorOf, agentClaims, amrOf, authContext, authModeOf, authzVersionOf, bindAuthorizer, cacheTagsOf, checkAal, checkSession, claimsFingerprint, clearSessionCookies, clearSupportCookie, clientIp, contextFromSupabase, createServer, csvColumns, csvResponse, defineAuthorizationHooks, defineAuthorizer, defineResource, delegationOf, deleteAccount, endSessions, extendServer, flushEvents, fromProblem, guard, handle, hasRole, impersonationEvents, impersonatorOf, isPrefetch, isProblem, isRateLimitPolicyName, isResult, isSessionSnapshot, issuedAtOf, jwtClaimsOf, lastSignIns, liveRelayTopic, parseDbStats, passwordGrant, periodSeconds, problemResponse, rateLimitHeader, rateLimitPolicyHeader, readSession, realtimeRelay, realtimeTopicAccess, refreshFor, refreshSession, requestMemo, resolveAuth, resolveToken, respond, rolesAt, safeRedirectPath, serverCore, sessionCacheKey, sessionCookieName, sessionMemo, sessionStatus, sessionStatusRoute, settle, shouldCheckSession, shouldRefresh, snapshotOf, sqlSupportStore, subjectReaders, subscribeAuthzVersions, supportClaims, supportCookie, supportCookieValue, supportOf, supportSessions, supportStorage, suspendAccount, tagCache, tagFor, toCsv, toProblem, toSession, unexpectedResponse, userClaimsOf, withBetterDb, withBetterPostgres, withBetterSupabase, withBlock, withDbStats, withFlush, withGuard, withServerTiming, withSession, writeSession };