better-supabase 0.7.1 → 0.9.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 (1377) hide show
  1. package/README.md +146 -81
  2. package/bin/better-supabase.js +14 -2
  3. package/dist/access-sql-DUSSMaC3.js +201 -0
  4. package/dist/actor-BAhb-d1S.d.ts +104 -0
  5. package/dist/actor-KHDOKGb1.js +151 -0
  6. package/dist/adapter-CEjmtazZ.js +1984 -0
  7. package/dist/agents-BCNhIxVZ.d.ts +111 -0
  8. package/dist/ai-cache-Dps7aRu1.d.ts +70 -0
  9. package/dist/ai-cache-zeUL94pB.js +84 -0
  10. package/dist/ai-chat-3GBSlFR4.d.ts +608 -0
  11. package/dist/ai-files-D2_9YLjF.js +331 -0
  12. package/dist/ai-message-DfaP8sis.js +14 -0
  13. package/dist/ai-sdk/agents/index.d.ts +154 -10
  14. package/dist/ai-sdk/agents/index.js +172 -17
  15. package/dist/ai-sdk/batches/index.d.ts +4 -4
  16. package/dist/ai-sdk/batches/index.js +5 -5
  17. package/dist/ai-sdk/cache/index.d.ts +2 -2
  18. package/dist/ai-sdk/cache/index.js +2 -2
  19. package/dist/ai-sdk/chat/index.d.ts +1 -1
  20. package/dist/ai-sdk/chat/index.js +10 -21
  21. package/dist/ai-sdk/embeddings/index.d.ts +3 -3
  22. package/dist/ai-sdk/embeddings/index.js +1 -2
  23. package/dist/ai-sdk/files/index.d.ts +3 -3
  24. package/dist/ai-sdk/files/index.js +4 -4
  25. package/dist/ai-sdk/index.d.ts +38 -9
  26. package/dist/ai-sdk/index.js +5 -3
  27. package/dist/ai-sdk/mcp/index.d.ts +43 -6
  28. package/dist/ai-sdk/mcp/index.js +37 -8
  29. package/dist/ai-sdk/memory/index.d.ts +5 -2
  30. package/dist/ai-sdk/memory/index.js +2 -2
  31. package/dist/ai-sdk/react/index.js +1 -2
  32. package/dist/ai-sdk/workflow/index.d.ts +7 -6
  33. package/dist/ai-sdk/workflow/index.js +15 -26
  34. package/dist/ai-tasks-Ih5oHfyj.d.ts +157 -0
  35. package/dist/announcements-BHCzraR6.js +128 -0
  36. package/dist/api-CFjuYKFY.js +680 -0
  37. package/dist/api-TG3jE5aO.d.ts +411 -0
  38. package/dist/api-keys-BEQatEUW.js +318 -0
  39. package/dist/api-keys-BYoGCM9p.d.ts +205 -0
  40. package/dist/app-tables-BGl5_U2x.js +584 -0
  41. package/dist/approvals-a2KmioBh.d.ts +124 -0
  42. package/dist/arazzo/index.d.ts +255 -0
  43. package/dist/arazzo/index.js +622 -0
  44. package/dist/assistant-Bf2cA6RO.d.ts +148 -0
  45. package/dist/astro/index.d.ts +9 -8
  46. package/dist/astro/index.js +12 -9
  47. package/dist/asyncapi/index.d.ts +103 -0
  48. package/dist/asyncapi/index.js +569 -0
  49. package/dist/attachments-DAj2Yp95.js +264 -0
  50. package/dist/attributes-D4mA7WhI.js +38 -0
  51. package/dist/audit-log-BMMuGPdP.js +786 -0
  52. package/dist/auth-CdBKt0CS.js +20 -0
  53. package/dist/auth-reply-DUCwb8YO.js +31 -0
  54. package/dist/auth-reply-Dj124sbX.d.ts +11 -0
  55. package/dist/authorization-DpRfs6Wx.d.ts +1412 -0
  56. package/dist/authorization-hooks-DkmrnDX7.d.ts +214 -0
  57. package/dist/authorizer-DH7DjkL3.js +126 -0
  58. package/dist/authorizer-eiOjo-3l.d.ts +73 -0
  59. package/dist/base64-BmeCBgGo.js +42 -0
  60. package/dist/batch-limit-DKVQDz4o.js +5 -0
  61. package/dist/before-route-B98js9m_.js +30 -0
  62. package/dist/better-result/index.d.ts +118 -0
  63. package/dist/better-result/index.js +64 -0
  64. package/dist/bind-a7sUgvf4.d.ts +70 -0
  65. package/dist/block-env-CBd_SoL5.js +29 -0
  66. package/dist/block-env-Dxl7rsI0.d.ts +10 -0
  67. package/dist/block-events-DD9Q_NUz.js +96 -0
  68. package/dist/block-fields-5UA84ZUg.d.ts +55 -0
  69. package/dist/block-fields-ffD6Gxe4.js +139 -0
  70. package/dist/block-helpers-C86hPEjI.d.ts +37 -0
  71. package/dist/block-helpers-CfG_MvDR.js +264 -0
  72. package/dist/block-hooks-BLPWD3Jt.d.ts +107 -0
  73. package/dist/block-hooks-BsXncUlZ.js +114 -0
  74. package/dist/block-transport-DaGGXjIw.d.ts +70 -0
  75. package/dist/block-transport-G4t1umrR.js +85 -0
  76. package/dist/blocks/access-requests/index.d.ts +98 -0
  77. package/dist/blocks/access-requests/index.js +99 -0
  78. package/dist/blocks/agents/index.d.ts +21 -3
  79. package/dist/blocks/agents/index.js +65 -18
  80. package/dist/blocks/ai-cache/index.d.ts +8 -3
  81. package/dist/blocks/ai-cache/index.js +15 -3
  82. package/dist/blocks/ai-chat/index.d.ts +28 -4
  83. package/dist/blocks/ai-chat/index.js +31 -5
  84. package/dist/blocks/ai-chat/react/index.d.ts +12 -46
  85. package/dist/blocks/ai-chat/react/index.js +43 -335
  86. package/dist/blocks/ai-chat/solid/index.d.ts +34 -0
  87. package/dist/blocks/ai-chat/solid/index.js +41 -0
  88. package/dist/blocks/ai-chat/svelte/index.d.ts +34 -0
  89. package/dist/blocks/ai-chat/svelte/index.js +41 -0
  90. package/dist/blocks/ai-chat/vue/index.d.ts +34 -0
  91. package/dist/blocks/ai-chat/vue/index.js +41 -0
  92. package/dist/blocks/ai-files/index.d.ts +11 -4
  93. package/dist/blocks/ai-files/index.js +16 -3
  94. package/dist/blocks/ai-providers/index.d.ts +18 -3
  95. package/dist/blocks/ai-providers/index.js +41 -15
  96. package/dist/blocks/ai-tasks/index.d.ts +10 -3
  97. package/dist/blocks/ai-tasks/index.js +71 -21
  98. package/dist/blocks/announcements/index.d.ts +18 -3
  99. package/dist/blocks/announcements/index.js +27 -3
  100. package/dist/blocks/announcements/react/index.d.ts +4 -14
  101. package/dist/blocks/announcements/react/index.js +22 -70
  102. package/dist/blocks/announcements/solid/index.d.ts +13 -0
  103. package/dist/blocks/announcements/solid/index.js +14 -0
  104. package/dist/blocks/announcements/svelte/index.d.ts +13 -0
  105. package/dist/blocks/announcements/svelte/index.js +14 -0
  106. package/dist/blocks/announcements/vue/index.d.ts +13 -0
  107. package/dist/blocks/announcements/vue/index.js +14 -0
  108. package/dist/blocks/api-keys/index.d.ts +24 -7
  109. package/dist/blocks/api-keys/index.js +30 -295
  110. package/dist/blocks/api-keys/react/index.d.ts +29 -0
  111. package/dist/blocks/api-keys/react/index.js +43 -0
  112. package/dist/blocks/api-keys/react/server.d.ts +7 -0
  113. package/dist/blocks/api-keys/react/server.js +7 -0
  114. package/dist/blocks/api-keys/solid/index.d.ts +13 -0
  115. package/dist/blocks/api-keys/solid/index.js +14 -0
  116. package/dist/blocks/api-keys/svelte/index.d.ts +13 -0
  117. package/dist/blocks/api-keys/svelte/index.js +14 -0
  118. package/dist/blocks/api-keys/vue/index.d.ts +13 -0
  119. package/dist/blocks/api-keys/vue/index.js +14 -0
  120. package/dist/blocks/approvals/index.d.ts +22 -0
  121. package/dist/blocks/approvals/index.js +189 -0
  122. package/dist/blocks/attachments/index.d.ts +18 -223
  123. package/dist/blocks/attachments/index.js +26 -233
  124. package/dist/blocks/audit/index.d.ts +72 -354
  125. package/dist/blocks/audit/index.js +91 -695
  126. package/dist/blocks/audit/react/index.d.ts +26 -0
  127. package/dist/blocks/audit/react/index.js +44 -0
  128. package/dist/blocks/audit/react/server.d.ts +7 -0
  129. package/dist/blocks/audit/react/server.js +7 -0
  130. package/dist/blocks/audit/solid/index.d.ts +13 -0
  131. package/dist/blocks/audit/solid/index.js +14 -0
  132. package/dist/blocks/audit/svelte/index.d.ts +13 -0
  133. package/dist/blocks/audit/svelte/index.js +14 -0
  134. package/dist/blocks/audit/vue/index.d.ts +13 -0
  135. package/dist/blocks/audit/vue/index.js +14 -0
  136. package/dist/blocks/billing/index.d.ts +40 -13
  137. package/dist/blocks/billing/index.js +49 -18
  138. package/dist/blocks/billing/react/index.d.ts +34 -0
  139. package/dist/blocks/billing/react/index.js +36 -0
  140. package/dist/blocks/billing/react/server.d.ts +7 -0
  141. package/dist/blocks/billing/react/server.js +7 -0
  142. package/dist/blocks/billing/solid/index.d.ts +15 -0
  143. package/dist/blocks/billing/solid/index.js +14 -0
  144. package/dist/blocks/billing/svelte/index.d.ts +15 -0
  145. package/dist/blocks/billing/svelte/index.js +14 -0
  146. package/dist/blocks/billing/vue/index.d.ts +15 -0
  147. package/dist/blocks/billing/vue/index.js +14 -0
  148. package/dist/blocks/bookings/index.d.ts +36 -0
  149. package/dist/blocks/bookings/index.js +28 -0
  150. package/dist/blocks/bookings/react/index.d.ts +31 -0
  151. package/dist/blocks/bookings/react/index.js +59 -0
  152. package/dist/blocks/bookings/react/server.d.ts +6 -0
  153. package/dist/blocks/bookings/react/server.js +7 -0
  154. package/dist/blocks/bookings/solid/index.d.ts +13 -0
  155. package/dist/blocks/bookings/solid/index.js +14 -0
  156. package/dist/blocks/bookings/svelte/index.d.ts +13 -0
  157. package/dist/blocks/bookings/svelte/index.js +14 -0
  158. package/dist/blocks/bookings/vue/index.d.ts +13 -0
  159. package/dist/blocks/bookings/vue/index.js +14 -0
  160. package/dist/blocks/bulk/index.d.ts +142 -0
  161. package/dist/blocks/bulk/index.js +154 -0
  162. package/dist/blocks/calendar/index.d.ts +20 -0
  163. package/dist/blocks/calendar/index.js +33 -0
  164. package/dist/blocks/calendar/react/index.d.ts +26 -0
  165. package/dist/blocks/calendar/react/index.js +58 -0
  166. package/dist/blocks/calendar/react/server.d.ts +6 -0
  167. package/dist/blocks/calendar/react/server.js +7 -0
  168. package/dist/blocks/calendar/solid/index.d.ts +13 -0
  169. package/dist/blocks/calendar/solid/index.js +14 -0
  170. package/dist/blocks/calendar/svelte/index.d.ts +13 -0
  171. package/dist/blocks/calendar/svelte/index.js +14 -0
  172. package/dist/blocks/calendar/vue/index.d.ts +13 -0
  173. package/dist/blocks/calendar/vue/index.js +14 -0
  174. package/dist/blocks/catalog/index.d.ts +224 -0
  175. package/dist/blocks/catalog/index.js +292 -0
  176. package/dist/blocks/collections/index.d.ts +184 -0
  177. package/dist/blocks/collections/index.js +208 -0
  178. package/dist/blocks/comments/index.d.ts +19 -166
  179. package/dist/blocks/comments/index.js +26 -206
  180. package/dist/blocks/comments/react/index.d.ts +27 -0
  181. package/dist/blocks/comments/react/index.js +45 -0
  182. package/dist/blocks/comments/react/server.d.ts +7 -0
  183. package/dist/blocks/comments/react/server.js +7 -0
  184. package/dist/blocks/comments/solid/index.d.ts +13 -0
  185. package/dist/blocks/comments/solid/index.js +14 -0
  186. package/dist/blocks/comments/svelte/index.d.ts +13 -0
  187. package/dist/blocks/comments/svelte/index.js +14 -0
  188. package/dist/blocks/comments/vue/index.d.ts +13 -0
  189. package/dist/blocks/comments/vue/index.js +14 -0
  190. package/dist/blocks/connectors/index.d.ts +21 -3
  191. package/dist/blocks/connectors/index.js +54 -18
  192. package/dist/blocks/custom-fields/index.d.ts +62 -0
  193. package/dist/blocks/custom-fields/index.js +85 -0
  194. package/dist/blocks/data-lifecycle/index.d.ts +119 -10
  195. package/dist/blocks/data-lifecycle/index.js +103 -12
  196. package/dist/blocks/deals/index.d.ts +262 -0
  197. package/dist/blocks/deals/index.js +380 -0
  198. package/dist/blocks/documents/index.d.ts +17 -0
  199. package/dist/blocks/documents/index.js +118 -0
  200. package/dist/blocks/domains/index.d.ts +117 -0
  201. package/dist/blocks/domains/index.js +118 -0
  202. package/dist/blocks/drive/index.d.ts +296 -0
  203. package/dist/blocks/drive/index.js +483 -0
  204. package/dist/blocks/employees/index.d.ts +205 -0
  205. package/dist/blocks/employees/index.js +289 -0
  206. package/dist/blocks/engagements/index.d.ts +125 -0
  207. package/dist/blocks/engagements/index.js +143 -0
  208. package/dist/blocks/entitlements/index.d.ts +40 -4
  209. package/dist/blocks/entitlements/index.js +14 -24
  210. package/dist/blocks/entitlements/react/index.d.ts +24 -0
  211. package/dist/blocks/entitlements/react/index.js +35 -0
  212. package/dist/blocks/entitlements/react/server.d.ts +6 -0
  213. package/dist/blocks/entitlements/react/server.js +7 -0
  214. package/dist/blocks/entitlements/solid/index.d.ts +13 -0
  215. package/dist/blocks/entitlements/solid/index.js +14 -0
  216. package/dist/blocks/entitlements/svelte/index.d.ts +13 -0
  217. package/dist/blocks/entitlements/svelte/index.js +14 -0
  218. package/dist/blocks/entitlements/vue/index.d.ts +13 -0
  219. package/dist/blocks/entitlements/vue/index.js +14 -0
  220. package/dist/blocks/flags/index.d.ts +61 -187
  221. package/dist/blocks/flags/index.js +65 -252
  222. package/dist/blocks/forms/index.d.ts +302 -0
  223. package/dist/blocks/forms/index.js +341 -0
  224. package/dist/blocks/forms/react/index.d.ts +16 -0
  225. package/dist/blocks/forms/react/index.js +25 -0
  226. package/dist/blocks/forms/react/server.d.ts +6 -0
  227. package/dist/blocks/forms/react/server.js +7 -0
  228. package/dist/blocks/forms/solid/index.d.ts +13 -0
  229. package/dist/blocks/forms/solid/index.js +14 -0
  230. package/dist/blocks/forms/svelte/index.d.ts +13 -0
  231. package/dist/blocks/forms/svelte/index.js +14 -0
  232. package/dist/blocks/forms/vue/index.d.ts +13 -0
  233. package/dist/blocks/forms/vue/index.js +14 -0
  234. package/dist/blocks/hiring/index.d.ts +154 -0
  235. package/dist/blocks/hiring/index.js +180 -0
  236. package/dist/blocks/identities/index.d.ts +83 -0
  237. package/dist/blocks/identities/index.js +103 -0
  238. package/dist/blocks/imports/index.d.ts +91 -0
  239. package/dist/blocks/imports/index.js +123 -0
  240. package/dist/blocks/inbox/index.d.ts +22 -3
  241. package/dist/blocks/inbox/index.js +78 -6
  242. package/dist/blocks/inbox/react/index.d.ts +3 -2
  243. package/dist/blocks/inbox/react/index.js +55 -199
  244. package/dist/blocks/inbox/react/server.d.ts +2 -1
  245. package/dist/blocks/inbox/solid/index.d.ts +27 -0
  246. package/dist/blocks/inbox/solid/index.js +32 -0
  247. package/dist/blocks/inbox/svelte/index.d.ts +27 -0
  248. package/dist/blocks/inbox/svelte/index.js +32 -0
  249. package/dist/blocks/inbox/vue/index.d.ts +27 -0
  250. package/dist/blocks/inbox/vue/index.js +32 -0
  251. package/dist/blocks/index.d.ts +9 -64
  252. package/dist/blocks/index.js +193 -57
  253. package/dist/blocks/inventory/index.d.ts +181 -0
  254. package/dist/blocks/inventory/index.js +219 -0
  255. package/dist/blocks/invoices/index.d.ts +154 -0
  256. package/dist/blocks/invoices/index.js +216 -0
  257. package/dist/blocks/jobs/index.d.ts +188 -49
  258. package/dist/blocks/jobs/index.js +698 -527
  259. package/dist/blocks/knowledge/index.d.ts +10 -3
  260. package/dist/blocks/knowledge/index.js +17 -3
  261. package/dist/blocks/leave/index.d.ts +241 -0
  262. package/dist/blocks/leave/index.js +209 -0
  263. package/dist/blocks/memory/index.d.ts +9 -4
  264. package/dist/blocks/memory/index.js +49 -12
  265. package/dist/blocks/metrics/index.d.ts +174 -0
  266. package/dist/blocks/metrics/index.js +217 -0
  267. package/dist/blocks/mfa/index.d.ts +7 -0
  268. package/dist/blocks/mfa/index.js +12 -0
  269. package/dist/blocks/mfa/react/index.d.ts +30 -0
  270. package/dist/blocks/mfa/react/index.js +45 -0
  271. package/dist/blocks/mfa/react/server.d.ts +9 -0
  272. package/dist/blocks/mfa/react/server.js +12 -0
  273. package/dist/blocks/mfa/solid/index.d.ts +20 -0
  274. package/dist/blocks/mfa/solid/index.js +23 -0
  275. package/dist/blocks/mfa/svelte/index.d.ts +20 -0
  276. package/dist/blocks/mfa/svelte/index.js +23 -0
  277. package/dist/blocks/mfa/vue/index.d.ts +20 -0
  278. package/dist/blocks/mfa/vue/index.js +23 -0
  279. package/dist/blocks/notifications/index.d.ts +345 -26
  280. package/dist/blocks/notifications/index.js +744 -120
  281. package/dist/blocks/notifications/react/index.d.ts +7 -32
  282. package/dist/blocks/notifications/react/index.js +23 -49
  283. package/dist/blocks/notifications/react/server.d.ts +2 -1
  284. package/dist/blocks/notifications/solid/index.d.ts +13 -0
  285. package/dist/blocks/notifications/solid/index.js +14 -0
  286. package/dist/blocks/notifications/svelte/index.d.ts +13 -0
  287. package/dist/blocks/notifications/svelte/index.js +14 -0
  288. package/dist/blocks/notifications/vue/index.d.ts +13 -0
  289. package/dist/blocks/notifications/vue/index.js +14 -0
  290. package/dist/blocks/number-sequences/index.d.ts +39 -0
  291. package/dist/blocks/number-sequences/index.js +38 -0
  292. package/dist/blocks/oauth-server/index.d.ts +228 -0
  293. package/dist/blocks/oauth-server/index.js +309 -0
  294. package/dist/blocks/onboarding/index.d.ts +8 -3
  295. package/dist/blocks/onboarding/index.js +17 -63
  296. package/dist/blocks/onboarding/react/index.d.ts +3 -12
  297. package/dist/blocks/onboarding/react/index.js +18 -60
  298. package/dist/blocks/onboarding/react/server.d.ts +2 -2
  299. package/dist/blocks/onboarding/react/server.js +2 -2
  300. package/dist/blocks/onboarding/solid/index.d.ts +13 -0
  301. package/dist/blocks/onboarding/solid/index.js +14 -0
  302. package/dist/blocks/onboarding/svelte/index.d.ts +13 -0
  303. package/dist/blocks/onboarding/svelte/index.js +14 -0
  304. package/dist/blocks/onboarding/vue/index.d.ts +13 -0
  305. package/dist/blocks/onboarding/vue/index.js +14 -0
  306. package/dist/blocks/orders/index.d.ts +264 -0
  307. package/dist/blocks/orders/index.js +589 -0
  308. package/dist/blocks/organizations/index.d.ts +31 -238
  309. package/dist/blocks/organizations/index.js +49 -399
  310. package/dist/blocks/organizations/react/index.d.ts +108 -0
  311. package/dist/blocks/organizations/react/index.js +113 -0
  312. package/dist/blocks/organizations/react/server.d.ts +11 -0
  313. package/dist/blocks/organizations/react/server.js +15 -0
  314. package/dist/blocks/organizations/solid/index.d.ts +34 -0
  315. package/dist/blocks/organizations/solid/index.js +32 -0
  316. package/dist/blocks/organizations/svelte/index.d.ts +34 -0
  317. package/dist/blocks/organizations/svelte/index.js +32 -0
  318. package/dist/blocks/organizations/vue/index.d.ts +34 -0
  319. package/dist/blocks/organizations/vue/index.js +32 -0
  320. package/dist/blocks/outbox/index.d.ts +12 -3
  321. package/dist/blocks/outbox/index.js +97 -67
  322. package/dist/blocks/parties/index.d.ts +181 -0
  323. package/dist/blocks/parties/index.js +258 -0
  324. package/dist/blocks/pipelines/index.d.ts +189 -0
  325. package/dist/blocks/pipelines/index.js +204 -0
  326. package/dist/blocks/profiles/index.d.ts +14 -9
  327. package/dist/blocks/profiles/index.js +20 -6
  328. package/dist/blocks/promotions/index.d.ts +125 -0
  329. package/dist/blocks/promotions/index.js +123 -0
  330. package/dist/blocks/push/index.d.ts +48 -28
  331. package/dist/blocks/push/index.js +45 -16
  332. package/dist/blocks/quotes/index.d.ts +152 -0
  333. package/dist/blocks/quotes/index.js +209 -0
  334. package/dist/blocks/rank/index.d.ts +71 -0
  335. package/dist/blocks/rank/index.js +53 -0
  336. package/dist/blocks/reactions/index.d.ts +18 -0
  337. package/dist/blocks/reactions/index.js +26 -0
  338. package/dist/blocks/reactions/react/index.d.ts +23 -0
  339. package/dist/blocks/reactions/react/index.js +46 -0
  340. package/dist/blocks/reactions/react/server.d.ts +6 -0
  341. package/dist/blocks/reactions/react/server.js +7 -0
  342. package/dist/blocks/reactions/solid/index.d.ts +13 -0
  343. package/dist/blocks/reactions/solid/index.js +14 -0
  344. package/dist/blocks/reactions/svelte/index.d.ts +13 -0
  345. package/dist/blocks/reactions/svelte/index.js +14 -0
  346. package/dist/blocks/reactions/vue/index.d.ts +13 -0
  347. package/dist/blocks/reactions/vue/index.js +14 -0
  348. package/dist/blocks/relations/index.d.ts +180 -0
  349. package/dist/blocks/relations/index.js +254 -0
  350. package/dist/blocks/reminders/index.d.ts +177 -0
  351. package/dist/blocks/reminders/index.js +171 -0
  352. package/dist/blocks/reviews/index.d.ts +148 -0
  353. package/dist/blocks/reviews/index.js +139 -0
  354. package/dist/blocks/rules/index.d.ts +103 -0
  355. package/dist/blocks/rules/index.js +54 -0
  356. package/dist/blocks/rules/react/index.d.ts +34 -0
  357. package/dist/blocks/rules/react/index.js +55 -0
  358. package/dist/blocks/rules/react/server.d.ts +6 -0
  359. package/dist/blocks/rules/react/server.js +7 -0
  360. package/dist/blocks/rules/solid/index.d.ts +13 -0
  361. package/dist/blocks/rules/solid/index.js +14 -0
  362. package/dist/blocks/rules/svelte/index.d.ts +13 -0
  363. package/dist/blocks/rules/svelte/index.js +14 -0
  364. package/dist/blocks/rules/vue/index.d.ts +13 -0
  365. package/dist/blocks/rules/vue/index.js +14 -0
  366. package/dist/blocks/saved-views/index.d.ts +17 -0
  367. package/dist/blocks/saved-views/index.js +29 -0
  368. package/dist/blocks/saved-views/react/index.d.ts +34 -0
  369. package/dist/blocks/saved-views/react/index.js +57 -0
  370. package/dist/blocks/saved-views/react/server.d.ts +7 -0
  371. package/dist/blocks/saved-views/react/server.js +7 -0
  372. package/dist/blocks/saved-views/solid/index.d.ts +13 -0
  373. package/dist/blocks/saved-views/solid/index.js +14 -0
  374. package/dist/blocks/saved-views/svelte/index.d.ts +13 -0
  375. package/dist/blocks/saved-views/svelte/index.js +14 -0
  376. package/dist/blocks/saved-views/vue/index.d.ts +13 -0
  377. package/dist/blocks/saved-views/vue/index.js +14 -0
  378. package/dist/blocks/search/index.d.ts +31 -0
  379. package/dist/blocks/search/index.js +61 -0
  380. package/dist/blocks/search/react/index.d.ts +36 -0
  381. package/dist/blocks/search/react/index.js +49 -0
  382. package/dist/blocks/search/react/server.d.ts +7 -0
  383. package/dist/blocks/search/react/server.js +8 -0
  384. package/dist/blocks/search/solid/index.d.ts +13 -0
  385. package/dist/blocks/search/solid/index.js +14 -0
  386. package/dist/blocks/search/svelte/index.d.ts +13 -0
  387. package/dist/blocks/search/svelte/index.js +14 -0
  388. package/dist/blocks/search/vue/index.d.ts +13 -0
  389. package/dist/blocks/search/vue/index.js +14 -0
  390. package/dist/blocks/service-principals/index.d.ts +126 -0
  391. package/dist/blocks/service-principals/index.js +107 -0
  392. package/dist/blocks/sessions/index.d.ts +7 -0
  393. package/dist/blocks/sessions/index.js +14 -0
  394. package/dist/blocks/sessions/react/index.d.ts +31 -0
  395. package/dist/blocks/sessions/react/index.js +51 -0
  396. package/dist/blocks/sessions/react/server.d.ts +9 -0
  397. package/dist/blocks/sessions/react/server.js +11 -0
  398. package/dist/blocks/sessions/solid/index.d.ts +20 -0
  399. package/dist/blocks/sessions/solid/index.js +23 -0
  400. package/dist/blocks/sessions/svelte/index.d.ts +20 -0
  401. package/dist/blocks/sessions/svelte/index.js +23 -0
  402. package/dist/blocks/sessions/vue/index.d.ts +20 -0
  403. package/dist/blocks/sessions/vue/index.js +23 -0
  404. package/dist/blocks/settings/index.d.ts +20 -86
  405. package/dist/blocks/settings/index.js +28 -101
  406. package/dist/blocks/settings/react/index.d.ts +25 -0
  407. package/dist/blocks/settings/react/index.js +37 -0
  408. package/dist/blocks/settings/react/server.d.ts +7 -0
  409. package/dist/blocks/settings/react/server.js +7 -0
  410. package/dist/blocks/settings/solid/index.d.ts +13 -0
  411. package/dist/blocks/settings/solid/index.js +14 -0
  412. package/dist/blocks/settings/svelte/index.d.ts +13 -0
  413. package/dist/blocks/settings/svelte/index.js +14 -0
  414. package/dist/blocks/settings/vue/index.d.ts +13 -0
  415. package/dist/blocks/settings/vue/index.js +14 -0
  416. package/dist/blocks/share-links/index.d.ts +99 -0
  417. package/dist/blocks/share-links/index.js +99 -0
  418. package/dist/blocks/shared-signals/index.d.ts +395 -0
  419. package/dist/blocks/shared-signals/index.js +880 -0
  420. package/dist/blocks/signing-keys/index.d.ts +29 -0
  421. package/dist/blocks/signing-keys/index.js +161 -0
  422. package/dist/blocks/sla/index.d.ts +114 -0
  423. package/dist/blocks/sla/index.js +122 -0
  424. package/dist/blocks/sso/index.d.ts +169 -147
  425. package/dist/blocks/sso/index.js +259 -321
  426. package/dist/blocks/state-machine/index.d.ts +105 -0
  427. package/dist/blocks/state-machine/index.js +116 -0
  428. package/dist/blocks/tags/index.d.ts +20 -0
  429. package/dist/blocks/tags/index.js +28 -0
  430. package/dist/blocks/tags/react/index.d.ts +28 -0
  431. package/dist/blocks/tags/react/index.js +47 -0
  432. package/dist/blocks/tags/react/server.d.ts +7 -0
  433. package/dist/blocks/tags/react/server.js +7 -0
  434. package/dist/blocks/tags/solid/index.d.ts +13 -0
  435. package/dist/blocks/tags/solid/index.js +14 -0
  436. package/dist/blocks/tags/svelte/index.d.ts +13 -0
  437. package/dist/blocks/tags/svelte/index.js +14 -0
  438. package/dist/blocks/tags/vue/index.d.ts +13 -0
  439. package/dist/blocks/tags/vue/index.js +14 -0
  440. package/dist/blocks/targets/index.d.ts +82 -0
  441. package/dist/blocks/targets/index.js +73 -0
  442. package/dist/blocks/teams/index.d.ts +61 -0
  443. package/dist/blocks/teams/index.js +83 -0
  444. package/dist/blocks/templates/index.d.ts +362 -0
  445. package/dist/blocks/templates/index.js +483 -0
  446. package/dist/blocks/time-tracking/index.d.ts +250 -0
  447. package/dist/blocks/time-tracking/index.js +317 -0
  448. package/dist/blocks/trash/index.d.ts +78 -0
  449. package/dist/blocks/trash/index.js +79 -0
  450. package/dist/blocks/undo/index.d.ts +9 -0
  451. package/dist/blocks/undo/index.js +14 -0
  452. package/dist/blocks/undo/react/index.d.ts +17 -0
  453. package/dist/blocks/undo/react/index.js +24 -0
  454. package/dist/blocks/undo/react/server.d.ts +7 -0
  455. package/dist/blocks/undo/react/server.js +7 -0
  456. package/dist/blocks/undo/solid/index.d.ts +13 -0
  457. package/dist/blocks/undo/solid/index.js +14 -0
  458. package/dist/blocks/undo/svelte/index.d.ts +13 -0
  459. package/dist/blocks/undo/svelte/index.js +14 -0
  460. package/dist/blocks/undo/vue/index.d.ts +13 -0
  461. package/dist/blocks/undo/vue/index.js +14 -0
  462. package/dist/blocks/usage/index.d.ts +10 -4
  463. package/dist/blocks/usage/index.js +16 -232
  464. package/dist/blocks/usage/react/index.d.ts +23 -0
  465. package/dist/blocks/usage/react/index.js +38 -0
  466. package/dist/blocks/usage/react/server.d.ts +7 -0
  467. package/dist/blocks/usage/react/server.js +7 -0
  468. package/dist/blocks/usage/solid/index.d.ts +13 -0
  469. package/dist/blocks/usage/solid/index.js +14 -0
  470. package/dist/blocks/usage/svelte/index.d.ts +13 -0
  471. package/dist/blocks/usage/svelte/index.js +14 -0
  472. package/dist/blocks/usage/vue/index.d.ts +13 -0
  473. package/dist/blocks/usage/vue/index.js +14 -0
  474. package/dist/blocks/versions/index.d.ts +165 -0
  475. package/dist/blocks/versions/index.js +283 -0
  476. package/dist/blocks/waitlist/index.d.ts +57 -19
  477. package/dist/blocks/waitlist/index.js +47 -12
  478. package/dist/blocks/webhooks/index.d.ts +275 -134
  479. package/dist/blocks/webhooks/index.js +775 -231
  480. package/dist/blocks/work-items/index.d.ts +208 -0
  481. package/dist/blocks/work-items/index.js +305 -0
  482. package/dist/blocks/workflow-builder/index.d.ts +26 -6
  483. package/dist/blocks/workflow-builder/index.js +30 -5
  484. package/dist/blocks/workflow-builder/react/index.d.ts +4 -19
  485. package/dist/blocks/workflow-builder/react/index.js +45 -89
  486. package/dist/blocks/workflow-builder/solid/index.d.ts +20 -0
  487. package/dist/blocks/workflow-builder/solid/index.js +23 -0
  488. package/dist/blocks/workflow-builder/svelte/index.d.ts +20 -0
  489. package/dist/blocks/workflow-builder/svelte/index.js +23 -0
  490. package/dist/blocks/workflow-builder/vue/index.d.ts +20 -0
  491. package/dist/blocks/workflow-builder/vue/index.js +23 -0
  492. package/dist/blocks/workflows/index.d.ts +20 -3
  493. package/dist/blocks/workflows/index.js +27 -3
  494. package/dist/blocks/workflows/react/index.d.ts +5 -20
  495. package/dist/blocks/workflows/react/index.js +41 -113
  496. package/dist/blocks/workflows/solid/index.d.ts +20 -0
  497. package/dist/blocks/workflows/solid/index.js +23 -0
  498. package/dist/blocks/workflows/svelte/index.d.ts +20 -0
  499. package/dist/blocks/workflows/svelte/index.js +23 -0
  500. package/dist/blocks/workflows/vue/index.d.ts +20 -0
  501. package/dist/blocks/workflows/vue/index.js +23 -0
  502. package/dist/blocks-D2WQ63PI.js +84 -0
  503. package/dist/bookings-0XnhHNLM.js +181 -0
  504. package/dist/build-Cm0T5MVG.js +1815 -0
  505. package/dist/build-Cx08LOT7.js +689 -0
  506. package/dist/calendar-DcFrpSOy.js +801 -0
  507. package/dist/casing-DdFWEtqr.js +16 -0
  508. package/dist/channel-D-LHCY11.d.ts +78 -0
  509. package/dist/channels-C-GCoNLd.js +114 -0
  510. package/dist/chat-sdk/index.d.ts +131 -7
  511. package/dist/chat-sdk/index.js +155 -6
  512. package/dist/chat-sdk/react/index.d.ts +2 -1
  513. package/dist/chat-sdk/react/index.js +1 -2
  514. package/dist/chat-sdk/react/server.d.ts +2 -1
  515. package/dist/chat-sdk/react/server.js +1 -2
  516. package/dist/claims-0KZVT804.js +61 -0
  517. package/dist/cli/add-B0zoJUYX.js +651 -0
  518. package/dist/cli/add-Bd5M6uEZ.js +75 -0
  519. package/dist/cli/add-CT6p5psO.js +1 -0
  520. package/dist/cli/adopt-C9jl8z0h.js +1 -0
  521. package/dist/cli/adopt-CebmlHL-.js +393 -0
  522. package/dist/cli/agent-CCt_aXpJ.js +176 -0
  523. package/dist/cli/apply-Dxk8AUNU.js +302 -0
  524. package/dist/cli/arazzo-1.0-BalU61-_.js +532 -0
  525. package/dist/cli/arazzo-1.1-Bnfasbun.js +705 -0
  526. package/dist/cli/asyncapi-3.0.0-C78dBjR6.js +5593 -0
  527. package/dist/cli/asyncapi-3.1.0-g-7aIfV1.js +5833 -0
  528. package/dist/cli/bin.js +41 -21
  529. package/dist/cli/blocks-Bzz326nn.js +1 -0
  530. package/dist/cli/blocks-Dq25D80b.js +124 -0
  531. package/dist/cli/cache-Cx6qlRDR.js +502 -0
  532. package/dist/cli/catalog-Di15TkM2.js +187 -0
  533. package/dist/cli/codemod-woL4cVe0.js +985 -0
  534. package/dist/cli/command-CK9Cn8HV.js +836 -0
  535. package/dist/cli/completion-BO5UK6bG.js +1 -0
  536. package/dist/cli/completion-CWoDmFZr.js +205 -0
  537. package/dist/cli/completion-J3dqnNoW.js +30 -0
  538. package/dist/cli/config-C1d-mbUA.js +1 -0
  539. package/dist/cli/config-CLHpTk9Y.js +1421 -0
  540. package/dist/cli/config-MuKY9kUj.js +39 -0
  541. package/dist/cli/config-edit-Ct1lXe8a.js +603 -0
  542. package/dist/cli/db-DT1bGB02.js +78 -0
  543. package/dist/cli/design-edit-B-WsnhUF.js +175 -0
  544. package/dist/cli/detect-DYDUBLU4.js +84 -0
  545. package/dist/cli/dist-D_VbPAMR.js +129 -0
  546. package/dist/cli/dist-DuiMMRbJ.js +1112 -0
  547. package/dist/cli/doctor-SGw36h1D.js +3806 -0
  548. package/dist/cli/emit-ZAYeh4Yn.js +1042 -0
  549. package/dist/cli/env-CVXvJoLP.js +142 -0
  550. package/dist/cli/env-D3gZ-OHj.js +1 -0
  551. package/dist/cli/env-DJ4ZaJpe.js +1 -0
  552. package/dist/cli/env-DMw_MBe5.js +996 -0
  553. package/dist/cli/errors-Caiz_0so.js +98 -0
  554. package/dist/cli/fastest-levenshtein-dhRRvrM2.js +120 -0
  555. package/dist/cli/files-DDvAwTko.js +963 -0
  556. package/dist/cli/gen-B344S1Oy.js +304 -0
  557. package/dist/cli/guards-CUlDNYZs.js +8 -0
  558. package/dist/cli/index.d.ts +297 -140
  559. package/dist/cli/index.js +13 -8
  560. package/dist/cli/init-B1dL-RZL.js +393 -0
  561. package/dist/cli/introspect-BPcFi8ow.js +51 -0
  562. package/dist/cli/io-BiPX-_J1.js +37 -0
  563. package/dist/cli/jsonc-C8M0EhHB.js +1 -0
  564. package/dist/cli/keys-jsxCNJdz.js +106 -0
  565. package/dist/cli/metadata-CxBZo4KM.js +138 -0
  566. package/dist/cli/module-migration-YFFdWC5X.js +592 -0
  567. package/dist/cli/moved-D9OT_Uyy.js +152 -0
  568. package/dist/cli/names-D6LrQnOv.js +6 -0
  569. package/dist/cli/openapi-3.0-t7xwkuRH.js +928 -0
  570. package/dist/cli/openapi-3.1-CVze7tBS.js +870 -0
  571. package/dist/cli/openapi-3.2-CFoMYM01.js +1031 -0
  572. package/dist/cli/parse-DE59XyWt.js +170 -0
  573. package/dist/cli/preset-DmcC4nWE.js +129 -0
  574. package/dist/cli/prompts-Bvg_DBpe.js +1137 -0
  575. package/dist/cli/redact-PjNswZOs.js +35 -0
  576. package/dist/cli/rolldown-runtime-kJ_i4yPo.js +26 -0
  577. package/dist/cli/run-CM_2g6KQ.js +684 -0
  578. package/dist/cli/run-DaHfUUO9.js +1539 -0
  579. package/dist/cli/scaffold-CFdDq36-.js +898 -0
  580. package/dist/cli/scaffold-Dsjr7sOT.js +1 -0
  581. package/dist/cli/seed-DldDdXWO.js +113 -0
  582. package/dist/cli/seed-DtRFysBe.js +1 -0
  583. package/dist/cli/shell-Btv_4Yhe.js +10 -0
  584. package/dist/cli/skills-v34NDb2D.js +121 -0
  585. package/dist/cli/snapshot-DAxLi4hO.js +347 -0
  586. package/dist/cli/source-args-0I9zylYy.js +47 -0
  587. package/dist/cli/spec-CaaSOYZS.js +7501 -0
  588. package/dist/cli/spec-De0cfceR.js +1 -0
  589. package/dist/cli/sql-0I1EQ3JG.js +2 -0
  590. package/dist/cli/sql-DDLWwzh9.js +326 -0
  591. package/dist/cli/stack-status-CRanl-CN.js +456 -0
  592. package/dist/cli/style-2JfIPVMQ.js +29 -0
  593. package/dist/cli/supabase-toml-DjLgBYEo.js +1153 -0
  594. package/dist/cli/sync-DFx-6t5n.js +88 -0
  595. package/dist/cli/topics-GJuIDwlY.js +30 -0
  596. package/dist/cli/trace-VbdO6bcN.js +7 -0
  597. package/dist/cli/typegen--TfvOPLh.js +494 -0
  598. package/dist/cli/typegen-version-BNBPI4Eu.js +9 -0
  599. package/dist/cli/validate-BfWNKvxy.js +7522 -0
  600. package/dist/cli/version-DNRW4DAP.js +4 -0
  601. package/dist/cli/write-BS6F3Yvz.js +180 -0
  602. package/dist/client/index.d.ts +7 -3
  603. package/dist/client/index.js +207 -4
  604. package/dist/client/native/index.d.ts +71 -4
  605. package/dist/client/native/index.js +65 -6
  606. package/dist/client-01MAuqzW.js +137 -0
  607. package/dist/client-B7QVIrJS.d.ts +263 -0
  608. package/dist/client-B9CqzgPO.js +75 -0
  609. package/dist/client-BAcILZOf.d.ts +122 -0
  610. package/dist/client-BOT2KVIp.js +128 -0
  611. package/dist/client-BUGIS__1.js +1051 -0
  612. package/dist/client-BVZtq_rQ.d.ts +34 -0
  613. package/dist/client-BYaOGCOG.js +84 -0
  614. package/dist/client-BfgwocyT.js +155 -0
  615. package/dist/client-BpS8a6Fm.js +51 -0
  616. package/dist/client-BxnLb9bz.d.ts +58 -0
  617. package/dist/client-BzCv4kKE.js +65 -0
  618. package/dist/client-C-2ajnhM.js +89 -0
  619. package/dist/client-C5mUEb2n.js +55 -0
  620. package/dist/client-C9QrWrnt.js +209 -0
  621. package/dist/client-CFdMJ1h9.d.ts +277 -0
  622. package/dist/client-CHmjaI0j.d.ts +103 -0
  623. package/dist/client-CWs3X4g1.js +120 -0
  624. package/dist/client-Cdedx_lw.js +438 -0
  625. package/dist/client-CiGjS6us.js +169 -0
  626. package/dist/client-CilSKXNu.js +288 -0
  627. package/dist/client-ClZ0D-if.d.ts +162 -0
  628. package/dist/client-Cmensu7l.d.ts +419 -0
  629. package/dist/client-Co6BPmvr.d.ts +108 -0
  630. package/dist/client-CoVGPi0c.js +133 -0
  631. package/dist/client-CvITjt-9.js +310 -0
  632. package/dist/client-CzI4QpG3.d.ts +131 -0
  633. package/dist/client-D5CZ2a34.d.ts +455 -0
  634. package/dist/client-DAHsEOVV.d.ts +155 -0
  635. package/dist/client-DG7FwgZD.js +104 -0
  636. package/dist/client-DIrCmOXz.d.ts +157 -0
  637. package/dist/client-DNq8xYb-.js +52 -0
  638. package/dist/client-DdfSIZ3M.d.ts +107 -0
  639. package/dist/client-DjTRlN3a.d.ts +68 -0
  640. package/dist/client-Ezlbskjq.js +60 -0
  641. package/dist/client-LLmgiCwJ.d.ts +135 -0
  642. package/dist/client-Lx3mlvKO.d.ts +100 -0
  643. package/dist/client-PGRk3DGz.js +98 -0
  644. package/dist/client-Tc47rlSe.d.ts +350 -0
  645. package/dist/client-VvY0K3wr.d.ts +59 -0
  646. package/dist/client-WAdEBBrH.d.ts +65 -0
  647. package/dist/client-WHeWTNVL.d.ts +130 -0
  648. package/dist/client-WgrPIHH-.js +213 -0
  649. package/dist/client-ZTtSV3l8.js +40 -0
  650. package/dist/client-b2_GjzzD.js +438 -0
  651. package/dist/client-ckXfdy1k.js +38 -0
  652. package/dist/client-e-hFkLUf.d.ts +34 -0
  653. package/dist/client-hyP_nS5R.d.ts +218 -0
  654. package/dist/client-i8glKhxh.js +67 -0
  655. package/dist/client-iDLLvGR4.d.ts +52 -0
  656. package/dist/client-pxjbgOz6.js +376 -0
  657. package/dist/client-xkSCp9hN.d.ts +101 -0
  658. package/dist/client-ysNShuJZ.d.ts +39 -0
  659. package/dist/clone-dN9XOL1K.js +29 -0
  660. package/dist/cloud-event-5rCEzIgK.d.ts +64 -0
  661. package/dist/cloud-event-CTh9Sr5w.js +60 -0
  662. package/dist/codecs-Bm46sVxr.d.ts +215 -0
  663. package/dist/codecs-YAekuQ08.js +358 -0
  664. package/dist/codegen-DeoSt1mi.d.ts +1571 -0
  665. package/dist/comments-D7sbd0xG.js +230 -0
  666. package/dist/commerce/index.d.ts +211 -0
  667. package/dist/commerce/index.js +517 -0
  668. package/dist/compiler-BVxepAY0.d.ts +14 -0
  669. package/dist/compose-Djntdacs.js +53 -0
  670. package/dist/composite-M6zPs1Xm.js +130 -0
  671. package/dist/composite-djtGy6bz.d.ts +67 -0
  672. package/dist/config/index.d.ts +7 -6
  673. package/dist/config/index.js +7 -4
  674. package/dist/config-BA1K4OPk.js +557 -0
  675. package/dist/config-C4MozTD7.js +3892 -0
  676. package/dist/connector-Cp8CqssJ.d.ts +107 -0
  677. package/dist/connectors-CjD-R2ks.d.ts +141 -0
  678. package/dist/context-1sSWcd7_.d.ts +365 -0
  679. package/dist/context-B8_p3MXo.js +11 -0
  680. package/dist/context-BDDgjoxz.js +41 -0
  681. package/dist/context-C-bHOtoD.js +5 -0
  682. package/dist/context-D6ZMKc2L.js +19 -0
  683. package/dist/context-wDKxSl9x.js +9 -0
  684. package/dist/controller-D6NyNrlZ.js +79 -0
  685. package/dist/controller-D845qKh2.d.ts +46 -0
  686. package/dist/credentials/index.d.ts +15 -5
  687. package/dist/credentials/index.js +17 -17
  688. package/dist/cron-CUaKTjWC.js +145 -0
  689. package/dist/cron-route-CoL-By8Z.js +24 -0
  690. package/dist/csv-CVfXqaAu.js +89 -0
  691. package/dist/csv-D3eKxV1T.d.ts +28 -0
  692. package/dist/define-B2ln373W.js +1896 -0
  693. package/dist/define-DxHHbNEb.d.ts +1005 -0
  694. package/dist/define-block-B6Gjs57G.js +52 -0
  695. package/dist/define-block-CLZMEFeK.d.ts +256 -0
  696. package/dist/delegation-Dt8hMJWu.js +140 -0
  697. package/dist/delivery-B1mKaOen.js +30 -0
  698. package/dist/delivery-BeHgwS3k.d.ts +69 -0
  699. package/dist/documents-CWn2mtYf.d.ts +96 -0
  700. package/dist/edge/index.d.ts +68 -13
  701. package/dist/edge/index.js +75 -43
  702. package/dist/elysia/index.d.ts +1 -1
  703. package/dist/elysia/index.js +2 -2
  704. package/dist/email-BcmbIWOX.d.ts +42 -0
  705. package/dist/embeddings-C2S14foD.d.ts +78 -0
  706. package/dist/embeddings-CVeOoufZ.js +128 -0
  707. package/dist/ended-nIdGCHxk.d.ts +70 -0
  708. package/dist/entitlements-BvU5dLpW.js +45 -0
  709. package/dist/entitlements-CbzCKalR.d.ts +53 -0
  710. package/dist/env/index.d.ts +1 -1
  711. package/dist/env/index.js +12 -5
  712. package/dist/envelope-CHYk5MXU.js +90 -0
  713. package/dist/errors-8i6tSICm.js +301 -0
  714. package/dist/errors-PIpcoe4z.js +44 -0
  715. package/dist/escape-like-AUEPn-C5.d.ts +15 -0
  716. package/dist/eve/index.d.ts +5 -5
  717. package/dist/eve/index.js +11 -17
  718. package/dist/eve-DUF70Vgi.d.ts +151 -0
  719. package/dist/event-calendar-DJXXMJ3x.d.ts +217 -0
  720. package/dist/event-map-Dy2vKHai.js +19 -0
  721. package/dist/events/index.d.ts +4 -4
  722. package/dist/events/index.js +16 -8
  723. package/dist/events-CTreRrgP.d.ts +800 -0
  724. package/dist/events-xbpHkcK9.js +82 -0
  725. package/dist/executor-BBHgjUJu.d.ts +39 -0
  726. package/dist/executor-BINiCFNp.d.ts +95 -0
  727. package/dist/executor-BXSHYyW2.js +735 -0
  728. package/dist/executor-D1kYY5BA.js +206 -0
  729. package/dist/executor-D3K3tycZ.js +147 -0
  730. package/dist/executor-DKrZr3AC.d.ts +174 -0
  731. package/dist/executor-DoE-5iM8.d.ts +272 -0
  732. package/dist/expo/index.d.ts +4 -4
  733. package/dist/expo/index.js +7 -7
  734. package/dist/expo-sqlite/index.d.ts +3 -3
  735. package/dist/expo-sqlite/index.js +2 -2
  736. package/dist/extra-CGQ2_q3S.js +10 -0
  737. package/dist/field-errors-CWXN_h_E.d.ts +113 -0
  738. package/dist/field-errors-DZVRWRkf.js +13 -0
  739. package/dist/field-formats-Cif5gr7c.js +102 -0
  740. package/dist/field-formats-DCvOQlOy.d.ts +39 -0
  741. package/dist/filter-evaluate-C0ULNfS7.js +214 -0
  742. package/dist/filter-evaluate-CjcFq0RT.d.ts +34 -0
  743. package/dist/filter-query-B3lhckKX.js +518 -0
  744. package/dist/filter-query-BKmRYrTE.d.ts +121 -0
  745. package/dist/flags-D_D1jAHB.d.ts +230 -0
  746. package/dist/flags-XAOsdNAc.js +279 -0
  747. package/dist/flags-sdk/index.d.ts +143 -0
  748. package/dist/flags-sdk/index.js +175 -0
  749. package/dist/form-definition-sagoefQr.d.ts +95 -0
  750. package/dist/format-CNwSjXUU.d.ts +70 -0
  751. package/dist/framework-DG5Bv39u.d.ts +26 -0
  752. package/dist/framework-INbjMZX1.js +51 -0
  753. package/dist/function-contract-B4c0XuTU.d.ts +21 -0
  754. package/dist/function-contract-DmkBg19r.js +5 -0
  755. package/dist/gateway-Cup00l0w.d.ts +109 -0
  756. package/dist/gateway-options-ClvvfuuU.d.ts +29 -0
  757. package/dist/gateway-options-DcLcWL5_.js +27 -0
  758. package/dist/graph-Cs28bOlK.js +360 -0
  759. package/dist/groups-CxonH3u5.js +16 -0
  760. package/dist/guards-6F2bCtQR.js +12 -0
  761. package/dist/h3/index.d.ts +7 -3
  762. package/dist/h3/index.js +3 -3
  763. package/dist/h3/v1/index.d.ts +1 -1
  764. package/dist/h3/v1/index.js +2 -2
  765. package/dist/history-GH1HJWQV.js +271 -0
  766. package/dist/hmac-DCud0myt.js +22 -0
  767. package/dist/hono/index.d.ts +33 -14
  768. package/dist/hono/index.js +58 -25
  769. package/dist/http-BF-V0CUF.js +25 -0
  770. package/dist/impersonation-CDeCzZP6.js +62 -0
  771. package/dist/impersonation-D5chOkrO.d.ts +25 -0
  772. package/dist/inbox-3ql4phyr.d.ts +400 -0
  773. package/dist/index-BIi8i6BO.d.ts +97 -0
  774. package/dist/index-BkzNUPId.d.ts +131 -0
  775. package/dist/index-BlqrGvb3.d.ts +227 -0
  776. package/dist/index-Brhm22eL.d.ts +138 -0
  777. package/dist/index-Bvw9r94Q.d.ts +245 -0
  778. package/dist/index-CETRk4YO.d.ts +82 -0
  779. package/dist/index-CrZitvSC.d.ts +258 -0
  780. package/dist/index-DfVXx4Ia.d.ts +202 -0
  781. package/dist/index-DygICr8c.d.ts +206 -0
  782. package/dist/index-Ej8XQ9w9.d.ts +58 -0
  783. package/dist/index-jhqhynmh.d.ts +58 -0
  784. package/dist/index.d.ts +90 -15
  785. package/dist/index.js +16 -14
  786. package/dist/iso4217-A8ev-CNW.js +176 -0
  787. package/dist/json-schema-CpYINXG8.js +548 -0
  788. package/dist/jws-35a8VhFV.d.ts +60 -0
  789. package/dist/jws-milwSg7E.js +191 -0
  790. package/dist/kit-D5S7KNFe.d.ts +66 -0
  791. package/dist/kit-MUK34-ZG.js +104 -0
  792. package/dist/knowledge-BXQ2-V0a.d.ts +187 -0
  793. package/dist/knowledge-CJ_Mm3ic.js +272 -0
  794. package/dist/lint/index.js +9 -9
  795. package/dist/list/index.d.ts +4 -3
  796. package/dist/list/index.js +4 -2
  797. package/dist/list-query-CBXNfJt4.js +549 -0
  798. package/dist/list-query-PvP3pPGQ.d.ts +194 -0
  799. package/dist/live-DwDreq1n.js +239 -0
  800. package/dist/mcp/index.d.ts +67 -20
  801. package/dist/mcp/index.js +220 -135
  802. package/dist/mcp/sdk/index.d.ts +38 -6
  803. package/dist/mcp/sdk/index.js +89 -7
  804. package/dist/message-B0fNJgq8.d.ts +93 -0
  805. package/dist/messages-Cx4ttLJJ.js +493 -0
  806. package/dist/metric-definition-95HFoW1V.d.ts +70 -0
  807. package/dist/metric-definition-BjxVn4R5.js +117 -0
  808. package/dist/mfa-DtflCXEt.d.ts +30 -0
  809. package/dist/model-lUafmeBQ.d.ts +311 -0
  810. package/dist/model-messages-CTu-6kBu.js +206 -0
  811. package/dist/model-messages-D4GZllOD.d.ts +34 -0
  812. package/dist/money-95Rff2vT.js +185 -0
  813. package/dist/money-DFpWzQvk.d.ts +76 -0
  814. package/dist/money-codec-C_-K5ZUU.js +41 -0
  815. package/dist/nestjs/index.d.ts +2 -2
  816. package/dist/nestjs/index.js +2 -2
  817. package/dist/next/index.d.ts +115 -55
  818. package/dist/next/index.js +208 -173
  819. package/dist/node/index.d.ts +2 -2
  820. package/dist/node/index.js +3 -3
  821. package/dist/nuxt/index.d.ts +2 -2
  822. package/dist/nuxt/index.js +3 -2
  823. package/dist/onboarding-BZvLLPaY.js +68 -0
  824. package/dist/openapi/index.d.ts +4 -2
  825. package/dist/openapi/index.js +21 -343
  826. package/dist/operations-C9-UcReN.d.ts +6 -0
  827. package/dist/operations-CsIfMED7.js +29 -0
  828. package/dist/optional-peer-Dx8TaYXw.js +29 -0
  829. package/dist/options-B0FcEKtH.js +5 -0
  830. package/dist/options-B5BU2Yg7.js +11 -0
  831. package/dist/options-B5woz53h.js +5 -0
  832. package/dist/options-B7EJaOX-.js +5 -0
  833. package/dist/options-B9q8CROT.js +9 -0
  834. package/dist/options-BA6jV6py.js +10 -0
  835. package/dist/options-BAW2ZlZ8.js +8 -0
  836. package/dist/options-BMjcVuaI.js +82 -0
  837. package/dist/options-BOOEzor9.js +5 -0
  838. package/dist/options-BQ07tTYr.js +11 -0
  839. package/dist/options-BZsoPJGX.js +5 -0
  840. package/dist/options-BesyRBnM.js +16 -0
  841. package/dist/options-Bf96aVk5.js +10 -0
  842. package/dist/options-BlOPivzq.js +8 -0
  843. package/dist/options-Blg8lAkz.js +9 -0
  844. package/dist/options-Bn4cu0gt.js +5 -0
  845. package/dist/options-BnoJOwXK.js +8 -0
  846. package/dist/options-Bp7sgKvl.js +30 -0
  847. package/dist/options-BqO9GYig.js +311 -0
  848. package/dist/options-BxcFLL1G.js +8 -0
  849. package/dist/options-C1HR4UZd.js +13 -0
  850. package/dist/options-C239TLRF.js +14 -0
  851. package/dist/options-C2HesT4A.js +9 -0
  852. package/dist/options-C3EzJATk.js +10 -0
  853. package/dist/options-C3hZ1fbC.js +10 -0
  854. package/dist/options-C6rEL6U5.js +8 -0
  855. package/dist/options-CAzVf-1T.js +8 -0
  856. package/dist/options-CGKSUFUq.js +5 -0
  857. package/dist/options-CGjGE8xS.js +12 -0
  858. package/dist/options-CK5WiFwG.js +11 -0
  859. package/dist/options-CN7PHkhU.js +8 -0
  860. package/dist/options-CTZNpb2n.js +5 -0
  861. package/dist/options-CUqQ9pm7.js +9 -0
  862. package/dist/options-CVSWgZpY.js +8 -0
  863. package/dist/options-CXFz0xxS.js +8 -0
  864. package/dist/options-CZk_fbbg.js +9 -0
  865. package/dist/options-C_pb1Nd3.js +5 -0
  866. package/dist/options-CbSOj53r.d.ts +52 -0
  867. package/dist/options-ChD15QxJ.js +9 -0
  868. package/dist/options-CkScQRJR.js +12 -0
  869. package/dist/options-CljDnQt2.js +25 -0
  870. package/dist/options-CmraJhd9.js +5 -0
  871. package/dist/options-CoX6VKGb.js +8 -0
  872. package/dist/options-CrC78Tee.js +5 -0
  873. package/dist/options-Ctac2R0Q.js +11 -0
  874. package/dist/options-CyD7OObW.js +17 -0
  875. package/dist/options-D1blDihM.js +9 -0
  876. package/dist/options-D2Gd6VfS.js +8 -0
  877. package/dist/options-D2d1fFuw.js +11 -0
  878. package/dist/options-D40CQzji.js +8 -0
  879. package/dist/options-D7f2VKzj.js +25 -0
  880. package/dist/options-DARkpk2P.js +8 -0
  881. package/dist/options-DCduTMST.js +17 -0
  882. package/dist/options-DDzggjN7.js +8 -0
  883. package/dist/options-DMR68VAC.js +5 -0
  884. package/dist/options-DNKyN5uq.js +20 -0
  885. package/dist/options-DTDxFT6J.js +13 -0
  886. package/dist/options-DWpz3V3F.js +8 -0
  887. package/dist/options-DXqcJyOP.js +10 -0
  888. package/dist/options-DYBQ973I.js +8 -0
  889. package/dist/options-Dh4wu9Ks.js +15 -0
  890. package/dist/options-Dmg1Da9P.js +25 -0
  891. package/dist/options-DsUgamiy.js +5 -0
  892. package/dist/options-EsUh744F.js +13 -0
  893. package/dist/options-Fz6xqu-b.js +5 -0
  894. package/dist/options-HSv8R6h5.js +13 -0
  895. package/dist/options-I8zVkB_y.js +8 -0
  896. package/dist/options-MHhjOoWz.js +5 -0
  897. package/dist/options-MLxMkd5B.js +11 -0
  898. package/dist/options-OJ5zmgr8.js +10 -0
  899. package/dist/options-QKP9FFcK.js +8 -0
  900. package/dist/options-SViK_2x2.js +12 -0
  901. package/dist/options-XgfTctbI.js +5 -0
  902. package/dist/options-_9arpsX9.js +11 -0
  903. package/dist/options-eBPsk4WI.js +11 -0
  904. package/dist/options-kLKILdIu.js +10 -0
  905. package/dist/options-n22NK3PL.js +8 -0
  906. package/dist/options-oeAq4Ho5.js +12 -0
  907. package/dist/options-qU8vgxhO.js +18 -0
  908. package/dist/options-vXMBEWSH.js +5 -0
  909. package/dist/options-wFnK3qWl.js +9 -0
  910. package/dist/options-xVAy3UsV.js +9 -0
  911. package/dist/organizations-Cw7om8uk.js +398 -0
  912. package/dist/orpc/index.d.ts +26 -10
  913. package/dist/orpc/index.js +66 -44
  914. package/dist/otel/index.d.ts +3 -3
  915. package/dist/otel/index.js +42 -7
  916. package/dist/outbox-BTeqjbum.d.ts +181 -0
  917. package/dist/overlay/index.d.ts +233 -0
  918. package/dist/overlay/index.js +554 -0
  919. package/dist/payments-h45bswA-.js +16 -0
  920. package/dist/pg-error-BV_X8Isq.js +26 -0
  921. package/dist/plugin-CDLJRFFg.d.ts +190 -0
  922. package/dist/plugins/actor/index.d.ts +1 -1
  923. package/dist/plugins/actor/index.js +1 -1
  924. package/dist/plugins/rules/index.d.ts +2 -2
  925. package/dist/plugins/rules/index.js +2 -2
  926. package/dist/plugins/soft-delete/index.d.ts +3 -3
  927. package/dist/plugins/soft-delete/index.js +11 -4
  928. package/dist/plugins/tenant/index.d.ts +9 -3
  929. package/dist/plugins/tenant/index.js +17 -3
  930. package/dist/plugins/timestamps/index.d.ts +1 -1
  931. package/dist/plugins/timestamps/index.js +8 -1
  932. package/dist/plugins/validation/index.d.ts +2 -2
  933. package/dist/plugins/validation/index.js +4 -4
  934. package/dist/pool-CwOCsX9e.d.ts +114 -0
  935. package/dist/pool-errors-GMjlCN0d.js +27 -0
  936. package/dist/ports-Ca63W4cW.d.ts +285 -0
  937. package/dist/ports-DAdQyjZz.d.ts +86 -0
  938. package/dist/ports-DGK3KTDH.js +133 -0
  939. package/dist/postgres/index.d.ts +28 -6
  940. package/dist/postgres/index.js +54 -22
  941. package/dist/postgrest-Cw9Bs5gm.d.ts +53 -0
  942. package/dist/powersync/index.d.ts +5 -5
  943. package/dist/powersync/index.js +4 -4
  944. package/dist/powersync/react/index.d.ts +8 -3
  945. package/dist/powersync/react/index.js +11 -3
  946. package/dist/preset/index.d.ts +352 -0
  947. package/dist/preset/index.js +564 -0
  948. package/dist/problem-CEjyhV5T.d.ts +76 -0
  949. package/dist/problem-pIiL3WbE.js +159 -0
  950. package/dist/provider-9gBq9W6k.d.ts +113 -0
  951. package/dist/provider-DKh2xFZb.js +57 -0
  952. package/dist/push-token-D6REHMMw.js +16 -0
  953. package/dist/push-token-asrmi7-6.d.ts +24 -0
  954. package/dist/query/index.d.ts +2 -2
  955. package/dist/query/index.js +1 -1
  956. package/dist/query-Ctz-qKRx.js +303 -0
  957. package/dist/range-DwpWe8e7.js +7 -0
  958. package/dist/range-p5V4rBhZ.d.ts +8 -0
  959. package/dist/rank-keys-DcPMt_Jk.js +60 -0
  960. package/dist/rank-result-DS8IoLup.js +18 -0
  961. package/dist/rank-result-Dngpt2n5.d.ts +14 -0
  962. package/dist/rate-limit--QSiFA6D.js +325 -0
  963. package/dist/rate-limit-BtbddDnE.js +49 -0
  964. package/dist/rate-limit-C88MEAOm.d.ts +41 -0
  965. package/dist/ratelimit-headers-Bsxx0Tj2.js +62 -0
  966. package/dist/react/index.d.ts +9 -9
  967. package/dist/react/index.js +4 -5
  968. package/dist/react/native/index.d.ts +1 -1
  969. package/dist/react/native/index.js +2 -2
  970. package/dist/react/server.d.ts +10 -10
  971. package/dist/react/server.js +4 -4
  972. package/dist/react/session.d.ts +2 -2
  973. package/dist/react-CKXUkwPx.js +410 -0
  974. package/dist/react-controller-Xn-IU44p.js +40 -0
  975. package/dist/react-env-Cc7Vf6eb.js +13 -0
  976. package/dist/react-router/index.d.ts +6 -6
  977. package/dist/react-router/index.js +10 -12
  978. package/dist/read-set-GEb-i0On.js +314 -0
  979. package/dist/realtime/index.d.ts +8 -4
  980. package/dist/realtime/index.js +5 -333
  981. package/dist/realtime-status-D2dw1hOE.js +12 -0
  982. package/dist/realtime-status-DcsO00Gw.d.ts +8 -0
  983. package/dist/refusal-br4Ma9Cf.js +61 -0
  984. package/dist/refusal-oD3ISM9h.d.ts +18 -0
  985. package/dist/replicas-BvIL4_jL.js +94 -0
  986. package/dist/resolve-CAR9oaKW.js +869 -0
  987. package/dist/resolve-w9EpHzuM.d.ts +266 -0
  988. package/dist/resource-B4ka8dDH.d.ts +106 -0
  989. package/dist/resource-C8r5xGqY.js +463 -0
  990. package/dist/resource-DO4gbVi5.js +91 -0
  991. package/dist/resource-execute-D0yQ-8WR.d.ts +12 -0
  992. package/dist/resource-schemas-BJ518zFH.js +79 -0
  993. package/dist/resource-types-D2vEQVXI.d.ts +141 -0
  994. package/dist/resources-BklF-rjz.d.ts +7 -0
  995. package/dist/resources-BlZdLsWQ.js +64 -0
  996. package/dist/respond-C7I8kLK_.d.ts +93 -0
  997. package/dist/respond-DdhZ9Wbn.js +95 -0
  998. package/dist/result-BlgnMB0H.js +128 -0
  999. package/dist/result-Cj3yWhjH.d.ts +298 -0
  1000. package/dist/rrule-zBh3CA1r.js +290 -0
  1001. package/dist/rules-B0-3Iosu.js +261 -0
  1002. package/dist/run-watch-CQRdpy-f.js +53 -0
  1003. package/dist/sales-lines-BAk8In-Z.js +41 -0
  1004. package/dist/sales-lines-C6gafAwg.d.ts +28 -0
  1005. package/dist/sales-shared-DAAGwQwN.d.ts +8 -0
  1006. package/dist/sales-shared-SblIgCgI.js +22 -0
  1007. package/dist/scope-DgWjuXUw.js +90 -0
  1008. package/dist/search-B6h03cAx.js +106 -0
  1009. package/dist/search-BtoWY6n_.js +9 -0
  1010. package/dist/server/index.d.ts +211 -43
  1011. package/dist/server/index.js +296 -19
  1012. package/dist/server-types-OohhIQb2.d.ts +737 -0
  1013. package/dist/session-DiWHtYrO.d.ts +95 -0
  1014. package/dist/settings-BXFn9zpS.js +90 -0
  1015. package/dist/shared-BmMcMoW9.js +89 -0
  1016. package/dist/shared-DbO3Dfms.js +14 -0
  1017. package/dist/shared-as0EG51f.js +60 -0
  1018. package/dist/sign-in-D0xN6E_l.js +360 -0
  1019. package/dist/signatures-B41ApWXA.js +145 -0
  1020. package/dist/signatures-BMdJ7neI.d.ts +43 -0
  1021. package/dist/signers-CXt_wA9S.d.ts +385 -0
  1022. package/dist/signing-keys-Bq9mMCTh.d.ts +90 -0
  1023. package/dist/solid/index.d.ts +9 -18
  1024. package/dist/solid/index.js +4 -9
  1025. package/dist/solid-controller-DLsmrczT.js +33 -0
  1026. package/dist/solid-controller-DqlvYFZa.d.ts +11 -0
  1027. package/dist/solid-start/index.d.ts +8 -12
  1028. package/dist/solid-start/index.js +10 -9
  1029. package/dist/spec/index.d.ts +154 -0
  1030. package/dist/spec/index.js +386 -0
  1031. package/dist/spec-pins-Dqpml2dZ.d.ts +84 -0
  1032. package/dist/spec-pins-S4PymRxp.js +60 -0
  1033. package/dist/sql/index.d.ts +327 -140
  1034. package/dist/sql/index.js +71245 -23282
  1035. package/dist/sql-DJrXG-3Z.js +422 -0
  1036. package/dist/sso-BDva_-4y.js +217 -0
  1037. package/dist/sso-BN0LnMqE.d.ts +131 -0
  1038. package/dist/ssr/index.d.ts +5 -6
  1039. package/dist/ssr/index.js +4 -3
  1040. package/dist/standard-B5fe2gnW.js +25 -0
  1041. package/dist/standard-C3_AAF3g.d.ts +7 -0
  1042. package/dist/stats-D6vkb89f.js +1128 -0
  1043. package/dist/storage/index.d.ts +4 -6
  1044. package/dist/storage/index.js +23 -20
  1045. package/dist/storage-api-Cx-1iWBd.d.ts +582 -0
  1046. package/dist/store-B5qYKsTh.d.ts +68 -0
  1047. package/dist/streams/index.d.ts +5 -5
  1048. package/dist/streams/index.js +6 -5
  1049. package/dist/streams/redis/index.d.ts +1 -1
  1050. package/dist/streams/redis/index.js +6 -12
  1051. package/dist/stripe/index.d.ts +16 -0
  1052. package/dist/stripe/index.js +14 -0
  1053. package/dist/stripe-NVcNOhHy.d.ts +140 -0
  1054. package/dist/stripe-XZrSmDTf.js +32 -0
  1055. package/dist/subject-readers-DiePmaYP.js +46 -0
  1056. package/dist/subject-readers-fxP5wu0s.d.ts +21 -0
  1057. package/dist/subjects-z8VWA_lF.d.ts +69 -0
  1058. package/dist/subscribers-Cq--DL1E.js +34 -0
  1059. package/dist/supabase-mcp-C6tMoEjn.d.ts +43 -0
  1060. package/dist/supabase-mcp-DomZ-amK.js +28 -0
  1061. package/dist/support-BsKkvA92.js +522 -0
  1062. package/dist/support-view-BbcZR7Io.d.ts +17 -0
  1063. package/dist/svelte/index.d.ts +8 -17
  1064. package/dist/svelte/index.js +3 -3
  1065. package/dist/svelte-controller-CDAujo-_.js +50 -0
  1066. package/dist/svelte-controller-DxxHEHxD.d.ts +10 -0
  1067. package/dist/sveltekit/index.d.ts +11 -13
  1068. package/dist/sveltekit/index.js +12 -14
  1069. package/dist/table-schema-pQlER3jj.js +283 -0
  1070. package/dist/tags-Dw8vKkcI.d.ts +30 -0
  1071. package/dist/tags-PaG0Ns_9.js +84 -0
  1072. package/dist/tanstack-db/index.d.ts +2 -2
  1073. package/dist/tanstack-db/index.js +1 -1
  1074. package/dist/tanstack-db/supabase/index.d.ts +51 -0
  1075. package/dist/tanstack-db/supabase/index.js +268 -0
  1076. package/dist/tanstack-start/index.d.ts +7 -7
  1077. package/dist/tanstack-start/index.js +9 -13
  1078. package/dist/tax-DDZUB_oc.d.ts +273 -0
  1079. package/dist/tax-D_kSCdWU.js +140 -0
  1080. package/dist/tee-Bsisfd7t.js +101 -0
  1081. package/dist/template-DdgYjIZ5.js +123 -0
  1082. package/dist/temporal-CB5Fpo5d.js +79 -0
  1083. package/dist/temporal-required-DqRzr7i0.js +20 -0
  1084. package/dist/tenant-B-fiItlJ.js +27 -0
  1085. package/dist/tenant-DdmVhsff.d.ts +17 -0
  1086. package/dist/tenant-naming-Bsj3Uvap.js +27 -0
  1087. package/dist/testing/index.d.ts +459 -125
  1088. package/dist/testing/index.js +2368 -720
  1089. package/dist/timing-BMCWgpdZ.js +92 -0
  1090. package/dist/topic-ByDz9edV.js +542 -0
  1091. package/dist/topic-cMS7UtM1.d.ts +359 -0
  1092. package/dist/ttl-cache-XzZ-Q2eX.js +29 -0
  1093. package/dist/types-6oiy6rIa.d.ts +205 -0
  1094. package/dist/types-BRTSpOIh.js +5 -0
  1095. package/dist/types-CdP0z5tS.d.ts +564 -0
  1096. package/dist/types-D0W4fnsc.d.ts +162 -0
  1097. package/dist/types-R6syp1el.js +29 -0
  1098. package/dist/types-YfwpIE-d.d.ts +357 -0
  1099. package/dist/types-pNY_UpDs.d.ts +240 -0
  1100. package/dist/types-vaoP9S9_.d.ts +635 -0
  1101. package/dist/undo-CEGIRkPA.js +44 -0
  1102. package/dist/undo-DGo0skyO.d.ts +43 -0
  1103. package/dist/usage-CdlfuS3i.js +250 -0
  1104. package/dist/usage-DM2rzczj.d.ts +202 -0
  1105. package/dist/vault-tOkPLnMB.d.ts +50 -0
  1106. package/dist/vercel-connect/index.d.ts +1 -1
  1107. package/dist/vercel-connect/index.js +6 -12
  1108. package/dist/verify-D63A2ALE.d.ts +142 -0
  1109. package/dist/verify-EQcC3Khp.js +126 -0
  1110. package/dist/version-D87ZteKy.js +4 -0
  1111. package/dist/view-DZ63nqwt.js +105 -0
  1112. package/dist/view-Dv0A949a.d.ts +90 -0
  1113. package/dist/vue/index.d.ts +9 -18
  1114. package/dist/vue/index.js +4 -17
  1115. package/dist/vue-controller-C9tFzqAc.js +32 -0
  1116. package/dist/vue-controller-HPfMTDa2.d.ts +11 -0
  1117. package/dist/watch-DDuWYv0Y.d.ts +84 -0
  1118. package/dist/watch-DT_B3CnJ.js +139 -0
  1119. package/dist/watch-topic-EFpOIIDE.d.ts +28 -0
  1120. package/dist/watch-topic-miuymrli.js +57 -0
  1121. package/dist/wire-DEY0ajW9.js +45 -0
  1122. package/dist/workflow-builder-OrglbXUh.js +681 -0
  1123. package/dist/workflow-sdk/builder/index.d.ts +46 -15
  1124. package/dist/workflow-sdk/builder/index.js +69 -21
  1125. package/dist/workflow-sdk/index.d.ts +75 -2
  1126. package/dist/workflow-sdk/index.js +83 -3
  1127. package/dist/workflow-sdk/world/index.d.ts +51 -20
  1128. package/dist/workflow-sdk/world/index.js +189 -128
  1129. package/dist/workflow-sdk-BZ6qsRlY.js +243 -0
  1130. package/dist/workflow-sdk-q8cLwW1K.d.ts +161 -0
  1131. package/dist/workflow-sdk-world-DPMdBfzn.js +2103 -0
  1132. package/dist/workflows-C-OvZpeg.js +282 -0
  1133. package/dist/workflows-DFQeXDAI.d.ts +243 -0
  1134. package/dist/yjs/index.d.ts +67 -0
  1135. package/dist/yjs/index.js +212 -0
  1136. package/package.json +837 -44
  1137. package/schemas/cli-commands-v1.json +220 -0
  1138. package/schemas/cli-result-v1.json +138 -0
  1139. package/schemas/config-v1.json +4317 -653
  1140. package/schemas/doctor-report-v1.json +27 -1
  1141. package/schemas/snapshot-v2.json +271 -0
  1142. package/schemas/spec-manifest-v1.json +95 -0
  1143. package/skills/better-supabase/SKILL.md +19 -8
  1144. package/skills/better-supabase/references/plugins.md +2 -2
  1145. package/skills/better-supabase/references/troubleshooting.md +20 -20
  1146. package/skills/better-supabase/references/ui-recipes.md +18652 -0
  1147. package/skills/better-supabase-api/SKILL.md +14 -14
  1148. package/skills/better-supabase-api/references/adapters.md +12 -2
  1149. package/skills/better-supabase-api/references/ai.md +4 -4
  1150. package/skills/better-supabase-api/references/expo.md +7 -4
  1151. package/skills/better-supabase-api/references/powersync.md +2 -1
  1152. package/skills/better-supabase-api/references/streams-credentials.md +8 -5
  1153. package/skills/better-supabase-auth/SKILL.md +18 -11
  1154. package/skills/better-supabase-auth/references/authorization-providers.md +5 -5
  1155. package/skills/better-supabase-testing/SKILL.md +2 -2
  1156. package/testing/act-cases.json +370 -0
  1157. package/dist/access-sql-B0dF-SoF.js +0 -181
  1158. package/dist/actor-CQTlc1Hh.d.ts +0 -43
  1159. package/dist/adapter-iKgjHVPx.js +0 -1190
  1160. package/dist/agents-CVHGd7It.d.ts +0 -94
  1161. package/dist/ai-cache-CBWs1W0k.js +0 -80
  1162. package/dist/ai-cache-DaDu_G5L.d.ts +0 -64
  1163. package/dist/ai-chat-7amvXjmQ.d.ts +0 -590
  1164. package/dist/ai-chat-B24UI962.js +0 -743
  1165. package/dist/ai-files-CSPNswUY.js +0 -319
  1166. package/dist/ai-tasks-Cd1HMFvB.d.ts +0 -131
  1167. package/dist/announcements-CiyOs-xP.js +0 -107
  1168. package/dist/announcements-ZRGnScWI.d.ts +0 -72
  1169. package/dist/api-keys-CIjghYjV.d.ts +0 -156
  1170. package/dist/assistant-DwdpDH1q.d.ts +0 -147
  1171. package/dist/attributes-5dYul0u0.js +0 -13
  1172. package/dist/authorization-BFCqamMs.d.ts +0 -172
  1173. package/dist/base64-D2gsioCY.js +0 -42
  1174. package/dist/before-route-CnR0C0Jr.js +0 -30
  1175. package/dist/bind-Bu_ZNa3W.d.ts +0 -62
  1176. package/dist/bind-l4kAvYSv.js +0 -97
  1177. package/dist/block-events-CrT8r7Fe.js +0 -76
  1178. package/dist/block-fields-GFkxl4dP.js +0 -49
  1179. package/dist/block-fields-SJ6fEf3o.d.ts +0 -31
  1180. package/dist/block-helpers-CKtbFcRv.d.ts +0 -37
  1181. package/dist/block-helpers-DWHxHsEQ.js +0 -190
  1182. package/dist/block-hooks-BmjMyXTa.js +0 -114
  1183. package/dist/block-hooks-plbZ0AbJ.d.ts +0 -107
  1184. package/dist/block-transport-BLr-ZgLc.js +0 -59
  1185. package/dist/block-transport-DvaoPuZ9.d.ts +0 -48
  1186. package/dist/bucket-Cl46E3jS.d.ts +0 -347
  1187. package/dist/build-BXIzE6Xv.js +0 -689
  1188. package/dist/channel-_2Mx1Oyu.d.ts +0 -70
  1189. package/dist/channels-CkgQ_X60.js +0 -114
  1190. package/dist/claims-DDBQT6BK.js +0 -61
  1191. package/dist/cli/bin.d.ts +0 -1
  1192. package/dist/cli/codemod-Bl_tQpHn.js +0 -491
  1193. package/dist/cli/command-CK14fP91.js +0 -502
  1194. package/dist/cli/config-B9HkL8nF.js +0 -2
  1195. package/dist/cli/config-DU7EJImt.js +0 -49
  1196. package/dist/cli/config-NiPxAmUF.js +0 -1077
  1197. package/dist/cli/db-DOBPt660.js +0 -52
  1198. package/dist/cli/diff-SjkO5rNr.js +0 -752
  1199. package/dist/cli/dist-DBPdlPYK.js +0 -1112
  1200. package/dist/cli/dist-DRAVaE6w.js +0 -842
  1201. package/dist/cli/dist-DreZ24Sz.js +0 -129
  1202. package/dist/cli/doctor-DJk0aeBi.js +0 -3591
  1203. package/dist/cli/env-CJLbUVPB.js +0 -108
  1204. package/dist/cli/env-qXgyMV6r.js +0 -511
  1205. package/dist/cli/errors-4v4DTtda.js +0 -56
  1206. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +0 -120
  1207. package/dist/cli/gen-CCMc39Dx.js +0 -3100
  1208. package/dist/cli/gen-DRmzPI75.js +0 -2
  1209. package/dist/cli/init-BHuEQrQK.js +0 -765
  1210. package/dist/cli/introspect-DzR2nyQb.js +0 -81
  1211. package/dist/cli/io-DI1mTdSA.js +0 -33
  1212. package/dist/cli/jsonc-BUniUpOk.js +0 -2
  1213. package/dist/cli/keys-BGCnnb-s.js +0 -85
  1214. package/dist/cli/openapi-BlRJ18s3.js +0 -66
  1215. package/dist/cli/project-DZRaBaY7.js +0 -75
  1216. package/dist/cli/prompts-BNcNfAve.js +0 -1142
  1217. package/dist/cli/prompts-BTYQGunc.js +0 -2
  1218. package/dist/cli/read-sets-CETOSFx4.js +0 -223
  1219. package/dist/cli/rolldown-runtime-BPOCksWG.js +0 -24
  1220. package/dist/cli/run-BqDPh9UH.js +0 -361
  1221. package/dist/cli/seed-DQzqT7LP.js +0 -100
  1222. package/dist/cli/skills-QQs4q68J.js +0 -138
  1223. package/dist/cli/sql-B8csQPyn.js +0 -495
  1224. package/dist/cli/sql-layout-D6cphHT3.js +0 -69
  1225. package/dist/cli/style-C0NFdJRL.js +0 -29
  1226. package/dist/cli/supabase-toml-B-bIukQF.js +0 -1095
  1227. package/dist/cli/typegen-version-DeFbtzYI.js +0 -8
  1228. package/dist/cli/version-59gcepIG.js +0 -4
  1229. package/dist/client-CI0Z2zWX.js +0 -277
  1230. package/dist/clone-BfgmLU90.js +0 -21
  1231. package/dist/cloud-event-BnmD5fb9.js +0 -46
  1232. package/dist/cloud-event-C1JCc1bH.d.ts +0 -50
  1233. package/dist/compiler-DY_wkj4R.d.ts +0 -14
  1234. package/dist/compose-BIBNgAnq.js +0 -53
  1235. package/dist/composite--yqry7uW.d.ts +0 -146
  1236. package/dist/composite-DXVUibdi.js +0 -130
  1237. package/dist/config-D44Y7PAv.d.ts +0 -984
  1238. package/dist/config-Dn7F5k9A.js +0 -202
  1239. package/dist/connector-Dv8kK0MO.d.ts +0 -107
  1240. package/dist/connectors-CrpknU0P.d.ts +0 -120
  1241. package/dist/context-BCrWe7ow.d.ts +0 -237
  1242. package/dist/cron-DVPUg5k3.js +0 -145
  1243. package/dist/csv-DCK-PT1n.js +0 -25
  1244. package/dist/define-B-TVjC25.js +0 -1915
  1245. package/dist/define-CJJkkr1-.d.ts +0 -980
  1246. package/dist/embeddings-4ZvFzjsU.js +0 -127
  1247. package/dist/embeddings-C9KGQnXt.d.ts +0 -68
  1248. package/dist/ended-XjeGQ5XW.d.ts +0 -63
  1249. package/dist/entitlements-BKSVGtrG.d.ts +0 -51
  1250. package/dist/entitlements-DRl6ZJUm.js +0 -24
  1251. package/dist/envelope-DqnYGRw7.js +0 -47
  1252. package/dist/errors-B11Duf73.js +0 -266
  1253. package/dist/errors-DLjJHPLK.js +0 -44
  1254. package/dist/escape-like-ImTK0ps_.d.ts +0 -15
  1255. package/dist/eve-gT2Fpjut.d.ts +0 -151
  1256. package/dist/events-DWDhx77e.d.ts +0 -549
  1257. package/dist/executor-BVBFM86G.d.ts +0 -36
  1258. package/dist/executor-Bq28caBo.js +0 -718
  1259. package/dist/executor-C-R4pucm.js +0 -83
  1260. package/dist/executor-CaWigTyR.d.ts +0 -148
  1261. package/dist/executor-DbvExCFC.d.ts +0 -30
  1262. package/dist/executor-De1Of3m3.js +0 -523
  1263. package/dist/executor-DvsBbNBS.d.ts +0 -261
  1264. package/dist/field-errors-BFOa7WhB.d.ts +0 -99
  1265. package/dist/field-errors-VF1yb8yI.js +0 -13
  1266. package/dist/framework-BmzgK32X.d.ts +0 -25
  1267. package/dist/framework-a7aRCIqv.js +0 -51
  1268. package/dist/gateway-C1ssRHnf.d.ts +0 -130
  1269. package/dist/graph-BGyLLFLX.js +0 -265
  1270. package/dist/guards-BnzDvFrz.js +0 -10
  1271. package/dist/history-BZih6SFP.js +0 -64
  1272. package/dist/hooks-1BTCJsiw.js +0 -309
  1273. package/dist/impersonation-DcqaTBHx.js +0 -148
  1274. package/dist/inbox-i7E1ppo6.d.ts +0 -384
  1275. package/dist/index-ACiNPN4Z.d.ts +0 -74
  1276. package/dist/index-Bepi-Hgg.d.ts +0 -206
  1277. package/dist/index-BkWT_uBO.d.ts +0 -51
  1278. package/dist/index-Bxih3l0u.d.ts +0 -81
  1279. package/dist/index-CLg87OBW.d.ts +0 -76
  1280. package/dist/index-CaxrMD1A.d.ts +0 -1
  1281. package/dist/index-CdZKQF98.d.ts +0 -138
  1282. package/dist/index-CewnYSpv.d.ts +0 -267
  1283. package/dist/index-ClmjK_o-.d.ts +0 -55
  1284. package/dist/index-D48jVS-N.d.ts +0 -246
  1285. package/dist/index-DQh4VCc9.d.ts +0 -98
  1286. package/dist/index-DunNRdnT.d.ts +0 -300
  1287. package/dist/json-schema-CvER4kgT.js +0 -502
  1288. package/dist/keys-4MnkWTTh.js +0 -35
  1289. package/dist/kit-CkNzZUqv.d.ts +0 -60
  1290. package/dist/kit-Fm-Fv7q5.js +0 -78
  1291. package/dist/knowledge-BXBbT0Tr.d.ts +0 -176
  1292. package/dist/knowledge-CDmiMLew.js +0 -255
  1293. package/dist/list-query-BRt-zDF7.d.ts +0 -164
  1294. package/dist/list-query-gl808ac1.js +0 -459
  1295. package/dist/live-CTAZQkwl.js +0 -257
  1296. package/dist/message-CaKlqdsX.js +0 -219
  1297. package/dist/message-CfDoR0Y6.d.ts +0 -94
  1298. package/dist/messages-B9-5887d.js +0 -715
  1299. package/dist/messages-DYP0sUTe.d.ts +0 -59
  1300. package/dist/mfa-Daak8ukG.d.ts +0 -54
  1301. package/dist/modules-DpPwzC7E.d.ts +0 -192
  1302. package/dist/modules-dxrVK3ID.js +0 -228
  1303. package/dist/onboarding-BZGHYJks.d.ts +0 -62
  1304. package/dist/outbox-Cq7pPYvl.d.ts +0 -161
  1305. package/dist/path-Cv__-OTj.d.ts +0 -17
  1306. package/dist/plugin-FGY8UhYm.d.ts +0 -151
  1307. package/dist/pool-CAu2nLrL.d.ts +0 -89
  1308. package/dist/postgrest-BLVdEnfe.d.ts +0 -53
  1309. package/dist/problem-BreOl8b8.js +0 -115
  1310. package/dist/problem-DD8LZFjc.d.ts +0 -67
  1311. package/dist/provider-D-UEMvSD.d.ts +0 -113
  1312. package/dist/provider-Di3sRutW.js +0 -57
  1313. package/dist/query-BQ-NaZon.js +0 -303
  1314. package/dist/queue-CLJurhdy.d.ts +0 -427
  1315. package/dist/react-F80mF4-t.js +0 -409
  1316. package/dist/react-topic-seAWjL3_.js +0 -38
  1317. package/dist/read-set-DuKWC9Mj.js +0 -314
  1318. package/dist/refusal-CeJWxmkH.js +0 -61
  1319. package/dist/refusal-DhSU2Vvr.d.ts +0 -15
  1320. package/dist/replicas-CwrYp5Zj.js +0 -94
  1321. package/dist/resolve-ChTYeTEl.d.ts +0 -169
  1322. package/dist/resolve-tJZY3FEa.js +0 -697
  1323. package/dist/resource-BsvzCMlX.js +0 -212
  1324. package/dist/resource-IPS5xi9q.d.ts +0 -74
  1325. package/dist/respond-BUgsXNgk.js +0 -91
  1326. package/dist/respond-DprrgyXX.d.ts +0 -93
  1327. package/dist/result-D7y2fT87.d.ts +0 -282
  1328. package/dist/result-DzUbqgdW.js +0 -128
  1329. package/dist/scope-CDHcS7dE.js +0 -90
  1330. package/dist/search-CUWCRYKL.js +0 -9
  1331. package/dist/server-Bh0EVSwU.d.ts +0 -558
  1332. package/dist/session-ChiXDZdZ.d.ts +0 -72
  1333. package/dist/shared-CBxosKK3.js +0 -75
  1334. package/dist/shared-CxWmcyqB.js +0 -73
  1335. package/dist/sign-in-Cb1mwv6X.js +0 -86
  1336. package/dist/signers-4y62_VIi.d.ts +0 -166
  1337. package/dist/spec-pins--j6mTok4.js +0 -27
  1338. package/dist/spec-pins-C89s6DyW.d.ts +0 -27
  1339. package/dist/standard-BK0cqJ7y.js +0 -25
  1340. package/dist/standard-CYsm7xpT.d.ts +0 -7
  1341. package/dist/stats-D_AD9GGu.js +0 -1123
  1342. package/dist/store-CGjgWu5n.d.ts +0 -68
  1343. package/dist/stripe-BMj5LtsT.js +0 -38
  1344. package/dist/stripe-D04h-QBT.d.ts +0 -136
  1345. package/dist/support-ig_K4PvS.js +0 -314
  1346. package/dist/support-view-CVOJJLpc.d.ts +0 -17
  1347. package/dist/tags-Cx-LnIQP.d.ts +0 -30
  1348. package/dist/tee-CoEppCER.js +0 -101
  1349. package/dist/template--jDkl3UP.js +0 -114
  1350. package/dist/template-CwO38Do8.d.ts +0 -6
  1351. package/dist/temporal-BJGitm-h.js +0 -79
  1352. package/dist/temporal-required-BVvW7SYJ.js +0 -20
  1353. package/dist/tenant-BxNFk9bx.d.ts +0 -17
  1354. package/dist/tenant-I1TEZ_BX.js +0 -27
  1355. package/dist/timing-CyTQPubY.js +0 -92
  1356. package/dist/types-6BdhkmZe.d.ts +0 -340
  1357. package/dist/types-6cYK9pbJ.js +0 -29
  1358. package/dist/types-6rocye-y.d.ts +0 -156
  1359. package/dist/types-AYOvcyep.d.ts +0 -359
  1360. package/dist/types-C05bxW-W.d.ts +0 -220
  1361. package/dist/types-DGs5L0VN.d.ts +0 -192
  1362. package/dist/usage-CrvrL2vw.d.ts +0 -177
  1363. package/dist/vault-wlbBhqm7.d.ts +0 -50
  1364. package/dist/verify-DL41hbvY.js +0 -232
  1365. package/dist/verify-DPuWngD4.d.ts +0 -172
  1366. package/dist/version-C6OfRAIn.js +0 -4
  1367. package/dist/view-BeFcRV6G.js +0 -99
  1368. package/dist/view-CF7ihnkg.d.ts +0 -87
  1369. package/dist/watch-DI34G86j.d.ts +0 -60
  1370. package/dist/watch-Xuxrvxq-.js +0 -122
  1371. package/dist/wire-DtZQ4sdE.js +0 -45
  1372. package/dist/workflow-builder-DJ1oHn8g.js +0 -398
  1373. package/dist/workflow-sdk-B3yLD6Br.d.ts +0 -98
  1374. package/dist/workflow-sdk-C2WJ_G2O.js +0 -173
  1375. package/dist/workflow-sdk-world-D-prir6O.js +0 -1397
  1376. package/dist/workflows-B0j9WKPu.d.ts +0 -185
  1377. package/dist/workflows-wu1WWBn7.js +0 -216
