better-supabase 0.5.1 → 0.6.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 (536) hide show
  1. package/README.md +187 -46
  2. package/dist/access-sql-B0dF-SoF.js +181 -0
  3. package/dist/{actor-DaJjqD5R.d.ts → actor-CQTlc1Hh.d.ts} +1 -1
  4. package/dist/adapter-Bc1o3zyy.js +1106 -0
  5. package/dist/agents-CVHGd7It.d.ts +94 -0
  6. package/dist/ai-cache-CBWs1W0k.js +80 -0
  7. package/dist/ai-cache-DaDu_G5L.d.ts +64 -0
  8. package/dist/ai-chat-7amvXjmQ.d.ts +590 -0
  9. package/dist/ai-chat-B24UI962.js +743 -0
  10. package/dist/ai-files-CSPNswUY.js +319 -0
  11. package/dist/ai-sdk/agents/index.d.ts +74 -0
  12. package/dist/ai-sdk/agents/index.js +159 -0
  13. package/dist/ai-sdk/batches/index.d.ts +74 -0
  14. package/dist/ai-sdk/batches/index.js +180 -0
  15. package/dist/ai-sdk/cache/index.d.ts +38 -0
  16. package/dist/ai-sdk/cache/index.js +127 -0
  17. package/dist/ai-sdk/chat/index.d.ts +2 -0
  18. package/dist/ai-sdk/chat/index.js +246 -0
  19. package/dist/ai-sdk/embeddings/index.d.ts +3 -0
  20. package/dist/ai-sdk/embeddings/index.js +2 -0
  21. package/dist/ai-sdk/files/index.d.ts +61 -0
  22. package/dist/ai-sdk/files/index.js +111 -0
  23. package/dist/ai-sdk/index.d.ts +145 -0
  24. package/dist/ai-sdk/index.js +174 -0
  25. package/dist/ai-sdk/mcp/index.d.ts +102 -0
  26. package/dist/ai-sdk/mcp/index.js +362 -0
  27. package/dist/ai-sdk/memory/index.d.ts +67 -0
  28. package/dist/ai-sdk/memory/index.js +175 -0
  29. package/dist/ai-sdk/react/index.d.ts +2 -0
  30. package/dist/ai-sdk/react/index.js +3 -0
  31. package/dist/ai-sdk/react/server.d.ts +6 -0
  32. package/dist/ai-sdk/react/server.js +7 -0
  33. package/dist/ai-sdk/workflow/index.d.ts +249 -0
  34. package/dist/ai-sdk/workflow/index.js +575 -0
  35. package/dist/ai-sdk/workflow/react/index.d.ts +20 -0
  36. package/dist/ai-sdk/workflow/react/index.js +66 -0
  37. package/dist/ai-sdk/workflow/react/server.d.ts +10 -0
  38. package/dist/ai-sdk/workflow/react/server.js +12 -0
  39. package/dist/ai-tasks-Cd1HMFvB.d.ts +131 -0
  40. package/dist/announcements-CiyOs-xP.js +107 -0
  41. package/dist/announcements-ZRGnScWI.d.ts +72 -0
  42. package/dist/api-keys-rCGtKaZA.d.ts +156 -0
  43. package/dist/assistant-DwdpDH1q.d.ts +147 -0
  44. package/dist/astro/index.d.ts +86 -0
  45. package/dist/astro/index.js +93 -0
  46. package/dist/attributes-5dYul0u0.js +13 -0
  47. package/dist/auth-link-BjFPqwLz.d.ts +97 -0
  48. package/dist/auth-link-DrDX9KOT.js +114 -0
  49. package/dist/authorization-BFCqamMs.d.ts +172 -0
  50. package/dist/before-route-CnR0C0Jr.js +30 -0
  51. package/dist/bind-Bu_ZNa3W.d.ts +62 -0
  52. package/dist/bind-l4kAvYSv.js +97 -0
  53. package/dist/block-events-CrT8r7Fe.js +76 -0
  54. package/dist/block-fields-GFkxl4dP.js +49 -0
  55. package/dist/block-fields-SJ6fEf3o.d.ts +31 -0
  56. package/dist/block-helpers-CKtbFcRv.d.ts +37 -0
  57. package/dist/block-helpers-DWHxHsEQ.js +190 -0
  58. package/dist/block-hooks-BmjMyXTa.js +114 -0
  59. package/dist/block-hooks-plbZ0AbJ.d.ts +107 -0
  60. package/dist/{kit-transport-DJlHQUBU.js → block-transport-BLr-ZgLc.js} +11 -7
  61. package/dist/block-transport-DvaoPuZ9.d.ts +48 -0
  62. package/dist/blocks/agents/index.d.ts +3 -0
  63. package/dist/blocks/agents/index.js +123 -0
  64. package/dist/blocks/ai-cache/index.d.ts +3 -0
  65. package/dist/blocks/ai-cache/index.js +3 -0
  66. package/dist/blocks/ai-chat/index.d.ts +31 -0
  67. package/dist/blocks/ai-chat/index.js +5 -0
  68. package/dist/blocks/ai-chat/react/index.d.ts +77 -0
  69. package/dist/blocks/ai-chat/react/index.js +387 -0
  70. package/dist/blocks/ai-chat/react/server.d.ts +16 -0
  71. package/dist/blocks/ai-chat/react/server.js +14 -0
  72. package/dist/blocks/ai-files/index.d.ts +14 -0
  73. package/dist/blocks/ai-files/index.js +3 -0
  74. package/dist/blocks/ai-providers/index.d.ts +7 -0
  75. package/dist/blocks/ai-providers/index.js +211 -0
  76. package/dist/blocks/ai-tasks/index.d.ts +3 -0
  77. package/dist/blocks/ai-tasks/index.js +203 -0
  78. package/dist/blocks/announcements/index.d.ts +3 -0
  79. package/dist/blocks/announcements/index.js +3 -0
  80. package/dist/blocks/announcements/react/index.d.ts +31 -0
  81. package/dist/blocks/announcements/react/index.js +86 -0
  82. package/dist/blocks/announcements/react/server.d.ts +6 -0
  83. package/dist/blocks/announcements/react/server.js +7 -0
  84. package/dist/blocks/api-keys/index.d.ts +35 -0
  85. package/dist/blocks/api-keys/index.js +334 -0
  86. package/dist/blocks/attachments/index.d.ts +210 -0
  87. package/dist/blocks/attachments/index.js +222 -0
  88. package/dist/blocks/audit/index.d.ts +366 -0
  89. package/dist/blocks/audit/index.js +714 -0
  90. package/dist/blocks/billing/index.d.ts +265 -0
  91. package/dist/blocks/billing/index.js +400 -0
  92. package/dist/blocks/comments/index.d.ts +200 -0
  93. package/dist/blocks/comments/index.js +207 -0
  94. package/dist/blocks/connectors/index.d.ts +3 -0
  95. package/dist/blocks/connectors/index.js +164 -0
  96. package/dist/blocks/data-lifecycle/index.d.ts +251 -0
  97. package/dist/blocks/data-lifecycle/index.js +353 -0
  98. package/dist/blocks/entitlements/index.d.ts +14 -0
  99. package/dist/blocks/entitlements/index.js +25 -0
  100. package/dist/blocks/flags/index.d.ts +226 -0
  101. package/dist/blocks/flags/index.js +282 -0
  102. package/dist/blocks/inbox/index.d.ts +6 -0
  103. package/dist/blocks/inbox/index.js +499 -0
  104. package/dist/blocks/inbox/react/index.d.ts +2 -0
  105. package/dist/blocks/inbox/react/index.js +225 -0
  106. package/dist/blocks/inbox/react/server.d.ts +10 -0
  107. package/dist/blocks/inbox/react/server.js +12 -0
  108. package/dist/blocks/index.d.ts +65 -0
  109. package/dist/blocks/index.js +77 -0
  110. package/dist/blocks/jobs/index.d.ts +297 -0
  111. package/dist/blocks/jobs/index.js +1051 -0
  112. package/dist/blocks/knowledge/index.d.ts +3 -0
  113. package/dist/blocks/knowledge/index.js +3 -0
  114. package/dist/blocks/memory/index.d.ts +8 -0
  115. package/dist/blocks/memory/index.js +289 -0
  116. package/dist/blocks/notifications/index.d.ts +297 -0
  117. package/dist/blocks/notifications/index.js +425 -0
  118. package/dist/blocks/notifications/react/index.d.ts +44 -0
  119. package/dist/blocks/notifications/react/index.js +67 -0
  120. package/dist/blocks/notifications/react/server.d.ts +6 -0
  121. package/dist/blocks/notifications/react/server.js +7 -0
  122. package/dist/blocks/onboarding/index.d.ts +3 -0
  123. package/dist/blocks/onboarding/index.js +64 -0
  124. package/dist/blocks/onboarding/react/index.d.ts +28 -0
  125. package/dist/blocks/onboarding/react/index.js +78 -0
  126. package/dist/blocks/onboarding/react/server.d.ts +7 -0
  127. package/dist/blocks/onboarding/react/server.js +7 -0
  128. package/dist/blocks/organizations/index.d.ts +241 -0
  129. package/dist/blocks/organizations/index.js +400 -0
  130. package/dist/blocks/outbox/index.d.ts +3 -0
  131. package/dist/blocks/outbox/index.js +266 -0
  132. package/dist/blocks/profiles/index.d.ts +40 -0
  133. package/dist/blocks/profiles/index.js +28 -0
  134. package/dist/blocks/push/index.d.ts +185 -0
  135. package/dist/blocks/push/index.js +258 -0
  136. package/dist/blocks/settings/index.d.ts +89 -0
  137. package/dist/blocks/settings/index.js +104 -0
  138. package/dist/blocks/sso/index.d.ts +198 -0
  139. package/dist/blocks/sso/index.js +1297 -0
  140. package/dist/blocks/usage/index.d.ts +4 -0
  141. package/dist/blocks/usage/index.js +233 -0
  142. package/dist/blocks/waitlist/index.d.ts +117 -0
  143. package/dist/blocks/waitlist/index.js +132 -0
  144. package/dist/blocks/webhooks/index.d.ts +304 -0
  145. package/dist/{webhooks → blocks/webhooks}/index.js +468 -148
  146. package/dist/blocks/workflow-builder/index.d.ts +21 -0
  147. package/dist/blocks/workflow-builder/index.js +5 -0
  148. package/dist/blocks/workflow-builder/react/index.d.ts +47 -0
  149. package/dist/blocks/workflow-builder/react/index.js +119 -0
  150. package/dist/blocks/workflow-builder/react/server.d.ts +8 -0
  151. package/dist/blocks/workflow-builder/react/server.js +11 -0
  152. package/dist/blocks/workflows/index.d.ts +3 -0
  153. package/dist/blocks/workflows/index.js +3 -0
  154. package/dist/blocks/workflows/react/index.d.ts +47 -0
  155. package/dist/blocks/workflows/react/index.js +137 -0
  156. package/dist/blocks/workflows/react/server.d.ts +8 -0
  157. package/dist/blocks/workflows/react/server.js +11 -0
  158. package/dist/{index-SaTsEfGP.d.ts → bucket-Cl46E3jS.d.ts} +134 -91
  159. package/dist/{simplify-_0oUKqCd.js → build-BXIzE6Xv.js} +222 -124
  160. package/dist/{channel-FQW-CnGR.d.ts → channel-_2Mx1Oyu.d.ts} +2 -2
  161. package/dist/channels-CkgQ_X60.js +114 -0
  162. package/dist/chat-sdk/index.d.ts +303 -0
  163. package/dist/chat-sdk/index.js +733 -0
  164. package/dist/chat-sdk/react/index.d.ts +2 -0
  165. package/dist/chat-sdk/react/index.js +3 -0
  166. package/dist/chat-sdk/react/server.d.ts +3 -0
  167. package/dist/chat-sdk/react/server.js +2 -0
  168. package/dist/claims-DDBQT6BK.js +61 -0
  169. package/dist/{claims-BAf6c7is.js → claims-PqzCxaTD.js} +39 -2
  170. package/dist/cli/bin.js +1 -1
  171. package/dist/cli/{codemod-DqV7612m.js → codemod-Bl_tQpHn.js} +105 -2
  172. package/dist/cli/config-B9HkL8nF.js +2 -0
  173. package/dist/cli/config-DU7EJImt.js +49 -0
  174. package/dist/cli/config-NiPxAmUF.js +1077 -0
  175. package/dist/cli/{dist-xn4wlboc.js → dist-DRAVaE6w.js} +111 -1
  176. package/dist/cli/{doctor-C_A-KEAz.js → doctor-B4JOm_ox.js} +991 -317
  177. package/dist/cli/{env-DmKv_G4A.js → env-CJLbUVPB.js} +1 -1
  178. package/dist/cli/{env-jIFUADLY.js → env-qXgyMV6r.js} +2 -2
  179. package/dist/cli/fresh-import-BeYf9I3x.js +40 -0
  180. package/dist/cli/gen-BGLb_zou.js +3100 -0
  181. package/dist/cli/gen-DAH8FM2W.js +2 -0
  182. package/dist/cli/index.d.ts +44 -5
  183. package/dist/cli/index.js +7 -7
  184. package/dist/cli/{init-DMa6M7hV.js → init-BIIwclPg.js} +242 -27
  185. package/dist/cli/{introspect-BKlaIu_M.js → introspect-DNUTuNvb.js} +5 -5
  186. package/dist/cli/{io-k41_z-jT.js → io-DI1mTdSA.js} +12 -3
  187. package/dist/cli/{keys-ByKHnvP9.js → keys-BGCnnb-s.js} +8 -6
  188. package/dist/cli/{ohash-D7bG_EOb.js → ohash-BjQY1cBb.js} +1 -1
  189. package/dist/cli/{openapi-Cp5Fyta-.js → openapi-BlRJ18s3.js} +2 -2
  190. package/dist/cli/{project-B_GeXyHh.js → project-DZRaBaY7.js} +11 -6
  191. package/dist/cli/read-sets-CETOSFx4.js +223 -0
  192. package/dist/cli/{run-Bo_NLT5n.js → run-DNWxHynX.js} +33 -23
  193. package/dist/cli/{seed-CUlqWh-8.js → seed-DQzqT7LP.js} +8 -6
  194. package/dist/cli/{skills-LJJIhLFR.js → skills-QQs4q68J.js} +3 -2
  195. package/dist/cli/sql-D0GUtUPp.js +536 -0
  196. package/dist/cli/{config-CPVpXP5I.js → supabase-toml-B-bIukQF.js} +72 -942
  197. package/dist/cli/typegen-version-DeFbtzYI.js +8 -0
  198. package/dist/cli/{version-BCtlgDQx.js → version-hr88OZG0.js} +1 -1
  199. package/dist/client/index.d.ts +3 -2
  200. package/dist/client/index.js +11 -71
  201. package/dist/client/native/index.d.ts +228 -0
  202. package/dist/client/native/index.js +361 -0
  203. package/dist/client-CI0Z2zWX.js +277 -0
  204. package/dist/cloud-event-BnmD5fb9.js +46 -0
  205. package/dist/cloud-event-C1JCc1bH.d.ts +50 -0
  206. package/dist/{compiler-BXH_aZ7N.d.ts → compiler-DY_wkj4R.d.ts} +2 -2
  207. package/dist/compose-BIBNgAnq.js +53 -0
  208. package/dist/composite-Bt1YK3Ke.d.ts +146 -0
  209. package/dist/composite-DhE66P5u.js +130 -0
  210. package/dist/config/index.d.ts +6 -4
  211. package/dist/config/index.js +5 -5
  212. package/dist/{config-Bv7gvfzC.d.ts → config-D44Y7PAv.d.ts} +406 -214
  213. package/dist/{config-DreERlKo.js → config-Dn7F5k9A.js} +62 -14
  214. package/dist/connector-Dv8kK0MO.d.ts +107 -0
  215. package/dist/connectors-hkLCwyEz.d.ts +120 -0
  216. package/dist/context-BCrWe7ow.d.ts +237 -0
  217. package/dist/credentials/index.d.ts +31 -0
  218. package/dist/credentials/index.js +168 -0
  219. package/dist/cron-DVPUg5k3.js +145 -0
  220. package/dist/csv-CPAzEmB6.js +25 -0
  221. package/dist/db-exception-CnWcYPxL.js +15 -0
  222. package/dist/{define-cNKzqwL_.js → define-B-TVjC25.js} +784 -227
  223. package/dist/{define-Bp_BZa5h.d.ts → define-CJJkkr1-.d.ts} +353 -61
  224. package/dist/edge/index.d.ts +69 -11
  225. package/dist/edge/index.js +191 -75
  226. package/dist/elysia/index.d.ts +59 -0
  227. package/dist/elysia/index.js +62 -0
  228. package/dist/embeddings-4ZvFzjsU.js +127 -0
  229. package/dist/embeddings-C9KGQnXt.d.ts +68 -0
  230. package/dist/{refresh-CFPX0Qft.d.ts → ended-jLfvw8h-.d.ts} +20 -2
  231. package/dist/{entitlements-BiJZ4t49.d.ts → entitlements-DHrY2UDZ.d.ts} +16 -7
  232. package/dist/entitlements-DRl6ZJUm.js +24 -0
  233. package/dist/envelope-DqnYGRw7.js +47 -0
  234. package/dist/{errors-DtwXa6b8.js → errors-B11Duf73.js} +82 -13
  235. package/dist/errors-DLjJHPLK.js +44 -0
  236. package/dist/escape-like-DHfx6jRr.js +7 -0
  237. package/dist/escape-like-ImTK0ps_.d.ts +15 -0
  238. package/dist/eve/index.d.ts +220 -0
  239. package/dist/eve/index.js +504 -0
  240. package/dist/eve-gT2Fpjut.d.ts +151 -0
  241. package/dist/events/index.d.ts +4 -3
  242. package/dist/events/index.js +9 -50
  243. package/dist/events-DWDhx77e.d.ts +549 -0
  244. package/dist/executor-Bq28caBo.js +718 -0
  245. package/dist/executor-C-R4pucm.js +83 -0
  246. package/dist/executor-CaWigTyR.d.ts +148 -0
  247. package/dist/executor-D14tGopJ.d.ts +36 -0
  248. package/dist/{executor-Cghdstwu.d.ts → executor-DbvExCFC.d.ts} +7 -1
  249. package/dist/{sql-Coh6n4cC.js → executor-De1Of3m3.js} +210 -11
  250. package/dist/executor-DvsBbNBS.d.ts +261 -0
  251. package/dist/expo/index.d.ts +79 -0
  252. package/dist/expo/index.js +129 -0
  253. package/dist/expo-sqlite/index.d.ts +58 -0
  254. package/dist/expo-sqlite/index.js +104 -0
  255. package/dist/field-errors-BFOa7WhB.d.ts +99 -0
  256. package/dist/field-errors-VF1yb8yI.js +13 -0
  257. package/dist/framework-CONDyPYx.js +51 -0
  258. package/dist/framework-DuVb4Xa8.d.ts +25 -0
  259. package/dist/gateway-C1ssRHnf.d.ts +130 -0
  260. package/dist/graph-BGyLLFLX.js +265 -0
  261. package/dist/h3/index.d.ts +59 -0
  262. package/dist/h3/index.js +55 -0
  263. package/dist/h3/v1/index.d.ts +63 -0
  264. package/dist/h3/v1/index.js +49 -0
  265. package/dist/history-BZih6SFP.js +64 -0
  266. package/dist/hono/index.d.ts +56 -7
  267. package/dist/hono/index.js +97 -45
  268. package/dist/{react-C_s7kiNz.js → hooks-1BTCJsiw.js} +104 -133
  269. package/dist/http-Cv4T29xh.js +40 -0
  270. package/dist/http-DdYadv-o.js +126 -0
  271. package/dist/image-url-pIVlO6R1.js +41 -0
  272. package/dist/{impersonation-BgSHAYLf.js → impersonation-DcqaTBHx.js} +21 -6
  273. package/dist/inbox-i7E1ppo6.d.ts +384 -0
  274. package/dist/index-ACiNPN4Z.d.ts +74 -0
  275. package/dist/{index-DgILJRXj.d.ts → index-Bepi-Hgg.d.ts} +91 -28
  276. package/dist/index-BkWT_uBO.d.ts +51 -0
  277. package/dist/index-BpFYMsi0.d.ts +53 -0
  278. package/dist/{index-C7f8cOBM.d.ts → index-Bxih3l0u.d.ts} +2 -2
  279. package/dist/index-CaxrMD1A.d.ts +1 -0
  280. package/dist/index-ClmjK_o-.d.ts +55 -0
  281. package/dist/index-CtuakVRc.d.ts +267 -0
  282. package/dist/index-D48jVS-N.d.ts +246 -0
  283. package/dist/index-DDrYaUcq.d.ts +138 -0
  284. package/dist/index-DQh4VCc9.d.ts +98 -0
  285. package/dist/{live-D9W2M5aO.d.ts → index-DunNRdnT.d.ts} +136 -38
  286. package/dist/index.d.ts +65 -18
  287. package/dist/index.js +42 -14
  288. package/dist/invalidate-BBqhpxyB.d.ts +19 -0
  289. package/dist/json-CEnpTHlZ.js +16 -0
  290. package/dist/json-schema-CvER4kgT.js +502 -0
  291. package/dist/keys-4MnkWTTh.js +35 -0
  292. package/dist/kit-B5tid-q5.d.ts +60 -0
  293. package/dist/kit-Fm-Fv7q5.js +78 -0
  294. package/dist/knowledge-BXBbT0Tr.d.ts +176 -0
  295. package/dist/knowledge-CDmiMLew.js +255 -0
  296. package/dist/lint/index.d.ts +1 -1
  297. package/dist/lint/index.js +37 -0
  298. package/dist/list/index.d.ts +3 -2
  299. package/dist/list/index.js +1 -444
  300. package/dist/{index-fag8VJZ2.d.ts → list-query-BRt-zDF7.d.ts} +20 -12
  301. package/dist/list-query-gl808ac1.js +459 -0
  302. package/dist/{live-CdJBOc4f.js → live-CTAZQkwl.js} +99 -14
  303. package/dist/logger-DWVYUmwi.d.ts +18 -0
  304. package/dist/mcp/index.d.ts +38 -11
  305. package/dist/mcp/index.js +82 -46
  306. package/dist/mcp/sdk/index.d.ts +105 -0
  307. package/dist/mcp/sdk/index.js +126 -0
  308. package/dist/message-CaKlqdsX.js +219 -0
  309. package/dist/message-CfDoR0Y6.d.ts +94 -0
  310. package/dist/messages-B9-5887d.js +715 -0
  311. package/dist/messages-DYP0sUTe.d.ts +59 -0
  312. package/dist/mfa-Daak8ukG.d.ts +54 -0
  313. package/dist/modules-DpPwzC7E.d.ts +192 -0
  314. package/dist/modules-dxrVK3ID.js +228 -0
  315. package/dist/nestjs/index.d.ts +68 -0
  316. package/dist/nestjs/index.js +164 -0
  317. package/dist/next/client/index.d.ts +35 -0
  318. package/dist/next/client/index.js +36 -0
  319. package/dist/next/image/index.js +3 -24
  320. package/dist/next/index.d.ts +150 -51
  321. package/dist/next/index.js +261 -107
  322. package/dist/node/index.d.ts +4 -0
  323. package/dist/node/index.js +210 -0
  324. package/dist/node-headers-AO06VhqH.d.ts +24 -0
  325. package/dist/node-headers-C71zNQHe.js +55 -0
  326. package/dist/nuxt/index.d.ts +60 -0
  327. package/dist/nuxt/index.js +64 -0
  328. package/dist/onboarding-BZGHYJks.d.ts +62 -0
  329. package/dist/openapi/index.d.ts +1 -1
  330. package/dist/openapi/index.js +3 -3
  331. package/dist/orpc/index.d.ts +70 -11
  332. package/dist/orpc/index.js +73 -24
  333. package/dist/otel/index.d.ts +7 -7
  334. package/dist/otel/index.js +87 -57
  335. package/dist/outbox-Cq7pPYvl.d.ts +161 -0
  336. package/dist/{plugin-o144f322.d.ts → plugin-FGY8UhYm.d.ts} +30 -10
  337. package/dist/plugin-tfXW1-qO.js +45 -0
  338. package/dist/plugins/actor/index.d.ts +2 -2
  339. package/dist/plugins/actor/index.js +3 -3
  340. package/dist/plugins/rules/index.d.ts +11 -4
  341. package/dist/plugins/rules/index.js +40 -13
  342. package/dist/plugins/soft-delete/index.d.ts +5 -4
  343. package/dist/plugins/soft-delete/index.js +10 -5
  344. package/dist/plugins/tenant/index.d.ts +2 -2
  345. package/dist/plugins/tenant/index.js +18 -11
  346. package/dist/plugins/timestamps/index.d.ts +5 -3
  347. package/dist/plugins/timestamps/index.js +6 -4
  348. package/dist/plugins/validation/index.d.ts +2 -2
  349. package/dist/plugins/validation/index.js +10 -6
  350. package/dist/{pool-C9voM4Kw.d.ts → pool-CAu2nLrL.d.ts} +2 -2
  351. package/dist/postgres/index.d.ts +4 -4
  352. package/dist/postgres/index.js +1 -2
  353. package/dist/{postgrest-CruYKAvV.d.ts → postgrest-BLVdEnfe.d.ts} +13 -3
  354. package/dist/powersync/index.d.ts +43 -0
  355. package/dist/powersync/index.js +199 -0
  356. package/dist/powersync/react/index.d.ts +87 -0
  357. package/dist/powersync/react/index.js +108 -0
  358. package/dist/{problem-BW1KhzSG.js → problem-BreOl8b8.js} +14 -3
  359. package/dist/{problem-Blxe8S31.d.ts → problem-DD8LZFjc.d.ts} +21 -3
  360. package/dist/provider-BH5UWZJ6.d.ts +113 -0
  361. package/dist/provider-Di3sRutW.js +57 -0
  362. package/dist/query/index.d.ts +4 -2
  363. package/dist/query/index.js +4 -3
  364. package/dist/{query-CCbo44lG.js → query-BQ-NaZon.js} +141 -24
  365. package/dist/{queue-BYUCjr59.d.ts → queue-CLJurhdy.d.ts} +258 -83
  366. package/dist/react/index.d.ts +11 -7
  367. package/dist/react/index.js +7 -3
  368. package/dist/react/native/index.d.ts +116 -0
  369. package/dist/react/native/index.js +130 -0
  370. package/dist/react/server.d.ts +28 -10
  371. package/dist/react/server.js +22 -5
  372. package/dist/react/session.d.ts +2 -2
  373. package/dist/react-4ZqkP8da.js +79 -0
  374. package/dist/react-F80mF4-t.js +409 -0
  375. package/dist/react-router/index.d.ts +83 -0
  376. package/dist/react-router/index.js +89 -0
  377. package/dist/react-topic-seAWjL3_.js +38 -0
  378. package/dist/{read-set-DQFt1N_Y.js → read-set-DuKWC9Mj.js} +67 -25
  379. package/dist/realtime/index.d.ts +4 -5
  380. package/dist/realtime/index.js +144 -82
  381. package/dist/refusal-CeJWxmkH.js +61 -0
  382. package/dist/refusal-D1rgbzNq.d.ts +15 -0
  383. package/dist/replicas-CwrYp5Zj.js +94 -0
  384. package/dist/{view-Bn6HNKUe.js → resolve-D_IcnvFd.js} +248 -240
  385. package/dist/{impersonation-BmePsuxz.d.ts → resolve-KW8nCeL6.d.ts} +29 -69
  386. package/dist/{resource-mqfJW4YF.js → resource-BsvzCMlX.js} +4 -4
  387. package/dist/{resource-DNRFP7KR.d.ts → resource-D6Uc7Sk7.d.ts} +5 -5
  388. package/dist/respond-BUgsXNgk.js +91 -0
  389. package/dist/respond-DoBijysA.d.ts +93 -0
  390. package/dist/result-D7y2fT87.d.ts +282 -0
  391. package/dist/{result-BKiliPY7.js → result-DzUbqgdW.js} +27 -2
  392. package/dist/run-state-CPo-1X2B.d.ts +12 -0
  393. package/dist/run-state-CgBKzYMW.js +13 -0
  394. package/dist/search-CUWCRYKL.js +9 -0
  395. package/dist/server/index.d.ts +143 -22
  396. package/dist/server/index.js +46 -14
  397. package/dist/{respond-BR1fJWOB.d.ts → server-CeVeeXwz.d.ts} +224 -85
  398. package/dist/session-ChiXDZdZ.d.ts +72 -0
  399. package/dist/shared-CBxosKK3.js +75 -0
  400. package/dist/{shared-D1dRUrAg.js → shared-CxWmcyqB.js} +2 -1
  401. package/dist/shared-DDSjEGDR.d.ts +11 -0
  402. package/dist/sign-in-BCV5S2WZ.d.ts +49 -0
  403. package/dist/sign-in-Cb1mwv6X.js +86 -0
  404. package/dist/{signers-Cc6r98SW.d.ts → signers-4y62_VIi.d.ts} +13 -9
  405. package/dist/simplify-DDO0j0LI.js +83 -0
  406. package/dist/solid/index.d.ts +131 -0
  407. package/dist/solid/index.js +344 -0
  408. package/dist/solid-start/index.d.ts +75 -0
  409. package/dist/solid-start/index.js +58 -0
  410. package/dist/{spec-pins-C3oc-Tdp.js → spec-pins--j6mTok4.js} +8 -1
  411. package/dist/{spec-pins-CqorWDnO.d.ts → spec-pins-C89s6DyW.d.ts} +7 -0
  412. package/dist/sql/index.d.ts +313 -212
  413. package/dist/sql/index.js +34872 -6579
  414. package/dist/ssr/index.d.ts +6 -5
  415. package/dist/ssr/index.js +3 -3
  416. package/dist/{standard-D7r6dMUu.js → standard-BK0cqJ7y.js} +4 -3
  417. package/dist/{standard-J4YgMOLm.d.ts → standard-CYsm7xpT.d.ts} +1 -1
  418. package/dist/stats-D_AD9GGu.js +1123 -0
  419. package/dist/storage/index.d.ts +5 -5
  420. package/dist/storage/index.js +1027 -368
  421. package/dist/store-BdH5ruDx.js +66 -0
  422. package/dist/store-CGjgWu5n.d.ts +68 -0
  423. package/dist/streams/index.d.ts +76 -0
  424. package/dist/streams/index.js +77 -0
  425. package/dist/streams/redis/index.d.ts +52 -0
  426. package/dist/streams/redis/index.js +185 -0
  427. package/dist/stripe-BMj5LtsT.js +38 -0
  428. package/dist/stripe-D04h-QBT.d.ts +136 -0
  429. package/dist/{support-wHnK6zp-.js → support-BivNqSpK.js} +32 -11
  430. package/dist/{support-view-B8SlToQX.d.ts → support-view-33Oi07TT.d.ts} +1 -1
  431. package/dist/svelte/index.d.ts +114 -0
  432. package/dist/svelte/index.js +344 -0
  433. package/dist/sveltekit/index.d.ts +116 -0
  434. package/dist/sveltekit/index.js +106 -0
  435. package/dist/tags-B99_U9Pp.js +39 -0
  436. package/dist/tags-Cx-LnIQP.d.ts +30 -0
  437. package/dist/tanstack-db/index.d.ts +71 -0
  438. package/dist/tanstack-db/index.js +72 -0
  439. package/dist/tanstack-start/index.d.ts +75 -0
  440. package/dist/tanstack-start/index.js +95 -0
  441. package/dist/tee-CoEppCER.js +101 -0
  442. package/dist/{template-TxrGI5jR.js → template--jDkl3UP.js} +34 -13
  443. package/dist/template-CwO38Do8.d.ts +6 -0
  444. package/dist/temporal-BJGitm-h.js +79 -0
  445. package/dist/{temporal-required-CAt4Dc9Z.js → temporal-required-BVvW7SYJ.js} +3 -2
  446. package/dist/tenant-I1TEZ_BX.js +27 -0
  447. package/dist/tenant-icPeuUBN.d.ts +17 -0
  448. package/dist/testing/index.d.ts +315 -41
  449. package/dist/testing/index.js +1046 -79
  450. package/dist/timing-CyTQPubY.js +92 -0
  451. package/dist/ttl-Hr34gRu8.js +12 -0
  452. package/dist/{types-Dnf_wRDz.d.ts → types-6BdhkmZe.d.ts} +111 -45
  453. package/dist/types-BI-0Sl2v.d.ts +359 -0
  454. package/dist/types-C05bxW-W.d.ts +220 -0
  455. package/dist/types-DGs5L0VN.d.ts +192 -0
  456. package/dist/types-TaiEeBGA.d.ts +156 -0
  457. package/dist/usage-CrvrL2vw.d.ts +177 -0
  458. package/dist/user-change-XzvgafU4.js +130 -0
  459. package/dist/vault-CSO7ss5C.d.ts +50 -0
  460. package/dist/vercel-connect/index.d.ts +47 -0
  461. package/dist/vercel-connect/index.js +119 -0
  462. package/dist/{verify-Ce49ETPz.js → verify-DL41hbvY.js} +85 -13
  463. package/dist/verify-DPuWngD4.d.ts +172 -0
  464. package/dist/{version-DOYTYwHP.js → version-Bvv6aLjZ.js} +1 -1
  465. package/dist/view-BeFcRV6G.js +99 -0
  466. package/dist/{view-CbgObAeb.d.ts → view-DbxLNu8L.d.ts} +22 -5
  467. package/dist/vue/index.d.ts +141 -0
  468. package/dist/vue/index.js +362 -0
  469. package/dist/watch-DI34G86j.d.ts +60 -0
  470. package/dist/watch-Xuxrvxq-.js +122 -0
  471. package/dist/{wire-D2dT86vi.js → wire-DtZQ4sdE.js} +15 -5
  472. package/dist/workflow-builder-DJ1oHn8g.js +398 -0
  473. package/dist/workflow-sdk/builder/index.d.ts +85 -0
  474. package/dist/workflow-sdk/builder/index.js +250 -0
  475. package/dist/workflow-sdk/index.d.ts +14 -0
  476. package/dist/workflow-sdk/index.js +3 -0
  477. package/dist/workflow-sdk/world/index.d.ts +107 -0
  478. package/dist/workflow-sdk/world/index.js +702 -0
  479. package/dist/workflow-sdk-B3yLD6Br.d.ts +98 -0
  480. package/dist/workflow-sdk-C2WJ_G2O.js +173 -0
  481. package/dist/workflow-sdk-world-D-prir6O.js +1397 -0
  482. package/dist/workflows-B0j9WKPu.d.ts +185 -0
  483. package/dist/workflows-wu1WWBn7.js +216 -0
  484. package/package.json +461 -20
  485. package/schemas/ai-message-v1.json +258 -0
  486. package/schemas/config-v1.json +1074 -140
  487. package/schemas/doctor-report-v1.json +20 -1
  488. package/schemas/snapshot-v2.json +8 -0
  489. package/skills/better-supabase/SKILL.md +41 -12
  490. package/skills/better-supabase/references/frontend.md +96 -0
  491. package/skills/better-supabase/references/plugins.md +10 -6
  492. package/skills/better-supabase/references/troubleshooting.md +2 -1
  493. package/skills/better-supabase-api/SKILL.md +76 -22
  494. package/skills/better-supabase-api/references/adapters.md +207 -0
  495. package/skills/better-supabase-api/references/ai.md +125 -0
  496. package/skills/better-supabase-api/references/expo.md +65 -0
  497. package/skills/better-supabase-api/references/powersync.md +35 -0
  498. package/skills/better-supabase-api/references/streams-credentials.md +105 -0
  499. package/skills/better-supabase-auth/SKILL.md +47 -11
  500. package/skills/better-supabase-auth/references/authorization-providers.md +51 -0
  501. package/skills/better-supabase-testing/SKILL.md +3 -1
  502. package/dist/cli/config-dtoekM0v.js +0 -2
  503. package/dist/cli/gen-CIdSQZad.js +0 -2
  504. package/dist/cli/gen-CjElD1Vw.js +0 -2241
  505. package/dist/cli/read-sets-VQcPgxU9.js +0 -436
  506. package/dist/cli/sql-CVx-1O8G.js +0 -373
  507. package/dist/entitlements-CTEBm7IB.js +0 -20
  508. package/dist/events-aIO7vsIk.d.ts +0 -230
  509. package/dist/executor-BunPOO-k.d.ts +0 -402
  510. package/dist/executor-XycESQPH.js +0 -94
  511. package/dist/index-CWMDdEr4.d.ts +0 -50
  512. package/dist/index-DOESKG27.d.ts +0 -100
  513. package/dist/index-DmXSxbLi.d.ts +0 -62
  514. package/dist/index-Dp2VDwog.d.ts +0 -161
  515. package/dist/jobs/index.d.ts +0 -226
  516. package/dist/jobs/index.js +0 -1007
  517. package/dist/json-schema-m1fiwKgN.js +0 -208
  518. package/dist/kit-events-CTFDfSwb.js +0 -60
  519. package/dist/kit-transport-B82k1djG.d.ts +0 -33
  520. package/dist/kits-CGLLrNPd.js +0 -157
  521. package/dist/mfa-Cu96oJdw.d.ts +0 -30
  522. package/dist/notifications/index.d.ts +0 -162
  523. package/dist/notifications/index.js +0 -264
  524. package/dist/orgs/index.d.ts +0 -132
  525. package/dist/orgs/index.js +0 -245
  526. package/dist/permdock-sql-C2Inc6Xc.d.ts +0 -29
  527. package/dist/permdock-sql-DgOfeixi.js +0 -51
  528. package/dist/plugin-DwARMuQp.js +0 -59
  529. package/dist/server-B1mZ7CgQ.js +0 -660
  530. package/dist/stats-Bqob3WqT.js +0 -592
  531. package/dist/support-CEPwlw2G.d.ts +0 -89
  532. package/dist/template-mbvlysOo.d.ts +0 -6
  533. package/dist/temporal-CVlIV2DZ.js +0 -40
  534. package/dist/user-change-CHFEUro1.js +0 -49
  535. package/dist/webhooks/index.d.ts +0 -285
  536. package/skills/better-supabase-auth/references/permdock.md +0 -122