@@ -1,20 +1,26 @@
1
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";
7
- import { a as toBase64Url, r as fromBase64, t as base64ToText } from "../base64-D2gsioCY.js";
8
- import { t as isList } from "../guards-BnzDvFrz.js";
9
- import { a as resolveConfig } from "../modules-dxrVK3ID.js";
2
+ import { n as dbError } from "../errors-8i6tSICm.js";
3
+ import { d as temporalText } from "../temporal-CB5Fpo5d.js";
4
+ import { t as temporal } from "../temporal-required-DqRzr7i0.js";
5
+ import { a as ok } from "../result-BlgnMB0H.js";
6
+ import { t as BetterSupabase } from "../define-B2ln373W.js";
7
+ import { t as isList } from "../guards-6F2bCtQR.js";
8
+ import { a as toBase64Url, r as fromBase64, t as base64ToText } from "../base64-BmeCBgGo.js";
9
+ import { o as resolveConfig } from "../config-BA1K4OPk.js";
10
10
  import { ENV_VARIABLES } from "../env/index.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
+ import { t as defineController } from "../controller-D6NyNrlZ.js";
12
+ import "../replicas-BvIL4_jL.js";
13
+ import { a as tenantCredentialRef } from "../provider-DKh2xFZb.js";
14
+ import { n as rawError } from "../block-transport-G4t1umrR.js";
15
+ import { t as postgresExecutor } from "../executor-D1kYY5BA.js";
16
+ import { i as requestOf, n as checkPermission, r as defineAuthorizer } from "../authorizer-DH7DjkL3.js";
17
+ import { n as parseDbStats } from "../timing-BMCWgpdZ.js";
18
+ import { t as mailHeaders } from "../delivery-B1mKaOen.js";
17
19
  import { toCloudEvents } from "../events/index.js";
20
+ import { d as money, l as isRate } from "../money-95Rff2vT.js";
21
+ import { t as SHIPPING_LINE } from "../tax-D_kSCdWU.js";
22
+ import { a as isScopeName, c as templateFunctions, l as templatePlaceholders, n as TEMPLATE_PLACEHOLDERS, s as scopeProblems, u as templateProblems } from "../access-sql-DUSSMaC3.js";
23
+ import { t as buildApiModel } from "../build-Cm0T5MVG.js";
18
24
  import { timestamps } from "../plugins/timestamps/index.js";
19
25
  import { softDelete } from "../plugins/soft-delete/index.js";
20
26
  import { tenant } from "../plugins/tenant/index.js";
@@ -149,11 +155,11 @@ async function findSigningKeys(from) {
149
155
  const supabase = join(dir, "supabase");
150
156
  const toml = join(supabase, "config.toml");
151
157
  if (existsSync(toml)) {
152
- const configured = SIGNING_KEYS_PATH.exec(await readFile(toml, "utf8"))?.[1];
158
+ const configured = SIGNING_KEYS_PATH.exec(await readFile(toml, "utf-8"))?.[1];
153
159
  const path = resolve(supabase, configured ?? "signing_keys.json");
154
160
  if (existsSync(path)) return path;
155
161
  }
156
- if (dirname(dir) === dir) return void 0;
162
+ if (dirname(dir) === dir) return;
157
163
  }