@@ -1,14 +1,24 @@
1
- import { n as dbError } from "../errors-DtwXa6b8.js";
2
- import { i as temporalText } from "../temporal-CVlIV2DZ.js";
3
- import { t as temporal } from "../temporal-required-CAt4Dc9Z.js";
4
- import { i as ok } from "../result-BKiliPY7.js";
5
- import { t as BetterSupabase } from "../define-cNKzqwL_.js";
1
+ import { t as DbException } from "../db-exception-CnWcYPxL.js";
2
+ import { n as dbError } from "../errors-B11Duf73.js";
3
+ import { d as temporalText } from "../temporal-BJGitm-h.js";
4
+ import { t as temporal } from "../temporal-required-BVvW7SYJ.js";
5
+ import { a as ok } from "../result-DzUbqgdW.js";
6
+ import { t as BetterSupabase } from "../define-B-TVjC25.js";
6
7
  import { a as toBase64Url, r as fromBase64, t as base64ToText } from "../base64-D2gsioCY.js";
7
- import { a as resolveConfig } from "../kits-CGLLrNPd.js";
8
8
  import { t as isList } from "../guards-BnzDvFrz.js";
9
+ import { a as resolveConfig } from "../modules-dxrVK3ID.js";
9
10
  import { ENV_VARIABLES } from "../env/index.js";