158
164
  }
159
165
  const isSigningKey = (value) => typeof value === "object" && value !== null && "kid" in value && typeof value.kid === "string" && "d" in value && typeof value.d === "string";
@@ -165,7 +171,7 @@ const isSigningKey = (value) => typeof value === "object" && value !== null && "
165
171
  async function localSigningKey(path) {
166
172
  const file = path ?? process.env["SUPABASE_SIGNING_KEYS_PATH"] ?? await findSigningKeys(process.cwd());
167
173
  if (!file || !existsSync(file)) throw new TypeError("No local signing key. Run `better-supabase keys`, set `signing_keys_path` in supabase/config.toml and restart the stack, or pass { alg: \"HS256\" } to sign with the local JWT secret.");
168
- const keys = JSON.parse(await readFile(file, "utf8"));
174
+ const keys = JSON.parse(await readFile(file, "utf-8"));
169
175
  const first = Array.isArray(keys) ? keys[0] : void 0;
170
176
  if (!isSigningKey(first)) throw new TypeError(`${file} has no private ES256 key first.`);
171
177
  return first;
@@ -179,7 +185,7 @@ async function signLocalJwt(claims, path) {
179
185
  /** The local CLI stack's default JWT secret. */
180
186
  const LOCAL_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long";
181
187
  function env$1(...names) {
182
- if (typeof process === "undefined") return void 0;
188
+ if (typeof process === "undefined") return;
183
189
  for (const name of names) {
184
190
  const value = process.env[name];
185
191
  if (value) return value;
@@ -230,7 +236,10 @@ async function asUser(betterSupabase, claims, stack = {}) {
230
236
  },
231
237
  claims: full
232
238
  };
233
- const supabase = createClient(url, publishableKey, { accessToken: () => Promise.resolve(token) });
239
+ const supabase = createClient(url, publishableKey, {
240
+ accessToken: () => Promise.resolve(token),
241
+ ...stack.fetch ? { global: { fetch: stack.fetch } } : {}
242
+ });
234
243
  return {
235
244
  token,
236
245
  claims: full,
@@ -240,6 +249,141 @@ async function asUser(betterSupabase, claims, stack = {}) {
240
249
  };
241
250
  }
242
251
  //#endregion
252
+ //#region src/lite/runtime.ts
253
+ const LITE_INSTALL = "Supabase Lite is an optional peer: install it with `pnpm add -D @supabase/lite`";
254
+ /**
255
+ * `@supabase/lite` is an optional peer: variable specifiers keep bundlers
256
+ * from resolving it, and a missing install returns `undefined` so callers
257
+ * can say how to add it.
258
+ */
259
+ async function loadLite() {
260
+ const root = "@supabase/lite";
261
+ const pglite = "@supabase/lite/pglite";
262
+ const sqlite = "@supabase/lite/sqlite";
263
+ try {
264
+ const [{ App }, pg, sq] = await Promise.all([
265
+ import(root),
266
+ import(pglite),
267
+ import(sqlite)
268
+ ]);
269
+ return {
270
+ App,
271
+ createPgliteConnection: () => pg.createPgliteConnection(),
272
+ createSqliteConnection: (url) => sq.createConnection({ url })
273
+ };
274
+ } catch {
275
+ return;
276
+ }
277
+ }
278
+ /** Applies migrations, then declarative schemas, with Lite's migrator. */
279
+ async function migrateLite(connection, sql) {
280
+ const ddl = sql.filter((part) => part.trim() !== "").join("\n;\n");
281
+ if (ddl !== "") await connection.createMigrator(ddl).migrate();
282
+ }
283
+ /** Runs seed SQL: as is on PGlite, translated to SQLite on `sqlite-postgres`. */
284
+ async function seedLite(target, sql) {
285
+ if (sql.trim() === "") return;
286
+ if (target.driver === "pglite") {
287
+ await target.connection.driver.exec(sql);
288
+ return;
289
+ }
290
+ const { connection } = target;
291
+ const translated = await connection.translateDdl(sql);
292
+ const statements = typeof translated === "string" ? translated : typeof translated === "object" && translated !== null && "ddl" in translated && typeof translated.ddl === "string" ? translated.ddl : "";
293
+ if (statements !== "") await connection.transaction([statements]);
294
+ }
295
+ //#endregion
296
+ //#region src/testing/lite.ts
297
+ const LITE_URL = "http://localhost:54321";
298
+ function randomToken(bytes) {
299
+ return toBase64Url(crypto.getRandomValues(new Uint8Array(bytes)));
300
+ }
301
+ function list(value) {
302
+ if (value === void 0) return [];
303
+ return typeof value === "string" ? [value] : [...value];
304
+ }
305
+ /**
306
+ * Supabase Lite in this process, for tests without Docker: an in-memory
307
+ * database with your schema, Auth with fresh API keys, and supabase-js
308
+ * clients whose requests go straight to Lite's `fetch` handler (no port).
309
+ *
310
+ * ```ts
311
+ * await using stack = await liteStack({ schemas: [await readFile("supabase/schemas/notes.sql", "utf8")] });
312
+ * const alice = await stack.asUser(betterSupabase, { sub: aliceId });
313
+ * expect(await alice.db.notes.count().orThrow()).toBe(1);
314
+ * ```
315
+ */
316
+ async function liteStack(options = {}) {
317
+ const lite = await loadLite();
318
+ if (!lite) throw new Error(`liteStack() needs @supabase/lite. ${LITE_INSTALL}`);
319
+ const driver = options.driver ?? "sqlite-postgres";
320
+ const jwtSecret = options.jwtSecret ?? randomToken(32);
321
+ const publishableKey = `sb_publishable_${randomToken(16)}`;
322
+ const secretKey = `sb_secret_${randomToken(16)}`;
323
+ let target;
324
+ if (driver === "pglite") target = {
325
+ driver,
326
+ connection: await lite.createPgliteConnection()
327
+ };
328
+ else target = {
329
+ driver,
330
+ connection: await lite.createSqliteConnection(":memory:")
331
+ };
332
+ const { connection } = target;
333
+ try {
334
+ const app = new lite.App({
335
+ connection,
336
+ auth: {
337
+ enabled: true,
338
+ jwt_secret: jwtSecret,
339
+ publishable_key: publishableKey,
340
+ secret_key: secretKey
341
+ }
342
+ });
343
+ await app.init();
344
+ await migrateLite(connection, [...options.migrations ?? [], ...options.schemas ?? []]);
345
+ for (const seed of list(options.seed)) await seedLite(target, seed);
346
+ const fetch = (input, init) => app.fetch(new Request(input, init));
347
+ const stateless = {
348
+ auth: {
349
+ persistSession: false,
350
+ autoRefreshToken: false
351
+ },
352
+ global: { fetch }
353
+ };
354
+ const close = () => connection.close();
355
+ return {
356
+ url: LITE_URL,
357
+ publishableKey,
358
+ secretKey,
359
+ jwtSecret,
360
+ fetch,
361
+ env: {
362
+ url: LITE_URL,
363
+ publishableKey,
364
+ secretKey,
365
+ secretKeys: { default: secretKey },
366
+ jwksUrl: new URL(`${LITE_URL}/auth/v1/.well-known/jwks.json`),
367
+ jwtSecret
368
+ },
369
+ supabase: createClient(LITE_URL, publishableKey, stateless),
370
+ admin: createClient(LITE_URL, secretKey, stateless),
371
+ asUser: (betterSupabase, claims) => asUser(betterSupabase, claims, {
372
+ url: LITE_URL,
373
+ publishableKey,
374
+ alg: "HS256",
375
+ jwtSecret,
376
+ fetch
377
+ }),
378
+ close,
379
+ [Symbol.asyncDispose]: close
380
+ };
381
+ } catch (error) {
382
+ await connection.close();
383
+ throw error;
384
+ }
385
+ }
386
+ //#endregion
243
387
  //#region src/testing/budget.ts
244
388
  function isPrefetch(response) {
245
389
  const headers = response.request().headers();
@@ -449,395 +593,684 @@ function deepFreeze(value) {
449
593
  }
450
594
  const frozenCopy = (value) => deepFreeze(structuredClone(value));
451
595
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
452
- const recorder = (ops) => ({
453
- name: "recorder",
454
- execute: (op) => {
455
- ops.push(op);
456
- return Promise.resolve(ok({
457
- rows: [],
458
- count: 0
459
- }));
460
- }
461
- });
462
- /** The operation `run` sends to the executor, built by `betterSupabase` without touching a database. */
463
- async function captureOp(betterSupabase, run) {
464
- const ops = [];
465
- await run(betterSupabase.connect(recorder(ops)));
466
- const [op] = ops;
467
- if (!op) throw new Error("the repository call did not reach the executor");
468
- return op;
596
+ //#endregion
597
+ //#region src/testing/isolation.ts
598
+ const scalar = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
599
+ function keyOf(table, row) {
600
+ return Object.fromEntries(table.primaryKey.map((column) => [column, row[column]]));
469
601
  }
470
- function tableKey(betterSupabase, table) {
471
- const key = table ?? Object.keys(betterSupabase.meta.tables)[0];
472
- if (!key || !(key in betterSupabase.meta.tables)) throw new TypeError(`better-supabase: unknown table "${String(key)}"`);
473
- return key;
602
+ async function rowsOf(outcome) {
603
+ const result = await outcome;
604
+ if (!result.ok) throw new Error(result.error.message);
605
+ return Array.isArray(result.data) ? result.data : [result.data];
606
+ }
607
+ function env(names) {
608
+ if (typeof process === "undefined") return;
609
+ for (const name of names) {
610
+ const value = process.env[name];
611
+ if (value) return value;
612
+ }
474
613
  }
475
- const hasName = (subject) => ["has a name", () => {
476
- expect(typeof subject.name === "string" && subject.name.length > 0, "name must be a non-empty string");
477
- }];
478
614
  /**
479
- * Proves an `Executor` meets the contract: rows keyed by the selection's
480
- * aliases, counts, `aborted` errors, failures returned as `Result`s rather
481
- * than thrown, and (with `create`) a write round trip and a `deleteMany`
482
- * that `maxAffected` refuses without deleting anything.
615
+ * Checks row-level security between two tenants against a running stack: for
616
+ * each table, a user of one tenant must not select, insert, update or delete
617
+ * the other tenant's rows. Runs without `betterSupabase`'s plugins, so the `tenant()`
618
+ * plugin can't hide a missing policy. Throws a `ConformanceError` naming each
619
+ * table and command that leaks; removes every row it created.
483
620
  *
484
621
  * ```ts
485
- * it('conforms', () => testExecutor(myExecutor, { betterSupabase, table: 'tags', create: { name: 'x' } }));
622
+ * await expectTenantIsolation(betterSupabase, {
623
+ * tenants: [
624
+ * { id: ACME, claims: { sub: alice, tenant_id: ACME } },
625
+ * { id: GLOBEX, claims: { sub: bob, tenant_id: GLOBEX } },
626
+ * ],
627
+ * tables: {
628
+ * tags: {
629
+ * row: (tenant, n) => ({ organizationId: tenant.id, name: `iso-${n}` }),
630
+ * update: { name: 'changed' },
631
+ * },
632
+ * },
633
+ * });
486
634
  * ```
487
635
  */
488
- function testExecutor(executor, options) {
489
- const { betterSupabase } = options;
490
- const table = tableKey(betterSupabase, options.table);
491
- const context = { errorMappers: [] };
492
- const read = () => captureOp(betterSupabase, (db) => db[table]["findMany"]({ limit: 2 }));
493
- const create = options.create;
494
- return conform(`Executor "${executor.name}"`, [
495
- hasName(executor),
496
- ["reads rows keyed by the selection aliases", async () => {
497
- const op = await read();
498
- const result = await executor.execute(op, context);
499
- expect(result.ok, `select failed: ${result.ok ? "" : result.error.message}`);
500
- expect(isList(result.data.rows), "rows must be an array");
501
- expect(result.data.rows.length <= 2, `limit 2 returned ${result.data.rows.length} rows`);
502
- expect(result.data.count === null || typeof result.data.count === "number", "count must be a number or null");
503
- if (op.kind !== "select") return;
504
- const aliases = new Set(op.selection.columns.map((column) => column.alias));
505
- for (const row of result.data.rows) {
506
- const extra = Object.keys(row).filter((key) => !aliases.has(key));
507
- expect(extra.length === 0, `rows must use selection aliases; got ${extra.join(", ")}`);
508
- }
509
- }],
510
- ["counts rows", async () => {
511
- const op = await captureOp(betterSupabase, (db) => db[table]["count"]());
512
- const result = await executor.execute(op, context);
513
- expect(result.ok, `count failed: ${result.ok ? "" : result.error.message}`);
514
- expect(typeof result.data.count === "number" && result.data.count >= 0, "count must be a number");
515
- }],
516
- ["returns an aborted error for an aborted signal", async () => {
517
- const controller = new AbortController();
518
- controller.abort();
519
- const result = await executor.execute(await read(), {
520
- ...context,
521
- signal: controller.signal
522
- });
523
- expect(!result.ok, "an aborted request must fail");
524
- expect(result.error.kind === "aborted", `expected kind "aborted", got "${result.error.kind}"`);
525
- }],
526
- ["returns failures as results instead of throwing", async () => {
527
- const op = await read();
528
- const missing = {
529
- ...op,
530
- table: {
531
- ...op.table,
532
- name: "__better_supabase_missing__"
533
- }
534
- };
535
- const result = await executor.execute(missing, context);
536
- expect(!result.ok, "a missing table must fail");
537
- expect(typeof result.error.kind === "string" && typeof result.error.message === "string", "errors must be DbErrors");
538
- }],
539
- executor.rpc && ["returns rpc failures as results", async () => {
540
- expect(!(await executor.rpc("__better_supabase_missing__", {}, {
541
- ...context,
542
- schema: "public"
543
- })).ok, "calling a missing function must fail");
544
- }],
545
- executor.functionSources && ["reads from SelectOp.source instead of the table", async () => {
546
- const op = await read();
547
- if (op.kind !== "select") return;
548
- expect(!(await executor.execute({
549
- ...op,
550
- source: {
551
- schema: op.table.schema,
552
- name: "__better_supabase_missing__",
553
- args: {}
554
- }
555
- }, context)).ok, "a missing source function must fail; reading the table ignores source");
556
- }],
557
- executor.batch && ["batch returns one result per operation, in order", async () => {
558
- const rows = await read();
559
- const count = await captureOp(betterSupabase, (db) => db[table]["count"]());
560
- const missing = {
561
- ...rows,
562
- table: {
563
- ...rows.table,
564
- name: "__better_supabase_missing__"
565
- }
566
- };
567
- const results = await executor.batch([
568
- rows,
569
- count,
570
- missing
571
- ], context);
572
- expect(results.length === 3, `expected 3 results, got ${results.length}`);
573
- const [first, second, third] = results;
574
- expect(first?.ok === true && Array.isArray(first.data.rows), "the first operation must return rows");
575
- expect(second?.ok === true && typeof second.data.count === "number", "the second operation must return a count");
576
- expect(third?.ok === false, "a failing operation must fail on its own, as a result");
577
- }],
578
- create && ["round-trips a write", async () => {
579
- const repository = betterSupabase.connect(executor)[table];
580
- const created = await repository["create"](create);
581
- expect(created.ok, `create failed: ${JSON.stringify(created.error)}`);
582
- const row = created.data;
583
- for (const [key, value] of Object.entries(create)) expect(same(row[key], value), `created row has ${key} = ${JSON.stringify(row[key])}`);
584
- const [primary] = betterSupabase.meta.tables[table].primaryKey;
585
- expect(primary, "write checks need a table with a primary key");
586
- const removed = await repository["delete"](row[primary]);
587
- expect(removed.ok, `delete failed: ${JSON.stringify(removed.error)}`);
588
- const again = await repository["findFirst"]({ where: { [primary]: row[primary] } });
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);
636
+ async function expectTenantIsolation(betterSupabase, options) {
637
+ const stack = options.stack ?? {};
638
+ const bare = new BetterSupabase(betterSupabase.schema);
639
+ const url = stack.url ?? env(ENV_VARIABLES.url) ?? "http://127.0.0.1:54321";
640
+ const secretKey = stack.secretKey ?? env(ENV_VARIABLES.secretKey);
641
+ if (!secretKey) throw new TypeError("expectTenantIsolation needs stack.secretKey or $SUPABASE_SECRET_KEY to seed rows.");
642
+ const admin = bare.connect(createClient(url, secretKey, { auth: {
643
+ persistSession: false,
644
+ autoRefreshToken: false
645
+ } }));
646
+ await options.seed?.();
647
+ const tenants = options.tenants;
648
+ const users = await Promise.all(tenants.map(async (tenant) => (await asUser(bare, tenant.claims, stack)).db));
649
+ const label = (tenant) => tenant.name ?? tenant.id;
650
+ const cleanup = [];
651
+ const reads = [];
652
+ const deletes = [];
653
+ try {
654
+ for (const [key, spec] of Object.entries(options.tables)) {
655
+ const table = bare.meta.tables[key];
656
+ if (!table || table.primaryKey.length === 0) throw new TypeError(`expectTenantIsolation: "${key}" is not a table with a primary key`);
657
+ const seeded = [];
658
+ for (const tenant of tenants) {
659
+ const [row] = await rowsOf(admin[key].create(spec.row(tenant, 0)));
660
+ const where = keyOf(table, row);
661
+ cleanup.push(() => admin[key].deleteMany({ where }));
662
+ seeded.push(row);
615
663
  }
616
- }]
617
- ]);
618
- }
619
- /**
620
- * Proves a `CacheAdapter` accepts table and row targets, repeated and
621
- * concurrent calls, and never mutates its input.
622
- */
623
- function testCacheAdapter(adapter, options = {}) {
624
- const table = options.table ?? "customers";
625
- const invalidate = (target) => Promise.resolve(adapter.invalidate(deepFreeze(target)));
626
- return conform(`CacheAdapter "${adapter.name}"`, [
627
- hasName(adapter),
628
- ["invalidates a table", () => invalidate({
629
- table,
630
- tables: [table],
631
- ids: []
632
- })],
633
- ["invalidates rows of a tenant", () => invalidate({
634
- table,
635
- tables: [table],
636
- ids: ["1", "a,b"],
637
- tenant: "organization_1"
638
- })],
639
- ["accepts repeated and concurrent calls", async () => {
640
- await invalidate({
641
- table,
642
- tables: [table],
643
- ids: ["1"]
644
- });
645
- await Promise.all(Array.from({ length: 10 }, (_, index) => invalidate({
646
- table,
647
- tables: [table],
648
- ids: [String(index)]
649
- })));
650
- }],
651
- ["accepts tables it has never seen", () => invalidate({
652
- table: "__better_supabase_unknown__",
653
- tables: ["__better_supabase_unknown__"],
654
- ids: []
655
- })],
656
- ["invalidates tables reached through foreign keys", () => invalidate({
657
- table,
658
- tables: [table, `${table}_children`],
659
- ids: ["1"]
660
- })]
661
- ]);
664
+ for (const [self, other] of [[0, 1], [1, 0]]) {
665
+ const user = users[self][key];
666
+ const who = label(tenants[self]);
667
+ const whose = `${label(tenants[other])}'s`;
668
+ const own = keyOf(table, seeded[self]);
669
+ const target = keyOf(table, seeded[other]);
670
+ const current = async () => (await rowsOf(admin[key].findMany({ where: target })))[0];
671
+ reads.push([`${key}: ${who} reads its own row`, async () => {
672
+ expect((await rowsOf(user.findMany({ where: own }))).length === 1, `${who} can't read its own row, so the other checks prove nothing; check its claims and memberships`);
673
+ }], [`${key}: ${who} can't select ${whose} rows`, async () => {
674
+ expect((await rowsOf(user.findMany({ where: target }))).length === 0, `select returned ${whose} row`);
675
+ }], [`${key}: ${who} can't insert ${whose} rows`, async () => {
676
+ const row = spec.row(tenants[other], 1);
677
+ const result = await user.create(row, { returning: false });
678
+ if (result.ok) {
679
+ const where = Object.fromEntries(Object.entries(row).filter(([, value]) => scalar(value)));
680
+ cleanup.push(() => admin[key].deleteMany({ where }));
681
+ }
682
+ expect(!result.ok, `insert of a row for ${whose} tenant succeeded`);
683
+ expect(result.error.kind === "forbidden", `insert failed with ${result.error.kind} (${result.error.message}), not an RLS error; check the row factory`);
684
+ }], [`${key}: ${who} can't update ${whose} rows`, async () => {
685
+ const before = await current();
686
+ const patch = spec.update;
687
+ expect(Object.entries(patch).some(([column, value]) => before?.[column] !== value), "update must change the row to detect a leak");
688
+ await user.updateMany({
689
+ where: target,
690
+ data: patch,
691
+ returning: false
692
+ });
693
+ const after = await current();
694
+ expect(Object.entries(patch).every(([column]) => after?.[column] === before?.[column]), `update changed ${whose} row`);
695
+ }]);
696
+ deletes.push([`${key}: ${who} can't delete ${whose} rows`, async () => {
697
+ await user.deleteMany({ where: target });
698
+ expect(await current() !== void 0, `delete removed ${whose} row`);
699
+ }]);
700
+ }
701
+ }
702
+ return await conform("Tenant isolation", [...reads, ...deletes]);
703
+ } finally {
704
+ for (const remove of cleanup.reverse()) await remove();
705
+ }
662
706
  }
663
- /** Proves an `EventSink` accepts empty and full batches without mutating them, and delivers them when readable. */
664
- function testEventSink(sink, options = {}) {
665
- const events = toCloudEvents({
666
- table: "customers",
667
- kind: "insert",
668
- rows: [{
669
- id: "1",
670
- name: "Acme"
671
- }, {
672
- id: "2",
673
- name: "Globex"
674
- }],
675
- context: { tenant: "organization_1" }
676
- }, { source: "/better-supabase/conformance" });
677
- return conform("EventSink", [
678
- ["accepts an empty batch", () => sink.send(deepFreeze([]))],
679
- ["accepts a batch without mutating it", () => sink.send(frozenCopy(events))],
680
- options.received && ["delivers every event", async () => {
681
- const ids = new Set((await options.received()).map((event) => event.id));
682
- const missing = events.filter((event) => !ids.has(event.id));
683
- expect(missing.length === 0, `${missing.length} of ${events.length} events were not delivered`);
684
- }]
685
- ]);
707
+ //#endregion
708
+ //#region src/testing/claims-fixtures.ts
709
+ const base = {
710
+ sub: "6f1c2c1e-5d0a-4d9e-9a51-6b1f0e7c2a10",
711
+ aud: "authenticated",
712
+ role: "authenticated",
713
+ iss: "https://project.supabase.co/auth/v1",
714
+ aal: "aal1",
715
+ session_id: "b7a8f9c0-1111-4222-8333-944455556666",
716
+ is_anonymous: false
717
+ };
718
+ const admin = "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d";
719
+ const supportSessionId = "4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b";
720
+ const client = "5f0e4d3c-2b1a-4098-8776-655443322110";
721
+ const chain = {
722
+ sub: "agent-runner",
723
+ act: { sub: "mcp-client-42" }
724
+ };
725
+ const agentAct = {
726
+ kind: "agent",
727
+ sub: "agent-7",
728
+ tools: ["search_knowledge"],
729
+ act: chain
730
+ };
731
+ const agentActReadOnly = {
732
+ kind: "agent",
733
+ sub: "agent-7",
734
+ tools: ["search_knowledge"],
735
+ read_only: true,
736
+ act: chain
737
+ };
738
+ /**
739
+ * `supportSession` and `supportSessionReadOnly` are what `supportClaims`
740
+ * mints (`act.kind: "support"`), `impersonation` what `actingAs` mints
741
+ * (`act.kind: "impersonation"`), `oauthClient` a Supabase OAuth server
742
+ * token (its identity scopes `openid` and `email` are left out of the
743
+ * delegation), `agentChain` an RFC 8693 chain without `kind`, and
744
+ * `agentRun` and `agentRunReadOnly` what `agentClaims` mints over that
745
+ * chain (`act.kind: "agent"`, with `readOnly: true` for the second).
746
+ */
747
+ const supabaseClaimFixtures = {
748
+ supportSession: {
749
+ claims: {
750
+ ...base,
751
+ act: {
752
+ kind: "support",
753
+ sub: admin,
754
+ reason: "ticket 42",
755
+ session_id: supportSessionId,
756
+ read_only: false
757
+ }
758
+ },
759
+ expect: {
760
+ actor: {
761
+ kind: "support",
762
+ id: admin,
763
+ sessionId: supportSessionId,
764
+ readOnly: false,
765
+ reason: "ticket 42"
766
+ },
767
+ impersonator: {
768
+ kind: "support",
769
+ id: admin,
770
+ sessionId: supportSessionId,
771
+ readOnly: false,
772
+ reason: "ticket 42"
773
+ }
774
+ }
775
+ },
776
+ supportSessionReadOnly: {
777
+ claims: {
778
+ ...base,
779
+ act: {
780
+ kind: "support",
781
+ sub: admin,
782
+ reason: "ticket 42",
783
+ session_id: supportSessionId,
784
+ read_only: true
785
+ }
786
+ },
787
+ expect: {
788
+ actor: {
789
+ kind: "support",
790
+ id: admin,
791
+ sessionId: supportSessionId,
792
+ readOnly: true,
793
+ reason: "ticket 42"
794
+ },
795
+ impersonator: {
796
+ kind: "support",
797
+ id: admin,
798
+ sessionId: supportSessionId,
799
+ readOnly: true,
800
+ reason: "ticket 42"
801
+ }
802
+ }
803
+ },
804
+ impersonation: {
805
+ claims: {
806
+ ...base,
807
+ act: {
808
+ kind: "impersonation",
809
+ sub: admin,
810
+ reason: "ticket 42"
811
+ }
812
+ },
813
+ expect: {
814
+ actor: {
815
+ kind: "impersonation",
816
+ id: admin,
817
+ reason: "ticket 42"
818
+ },
819
+ impersonator: {
820
+ kind: "impersonation",
821
+ id: admin,
822
+ reason: "ticket 42"
823
+ }
824
+ }
825
+ },
826
+ oauthClient: {
827
+ claims: {
828
+ ...base,
829
+ client_id: client,
830
+ scope: "openid email posts:read"
831
+ },
832
+ expect: {
833
+ actor: {
834
+ kind: "oauth-client",
835
+ id: client
836
+ },
837
+ delegation: { scopes: ["posts:read"] }
838
+ }
839
+ },
840
+ agentChain: {
841
+ claims: {
842
+ ...base,
843
+ scope: "posts:read",
844
+ act: chain
845
+ },
846
+ expect: {
847
+ actor: {
848
+ kind: "oauth-client",
849
+ id: "agent-runner",
850
+ chain
851
+ },
852
+ delegation: {
853
+ scopes: ["posts:read"],
854
+ chain
855
+ }
856
+ }
857
+ },
858
+ agentRun: {
859
+ claims: {
860
+ ...base,
861
+ scope: "posts:read",
862
+ act: agentAct
863
+ },
864
+ expect: {
865
+ actor: {
866
+ kind: "agent",
867
+ id: "agent-7",
868
+ tools: ["search_knowledge"],
869
+ readOnly: false,
870
+ chain: agentAct
871
+ },
872
+ delegation: {
873
+ scopes: ["posts:read"],
874
+ chain: agentAct
875
+ }
876
+ }
877
+ },
878
+ agentRunReadOnly: {
879
+ claims: {
880
+ ...base,
881
+ scope: "posts:read",
882
+ act: agentActReadOnly
883
+ },
884
+ expect: {
885
+ actor: {
886
+ kind: "agent",
887
+ id: "agent-7",
888
+ tools: ["search_knowledge"],
889
+ readOnly: true,
890
+ chain: agentActReadOnly
891
+ },
892
+ delegation: {
893
+ scopes: ["posts:read"],
894
+ chain: agentActReadOnly
895
+ }
896
+ }
897
+ }
898
+ };
899
+ const refused = {
900
+ kind: "invalid",
901
+ reason: "actor",
902
+ code: "ACTOR_INVALID"
903
+ };
904
+ /**
905
+ * `legacySupportSession` is a 0.5.0 support token: `session_id` without
906
+ * `kind`. better-supabase refuses it instead of reading it as a support
907
+ * session; mint a new one with `supportClaims`. The others are malformed
908
+ * levels: a support or agent `read_only` that is not a boolean, an agent
909
+ * with a `session_id`, an empty tool name or `tools` that is not a list, a
910
+ * `kind` better-supabase does not know, and a nested level without `sub`.
911
+ */
912
+ const supabaseInvalidClaimFixtures = {
913
+ legacySupportSession: {
914
+ claims: {
915
+ ...base,
916
+ act: {
917
+ sub: admin,
918
+ session_id: supportSessionId,
919
+ reason: "ticket 42"
920
+ }
921
+ },
922
+ expect: refused
923
+ },
924
+ supportReadOnlyNotBoolean: {
925
+ claims: {
926
+ ...base,
927
+ act: {
928
+ kind: "support",
929
+ sub: admin,
930
+ session_id: supportSessionId,
931
+ read_only: "false"
932
+ }
933
+ },
934
+ expect: refused
935
+ },
936
+ agentWithSessionId: {
937
+ claims: {
938
+ ...base,
939
+ act: {
940
+ kind: "agent",
941
+ sub: "agent-7",
942
+ session_id: supportSessionId
943
+ }
944
+ },
945
+ expect: refused
946
+ },
947
+ agentEmptyToolName: {
948
+ claims: {
949
+ ...base,
950
+ act: {
951
+ kind: "agent",
952
+ sub: "agent-7",
953
+ tools: ["search_knowledge", ""]
954
+ }
955
+ },
956
+ expect: refused
957
+ },
958
+ agentToolsNotArray: {
959
+ claims: {
960
+ ...base,
961
+ act: {
962
+ kind: "agent",
963
+ sub: "agent-7",
964
+ tools: "search_knowledge"
965
+ }
966
+ },
967
+ expect: refused
968
+ },
969
+ agentReadOnlyNotBoolean: {
970
+ claims: {
971
+ ...base,
972
+ act: {
973
+ kind: "agent",
974
+ sub: "agent-7",
975
+ read_only: "true"
976
+ }
977
+ },
978
+ expect: refused
979
+ },
980
+ unknownKind: {
981
+ claims: {
982
+ ...base,
983
+ act: {
984
+ kind: "delegate",
985
+ sub: "agent-7"
986
+ }
987
+ },
988
+ expect: refused
989
+ },
990
+ levelWithoutSub: {
991
+ claims: {
992
+ ...base,
993
+ act: {
994
+ kind: "agent",
995
+ sub: "agent-7",
996
+ act: { client_id: client }
997
+ }
998
+ },
999
+ expect: refused
1000
+ }
1001
+ };
1002
+ //#endregion
1003
+ //#region src/testing/seed.ts
1004
+ const SEED = Symbol.for("better-supabase.seed");
1005
+ function isSeed(value) {
1006
+ return typeof value === "object" && value !== null && SEED in value;
686
1007
  }
687
- function claimedJob(queue, row) {
688
- const instant = temporal().Instant.fromEpochMilliseconds(0);
1008
+ const quote = (text) => `'${text.replaceAll("'", "''")}'`;
1009
+ function arrayLiteral(values) {
1010
+ return `{${values.map((value) => {
1011
+ if (value === null) return "NULL";
1012
+ if (Array.isArray(value)) return arrayLiteral(value);
1013
+ return `"${(temporalText(value) ?? String(value)).replaceAll(/[\\"]/g, "\\$&")}"`;
1014
+ }).join(",")}}`;
1015
+ }
1016
+ function literal(value, column, where) {
1017
+ if (value === null) return "null";
1018
+ if (column.json) return quote(JSON.stringify(value));
1019
+ if (Array.isArray(value)) return quote(arrayLiteral(value));
1020
+ const temporalValue = temporalText(value);
1021
+ if (temporalValue !== void 0) return quote(temporalValue);
1022
+ const kind = typeof value;
1023
+ switch (kind) {
1024
+ case "string": return quote(value);
1025
+ case "boolean":
1026
+ case "bigint": return String(value);
1027
+ case "number":
1028
+ if (!Number.isFinite(value)) throw new TypeError(`${where}: ${String(value)} is not a finite number`);
1029
+ return String(value);
1030
+ case "object":
1031
+ case "function":
1032
+ case "symbol":
1033
+ case "undefined": throw new TypeError(`${where}: unsupported value of type ${kind}`);
1034
+ default: throw new TypeError(`${where}: unsupported value of type ${String(kind)}`);
1035
+ }
1036
+ }
1037
+ const ident = (name) => `"${name.replaceAll("\"", "\"\"")}"`;
1038
+ /** Parents first (by forward relations), otherwise in the order given. */
1039
+ function order(meta, keys) {
1040
+ const sorted = [];
1041
+ const visiting = /* @__PURE__ */ new Set();
1042
+ const visit = (key) => {
1043
+ if (sorted.includes(key) || visiting.has(key)) return;
1044
+ visiting.add(key);
1045
+ for (const relation of Object.values(meta.tables[key]?.relations ?? {})) if (relation.direction === "forward" && relation.table !== key && keys.includes(relation.table)) visit(relation.table);
1046
+ visiting.delete(key);
1047
+ sorted.push(key);
1048
+ };
1049
+ for (const key of keys) visit(key);
1050
+ return sorted;
1051
+ }
1052
+ function statement(table, rows) {
1053
+ const entries = Object.entries(rows);
1054
+ if (entries.length === 0) return;
1055
+ const columns = [];
1056
+ for (const [, row] of entries) for (const [key, value] of Object.entries(row)) {
1057
+ if (value === void 0 || columns.includes(key)) continue;
1058
+ const column = table.columns[key];
1059
+ if (!column) throw new TypeError(`seed ${table.key}: unknown column "${key}"`);
1060
+ if (column.generated) throw new TypeError(`seed ${table.key}: "${key}" is a generated column`);
1061
+ columns.push(key);
1062
+ }
1063
+ const values = entries.map(([name, row]) => {
1064
+ return ` (${columns.map((key) => row[key] === void 0 ? "default" : literal(row[key], table.columns[key], `seed ${table.key}.${name}.${key}`)).join(", ")})`;
1065
+ });
1066
+ return `insert into ${`${ident(table.schema)}.${ident(table.name)}`} (${columns.map((key) => ident(table.columns[key].db)).join(", ")}) values\n${values.join(",\n")}\non conflict do nothing;`;
1067
+ }
1068
+ /**
1069
+ * Typed fixtures shared by `supabase/seeds` and your tests. Rows use app
1070
+ * casing and Insert types; `better-supabase seed` renders them to SQL.
1071
+ *
1072
+ * ```ts
1073
+ * export const seed = defineSeed(betterSupabase, {
1074
+ * organizations: { acme: { id: ACME, name: 'Acme' } },
1075
+ * customers: { first: { organizationId: ACME, name: 'First customer' } },
1076
+ * });
1077
+ * ```
1078
+ */
1079
+ function defineSeed(betterSupabase, fixtures) {
1080
+ const meta = betterSupabase.meta;
1081
+ const statements = () => {
1082
+ const keys = Object.keys(fixtures).filter((key) => fixtures[key] !== void 0);
1083
+ for (const key of keys) {
1084
+ const table = meta.tables[key];
1085
+ if (!table) throw new TypeError(`seed: unknown table "${key}"`);
1086
+ if (table.kind === "view") throw new TypeError(`seed: "${key}" is a view`);
1087
+ }
1088
+ return order(meta, keys).flatMap((key) => {
1089
+ const sql = statement(meta.tables[key], fixtures[key]);
1090
+ return sql ? [sql] : [];
1091
+ });
1092
+ };
689
1093
  return {
690
- id: Number(row.id),
691
- queue,
692
- payload: row.message?.payload,
693
- attempts: row.attempts,
694
- maxAttempts: row.message?.max_attempts ?? 5,
695
- enqueuedAt: instant,
696
- visibleUntil: instant,
697
- lastError: row.message?.last_error ?? null,
698
- context: {}
1094
+ [SEED]: true,
1095
+ rows: fixtures,
1096
+ statements,
1097
+ sql: () => statements().join("\n\n"),
1098
+ async insert(sql) {
1099
+ for (const text of statements()) await sql.queryRaw(text);
1100
+ }
699
1101
  };
700
1102
  }