10
- import { n as postgresExecutor } from "../executor-XycESQPH.js";
11
+ import { n as postgresExecutor } from "../executor-De1Of3m3.js";
12
+ import "../replicas-CwrYp5Zj.js";
13
+ import { a as tenantCredentialRef } from "../provider-Di3sRutW.js";
14
+ import { n as parseDbStats } from "../timing-CyTQPubY.js";
15
+ import { t as rawError } from "../block-transport-BLr-ZgLc.js";
16
+ import { a as isScopeName, c as templateFunctions, l as templatePlaceholders, n as TEMPLATE_PLACEHOLDERS, s as scopeProblems, u as templateProblems } from "../access-sql-B0dF-SoF.js";
11
17
  import { toCloudEvents } from "../events/index.js";
18
+ import { timestamps } from "../plugins/timestamps/index.js";
19
+ import { softDelete } from "../plugins/soft-delete/index.js";
20
+ import { tenant } from "../plugins/tenant/index.js";
21
+ import { actor } from "../plugins/actor/index.js";
12
22
  import { createClient } from "@supabase/supabase-js";
13
23
  import { existsSync } from "node:fs";
14
24
  import { readFile } from "node:fs/promises";
@@ -235,6 +245,11 @@ function isPrefetch(response) {
235
245
  const headers = response.request().headers();
236
246
  return headers["next-router-prefetch"] !== void 0 || headers["purpose"] === "prefetch" || (headers["sec-purpose"]?.includes("prefetch") ?? false);
237
247
  }