1103
+ //#endregion
1104
+ //#region src/testing/kits/executor.ts
1105
+ const recorder = (ops) => ({
1106
+ name: "recorder",
1107
+ execute: (op) => {
1108
+ ops.push(op);
1109
+ return Promise.resolve(ok({
1110
+ rows: [],
1111
+ count: 0
1112
+ }));
1113
+ }
1114
+ });
1115
+ /** The operation `run` sends to the executor, built by `betterSupabase` without touching a database. */
1116
+ async function captureOp(betterSupabase, run) {
1117
+ const ops = [];
1118
+ await run(betterSupabase.connect(recorder(ops)));
1119
+ const [op] = ops;
1120
+ if (!op) throw new Error("the repository call did not reach the executor");
1121
+ return op;
1122
+ }
1123
+ function tableKey(betterSupabase, table) {
1124
+ const key = table ?? Object.keys(betterSupabase.meta.tables)[0];
1125
+ if (!key || !(key in betterSupabase.meta.tables)) throw new TypeError(`better-supabase: unknown table "${String(key)}"`);
1126
+ return key;
1127
+ }
1128
+ const hasName = (subject) => ["has a name", () => {
1129
+ expect(typeof subject.name === "string" && subject.name.length > 0, "name must be a non-empty string");
1130
+ }];
701
1131
  /**
702
- * Proves a `QueueBackend` round-trips payloads, hides claimed messages for
703
- * their lease, counts attempts, retries and dead-letters on `fail`, and
704
- * (when it has `leases`) rejects a stale attempt and extends a lease. It
705
- * writes to `options.queue` on the backend's real store.
1132
+ * Proves an `Executor` meets the contract: rows keyed by the selection's
1133
+ * aliases, counts, `aborted` errors, failures returned as `Result`s rather
1134
+ * than thrown, and (with `create`) a write round trip and a `deleteMany`
1135
+ * that `maxAffected` refuses without deleting anything.
1136
+ *
1137
+ * ```ts
1138
+ * it('conforms', () => testExecutor(myExecutor, { betterSupabase, table: 'tags', create: { name: 'x' } }));
1139
+ * ```
706
1140
  */
707
- function testQueueBackend(backend, options) {
708
- const { queue } = options;
709
- const claimOne = async () => {
710
- const [row] = await backend.read(queue, 30, 1);
711
- return row && claimedJob(queue, row);
712
- };
713
- return conform(`QueueBackend "${backend.name}"`, [
714
- hasName(backend),
715
- ["has apiVersion 1", () => {
716
- const version = backend.apiVersion;
717
- expect(version === 1, "apiVersion must be 1");
1141
+ function testExecutor(executor, options) {
1142
+ const { betterSupabase } = options;
1143
+ const table = tableKey(betterSupabase, options.table);
1144
+ const context = { errorMappers: [] };
1145
+ const read = () => captureOp(betterSupabase, (db) => db[table]["findMany"]({ limit: 2 }));
1146
+ const create = options.create;
1147
+ return conform(`Executor "${executor.name}"`, [
1148
+ hasName(executor),
1149
+ ["reads rows keyed by the selection aliases", async () => {
1150
+ const op = await read();
1151
+ const result = await executor.execute(op, context);
1152
+ expect(result.ok, `select failed: ${result.ok ? "" : result.error.message}`);
1153
+ expect(isList(result.data.rows), "rows must be an array");
1154
+ expect(result.data.rows.length <= 2, `limit 2 returned ${result.data.rows.length} rows`);
1155
+ expect(result.data.count === null || typeof result.data.count === "number", "count must be a number or null");
1156
+ if (op.kind !== "select") return;
1157
+ const aliases = new Set(op.selection.columns.map((column) => column.alias));
1158
+ for (const row of result.data.rows) {
1159
+ const extra = Object.keys(row).filter((key) => !aliases.has(key));
1160
+ expect(extra.length === 0, `rows must use selection aliases; got ${extra.join(", ")}`);
1161
+ }
718
1162
  }],
719
- ["round-trips a payload and leases it", async () => {
720
- const id = await backend.send(queue, { n: 1 }, 0, 3, void 0);
721
- expect(Number.isFinite(id), "send must return a numeric id");
722
- const job = await claimOne();
723
- expect(job?.id === id, "read must return the sent message");
724
- expect(same(job.payload, { n: 1 }), "the payload must round-trip");
725
- expect(job.attempts === 1, "the first claim must be attempt 1");
726
- expect(job.maxAttempts === 3, "max_attempts must round-trip");
727
- expect(await claimOne() === void 0, "a leased message must stay hidden from other reads");
728
- expect(await backend.complete(job), "complete must return true");
1163
+ ["counts rows", async () => {
1164
+ const op = await captureOp(betterSupabase, (db) => db[table]["count"]());
1165
+ const result = await executor.execute(op, context);
1166
+ expect(result.ok, `count failed: ${result.ok ? "" : result.error.message}`);
1167
+ expect(typeof result.data.count === "number" && result.data.count >= 0, "count must be a number");
729
1168
  }],
730
- backend.leases && ["rejects a stale attempt and extends a lease", async () => {
731
- await backend.send(queue, { n: 2 }, 0, 3, void 0);
732
- const job = await claimOne();
733
- expect(job, "read must return the sent message");
734
- expect(!await backend.complete({
735
- ...job,
736
- attempts: job.attempts + 1
737
- }), "complete with another attempt must return false");
738
- expect(await backend.extend(job, 60), "extend must return true");
739
- expect(await backend.complete(job), "complete must return true");
1169
+ ["returns an aborted error for an aborted signal", async () => {
1170
+ const controller = new AbortController();
1171
+ controller.abort();
1172
+ const result = await executor.execute(await read(), {
1173
+ ...context,
1174
+ signal: controller.signal
1175
+ });
1176
+ expect(!result.ok, "an aborted request must fail");
1177
+ expect(result.error.kind === "aborted", `expected kind "aborted", got "${result.error.kind}"`);
740
1178
  }],
741
- ["retries, then dead-letters at max attempts", async () => {
742
- await backend.send(queue, { n: 3 }, 0, 2, void 0);
743
- const first = await claimOne();
744
- expect(first, "read must return the sent message");
745
- expect(await backend.fail(first, "boom", 0) === "queued", "fail before max_attempts must return queued");
746
- const second = await claimOne();
747
- expect(second?.attempts === 2, "a retried message must be attempt 2");
748
- expect(await backend.fail(second, "boom", 0) === "dead", "fail at max_attempts must return dead");
749
- expect(await claimOne() === void 0, "a dead message must not be claimed again");
1179
+ ["returns failures as results instead of throwing", async () => {
1180
+ const op = await read();
1181
+ const missing = {
1182
+ ...op,
1183
+ table: {
1184
+ ...op.table,
1185
+ name: "__better_supabase_missing__"
1186
+ }
1187
+ };
1188
+ const result = await executor.execute(missing, context);
1189
+ expect(!result.ok, "a missing table must fail");
1190
+ expect(typeof result.error.kind === "string" && typeof result.error.message === "string", "errors must be DbErrors");
750
1191
  }],
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
- }
1192
+ executor.rpc && ["returns rpc failures as results", async () => {
1193
+ expect(!(await executor.rpc("__better_supabase_missing__", {}, {
1194
+ ...context,
1195
+ schema: "public"
1196
+ })).ok, "calling a missing function must fail");
763
1197
  }],
764
- (options.dedupe ?? backend.leases) && ["returns the first id for a repeated dedupe key", async () => {
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");
766
- const job = await claimOne();
767
- expect(job, "read must return the sent message");
768
- await backend.complete(job);
769
- }]
770
- ]);
771
- }
772
- /**
773
- * Proves a `SupportSessionStore` starts sessions with the input it was
774
- * given, shows a running session only to its admin, ends the admin's
775
- * previous session on start, ends a session once, and lists sessions.
776
- * It writes sessions to the store; they are all ended when it returns.
777
- */
778
- function testSupportSessionStore(store, options) {
779
- const { admin } = options;
780
- const [first, second] = options.targets;
781
- const start = (targetUserId) => store.start({
782
- adminId: admin.id,
783
- adminClaims: admin.claims,
784
- targetUserId,
785
- reason: "conformance",
786
- ttlSeconds: 600,
787
- readOnly: true,
788
- metadata: { ticket: "T-1" }
789
- });
790
- return conform(`SupportSessionStore "${store.name}"`, [
791
- hasName(store),
792
- ["has apiVersion 1", () => {
793
- const version = store.apiVersion;
794
- expect(version === 1, "apiVersion must be 1");
1198
+ executor.functionSources && ["reads from SelectOp.source instead of the table", async () => {
1199
+ const op = await read();
1200
+ if (op.kind !== "select") return;
1201
+ expect(!(await executor.execute({
1202
+ ...op,
1203
+ source: {
1204
+ schema: op.table.schema,
1205
+ name: "__better_supabase_missing__",
1206
+ args: {}
1207
+ }
1208
+ }, context)).ok, "a missing source function must fail; reading the table ignores source");
795
1209
  }],
796
- ["starts a session with its input and shows it to its admin only", async () => {
797
- const session = await start(first);
798
- try {
799
- expect(session.id.length > 0, "start must return an id");
800
- expect(session.adminId === admin.id, "adminId must round-trip");
801
- expect(session.targetUserId === first, "targetUserId must round-trip");
802
- expect(session.reason === "conformance", "reason must round-trip");
803
- expect(session.readOnly, "readOnly must round-trip");
804
- expect(same(session.metadata, { ticket: "T-1" }), "metadata must round-trip");
805
- const seconds = (session.expiresAt.epochMilliseconds - session.startedAt.epochMilliseconds) / 1e3;
806
- expect(Math.abs(seconds - 600) < 5, "expiresAt must be ttlSeconds after startedAt");
807
- expect((await store.get(session.id, admin.id))?.id === session.id, "get must return the running session");
808
- expect(await store.get(session.id, first) === void 0, "get must hide a session from anyone but its admin");
809
- } finally {
810
- await store.end(session.id, "admin");
811
- }
1210
+ executor.batch && ["batch returns one result per operation, in order", async () => {
1211
+ const rows = await read();
1212
+ const count = await captureOp(betterSupabase, (db) => db[table]["count"]());
1213
+ const missing = {
1214
+ ...rows,
1215
+ table: {
1216
+ ...rows.table,
1217
+ name: "__better_supabase_missing__"
1218
+ }
1219
+ };
1220
+ const results = await executor.batch([
1221
+ rows,
1222
+ count,
1223
+ missing
1224
+ ], context);
1225
+ expect(results.length === 3, `expected 3 results, got ${results.length}`);
1226
+ const [first, second, third] = results;
1227
+ expect(first?.ok === true && Array.isArray(first.data.rows), "the first operation must return rows");
1228
+ expect(second?.ok === true && typeof second.data.count === "number", "the second operation must return a count");
1229
+ expect(third?.ok === false, "a failing operation must fail on its own, as a result");
812
1230
  }],
813
- ["ends the admin's previous session on start", async () => {
814
- const older = await start(first);
815
- const newer = await start(second);
816
- try {
817
- expect(await store.get(older.id, admin.id) === void 0, "starting a session must end the admin's previous one");
818
- expect((await store.get(newer.id, admin.id))?.id === newer.id, "the new session must be running");
819
- } finally {
820
- await store.end(newer.id, "admin");
821
- }
1231
+ create && ["round-trips a write", async () => {
1232
+ const repository = betterSupabase.connect(executor)[table];
1233
+ const created = await repository["create"](create);
1234
+ expect(created.ok, `create failed: ${JSON.stringify(created.error)}`);
1235
+ const row = created.data;
1236
+ for (const [key, value] of Object.entries(create)) expect(same(row[key], value), `created row has ${key} = ${JSON.stringify(row[key])}`);
1237
+ const [primary] = betterSupabase.meta.tables[table].primaryKey;
1238
+ expect(primary, "write checks need a table with a primary key");
1239
+ const removed = await repository["delete"](row[primary]);
1240
+ expect(removed.ok, `delete failed: ${JSON.stringify(removed.error)}`);
1241
+ const again = await repository["findFirst"]({ where: { [primary]: row[primary] } });
1242
+ expect(again.ok && again.data === null, "the deleted row is still readable");
822
1243
  }],
823
- ["ends a session once and lists it", async () => {
824
- const session = await start(first);
825
- expect(await store.end(session.id, "admin"), "end must return true");
826
- expect(!await store.end(session.id, "admin"), "ending an ended session must return false");
827
- expect(await store.get(session.id, admin.id) === void 0, "get must not return an ended session");
828
- const entry = (await store.list({
829
- adminId: admin.id,
830
- active: false
831
- })).find((item) => item.id === session.id);
832
- expect(entry, "list must include the ended session");
833
- expect(entry.endedAt !== void 0, "an ended session has endedAt");
834
- expect((await store.list({
835
- adminId: admin.id,
836
- active: true
837
- })).every((item) => item.id !== session.id), "list({ active: true }) must leave out ended sessions");
1244
+ create && ["refuses a write past maxAffected and writes nothing", async () => {
1245
+ const repository = betterSupabase.connect(executor)[table];
1246
+ const [primary] = betterSupabase.meta.tables[table].primaryKey;
1247
+ expect(primary, "write checks need a table with a primary key");
1248
+ const created = await repository["create"](create);
1249
+ expect(created.ok, `create failed: ${JSON.stringify(created.error)}`);
1250
+ const id = created.data[primary];
1251
+ try {
1252
+ const refused = await repository["deleteMany"]({
1253
+ where: { [primary]: id },
1254
+ maxAffected: 0
1255
+ });
1256
+ expect(!refused.ok, "deleteMany past maxAffected must fail");
1257
+ const kind = refused.error?.kind;
1258
+ expect(kind === "max_affected", `expected kind "max_affected", got "${String(kind)}"`);
1259
+ const kept = await repository["findFirst"]({ where: { [primary]: id } });
1260
+ expect(kept.ok && kept.data !== null, "a refused deleteMany must not delete the row");
1261
+ const allowed = await repository["deleteMany"]({
1262
+ where: { [primary]: id },
1263
+ maxAffected: 1
1264
+ });
1265
+ expect(allowed.ok, `deleteMany within maxAffected failed: ${JSON.stringify(allowed.error)}`);
1266
+ } finally {
1267
+ await repository["delete"](id);
1268
+ }
838
1269
  }]
839
1270
  ]);
840
1271
  }
1272
+ //#endregion
1273
+ //#region src/testing/kits/auth-resolver.ts
841
1274
  async function resolveSafely(resolver, request) {
842
1275
  try {
843
1276
  return await resolver.resolve(request);
@@ -873,6 +1306,393 @@ function testAuthResolver(resolver, options) {
873
1306
  }]
874
1307
  ]);
875
1308
  }
1309
+ //#endregion
1310
+ //#region src/testing/kits/cache-adapter.ts
1311
+ /**
1312
+ * Proves a `CacheAdapter` accepts table and row targets, repeated and
1313
+ * concurrent calls, and never mutates its input.
1314
+ */
1315
+ function testCacheAdapter(adapter, options = {}) {
1316
+ const table = options.table ?? "customers";
1317
+ const invalidate = (target) => Promise.resolve(adapter.invalidate(deepFreeze(target)));
1318
+ return conform(`CacheAdapter "${adapter.name}"`, [
1319
+ hasName(adapter),
1320
+ ["invalidates a table", () => invalidate({
1321
+ table,
1322
+ tables: [table],
1323
+ ids: []
1324
+ })],
1325
+ ["invalidates rows of a tenant", () => invalidate({
1326
+ table,
1327
+ tables: [table],
1328
+ ids: ["1", "a,b"],
1329
+ tenant: "organization_1"
1330
+ })],
1331
+ ["accepts repeated and concurrent calls", async () => {
1332
+ await invalidate({
1333
+ table,
1334
+ tables: [table],
1335
+ ids: ["1"]
1336
+ });
1337
+ await Promise.all(Array.from({ length: 10 }, (_, index) => invalidate({
1338
+ table,
1339
+ tables: [table],
1340
+ ids: [String(index)]
1341
+ })));
1342
+ }],
1343
+ ["accepts tables it has never seen", () => invalidate({
1344
+ table: "__better_supabase_unknown__",
1345
+ tables: ["__better_supabase_unknown__"],
1346
+ ids: []
1347
+ })],
1348
+ ["invalidates tables reached through foreign keys", () => invalidate({
1349
+ table,
1350
+ tables: [table, `${table}_children`],
1351
+ ids: ["1"]
1352
+ })]
1353
+ ]);
1354
+ }
1355
+ //#endregion
1356
+ //#region src/testing/kits/event-sink.ts
1357
+ /** Proves an `EventSink` accepts empty and full batches without mutating them, and delivers them when readable. */
1358
+ function testEventSink(sink, options = {}) {
1359
+ const events = toCloudEvents({
1360
+ table: "customers",
1361
+ kind: "insert",
1362
+ rows: [{
1363
+ id: "1",
1364
+ name: "Acme"
1365
+ }, {
1366
+ id: "2",
1367
+ name: "Globex"
1368
+ }],
1369
+ context: { tenant: "organization_1" }
1370
+ }, { source: "/better-supabase/conformance" });
1371
+ return conform("EventSink", [
1372
+ ["accepts an empty batch", () => sink.send(deepFreeze([]))],
1373
+ ["accepts a batch without mutating it", () => sink.send(frozenCopy(events))],
1374
+ options.received && ["delivers every event", async () => {
1375
+ const ids = new Set((await options.received()).map((event) => event.id));
1376
+ const missing = events.filter((event) => !ids.has(event.id));
1377
+ expect(missing.length === 0, `${missing.length} of ${events.length} events were not delivered`);
1378
+ }]
1379
+ ]);
1380
+ }
1381
+ //#endregion
1382
+ //#region src/testing/kits/payment-provider.ts
1383
+ const kindOf$2 = (result) => result.ok ? "ok" : result.error.kind;
1384
+ const half = (value) => ({
1385
+ amount: value.amount / 2n,
1386
+ currency: value.currency
1387
+ });
1388
+ /**
1389
+ * Runs the `PaymentProvider` contract against `provider`: the API version,
1390
+ * refused amounts, a created payment and its fields, an idempotency key
1391
+ * that pays once, `not_found` for an unknown payment, and, with the
1392
+ * options that drive them, capture and refunds of an authorized payment
1393
+ * and webhook verification. Every call carries a fresh reference, so the
1394
+ * kit runs against a test account without cleanup.
1395
+ */
1396
+ function testPaymentProvider(provider, options = {}) {
1397
+ const amount = options.amount ?? money(1000n, "EUR");
1398
+ const reference = () => `conformance-${crypto.randomUUID()}`;
1399
+ const { authorize, webhook } = options;
1400
+ const authorized = async () => {
1401
+ const created = await provider.createPayment({
1402
+ amount,
1403
+ reference: reference(),
1404
+ capture: "manual"
1405
+ }).orThrow();
1406
+ await authorize?.(created);
1407
+ return created;
1408
+ };
1409
+ return conform(`PaymentProvider "${provider.name}"`, [
1410
+ ["has apiVersion 1 and a name", () => {
1411
+ const version = provider.apiVersion;
1412
+ expect(version === 1, `apiVersion is ${String(version)}`);
1413
+ expect(provider.name.length > 0, "name is empty");
1414
+ }],
1415
+ ["refuses a zero amount and a number instead of Money", async () => {
1416
+ const zero = await provider.createPayment({
1417
+ amount: {
1418
+ amount: 0n,
1419
+ currency: amount.currency
1420
+ },
1421
+ reference: reference()
1422
+ });
1423
+ expect(kindOf$2(zero) === "invalid_input", `zero gave ${kindOf$2(zero)}`);
1424
+ const plain = await provider.createPayment({
1425
+ amount: 1e3,
1426
+ reference: reference()
1427
+ });
1428
+ expect(kindOf$2(plain) === "invalid_input", `a number gave ${kindOf$2(plain)}`);
1429
+ }],
1430
+ ["creates a payment with its amount, reference and provider", async () => {
1431
+ const ref = reference();
1432
+ const payment = await provider.createPayment({
1433
+ amount,
1434
+ reference: ref
1435
+ }).orThrow();
1436
+ expect(payment.id.length > 0, "the payment has no id");
1437
+ expect(payment.provider === provider.name, `provider is ${payment.provider}`);
1438
+ expect(payment.reference === ref, `reference is ${payment.reference}`);
1439
+ expect(payment.amount.amount === amount.amount && payment.amount.currency === amount.currency, `amount is ${String(payment.amount.amount)} ${payment.amount.currency}`);
1440
+ expect(payment.refunded.amount === 0n, "a new payment has refunds");
1441
+ expect(typeof payment.amount.amount === "bigint", "the amount is not a bigint");
1442
+ }],
1443
+ ["pays once for one idempotency key", async () => {
1444
+ const key = crypto.randomUUID();
1445
+ const input = {
1446
+ amount,
1447
+ reference: reference()
1448
+ };
1449
+ const first = await provider.createPayment(input, { idempotencyKey: key }).orThrow();
1450
+ const again = await provider.createPayment(input, { idempotencyKey: key }).orThrow();
1451
+ expect(first.id === again.id, "the same key created a second payment");
1452
+ }],
1453
+ ["returns not_found for an unknown payment", async () => {
1454
+ const id = `conformance_missing_${crypto.randomUUID()}`;
1455
+ const captured = await provider.capture(id);
1456
+ expect(kindOf$2(captured) === "not_found", `capture gave ${kindOf$2(captured)}`);
1457
+ const refunded = await provider.refund(id);
1458
+ expect(kindOf$2(refunded) === "not_found", `refund gave ${kindOf$2(refunded)}`);
1459
+ }],
1460
+ provider.get !== void 0 && ["reads a payment back", async () => {
1461
+ const created = await provider.createPayment({
1462
+ amount,
1463
+ reference: reference()
1464
+ }).orThrow();
1465
+ const read = await provider.get?.(created.id).orThrow();
1466
+ expect(read?.id === created.id, "get returned another payment");
1467
+ expect(read.reference === created.reference, "get lost the reference");
1468
+ }],
1469
+ authorize !== void 0 && ["captures an authorized payment", async () => {
1470
+ const payment = await authorized();
1471
+ const captured = await provider.capture(payment.id).orThrow();
1472
+ expect(captured.status === "succeeded", `status is ${captured.status}`);
1473
+ expect(captured.captured.amount === amount.amount, `captured ${String(captured.captured.amount)}`);
1474
+ }],
1475
+ authorize !== void 0 && ["refunds part of a payment and refuses more than is left", async () => {
1476
+ const payment = await authorized();
1477
+ await provider.capture(payment.id).orThrow();
1478
+ const part = half(amount);
1479
+ const refund = await provider.refund(payment.id, { amount: part }).orThrow();
1480
+ expect(refund.paymentId === payment.id, "the refund names another payment");
1481
+ expect(refund.amount.amount === part.amount, `refunded ${String(refund.amount.amount)}`);
1482
+ expect(refund.status === "succeeded" || refund.status === "pending", `refund status is ${refund.status}`);
1483
+ const tooMuch = await provider.refund(payment.id, { amount });
1484
+ expect(kindOf$2(tooMuch) === "invalid_input", `refunding more than is left gave ${kindOf$2(tooMuch)}`);
1485
+ }],
1486
+ provider.cancel !== void 0 && ["cancels a payment that was not captured", async () => {
1487
+ const created = await provider.createPayment({
1488
+ amount,
1489
+ reference: reference(),
1490
+ capture: "manual"
1491
+ }).orThrow();
1492
+ const canceled = await provider.cancel?.(created.id).orThrow();
1493
+ expect(canceled?.status === "canceled", `status is ${String(canceled?.status)}`);
1494
+ }],
1495
+ webhook !== void 0 && ["parses a signed webhook and refuses a tampered one", async () => {
1496
+ const payment = await provider.createPayment({
1497
+ amount,
1498
+ reference: reference()
1499
+ }).orThrow();
1500
+ const { valid, tampered } = await webhook(payment);
1501
+ const events = await provider.parseWebhook(valid).orThrow();
1502
+ expect(events.length > 0, "a signed delivery gave no events");
1503
+ for (const event of events) {
1504
+ expect(event.id.length > 0, "an event has no id");
1505
+ expect(event.paymentId === payment.id, `an event names payment ${event.paymentId}`);
1506
+ }
1507
+ const refused = await provider.parseWebhook(tampered);
1508
+ expect(kindOf$2(refused) === "unauthorized", `a tampered delivery gave ${kindOf$2(refused)}`);
1509
+ }]
1510
+ ]);
1511
+ }
1512
+ //#endregion
1513
+ //#region src/testing/kits/tax-provider.ts
1514
+ const kindOf$1 = (result) => result.ok ? "ok" : result.error.kind;
1515
+ const sum = (values) => values.reduce((total, value) => total + value.amount, 0n);
1516
+ /**
1517
+ * Runs the `TaxProvider` contract against `provider`: the API version,
1518
+ * refused carts, a quote with one line per cart line in order and a total
1519
+ * that adds up, inclusive prices, shipping, exempt customers, commit, and
1520
+ * full and partial reversals with their limits. Every commit carries a
1521
+ * fresh reference, so the kit runs against a test account without cleanup.
1522
+ */
1523
+ function testTaxProvider(provider, options) {
1524
+ const currency = options.currency ?? "EUR";
1525
+ const { address, taxCategory } = options;
1526
+ const expectTax = options.expectTax ?? true;
1527
+ const line = (id, minor, inclusive = false) => ({
1528
+ id,
1529
+ amount: money(minor, currency),
1530
+ inclusive,
1531
+ ...taxCategory === void 0 ? {} : { taxCategory }
1532
+ });
1533
+ const cart = (overrides = {}) => ({
1534
+ currency,
1535
+ address,
1536
+ lines: [line("a", 10000n), line("b", 2500n)],
1537
+ ...overrides
1538
+ });
1539
+ const reference = () => `conformance-${crypto.randomUUID()}`;
1540
+ const committed = async () => {
1541
+ const quote = await provider.quote(cart()).orThrow();
1542
+ return provider.commit({
1543
+ quote,
1544
+ reference: reference()
1545
+ }).orThrow();
1546
+ };
1547
+ const addsUp = (quote) => {
1548
+ expect(quote.totalTax.amount === sum(quote.lines.map((each) => each.tax)), "totalTax is not the sum of the lines' tax");
1549
+ for (const each of quote.lines) {
1550
+ expect(isRate(each.rate), `line ${each.lineId} has rate "${each.rate}"`);
1551
+ expect(each.tax.currency === currency && each.taxable.currency === currency, `line ${each.lineId} is not in ${currency}`);
1552
+ }
1553
+ };
1554
+ return conform(`TaxProvider "${provider.name}"`, [
1555
+ ["has apiVersion 1 and a name", () => {
1556
+ const version = provider.apiVersion;
1557
+ expect(version === 1, `apiVersion is ${String(version)}`);
1558
+ expect(provider.name.length > 0, "name is empty");
1559
+ }],
1560
+ ["refuses a cart that mixes currencies or has no country", async () => {
1561
+ const other = currency === "USD" ? "EUR" : "USD";
1562
+ const mixed = await provider.quote(cart({ lines: [{
1563
+ id: "a",
1564
+ amount: money(100n, other)
1565
+ }] }));
1566
+ expect(kindOf$1(mixed) === "invalid_input", `mixed currencies gave ${kindOf$1(mixed)}`);
1567
+ const nowhere = await provider.quote(cart({ address: {
1568
+ ...address,
1569
+ country: ""
1570
+ } }));
1571
+ expect(kindOf$1(nowhere) === "invalid_input", `no country gave ${kindOf$1(nowhere)}`);
1572
+ }],
1573
+ ["quotes one line per cart line, in order, with a total that adds up", async () => {
1574
+ const quote = await provider.quote(cart()).orThrow();
1575
+ expect(quote.provider === provider.name, `provider is ${quote.provider}`);
1576
+ expect(quote.lines.map((each) => each.lineId).join(",") === "a,b", `lines are ${quote.lines.map((each) => each.lineId).join(",")}`);
1577
+ expect(quote.lines[0]?.taxable.amount === 10000n, "line a lost its amount");
1578
+ addsUp(quote);
1579
+ expect(expectTax ? quote.totalTax.amount > 0n : quote.totalTax.amount === 0n, `totalTax is ${String(quote.totalTax.amount)}`);
1580
+ }],
1581
+ ["splits an inclusive price into taxable and tax", async () => {
1582
+ const only = (await provider.quote(cart({ lines: [line("a", 12100n, true)] })).orThrow()).lines[0];
1583
+ expect(only?.inclusive === true, "the line is not inclusive");
1584
+ expect(only.taxable.amount + only.tax.amount === 12100n, "taxable and tax don't add up to the price");
1585
+ }],
1586
+ ["quotes shipping as the last line", async () => {
1587
+ const quote = await provider.quote(cart({ shipping: { amount: money(500n, currency) } })).orThrow();
1588
+ expect(quote.lines.at(-1)?.lineId === SHIPPING_LINE, "the last line is not shipping");
1589
+ addsUp(quote);
1590
+ }],
1591
+ ["charges an exempt customer no tax", async () => {
1592
+ const quote = await provider.quote(cart({ customer: { exempt: true } })).orThrow();
1593
+ expect(quote.totalTax.amount === 0n, `totalTax is ${String(quote.totalTax.amount)}`);
1594
+ }],
1595
+ ["commits a quote as a transaction", async () => {
1596
+ const quote = await provider.quote(cart()).orThrow();
1597
+ const ref = reference();
1598
+ const transaction = await provider.commit({
1599
+ quote,
1600
+ reference: ref
1601
+ }).orThrow();
1602
+ expect(transaction.id.length > 0, "the transaction has no id");
1603
+ expect(transaction.reference === ref, "the reference changed");
1604
+ expect(transaction.totalTax.amount === quote.totalTax.amount, "the transaction's tax differs from the quote");
1605
+ }],
1606
+ ["reverses a whole sale", async () => {
1607
+ const sale = await committed();
1608
+ const reversal = await provider.reverse({
1609
+ transaction: sale,
1610
+ reference: reference()
1611
+ }).orThrow();
1612
+ expect(reversal.totalTax.amount === -sale.totalTax.amount, `reversed ${String(reversal.totalTax.amount)} of ${String(sale.totalTax.amount)}`);
1613
+ }],
1614
+ ["reverses part of a line and refuses more than was sold", async () => {
1615
+ const sale = await committed();
1616
+ const sold = sale.lines[0];
1617
+ expect(sold !== void 0, "the sale has no lines");
1618
+ const part = {
1619
+ lineId: sold.lineId,
1620
+ amount: money(sold.taxable.amount / 2n, currency),
1621
+ tax: money(sold.tax.amount / 2n, currency)
1622
+ };
1623
+ const reversal = await provider.reverse({
1624
+ transaction: sale,
1625
+ reference: reference(),
1626
+ lines: [part]
1627
+ }).orThrow();
1628
+ expect(reversal.totalTax.amount === -part.tax.amount, `reversed ${String(reversal.totalTax.amount)}`);
1629
+ const tooMuch = await provider.reverse({
1630
+ transaction: sale,
1631
+ reference: reference(),
1632
+ lines: [{
1633
+ ...part,
1634
+ amount: money(sold.taxable.amount + 1n, currency)
1635
+ }]
1636
+ });
1637
+ expect(kindOf$1(tooMuch) === "invalid_input", `reversing more than was sold gave ${kindOf$1(tooMuch)}`);
1638
+ const unknown = await provider.reverse({
1639
+ transaction: sale,
1640
+ reference: reference(),
1641
+ lines: [{
1642
+ ...part,
1643
+ lineId: "conformance-unknown"
1644
+ }]
1645
+ });
1646
+ expect(kindOf$1(unknown) === "invalid_input", `an unknown line gave ${kindOf$1(unknown)}`);
1647
+ }]
1648
+ ]);
1649
+ }
1650
+ //#endregion
1651
+ //#region src/testing/kits/incoming-destination.ts
1652
+ /**
1653
+ * Proves an `IncomingDestination` accepts a delivery once per endpoint and
1654
+ * message id: a repeat reports the first id as a duplicate, while another
1655
+ * message id or another endpoint is new.
1656
+ * It delivers four messages with random endpoint ids; the destination keeps them.
1657
+ */
1658
+ function testIncomingDestination(destination, options) {
1659
+ const endpoint = crypto.randomUUID();
1660
+ const delivery = (messageId, endpointId = endpoint) => ({
1661
+ endpoint: {
1662
+ id: endpointId,
1663
+ tenant: options.tenant,
1664
+ metadata: {}
1665
+ },
1666
+ source: options.source ?? "conformance",
1667
+ messageId,
1668
+ type: "conformance.test",
1669
+ payload: {
1670
+ type: "conformance.test",
1671
+ messageId
1672
+ },
1673
+ headers: {}
1674
+ });
1675
+ return conform(`IncomingDestination "${destination.name}"`, [
1676
+ hasName(destination),
1677
+ ["has apiVersion 1", () => {
1678
+ const version = destination.apiVersion;
1679
+ expect(version === 1, "apiVersion must be 1");
1680
+ }],
1681
+ ["reports a repeated endpoint and message id as a duplicate of the first", async () => {
1682
+ const first = await destination.deliver(delivery("m1"));
1683
+ expect(!first.duplicate, "the first delivery must not be a duplicate");
1684
+ const again = await destination.deliver(delivery("m1"));
1685
+ expect(again.duplicate, "a repeated delivery must be a duplicate");
1686
+ expect(String(again.id) === String(first.id), "a duplicate must return the first delivery's id");
1687
+ }],
1688
+ ["treats another message id or another endpoint as a new delivery", async () => {
1689
+ expect(!(await destination.deliver(delivery("m2"))).duplicate, "another message id must be new");
1690
+ expect(!(await destination.deliver(delivery("m1", crypto.randomUUID()))).duplicate, "the same id on another endpoint must be new");
1691
+ }]
1692
+ ]);
1693
+ }
1694
+ //#endregion
1695
+ //#region src/testing/kits/plugin.ts
876
1696
  /**
877
1697
  * Proves a `Plugin` targets API v1, installs cleanly on its own and next to
878
1698
  * the first-party plugins in either order, adds repository methods without
@@ -998,356 +1818,292 @@ function testPlugin(plugin, options) {
998
1818
  ]);
999
1819
  }
1000
1820
  //#endregion
1001
- //#region src/testing/isolation.ts
1002
- const scalar = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1003
- function keyOf(table, row) {
1004
- return Object.fromEntries(table.primaryKey.map((column) => [column, row[column]]));
1005
- }
1006
- async function rowsOf(outcome) {
1007
- const result = await outcome;
1008
- if (!result.ok) throw new Error(result.error.message);
1009
- return Array.isArray(result.data) ? result.data : [result.data];
1010
- }
1011
- function env(names) {
1012
- if (typeof process === "undefined") return void 0;
1013
- for (const name of names) {
1014
- const value = process.env[name];
1015
- if (value) return value;
1016
- }
1821
+ //#region src/testing/kits/queue-backend.ts
1822
+ function claimedJob(queue, row) {
1823
+ const instant = temporal().Instant.fromEpochMilliseconds(0);
1824
+ return {
1825
+ id: Number(row.id),
1826
+ queue,
1827
+ payload: row.message?.payload,
1828
+ attempts: row.attempts,
1829
+ maxAttempts: row.message?.max_attempts ?? 5,
1830
+ enqueuedAt: instant,
1831
+ visibleUntil: instant,
1832
+ lastError: row.message?.last_error ?? null,
1833
+ context: {}
1834
+ };
1017
1835
  }
1018
1836
  /**
1019
- * Checks row-level security between two tenants against a running stack: for
1020
- * each table, a user of one tenant must not select, insert, update or delete
1021
- * the other tenant's rows. Runs without `betterSupabase`'s plugins, so the `tenant()`
1022
- * plugin can't hide a missing policy. Throws a `ConformanceError` naming each
1023
- * table and command that leaks; removes every row it created.
1024
- *
1025
- * ```ts
1026
- * await expectTenantIsolation(betterSupabase, {
1027
- * tenants: [
1028
- * { id: ACME, claims: { sub: alice, tenant_id: ACME } },
1029
- * { id: GLOBEX, claims: { sub: bob, tenant_id: GLOBEX } },
1030
- * ],
1031
- * tables: {
1032
- * tags: {
1033
- * row: (tenant, n) => ({ organizationId: tenant.id, name: `iso-${n}` }),
1034
- * update: { name: 'changed' },
1035
- * },
1036
- * },
1037
- * });
1038
- * ```
1837
+ * Proves a `QueueBackend` round-trips payloads, hides claimed messages for
1838
+ * their lease, counts attempts, retries and dead-letters on `fail`, and
1839
+ * (when it has `leases`) rejects a stale attempt and extends a lease. It
1840
+ * writes to `options.queue` on the backend's real store.
1039
1841
  */
1040
- async function expectTenantIsolation(betterSupabase, options) {
1041
- const stack = options.stack ?? {};
1042
- const bare = new BetterSupabase(betterSupabase.schema);
1043
- const url = stack.url ?? env(ENV_VARIABLES.url) ?? "http://127.0.0.1:54321";
1044
- const secretKey = stack.secretKey ?? env(ENV_VARIABLES.secretKey);
1045
- if (!secretKey) throw new TypeError("expectTenantIsolation needs stack.secretKey or $SUPABASE_SECRET_KEY to seed rows.");
1046
- const admin = bare.connect(createClient(url, secretKey, { auth: {
1047
- persistSession: false,
1048
- autoRefreshToken: false
1049
- } }));
1050
- await options.seed?.();
1051
- const tenants = options.tenants;
1052
- const users = await Promise.all(tenants.map(async (tenant) => (await asUser(bare, tenant.claims, stack)).db));
1053
- const label = (tenant) => tenant.name ?? tenant.id;
1054
- const cleanup = [];
1055
- const reads = [];
1056
- const deletes = [];
1057
- try {
1058
- for (const [key, spec] of Object.entries(options.tables)) {
1059
- const table = bare.meta.tables[key];
1060
- if (!table || table.primaryKey.length === 0) throw new TypeError(`expectTenantIsolation: "${key}" is not a table with a primary key`);
1061
- const seeded = [];
1062
- for (const tenant of tenants) {
1063
- const [row] = await rowsOf(admin[key].create(spec.row(tenant, 0)));
1064
- const where = keyOf(table, row);
1065
- cleanup.push(() => admin[key].deleteMany({ where }));
1066
- seeded.push(row);
1067
- }
1068
- for (const [self, other] of [[0, 1], [1, 0]]) {
1069
- const user = users[self][key];
1070
- const who = label(tenants[self]);
1071
- const whose = `${label(tenants[other])}'s`;
1072
- const own = keyOf(table, seeded[self]);
1073
- const target = keyOf(table, seeded[other]);
1074
- const current = async () => (await rowsOf(admin[key].findMany({ where: target })))[0];
1075
- reads.push([`${key}: ${who} reads its own row`, async () => {
1076
- expect((await rowsOf(user.findMany({ where: own }))).length === 1, `${who} can't read its own row, so the other checks prove nothing; check its claims and memberships`);
1077
- }], [`${key}: ${who} can't select ${whose} rows`, async () => {
1078
- expect((await rowsOf(user.findMany({ where: target }))).length === 0, `select returned ${whose} row`);
1079
- }], [`${key}: ${who} can't insert ${whose} rows`, async () => {
1080
- const row = spec.row(tenants[other], 1);
1081
- const result = await user.create(row, { returning: false });
1082
- if (result.ok) {
1083
- const where = Object.fromEntries(Object.entries(row).filter(([, value]) => scalar(value)));
1084
- cleanup.push(() => admin[key].deleteMany({ where }));
1085
- }
1086
- expect(!result.ok, `insert of a row for ${whose} tenant succeeded`);
1087
- expect(result.error.kind === "forbidden", `insert failed with ${result.error.kind} (${result.error.message}), not an RLS error; check the row factory`);
1088
- }], [`${key}: ${who} can't update ${whose} rows`, async () => {
1089
- const before = await current();
1090
- const patch = spec.update;
1091
- expect(Object.entries(patch).some(([column, value]) => before?.[column] !== value), "update must change the row to detect a leak");
1092
- await user.updateMany({
1093
- where: target,
1094
- data: patch,
1095
- returning: false
1096
- });
1097
- const after = await current();
1098
- expect(Object.entries(patch).every(([column]) => after?.[column] === before?.[column]), `update changed ${whose} row`);
1099
- }]);
1100
- deletes.push([`${key}: ${who} can't delete ${whose} rows`, async () => {
1101
- await user.deleteMany({ where: target });
1102
- expect(await current() !== void 0, `delete removed ${whose} row`);
1103
- }]);
1842
+ function testQueueBackend(backend, options) {
1843
+ const { queue } = options;
1844
+ const claimOne = async () => {
1845
+ const [row] = await backend.read(queue, 30, 1);
1846
+ return row && claimedJob(queue, row);
1847
+ };
1848
+ return conform(`QueueBackend "${backend.name}"`, [
1849
+ hasName(backend),
1850
+ ["has apiVersion 1", () => {
1851
+ const version = backend.apiVersion;
1852
+ expect(version === 1, "apiVersion must be 1");
1853
+ }],
1854
+ ["round-trips a payload and leases it", async () => {
1855
+ const id = await backend.send(queue, { n: 1 }, 0, 3, void 0);
1856
+ expect(Number.isFinite(id), "send must return a numeric id");
1857
+ const job = await claimOne();
1858
+ expect(job?.id === id, "read must return the sent message");
1859
+ expect(same(job.payload, { n: 1 }), "the payload must round-trip");
1860
+ expect(job.attempts === 1, "the first claim must be attempt 1");
1861
+ expect(job.maxAttempts === 3, "max_attempts must round-trip");
1862
+ expect(await claimOne() === void 0, "a leased message must stay hidden from other reads");
1863
+ expect(await backend.complete(job), "complete must return true");
1864
+ }],
1865
+ backend.leases && ["rejects a stale attempt and extends a lease", async () => {
1866
+ await backend.send(queue, { n: 2 }, 0, 3, void 0);
1867
+ const job = await claimOne();
1868
+ expect(job, "read must return the sent message");
1869
+ expect(!await backend.complete({
1870
+ ...job,
1871
+ attempts: job.attempts + 1
1872
+ }), "complete with another attempt must return false");
1873
+ expect(await backend.extend(job, 60), "extend must return true");
1874
+ expect(await backend.complete(job), "complete must return true");
1875
+ }],
1876
+ ["retries, then dead-letters at max attempts", async () => {
1877
+ await backend.send(queue, { n: 3 }, 0, 2, void 0);
1878
+ const first = await claimOne();
1879
+ expect(first, "read must return the sent message");
1880
+ expect(await backend.fail(first, "boom", 0) === "queued", "fail before max_attempts must return queued");
1881
+ const second = await claimOne();
1882
+ expect(second?.attempts === 2, "a retried message must be attempt 2");
1883
+ expect(await backend.fail(second, "boom", 0) === "dead", "fail at max_attempts must return dead");
1884
+ expect(await claimOne() === void 0, "a dead message must not be claimed again");
1885
+ }],
1886
+ backend.stats && backend.listDead && ["counts and lists dead letters", async () => {
1887
+ expect(((await backend.stats?.(queue))?.dead ?? 0) >= 1, "stats must count the dead letter");
1888
+ const [dead] = await backend.listDead?.(queue, 10, void 0) ?? [];
1889
+ expect(dead, "listDead must return the dead letter");
1890
+ expect(same(dead.message?.payload, { n: 3 }), "listDead must return the newest dead letter first");
1891
+ if (backend.retryDead) {
1892
+ expect(await backend.retryDead(queue, [Number(dead.id)], 10) === 1, "retryDead must enqueue the dead letter again");
1893
+ const job = await claimOne();
1894
+ expect(job, "the retried job must be claimable");
1895
+ expect(same(job.payload, { n: 3 }), "the retried job must keep its payload");
1896
+ await backend.complete(job);
1104
1897
  }
1105
- }
1106
- return await conform("Tenant isolation", [...reads, ...deletes]);
1107
- } finally {
1108
- for (const remove of cleanup.reverse()) await remove();
1109
- }
1898
+ }],
1899
+ (options.dedupe ?? backend.leases) && ["returns the first id for a repeated dedupe key", async () => {
1900
+ 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");
1901
+ const job = await claimOne();
1902
+ expect(job, "read must return the sent message");
1903
+ await backend.complete(job);
1904
+ }],
1905
+ backend.sendOnce && ["sends once per key, counting completed messages, when it has sendOnce", async () => {
1906
+ const key = `once-${Date.now()}`;
1907
+ const first = await backend.sendOnce?.(queue, { n: 5 }, 0, 3, key);
1908
+ expect(first && !first.duplicate, "the first sendOnce must send");
1909
+ const job = await claimOne();
1910
+ expect(job?.id === first.id, "read must return the sent message");
1911
+ await backend.complete(job);
1912
+ const again = await backend.sendOnce?.(queue, { n: 5 }, 0, 3, key);
1913
+ expect(again?.duplicate === true && again.id === first.id, "sendOnce after completion must return the first id as a duplicate");
1914
+ }]
1915
+ ]);
1110
1916
  }
1111
1917
  //#endregion
1112
- //#region src/testing/claims-fixtures.ts
1113
- const base = {
1114
- sub: "6f1c2c1e-5d0a-4d9e-9a51-6b1f0e7c2a10",
1115
- aud: "authenticated",
1116
- role: "authenticated",
1117
- iss: "https://project.supabase.co/auth/v1",
1118
- aal: "aal1",
1119
- session_id: "b7a8f9c0-1111-4222-8333-944455556666",
1120
- is_anonymous: false
1121
- };
1122
- const admin = "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d";
1123
- const supportSessionId = "4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b";
1124
- const client = "5f0e4d3c-2b1a-4098-8776-655443322110";
1125
- const chain = {
1126
- sub: "agent-runner",
1127
- act: { sub: "mcp-client-42" }
1128
- };
1918
+ //#region src/testing/kits/support-session-store.ts
1129
1919
  /**
1130
- * `supportSession` and `supportSessionReadOnly` are what `supportClaims`
1131
- * mints (`act.kind: "support"`), `impersonation` what `actingAs` mints
1132
- * (`act.kind: "impersonation"`), `oauthClient` a Supabase OAuth server token
1133
- * and `agentChain` an RFC 8693 chain without `kind`.
1920
+ * Proves a `SupportSessionStore` starts sessions with the input it was
1921
+ * given, shows a running session only to its admin, ends the admin's
1922
+ * previous session on start, ends a session once, lists sessions and, with
1923
+ * `expire`, leaves running sessions alone.
1924
+ * It writes sessions to the store; they are all ended when it returns.
1134
1925
  */
1135
- const supabaseClaimFixtures = {
1136
- supportSession: {
1137
- claims: {
1138
- ...base,
1139
- act: {
1140
- kind: "support",
1141
- sub: admin,
1142
- reason: "ticket 42",
1143
- session_id: supportSessionId,
1144
- read_only: false
1145
- }
1146
- },
1147
- expect: {
1148
- actor: {
1149
- kind: "support",
1150
- id: admin,
1151
- sessionId: supportSessionId,
1152
- readOnly: false,
1153
- reason: "ticket 42"
1154
- },
1155
- impersonator: {
1156
- kind: "support",
1157
- id: admin,
1158
- sessionId: supportSessionId,
1159
- readOnly: false,
1160
- reason: "ticket 42"
1161
- }
1162
- }
1163
- },
1164
- supportSessionReadOnly: {
1165
- claims: {
1166
- ...base,
1167
- act: {
1168
- kind: "support",
1169
- sub: admin,
1170
- reason: "ticket 42",
1171
- session_id: supportSessionId,
1172
- read_only: true
1173
- }
1174
- },
1175
- expect: {
1176
- actor: {
1177
- kind: "support",
1178
- id: admin,
1179
- sessionId: supportSessionId,
1180
- readOnly: true,
1181
- reason: "ticket 42"
1182
- },
1183
- impersonator: {
1184
- kind: "support",
1185
- id: admin,
1186
- sessionId: supportSessionId,
1187
- readOnly: true,
1188
- reason: "ticket 42"
1189
- }
1190
- }
1191
- },
1192
- impersonation: {
1193
- claims: {
1194
- ...base,
1195
- act: {
1196
- kind: "impersonation",
1197
- sub: admin,
1198
- reason: "ticket 42"
1926
+ function testSupportSessionStore(store, options) {
1927
+ const { admin } = options;
1928
+ const [first, second] = options.targets;
1929
+ const start = (targetUserId) => store.start({
1930
+ adminId: admin.id,
1931
+ adminClaims: admin.claims,
1932
+ targetUserId,
1933
+ reason: "conformance",
1934
+ ttlSeconds: 600,
1935
+ readOnly: true,
1936
+ metadata: { ticket: "T-1" }
1937
+ });
1938
+ return conform(`SupportSessionStore "${store.name}"`, [
1939
+ hasName(store),
1940
+ ["has apiVersion 1", () => {
1941
+ const version = store.apiVersion;
1942
+ expect(version === 1, "apiVersion must be 1");
1943
+ }],
1944
+ ["starts a session with its input and shows it to its admin only", async () => {
1945
+ const session = await start(first);
1946
+ try {
1947
+ expect(session.id.length > 0, "start must return an id");
1948
+ expect(session.adminId === admin.id, "adminId must round-trip");
1949
+ expect(session.targetUserId === first, "targetUserId must round-trip");
1950
+ expect(session.reason === "conformance", "reason must round-trip");
1951
+ expect(session.readOnly, "readOnly must round-trip");
1952
+ expect(same(session.metadata, { ticket: "T-1" }), "metadata must round-trip");
1953
+ const seconds = (session.expiresAt.epochMilliseconds - session.startedAt.epochMilliseconds) / 1e3;
1954
+ expect(Math.abs(seconds - 600) < 5, "expiresAt must be ttlSeconds after startedAt");
1955
+ expect((await store.get(session.id, admin.id))?.id === session.id, "get must return the running session");
1956
+ expect(await store.get(session.id, first) === void 0, "get must hide a session from anyone but its admin");
1957
+ } finally {
1958
+ await store.end(session.id, "admin");
1199
1959
  }
1200
- },
1201
- expect: {
1202
- actor: {
1203
- kind: "impersonation",
1204
- id: admin,
1205
- reason: "ticket 42"
1206
- },
1207
- impersonator: {
1208
- kind: "impersonation",
1209
- id: admin,
1210
- reason: "ticket 42"
1960
+ }],
1961
+ ["ends the admin's previous session on start", async () => {
1962
+ const older = await start(first);
1963
+ const newer = await start(second);
1964
+ try {
1965
+ expect(await store.get(older.id, admin.id) === void 0, "starting a session must end the admin's previous one");
1966
+ expect((await store.get(newer.id, admin.id))?.id === newer.id, "the new session must be running");
1967
+ } finally {
1968
+ await store.end(newer.id, "admin");
1211
1969
  }
1212
- }
1213
- },
1214
- oauthClient: {
1215
- claims: {
1216
- ...base,
1217
- client_id: client,
1218
- scope: "openid email posts:read"
1219
- },
1220
- expect: {
1221
- actor: {
1222
- kind: "oauth-client",
1223
- id: client
1224
- },
1225
- delegation: { scopes: [
1226
- "openid",
1227
- "email",
1228
- "posts:read"
1229
- ] }
1230
- }
1231
- },
1232
- agentChain: {
1233
- claims: {
1234
- ...base,
1235
- scope: "posts:read",
1236
- act: chain
1237
- },
1238
- expect: {
1239
- actor: {
1240
- kind: "oauth-client",
1241
- id: "agent-runner",
1242
- chain
1243
- },
1244
- delegation: {
1245
- scopes: ["posts:read"],
1246
- chain
1970
+ }],
1971
+ ["ends a session once and lists it", async () => {
1972
+ const session = await start(first);
1973
+ expect(await store.end(session.id, "admin"), "end must return true");
1974
+ expect(!await store.end(session.id, "admin"), "ending an ended session must return false");
1975
+ expect(await store.get(session.id, admin.id) === void 0, "get must not return an ended session");
1976
+ const entry = (await store.list({
1977
+ adminId: admin.id,
1978
+ active: false
1979
+ })).find((item) => item.id === session.id);
1980
+ expect(entry, "list must include the ended session");
1981
+ expect(entry.endedAt !== void 0, "an ended session has endedAt");
1982
+ expect((await store.list({
1983
+ adminId: admin.id,
1984
+ active: true
1985
+ })).every((item) => item.id !== session.id), "list({ active: true }) must leave out ended sessions");
1986
+ }],
1987
+ ["expires only sessions past their expiry, when it has expire", async () => {
1988
+ if (!store.expire) return;
1989
+ const session = await start(first);
1990
+ try {
1991
+ const expired = await store.expire();
1992
+ expect(expired.every((item) => item.id !== session.id), "expire must leave a running session alone");
1993
+ expect(expired.every((item) => item.endedBy === "expired"), "expire must end sessions with endedBy expired");
1994
+ expect((await store.get(session.id, admin.id))?.id === session.id, "the running session must still be running after expire");
1995
+ } finally {
1996
+ await store.end(session.id, "admin");
1247
1997
  }
1248
- }
1249
- }
1250
- };
1251
- //#endregion
1252
- //#region src/testing/seed.ts
1253
- const SEED = Symbol.for("better-supabase.seed");
1254
- function isSeed(value) {
1255
- return typeof value === "object" && value !== null && SEED in value;
1256
- }
1257
- const quote = (text) => `'${text.replaceAll("'", "''")}'`;
1258
- function arrayLiteral(values) {
1259
- return `{${values.map((value) => {
1260
- if (value === null) return "NULL";
1261
- if (Array.isArray(value)) return arrayLiteral(value);
1262
- return `"${(temporalText(value) ?? String(value)).replaceAll(/[\\"]/g, "\\$&")}"`;
1263
- }).join(",")}}`;
1264
- }
1265
- function literal(value, column, where) {
1266
- if (value === null) return "null";
1267
- if (column.json) return quote(JSON.stringify(value));
1268
- if (Array.isArray(value)) return quote(arrayLiteral(value));
1269
- const temporalValue = temporalText(value);
1270
- if (temporalValue !== void 0) return quote(temporalValue);
1271
- const kind = typeof value;
1272
- switch (kind) {
1273
- case "string": return quote(value);
1274
- case "boolean":
1275
- case "bigint": return String(value);
1276
- case "number":
1277
- if (!Number.isFinite(value)) throw new TypeError(`${where}: ${String(value)} is not a finite number`);
1278
- return String(value);
1279
- case "object":
1280
- case "function":
1281
- case "symbol":
1282
- case "undefined": throw new TypeError(`${where}: unsupported value of type ${kind}`);
1283
- default: throw new TypeError(`${where}: unsupported value of type ${String(kind)}`);
1284
- }
1998
+ }]
1999
+ ]);
1285
2000
  }
1286
- const ident = (name) => `"${name.replaceAll("\"", "\"\"")}"`;
1287
- /** Parents first (by forward relations), otherwise in the order given. */
1288
- function order(meta, keys) {
1289
- const sorted = [];
1290
- const visiting = /* @__PURE__ */ new Set();
1291
- const visit = (key) => {
1292
- if (sorted.includes(key) || visiting.has(key)) return;
1293
- visiting.add(key);
1294
- for (const relation of Object.values(meta.tables[key]?.relations ?? {})) if (relation.direction === "forward" && relation.table !== key && keys.includes(relation.table)) visit(relation.table);
1295
- visiting.delete(key);
1296
- sorted.push(key);
2001
+ //#endregion
2002
+ //#region src/testing/kits/controller-binding.ts
2003
+ const PROBE_INITIAL = {
2004
+ count: 0,
2005
+ label: "initial"
2006
+ };
2007
+ function probe() {
2008
+ let starts = 0;
2009
+ let stops = 0;
2010
+ let disposed = false;
2011
+ let made;
2012
+ const idle = () => void 0;
2013
+ let emit = idle;
2014
+ return {
2015
+ create: () => {
2016
+ const controller = defineController(PROBE_INITIAL, (state) => ({
2017
+ actions: {
2018
+ increment: () => {
2019
+ state.set((current) => ({ count: current.count + 1 }));
2020
+ },
2021
+ rename: (label) => {
2022
+ state.set({ label });
2023
+ }
2024
+ },
2025
+ start: () => {
2026
+ starts += 1;
2027
+ emit = (patch) => {
2028
+ state.set(patch);
2029
+ };
2030
+ return () => {
2031
+ stops += 1;
2032
+ emit = idle;
2033
+ };
2034
+ }
2035
+ }));
2036
+ made = {
2037
+ ...controller,
2038
+ dispose: () => {
2039
+ disposed = true;
2040
+ controller.dispose();
2041
+ }
2042
+ };
2043
+ return made;
2044
+ },
2045
+ emit: (patch) => {
2046
+ emit(patch);
2047
+ },
2048
+ starts: () => starts,
2049
+ stops: () => stops,
2050
+ disposed: () => disposed,
2051
+ controller: () => made
1297
2052
  };
1298
- for (const key of keys) visit(key);
1299
- return sorted;
1300
- }
1301
- function statement(table, rows) {
1302
- const entries = Object.entries(rows);
1303
- if (entries.length === 0) return void 0;
1304
- const columns = [];
1305
- for (const [, row] of entries) for (const [key, value] of Object.entries(row)) {
1306
- if (value === void 0 || columns.includes(key)) continue;
1307
- const column = table.columns[key];
1308
- if (!column) throw new TypeError(`seed ${table.key}: unknown column "${key}"`);
1309
- if (column.generated) throw new TypeError(`seed ${table.key}: "${key}" is a generated column`);
1310
- columns.push(key);
1311
- }
1312
- const values = entries.map(([name, row]) => {
1313
- return ` (${columns.map((key) => row[key] === void 0 ? "default" : literal(row[key], table.columns[key], `seed ${table.key}.${name}.${key}`)).join(", ")})`;
1314
- });
1315
- return `insert into ${`${ident(table.schema)}.${ident(table.name)}`} (${columns.map((key) => ident(table.columns[key].db)).join(", ")}) values\n${values.join(",\n")}\non conflict do nothing;`;
1316
2053
  }
1317
2054
  /**
1318
- * Typed fixtures shared by `supabase/seeds` and your tests. Rows use app
1319
- * casing and Insert types; `better-supabase seed` renders them to SQL.
1320
- *
1321
- * ```ts
1322
- * export const seed = defineSeed(betterSupabase, {
1323
- * organizations: { acme: { id: ACME, name: 'Acme' } },
1324
- * customers: { first: { organizationId: ACME, name: 'First customer' } },
1325
- * });
1326
- * ```
2055
+ * Proves a framework binding over a block controller renders the initial
2056
+ * snapshot, follows each change, passes the actions through and stops the
2057
+ * controller's work (and disposes it) on unmount.
1327
2058
  */
1328
- function defineSeed(betterSupabase, fixtures) {
1329
- const meta = betterSupabase.meta;
1330
- const statements = () => {
1331
- const keys = Object.keys(fixtures).filter((key) => fixtures[key] !== void 0);
1332
- for (const key of keys) {
1333
- const table = meta.tables[key];
1334
- if (!table) throw new TypeError(`seed: unknown table "${key}"`);
1335
- if (table.kind === "view") throw new TypeError(`seed: "${key}" is a view`);
1336
- }
1337
- return order(meta, keys).flatMap((key) => {
1338
- const sql = statement(meta.tables[key], fixtures[key]);
1339
- return sql ? [sql] : [];
1340
- });
2059
+ function testControllerBinding(name, harness) {
2060
+ const settle = async () => {
2061
+ await harness.settle?.();
1341
2062
  };
1342
- return {
1343
- [SEED]: true,
1344
- rows: fixtures,
1345
- statements,
1346
- sql: () => statements().join("\n\n"),
1347
- async insert(sql) {
1348
- for (const text of statements()) await sql.queryRaw(text);
2063
+ const mounted = async (run) => {
2064
+ const current = probe();
2065
+ const binding = await harness.mount(current.create);
2066
+ try {
2067
+ await settle();
2068
+ await run(binding, current);
2069
+ } finally {
2070
+ await binding.unmount();
1349
2071
  }
1350
2072
  };
2073
+ return conform(`ControllerBinding "${name}"`, [
2074
+ ["renders the initial snapshot", () => mounted((binding, current) => {
2075
+ const state = binding.read();
2076
+ expect(state.count === PROBE_INITIAL.count && state.label === PROBE_INITIAL.label, `rendered ${JSON.stringify(state)} instead of the initial snapshot`);
2077
+ expect(current.starts() === 1, `the controller started ${current.starts()} times on mount, not once`);
2078
+ })],
2079
+ ["renders a change from the controller", () => mounted(async (binding, current) => {
2080
+ current.emit({ label: "changed" });
2081
+ await settle();
2082
+ expect(binding.read().label === "changed", `rendered label ${JSON.stringify(binding.read().label)} after the controller changed it`);
2083
+ current.emit({ count: 5 });
2084
+ await settle();
2085
+ expect(binding.read().count === 5, `rendered count ${binding.read().count} after a second change`);
2086
+ })],
2087
+ ["passes the controller's actions through", () => mounted(async (binding, current) => {
2088
+ expect(binding.actions.increment === current.controller()?.actions.increment, "the binding's actions are not the controller's");
2089
+ binding.actions.increment();
2090
+ binding.actions.rename("renamed");
2091
+ await settle();
2092
+ const state = binding.read();
2093
+ expect(state.count === 1 && state.label === "renamed", `rendered ${JSON.stringify(state)} after the actions ran`);
2094
+ })],
2095
+ ["stops the controller on unmount", async () => {
2096
+ const current = probe();
2097
+ const binding = await harness.mount(current.create);
2098
+ await settle();
2099
+ await binding.unmount();
2100
+ await settle();
2101
+ expect(current.stops() === current.starts() && current.starts() > 0, `the controller started ${current.starts()} times and stopped ${current.stops()} times`);
2102
+ if (harness.disposes ?? true) expect(current.disposed(), "unmount didn't dispose the controller");
2103
+ current.emit({ count: 9 });
2104
+ await settle();
2105
+ }]
2106
+ ]);
1351
2107
  }
1352
2108
  //#endregion
1353
2109
  //#region src/testing/adapter.ts
@@ -1484,13 +2240,13 @@ function testBlockTransportMiddleware(middleware) {
1484
2240
  }
1485
2241
  //#endregion
1486
2242
  //#region src/testing/authorization-provider.ts
1487
- function isPlainJson(value) {
2243
+ function isPlainJson$1(value) {
1488
2244
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1489
2245
  if (typeof value === "number") return Number.isFinite(value);
1490
- if (Array.isArray(value)) return value.every(isPlainJson);
2246
+ if (Array.isArray(value)) return value.every(isPlainJson$1);
1491
2247
  if (typeof value !== "object") return false;
1492
2248
  const prototype = Object.getPrototypeOf(value);
1493
- return (prototype === Object.prototype || prototype === null) && Object.values(value).every((entry) => entry === void 0 || isPlainJson(entry));
2249
+ return (prototype === Object.prototype || prototype === null) && Object.values(value).every((entry) => entry === void 0 || isPlainJson$1(entry));
1494
2250
  }
1495
2251
  const QUALIFIED = /^[a-z_][\w$]*\.[a-z_][\w${}]*$/i;
1496
2252
  const TABLE = /^[a-z_][\w$]*\.[a-z_][\w$]*$/i;
@@ -1520,7 +2276,7 @@ function testAuthorizationProvider(provider) {
1520
2276
  expect(provider.apiVersion === 1, `apiVersion is ${String(provider.apiVersion)}`);
1521
2277
  }],
1522
2278
  ["is plain JSON", () => {
1523
- expect(isPlainJson(provider), "it doesn't survive a JSON round trip");
2279
+ expect(isPlainJson$1(provider), "it doesn't survive a JSON round trip");
1524
2280
  }],
1525
2281
  ["names valid, unique scopes", () => {
1526
2282
  expect(scopes.size > 0, "scopes is empty");
@@ -1559,6 +2315,14 @@ function testAuthorizationProvider(provider) {
1559
2315
  for (const scope of permission.scopes ?? []) expect(scopes.has(scope), `permission "${permission.key}" names unknown scope "${scope}"`);
1560
2316
  }
1561
2317
  }],
2318
+ provider.operations && ["operations name listed permissions", () => {
2319
+ const keys = provider.permissions === void 0 ? void 0 : new Set(provider.permissions.map((entry) => entry.key));
2320
+ for (const [name, operation] of Object.entries(provider.operations)) {
2321
+ expect(name !== "", "an operation name is empty");
2322
+ expect(operation.permission !== "", `operation "${name}" names no permission`);
2323
+ if (keys) expect(keys.has(operation.permission), `operation "${name}" needs "${operation.permission}", which permissions doesn't list`);
2324
+ }
2325
+ }],
1562
2326
  provider.memberships && ["memberships name known scopes", () => {
1563
2327
  for (const membership of provider.memberships) {
1564
2328
  expect(TABLE.test(membership.table), `membership table "${membership.table}" is not schema.table`);
@@ -1603,6 +2367,566 @@ function testAuthorizationProvider(provider) {
1603
2367
  ]);
1604
2368
  }
1605
2369
  //#endregion
2370
+ //#region src/testing/authorization-hooks.ts
2371
+ /**
2372
+ * Proves `AuthorizationHooks` target API v1 and fail closed for strangers:
2373
+ * fan-out returns only subscribers it was given, an identity nobody linked
2374
+ * and a forged ticket resolve to `null`, a requester never approves their
2375
+ * own request, an unknown publisher can neither publish nor run, an
2376
+ * unlinked chat author gets nothing, an admin with no claims cannot start
2377
+ * a support session and never approves their own, and a delegated token
2378
+ * with no scopes maps to no permissions.
2379
+ */
2380
+ function testAuthorizationHooks(hooks) {
2381
+ const stranger = crypto.randomUUID();
2382
+ const event = {
2383
+ type: "conformance.checked",
2384
+ tenant: crypto.randomUUID(),
2385
+ actorId: stranger,
2386
+ payload: {}
2387
+ };
2388
+ const subscribers = [{
2389
+ principal: crypto.randomUUID(),
2390
+ kind: "user"
2391
+ }, {
2392
+ principal: crypto.randomUUID(),
2393
+ kind: "service"
2394
+ }];
2395
+ const subset = async (policy, where) => {
2396
+ const kept = await policy(event, subscribers);
2397
+ for (const entry of kept) expect(subscribers.includes(entry), `${where} returned a subscriber it was not given`);
2398
+ expect((await policy(event, [])).length === 0, `${where} returned subscribers from an empty list`);
2399
+ };
2400
+ const supportStart = (extra) => {
2401
+ const now = Math.floor(Date.now() / 1e3);
2402
+ const claims = {
2403
+ sub: stranger,
2404
+ role: "authenticated",
2405
+ aud: "authenticated",
2406
+ iss: "conformance",
2407
+ exp: now + 60,
2408
+ iat: now,
2409
+ ...extra
2410
+ };
2411
+ return {
2412
+ admin: {
2413
+ kind: "user",
2414
+ token: "",
2415
+ claims,
2416
+ user: {
2417
+ id: stranger,
2418
+ role: "authenticated"
2419
+ },
2420
+ source: "conformance",
2421
+ expiresAt: claims.exp
2422
+ },
2423
+ targetUserId: crypto.randomUUID(),
2424
+ reason: "conformance",
2425
+ readOnly: true,
2426
+ metadata: {}
2427
+ };
2428
+ };
2429
+ const { delivery, identities, approverOf, tickets, automation, chat, delegation } = hooks;
2430
+ return conform(`AuthorizationHooks "${hooks.name}"`, [
2431
+ ["targets API v1 and has a name", () => {
2432
+ expect(hooks.apiVersion === 1, `apiVersion is ${String(hooks.apiVersion)}`);
2433
+ expect(hooks.name !== "", "name is empty");
2434
+ }],
2435
+ delivery?.subscribers && ["subscribers returns a subset", () => subset(delivery.subscribers, "delivery.subscribers")],
2436
+ delivery?.audience && ["audience returns a subset", () => subset(delivery.audience, "delivery.audience")],
2437
+ identities && ["an unlinked identity is null", async () => {
2438
+ expect(await identities.principalByIdentity({
2439
+ kind: "email",
2440
+ value: `${stranger}@conformance.invalid`
2441
+ }) === null, "an address nobody linked resolved to someone");
2442
+ }],
2443
+ approverOf && ["a requester never approves their own request", async () => {
2444
+ expect(!await approverOf({
2445
+ userId: stranger,
2446
+ claims: { sub: stranger }
2447
+ }, {
2448
+ kind: "tool",
2449
+ subject: crypto.randomUUID(),
2450
+ tenant: event.tenant,
2451
+ requester: stranger
2452
+ }), "approverOf allowed the requester");
2453
+ }],
2454
+ tickets && ["a forged ticket is null", async () => {
2455
+ expect(await tickets.subject(`forged.${stranger}.ticket`) === null, "a forged ticket resolved to a context");
2456
+ }],
2457
+ automation?.mayPublish && ["an unknown publisher may not publish", async () => {
2458
+ expect(!await automation.mayPublish({
2459
+ publisher: stranger,
2460
+ tenant: event.tenant,
2461
+ permissions: ["conformance.write"],
2462
+ claims: {
2463
+ sub: stranger,
2464
+ role: "authenticated"
2465
+ }
2466
+ }), "mayPublish allowed a publisher nobody knows");
2467
+ }],
2468
+ automation?.runAs && ["an unknown publisher runs as no one", async () => {
2469
+ expect(await automation.runAs({
2470
+ publisher: stranger,
2471
+ tenant: event.tenant,
2472
+ permissions: ["conformance.read"]
2473
+ }) === null, "runAs returned claims for an unknown publisher");
2474
+ }],
2475
+ chat?.onAction && ["an unlinked chat author is refused", async () => {
2476
+ const decided = await chat.onAction({
2477
+ id: "approve",
2478
+ author: {
2479
+ kind: "chat",
2480
+ provider: "conformance",
2481
+ value: stranger
2482
+ }
2483
+ }, null);
2484
+ expect(decided === void 0 || !decided.allowed, "onAction allowed an author with no identity link");
2485
+ }],
2486
+ hooks.support && ["an admin with no claims cannot start a support session", async () => {
2487
+ expect(await hooks.support.authorize(supportStart({})) === false, "support.authorize allowed an admin with no claims");
2488
+ }],
2489
+ hooks.support && ["an admin never approves their own support session", async () => {
2490
+ const decision = await hooks.support.authorize(supportStart({
2491
+ user_role: "admin",
2492
+ app_role: "admin"
2493
+ }));
2494
+ expect(typeof decision !== "object" || decision.approvedBy !== stranger, "support.authorize named the admin as their own approver");
2495
+ }],
2496
+ delegation && ["a delegation with no scopes grants no permissions", async () => {
2497
+ expect((await delegation.permissions({
2498
+ userId: stranger,
2499
+ tenant: event.tenant,
2500
+ clientId: "conformance",
2501
+ scopes: []
2502
+ })).length === 0, "delegation.permissions granted keys to a token with no scopes");
2503
+ }]
2504
+ ]);
2505
+ }
2506
+ //#endregion
2507
+ //#region src/testing/delivery-transport.ts
2508
+ const KEY = "00000000-0000-4000-8000-00000000d0f0";
2509
+ /**
2510
+ * Runs the `DeliveryTransport` contract against `transport`: API version 1,
2511
+ * the `email` channel, a provider name, and one frozen sample message it
2512
+ * sends without mutating, with the delivery id as its idempotency key.
2513
+ * Throws a `ConformanceError` listing every failed check.
2514
+ */
2515
+ function testDeliveryTransport(transport, options = {}) {
2516
+ const message = deepFreeze({
2517
+ from: "Conformance <conformance@example.test>",
2518
+ to: options.to ?? "delivered@example.test",
2519
+ subject: "Conformance check",
2520
+ text: "Conformance check",
2521
+ category: "notification",
2522
+ headers: mailHeaders("notification", { url: "https://example.test/unsubscribe" })
2523
+ });
2524
+ return conform(`DeliveryTransport "${transport.provider}"`, [
2525
+ ["has apiVersion 1", () => {
2526
+ const version = transport.apiVersion;
2527
+ expect(version === 1, "apiVersion must be 1");
2528
+ }],
2529
+ ["sends on the email channel", () => {
2530
+ const channel = transport.channel;
2531
+ expect(channel === "email", `channel must be "email", got ${String(channel)}`);
2532
+ }],
2533
+ ["has a provider name", () => {
2534
+ expect(typeof transport.provider === "string" && transport.provider !== "", "provider must be a non-empty string");
2535
+ }],
2536
+ ["sends a message without mutating it and returns a valid receipt", async () => {
2537
+ const receipt = await transport.send(message, { idempotencyKey: KEY });
2538
+ if (receipt === void 0) return;
2539
+ expect(receipt.providerMessageId === void 0 || typeof receipt.providerMessageId === "string", "providerMessageId must be a string");
2540
+ }],
2541
+ options.received && ["passes the idempotency key and the headers to the provider", async () => {
2542
+ const found = (await options.received()).find((entry) => entry.idempotencyKey === KEY);
2543
+ expect(found, `no message arrived with idempotency key ${KEY}`);
2544
+ for (const [name, value] of Object.entries(message.headers)) expect(found.headers[name] === value, `header ${name} must be "${value}", got ${String(found.headers[name])}`);
2545
+ }]
2546
+ ]);
2547
+ }
2548
+ //#endregion
2549
+ //#region src/testing/authorizer.ts
2550
+ const OUTCOMES = /* @__PURE__ */ new Set([
2551
+ "granted",
2552
+ "denied",
2553
+ "approval-required"
2554
+ ]);
2555
+ const isOutcome = (value) => typeof value === "object" && value !== null && "outcome" in value && typeof value.outcome === "string" && OUTCOMES.has(value.outcome);
2556
+ /** Input that claims a privileged caller; the subject must decide, never this. */
2557
+ const FORGED = {
2558
+ subject: {
2559
+ type: "service",
2560
+ id: "service"
2561
+ },
2562
+ actor: {
2563
+ id: "service",
2564
+ kind: "service",
2565
+ role: "service_role"
2566
+ },
2567
+ role: "service_role",
2568
+ user_id: "00000000-0000-4000-8000-000000000000",
2569
+ memberships: [{
2570
+ tenant: "conformance",
2571
+ role: "owner"
2572
+ }],
2573
+ claims: {
2574
+ role: "service_role",
2575
+ user_role: "admin",
2576
+ roles: ["admin"],
2577
+ memberships: [{
2578
+ tenant: "conformance",
2579
+ role: "owner"
2580
+ }]
2581
+ }
2582
+ };
2583
+ /**
2584
+ * Proves an `Authorizer` targets API v1, keys every permission to the same
2585
+ * string each time (and after a JSON round trip, so references are plain
2586
+ * JSON), answers every request with a known outcome without mutating it,
2587
+ * keeps the request order in `evaluations`, decides from the subject rather
2588
+ * than the input (forged roles, claims and memberships in the resource
2589
+ * properties or the context never turn a denial into a grant), never grants an unknown permission, and grants
2590
+ * `options.granted` through `checkPermission`.
2591
+ */
2592
+ function testAuthorizer(authorizer, options) {
2593
+ const { subject, context } = requestOf(void 0);
2594
+ const requestFor = (action, id, properties) => ({
2595
+ subject,
2596
+ action,
2597
+ resource: {
2598
+ type: "conformance",
2599
+ id,
2600
+ ...properties ? { properties } : {}
2601
+ },
2602
+ context
2603
+ });
2604
+ const requests = options.permissions.flatMap((permission) => [
2605
+ "a",
2606
+ "b",
2607
+ "c"
2608
+ ].map((id) => requestFor(permission, id)));
2609
+ return conform(`Authorizer "${authorizer.name}"`, [
2610
+ hasName(authorizer),
2611
+ ["targets API v1", () => {
2612
+ const version = authorizer.apiVersion;
2613
+ expect(version === 1, `apiVersion is ${String(version)}`);
2614
+ expect(options.permissions.length > 0, "options.permissions lists no permission to check");
2615
+ }],
2616
+ ["keys every permission stably, as plain JSON", () => {
2617
+ for (const permission of options.permissions) {
2618
+ const key = authorizer.key(permission);
2619
+ expect(typeof key === "string" && key.length > 0, "key() must return a non-empty string");
2620
+ expect(authorizer.key(permission) === key, `key() gave two keys for ${key}`);
2621
+ const copy = JSON.parse(JSON.stringify(permission));
2622
+ expect(same(copy, permission) && authorizer.key(copy) === key, `the reference for ${key} is not plain JSON`);
2623
+ }
2624
+ }],
2625
+ ["answers every request with a known outcome, without mutating it", async () => {
2626
+ for (const request of requests) {
2627
+ const outcome = await authorizer.evaluate(frozenCopy(request));
2628
+ expect(isOutcome(outcome), `evaluate() gave no known outcome for ${authorizer.key(request.action)}`);
2629
+ }
2630
+ }],
2631
+ authorizer.evaluations !== void 0 && ["keeps the request order in evaluations", async () => {
2632
+ const evaluations = authorizer.evaluations.bind(authorizer);
2633
+ const single = await Promise.all(requests.map((request) => Promise.resolve(authorizer.evaluate(request))));
2634
+ const batch = await evaluations(frozenCopy(requests));
2635
+ expect(batch.length === requests.length, `evaluations() gave ${batch.length} outcomes for ${requests.length} requests`);
2636
+ expect(same(batch.map((outcome) => outcome.outcome), single.map((outcome) => outcome.outcome)), "evaluations() disagrees with evaluate() for the same requests");
2637
+ const reversed = await evaluations(frozenCopy(requests.toReversed()));
2638
+ expect(same(reversed.toReversed().map((outcome) => outcome.outcome), batch.map((outcome) => outcome.outcome)), "evaluations() does not keep the request order");
2639
+ }],
2640
+ ["decides from the subject, never from the input", async () => {
2641
+ for (const permission of options.permissions) {
2642
+ const plain = await authorizer.evaluate(requestFor(permission, "a"));
2643
+ const forgedResource = await authorizer.evaluate(requestFor(permission, "a", FORGED));
2644
+ const forgedContext = await authorizer.evaluate({
2645
+ ...requestFor(permission, "a"),
2646
+ context: {
2647
+ ...context,
2648
+ ...FORGED
2649
+ }
2650
+ });
2651
+ for (const forged of [forgedResource, forgedContext]) expect(forged.outcome !== "granted" || plain.outcome === "granted", `input claiming a service caller was granted ${authorizer.key(permission)}`);
2652
+ }
2653
+ }],
2654
+ options.unknown !== void 0 && ["never grants an unknown permission", async () => {
2655
+ expect(!(await checkPermission({
2656
+ authorizer,
2657
+ permission: options.unknown,
2658
+ resource: { type: "conformance" },
2659
+ context: options.granted?.context
2660
+ })).ok, "an unknown permission was granted");
2661
+ }],
2662
+ options.granted !== void 0 && ["grants the granted permission through checkPermission", async () => {
2663
+ const checked = await checkPermission({
2664
+ authorizer,
2665
+ permission: options.granted.permission,
2666
+ resource: { type: "conformance" },
2667
+ context: options.granted.context
2668
+ });
2669
+ expect(checked.ok, checked.ok ? "" : `the permission was refused: ${checked.error.message}`);
2670
+ }]
2671
+ ]);
2672
+ }
2673
+ /**
2674
+ * An in-memory `Authorizer` for tests and examples: permissions come from
2675
+ * the subject's `role` and id only, never from the request's resource or
2676
+ * context, and it ignores `subject.properties.claims`.
2677
+ */
2678
+ function staticAuthorizer(options) {
2679
+ const approvals = new Set(options.approvals);
2680
+ const evaluate = (request) => {
2681
+ const { subject, action } = request;
2682
+ if (approvals.has(action)) return {
2683
+ outcome: "approval-required",
2684
+ approval: { id: `approval:${action}` }
2685
+ };
2686
+ const role = subject.properties?.["role"];
2687
+ return [...options.roles?.[typeof role === "string" ? role : subject.type] ?? [], ...options.grants?.[subject.id] ?? []].includes(action) ? { outcome: "granted" } : {
2688
+ outcome: "denied",
2689
+ reason: `Permission ${action} is required`
2690
+ };
2691
+ };
2692
+ return defineAuthorizer({
2693
+ name: "static",
2694
+ key: (ref) => ref,
2695
+ evaluate,
2696
+ evaluations: (requests) => Promise.resolve(requests.map(evaluate))
2697
+ });
2698
+ }
2699
+ //#endregion
2700
+ //#region src/testing/document-format.ts
2701
+ const STATUSES$1 = /* @__PURE__ */ new Set([
2702
+ "stable",
2703
+ "legacy",
2704
+ "preview"
2705
+ ]);
2706
+ const SEVERITIES = /* @__PURE__ */ new Set([
2707
+ "error",
2708
+ "warning",
2709
+ "info"
2710
+ ]);
2711
+ const CODE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
2712
+ const jsonSchema = (schema) => ({ "~standard": {
2713
+ version: 1,
2714
+ vendor: "better-supabase-conformance",
2715
+ validate: (value) => ({ value }),
2716
+ jsonSchema: {
2717
+ input: () => schema,
2718
+ output: () => schema
2719
+ }
2720
+ } });
2721
+ function sampleModel() {
2722
+ const customer = {
2723
+ type: "object",
2724
+ required: ["id", "name"],
2725
+ properties: {
2726
+ id: {
2727
+ type: "string",
2728
+ format: "uuid"
2729
+ },
2730
+ name: { type: "string" },
2731
+ note: { type: ["string", "null"] }
2732
+ }
2733
+ };
2734
+ return buildApiModel({ meta: {
2735
+ version: 1,
2736
+ casing: "camel",
2737
+ tables: {},
2738
+ enums: {},
2739
+ functions: {}
2740
+ } }, {
2741
+ info: {
2742
+ title: "Conformance",
2743
+ version: "1.0.0"
2744
+ },
2745
+ servers: [{
2746
+ url: "https://api.test",
2747
+ name: "test"
2748
+ }],
2749
+ tags: [{
2750
+ name: "customers",
2751
+ summary: "Customers"
2752
+ }],
2753
+ basePath: "/api",
2754
+ routes: [
2755
+ {
2756
+ method: "get",
2757
+ path: "/customers/{id}",
2758
+ operationId: "getCustomer",
2759
+ tags: ["customers"],
2760
+ output: jsonSchema(customer)
2761
+ },
2762
+ {
2763
+ method: "post",
2764
+ path: "/customers",
2765
+ operationId: "createCustomer",
2766
+ tags: ["customers"],
2767
+ input: jsonSchema(customer),
2768
+ output: jsonSchema(customer),
2769
+ status: 201
2770
+ },
2771
+ {
2772
+ method: "get",
2773
+ path: "/customers/changes",
2774
+ operationId: "streamCustomerChanges",
2775
+ stream: { item: jsonSchema(customer) }
2776
+ }
2777
+ ],
2778
+ webhooks: [{
2779
+ id: "customer.created",
2780
+ payload: customer,
2781
+ standardWebhooks: true
2782
+ }],
2783
+ realtimeUrl: "wss://abc.supabase.co/realtime/v1/websocket",
2784
+ messages: { customerChanged: {
2785
+ id: "customerChanged",
2786
+ payload: customer
2787
+ } },
2788
+ channels: [{
2789
+ id: "customers",
2790
+ address: "bs:t:public.customers",
2791
+ protocol: "ws",
2792
+ messages: ["customerChanged"],
2793
+ operations: [{
2794
+ id: "onCustomerChanged",
2795
+ action: "receive"
2796
+ }]
2797
+ }],
2798
+ workflows: [{
2799
+ id: "createThenGet",
2800
+ steps: [{
2801
+ id: "create",
2802
+ operationId: "createCustomer",
2803
+ outputs: { id: "$response.body#/id" }
2804
+ }, {
2805
+ id: "get",
2806
+ operationId: "getCustomer",
2807
+ parameters: [{
2808
+ name: "id",
2809
+ in: "path",
2810
+ value: "$steps.create.outputs.id"
2811
+ }]
2812
+ }]
2813
+ }],
2814
+ security: ["bearer"]
2815
+ }).model;
2816
+ }
2817
+ function isPlainJson(value) {
2818
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
2819
+ if (typeof value === "number") return Number.isFinite(value);
2820
+ if (Array.isArray(value)) return value.every(isPlainJson);
2821
+ if (typeof value !== "object") return false;
2822
+ const prototype = Object.getPrototypeOf(value);
2823
+ if (prototype !== Object.prototype && prototype !== null) return false;
2824
+ return Object.values(value).every((entry) => entry !== void 0 && isPlainJson(entry));
2825
+ }
2826
+ function diagnosticProblem(diagnostic) {
2827
+ if (typeof diagnostic.code !== "string" || !CODE.test(diagnostic.code)) return `the code ${JSON.stringify(diagnostic.code)} is not kebab-case`;
2828
+ const severity = diagnostic.severity;
2829
+ if (typeof severity !== "string" || !SEVERITIES.has(severity)) return `${diagnostic.code} has the severity ${String(severity)}`;
2830
+ if (typeof diagnostic.message !== "string" || diagnostic.message === "") return `${diagnostic.code} has no message`;
2831
+ if (diagnostic.pointer !== void 0 && !/^(?:\/|$)/.test(diagnostic.pointer)) return `${diagnostic.code} has the pointer ${diagnostic.pointer}, which is not a JSON Pointer`;
2832
+ }
2833
+ /**
2834
+ * Proves a `DocumentFormat` targets API v1, lists versions with pins and a
2835
+ * known status, defaults to a version it lists that is not a preview, and,
2836
+ * for every version: lowers the model without mutating it (also when the
2837
+ * model is frozen), renders the same document twice, renders plain JSON or
2838
+ * text, reports diagnostics with a kebab-case code, a severity and a
2839
+ * message, finds no error in its own output with `lint`, and names a bare,
2840
+ * stable file.
2841
+ */
2842
+ function testDocumentFormat(format, options = {}) {
2843
+ const subject = format;
2844
+ const id = subject.id;
2845
+ const defaultVersion = subject.defaultVersion;
2846
+ const versions = options.versions ?? Object.keys(subject.versions);
2847
+ let model;
2848
+ const modelOf = () => model ??= options.model ?? sampleModel();
2849
+ const lower = (version, input) => {
2850
+ const diagnostics = [];
2851
+ return {
2852
+ document: subject.lower(input, version, { report: (diagnostic) => {
2853
+ diagnostics.push(diagnostic);
2854
+ } }),
2855
+ diagnostics
2856
+ };
2857
+ };
2858
+ return conform(`DocumentFormat "${String(id)}"`, [
2859
+ ["targets API v1 with an id", () => {
2860
+ const version = subject.apiVersion;
2861
+ expect(version === 1, `apiVersion is ${String(version)}`);
2862
+ expect(typeof id === "string" && id.length > 0, "id must be a non-empty string");
2863
+ }],
2864
+ ["lists versions with a pin and a known status", () => {
2865
+ const listed = Object.entries(subject.versions);
2866
+ expect(listed.length > 0, "versions lists no version");
2867
+ for (const [version, entry] of listed) {
2868
+ const status = entry.status;
2869
+ expect(typeof entry.pin === "string" && entry.pin.length > 0, `version ${version} has no pin`);
2870
+ expect(typeof status === "string" && STATUSES$1.has(status), `version ${version} has the status ${String(status)}`);
2871
+ }
2872
+ for (const version of versions) expect(Object.hasOwn(subject.versions, version), `options.versions names ${version}, which the format does not list`);
2873
+ }],
2874
+ ["defaults to a listed version that is not a preview", () => {
2875
+ const entry = Object.hasOwn(subject.versions, subject.defaultVersion) ? subject.versions[subject.defaultVersion] : void 0;
2876
+ expect(entry !== void 0, `the default version ${String(defaultVersion)} is not in versions`);
2877
+ expect(entry.status !== "preview", `the default version ${String(defaultVersion)} is a preview`);
2878
+ }],
2879
+ ["lowers every version without mutating the model", () => {
2880
+ const original = modelOf();
2881
+ const before = JSON.stringify(original);
2882
+ for (const version of versions) {
2883
+ lower(version, original);
2884
+ expect(JSON.stringify(original) === before, `lower(model, "${version}") changed the model`);
2885
+ lower(version, frozenCopy(original));
2886
+ }
2887
+ }],
2888
+ ["renders the same document twice", () => {
2889
+ for (const version of versions) {
2890
+ const first = lower(version, structuredClone(modelOf()));
2891
+ const second = lower(version, structuredClone(modelOf()));
2892
+ expect(same(first.document, second.document), `lower(model, "${version}") is not deterministic`);
2893
+ expect(same(first.diagnostics, second.diagnostics), `lower(model, "${version}") reports different diagnostics each time`);
2894
+ }
2895
+ }],
2896
+ ["renders plain JSON or text", () => {
2897
+ for (const version of versions) {
2898
+ const { document } = lower(version, modelOf());
2899
+ expect(typeof document === "string" || typeof document === "object" && document !== null && isPlainJson(document), `lower(model, "${version}") returned something other than a JSON object or a string`);
2900
+ }
2901
+ }],
2902
+ ["reports diagnostics with a code, a severity and a message", () => {
2903
+ for (const version of versions) {
2904
+ const { document, diagnostics } = lower(version, modelOf());
2905
+ const linted = subject.lint?.(document, version) ?? [];
2906
+ for (const diagnostic of [...diagnostics, ...linted]) {
2907
+ const problem = diagnosticProblem(diagnostic);
2908
+ expect(problem === void 0, `${version}: ${problem ?? ""}`);
2909
+ }
2910
+ }
2911
+ }],
2912
+ subject.lint !== void 0 && ["finds no error in its own output", () => {
2913
+ for (const version of versions) {
2914
+ const { document, diagnostics } = lower(version, modelOf());
2915
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) continue;
2916
+ const errors = (subject.lint?.(document, version) ?? []).filter((diagnostic) => diagnostic.severity === "error");
2917
+ expect(errors.length === 0, `lint(lower(model, "${version}")) reports ${errors.map((error) => error.code).join(", ")}`);
2918
+ }
2919
+ }],
2920
+ ["names a bare, stable file per version", () => {
2921
+ for (const version of versions) {
2922
+ const name = subject.fileName(version);
2923
+ expect(typeof name === "string" && /^[^/\\]+\.[A-Za-z0-9]+$/.test(name), `fileName("${version}") is ${JSON.stringify(name)}, not a file name with an extension`);
2924
+ expect(subject.fileName(version) === name, `fileName("${version}") changes between calls`);
2925
+ }
2926
+ }]
2927
+ ]);
2928
+ }
2929
+ //#endregion
1606
2930
  //#region src/testing/generator.ts
1607
2931
  function relativeImport(from, to) {
1608
2932
  const fromParts = from.split("/").slice(0, -1);
@@ -1842,7 +3166,8 @@ function sample(channel, email) {
1842
3166
  priority: null,
1843
3167
  createdAt,
1844
3168
  readAt: null,
1845
- resolvedAt: null
3169
+ resolvedAt: null,
3170
+ fields: Object.freeze({})
1846
3171
  }),
1847
3172
  text: Object.freeze({ title: "Conformance check" })
1848
3173
  });
@@ -2078,7 +3403,7 @@ function testChatState(state, options) {
2078
3403
  "d"
2079
3404
  ]) await state.appendToList(key, value, { maxLength: 3 });
2080
3405
  const list = await state.getList(key);
2081
- expect(list.join() === "b,c,d", `list is ${list.join()}`);
3406
+ expect(list.join(",") === "b,c,d", `list is ${list.join(",")}`);
2082
3407
  expect((await state.getList(fresh())).length === 0, "missing list is not empty");
2083
3408
  }],
2084
3409
  ["queues per thread, keeps the newest maxSize and reports the depth", async () => {
@@ -2350,4 +3675,327 @@ function testCredentialProvider(provider, options) {
2350
3675
  ]);
2351
3676
  }
2352
3677
  //#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 };
3678
+ //#region src/testing/act-cases.ts
3679
+ /**
3680
+ * The RFC 8693 `act` chains better-supabase accepts and refuses, as plain
3681
+ * JSON (`better-supabase/testing/act-cases.json`), so another library that
3682
+ * reads the same claims can run the same cases.
3683
+ */
3684
+ const actCases = {
3685
+ version: 1,
3686
+ cases: [
3687
+ {
3688
+ "name": "no actor",
3689
+ "claims": {},
3690
+ "valid": true,
3691
+ "kind": null,
3692
+ "readOnly": null,
3693
+ "tools": null,
3694
+ "mayWrite": true
3695
+ },
3696
+ {
3697
+ "name": "oauth client_id",
3698
+ "claims": { "client_id": "client-1" },
3699
+ "valid": true,
3700
+ "kind": "oauth-client",
3701
+ "readOnly": null,
3702
+ "tools": null,
3703
+ "mayWrite": true
3704
+ },
3705
+ {
3706
+ "name": "chain without kind",
3707
+ "claims": { "act": {
3708
+ "sub": "agent-runner",
3709
+ "act": { "sub": "mcp-client-42" }
3710
+ } },
3711
+ "valid": true,
3712
+ "kind": "oauth-client",
3713
+ "readOnly": null,
3714
+ "tools": null,
3715
+ "mayWrite": true
3716
+ },
3717
+ {
3718
+ "name": "support, read_only missing",
3719
+ "claims": { "act": {
3720
+ "kind": "support",
3721
+ "sub": "admin-1",
3722
+ "session_id": "session-1"
3723
+ } },
3724
+ "valid": true,
3725
+ "kind": "support",
3726
+ "readOnly": true,
3727
+ "tools": null,
3728
+ "mayWrite": false
3729
+ },
3730
+ {
3731
+ "name": "support, read_only true",
3732
+ "claims": { "act": {
3733
+ "kind": "support",
3734
+ "sub": "admin-1",
3735
+ "session_id": "session-1",
3736
+ "read_only": true
3737
+ } },
3738
+ "valid": true,
3739
+ "kind": "support",
3740
+ "readOnly": true,
3741
+ "tools": null,
3742
+ "mayWrite": false
3743
+ },
3744
+ {
3745
+ "name": "support, read_only false",
3746
+ "claims": { "act": {
3747
+ "kind": "support",
3748
+ "sub": "admin-1",
3749
+ "session_id": "session-1",
3750
+ "read_only": false
3751
+ } },
3752
+ "valid": true,
3753
+ "kind": "support",
3754
+ "readOnly": false,
3755
+ "tools": null,
3756
+ "mayWrite": true
3757
+ },
3758
+ {
3759
+ "name": "support, read_only as a string",
3760
+ "claims": { "act": {
3761
+ "kind": "support",
3762
+ "sub": "admin-1",
3763
+ "session_id": "session-1",
3764
+ "read_only": "false"
3765
+ } },
3766
+ "valid": false,
3767
+ "kind": null,
3768
+ "readOnly": null,
3769
+ "tools": null,
3770
+ "mayWrite": false
3771
+ },
3772
+ {
3773
+ "name": "support without session_id",
3774
+ "claims": { "act": {
3775
+ "kind": "support",
3776
+ "sub": "admin-1",
3777
+ "read_only": false
3778
+ } },
3779
+ "valid": false,
3780
+ "kind": null,
3781
+ "readOnly": null,
3782
+ "tools": null,
3783
+ "mayWrite": true
3784
+ },
3785
+ {
3786
+ "name": "session_id without kind (0.5.0 support)",
3787
+ "claims": { "act": {
3788
+ "sub": "admin-1",
3789
+ "session_id": "session-1"
3790
+ } },
3791
+ "valid": false,
3792
+ "kind": null,
3793
+ "readOnly": null,
3794
+ "tools": null,
3795
+ "mayWrite": false
3796
+ },
3797
+ {
3798
+ "name": "impersonation",
3799
+ "claims": { "act": {
3800
+ "kind": "impersonation",
3801
+ "sub": "admin-1",
3802
+ "reason": "ticket 42"
3803
+ } },
3804
+ "valid": true,
3805
+ "kind": "impersonation",
3806
+ "readOnly": null,
3807
+ "tools": null,
3808
+ "mayWrite": false
3809
+ },
3810
+ {
3811
+ "name": "impersonation, read_only false",
3812
+ "claims": { "act": {
3813
+ "kind": "impersonation",
3814
+ "sub": "admin-1",
3815
+ "read_only": false
3816
+ } },
3817
+ "valid": true,
3818
+ "kind": "impersonation",
3819
+ "readOnly": null,
3820
+ "tools": null,
3821
+ "mayWrite": false
3822
+ },
3823
+ {
3824
+ "name": "agent, every tool",
3825
+ "claims": { "act": {
3826
+ "kind": "agent",
3827
+ "sub": "agent-7"
3828
+ } },
3829
+ "valid": true,
3830
+ "kind": "agent",
3831
+ "readOnly": false,
3832
+ "tools": null,
3833
+ "mayWrite": true
3834
+ },
3835
+ {
3836
+ "name": "agent, listed tools",
3837
+ "claims": { "act": {
3838
+ "kind": "agent",
3839
+ "sub": "agent-7",
3840
+ "tools": ["search_knowledge"]
3841
+ } },
3842
+ "valid": true,
3843
+ "kind": "agent",
3844
+ "readOnly": false,
3845
+ "tools": ["search_knowledge"],
3846
+ "mayWrite": true
3847
+ },
3848
+ {
3849
+ "name": "agent, no tools",
3850
+ "claims": { "act": {
3851
+ "kind": "agent",
3852
+ "sub": "agent-7",
3853
+ "tools": []
3854
+ } },
3855
+ "valid": true,
3856
+ "kind": "agent",
3857
+ "readOnly": false,
3858
+ "tools": [],
3859
+ "mayWrite": true
3860
+ },
3861
+ {
3862
+ "name": "agent, read_only true",
3863
+ "claims": { "act": {
3864
+ "kind": "agent",
3865
+ "sub": "agent-7",
3866
+ "read_only": true
3867
+ } },
3868
+ "valid": true,
3869
+ "kind": "agent",
3870
+ "readOnly": true,
3871
+ "tools": null,
3872
+ "mayWrite": false
3873
+ },
3874
+ {
3875
+ "name": "agent, read_only false",
3876
+ "claims": { "act": {
3877
+ "kind": "agent",
3878
+ "sub": "agent-7",
3879
+ "read_only": false
3880
+ } },
3881
+ "valid": true,
3882
+ "kind": "agent",
3883
+ "readOnly": false,
3884
+ "tools": null,
3885
+ "mayWrite": true
3886
+ },
3887
+ {
3888
+ "name": "agent, read_only as a string",
3889
+ "claims": { "act": {
3890
+ "kind": "agent",
3891
+ "sub": "agent-7",
3892
+ "read_only": "true"
3893
+ } },
3894
+ "valid": false,
3895
+ "kind": null,
3896
+ "readOnly": null,
3897
+ "tools": null,
3898
+ "mayWrite": false
3899
+ },
3900
+ {
3901
+ "name": "agent over an OAuth client",
3902
+ "claims": {
3903
+ "scope": "posts:read",
3904
+ "act": {
3905
+ "kind": "agent",
3906
+ "sub": "agent-7",
3907
+ "tools": ["search_knowledge"],
3908
+ "act": { "sub": "mcp-client-42" }
3909
+ }
3910
+ },
3911
+ "valid": true,
3912
+ "kind": "agent",
3913
+ "readOnly": false,
3914
+ "tools": ["search_knowledge"],
3915
+ "mayWrite": true
3916
+ },
3917
+ {
3918
+ "name": "agent, tools not a list",
3919
+ "claims": { "act": {
3920
+ "kind": "agent",
3921
+ "sub": "agent-7",
3922
+ "tools": "search_knowledge"
3923
+ } },
3924
+ "valid": false,
3925
+ "kind": null,
3926
+ "readOnly": null,
3927
+ "tools": null,
3928
+ "mayWrite": true
3929
+ },
3930
+ {
3931
+ "name": "agent, empty tool name",
3932
+ "claims": { "act": {
3933
+ "kind": "agent",
3934
+ "sub": "agent-7",
3935
+ "tools": [""]
3936
+ } },
3937
+ "valid": false,
3938
+ "kind": null,
3939
+ "readOnly": null,
3940
+ "tools": null,
3941
+ "mayWrite": true
3942
+ },
3943
+ {
3944
+ "name": "agent with session_id",
3945
+ "claims": { "act": {
3946
+ "kind": "agent",
3947
+ "sub": "agent-7",
3948
+ "session_id": "session-1"
3949
+ } },
3950
+ "valid": false,
3951
+ "kind": null,
3952
+ "readOnly": null,
3953
+ "tools": null,
3954
+ "mayWrite": true
3955
+ },
3956
+ {
3957
+ "name": "unknown kind",
3958
+ "claims": { "act": {
3959
+ "kind": "robot",
3960
+ "sub": "robot-1"
3961
+ } },
3962
+ "valid": false,
3963
+ "kind": null,
3964
+ "readOnly": null,
3965
+ "tools": null,
3966
+ "mayWrite": false
3967
+ },
3968
+ {
3969
+ "name": "act not an object",
3970
+ "claims": { "act": "agent-7" },
3971
+ "valid": false,
3972
+ "kind": null,
3973
+ "readOnly": null,
3974
+ "tools": null,
3975
+ "mayWrite": true
3976
+ },
3977
+ {
3978
+ "name": "act without sub",
3979
+ "claims": { "act": { "kind": "agent" } },
3980
+ "valid": false,
3981
+ "kind": null,
3982
+ "readOnly": null,
3983
+ "tools": null,
3984
+ "mayWrite": true
3985
+ },
3986
+ {
3987
+ "name": "nested level without sub",
3988
+ "claims": { "act": {
3989
+ "sub": "agent-runner",
3990
+ "act": { "sub": "" }
3991
+ } },
3992
+ "valid": false,
3993
+ "kind": null,
3994
+ "readOnly": null,
3995
+ "tools": null,
3996
+ "mayWrite": true
3997
+ }
3998
+ ]
3999
+ };
4000
+ //#endregion
4001
+ export { ConformanceError, LOCAL_JWT_SECRET, PROBE_INITIAL, actCases, asUser, createTestSigner, defineSeed, expectDbBudget, expectInstant, expectTenantIsolation, isSeed, liteStack, localAuth, localSigningKey, signLocalJwt, signTestJwt, signTestJwtWithKey, staticAuthorizer, supabaseClaimFixtures, supabaseInvalidClaimFixtures, testAdapter, testAiTaskRunner, testAuthResolver, testAuthorizationHooks, testAuthorizationProvider, testAuthorizer, testBlockTransportMiddleware, testBuilderStarter, testCacheAdapter, testChatState, testControllerBinding, testCredentialProvider, testDeliveryTransport, testDocumentFormat, testEmbedder, testEveDocumentBackend, testEventSink, testExecutor, testGenerator, testGraphCompiler, testIncomingDestination, testNotificationChannel, testPaymentProvider, testPlugin, testQueueBackend, testStreamStore, testSupportSessionStore, testTaxProvider, testWebhookSecretStore, testWebhookSigner, testWebhookTransport };