248
+ function sleep$1(ms) {
249
+ return new Promise((done) => {
250
+ setTimeout(done, ms);
251
+ });
252
+ }
238
253
  function settle(promise, ms) {
239
254
  let timer;
240
255
  return Promise.race([promise, new Promise((done) => {
@@ -243,32 +258,74 @@ function settle(promise, ms) {
243
258
  clearTimeout(timer);
244
259
  });
245
260
  }
246
- /**
247
- * Measures every document and RSC navigation response during `during` and
248
- * fails when one render makes more database calls or waves than allowed.
249
- * Needs `createNext(betterSupabase, { debug })` and `bs.debugRoute()` in the app.
250
- */
251
- async function expectDbBudget(page, expectation) {
261
+ function expectResponseBudget(response, expectation) {
262
+ const header = expectation.header ?? "x-bs-db-calls";
263
+ const stats = parseDbStats(response.headers.get(header));
264
+ if (!stats) throw new Error(`expectDbBudget: the response has no ${header} header. Add withDbStats() to the entries.`);
265
+ if (expectation.maxCalls !== void 0 && stats.calls > expectation.maxCalls || expectation.maxWaves !== void 0 && stats.waves > expectation.maxWaves) throw new Error(`expectDbBudget: ${stats.calls} calls in ${stats.waves} waves, over the budget of ${expectation.maxCalls ?? "∞"} calls and ${expectation.maxWaves ?? "∞"} waves`);
266
+ return stats;
267
+ }
268
+ function expectDbBudget(target, expectation) {
269
+ if (target instanceof Response) return expectResponseBudget(target, expectation);
270
+ return expectPageBudget(target, expectation);
271
+ }
272
+ async function expectPageBudget(page, expectation) {
252
273
  const seen = [];
274
+ let lastSeen = 0;
253
275
  const listener = (response) => {
254
276
  const headers = response.headers();
255
277
  const id = headers["x-bs-request-id"];
256
278
  const stats = headers["x-bs-stats"];
257
279
  if (!id || !stats || !expectation.prefetches && isPrefetch(response)) return;
280
+ lastSeen = Date.now();
258
281
  seen.push({
259
282
  response,
260
283
  id,
261
284
  url: new URL(stats, response.url()).href
262
285
  });
263
286
  };
287
+ const settleMs = expectation.settleMs ?? 500;
288
+ const timeoutMs = expectation.timeoutMs ?? 1e4;
289
+ /** Waits until no response has arrived for `settleMs`, at most `timeoutMs`. */
290
+ const quiet = async () => {
291
+ const start = Date.now();
292
+ lastSeen = Math.max(lastSeen, start);
293
+ for (;;) {
294
+ const left = lastSeen + settleMs - Date.now();
295
+ if (left <= 0 || Date.now() - start >= timeoutMs) return;
296
+ await sleep$1(left);
297
+ }
298
+ };
299
+ const failed = /* @__PURE__ */ new WeakSet();
300
+ const aborts = /* @__PURE__ */ new Map();
301
+ const onFailed = (request) => {
302
+ failed.add(request);
303
+ aborts.get(request)?.();
304
+ };
305
+ const ended = (response) => {
306
+ const request = response.request();
307
+ if (failed.has(request)) return Promise.resolve();
308
+ return Promise.race([response.finished(), new Promise((done) => {
309
+ aborts.set(request, done);
310
+ })]);
311
+ };
264
312
  page.on("response", listener);
313
+ page.on("requestfailed", onFailed);
265
314
  try {
266
- await (expectation.during ? expectation.during() : page.reload());
315
+ try {
316
+ await (expectation.during ? expectation.during() : page.reload());
317
+ await quiet();
318
+ } finally {
319
+ page.off("response", listener);
320
+ }
321
+ if (seen.length === 0) {
322
+ if (expectation.requireRequest === false) return [];
323
+ throw new Error("expectDbBudget: no response carried x-bs-request-id. Enable createNext(betterSupabase, { debug: { enabled: true } }) and run the proxy.");
324
+ }
325
+ await Promise.all(seen.map(({ response }) => settle(ended(response), timeoutMs)));
267
326
  } finally {
268
- page.off("response", listener);
327
+ page.off("requestfailed", onFailed);
269
328
  }
270
- if (seen.length === 0) throw new Error("expectDbBudget: no response carried x-bs-request-id. Enable createNext(betterSupabase, { debug: { enabled: true } }) and run the proxy.");
271
- await Promise.all(seen.map(({ response }) => settle(response.finished(), expectation.timeoutMs ?? 1e4)));
272
329
  const renders = [];
273
330
  for (const { response, id, url } of seen) {
274
331
  const reply = await page.request.get(url);
@@ -287,6 +344,60 @@ async function expectDbBudget(page, expectation) {
287
344
  return renders;
288
345
  }
289
346
  //#endregion
347
+ //#region src/testing/instant.ts
348
+ /**
349
+ * `@next/playwright` is an optional peer: loading it on demand keeps it out
350
+ * of apps that never call `expectInstant`, and a missing install returns
351
+ * `undefined` so the caller can say how to add it.
352
+ */
353
+ async function loadInstant() {
354
+ const specifier = "@next/playwright";
355
+ try {
356
+ return (await import(specifier)).instant;
357
+ } catch {
358
+ return;
359
+ }
360
+ }
361
+ async function assertUnderLock(expectation) {
362
+ const timeout = expectation.timeoutMs ?? 5e3;
363
+ for (const locator of expectation.visible) try {
364
+ await locator.waitFor({
365
+ state: "visible",
366
+ timeout
367
+ });
368
+ } catch (error) {
369
+ throw new Error(`expectInstant: ${String(locator)} was not visible under the instant() lock. It is not in the prefetched UI.`, { cause: error });
370
+ }
371
+ for (const locator of expectation.absent ?? []) {
372
+ const count = await locator.count();
373
+ if (count > 0) throw new Error(`expectInstant: ${String(locator)} matched ${count} elements under the instant() lock. It should stream in after the navigation.`);
374
+ }
375
+ }
376
+ /**
377
+ * Runs `during` inside `@next/playwright`'s `instant()`, checks what the
378
+ * prefetched UI shows while the lock holds, and with `maxCalls` or
379
+ * `maxWaves` checks the navigation's database budget like `expectDbBudget`.
380
+ * Needs a `next build` with the testing API exposed.
381
+ */
382
+ async function expectInstant(page, expectation) {
383
+ const instant = await loadInstant();
384
+ if (!instant) throw new Error("expectInstant: install @next/playwright as a dev dependency (pnpm add -D @next/playwright).");
385
+ const run = () => instant(page, async () => {
386
+ await expectation.during();
387
+ await assertUnderLock(expectation);
388
+ }, expectation.baseURL === void 0 ? void 0 : { baseURL: expectation.baseURL });
389
+ if (expectation.maxCalls === void 0 && expectation.maxWaves === void 0) {
390
+ await run();
391
+ return [];
392
+ }
393
+ return expectDbBudget(page, {
394
+ during: run,
395
+ ...expectation.settleMs === void 0 ? {} : { settleMs: expectation.settleMs },
396
+ ...expectation.maxCalls === void 0 ? {} : { maxCalls: expectation.maxCalls },
397
+ ...expectation.maxWaves === void 0 ? {} : { maxWaves: expectation.maxWaves }
398
+ });
399
+ }
400
+ //#endregion
290
401
  //#region src/testing/conformance.ts
291
402
  /** Thrown by a conformance kit when any check fails. Lists every failure, not just the first. */
292
403
  var ConformanceError = class extends Error {
@@ -367,7 +478,8 @@ const hasName = (subject) => ["has a name", () => {
367
478
  /**
368
479
  * Proves an `Executor` meets the contract: rows keyed by the selection's
369
480
  * aliases, counts, `aborted` errors, failures returned as `Result`s rather
370
- * than thrown, and (with `create`) a write round trip.
481
+ * than thrown, and (with `create`) a write round trip and a `deleteMany`
482
+ * that `maxAffected` refuses without deleting anything.
371
483
  *
372
484
  * ```ts
373
485
  * it('conforms', () => testExecutor(myExecutor, { betterSupabase, table: 'tags', create: { name: 'x' } }));
@@ -475,6 +587,32 @@ function testExecutor(executor, options) {
475
587
  expect(removed.ok, `delete failed: ${JSON.stringify(removed.error)}`);
476
588
  const again = await repository["findFirst"]({ where: { [primary]: row[primary] } });
477
589
  expect(again.ok && again.data === null, "the deleted row is still readable");
590
+ }],
591
+ create && ["refuses a write past maxAffected and writes nothing", async () => {
592
+ const repository = betterSupabase.connect(executor)[table];
593
+ const [primary] = betterSupabase.meta.tables[table].primaryKey;
594
+ expect(primary, "write checks need a table with a primary key");
595
+ const created = await repository["create"](create);
596
+ expect(created.ok, `create failed: ${JSON.stringify(created.error)}`);
597
+ const id = created.data[primary];
598
+ try {
599
+ const refused = await repository["deleteMany"]({
600
+ where: { [primary]: id },
601
+ maxAffected: 0
602
+ });
603
+ expect(!refused.ok, "deleteMany past maxAffected must fail");
604
+ const kind = refused.error?.kind;
605
+ expect(kind === "max_affected", `expected kind "max_affected", got "${String(kind)}"`);
606
+ const kept = await repository["findFirst"]({ where: { [primary]: id } });
607
+ expect(kept.ok && kept.data !== null, "a refused deleteMany must not delete the row");
608
+ const allowed = await repository["deleteMany"]({
609
+ where: { [primary]: id },
610
+ maxAffected: 1
611
+ });
612
+ expect(allowed.ok, `deleteMany within maxAffected failed: ${JSON.stringify(allowed.error)}`);
613
+ } finally {
614
+ await repository["delete"](id);
615
+ }
478
616
  }]
479
617
  ]);
480
618
  }
@@ -496,7 +634,7 @@ function testCacheAdapter(adapter, options = {}) {
496
634
  table,
497
635
  tables: [table],
498
636
  ids: ["1", "a,b"],
499
- tenant: "org_1"
637
+ tenant: "organization_1"
500
638
  })],
501
639
  ["accepts repeated and concurrent calls", async () => {
502
640
  await invalidate({
@@ -534,7 +672,7 @@ function testEventSink(sink, options = {}) {
534
672
  id: "2",
535
673
  name: "Globex"
536
674
  }],
537
- context: { tenant: "org_1" }
675
+ context: { tenant: "organization_1" }
538
676
  }, { source: "/better-supabase/conformance" });
539
677
  return conform("EventSink", [
540
678
  ["accepts an empty batch", () => sink.send(deepFreeze([]))],
@@ -610,6 +748,19 @@ function testQueueBackend(backend, options) {
610
748
  expect(await backend.fail(second, "boom", 0) === "dead", "fail at max_attempts must return dead");
611
749
  expect(await claimOne() === void 0, "a dead message must not be claimed again");
612
750
  }],
751
+ backend.stats && backend.listDead && ["counts and lists dead letters", async () => {
752
+ expect(((await backend.stats?.(queue))?.dead ?? 0) >= 1, "stats must count the dead letter");
753
+ const [dead] = await backend.listDead?.(queue, 10, void 0) ?? [];
754
+ expect(dead, "listDead must return the dead letter");
755
+ expect(same(dead.message?.payload, { n: 3 }), "listDead must return the newest dead letter first");
756
+ if (backend.retryDead) {
757
+ expect(await backend.retryDead(queue, [Number(dead.id)], 10) === 1, "retryDead must enqueue the dead letter again");
758
+ const job = await claimOne();
759
+ expect(job, "the retried job must be claimable");
760
+ expect(same(job.payload, { n: 3 }), "the retried job must keep its payload");
761
+ await backend.complete(job);
762
+ }
763
+ }],
613
764
  (options.dedupe ?? backend.leases) && ["returns the first id for a repeated dedupe key", async () => {
614
765
  expect(await backend.send(queue, { n: 4 }, 0, 3, "same") === await backend.send(queue, { n: 4 }, 0, 3, "same"), "a waiting dedupe key must return its id");
615
766
  const job = await claimOne();
@@ -722,65 +873,11 @@ function testAuthResolver(resolver, options) {
722
873
  }]
723
874
  ]);
724
875
  }
725
- function relativeImport(from, to) {
726
- const fromParts = from.split("/").slice(0, -1);
727
- const toParts = to.split("/");
728
- let common = 0;
729
- while (common < fromParts.length && fromParts[common] === toParts[common]) common += 1;
730
- const path = [...Array.from({ length: fromParts.length - common }, () => ".."), ...toParts.slice(common)].join("/");
731
- return path.startsWith(".") ? path : `./${path}`;
732
- }
733
- const EMPTY_INTROSPECTION = {
734
- version: 1,
735
- schemas: [],
736
- tables: [],
737
- foreignTables: [],
738
- views: [],
739
- materializedViews: [],
740
- columns: [],
741
- primaryKeys: [],
742
- relationships: [],
743
- functions: [],
744
- types: []
745
- };
746
- /** Proves a `Generator` writes relative, unique paths, is deterministic and doesn't mutate its input. */
747
- function testGenerator(generator, options) {
748
- const config = options.config ?? resolveConfig({}, "/project");
749
- const input = () => ({
750
- meta: frozenCopy(options.meta),
751
- introspection: frozenCopy(options.introspection ?? EMPTY_INTROSPECTION),
752
- extras: frozenCopy(options.extras ?? {
753
- tables: [],
754
- buckets: [],
755
- realtime: []
756
- }),
757
- config,
758
- output: `${config.root}/${config.output}`,
759
- importPath: (from, to) => relativeImport(from, to)
760
- });
761
- return conform(`Generator "${generator.name}"`, [
762
- hasName(generator),
763
- ["writes files inside the project", async () => {
764
- const files = await generator.generate(input());
765
- expect(isList(files), "generate() must return an array");
766
- for (const file of files) {
767
- expect(typeof file.path === "string" && typeof file.contents === "string", "files need a path and contents");
768
- expect(!file.path.startsWith("/") && !/^[A-Za-z]:/.test(file.path), `${file.path} must be relative`);
769
- expect(!file.path.split(/[\\/]/).includes(".."), `${file.path} must stay inside the project`);
770
- }
771
- const paths = files.map((file) => file.path);
772
- expect(new Set(paths).size === paths.length, "paths must be unique");
773
- }],
774
- ["is deterministic and does not mutate its input", async () => {
775
- const first = await generator.generate(input());
776
- const second = await generator.generate(input());
777
- expect(same(first, second), "two runs on the same input differ");
778
- }]
779
- ]);
780
- }
781
876
  /**
782
- * Proves a `Plugin` targets API v1, installs cleanly, keeps `transformQuery`
783
- * and `beforeMutation` pure and deterministic, and keeps executors'
877
+ * Proves a `Plugin` targets API v1, installs cleanly on its own and next to
878
+ * the first-party plugins in either order, adds repository methods without
879
+ * replacing base ones, keeps `context`, `transformQuery` and
880
+ * `beforeMutation` pure and deterministic, and keeps executors'
784
881
  * results intact when it wraps them.
785
882
  */
786
883
  function testPlugin(plugin, options) {
@@ -804,6 +901,41 @@ function testPlugin(plugin, options) {
804
901
  const db = betterSupabase.use(plugin).connect(recorder([]), context);
805
902
  for (const key of Object.keys(betterSupabase.meta.tables)) expect(db[key], `db.${key} is missing`);
806
903
  }],
904
+ ["declares a known hook order", () => {
905
+ const enforce = plugin.enforce;
906
+ expect(enforce === void 0 || enforce === "first" || enforce === "pre" || enforce === "post", `enforce is ${String(enforce)}; use "first", "pre", "post" or leave it out`);
907
+ }],
908
+ ["installs next to the first-party plugins in either order", () => {
909
+ const others = [
910
+ timestamps(),
911
+ softDelete(),
912
+ tenant(),
913
+ actor()
914
+ ].filter((other) => other.name !== plugin.name);
915
+ let before = betterSupabase;
916
+ for (const other of others) before = before.use(other);
917
+ let after = betterSupabase.use(plugin);
918
+ for (const other of others) after = after.use(other);
919
+ for (const installed of [before.use(plugin), after]) {
920
+ const db = installed.connect(recorder([]), context);
921
+ for (const key of Object.keys(betterSupabase.meta.tables)) expect(db[key], `db.${key} is missing`);
922
+ }
923
+ }],
924
+ plugin.repository && ["repository adds functions and keeps the base methods", () => {
925
+ const db = betterSupabase.connect(recorder([]), context);
926
+ for (const [key, table] of Object.entries(betterSupabase.meta.tables)) {
927
+ const base = db[key];
928
+ const added = plugin.repository({
929
+ table,
930
+ base
931
+ });
932
+ if (added === void 0) continue;
933
+ for (const [name, method] of Object.entries(added)) {
934
+ expect(typeof method === "function", `${key}.${name} is not a function`);
935
+ expect(!(name in base), `${key}.${name} replaces a base method`);
936
+ }
937
+ }
938
+ }],
807
939
  plugin.transformQuery && ["transformQuery is pure and deterministic", async () => {
808
940
  const op = await captureOp(betterSupabase, (db) => db[table]["findMany"]({ limit: 1 }));
809
941
  const first = plugin.transformQuery(frozenCopy(op), hook(op));
@@ -848,6 +980,13 @@ function testPlugin(plugin, options) {
848
980
  const result = await wrapped.execute(op, { errorMappers: [] });
849
981
  expect(result.ok && same(result.data, data), "the wrapped executor changed the result");
850
982
  }],
983
+ plugin.context && ["context is pure and deterministic", () => {
984
+ const args = { schema: betterSupabase.meta };
985
+ const first = plugin.context(frozenCopy(context), args);
986
+ const second = plugin.context(frozenCopy(context), args);
987
+ expect(typeof first === "object" && first !== null, "must return a context object");
988
+ expect(same(first, second), "two calls with the same input differ");
989
+ }],
851
990
  plugin.mapError && ["mapError returns a DbError or undefined", () => {
852
991
  const fallback = dbError("unexpected", "internal error");
853
992
  const mapped = plugin.mapError({
@@ -1211,6 +1350,471 @@ function defineSeed(betterSupabase, fixtures) {
1211
1350
  };
1212
1351
  }
1213
1352
  //#endregion
1353
+ //#region src/testing/adapter.ts
1354
+ const MODULE_SERVER = {
1355
+ env: {
1356
+ url: "http://127.0.0.1:54321",
1357
+ publishableKey: "sb_publishable_kit",
1358
+ jwksUrl: new URL("http://127.0.0.1:54321/auth/v1/.well-known/jwks.json")
1359
+ },
1360
+ readUrl: "http://127.0.0.1:54329",
1361
+ prefetchJwks: false,
1362
+ exposeErrors: false
1363
+ };
1364
+ /**
1365
+ * Proves a framework adapter refuses callers its guard rejects, answers
1366
+ * `DbError`s with their status, hides unexpected errors, applies the
1367
+ * context's cookies and hands pending event sends to `waitUntil`, the way
1368
+ * `handle()` from `better-supabase/server` does.
1369
+ */
1370
+ function testAdapter(name, options) {
1371
+ const { events } = options.betterSupabase;
1372
+ const call = async (run, allow = ["anon"], waitUntil = () => void 0) => options.serve(MODULE_SERVER, run, {
1373
+ allow,
1374
+ waitUntil
1375
+ })(new Request("http://127.0.0.1/module"));
1376
+ const errorStatus = (expected) => options.errorsInBody ? 200 : expected;
1377
+ return conform(`Adapter "${name}"`, [
1378
+ ["refuses a caller the guard rejects", async () => {
1379
+ let ran = false;
1380
+ const response = await call(() => {
1381
+ ran = true;
1382
+ }, ["user"]);
1383
+ expect(response.status === 401, `expected 401 for an anonymous caller, got ${response.status}`);
1384
+ expect(!ran, "the handler ran for a refused caller");
1385
+ }],
1386
+ ["runs the handler for an allowed caller", async () => {
1387
+ const response = await call(() => ({ module: "ran" }));
1388
+ expect(response.status === 200, `expected 200, got ${response.status}`);
1389
+ const body = await response.text();
1390
+ expect(body.includes("ran"), `the handler's data is missing: ${body}`);
1391
+ }],
1392
+ ["answers a DbError with its status", async () => {
1393
+ const response = await call(() => {
1394
+ throw new DbException(dbError("not_found", "module: no such row"));
1395
+ });
1396
+ expect(response.status === errorStatus(404), `expected ${errorStatus(404)}, got ${response.status}`);
1397
+ const body = await response.text();
1398
+ expect(body.includes("module: no such row"), `the error message is missing: ${body}`);
1399
+ }],
1400
+ ["hides the message of an unexpected error", async () => {
1401
+ const response = await call(() => {
1402
+ throw new Error("module-internal-detail");
1403
+ });
1404
+ expect(response.status === errorStatus(500), `expected ${errorStatus(500)}, got ${response.status}`);
1405
+ expect(!(await response.text()).includes("module-internal-detail"), "the response leaks the error message");
1406
+ }],
1407
+ options.cookies !== false && ["applies the context's cookies", async () => {
1408
+ let replica = false;
1409
+ const response = await call((ctx) => {
1410
+ replica = ctx.replica !== void 0;
1411
+ ctx.replica?.pin();
1412
+ return { pinned: true };
1413
+ });
1414
+ expect(replica, "ctx.replica is unset: pass `server` to the adapter");
1415
+ expect(response.headers.getSetCookie().some((cookie) => cookie.startsWith(`bs-primary-until=`)), `a write must send bs-primary-until; apply ctx.apply() or ctx.cookies()`);
1416
+ }],
1417
+ ["hands pending event sends to waitUntil", async () => {
1418
+ const { promise: send, resolve } = Promise.withResolvers();
1419
+ const waited = [];
1420
+ await call(() => {
1421
+ events.track(send);
1422
+ return { sent: true };
1423
+ }, ["anon"], (promise) => {
1424
+ waited.push(promise);
1425
+ });
1426
+ resolve();
1427
+ expect(waited.length > 0, "waitUntil was not called while an event send was pending");
1428
+ await Promise.all(waited);
1429
+ }]
1430
+ ]);
1431
+ }
1432
+ //#endregion
1433
+ //#region src/testing/transport-middleware.ts
1434
+ const REQUEST = {
1435
+ schema: "better_supabase",
1436
+ fn: "list_my_organizations",
1437
+ args: {
1438
+ organization: "organization_1",
1439
+ filters: { status: "open" }
1440
+ }
1441
+ };
1442
+ /**
1443
+ * Proves a `BlockTransportMiddleware` meets the contract: it declares API 1
1444
+ * and a name, calls `next` at most once per call, leaves the request it got
1445
+ * unchanged, resolves when `next` does, and rejects with the database error
1446
+ * `next` rejected with, so blocks still map it to a `DbError`.
1447
+ *
1448
+ * ```ts
1449
+ * it('conforms', () => testBlockTransportMiddleware(statementTimeout('5s')));
1450
+ * ```
1451
+ */
1452
+ function testBlockTransportMiddleware(middleware) {
1453
+ return conform(`BlockTransportMiddleware "${middleware.name}"`, [
1454
+ hasName(middleware),
1455
+ ["declares apiVersion 1", () => {
1456
+ const version = middleware.apiVersion;
1457
+ expect(version === 1, "apiVersion must be 1");
1458
+ }],
1459
+ ["calls next at most once and resolves with a value", async () => {
1460
+ let calls = 0;
1461
+ await middleware.call(frozenCopy(REQUEST), (request) => {
1462
+ calls += 1;
1463
+ expect(typeof request.fn === "string" && typeof request.schema === "string", "next got a request without a schema and function");
1464
+ return Promise.resolve([{ id: "organization_1" }]);
1465
+ });
1466
+ expect(calls <= 1, `next was called ${String(calls)} times`);
1467
+ }],
1468
+ ["keeps the database error next rejects with", async () => {
1469
+ const denied = Object.assign(/* @__PURE__ */ new Error("permission denied"), {
1470
+ code: "42501",
1471
+ hint: "ORGANIZATION_FORBIDDEN"
1472
+ });
1473
+ let rejected;
1474
+ try {
1475
+ await middleware.call(frozenCopy(REQUEST), () => Promise.reject(denied));
1476
+ } catch (cause) {
1477
+ rejected = cause;
1478
+ }
1479
+ expect(rejected !== void 0, "resolved although next rejected");
1480
+ const raw = rawError(rejected);
1481
+ expect(raw?.code === "42501" && raw.hint === "ORGANIZATION_FORBIDDEN", "the rejection lost the database error's code or hint");
1482
+ }]
1483
+ ]);
1484
+ }
1485
+ //#endregion
1486
+ //#region src/testing/authorization-provider.ts
1487
+ function isPlainJson(value) {
1488
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1489
+ if (typeof value === "number") return Number.isFinite(value);
1490
+ if (Array.isArray(value)) return value.every(isPlainJson);
1491
+ if (typeof value !== "object") return false;
1492
+ const prototype = Object.getPrototypeOf(value);
1493
+ return (prototype === Object.prototype || prototype === null) && Object.values(value).every((entry) => entry === void 0 || isPlainJson(entry));
1494
+ }
1495
+ const QUALIFIED = /^[a-z_][\w$]*\.[a-z_][\w${}]*$/i;
1496
+ const TABLE = /^[a-z_][\w$]*\.[a-z_][\w$]*$/i;
1497
+ const COLUMN = /^[a-z_][\w$]*\.[a-z_][\w$]*\.[a-z_][\w$]*$/i;
1498
+ const IDENTIFIER = /^[a-z_][\w$]*$/i;
1499
+ function expectRow(where, row) {
1500
+ expect(TABLE.test(row.table), `${where}.table "${row.table}" is not schema.table`);
1501
+ expect(IDENTIFIER.test(row.id), `${where}.id "${row.id}" is not a column`);
1502
+ expect(row.disabledAt !== void 0 || row.status !== void 0, `${where} sets neither disabledAt nor status`);
1503
+ for (const column of [row.disabledAt, row.status]) if (column !== void 0) expect(IDENTIFIER.test(column), `${where} column "${column}" is not a column`);
1504
+ expect(row.status === void 0 === (row.active === void 0), `${where} needs status and active together`);
1505
+ }
1506
+ /**
1507
+ * Proves an `AuthorizationProvider` targets API v1, is plain JSON, names
1508
+ * valid scopes with a tenant scope that has an id type, writes templates
1509
+ * that are safe to inline and use the placeholders they need, lists every
1510
+ * function its templates call in `requires`, and keeps its token hook,
1511
+ * suspension rows, role sources and deciding columns well formed.
1512
+ */
1513
+ function testAuthorizationProvider(provider) {
1514
+ const scopes = new Set(provider.scopes.map((scope) => scope.name));
1515
+ const templates = Object.entries(provider.functions).filter((entry) => typeof entry[1] === "string");
1516
+ const calls = templates.some(([, template]) => (template.includes("{scope}") ? [...scopes] : [void 0]).some((scope) => templateFunctions(template, scope).length > 0));
1517
+ return conform(`AuthorizationProvider "${provider.name}"`, [
1518
+ hasName(provider),
1519
+ ["targets authorization API v1", () => {
1520
+ expect(provider.apiVersion === 1, `apiVersion is ${String(provider.apiVersion)}`);
1521
+ }],
1522
+ ["is plain JSON", () => {
1523
+ expect(isPlainJson(provider), "it doesn't survive a JSON round trip");
1524
+ }],
1525
+ ["names valid, unique scopes", () => {
1526
+ expect(scopes.size > 0, "scopes is empty");
1527
+ expect(scopes.size === provider.scopes.length, "a scope name repeats");
1528
+ for (const scope of provider.scopes) expect(isScopeName(scope.name), `scope "${scope.name}" is not lower snake case`);
1529
+ const problems = scopeProblems(provider);
1530
+ expect(problems.length === 0, problems.join("; "));
1531
+ }],
1532
+ ["templates use only their placeholders", () => {
1533
+ for (const [name, template] of templates) {
1534
+ const allowed = TEMPLATE_PLACEHOLDERS[name];
1535
+ expect(allowed, `functions.${name} is not a known template`);
1536
+ for (const placeholder of templatePlaceholders(template)) expect(allowed.includes(placeholder), `functions.${name} uses {${placeholder}}; it takes ${allowed.map((key) => `{${key}}`).join(", ")}`);
1537
+ const needed = allowed.filter((key) => key === "permission" || key === "user" || key === "tenant" || key === "role" || key === "scope" && scopes.size > 1 && name.startsWith("idsWith"));
1538
+ for (const key of needed) expect(template.includes(`{${key}}`), key === "scope" ? `functions.${name} doesn't use {scope}, so it answers the same for every scope` : `functions.${name} doesn't use {${key}}`);
1539
+ }
1540
+ }],
1541
+ ["templates are safe to inline", () => {
1542
+ for (const [name, template] of templates) {
1543
+ const problems = templateProblems(template);
1544
+ expect(problems.length === 0, `functions.${name} ${problems.join(", ")}`);
1545
+ }
1546
+ }],
1547
+ ["requires every function the templates call", () => {
1548
+ expect(!calls || provider.requires !== void 0, "the templates call functions, but requires is unset");
1549
+ const required = new Set((provider.requires ?? []).map((entry) => entry.function));
1550
+ for (const entry of provider.requires ?? []) expect(QUALIFIED.test(entry.function), `requires "${entry.function}" is not schema.name`);
1551
+ for (const [name, template] of templates) for (const scope of template.includes("{scope}") ? scopes : [void 0]) for (const fn of templateFunctions(template, scope)) expect(required.has(fn), `functions.${name} calls ${fn}, which requires doesn't list`);
1552
+ }],
1553
+ provider.permissions && ["lists each permission once, at known scopes", () => {
1554
+ const keys = /* @__PURE__ */ new Set();
1555
+ for (const permission of provider.permissions) {
1556
+ expect(permission.key !== "", "a permission key is empty");
1557
+ expect(!keys.has(permission.key), `permission "${permission.key}" repeats`);
1558
+ keys.add(permission.key);
1559
+ for (const scope of permission.scopes ?? []) expect(scopes.has(scope), `permission "${permission.key}" names unknown scope "${scope}"`);
1560
+ }
1561
+ }],
1562
+ provider.memberships && ["memberships name known scopes", () => {
1563
+ for (const membership of provider.memberships) {
1564
+ expect(TABLE.test(membership.table), `membership table "${membership.table}" is not schema.table`);
1565
+ if ("value" in membership.scope) expect(scopes.has(membership.scope.value), `${membership.table} names unknown scope "${membership.scope.value}"`);
1566
+ }
1567
+ }],
1568
+ provider.suspension && ["suspension rows name a table, an id and a state column", () => {
1569
+ const { user, tenant } = provider.suspension;
1570
+ if (user) expectRow("suspension.user", user);
1571
+ if (tenant) expectRow("suspension.tenant", tenant);
1572
+ }],
1573
+ provider.roleSources && ["role sources name schema.table and columns", () => {
1574
+ for (const source of provider.roleSources) {
1575
+ expect(TABLE.test(source.table), `roleSources table "${source.table}" is not schema.table`);
1576
+ expect(TABLE.test(source.role.through.table), `roleSources ${source.table} reads roles from "${source.role.through.table}", which is not schema.table`);
1577
+ for (const column of [
1578
+ source.role.column,
1579
+ source.role.through.id,
1580
+ source.role.through.column
1581
+ ]) expect(IDENTIFIER.test(column), `roleSources ${source.table} names "${column}", which is not a column`);
1582
+ }
1583
+ }],
1584
+ provider.decidingColumns && ["deciding columns are schema.table.column", () => {
1585
+ for (const column of provider.decidingColumns) expect(COLUMN.test(column), `decidingColumns "${column}" is not schema.table.column`);
1586
+ }],
1587
+ provider.tokenHook && ["names its token hook and the claims it writes", () => {
1588
+ const hook = provider.tokenHook;
1589
+ expect(QUALIFIED.test(hook.function), `tokenHook.function "${hook.function}" is not schema.name`);
1590
+ expect(hook.ownedClaims.length > 0, "tokenHook.ownedClaims is empty");
1591
+ const owned = new Set(hook.ownedClaims);
1592
+ const written = new Set(owned);
1593
+ for (const claim of hook.registeredClaims ?? []) {
1594
+ expect(QUALIFIED.test(claim.function), `tokenHook claim "${claim.name}" calls "${claim.function}", which is not schema.name`);
1595
+ expect(!owned.has(claim.name), `tokenHook registers "${claim.name}", which ownedClaims also lists: a claim the hook owns has no second writer`);
1596
+ written.add(claim.name);
1597
+ }
1598
+ if (hook.budget !== void 0) {
1599
+ expect(Number.isInteger(hook.budget.bytes) && hook.budget.bytes > 0, "tokenHook.budget.bytes must be a positive integer");
1600
+ for (const claim of hook.budget.claims) expect(written.has(claim), `tokenHook.budget counts "${claim}", which neither ownedClaims nor registeredClaims lists`);
1601
+ }
1602
+ }]
1603
+ ]);
1604
+ }
1605
+ //#endregion
1606
+ //#region src/testing/generator.ts
1607
+ function relativeImport(from, to) {
1608
+ const fromParts = from.split("/").slice(0, -1);
1609
+ const toParts = to.split("/");
1610
+ let common = 0;
1611
+ while (common < fromParts.length && fromParts[common] === toParts[common]) common += 1;
1612
+ const path = [...Array.from({ length: fromParts.length - common }, () => ".."), ...toParts.slice(common)].join("/");
1613
+ return path.startsWith(".") ? path : `./${path}`;
1614
+ }
1615
+ /** A `GeneratorModel` from schema metadata, for generators tested without a database. */
1616
+ function modelFromMeta(meta) {
1617
+ return {
1618
+ tables: Object.values(meta.tables).map((table) => ({
1619
+ key: table.key,
1620
+ schema: table.schema,
1621
+ name: table.name,
1622
+ casing: meta.casing,
1623
+ columns: Object.entries(table.columns).map(([app, column]) => ({
1624
+ app,
1625
+ db: column.db,
1626
+ tsType: column.enum ? column.enum.map((value) => JSON.stringify(value)).join(" | ") : "unknown",
1627
+ nullable: column.nullable,
1628
+ optional: column.nullable || column.hasDefault || column.generated === true,
1629
+ readonly: column.generated === true,
1630
+ values: column.enum,
1631
+ json: column.json === true
1632
+ }))
1633
+ })),
1634
+ enums: Object.entries(meta.enums).map(([name, values]) => ({
1635
+ schema: "public",
1636
+ name,
1637
+ values
1638
+ }))
1639
+ };
1640
+ }
1641
+ const EMPTY_INTROSPECTION = {
1642
+ version: 1,
1643
+ schemas: [],
1644
+ tables: [],
1645
+ foreignTables: [],
1646
+ views: [],
1647
+ materializedViews: [],
1648
+ columns: [],
1649
+ primaryKeys: [],
1650
+ relationships: [],
1651
+ functions: [],
1652
+ types: []
1653
+ };
1654
+ /** Proves a `Generator` writes relative, unique paths, is deterministic and doesn't mutate its input. */
1655
+ function testGenerator(generator, options) {
1656
+ const config = options.config ?? resolveConfig({}, "/project");
1657
+ const input = () => ({
1658
+ meta: frozenCopy(options.meta),
1659
+ introspection: frozenCopy(options.introspection ?? EMPTY_INTROSPECTION),
1660
+ extras: frozenCopy(options.extras ?? {
1661
+ tables: [],
1662
+ buckets: [],
1663
+ realtime: []
1664
+ }),
1665
+ config,
1666
+ output: `${config.root}/${config.output}`,
1667
+ importPath: (from, to) => relativeImport(from, to),
1668
+ model: frozenCopy(options.model ?? modelFromMeta(options.meta))
1669
+ });
1670
+ return conform(`Generator "${generator.name}"`, [
1671
+ hasName(generator),
1672
+ ["targets generator API 1", () => {
1673
+ const version = generator.apiVersion;
1674
+ expect(version === void 0 || version === 1, `apiVersion is ${String(version)}; this better-supabase supports 1`);
1675
+ }],
1676
+ ["writes files inside the project", async () => {
1677
+ const files = await generator.generate(input());
1678
+ expect(isList(files), "generate() must return an array");
1679
+ for (const file of files) {
1680
+ expect(typeof file.path === "string" && typeof file.contents === "string", "files need a path and contents");
1681
+ expect(!file.path.startsWith("/") && !/^[A-Za-z]:/.test(file.path), `${file.path} must be relative`);
1682
+ expect(!file.path.split(/[\\/]/).includes(".."), `${file.path} must stay inside the project`);
1683
+ }
1684
+ const paths = files.map((file) => file.path);
1685
+ expect(new Set(paths).size === paths.length, "paths must be unique");
1686
+ }],
1687
+ ["is deterministic and does not mutate its input", async () => {
1688
+ const first = await generator.generate(input());
1689
+ const second = await generator.generate(input());
1690
+ expect(same(first, second), "two runs on the same input differ");
1691
+ }]
1692
+ ]);
1693
+ }
1694
+ //#endregion
1695
+ //#region src/testing/injectables.ts
1696
+ const hasApiVersion = (subject) => ["has apiVersion 1", () => {
1697
+ const version = subject.apiVersion;
1698
+ expect(version === 1, `apiVersion is ${String(version)}, not 1`);
1699
+ }];
1700
+ const rejects = async (run) => {
1701
+ try {
1702
+ await run();
1703
+ return false;
1704
+ } catch {
1705
+ return true;
1706
+ }
1707
+ };
1708
+ /**
1709
+ * Proves an `Embedder` for the knowledge and memory blocks names its model,
1710
+ * embeds an empty list to nothing, and returns one finite vector per value,
1711
+ * all of the same length on every call. It calls the real model.
1712
+ */
1713
+ function testEmbedder(embedder, options = {}) {
1714
+ const values = options.values ?? ["The first text.", "A second text."];
1715
+ return conform(`Embedder "${embedder.model}"`, [
1716
+ hasApiVersion(embedder),
1717
+ ["names its model", () => {
1718
+ expect(typeof embedder.model === "string" && embedder.model !== "", "model must be a non-empty string");
1719
+ }],
1720
+ ["embeds nothing to nothing", async () => {
1721
+ const vectors = await embedder.embed([]);
1722
+ expect(vectors.length === 0, `got ${vectors.length} vectors`);
1723
+ }],
1724
+ ["returns one finite vector per value, of one length", async () => {
1725
+ const first = await embedder.embed(values);
1726
+ expect(first.length === values.length, `got ${first.length} vectors for ${values.length} values`);
1727
+ const size = first[0]?.length ?? 0;
1728
+ expect(size > 0, "vectors must not be empty");
1729
+ expect(first.every((vector) => vector.length === size && vector.every(Number.isFinite)), "vectors must share one length and hold finite numbers");
1730
+ const [again] = await embedder.embed(values.slice(0, 1));
1731
+ expect(again?.length === size, "a second call changed the length");
1732
+ }]
1733
+ ]);
1734
+ }
1735
+ /**
1736
+ * Proves an `AiTaskRunner` resolves with nothing or a chat id and stops when
1737
+ * its signal is already aborted. It runs `options.task` for real.
1738
+ */
1739
+ function testAiTaskRunner(runner, options) {
1740
+ const { task, run } = options;
1741
+ return conform("AiTaskRunner", [
1742
+ hasApiVersion(runner),
1743
+ ["resolves with nothing or a chat id", async () => {
1744
+ const result = await runner(task, run, new AbortController().signal);
1745
+ expect(result === void 0 || typeof result === "object" && result !== null && (!("chatId" in result) || result.chatId === void 0 || typeof result.chatId === "string"), "must resolve with undefined or { chatId?: string }");
1746
+ }],
1747
+ ["stops on an aborted signal", async () => {
1748
+ expect(await rejects(() => runner(task, run, AbortSignal.abort())), "resolved although the signal was aborted");
1749
+ }]
1750
+ ]);
1751
+ }
1752
+ /**
1753
+ * Proves a `GraphCompiler` returns a JSON value (stored with the published
1754
+ * version), compiles the same graph to the same value and leaves the graph
1755
+ * unchanged.
1756
+ */
1757
+ function testGraphCompiler(compile, options) {
1758
+ const { graph } = options;
1759
+ return conform("GraphCompiler", [
1760
+ hasApiVersion(compile),
1761
+ ["returns a JSON value", async () => {
1762
+ const compiled = await compile(graph);
1763
+ if (compiled === void 0) return;
1764
+ const json = JSON.stringify(compiled);
1765
+ expect(same(JSON.parse(json), compiled), "the compiled form must survive a JSON round trip");
1766
+ }],
1767
+ ["compiles one graph to one value, without changing it", async () => {
1768
+ const before = structuredClone(graph);
1769
+ const first = await compile(graph);
1770
+ const second = await compile(graph);
1771
+ expect(same(first, second), "two compiles of one graph differ");
1772
+ expect(same(graph, before), "compile changed the graph");
1773
+ }]
1774
+ ]);
1775
+ }
1776
+ /**
1777
+ * Proves a `BuilderStarter` returns a run id and returns the same id when
1778
+ * the same idempotency key starts again. It starts real runs.
1779
+ */
1780
+ function testBuilderStarter(start, options) {
1781
+ const { call } = options;
1782
+ return conform("BuilderStarter", [hasApiVersion(start), ["returns one run id per idempotency key", async () => {
1783
+ const first = await start(call);
1784
+ expect(typeof first === "string" && first !== "", "must resolve with a non-empty run id");
1785
+ expect(await start(call) === first, "a repeated idempotency key started a new run");
1786
+ }]]);
1787
+ }
1788
+ /**
1789
+ * Proves an `EveDocumentBackend` reads a missing document as `null`, creates
1790
+ * one with `expectedVersion: null`, refuses a stale version and moves the
1791
+ * version on every write. It writes `options.key` on the real store.
1792
+ */
1793
+ function testEveDocumentBackend(backend, options = {}) {
1794
+ const key = options.key ?? `conformance/${crypto.randomUUID()}`;
1795
+ return conform("EveDocumentBackend", [hasApiVersion(backend), ["creates, reads and updates a document by version", async () => {
1796
+ expect(await backend.read({ key }) === null, "a missing document must read as null");
1797
+ const created = await backend.write({
1798
+ key,
1799
+ content: "one",
1800
+ expectedVersion: null
1801
+ });
1802
+ const read = await backend.read({ key });
1803
+ expect(read?.content === "one" && read.version === created.version, "read must return the written content and version");
1804
+ expect((await backend.write({
1805
+ key,
1806
+ content: "two",
1807
+ expectedVersion: created.version
1808
+ })).version !== created.version, "a write must change the version");
1809
+ expect(await rejects(() => backend.write({
1810
+ key,
1811
+ content: "three",
1812
+ expectedVersion: created.version
1813
+ })), "a write with a stale version must throw");
1814
+ expect((await backend.read({ key }))?.content === "two", "a refused write changed the document");
1815
+ }]]);
1816
+ }
1817
+ //#endregion
1214
1818
  //#region src/testing/notification-channel.ts
1215
1819
  const STATUSES = /* @__PURE__ */ new Set(["sent", "skipped"]);
1216
1820
  function sample(channel, email) {
@@ -1383,4 +1987,367 @@ function testWebhookSecretStore(store, options) {
1383
1987
  ]);
1384
1988
  }
1385
1989
  //#endregion
1386
- export { ConformanceError, LOCAL_JWT_SECRET, asUser, createTestSigner, defineSeed, expectDbBudget, expectTenantIsolation, isSeed, localAuth, localSigningKey, signLocalJwt, signTestJwt, signTestJwtWithKey, supabaseClaimFixtures, testAuthResolver, testCacheAdapter, testEventSink, testExecutor, testGenerator, testNotificationChannel, testPlugin, testQueueBackend, testSupportSessionStore, testWebhookSecretStore, testWebhookSigner, testWebhookTransport };
1990
+ //#region src/testing/chat-state.ts
1991
+ const sleep = (ms) => new Promise((resolve) => {
1992
+ setTimeout(resolve, ms);
1993
+ });
1994
+ /**
1995
+ * Runs the Chat SDK `StateAdapter` contract against `state`: subscriptions,
1996
+ * one lock holder at a time with token-checked release and extension, lock
1997
+ * and cache TTLs, `setIfNotExists`, list trimming, and a per-thread queue
1998
+ * that keeps the newest entries, reports its depth and drops expired ones.
1999
+ */
2000
+ function testChatState(state, options) {
2001
+ const fresh = options.id ?? (() => `conformance-${crypto.randomUUID()}`);
2002
+ const ttl = options.ttlMs ?? 300;
2003
+ const entry = (thread, text, expiresIn = 6e4) => ({
2004
+ enqueuedAt: Date.now(),
2005
+ expiresAt: Date.now() + expiresIn,
2006
+ message: options.message(`${thread}-${text}`, text)
2007
+ });
2008
+ return conform("StateAdapter", [
2009
+ ["subscribes and unsubscribes a thread", async () => {
2010
+ const thread = fresh();
2011
+ expect(!await state.isSubscribed(thread), "new thread is subscribed");
2012
+ await state.subscribe(thread);
2013
+ await state.subscribe(thread);
2014
+ expect(await state.isSubscribed(thread), "subscribe did not stick");
2015
+ await state.unsubscribe(thread);
2016
+ expect(!await state.isSubscribed(thread), "unsubscribe did not stick");
2017
+ }],
2018
+ ["gives the lock to one holder at a time", async () => {
2019
+ const thread = fresh();
2020
+ const [a, b] = await Promise.all([state.acquireLock(thread, 1e4), state.acquireLock(thread, 1e4)]);
2021
+ expect(a === null !== (b === null), "both or neither got the lock");
2022
+ const held = a ?? b;
2023
+ expect(held !== null, "no lock");
2024
+ expect(held.threadId === thread, "lock names another thread");
2025
+ expect(held.expiresAt > Date.now(), "lock is already expired");
2026
+ await state.releaseLock({
2027
+ ...held,
2028
+ token: "not-the-token"
2029
+ });
2030
+ expect(await state.acquireLock(thread, 1e4) === null, "a wrong token released the lock");
2031
+ expect(await state.extendLock(held, 2e4), "extendLock failed");
2032
+ await state.releaseLock(held);
2033
+ expect(await state.acquireLock(thread, 1e4) !== null, "released lock was not free");
2034
+ await state.forceReleaseLock(thread);
2035
+ expect(await state.acquireLock(thread, 1e4) !== null, "forceReleaseLock left the lock held");
2036
+ }],
2037
+ ["lets an expired lock go", async () => {
2038
+ const thread = fresh();
2039
+ const lock = await state.acquireLock(thread, ttl);
2040
+ expect(lock !== null, "no lock");
2041
+ await sleep(ttl + 100);
2042
+ expect(!await state.extendLock(lock, 1e4), "extended an expired lock");
2043
+ expect(await state.acquireLock(thread, 1e4) !== null, "expired lock is still held");
2044
+ }],
2045
+ ["stores values with a TTL", async () => {
2046
+ const key = fresh();
2047
+ expect(await state.get(key) === null, "missing key is not null");
2048
+ await state.set(key, {
2049
+ n: 1,
2050
+ list: ["a"]
2051
+ });
2052
+ const stored = await state.get(key);
2053
+ expect(stored?.n === 1 && stored.list[0] === "a", "value changed");
2054
+ await state.set(key, "text");
2055
+ expect(await state.get(key) === "text", "a string value changed");
2056
+ await state.delete(key);
2057
+ expect(await state.get(key) === null, "delete left the value");
2058
+ await state.set(key, 1, ttl);
2059
+ await sleep(ttl + 100);
2060
+ expect(await state.get(key) === null, "value outlived its TTL");
2061
+ }],
2062
+ ["sets a key only when it is missing", async () => {
2063
+ const key = fresh();
2064
+ expect(await state.setIfNotExists(key, "first"), "first set failed");
2065
+ expect(!await state.setIfNotExists(key, "second"), "second set won");
2066
+ expect(await state.get(key) === "first", "value was replaced");
2067
+ const short = fresh();
2068
+ await state.setIfNotExists(short, "old", ttl);
2069
+ await sleep(ttl + 100);
2070
+ expect(await state.setIfNotExists(short, "new"), "an expired key blocked the set");
2071
+ }],
2072
+ ["appends to a list and keeps the newest maxLength", async () => {
2073
+ const key = fresh();
2074
+ for (const value of [
2075
+ "a",
2076
+ "b",
2077
+ "c",
2078
+ "d"
2079
+ ]) await state.appendToList(key, value, { maxLength: 3 });
2080
+ const list = await state.getList(key);
2081
+ expect(list.join() === "b,c,d", `list is ${list.join()}`);
2082
+ expect((await state.getList(fresh())).length === 0, "missing list is not empty");
2083
+ }],
2084
+ ["queues per thread, keeps the newest maxSize and reports the depth", async () => {
2085
+ const thread = fresh();
2086
+ expect(await state.queueDepth(thread) === 0, "new queue is not empty");
2087
+ expect(await state.dequeue(thread) === null, "empty queue dequeued");
2088
+ let depth = 0;
2089
+ for (const text of [
2090
+ "one",
2091
+ "two",
2092
+ "three"
2093
+ ]) depth = await state.enqueue(thread, entry(thread, text), 2);
2094
+ expect(depth === 2, `depth after three with maxSize 2 is ${String(depth)}`);
2095
+ expect(await state.queueDepth(thread) === 2, "queueDepth disagrees");
2096
+ const first = await state.dequeue(thread);
2097
+ expect(first?.message.text === "two", "dequeue did not return the oldest kept entry");
2098
+ expect(first.message.id === `${thread}-two`, "message id changed");
2099
+ expect(await state.queueDepth(thread) === 1, "dequeue did not remove it");
2100
+ expect(await state.queueDepth(fresh()) === 0, "another thread shares the queue");
2101
+ }],
2102
+ ["drops expired queue entries", async () => {
2103
+ const thread = fresh();
2104
+ await state.enqueue(thread, entry(thread, "stale", ttl), 10);
2105
+ await sleep(ttl + 100);
2106
+ expect(await state.queueDepth(thread) === 0, "an expired entry counts");
2107
+ expect(await state.dequeue(thread) === null, "an expired entry dequeued");
2108
+ }]
2109
+ ]);
2110
+ }
2111
+ //#endregion
2112
+ //#region src/testing/streams.ts
2113
+ async function collect(stream, timeoutMs) {
2114
+ const chunks = [];
2115
+ const reader = stream.getReader();
2116
+ let timer;
2117
+ const timeout = new Promise((_, reject) => {
2118
+ timer = setTimeout(() => {
2119
+ reject(/* @__PURE__ */ new Error(`read did not end within ${String(timeoutMs)} ms`));
2120
+ }, timeoutMs);
2121
+ });
2122
+ try {
2123
+ for (;;) {
2124
+ const { done, value } = await Promise.race([reader.read(), timeout]);
2125
+ if (done) return chunks;
2126
+ chunks.push(value);
2127
+ }
2128
+ } finally {
2129
+ clearTimeout(timer);
2130
+ await reader.cancel().catch(() => void 0);
2131
+ }
2132
+ }
2133
+ const hintOf = (result) => result.ok ? void 0 : result.error.hint;
2134
+ /**
2135
+ * Runs the `StreamStore` contract against `store`: the API version, open
2136
+ * once, idempotent appends that refuse gaps, reads from any index that end
2137
+ * when the stream closes, a live read that sees later appends, cancel seen by
2138
+ * the writer, a closed stream that refuses appends, and a missing stream.
2139
+ */
2140
+ function testStreamStore(store, options = {}) {
2141
+ const fresh = options.id ?? (() => `conformance-${crypto.randomUUID()}`);
2142
+ const timeoutMs = options.timeoutMs ?? 5e3;
2143
+ return conform(`StreamStore "${store.name}"`, [
2144
+ ["has apiVersion 1 and a name", () => {
2145
+ const version = store.apiVersion;
2146
+ expect(version === 1, `apiVersion is ${String(version)}`);
2147
+ expect(store.name.length > 0, "name is empty");
2148
+ }],
2149
+ ["opens a stream once", async () => {
2150
+ const id = fresh();
2151
+ expect(await store.open(id).orThrow(), "first open was not new");
2152
+ expect(!await store.open(id).orThrow(), "second open was new");
2153
+ }],
2154
+ ["appends idempotently and refuses a gap", async () => {
2155
+ const id = fresh();
2156
+ await store.open(id).orThrow();
2157
+ const first = await store.append(id, 0, ["a", "b"]).orThrow();
2158
+ expect(first.next === 2, `next after two chunks is ${String(first.next)}`);
2159
+ const retried = await store.append(id, 0, [
2160
+ "a",
2161
+ "b",
2162
+ "c"
2163
+ ]).orThrow();
2164
+ expect(retried.next === 3, `next after a retried batch is ${String(retried.next)}`);
2165
+ const gap = await store.append(id, 5, ["x"]);
2166
+ expect(hintOf(gap) === "STREAM_GAP", `a gap gave ${JSON.stringify(gap)}`);
2167
+ await store.close(id).orThrow();
2168
+ const chunks = await collect(store.read(id), timeoutMs);
2169
+ expect(chunks.join("") === "abc", `read ${JSON.stringify(chunks)} after a retried batch`);
2170
+ }],
2171
+ ["reads from an index and ends once closed", async () => {
2172
+ const id = fresh();
2173
+ await store.open(id).orThrow();
2174
+ await store.append(id, 0, [
2175
+ "0",
2176
+ "1",
2177
+ "2",
2178
+ "3"
2179
+ ]).orThrow();
2180
+ expect(await store.close(id).orThrow(), "close returned false");
2181
+ expect(!await store.close(id).orThrow(), "a second close returned true");
2182
+ const tail = await collect(store.read(id, 2), timeoutMs);
2183
+ expect(tail.join(",") === "2,3", `read from 2 gave ${JSON.stringify(tail)}`);
2184
+ const status = await store.status(id).orThrow();
2185
+ expect(status?.next === 4 && status.closed && !status.cancelled, `status is ${JSON.stringify(status)}`);
2186
+ }],
2187
+ ["a live read sees later appends", async () => {
2188
+ const id = fresh();
2189
+ await store.open(id).orThrow();
2190
+ const reading = collect(store.read(id), timeoutMs);
2191
+ await store.append(id, 0, ["x"]).orThrow();
2192
+ await store.append(id, 1, ["y"]).orThrow();
2193
+ await store.close(id).orThrow();
2194
+ const chunks = await reading;
2195
+ expect(chunks.join("") === "xy", `live read gave ${JSON.stringify(chunks)}`);
2196
+ }],
2197
+ ["tells the writer about a cancel", async () => {
2198
+ const id = fresh();
2199
+ await store.open(id).orThrow();
2200
+ expect(!await store.isCancelled(id).orThrow(), "a new stream is cancelled");
2201
+ expect(await store.cancel(id).orThrow(), "cancel returned false");
2202
+ expect(await store.isCancelled(id).orThrow(), "isCancelled is false after cancel");
2203
+ expect((await store.append(id, 0, ["late"]).orThrow()).cancelled, "append did not report the cancel");
2204
+ await store.close(id).orThrow();
2205
+ expect(!await store.cancel(id).orThrow(), "cancel of a closed stream returned true");
2206
+ }],
2207
+ ["refuses appends to a closed stream", async () => {
2208
+ const id = fresh();
2209
+ await store.open(id).orThrow();
2210
+ await store.close(id).orThrow();
2211
+ const closed = await store.append(id, 0, ["x"]);
2212
+ expect(hintOf(closed) === "STREAM_CLOSED", `append gave ${JSON.stringify(closed)}`);
2213
+ }],
2214
+ ["treats a missing stream as empty", async () => {
2215
+ const id = fresh();
2216
+ expect(await store.status(id).orThrow() === void 0, "status of a missing stream");
2217
+ const chunks = await collect(store.read(id), timeoutMs);
2218
+ expect(chunks.length === 0, `read a missing stream as ${JSON.stringify(chunks)}`);
2219
+ const appended = await store.append(id, 0, ["x"]);
2220
+ expect(hintOf(appended) === "STREAM_NOT_FOUND", `append gave ${JSON.stringify(appended)}`);
2221
+ }],
2222
+ ["purges with a count", async () => {
2223
+ const purged = await store.purge().orThrow();
2224
+ expect(Number.isInteger(purged) && purged >= 0, `purge returned ${String(purged)}`);
2225
+ }]
2226
+ ]);
2227
+ }
2228
+ //#endregion
2229
+ //#region src/testing/credentials.ts
2230
+ const APP = { type: "app" };
2231
+ const kindOf = (result) => result.ok ? void 0 : result.error.kind;
2232
+ /**
2233
+ * Runs the `CredentialProvider` contract against `provider`: the API version,
2234
+ * an unknown ref, a stored token with its headers, a new value seen after it
2235
+ * is stored, separate credentials per user and per tenant (`ref` with a
2236
+ * `tenant`, which `seed` must store too), revoke (per user, as the
2237
+ * organization purger revokes connector grants), and inbound checks that
2238
+ * accept a signed request and refuse a tampered one.
2239
+ */
2240
+ function testCredentialProvider(provider, options) {
2241
+ const value = () => `token-${crypto.randomUUID()}`;
2242
+ const { ref, seed, userRef, inbound } = options;
2243
+ return conform(`CredentialProvider "${provider.name}"`, [
2244
+ ["has apiVersion 1 and a name", () => {
2245
+ const version = provider.apiVersion;
2246
+ expect(version === 1, `apiVersion is ${String(version)}`);
2247
+ expect(provider.name.length > 0, "name is empty");
2248
+ }],
2249
+ ["refuses a ref for another provider", async () => {
2250
+ const result = await provider.getToken({ provider: "conformance-unknown" }, { subject: APP });
2251
+ expect(kindOf(result) === "invalid_input", `got ${kindOf(result) ?? "a token"}`);
2252
+ }],
2253
+ ["returns a stored token with headers", async () => {
2254
+ const token = value();
2255
+ await seed(ref, APP, token);
2256
+ const got = await provider.getToken(ref, { subject: APP }).orThrow();
2257
+ expect(got.token === token, "returned another token");
2258
+ expect(Object.values(got.headers).some((header) => header.includes(token)), "no header carries the token");
2259
+ }],
2260
+ ["sees a new value once it is stored", async () => {
2261
+ await seed(ref, APP, value());
2262
+ await provider.getToken(ref, { subject: APP }).orThrow();
2263
+ const next = value();
2264
+ await seed(ref, APP, next);
2265
+ expect((await provider.getToken(ref, { subject: APP }).orThrow()).token === next, "returned the previous value");
2266
+ }],
2267
+ provider.set !== void 0 && ["stores a value through set", async () => {
2268
+ const set = provider.set?.bind(provider);
2269
+ expect(set !== void 0, "no set");
2270
+ const next = value();
2271
+ await set(ref, next, { subject: APP }).orThrow();
2272
+ expect((await provider.getToken(ref, { subject: APP }).orThrow()).token === next, "getToken missed the value set stored");
2273
+ }],
2274
+ userRef !== void 0 && ["keeps a credential per user", async () => {
2275
+ const alice = {
2276
+ type: "user",
2277
+ id: crypto.randomUUID()
2278
+ };
2279
+ const bob = {
2280
+ type: "user",
2281
+ id: crypto.randomUUID()
2282
+ };
2283
+ const [forAlice, forBob] = [value(), value()];
2284
+ await seed(userRef, alice, forAlice);
2285
+ await seed(userRef, bob, forBob);
2286
+ const a = await provider.getToken(userRef, { subject: alice }).orThrow();
2287
+ const b = await provider.getToken(userRef, { subject: bob }).orThrow();
2288
+ expect(a.token === forAlice && b.token === forBob, "users share a credential");
2289
+ expect(provider.capabilities(userRef).userSubjects, "capabilities do not report user subjects");
2290
+ expect(!(await provider.getToken(userRef, { subject: APP })).ok, "the app read a per-user credential");
2291
+ }],
2292
+ userRef !== void 0 && ["revokes one user's credential only", async () => {
2293
+ const alice = {
2294
+ type: "user",
2295
+ id: crypto.randomUUID()
2296
+ };
2297
+ const bob = {
2298
+ type: "user",
2299
+ id: crypto.randomUUID()
2300
+ };
2301
+ const forBob = value();
2302
+ await seed(userRef, alice, value());
2303
+ await seed(userRef, bob, forBob);
2304
+ expect(await provider.revoke(userRef, { subject: alice }).orThrow(), "revoke found nothing for the user");
2305
+ const after = await provider.getToken(userRef, { subject: alice });
2306
+ expect(kindOf(after) === "not_found", `after revoke got ${kindOf(after) ?? "a token"}`);
2307
+ expect((await provider.getToken(userRef, { subject: bob }).orThrow()).token === forBob, "revoking a user's credential removed another user's");
2308
+ await provider.revoke(userRef, { subject: bob }).orThrow();
2309
+ }],
2310
+ ["keeps a tenant's credential inside its namespace", async () => {
2311
+ const forA = tenantCredentialRef(crypto.randomUUID(), ref);
2312
+ const forB = tenantCredentialRef(crypto.randomUUID(), ref);
2313
+ const [app, a, b] = [
2314
+ value(),
2315
+ value(),
2316
+ value()
2317
+ ];
2318
+ await seed(ref, APP, app);
2319
+ await seed(forA, APP, a);
2320
+ await seed(forB, APP, b);
2321
+ const tokenOf = async (of) => (await provider.getToken(of, { subject: APP }).orThrow()).token;
2322
+ expect(await tokenOf(ref) === app && await tokenOf(forA) === a && await tokenOf(forB) === b, "a tenant's ref resolved another tenant's or the app's credential");
2323
+ expect(await provider.revoke(forA, { subject: APP }).orThrow(), "revoke found nothing in the tenant's namespace");
2324
+ const after = await provider.getToken(forA, { subject: APP });
2325
+ expect(kindOf(after) === "not_found", `after revoke got ${kindOf(after) ?? "a token"}`);
2326
+ expect(await tokenOf(forB) === b && await tokenOf(ref) === app, "revoking a tenant's ref removed another tenant's or the app's credential");
2327
+ await provider.revoke(forB, { subject: APP }).orThrow();
2328
+ }],
2329
+ ["revokes a credential", async () => {
2330
+ await seed(ref, APP, value());
2331
+ expect(await provider.revoke(ref, { subject: APP }).orThrow(), "revoke found nothing");
2332
+ const after = await provider.getToken(ref, { subject: APP });
2333
+ expect(kindOf(after) === "not_found", `after revoke got ${kindOf(after) ?? "a token"}`);
2334
+ expect(!await provider.revoke(ref, { subject: APP }).orThrow(), "a second revoke found a credential");
2335
+ }],
2336
+ inbound !== void 0 && ["verifies inbound requests", async () => {
2337
+ const verify = provider.verifyInbound?.bind(provider);
2338
+ expect(verify !== void 0, "no verifyInbound");
2339
+ const secret = `whsec_${btoa(crypto.randomUUID())}`;
2340
+ await seed(inbound.ref, APP, secret);
2341
+ const body = JSON.stringify({ hello: "world" });
2342
+ const signed = await inbound.sign(body, secret);
2343
+ expect(await verify(signed, inbound.ref).orThrow(), "refused a signed request");
2344
+ expect(!await verify(new Request(signed.url, {
2345
+ method: "POST",
2346
+ headers: signed.headers,
2347
+ body: `${body} `
2348
+ }), inbound.ref).orThrow(), "accepted a tampered request");
2349
+ }]
2350
+ ]);
2351
+ }
2352
+ //#endregion
2353
+ export { ConformanceError, LOCAL_JWT_SECRET, asUser, createTestSigner, defineSeed, expectDbBudget, expectInstant, expectTenantIsolation, isSeed, localAuth, localSigningKey, signLocalJwt, signTestJwt, signTestJwtWithKey, supabaseClaimFixtures, testAdapter, testAiTaskRunner, testAuthResolver, testAuthorizationProvider, testBlockTransportMiddleware, testBuilderStarter, testCacheAdapter, testChatState, testCredentialProvider, testEmbedder, testEveDocumentBackend, testEventSink, testExecutor, testGenerator, testGraphCompiler, testNotificationChannel, testPlugin, testQueueBackend, testStreamStore, testSupportSessionStore, testWebhookSecretStore, testWebhookSigner, testWebhookTransport };