permdock 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (552) hide show
  1. package/README.md +37 -35
  2. package/dist/{openapi-schema--ARjyfQr.js → 2020-C9VN-x8c.js} +5 -132
  3. package/dist/a2a/index.d.ts +8 -6
  4. package/dist/a2a/index.js +21 -31
  5. package/dist/{adapter-context-mlWsVGGg.js → adapter-context-CRhqyzCk.js} +1 -1
  6. package/dist/adoption-scan-B4R5fnMU.js +107 -0
  7. package/dist/agent/index.d.ts +3 -0
  8. package/dist/agent/index.js +1 -0
  9. package/dist/ai-sdk/index.d.ts +18 -23
  10. package/dist/ai-sdk/index.js +38 -21
  11. package/dist/app-data-MuyzVqX8.js +60 -0
  12. package/dist/approval-header-CJACtHkv.js +23 -0
  13. package/dist/approval-prompt-Dg5LeAcT.js +62 -0
  14. package/dist/approval-prompt-Djo_PyZc.d.ts +57 -0
  15. package/dist/approvals/index.d.ts +5 -3
  16. package/dist/approvals/index.js +8 -5
  17. package/dist/approvals-DJ6Yd5z1.js +995 -0
  18. package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
  19. package/dist/arazzo-B6lebmiQ.js +36 -0
  20. package/dist/{arazzo-BAgw9ap6.js → arazzo-BWhNF-k0.js} +24 -17
  21. package/dist/args-IIolxxKA.js +197 -0
  22. package/dist/astro/index.d.ts +43 -0
  23. package/dist/astro/index.js +27 -0
  24. package/dist/audience-Dg8XsRaO.js +55 -0
  25. package/dist/auth-DRLEizbL.js +178 -0
  26. package/dist/authzen/index.d.ts +26 -6
  27. package/dist/authzen/index.js +228 -75
  28. package/dist/automation-BMZPIVUM.d.ts +114 -0
  29. package/dist/automation-DVjOckjV.js +157 -0
  30. package/dist/better-auth/index.d.ts +8 -6
  31. package/dist/better-auth/index.js +11 -23
  32. package/dist/better-supabase/index.d.ts +438 -86
  33. package/dist/better-supabase/index.js +1027 -184
  34. package/dist/bind--9EFVYWF.d.ts +38 -0
  35. package/dist/{bind-CuzGytRf.js → bind-CZzhx-Vj.js} +22 -10
  36. package/dist/body-CH5of8Ne.js +10 -0
  37. package/dist/body-text-QcXok_SI.js +52 -0
  38. package/dist/build-instance-BMnAudHn.js +52 -0
  39. package/dist/{canonical-json-DemiK4Vp.js → canonical-json-BC8_nMTw.js} +6 -6
  40. package/dist/{capability-2kYTMWDv.d.ts → capability-BSslw1QV.d.ts} +44 -25
  41. package/dist/{capability-4qPdO9gk.js → capability-N1K9IpqS.js} +69 -42
  42. package/dist/casbin-Cr7RLtA3.js +30 -0
  43. package/dist/casbin-Dyu2gnUD.js +234 -0
  44. package/dist/casl-64gpT-8x.js +24 -0
  45. package/dist/casl-DjEDhTCP.js +299 -0
  46. package/dist/catalog/index.d.ts +74 -2
  47. package/dist/catalog/index.js +51 -2
  48. package/dist/catalog-BECMPrwc.js +100 -0
  49. package/dist/catalog-DjhMYYCq.js +1 -0
  50. package/dist/catalog-R_A__RsH.js +52 -0
  51. package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-C9wjsfNY.js} +54 -25
  52. package/dist/catalog-fingerprint-CCP0-yQS.js +153 -0
  53. package/dist/chat/index.d.ts +2 -0
  54. package/dist/chat/index.js +224 -0
  55. package/dist/chat-sdk/index.d.ts +87 -0
  56. package/dist/chat-sdk/index.js +197 -0
  57. package/dist/claims-map-7oQ3ue4X.js +13 -0
  58. package/dist/claims-map-Ca-QX8C6.d.ts +32 -0
  59. package/dist/claude-agent/index.d.ts +7 -14
  60. package/dist/claude-agent/index.js +26 -30
  61. package/dist/clerk/index.d.ts +9 -8
  62. package/dist/clerk/index.js +15 -45
  63. package/dist/cli/bin.js +36 -17
  64. package/dist/cli/index.d.ts +20 -6
  65. package/dist/cli/index.js +164 -4
  66. package/dist/cloud/index.d.ts +102 -63
  67. package/dist/cloud/index.js +240 -347
  68. package/dist/cloud-CIoHNevm.js +87 -0
  69. package/dist/codemod-Bmj9dWMH.js +37 -0
  70. package/dist/codemod-Cxvvc70T.js +2084 -0
  71. package/dist/{collect-DSTE3ml9.js → collect-CrZJJnEF.js} +79 -17
  72. package/dist/collect-DloHbDVa.js +55 -0
  73. package/dist/collect-FlXJlhus.js +1 -0
  74. package/dist/command-L8EstFNm.js +6 -0
  75. package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
  76. package/dist/compile/index.d.ts +1 -1
  77. package/dist/compile/index.js +1 -2
  78. package/dist/{compile-Ba4blROR.js → compile-CMqMx_IN.js} +13 -9
  79. package/dist/{compile-BkRyBDBw.d.ts → compile-Ckm2HqBZ.d.ts} +5 -3
  80. package/dist/completion-BqhRBJD8.js +143 -0
  81. package/dist/config-B46rm9sc.js +26 -0
  82. package/dist/{config-BJiM-xAm.js → config-bs_kZeYq.js} +15 -10
  83. package/dist/{config-report-CdCgbRnV.js → config-report-DUgj62Ai.js} +17 -8
  84. package/dist/{connection-CU8MQxRi.js → connection-DjhIJSs2.js} +8 -6
  85. package/dist/constants-DcJABbLW.js +20 -0
  86. package/dist/{context-Bcy85rGi.js → context-bbFHR-0D.js} +16 -5
  87. package/dist/convex/index.d.ts +11 -10
  88. package/dist/convex/index.js +18 -18
  89. package/dist/create-0VZ7Pdiu.d.ts +107 -0
  90. package/dist/create-BDS7wc3y.d.ts +138 -0
  91. package/dist/create-BFfm17UA.d.ts +110 -0
  92. package/dist/create-BWRSXgOF.js +403 -0
  93. package/dist/create-C3b4B5eF.js +130 -0
  94. package/dist/create-DnrmLnLW.js +323 -0
  95. package/dist/{credential-8P81ff4u.js → credential-DM2um3ha.js} +153 -32
  96. package/dist/credentials-DPyHmYz5.js +74 -0
  97. package/dist/deciding-columns-Sq12mEA6.js +123 -0
  98. package/dist/delegation-DaXVdDhM.js +286 -0
  99. package/dist/delegation-input-FTxMK3ji.js +19 -0
  100. package/dist/delivery/index.d.ts +5 -0
  101. package/dist/delivery/index.js +5 -0
  102. package/dist/delivery-DCAgNyCn.js +499 -0
  103. package/dist/delivery-event-xX4obB8F.js +40 -0
  104. package/dist/denied-text-D8jDHgsl.d.ts +19 -0
  105. package/dist/{describe-DMYiCjJV.d.ts → describe-BZUsixiL.d.ts} +11 -2
  106. package/dist/{describe-B_gVZEVi.js → describe-BybMRY7A.js} +10 -5
  107. package/dist/device-BARQ8eYG.js +193 -0
  108. package/dist/diff-CxZo9pTq.js +37 -0
  109. package/dist/{diff-2VaeChzX.js → diff-DCePiN6M.js} +61 -55
  110. package/dist/doctor-CxiwSfqH.js +44 -0
  111. package/dist/doctor-DFjUxf2f.js +2361 -0
  112. package/dist/drizzle/index.d.ts +5 -3
  113. package/dist/drizzle/index.js +6 -14
  114. package/dist/elysia/index.d.ts +16 -13
  115. package/dist/elysia/index.js +14 -13
  116. package/dist/env-DJZrPjRi.js +8 -0
  117. package/dist/errors-BDUwg2qC.js +27 -0
  118. package/dist/{errors-Cz5zn0mW.js → errors-BKy0EfhN.js} +6 -10
  119. package/dist/errors-BWXBMaKK.d.ts +11 -0
  120. package/dist/{errors-DPdqI50v.d.ts → errors-C2PXIv4c.d.ts} +2 -2
  121. package/dist/errors-CWTY6uih.js +143 -0
  122. package/dist/evaluate-_R8QkWaf.js +1732 -0
  123. package/dist/evaluations-16UqqQ6W.d.ts +32 -0
  124. package/dist/{evaluations-tz6D9A2G.js → evaluations-DZ2CbV5k.js} +181 -32
  125. package/dist/eve/index.d.ts +12 -16
  126. package/dist/eve/index.js +23 -29
  127. package/dist/event-time-CsegxXxB.js +23 -0
  128. package/dist/expiry-D4_YKRKD.js +52 -0
  129. package/dist/express/index.d.ts +13 -10
  130. package/dist/express/index.js +14 -12
  131. package/dist/fastify/index.d.ts +26 -13
  132. package/dist/fastify/index.js +17 -36
  133. package/dist/fetch-adapter-g0ypLkID.js +120 -0
  134. package/dist/fetch-adapter-qKJBuRqy.d.ts +31 -0
  135. package/dist/fields-D9tPVUUS.js +203 -0
  136. package/dist/files-C5ZI27eV.js +124 -0
  137. package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
  138. package/dist/{fixtures-ht28Cplr.js → fixtures-B7XrLeY7.js} +12 -12
  139. package/dist/flags-C3DktBDY.js +86 -0
  140. package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-D2lZt4Dd.js} +78 -65
  141. package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
  142. package/dist/grantee-Cn1HyUsI.js +254 -0
  143. package/dist/grantee-conditions-CcXf8hMM.js +271 -0
  144. package/dist/grantee-match-DHArqY82.js +142 -0
  145. package/dist/h3/index.d.ts +37 -0
  146. package/dist/h3/index.js +35 -0
  147. package/dist/headers-BIjfMaoC.js +8 -0
  148. package/dist/headers-CIbDTUTK.d.ts +7 -0
  149. package/dist/{helpers-H_KARDVC.js → helpers-DTcZOzhF.js} +47 -39
  150. package/dist/hono/index.d.ts +12 -9
  151. package/dist/hono/index.js +14 -13
  152. package/dist/{hosted-DJQw46hn.js → hosted-BZIAYvon.js} +64 -15
  153. package/dist/hosted-DBDX0M-P.d.ts +78 -0
  154. package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
  155. package/dist/{http-DPuDaChb.d.ts → http-B_LphE5Y.d.ts} +1 -2
  156. package/dist/http-DXJ37RDA.js +46 -0
  157. package/dist/{http-BIhmLfCc.js → http-ujKm0JXE.js} +3 -26
  158. package/dist/{openapi-import-cmwetbzw.js → import-CNV7I2Dw.js} +30 -26
  159. package/dist/{rls-import-4lLW_pbb.js → import-DMmOG27O.js} +271 -128
  160. package/dist/index-BcHcPyIo.d.ts +110 -0
  161. package/dist/index-CaxrMD1A.d.ts +1 -0
  162. package/dist/index-Chrs5fQ-.d.ts +210 -0
  163. package/dist/index-CxGUswIC.d.ts +431 -0
  164. package/dist/index-D8EovfsP.d.ts +91 -0
  165. package/dist/index-DOVf7RJv.d.ts +900 -0
  166. package/dist/{index-C8z4RjQV.d.ts → index-SGdIQyzc.d.ts} +34 -16
  167. package/dist/index.d.ts +20 -14
  168. package/dist/index.js +33 -23
  169. package/dist/init-DeMYsnYJ.js +96 -0
  170. package/dist/init-GG6Jh8HD.js +95 -0
  171. package/dist/{instance-options-jARdwfPN.d.ts → instance-options-Omfd793B.d.ts} +2 -2
  172. package/dist/instance-snapshot-Cxq4ygFc.js +600 -0
  173. package/dist/instant-BluqDvyS.js +19 -0
  174. package/dist/internals-Dk8A8fX3.js +17 -0
  175. package/dist/{json-schema-BsAflUGW.js → json-schema-B-vAwlde.js} +46 -9
  176. package/dist/json-schema-CN_iLFH2.d.ts +40 -0
  177. package/dist/jwt/index.d.ts +18 -11
  178. package/dist/jwt/index.js +166 -145
  179. package/dist/{kernel-_RRHmnMY.js → kernel-5JatXyND.js} +120 -43
  180. package/dist/kysely/index.d.ts +5 -3
  181. package/dist/kysely/index.js +6 -14
  182. package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
  183. package/dist/load-BxEtwlBS.js +1 -0
  184. package/dist/{load-DhWEB9P1.js → load-DFS_ry0W.js} +31 -7
  185. package/dist/{load-jose-BL6LH4bL.js → load-jose-DZ4dnjri.js} +1 -4
  186. package/dist/login-Dfe5RlYk.js +68 -0
  187. package/dist/login-imhuwvn3.js +254 -0
  188. package/dist/manifest-C8IzW3Do.js +42 -0
  189. package/dist/manifest-CtKR3GXL.js +370 -0
  190. package/dist/manifest-json-CAGzls-C.js +9 -0
  191. package/dist/{manifest-uDS9y5de.d.ts → manifest-mx5ghSFM.d.ts} +9 -2
  192. package/dist/manifest-schema-C0kK_8V0.js +399 -0
  193. package/dist/{map-claims-Z4QZOiFF.js → map-claims-D3oRmTk0.js} +19 -25
  194. package/dist/{map-error-pgUWNNtG.js → map-error-TCAdw-eW.js} +5 -5
  195. package/dist/{markers-B0hYLvMD.js → markers-C6joDI3o.js} +8 -3
  196. package/dist/mcp/index.d.ts +36 -9
  197. package/dist/mcp/index.js +476 -448
  198. package/dist/{rls-migrate-CN4TlJTO.js → migrate-BShP7Vf4.js} +119 -20
  199. package/dist/nest/index.d.ts +12 -9
  200. package/dist/nest/index.js +20 -20
  201. package/dist/next/index.d.ts +40 -20
  202. package/dist/next/index.js +37 -39
  203. package/dist/next/plugin.d.ts +1 -1
  204. package/dist/next/plugin.js +2 -2
  205. package/dist/node/index.d.ts +10 -10
  206. package/dist/node/index.js +7 -9
  207. package/dist/ocsf-BFp-PZZA.js +126 -0
  208. package/dist/openai/index.d.ts +9 -16
  209. package/dist/openai/index.js +18 -25
  210. package/dist/openapi/index.d.ts +3 -3
  211. package/dist/openapi/index.js +1 -1
  212. package/dist/{openapi-BqRBvAng.js → openapi-B7HZ0f-r.js} +9 -12
  213. package/dist/{openapi-CqqEbHsL.js → openapi-DTaEmTSW.js} +23 -26
  214. package/dist/openapi-ahr8rHu3.js +138 -0
  215. package/dist/operation-findings-DgATDpvh.js +36 -0
  216. package/dist/operations-CxybZErY.d.ts +59 -0
  217. package/dist/orpc/index.d.ts +13 -10
  218. package/dist/orpc/index.js +23 -20
  219. package/dist/otel/index.d.ts +3 -2
  220. package/dist/otel/index.js +53 -27
  221. package/dist/output-Dyw8fyPF.js +88 -0
  222. package/dist/output-files-B3IZJksC.js +1 -0
  223. package/dist/output-files-DhRBBE4n.js +138 -0
  224. package/dist/{rls-fields-CZOMlwHl.js → ownership-BatFgAA4.js} +2393 -2349
  225. package/dist/{package-root-zGkxO3oN.js → package-root-Dk_ETdkp.js} +4 -3
  226. package/dist/{parse-BuyjxhZV.js → parse-Cxq0WxrA.js} +3 -3
  227. package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-CVSs6J9I.js} +2 -4
  228. package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DgqtruPn.d.ts} +1 -1
  229. package/dist/parse-request-D_tUXG6M.js +26 -0
  230. package/dist/paths-DXSHAMIa.js +42 -0
  231. package/dist/pdp/index.d.ts +7 -3
  232. package/dist/pdp/index.js +47 -29
  233. package/dist/{peer-BOr_kJE1.js → peer-C8swJ2GH.js} +23 -4
  234. package/dist/peer-CRvC7Hg1.js +1 -0
  235. package/dist/permdock-B9JxkBwC.js +868 -0
  236. package/dist/permdock-D7eeLJER.d.ts +502 -0
  237. package/dist/permission-registry-Br4jSNVi.js +385 -0
  238. package/dist/permissions-B-wCP5ZC.js +30 -0
  239. package/dist/{permissions-RJrovw98.js → permissions-CZKiUfFM.js} +4 -3
  240. package/dist/{permissions-CDgvKG5L.d.ts → permissions-oEWfl-kl.d.ts} +1 -2
  241. package/dist/policies-CO1oOymN.js +295 -0
  242. package/dist/{policy-D2W7uv19.d.ts → policy-Czo4n3Hs.d.ts} +2 -2
  243. package/dist/policy-DLZJ5gAp.js +399 -0
  244. package/dist/policy-from-catalog-DzhEc3Y9.js +450 -0
  245. package/dist/policy-module-CTlwMvLI.js +89 -0
  246. package/dist/{policy-IB6yxpC3.d.ts → policy-types-DzwivBgR.d.ts} +939 -1057
  247. package/dist/postgrest-CW0IqC-U.js +566 -0
  248. package/dist/postgrest-NUqZT-RB.d.ts +100 -0
  249. package/dist/powersync-BHfuehf3.js +62 -0
  250. package/dist/powersync-CbzkqL_M.js +1 -0
  251. package/dist/{powersync-Dv1_aWF7.js → powersync-bYUDLOAO.js} +30 -32
  252. package/dist/preset/index.d.ts +146 -0
  253. package/dist/preset/index.js +1 -0
  254. package/dist/preset-DZdU0tne.js +1768 -0
  255. package/dist/principal-source-pHneukrx.js +103 -0
  256. package/dist/prisma/index.d.ts +6 -3
  257. package/dist/prisma/index.js +6 -12
  258. package/dist/{problem-B43ztKIm.js → problem-CTviyH5L.js} +18 -27
  259. package/dist/{problem-details-DX075l9V.d.ts → problem-details-DXlt-vks.d.ts} +1 -1
  260. package/dist/{problem-details-_AMOYz-w.js → problem-details-cn5X30gZ.js} +45 -42
  261. package/dist/problem-types-CJ2325SZ.js +5 -0
  262. package/dist/{prompt-CXYhLfsS.js → prompt-DfZLJLIn.js} +2 -2
  263. package/dist/protected-DEPHc2xV.d.ts +38 -0
  264. package/dist/{protected-utU-SLuy.js → protected-DfjY5mLA.js} +37 -8
  265. package/dist/{provider-CSQd7m92.js → provider-CC-u3bYP.js} +23 -10
  266. package/dist/{provider-DZY281jO.d.ts → provider-DMVt6yYn.d.ts} +1 -1
  267. package/dist/{provider-store-Cfbvo4DE.js → provider-store-B5Yrkhzr.js} +6 -3
  268. package/dist/pull-BgIfno6w.js +256 -0
  269. package/dist/pull-ByOks_Tg.js +1 -0
  270. package/dist/push-CmFVBOeI.js +116 -0
  271. package/dist/pushed-policy-BImMTTIM.d.ts +57 -0
  272. package/dist/pushed-policy-DobP7AHI.js +238 -0
  273. package/dist/react/index.d.ts +8 -8
  274. package/dist/react/index.js +6 -6
  275. package/dist/react/provider-client.d.ts +1 -1
  276. package/dist/react/provider-client.js +1 -2
  277. package/dist/react-native/index.d.ts +24 -63
  278. package/dist/react-native/index.js +45 -27
  279. package/dist/{refs-KYb4ntaz.js → refs-K6eWUJ0H.js} +1 -1
  280. package/dist/relation-walk-3OyDBF0S.js +186 -0
  281. package/dist/relations-BnCWnJGI.js +163 -0
  282. package/dist/renames-CGj7YwcQ.js +175 -0
  283. package/dist/{replay-CYyiLiwO.d.ts → replay-D7iKcLFe.d.ts} +1 -1
  284. package/dist/{replay-BsOyTrdV.js → replay-Du1tJjG7.js} +2 -1
  285. package/dist/request-context-BTRTG7OT.js +71 -0
  286. package/dist/request-context-C2_1lchh.d.ts +5 -0
  287. package/dist/request-schema-CMreVuFM.js +148 -0
  288. package/dist/resend/index.d.ts +126 -0
  289. package/dist/resend/index.js +379 -0
  290. package/dist/resource-relations-D_jw0BFx.js +243 -0
  291. package/dist/retire-CALjIQ7e.js +240 -0
  292. package/dist/{revocations-Cy06JNx2.d.ts → revocations-R07lFBnX.d.ts} +2 -0
  293. package/dist/rls-BA8an5Ap.js +336 -0
  294. package/dist/{rls-DiinlRML.js → rls-j6t43t7n.js} +1066 -1068
  295. package/dist/role-builder-CXbis5-X.js +623 -0
  296. package/dist/{rpc-DL9Sqvb1.js → rpc-aPyNi6tA.js} +18 -8
  297. package/dist/run-CNsApDpd.js +1 -0
  298. package/dist/run-DOfMtc9V.js +723 -0
  299. package/dist/run-ticket-BjhTOi6u.d.ts +124 -0
  300. package/dist/run-ticket-sxFI6LAk.js +337 -0
  301. package/dist/{runtime-CB8gQFvS.js → runtime-D3VTAiCA.js} +1 -1
  302. package/dist/{saas-DhpJjnBA.js → saas-FO-Q8oj0.js} +18 -15
  303. package/dist/scan-BOI5tJ-6.js +29 -0
  304. package/dist/{schema-D3MnDMxa.js → schema-B4v0Qrac.js} +106 -10
  305. package/dist/schema-yLuBWWsh.js +130 -0
  306. package/dist/scim/index.d.ts +2 -2
  307. package/dist/scim/index.js +1 -2
  308. package/dist/{scim-BFGBBAqR.js → scim-ClHtpyV3.js} +213 -87
  309. package/dist/{scopes-CVdPrWN-.js → scopes-CUPSjo9q.js} +20 -7
  310. package/dist/server/index.d.ts +13 -134
  311. package/dist/server/index.js +13 -10
  312. package/dist/server-DqCT4CgZ.js +453 -0
  313. package/dist/{create-B1d519XK.d.ts → server-types-BLd4MnBX.d.ts} +89 -70
  314. package/dist/{sha256-BO0swRXL.js → sha256-D9ihqmvd.js} +8 -1
  315. package/dist/shared-CD11nBbC.js +304 -0
  316. package/dist/{shared-DRu8VFVM.js → shared-DvvFzYXe.js} +8 -9
  317. package/dist/signals-D3NcTKUS.js +32 -0
  318. package/dist/sink-BEZSlMS_.js +957 -0
  319. package/dist/sink-Bj_lDUDq.d.ts +225 -0
  320. package/dist/{skills-Biq5uup3.js → skills-B6lVdD_W.js} +16 -8
  321. package/dist/skills-CTYwkl44.js +51 -0
  322. package/dist/snapshot-CXUWfsNy.js +269 -0
  323. package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-DZHQ0KF6.js} +11 -5
  324. package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-DqK9eDmM.d.ts} +7 -5
  325. package/dist/solid/index.d.ts +24 -25
  326. package/dist/solid/index.js +41 -11
  327. package/dist/solid-start/index.d.ts +52 -0
  328. package/dist/solid-start/index.js +49 -0
  329. package/dist/{sources-BdOYLw9j.d.ts → sources-BV9j0DeZ.d.ts} +11 -13
  330. package/dist/{sources-4p0-4xQm.js → sources-vs5dGOsS.js} +147 -142
  331. package/dist/sql-B5T1IL83.js +55 -0
  332. package/dist/src-DLfUWFAI.js +1851 -0
  333. package/dist/ssf/index.d.ts +4 -4
  334. package/dist/ssf/index.js +440 -106
  335. package/dist/standard-webhooks-6lg8v_is.d.ts +88 -0
  336. package/dist/standard-webhooks-DgFFD-Ga.js +228 -0
  337. package/dist/storage-C2bWdvJH.js +132 -0
  338. package/dist/{store-DMAWwAJV.js → store-4u_hxjWa.js} +42 -55
  339. package/dist/{store-options-ByhZq1jV.js → store-options-BqEakYRa.js} +38 -24
  340. package/dist/{stores-BT2pSCub.js → stores-BbsD4fAR.js} +7 -3
  341. package/dist/stores-ClZUgeoP.d.ts +30 -0
  342. package/dist/{stream-D1RXubw2.d.ts → stream-7-xmnf3U.d.ts} +2 -2
  343. package/dist/{stream-DDpvynSB.js → stream-CvdUS9V1.js} +10 -2
  344. package/dist/style-DiKWnAvn.js +1 -0
  345. package/dist/{style-CTm1AXDT.js → style-Uao76wO7.js} +3 -2
  346. package/dist/subject-BGoM5o8B.js +17 -0
  347. package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
  348. package/dist/supabase/index.d.ts +23 -106
  349. package/dist/supabase/index.js +5 -4
  350. package/dist/supabase/middleware.d.ts +13 -6
  351. package/dist/supabase/middleware.js +16 -11
  352. package/dist/supabase-9pqI57pW.js +92 -0
  353. package/dist/{supabase-C0qESOa-.js → supabase-Dxlp8A94.js} +124 -247
  354. package/dist/supabase-hook-CXcGpHqq.js +1 -0
  355. package/dist/{supabase-hook-ntETYCQu.js → supabase-hook-DdDQhMlw.js} +1659 -819
  356. package/dist/{supautils-u8jXTGrt.js → supautils-B6HYL9eL.js} +2 -2
  357. package/dist/support-session-C6xJQYut.js +158 -0
  358. package/dist/svelte/Protected.svelte +11 -4
  359. package/dist/svelte/index.d.ts +7 -7
  360. package/dist/svelte/index.js +26 -12
  361. package/dist/svelte/runtime.d.ts +10 -3
  362. package/dist/svelte/runtime.js +4 -3
  363. package/dist/svelte/source.d.ts +7 -7
  364. package/dist/svelte/source.js +5 -5
  365. package/dist/sveltekit/index.d.ts +51 -0
  366. package/dist/sveltekit/index.js +27 -0
  367. package/dist/tanstack-start/index.d.ts +51 -0
  368. package/dist/tanstack-start/index.js +40 -0
  369. package/dist/target-Bw37TFNA.js +51 -0
  370. package/dist/{validity-uOp8UpYn.js → tenancy-DN2QAx9z.js} +6 -42
  371. package/dist/terminal/index.d.ts +11 -9
  372. package/dist/terminal/index.js +36 -316
  373. package/dist/testing/index.d.ts +391 -106
  374. package/dist/testing/index.js +4574 -1286
  375. package/dist/testing/saas/index.d.ts +9 -8
  376. package/dist/testing/saas/index.js +2 -2
  377. package/dist/testing/saas/permissions.d.ts +1 -1
  378. package/dist/testing/saas/permissions.js +1 -2
  379. package/dist/text-BMFRfBqc.js +8 -0
  380. package/dist/{text-diff-BdkkYSbi.js → text-diff-BQcUapOb.js} +1 -1
  381. package/dist/thenable-yD7SabqG.js +31 -0
  382. package/dist/time-DkWjdL98.js +71 -0
  383. package/dist/{token-CIWxJOCh.js → token-BLZcjQgt.js} +15 -6
  384. package/dist/tool-refusal-CMLN5xhn.js +41 -0
  385. package/dist/trpc/index.d.ts +12 -9
  386. package/dist/trpc/index.js +18 -17
  387. package/dist/{types-DbjRN55U.d.ts → types-5Ut_pbKy.d.ts} +141 -13
  388. package/dist/types-BNORmDM8.d.ts +172 -0
  389. package/dist/types-BjV-fqcA.d.ts +57 -0
  390. package/dist/{types-QCuYJh4q.d.ts → types-BqAKcRNW.d.ts} +2 -1
  391. package/dist/types-C8-KNsFf.d.ts +107 -0
  392. package/dist/{types-aqOHTHcp.d.ts → types-CCpc2Dcv.d.ts} +77 -17
  393. package/dist/{types-Dnemwd77.d.ts → types-CZTnPFzM.d.ts} +50 -11
  394. package/dist/{types-C97IskBw.d.ts → types-CdlxsYQh.d.ts} +23 -11
  395. package/dist/{types-DxiCGmmd.d.ts → types-ClZaWzfb.d.ts} +9 -6
  396. package/dist/types-DX0LaKTb.d.ts +157 -0
  397. package/dist/types-DiQjupG2.d.ts +332 -0
  398. package/dist/types-DqzsnlSA.js +67 -0
  399. package/dist/types-_sHy8BQf.d.ts +65 -0
  400. package/dist/types-fbZ7JEXr.d.ts +89 -0
  401. package/dist/types-tIgIeIKJ.d.ts +66 -0
  402. package/dist/unplugin/index.d.ts +1 -1
  403. package/dist/unplugin/index.js +2 -2
  404. package/dist/usage-CHOtwL19.js +1 -0
  405. package/dist/usage-DHtE8j4E.js +32 -0
  406. package/dist/{usage-ItS7nLcl.js → usage-T5mXq98v.js} +56 -13
  407. package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-Bg5Tpluk.d.ts} +1 -1
  408. package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
  409. package/dist/validity-CZ7-y0Cj.js +79 -0
  410. package/dist/{verifier-BxBo6I44.js → verifier-DHuh-okk.js} +68 -21
  411. package/dist/verify-DbCL9bnB.js +1770 -0
  412. package/dist/{version-BvacVTZR.js → version-CEbQ5UeM.js} +4 -3
  413. package/dist/version-DXkYUISx.js +1 -0
  414. package/dist/{views-Soe7bGSC.js → views-DTIIO-oQ.js} +41 -2
  415. package/dist/{permissions-DttnqqxC.d.ts → vocabulary-D6lPdb4Z.d.ts} +120 -29
  416. package/dist/vue/index.d.ts +25 -21
  417. package/dist/vue/index.js +25 -9
  418. package/dist/waiter-BGUtGYoU.js +67 -0
  419. package/dist/waiter-CKy4MQSY.d.ts +36 -0
  420. package/dist/watch-Cb-NcaUw.js +1 -0
  421. package/dist/{watch-DVBJ9cBq.js → watch-CszoTJMq.js} +47 -8
  422. package/dist/webmcp/index.d.ts +2 -2
  423. package/dist/webmcp/index.js +13 -37
  424. package/dist/where-scope-Dv-WTP-w.js +306 -0
  425. package/dist/who-can-D0QoYbEb.js +364 -0
  426. package/dist/wire-delegation-Cv3W2eaM.js +36 -0
  427. package/dist/wire-denial-C7PaVYEM.js +27 -0
  428. package/dist/workflow/index.d.ts +5 -0
  429. package/dist/workflow/index.js +223 -0
  430. package/dist/workflow-sdk/index.d.ts +56 -0
  431. package/dist/workflow-sdk/index.js +78 -0
  432. package/dist/wrap-Bd1Tejur.d.ts +15 -0
  433. package/dist/wrap-gX61EtpB.js +27 -0
  434. package/package.json +140 -41
  435. package/schemas/access-event-v1.json +118 -0
  436. package/schemas/approval-request-v1.json +34 -1
  437. package/schemas/approval-signature-v1.json +94 -0
  438. package/schemas/automation-event-v1.json +113 -0
  439. package/schemas/capability-v1.json +84 -0
  440. package/schemas/catalog-event-v1.json +87 -0
  441. package/schemas/catalog-v1.json +129 -12
  442. package/schemas/cli-commands-v1.json +164 -0
  443. package/schemas/cli-result-v1.json +96 -0
  444. package/schemas/cloud-api-v1.json +5407 -0
  445. package/schemas/connector-v1.json +408 -0
  446. package/schemas/credential-event-v1.json +81 -0
  447. package/schemas/credential-v1.json +134 -0
  448. package/schemas/decision-event-v1.json +288 -0
  449. package/schemas/delivery-event-v1.json +117 -0
  450. package/schemas/directory-event-v1.json +85 -0
  451. package/schemas/evidence-export-v1.json +54 -0
  452. package/schemas/membership-event-v1.json +93 -0
  453. package/schemas/policy-v1.json +68 -0
  454. package/schemas/pushed-policy-v1.json +236 -0
  455. package/schemas/run-v1.json +162 -0
  456. package/schemas/snapshot-v1.json +138 -0
  457. package/schemas/supabase-claims-v1.json +4 -4
  458. package/schemas/supabase-manifest-v1.json +5 -1
  459. package/schemas/support-session-v1.json +111 -0
  460. package/skills/permdock/SKILL.md +16 -12
  461. package/skills/permdock-agents/SKILL.md +40 -23
  462. package/skills/permdock-agents/references/operations-automations-chat.md +221 -0
  463. package/skills/permdock-agents/references/runtimes.md +22 -18
  464. package/skills/permdock-approvals/SKILL.md +29 -13
  465. package/skills/permdock-approvals/references/delivery-and-waiters.md +172 -0
  466. package/skills/permdock-approvals/references/stores-and-resume.md +21 -7
  467. package/skills/permdock-audit/SKILL.md +3 -3
  468. package/skills/permdock-credentials/SKILL.md +5 -5
  469. package/skills/permdock-credentials/references/tokens-keys-links.md +19 -10
  470. package/skills/permdock-data/SKILL.md +6 -6
  471. package/skills/permdock-data/references/queries-and-rls.md +13 -8
  472. package/skills/permdock-data/references/relationships.md +3 -3
  473. package/skills/permdock-tenancy/SKILL.md +20 -7
  474. package/skills/permdock-tenancy/references/contacts-and-support-sessions.md +122 -0
  475. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +6 -4
  476. package/skills/permdock-wire/SKILL.md +18 -12
  477. package/skills/permdock-wire/references/adapters.md +146 -50
  478. package/skills/permdock-wire/references/policy-options.md +15 -6
  479. package/skills/permdock-wire/references/presets-and-recipes.md +58 -0
  480. package/dist/approvals-DbYAuNkG.js +0 -224
  481. package/dist/arazzo-C9chp3ST.js +0 -54
  482. package/dist/bind-C_ob3LKa.d.ts +0 -25
  483. package/dist/catalog-CRW5Mx_B.js +0 -2
  484. package/dist/catalog-CYj-mX2O.js +0 -48
  485. package/dist/catalog-DWF5cYqB.js +0 -55
  486. package/dist/cloud-CfwZ39Rl.js +0 -50
  487. package/dist/cloud-DWLXO2af.js +0 -118
  488. package/dist/collect-CHB22Sx4.js +0 -83
  489. package/dist/collect-DSWOk19R.js +0 -2
  490. package/dist/commands-BeaGNj_o.js +0 -97
  491. package/dist/config-ir40y8m0.js +0 -36
  492. package/dist/create-CMwIl-w2.js +0 -253
  493. package/dist/delegation-D8pUEuce.js +0 -103
  494. package/dist/diff-C9RDjjhs.js +0 -48
  495. package/dist/doctor-Djh6JDEk.js +0 -53
  496. package/dist/doctor-DvH0IQff.js +0 -1194
  497. package/dist/doctor-collect-CjdO0wQX.js +0 -665
  498. package/dist/duration-xK2jGFVc.js +0 -25
  499. package/dist/errors-CWfoNg9N.js +0 -47
  500. package/dist/errors-DRnGZHzF.js +0 -30
  501. package/dist/evaluation-items-CfCCejQd.js +0 -40
  502. package/dist/expiry-BoBJ8Tvc.js +0 -7
  503. package/dist/fields-BMl1RxoX.js +0 -26
  504. package/dist/grantee-Cv5Sogfv.js +0 -602
  505. package/dist/headers-BZSDieNT.js +0 -6
  506. package/dist/headers-Cf-zWvsO.d.ts +0 -6
  507. package/dist/ids-DC4V4CFu.js +0 -18
  508. package/dist/index-0nNqHspG.d.ts +0 -6
  509. package/dist/index-BNc7Wer_.d.ts +0 -483
  510. package/dist/index-C7Tw7MaW.d.ts +0 -201
  511. package/dist/instance-options-Dhhcnm3y.js +0 -22
  512. package/dist/load-DwJUafYN.js +0 -2
  513. package/dist/manifest-iaoBXHKc.js +0 -783
  514. package/dist/map-AcxK9o_D.js +0 -147
  515. package/dist/may-use-DndQ5tQ-.js +0 -77
  516. package/dist/openapi-CQTsgKoB.js +0 -164
  517. package/dist/permdock-CbgvLnuo.js +0 -3479
  518. package/dist/permissions-DVoF6dMt.js +0 -662
  519. package/dist/policy-xyWg7QdR.js +0 -1122
  520. package/dist/powersync-CrBX8hY6.js +0 -2
  521. package/dist/powersync-DE19b9dT.js +0 -62
  522. package/dist/protected-BdnIEFkY.d.ts +0 -27
  523. package/dist/protected-xAr24FEC.d.ts +0 -40
  524. package/dist/relations-cjOL01hZ.js +0 -505
  525. package/dist/rls-P-BeBx0n.js +0 -238
  526. package/dist/rls-graph-CHwx0V8q.js +0 -435
  527. package/dist/rls-ownership-DcYlZ09B.js +0 -843
  528. package/dist/rls-shared-Cewd_-zI.js +0 -495
  529. package/dist/rls-verify-CMv9H3wV.js +0 -746
  530. package/dist/run-BWc9qqvE.js +0 -174
  531. package/dist/server-DlZ28Oz5.js +0 -268
  532. package/dist/sink-B1fZzIMf.d.ts +0 -104
  533. package/dist/sink-Bd9PSyfq.js +0 -141
  534. package/dist/skills-C5CbhhpG.js +0 -56
  535. package/dist/snapshot-Enxd0n-s.js +0 -975
  536. package/dist/sql-xdg5f4Fi.js +0 -27
  537. package/dist/src-BDwEZWrZ.js +0 -568
  538. package/dist/store-jOBYpiXC.d.ts +0 -36
  539. package/dist/stores-C0IMQrGI.d.ts +0 -52
  540. package/dist/subject-C6dLxqHU.js +0 -24
  541. package/dist/supabase-config-Bj3iDH2I.js +0 -472
  542. package/dist/supabase-hook-gd3s8bxB.js +0 -2
  543. package/dist/supabase-s4o9s1O1.js +0 -83
  544. package/dist/thenable-okc2xDsj.js +0 -10
  545. package/dist/types-BnIEz1sU.d.ts +0 -48
  546. package/dist/types-Bvc4oWT6.d.ts +0 -9
  547. package/dist/types-CKNSyIf_.d.ts +0 -52
  548. package/dist/types-Cq_H8N_X.d.ts +0 -197
  549. package/dist/usage-B0YuTQRO.js +0 -42
  550. package/dist/usage-DorAJiU_.js +0 -2
  551. package/dist/vocabulary-DA8a8zif.d.ts +0 -56
  552. package/dist/watch-BwxLTYMZ.js +0 -2
@@ -1,15 +1,647 @@
1
+ import { r as isRecord, t as compact } from "../compact-CAf557ZB.js";
1
2
  import { n as freezeDeep } from "../freeze-CrU8nQeX.js";
2
- import { _ as listPermissions, p as isPermission } from "../permissions-DVoF6dMt.js";
3
- import { d as scopeList } from "../scopes-CVdPrWN-.js";
4
- import { t as anonymousSubject } from "../subject-C6dLxqHU.js";
5
- import { t as mayUse } from "../may-use-DndQ5tQ-.js";
6
- import { o as parseCredential, r as credentialSubject } from "../credential-8P81ff4u.js";
3
+ import { l as asApprover } from "../role-builder-CXbis5-X.js";
4
+ import { a as holder, i as flattenApprovers, r as approverLeaves } from "../approvers-CqPOLD-o.js";
5
+ import { t as loadPrincipal } from "../principal-source-pHneukrx.js";
6
+ import { a as isId, r as idList } from "../expiry-D4_YKRKD.js";
7
+ import { c as listPermissions, n as findPermission, o as isPermission } from "../permission-registry-Br4jSNVi.js";
8
+ import { i as dateAt, m as rfc3339, p as nowSeconds, u as isoAt } from "../time-DkWjdL98.js";
9
+ import { t as canonicalJson } from "../canonical-json-BC8_nMTw.js";
10
+ import { r as sha256Digest } from "../sha256-D9ihqmvd.js";
11
+ import { t as decideSupportSession } from "../support-session-C6xJQYut.js";
12
+ import { d as scopeList } from "../scopes-CUPSjo9q.js";
13
+ import { o as flattenGrantee } from "../grantee-Cn1HyUsI.js";
14
+ import { t as byCodePoint } from "../compare-pfyVnBjE.js";
15
+ import { o as scopesReaching, t as getPermDock } from "../permdock-B9JxkBwC.js";
16
+ import { t as anonymousSubject } from "../subject-BGoM5o8B.js";
17
+ import { i as linkPolicyViolation, n as capabilityOf, r as capabilitySubject } from "../capability-N1K9IpqS.js";
18
+ import { r as credentialSubject, s as parseCredential } from "../credential-DM2um3ha.js";
19
+ import { t as AccessReviewError } from "../errors-BDUwg2qC.js";
7
20
  import { t as policyRowConditionKeys } from "../row-conditions-h2HythXQ.js";
8
- import { i as quoteSqlLiteral, o as sqlIdent } from "../sql-xdg5f4Fi.js";
9
- import "../sources-4p0-4xQm.js";
10
- import { o as subjectFromSupabaseSession, t as parseSupabaseManifest } from "../manifest-iaoBXHKc.js";
11
- import { t as parseCatalog } from "../parse-BuyjxhZV.js";
21
+ import { n as subscribersFor } from "../audience-Dg8XsRaO.js";
22
+ import { l as sqlIdent, s as quoteSqlLiteral } from "../sql-B5T1IL83.js";
23
+ import "../sources-vs5dGOsS.js";
24
+ import { c as subjectFromSupabaseSession, t as callRpc } from "../postgrest-CW0IqC-U.js";
25
+ import { t as parseSupabaseManifest } from "../manifest-C8IzW3Do.js";
26
+ import { t as parseCatalog } from "../parse-Cxq0WxrA.js";
12
27
  import { AsyncResult, dbError } from "better-supabase";
28
+ //#region src/better-supabase/access-reviews.ts
29
+ const NOT_MEMBER = "ORGANIZATION_NOT_MEMBER";
30
+ /** The `hint` better-supabase's block functions raise their code in, or the SQLSTATE. */
31
+ function errorCode(error) {
32
+ if ("hint" in error && typeof error.hint === "string" && error.hint !== "") return error.hint;
33
+ return "code" in error && typeof error.code === "string" ? error.code : void 0;
34
+ }
35
+ var RpcFailure = class extends Error {
36
+ code;
37
+ constructor(fn, error) {
38
+ super(`${fn}: ${error.message}`);
39
+ this.code = errorCode(error);
40
+ }
41
+ };
42
+ /**
43
+ * Whether a member holding `role` at `scope` holds `key` through it. A role
44
+ * the policy does not declare, such as a custom role, counts as holding it.
45
+ */
46
+ function roleGrants(policy, role, scope, key) {
47
+ if (!policy.index.declaredRoles.has(role)) return true;
48
+ return policy.grants.some((grant) => grant.effect === "allow" && grant.permission.key === key && flattenGrantee(grant.to).some((item) => item.kind === "role" && item.role === role && item.scope === scope));
49
+ }
50
+ function fallbackRole(demoteTo, via) {
51
+ if (demoteTo === void 0 || typeof demoteTo === "string") return demoteTo;
52
+ for (const role of via) if (Object.hasOwn(demoteTo, role)) return demoteTo[role];
53
+ }
54
+ function invitationsOf(data) {
55
+ if (!Array.isArray(data)) return [];
56
+ return data.flatMap((row) => isRecord(row) && typeof row["id"] === "string" && typeof row["email"] === "string" && typeof row["role"] === "string" ? [{
57
+ id: row["id"],
58
+ email: row["email"],
59
+ role: row["role"]
60
+ }] : []);
61
+ }
62
+ /**
63
+ * Applies the revoked items of an access review to better-supabase's
64
+ * organizations and invitations blocks, then marks each one applied. A
65
+ * revoked member moves to the `demoteTo` role when the policy says that
66
+ * role lacks the permission, and is removed otherwise. With `email`, the
67
+ * tenant's open invitations to the member whose role grants the permission
68
+ * are revoked too. A member who is no longer in the organization counts as
69
+ * applied. Any other error leaves the item unapplied in `failed`, and one
70
+ * item's failure never stops the others. Throws `review-not-found` for an
71
+ * unknown review.
72
+ */
73
+ function accessReviewApplier(options) {
74
+ const schema = options.schema ?? "better_supabase";
75
+ const scope = options.scope ?? options.policy.scopes[0]?.name ?? "tenant";
76
+ const rpc = async (fn, args) => {
77
+ const result = await callRpc(options.client, schema, fn, args);
78
+ if (result.error !== null) throw new RpcFailure(fn, result.error);
79
+ return result.data;
80
+ };
81
+ const changeMember = async (review, item) => {
82
+ const role = fallbackRole(options.demoteTo, item.via);
83
+ try {
84
+ if (role !== void 0 && !roleGrants(options.policy, role, scope, item.permission)) await rpc("update_member_role", {
85
+ organization: review.tenant,
86
+ member: item.principal,
87
+ role
88
+ });
89
+ else await rpc("remove_member", {
90
+ organization: review.tenant,
91
+ member: item.principal
92
+ });
93
+ } catch (error) {
94
+ if (error instanceof RpcFailure && (error.code === NOT_MEMBER || error.code === "P0002")) return;
95
+ throw error;
96
+ }
97
+ };
98
+ const revokeInvitations = async (review, item) => {
99
+ const email = await options.email?.(item.principal);
100
+ if (email === void 0 || email === null || email === "") return;
101
+ const wanted = email.toLowerCase();
102
+ const open = invitationsOf(await rpc("list_organization_invitations", { organization: review.tenant }));
103
+ await Promise.all(open.filter((invitation) => invitation.email.toLowerCase() === wanted && roleGrants(options.policy, invitation.role, scope, item.permission)).map((invitation) => rpc("revoke_invitation", { invitation_id: invitation.id })));
104
+ };
105
+ const stillHeld = async (review, item) => {
106
+ if (options.permdockFor === void 0) return false;
107
+ const permission = findPermission(options.policy.permissions, item.permission);
108
+ if (permission === void 0) throw new Error(`${item.permission} is not in the policy, so the change cannot be checked`);
109
+ const decide = (await options.permdockFor(item.principal, review.tenant)).decide;
110
+ return decide(permission).outcome !== "denied";
111
+ };
112
+ /** `undefined` once the item is marked applied, else why it is not. */
113
+ const applyItem = async (review, item) => {
114
+ try {
115
+ await changeMember(review, item);
116
+ await revokeInvitations(review, item);
117
+ if (await stillHeld(review, item)) return `${item.principal} still holds ${item.permission} after the change`;
118
+ await options.reviews.markApplied(review.id, item.id, dateAt(options.now).toISOString());
119
+ return;
120
+ } catch (error) {
121
+ return error instanceof Error ? error.message : String(error);
122
+ }
123
+ };
124
+ return async (reviewId) => {
125
+ const review = await options.reviews.get(reviewId);
126
+ if (review === null) throw new AccessReviewError("review-not-found", `access review ${reviewId} not found`);
127
+ const applied = [];
128
+ const failed = [];
129
+ let cursor;
130
+ do {
131
+ const page = await options.reviews.items({
132
+ review: reviewId,
133
+ status: "revoked",
134
+ ...cursor === void 0 ? {} : { cursor }
135
+ });
136
+ const pending = page.items.filter((item) => item.appliedAt === void 0);
137
+ const causes = await Promise.all(pending.map((item) => applyItem(review, item)));
138
+ for (const [index, item] of pending.entries()) {
139
+ const cause = causes[index];
140
+ if (cause === void 0) applied.push(item.id);
141
+ else failed.push({
142
+ item: item.id,
143
+ cause
144
+ });
145
+ }
146
+ cursor = page.next;
147
+ } while (cursor !== void 0);
148
+ return {
149
+ applied,
150
+ failed
151
+ };
152
+ };
153
+ }
154
+ //#endregion
155
+ //#region src/better-supabase/api-keys.ts
156
+ const EPOCH = "1970-01-01T00:00:00.000Z";
157
+ const rfc3339Of = (instant) => isoAt(instant.epochMilliseconds);
158
+ /** When a key stops verifying: its expiry, or the end of a rotated key's grace period. */
159
+ function endOf(key) {
160
+ const { expiresAt, revokedAt } = key;
161
+ if (revokedAt === void 0) return expiresAt;
162
+ if (expiresAt === void 0) return revokedAt;
163
+ return revokedAt.epochMilliseconds < expiresAt.epochMilliseconds ? revokedAt : expiresAt;
164
+ }
165
+ /** `rls.apiKeys.serviceRoles` from the manifest, when it has `rls.apiKeys`. */
166
+ function manifestServiceRoles(manifest) {
167
+ return manifest === void 0 ? void 0 : parseSupabaseManifest(manifest).rls.apiKeys?.serviceRoles;
168
+ }
169
+ /**
170
+ * A personal key as a `user` credential acting as its user, a tenant key as
171
+ * a `service` credential holding `roles` in its tenant; `null` when `*`
172
+ * has no expansion, a tenant key has no roles, or the record is invalid.
173
+ */
174
+ function keyCredential(key, roles, allPermissions) {
175
+ const scopes = key.scopes.includes("*") ? allPermissions?.map((permission) => permission.key) : key.scopes;
176
+ if (scopes === void 0) return null;
177
+ const owner = key.userId;
178
+ const base = {
179
+ v: 1,
180
+ id: key.id,
181
+ permissions: scopes.map((permission) => ({ permission })),
182
+ createdBy: key.createdBy ?? owner ?? key.id,
183
+ createdAt: key.createdAt ?? EPOCH,
184
+ ...key.expiresAt === void 0 ? {} : { expiresAt: key.expiresAt },
185
+ name: key.name
186
+ };
187
+ if (owner !== void 0) return parseCredential({
188
+ ...base,
189
+ kind: "user",
190
+ principal: owner,
191
+ ...key.organizationId === void 0 ? {} : { tenant: key.organizationId }
192
+ }) ?? null;
193
+ if (roles === void 0 || key.organizationId === void 0) return null;
194
+ return parseCredential({
195
+ ...base,
196
+ kind: "service",
197
+ principal: key.id,
198
+ tenant: key.organizationId,
199
+ roles
200
+ }) ?? null;
201
+ }
202
+ /**
203
+ * better-supabase's API keys as a `CredentialVerifier`, for
204
+ * `subjectFromApiKey` from `permdock/server`, which reads PermDock's
205
+ * `pdk_<id>_<secret>` format: create the keys with `prefix: "pdk"`. The
206
+ * credential id is the key's `publicId`, the id in the token. A personal key
207
+ * is a `user` credential acting as its user (inside its tenant when the key
208
+ * is limited to one), a tenant key a `service` credential holding
209
+ * `serviceRoles` in its tenant, and the key's scopes are the credential's
210
+ * permissions. better-supabase records the use when it verifies, so there is
211
+ * no `touch`. A rotated key in its grace period verifies, and its credential
212
+ * expires when the grace period ends. An invalid, revoked, expired or
213
+ * rate-limited key, and a failed lookup, are `null`. `permdock/server` exports a different
214
+ * `apiKeyVerifier`, over the application's own key records.
215
+ */
216
+ function apiKeyVerifier(options) {
217
+ const fallback = manifestServiceRoles(options.manifest);
218
+ return freezeDeep({ async verify(secret) {
219
+ try {
220
+ const checked = await options.keys.verify(secret);
221
+ if (!checked.ok || checked.data.status !== "ok") return null;
222
+ const { key } = checked.data;
223
+ if (key.state !== "active" && key.state !== "grace") return null;
224
+ const end = endOf(key);
225
+ const roles = typeof options.serviceRoles === "function" ? options.serviceRoles(key) : options.serviceRoles ?? fallback;
226
+ return keyCredential({
227
+ id: key.publicId,
228
+ name: key.name,
229
+ scopes: key.scopes,
230
+ createdAt: rfc3339Of(key.createdAt),
231
+ ...key.organizationId === void 0 ? {} : { organizationId: key.organizationId },
232
+ ...key.userId === void 0 ? {} : { userId: key.userId },
233
+ ...key.createdBy === void 0 ? {} : { createdBy: key.createdBy },
234
+ ...end === void 0 ? {} : { expiresAt: rfc3339Of(end) }
235
+ }, roles, options.allPermissions);
236
+ } catch {
237
+ return null;
238
+ }
239
+ } });
240
+ }
241
+ /**
242
+ * The `claim` and `tenantClaim` options of better-supabase's
243
+ * `apiKeyClaims()`, from the manifest's `rls.apiKeys`, so a key's token
244
+ * carries the scopes ceiling, tenant and roles the `rls generate` helpers
245
+ * read. Throws when the manifest has no `rls.apiKeys`.
246
+ */
247
+ function apiKeyClaimOptions(manifest) {
248
+ const rls = parseSupabaseManifest(manifest).rls;
249
+ const apiKeys = rls.apiKeys;
250
+ if (!apiKeys) throw new TypeError("PermDock: the manifest has no rls.apiKeys. Set rls.apiKeys in permdock.config.ts, then run `permdock rls generate` and `permdock supabase inspect --out`.");
251
+ return freezeDeep({
252
+ claim: {
253
+ name: apiKeys.claim,
254
+ scopes: apiKeys.scopes,
255
+ tenant: apiKeys.tenant,
256
+ roles: apiKeys.roles,
257
+ serviceRoles: apiKeys.serviceRoles
258
+ },
259
+ tenantClaim: rls.tenantClaim
260
+ });
261
+ }
262
+ //#endregion
263
+ //#region src/better-supabase/subject.ts
264
+ /** `session` with the short codes in `claim` turned back into feature keys. */
265
+ function decodedFeatures(session, claim, keys) {
266
+ const claims = session?.claims;
267
+ if (session === null || session === void 0 || !isRecord(claims)) return session;
268
+ const coded = claims[claim];
269
+ if (!isRecord(coded)) return session;
270
+ const byCode = new Map(Object.entries(keys).map(([key, code]) => [code, key]));
271
+ const decoded = Object.fromEntries(Object.entries(coded).map(([tenant, codes]) => [tenant, Array.isArray(codes) ? codes.flatMap((code) => {
272
+ const key = typeof code === "string" ? byCode.get(code) : void 0;
273
+ return key === void 0 ? [] : [key];
274
+ }) : codes]));
275
+ return {
276
+ ...session,
277
+ claims: {
278
+ ...claims,
279
+ [claim]: decoded
280
+ }
281
+ };
282
+ }
283
+ function isApiKeySession(session) {
284
+ if (typeof session !== "object" || session === null) return false;
285
+ const record = Object.fromEntries(Object.entries(session));
286
+ return record["kind"] === "apiKey" && typeof record["keyId"] === "string" && typeof record["name"] === "string" && Array.isArray(record["scopes"]) && record["scopes"].every((scope) => typeof scope === "string");
287
+ }
288
+ function apiKeySubject(session, options) {
289
+ const roles = typeof options.serviceRoles === "function" ? options.serviceRoles(session) : options.serviceRoles ?? manifestServiceRoles(options.manifest);
290
+ const credential = keyCredential({
291
+ id: session.keyId,
292
+ name: session.name,
293
+ scopes: session.scopes,
294
+ ...session.organizationId === void 0 ? {} : { organizationId: session.organizationId },
295
+ ...session.userId === void 0 ? {} : { userId: session.userId },
296
+ ...session.createdBy === void 0 ? {} : { createdBy: session.createdBy },
297
+ ...session.createdAt === void 0 || !Number.isFinite(session.createdAt) ? {} : { createdAt: rfc3339(session.createdAt) }
298
+ }, roles, listPermissions(options.permissions));
299
+ return credential === null ? anonymousSubject() : credentialSubject(credential, { permissions: options.permissions });
300
+ }
301
+ /**
302
+ * `subjectFromSupabaseSession` for better-supabase's `AuthSession`, reading
303
+ * `memberships` for the memberships and the entitlements module's `features`
304
+ * claim for `principal.plans` (`plans` names another claim). With `apiKeys`,
305
+ * a verified `apiKey` session is the key's credential subject: a personal key
306
+ * acts as its user within the key's scopes, a tenant key as a `service`
307
+ * principal holding `serviceRoles`. Without `apiKeys`, and for `anon`,
308
+ * `service` and `invalid` sessions, the subject is anonymous.
309
+ */
310
+ function subjectFromBetterSupabase(session, options = {}) {
311
+ const { apiKeys, plans = "features", claims, ...rest } = options;
312
+ if (isApiKeySession(session)) return apiKeys === void 0 ? anonymousSubject() : apiKeySubject(session, apiKeys);
313
+ const claim = typeof plans === "string" ? plans : plans.claim ?? "features";
314
+ const keys = typeof plans === "string" ? void 0 : plans.keys;
315
+ return subjectFromSupabaseSession(keys === void 0 ? session : decodedFeatures(session, claim, keys), {
316
+ ...rest,
317
+ claims: {
318
+ memberships: "memberships",
319
+ ...claims,
320
+ plans: claim
321
+ }
322
+ });
323
+ }
324
+ //#endregion
325
+ //#region src/better-supabase/subscribers.ts
326
+ function permissionFor(events, type) {
327
+ if (Object.hasOwn(events, type)) return events[type];
328
+ let best;
329
+ for (const pattern of Object.keys(events)) if ((pattern === "*" || pattern.endsWith(".*") && type.startsWith(pattern.slice(0, -1))) && (best === void 0 || pattern.length > best.length)) best = pattern;
330
+ return best === void 0 ? void 0 : events[best];
331
+ }
332
+ /**
333
+ * The per-subscriber check for better-supabase's webhooks-out, outbox and
334
+ * realtime fan-out: the subscribers whose `principal` `decideFor` grants
335
+ * the event type's permission on its row, in the event's tenant, as the
336
+ * sender. An unmapped type, an event without a tenant or sender, a sender
337
+ * outside the tenant and a throw reach no one.
338
+ */
339
+ function subscriberPolicy(options) {
340
+ return async (event, subscribers) => {
341
+ try {
342
+ const permission = permissionFor(options.events, event.type);
343
+ const tenant = event.tenant;
344
+ const senderId = options.sender?.(event) ?? event.actorId;
345
+ if (permission === void 0 || tenant === null || senderId === null || senderId === void 0) return [];
346
+ const sender = await loadPrincipal(options.principals, senderId, tenant);
347
+ if (sender === null) return [];
348
+ const permdock = await getPermDock(options.policy, {
349
+ principal: sender,
350
+ context: {}
351
+ }, {
352
+ ...options.options,
353
+ principals: options.principals,
354
+ tenant
355
+ });
356
+ if (permdock.subject.principal?.tenant !== tenant) return [];
357
+ return await subscribersFor(permdock, {
358
+ permission,
359
+ row: options.row === void 0 ? event.payload : options.row(event)
360
+ }, subscribers);
361
+ } catch {
362
+ return [];
363
+ }
364
+ };
365
+ }
366
+ //#endregion
367
+ //#region src/better-supabase/support.ts
368
+ /**
369
+ * Bound to the admin, target, tenant, reason and mode, so the same start
370
+ * gets the same approval token each time.
371
+ */
372
+ function requestId(input) {
373
+ return sha256Digest(canonicalJson({
374
+ t: "better-supabase-support",
375
+ admin: input.admin.user.id,
376
+ target: input.targetUserId,
377
+ tenant: input.tenant ?? null,
378
+ reason: input.reason,
379
+ readOnly: input.readOnly
380
+ }));
381
+ }
382
+ /**
383
+ * The `authorize` option of better-supabase's `supportSessions`: the admin's
384
+ * verified claims become a PermDock subject, and the start is decided with
385
+ * `permdock.decideSupportSession` in the requested tenant. `true` only for
386
+ * `granted`: an `approval-required` start passes once `approved` returns a
387
+ * distinct approver for its token. `readOnly: false` needs `writable`. A
388
+ * throw is `false`.
389
+ */
390
+ function supportPolicy(options) {
391
+ return async (input) => {
392
+ try {
393
+ const base = await getPermDock(options.policy, subjectFromBetterSupabase({
394
+ kind: "user",
395
+ claims: input.admin.claims
396
+ }, options.subject), options.options);
397
+ if (base.subject.principal?.id !== input.admin.user.id) return false;
398
+ if (!input.readOnly && options.writable === void 0) return false;
399
+ const permdock = input.tenant === void 0 ? base : base.tenant(input.tenant);
400
+ const request = {
401
+ id: requestId(input),
402
+ principal: input.targetUserId,
403
+ reason: input.reason,
404
+ ...input.tenant === void 0 ? {} : { tenant: input.tenant },
405
+ ...input.readOnly ? {} : { permissions: options.writable },
406
+ ...typeof input.metadata["reference"] === "string" ? { reference: input.metadata["reference"] } : {}
407
+ };
408
+ const settings = options.options?.settings;
409
+ const decide = (approved) => decideSupportSession(permdock, request, {
410
+ ...settings === void 0 ? {} : { settings },
411
+ ...options.approval === false ? { approval: false } : {},
412
+ ...approved === void 0 ? {} : {
413
+ approved: approved.token,
414
+ approvedBy: approved.approvedBy
415
+ }
416
+ });
417
+ const decision = await decide();
418
+ switch (decision.outcome) {
419
+ case "granted": return true;
420
+ case "denied": return false;
421
+ case "approval-required": {
422
+ const found = await options.approved?.(input, {
423
+ token: decision.token,
424
+ session: decision.session
425
+ });
426
+ if (found === void 0) return false;
427
+ return (await decide({
428
+ token: decision.token,
429
+ approvedBy: found.approvedBy
430
+ })).outcome === "granted";
431
+ }
432
+ default: return decision;
433
+ }
434
+ } catch {
435
+ return false;
436
+ }
437
+ };
438
+ }
439
+ //#endregion
440
+ //#region src/better-supabase/hooks.ts
441
+ /** better-supabase's claim that caps a context to the permission keys it lists. */
442
+ const PERMISSION_CAP_CLAIM = "bs_permission_cap";
443
+ /** Whether `permdock` is granted `key` with no row; an unknown key and `approval-required` are `false`. */
444
+ function grants$1(permdock, key) {
445
+ const permission = findPermission(permdock.permissions, key);
446
+ return permission !== void 0 && permdock.decide(permission, void 0).outcome === "granted";
447
+ }
448
+ /** A list matches when every entry does, `anyOf` when one does; other leaves match nobody, as in SQL. */
449
+ function matches(approvers, userId, permdock) {
450
+ const items = flattenApprovers(approvers);
451
+ return items.length > 0 && items.every((item) => {
452
+ switch (item.kind) {
453
+ case "user": return item.id === userId;
454
+ case "permission": return grants$1(permdock, item.permission);
455
+ case "any-of": return item.of.some((entry) => matches(entry, userId, permdock));
456
+ case "actor":
457
+ case "anyone":
458
+ case "assurance":
459
+ case "authenticated":
460
+ case "inherit":
461
+ case "plan":
462
+ case "relation":
463
+ case "role": return false;
464
+ default: return item;
465
+ }
466
+ });
467
+ }
468
+ /**
469
+ * better-supabase's `AuthorizationHooks` (`createServer(betterSupabase,
470
+ * { authorization })`), decided by `policy`. Every hook builds the
471
+ * principal's instance from the claims `claims` loads now, never from the
472
+ * request, and fails closed: a throw, an unknown key and `approval-required`
473
+ * refuse. `automation` and `delegation` are always set; `identities`,
474
+ * `delivery`, `approverOf`, `support` and `tickets` only with the options
475
+ * they read. `delegation.permissions` maps each scope to the keys whose
476
+ * `.scope` it is, was renamed from, or that a policy `oauthScopes` entry
477
+ * covers, and reads no `authorization_details`.
478
+ */
479
+ function authorizationHooks(options) {
480
+ const { policy } = options;
481
+ const instance = async (claims, tenant) => {
482
+ const subject = subjectFromBetterSupabase({
483
+ kind: "user",
484
+ claims
485
+ }, options.subject);
486
+ if (subject.principal === null) return null;
487
+ const permdock = await getPermDock(policy, subject, {
488
+ ...options.options,
489
+ ...tenant === null || tenant === void 0 ? {} : { tenant }
490
+ });
491
+ return tenant === null || tenant === void 0 || permdock.subject.principal?.tenant === tenant ? permdock : null;
492
+ };
493
+ const holdsAll = async (claims, automation) => {
494
+ const permdock = await instance(claims, automation.tenant);
495
+ return permdock !== null && automation.permissions.every((key) => grants$1(permdock, key));
496
+ };
497
+ const claimsOf = async (userId, tenant) => {
498
+ const claims = await options.claims(userId, tenant === null || tenant === void 0 ? {} : { tenant });
499
+ return isRecord(claims) && claims["sub"] === userId ? claims : null;
500
+ };
501
+ const automation = {
502
+ async mayPublish(ref) {
503
+ try {
504
+ return ref.publisher !== null && await holdsAll(ref.claims, ref);
505
+ } catch {
506
+ return false;
507
+ }
508
+ },
509
+ async runAs(ref) {
510
+ try {
511
+ const claims = ref.publisher === null ? await options.service?.(ref.tenant) : await claimsOf(ref.publisher, ref.tenant);
512
+ if (!isRecord(claims) || !await holdsAll(claims, ref)) return null;
513
+ return Object.freeze({
514
+ ...claims,
515
+ [PERMISSION_CAP_CLAIM]: [...ref.permissions]
516
+ });
517
+ } catch {
518
+ return null;
519
+ }
520
+ }
521
+ };
522
+ const delegation = { permissions(ref) {
523
+ try {
524
+ const scopes = new Set(ref.scopes);
525
+ return listPermissions(policy.permissions).filter((permission) => scopesReaching(policy, permission).some((scope) => scopes.has(scope))).map((permission) => permission.key);
526
+ } catch {
527
+ return [];
528
+ }
529
+ } };
530
+ const { principals, events, approvers, support, tickets } = options;
531
+ const approverOf = approvers === void 0 ? void 0 : async (approver, request) => {
532
+ try {
533
+ if (approver.userId === request.requester || !Object.hasOwn(approvers, request.kind)) return false;
534
+ const given = approvers[request.kind];
535
+ if (given === void 0 || approver.claims["sub"] !== approver.userId) return false;
536
+ const permdock = await instance(approver.claims, request.tenant);
537
+ return permdock !== null && matches(asApprover(given), approver.userId, permdock);
538
+ } catch {
539
+ return false;
540
+ }
541
+ };
542
+ const identities = principals?.principalByIdentity === void 0 ? void 0 : { async principalByIdentity(identity) {
543
+ try {
544
+ const found = await principals.principalByIdentity?.(identity);
545
+ if (found === null || found === void 0) return null;
546
+ return found.kind === "contact" ? {
547
+ kind: "contact",
548
+ contactId: found.id
549
+ } : {
550
+ kind: "user",
551
+ userId: found.id
552
+ };
553
+ } catch {
554
+ return null;
555
+ }
556
+ } };
557
+ const fanOut = principals === void 0 || events === void 0 ? void 0 : subscriberPolicy({
558
+ policy,
559
+ principals,
560
+ events,
561
+ ...options.row === void 0 ? {} : { row: options.row },
562
+ ...options.options === void 0 ? {} : { options: options.options }
563
+ });
564
+ return Object.freeze({
565
+ apiVersion: 1,
566
+ name: "PermDock",
567
+ automation: Object.freeze(automation),
568
+ delegation: Object.freeze(delegation),
569
+ ...approverOf === void 0 ? {} : { approverOf },
570
+ ...identities === void 0 ? {} : { identities: Object.freeze(identities) },
571
+ ...fanOut === void 0 ? {} : { delivery: Object.freeze({
572
+ subscribers: fanOut,
573
+ audience: fanOut
574
+ }) },
575
+ ...support === void 0 ? {} : { support: Object.freeze({ authorize: supportPolicy({
576
+ ...support,
577
+ policy
578
+ }) }) },
579
+ ...tickets === void 0 ? {} : { tickets: Object.freeze({ async subject(ticket) {
580
+ try {
581
+ const found = await tickets(ticket);
582
+ if (!isRecord(found) || typeof found.userId !== "string") return null;
583
+ const claims = await claimsOf(found.userId, found.tenant);
584
+ return claims === null ? null : {
585
+ claims,
586
+ ...found.tenant === void 0 ? {} : { tenant: found.tenant }
587
+ };
588
+ } catch {
589
+ return null;
590
+ }
591
+ } }) }
592
+ });
593
+ }
594
+ //#endregion
595
+ //#region src/better-supabase/approvers.ts
596
+ /** `rls.approvals`' approver check, executable by `authenticated`. */
597
+ const IS_APPROVER = "permdock_is_approver";
598
+ /** The resource a cover's `resources` names for a request kind: `job` for `job.update`, else the kind. */
599
+ function resourceOf(kind) {
600
+ const dot = kind.indexOf(".");
601
+ return dot <= 0 ? kind : kind.slice(0, dot);
602
+ }
603
+ /**
604
+ * better-supabase's `isApprover` template: one `case` over `{kind}` whose
605
+ * branches call `permdock_is_approver` with the kind's approvers as JSON.
606
+ * A kind with no branch is `false`. Only `user`, `holder` and `anyOf`
607
+ * approvers match in SQL; other leaves are reported and match nobody.
608
+ */
609
+ function isApproverTemplate(input) {
610
+ const problems = [];
611
+ if (!input.listed) return { problems: [`approvers needs ${input.schema}.${IS_APPROVER}, which \`permdock rls generate\` writes with rls.approvals. Run it, then \`permdock supabase inspect --out\`.`] };
612
+ const fn = `${sqlIdent(input.schema)}.${sqlIdent(IS_APPROVER)}`;
613
+ const keys = input.catalog === void 0 ? void 0 : new Set(input.catalog.permissions.map((permission) => permission.key));
614
+ const branches = [];
615
+ for (const [kind, given] of Object.entries(input.approvers)) {
616
+ if (kind === "") {
617
+ problems.push("approvers has an empty kind, which no request has.");
618
+ continue;
619
+ }
620
+ const approvers = asApprover(given);
621
+ for (const leaf of approverLeaves(approvers)) {
622
+ if (leaf.kind === "user") continue;
623
+ if (leaf.kind !== "permission") problems.push(`The ${kind} approvers name a ${leaf.kind} approver, which ${IS_APPROVER} cannot check in SQL, so it matches nobody. Use user(), holder() or anyOf().`);
624
+ else if (keys !== void 0 && !keys.has(leaf.permission)) problems.push(`The ${kind} approver permission ${leaf.permission} is not in the catalog. Run \`permdock catalog\` after adding it to the policy.`);
625
+ }
626
+ const json = `${quoteSqlLiteral(JSON.stringify(approvers))}::jsonb`;
627
+ branches.push(input.covers === true ? `when ${quoteSqlLiteral(kind)} then ${fn}(${json}, ({tenant})::text, ({requester})::text, ${quoteSqlLiteral(resourceOf(kind))})` : `when ${quoteSqlLiteral(kind)} then ${fn}(${json}, ({tenant})::text)`);
628
+ }
629
+ if (branches.length === 0) {
630
+ problems.push("approvers names no kind, so isApprover is not set.");
631
+ return { problems };
632
+ }
633
+ if (!input.rootTenant) problems.push(`The tenant scope "${input.tenantScope}" is not a root scope, and ${IS_APPROVER} checks holder() approvers at the root scope, so they match nobody.`);
634
+ return {
635
+ template: `case {kind} ${branches.join(" ")} else false end`,
636
+ requires: {
637
+ function: fn,
638
+ args: input.covers === true ? "jsonb, text, text, text" : "jsonb, text",
639
+ role: "postgres"
640
+ },
641
+ problems
642
+ };
643
+ }
644
+ //#endregion
13
645
  //#region src/better-supabase/provider.ts
14
646
  /**
15
647
  * The helper each template calls (with `{scope}` unfilled), its default
@@ -91,6 +723,13 @@ const ASSIGN_ANY = {
91
723
  }
92
724
  };
93
725
  const listedName = (entry) => entry.listedAs ?? entry.helper;
726
+ /**
727
+ * No client role may execute `permitted_<scope>_users`, so `holdersOf` is
728
+ * required for `postgres`, inside better-supabase's `security definer`
729
+ * functions; better-supabase fills it for the tenant scope only.
730
+ */
731
+ const HOLDERS_OF = "permitted_{scope}_users";
732
+ const ACTOR_MAY_WRITE = "permdock_actor_may_write";
94
733
  const OPTIONAL = [
95
734
  "idsWithFor",
96
735
  "isPlatformFor",
@@ -100,10 +739,16 @@ const OPTIONAL = [
100
739
  "canAssignFor",
101
740
  "permissionsFor"
102
741
  ];
103
- /** Postgres aliases of the id types better-supabase reads (`uuid`, `text`, `bigint`, `integer`). */
742
+ /**
743
+ * Postgres aliases of the id types better-supabase reads (`uuid`, `text`,
744
+ * `bigint`, `integer`), matching its `moduleIdType` from
745
+ * `better-supabase/sql`, which this runtime entry does not import.
746
+ */
104
747
  const ID_TYPES = {
105
748
  uuid: "uuid",
106
749
  text: "text",
750
+ varchar: "text",
751
+ "character varying": "text",
107
752
  bigint: "bigint",
108
753
  int8: "bigint",
109
754
  integer: "integer",
@@ -198,17 +843,97 @@ function tokenHookOf(manifest) {
198
843
  hook: `-- permdock:hook ${manifest.markers.hook}`,
199
844
  grants: `-- permdock:grants ${manifest.markers.grants}`
200
845
  },
201
- grantsCommand: `permdock supabase hook generate --grants-out supabase/migrations/<timestamp>_permdock_hook_grants.sql`
846
+ grantsCommand: grantsCommandOf(manifest.hook.schemas ?? "pg-delta")
202
847
  };
203
848
  }
849
+ /** The command better-supabase doctor prints when the hook lacks its grants. */
850
+ function grantsCommandOf(schemas) {
851
+ switch (schemas) {
852
+ case "pg-delta": return "permdock supabase hook generate && supabase db schema declarative sync -f permdock_hook";
853
+ case "migra": return "permdock supabase hook generate --grants-out supabase/migrations/<timestamp>_permdock_hook_grants.sql";
854
+ case "migrations": return "permdock supabase hook generate --out supabase/migrations/<timestamp>_permdock_hook.sql";
855
+ default: return schemas;
856
+ }
857
+ }
858
+ /**
859
+ * better-supabase's `permitted` template. An instance of a scope (`{resource}`
860
+ * names the scope) is checked against that scope's ids; any other resource
861
+ * at the tenant, and with a catalog only for a key without row conditions.
862
+ * Without a catalog better-supabase refuses every key anyway.
863
+ */
864
+ function permittedTemplate(schema, scopes, tenantScope, catalog) {
865
+ const idsOf = (scope) => `select ids::text from ${schema}.${sqlIdent(`permitted_${scope}_ids_by_permission`)}({permission}) as ids`;
866
+ const branches = scopes.map((scope) => `when {resource} = ${quoteSqlLiteral(scope)} then {id} in (${idsOf(scope)})`);
867
+ const atTenant = `{tenant} in (${idsOf(tenantScope)})`;
868
+ const complete = catalog?.permissions.filter((permission) => !permission.rowConditions).map((permission) => quoteSqlLiteral(permission.key));
869
+ const otherwise = complete === void 0 ? atTenant : complete.length === 0 ? `false and ${atTenant}` : `{permission} in (${complete.join(", ")}) and ${atTenant}`;
870
+ return `case ${branches.join(" ")} else ${otherwise} end`;
871
+ }
872
+ const RISKS = {
873
+ low: "low",
874
+ medium: "medium",
875
+ high: "high",
876
+ critical: "high"
877
+ };
878
+ const EFFECTS = {
879
+ read: "read",
880
+ write: "write",
881
+ delete: "delete",
882
+ send: "external",
883
+ spend: "external",
884
+ external: "external"
885
+ };
886
+ /**
887
+ * The catalog's `defineOperations` entries as better-supabase operations. A
888
+ * name with a `.` is used as written (`workflow.node.http`); any other is
889
+ * the tool `ai.tool.<name>` and `mcp.tool.<name>`. `critical` risk is
890
+ * `high`, and `send` and `spend` effects are `external`.
891
+ */
892
+ function operationsOf(catalog) {
893
+ const operations = {};
894
+ for (const operation of catalog.operations ?? []) {
895
+ const effects = [...new Set((operation.meta.effects ?? []).map((effect) => EFFECTS[effect]))];
896
+ const meta = {
897
+ ...operation.meta.risk === void 0 ? {} : { risk: RISKS[operation.meta.risk] },
898
+ ...effects.length > 0 ? { effects } : {},
899
+ ...operation.meta.title === void 0 ? {} : { title: operation.meta.title }
900
+ };
901
+ const entry = {
902
+ permission: operation.permission,
903
+ ...Object.keys(meta).length > 0 ? { meta } : {}
904
+ };
905
+ const names = operation.name.includes(".") ? [operation.name] : [`ai.tool.${operation.name}`, `mcp.tool.${operation.name}`];
906
+ for (const name of names) operations[name] = entry;
907
+ }
908
+ return operations;
909
+ }
910
+ /**
911
+ * better-supabase's `delegation.scopes`: each permission's own scope, and
912
+ * each coarse scope the catalog's `oauthScopes` lists, to the permission keys
913
+ * it reaches, sorted by code point. A key the catalog does not list is
914
+ * dropped, so a stale `oauthScopes` entry grants nothing.
915
+ */
916
+ function delegatedScopes(catalog) {
917
+ const delegated = {};
918
+ const known = /* @__PURE__ */ new Set();
919
+ for (const permission of catalog?.permissions ?? []) {
920
+ known.add(permission.key);
921
+ (delegated[permission.scope] ??= /* @__PURE__ */ new Set()).add(permission.key);
922
+ }
923
+ for (const entry of catalog?.oauthScopes ?? []) for (const key of entry.permissions) if (known.has(key)) (delegated[entry.scope] ??= /* @__PURE__ */ new Set()).add(key);
924
+ return Object.fromEntries(Object.keys(delegated).toSorted(byCodePoint).map((scope) => [scope, [...delegated[scope] ?? []].toSorted(byCodePoint)]));
925
+ }
204
926
  /**
205
927
  * better-supabase's `authorization` config value, built from PermDock's
206
928
  * manifest and catalog: the scopes, the `rls generate` helpers as SQL
207
929
  * templates, the membership tables, suspension rows, the token hook and
208
930
  * every catalog key with `sqlComplete` set from its `rowConditions`. A
209
931
  * `_for` template and `permissionsFor` are set only when the manifest lists
210
- * their helper, which `rls.mode: 'database'` writes; `canApprove` only with
211
- * `approver`. Throws when the manifest or catalog is invalid, or when the
932
+ * their helper, which `rls.mode: 'database'` writes; `isApprover` only with
933
+ * `approvers` and `permdock_is_approver`, and otherwise `canApprove` only
934
+ * with `approver`. `holdersOf` (`permitted_<tenant scope>_users`) and
935
+ * `actorMayWrite` (`permdock_actor_may_write()`) are set when the manifest
936
+ * lists their helper. Throws when the manifest or catalog is invalid, or when the
212
937
  * tenant scope is ambiguous.
213
938
  */
214
939
  function authorizationProvider(options) {
@@ -264,25 +989,60 @@ function authorizationProvider(options) {
264
989
  const users = rls.suspension?.users;
265
990
  const tenants = rls.suspension?.scopes?.[tenantScope];
266
991
  const approver = options.approver;
267
- let canApprove;
268
- if (approver !== void 0) {
269
- if (catalog !== void 0 && !catalog.permissions.some((permission) => permission.key === approver.key)) problems.push(`The approver permission ${approver.key} is not in the catalog. Run \`permdock catalog\` after adding it to the policy.`);
270
- const name = `permitted_${tenantScope}_ids_by_permission`;
271
- canApprove = `{tenant} in (select ${schema}.${sqlIdent(name)}(${quoteSqlLiteral(approver.key)}))`;
992
+ const approvers = approver === void 0 ? options.approvers : {
993
+ "ai-tool": holder(approver),
994
+ ...options.approvers
995
+ };
996
+ let isApprover;
997
+ if (approvers !== void 0) {
998
+ const built = isApproverTemplate({
999
+ approvers,
1000
+ schema: rls.schema,
1001
+ listed: helpers.has(IS_APPROVER),
1002
+ covers: /\bp_requester\b/u.test(helpers.get("permdock_is_approver")?.args ?? ""),
1003
+ tenantScope,
1004
+ rootTenant,
1005
+ catalog
1006
+ });
1007
+ problems.push(...built.problems);
1008
+ isApprover = built.template;
1009
+ if (built.requires !== void 0) requires.push(built.requires);
1010
+ }
1011
+ const holdersName = HOLDERS_OF.replace("{scope}", tenantScope);
1012
+ const holders = helpers.get(holdersName);
1013
+ let holdersOf;
1014
+ if (holders !== void 0) {
1015
+ const fn = `${schema}.${sqlIdent(holders.name)}`;
1016
+ holdersOf = `${fn}({permission}, {tenant})`;
272
1017
  requires.push({
273
- function: `${schema}.${sqlIdent(name)}`,
274
- args: "text",
1018
+ function: fn,
1019
+ args: argTypes(holders.args),
275
1020
  role: "postgres"
276
1021
  });
277
1022
  }
1023
+ let actorMayWrite;
1024
+ if (helpers.has(ACTOR_MAY_WRITE)) {
1025
+ const fn = `${schema}.${sqlIdent(ACTOR_MAY_WRITE)}`;
1026
+ actorMayWrite = `${fn}()`;
1027
+ requires.push({
1028
+ function: fn,
1029
+ role: "authenticated"
1030
+ });
1031
+ }
1032
+ const permitted = listed("idsWith", tenantScope) ? permittedTemplate(schema, scopeNames.filter((scope) => listed("idsWith", scope)), tenantScope, catalog) : void 0;
278
1033
  const functions = {
279
1034
  ...optional,
280
1035
  idsWith: `${schema}.${TEMPLATES.idsWith.sql}`,
281
1036
  isPlatform: `${schema}.${TEMPLATES.isPlatform.sql}`,
282
- ...canApprove === void 0 ? {} : { canApprove }
1037
+ ...holdersOf === void 0 ? {} : { holdersOf },
1038
+ ...actorMayWrite === void 0 ? {} : { actorMayWrite },
1039
+ ...isApprover === void 0 ? {} : { isApprover },
1040
+ ...permitted === void 0 ? {} : { permitted }
283
1041
  };
1042
+ const operations = catalog === void 0 ? {} : operationsOf(catalog);
1043
+ const delegated = delegatedScopes(catalog);
284
1044
  const scopes = rls.scopes.map((scope) => {
285
- const idType = ID_TYPES[scope.type.trim().toLowerCase()];
1045
+ const idType = ID_TYPES[scope.type.trim().toLowerCase().replaceAll(/\s+/gu, " ")];
286
1046
  if (idType === void 0) problems.push(`Scope "${scope.name}" has ids of type ${scope.type}; better-supabase reads uuid, text, bigint and integer ids.`);
287
1047
  return scope.within === void 0 ? {
288
1048
  name: scope.name,
@@ -307,13 +1067,114 @@ function authorizationProvider(options) {
307
1067
  ...tenants ? { tenant: disabledRow(tenants) } : {}
308
1068
  } } : {},
309
1069
  ...roleSources.length > 0 ? { roleSources } : {},
310
- ...canApprove === void 0 ? {} : { approvals: { distinctApprover: true } },
1070
+ ...Object.keys(operations).length > 0 ? { operations } : {},
1071
+ ...Object.keys(delegated).length > 0 ? { delegation: { scopes: delegated } } : {},
311
1072
  decidingColumns: manifest.decidingColumns,
312
1073
  tokenHook: tokenHookOf(manifest),
313
1074
  ...problems.length > 0 ? { problems } : {}
314
1075
  });
315
1076
  }
316
1077
  //#endregion
1078
+ //#region src/better-supabase/authorizer.ts
1079
+ const OPERATIONS = /* @__PURE__ */ new Set([
1080
+ "list",
1081
+ "get",
1082
+ "create",
1083
+ "update",
1084
+ "delete"
1085
+ ]);
1086
+ const denied = (reason) => ({
1087
+ outcome: "denied",
1088
+ reason
1089
+ });
1090
+ function outcomeOf(decision, key) {
1091
+ switch (decision.outcome) {
1092
+ case "granted": return { outcome: "granted" };
1093
+ case "approval-required": return {
1094
+ outcome: "approval-required",
1095
+ approval: { id: decision.token }
1096
+ };
1097
+ case "denied": {
1098
+ const why = decision.denials[0]?.reason;
1099
+ return denied(why === void 0 ? `PermDock denied ${key}.` : `PermDock denied ${key}: ${why}.`);
1100
+ }
1101
+ default: return decision;
1102
+ }
1103
+ }
1104
+ function decideWith(permdock, request) {
1105
+ const { action } = request;
1106
+ if (!isPermission(action)) return denied("Unknown permission.");
1107
+ return outcomeOf(permdock.decide(action, request.resource.properties), action.key);
1108
+ }
1109
+ /** The verified claims better-supabase passes in `subject.properties.claims`, for a user or a delegated agent. */
1110
+ function claimsOf(request) {
1111
+ const { subject } = request;
1112
+ if (subject.type !== "user" && subject.type !== "agent") return void 0;
1113
+ const claims = subject.properties?.["claims"];
1114
+ return isRecord(claims) ? claims : void 0;
1115
+ }
1116
+ /**
1117
+ * better-supabase's `authorizer` for PermDock: every resource, action, route
1118
+ * guard and MCP tool that declares a `permission` is decided with `decide`.
1119
+ * The subject is mapped from the verified claims with
1120
+ * `subjectFromBetterSupabase`; the request's `context.tenant` selects the
1121
+ * active tenant only among the memberships the claims hold. A caller without
1122
+ * claims is anonymous, and a throw denies.
1123
+ */
1124
+ function authorizer(options) {
1125
+ const { operations } = options;
1126
+ const instanceFor = async (claims, tenant) => {
1127
+ const session = claims === void 0 ? null : {
1128
+ kind: "user",
1129
+ claims
1130
+ };
1131
+ const base = await getPermDock(options.policy, subjectFromBetterSupabase(session, options.subject), options.options);
1132
+ return typeof tenant === "string" ? base.tenant(tenant) : base;
1133
+ };
1134
+ return Object.freeze({
1135
+ apiVersion: 1,
1136
+ name: "PermDock",
1137
+ key(ref) {
1138
+ if (!isPermission(ref)) throw new TypeError("Not a PermDock permission");
1139
+ return ref.key;
1140
+ },
1141
+ ...operations === void 0 ? {} : { forOperation(table, operation) {
1142
+ if (!OPERATIONS.has(operation) || !Object.hasOwn(operations, table)) return void 0;
1143
+ return operations[table]?.[operation];
1144
+ } },
1145
+ async evaluate(request) {
1146
+ try {
1147
+ return decideWith(await instanceFor(claimsOf(request), request.context["tenant"]), request);
1148
+ } catch {
1149
+ return denied("The permission check failed.");
1150
+ }
1151
+ },
1152
+ evaluations(requests) {
1153
+ const cache = /* @__PURE__ */ new Map();
1154
+ const shared = (claims, tenant) => {
1155
+ let byTenant = cache.get(claims);
1156
+ if (byTenant === void 0) {
1157
+ byTenant = /* @__PURE__ */ new Map();
1158
+ cache.set(claims, byTenant);
1159
+ }
1160
+ let instance = byTenant.get(tenant);
1161
+ if (instance === void 0) {
1162
+ instance = instanceFor(claims, tenant);
1163
+ byTenant.set(tenant, instance);
1164
+ }
1165
+ return instance;
1166
+ };
1167
+ return Promise.all(requests.map(async (request) => {
1168
+ try {
1169
+ return decideWith(await shared(claimsOf(request), request.context["tenant"]), request);
1170
+ } catch {
1171
+ return denied("The permission check failed.");
1172
+ }
1173
+ }));
1174
+ }
1175
+ });
1176
+ }
1177
+ //#endregion
317
1178
  //#region src/better-supabase/policies.ts
318
1179
  /** PermDock splits a key with row conditions into `key#1`, `key#2`, ... in the policies it generates. */
319
1180
  const SPLIT_KEY = /#\d+$/u;
@@ -401,113 +1262,6 @@ function topicPolicy(access, options) {
401
1262
  });
402
1263
  }
403
1264
  //#endregion
404
- //#region src/better-supabase/api-keys.ts
405
- const seconds = (instant) => Math.floor(instant.epochMilliseconds / 1e3);
406
- /** When a key stops verifying: its expiry, or the end of a rotated key's grace period. */
407
- function endOf(key) {
408
- const { expiresAt, revokedAt } = key;
409
- if (revokedAt === void 0) return expiresAt;
410
- if (expiresAt === void 0) return revokedAt;
411
- return revokedAt.epochMilliseconds < expiresAt.epochMilliseconds ? revokedAt : expiresAt;
412
- }
413
- /** `rls.apiKeys.serviceRoles` from the manifest, when it has `rls.apiKeys`. */
414
- function manifestServiceRoles(manifest) {
415
- return manifest === void 0 ? void 0 : parseSupabaseManifest(manifest).rls.apiKeys?.serviceRoles;
416
- }
417
- /**
418
- * A personal key as a `user` credential acting as its user, a tenant key as
419
- * a `service` credential holding `roles` in its tenant; `null` when `*`
420
- * has no expansion, a tenant key has no roles, or the record is invalid.
421
- */
422
- function keyCredential(key, roles, allPermissions) {
423
- const scopes = key.scopes.includes("*") ? allPermissions?.map((permission) => permission.key) : key.scopes;
424
- if (scopes === void 0) return null;
425
- const owner = key.userId;
426
- const base = {
427
- v: 1,
428
- id: key.id,
429
- permissions: scopes.map((permission) => ({ permission })),
430
- createdBy: key.createdBy ?? owner ?? key.id,
431
- createdAt: key.createdAt ?? 0,
432
- ...key.expiresAt === void 0 ? {} : { expiresAt: key.expiresAt },
433
- name: key.name
434
- };
435
- if (owner !== void 0) return parseCredential({
436
- ...base,
437
- kind: "user",
438
- principal: owner,
439
- ...key.organizationId === void 0 ? {} : { tenant: key.organizationId }
440
- }) ?? null;
441
- if (roles === void 0 || key.organizationId === void 0) return null;
442
- return parseCredential({
443
- ...base,
444
- kind: "service",
445
- principal: key.id,
446
- tenant: key.organizationId,
447
- roles
448
- }) ?? null;
449
- }
450
- /**
451
- * better-supabase's API keys as a `CredentialVerifier`, for
452
- * `subjectFromApiKey` from `permdock/server`, which reads PermDock's
453
- * `pdk_<id>_<secret>` format: create the keys with `prefix: "pdk"`. The
454
- * credential id is the key's `publicId`, the id in the token. A personal key
455
- * is a `user` credential acting as its user (inside its tenant when the key
456
- * is limited to one), a tenant key a `service` credential holding
457
- * `serviceRoles` in its tenant, and the key's scopes are the credential's
458
- * permissions. better-supabase records the use when it verifies, so there is
459
- * no `touch`. A rotated key in its grace period verifies, and its credential
460
- * expires when the grace period ends. An invalid, revoked, expired or
461
- * rate-limited key, and a failed lookup, are `null`. `permdock/server` exports a different
462
- * `apiKeyVerifier`, over the application's own key records.
463
- */
464
- function apiKeyVerifier(options) {
465
- const fallback = manifestServiceRoles(options.manifest);
466
- return freezeDeep({ async verify(secret) {
467
- try {
468
- const checked = await options.keys.verify(secret);
469
- if (!checked.ok || checked.data.status !== "ok") return null;
470
- const { key } = checked.data;
471
- if (key.state !== "active" && key.state !== "grace") return null;
472
- const end = endOf(key);
473
- const roles = typeof options.serviceRoles === "function" ? options.serviceRoles(key) : options.serviceRoles ?? fallback;
474
- return keyCredential({
475
- id: key.publicId,
476
- name: key.name,
477
- scopes: key.scopes,
478
- createdAt: seconds(key.createdAt),
479
- ...key.organizationId === void 0 ? {} : { organizationId: key.organizationId },
480
- ...key.userId === void 0 ? {} : { userId: key.userId },
481
- ...key.createdBy === void 0 ? {} : { createdBy: key.createdBy },
482
- ...end === void 0 ? {} : { expiresAt: seconds(end) }
483
- }, roles, options.allPermissions);
484
- } catch {
485
- return null;
486
- }
487
- } });
488
- }
489
- /**
490
- * The `claim` and `tenantClaim` options of better-supabase's
491
- * `apiKeyClaims()`, from the manifest's `rls.apiKeys`, so a key's token
492
- * carries the scopes ceiling, tenant and roles the `rls generate` helpers
493
- * read. Throws when the manifest has no `rls.apiKeys`.
494
- */
495
- function apiKeyClaimOptions(manifest) {
496
- const rls = parseSupabaseManifest(manifest).rls;
497
- const apiKeys = rls.apiKeys;
498
- if (!apiKeys) throw new TypeError("PermDock: the manifest has no rls.apiKeys. Set rls.apiKeys in permdock.config.ts, then run `permdock rls generate` and `permdock supabase inspect --out`.");
499
- return freezeDeep({
500
- claim: {
501
- name: apiKeys.claim,
502
- scopes: apiKeys.scopes,
503
- tenant: apiKeys.tenant,
504
- roles: apiKeys.roles,
505
- serviceRoles: apiKeys.serviceRoles
506
- },
507
- tenantClaim: rls.tenantClaim
508
- });
509
- }
510
- //#endregion
511
1265
  //#region src/better-supabase/credentials.ts
512
1266
  /**
513
1267
  * `provider` with every `getToken`, `startAuthorization`,
@@ -577,7 +1331,7 @@ function toolPolicy(options) {
577
1331
  const permission = tool.meta;
578
1332
  if (!isPermission(permission)) return false;
579
1333
  try {
580
- return mayUse(await options.permdock(ctx), permission);
1334
+ return (await options.permdock(ctx)).mayUse(permission);
581
1335
  } catch {
582
1336
  return false;
583
1337
  }
@@ -585,64 +1339,153 @@ function toolPolicy(options) {
585
1339
  });
586
1340
  }
587
1341
  //#endregion
588
- //#region src/better-supabase/subject.ts
589
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
590
- /** `session` with the short codes in `claim` turned back into feature keys. */
591
- function decodedFeatures(session, claim, keys) {
592
- const claims = session?.claims;
593
- if (session === null || session === void 0 || !isRecord(claims)) return session;
594
- const coded = claims[claim];
595
- if (!isRecord(coded)) return session;
596
- const byCode = new Map(Object.entries(keys).map(([key, code]) => [code, key]));
597
- const decoded = Object.fromEntries(Object.entries(coded).map(([tenant, codes]) => [tenant, Array.isArray(codes) ? codes.flatMap((code) => {
598
- const key = typeof code === "string" ? byCode.get(code) : void 0;
599
- return key === void 0 ? [] : [key];
600
- }) : codes]));
601
- return {
602
- ...session,
603
- claims: {
604
- ...claims,
605
- [claim]: decoded
606
- }
1342
+ //#region src/better-supabase/principals.ts
1343
+ /**
1344
+ * A `PrincipalSource` over better-supabase: `principal(id)` maps the claims
1345
+ * `claims` loads with `subjectFromBetterSupabase`, and `principalByIdentity`
1346
+ * follows `identities` to a user or a contact. Claims for another user, a
1347
+ * link to a non-contact principal and a missing link are `null`.
1348
+ */
1349
+ function principalSource(options) {
1350
+ const principal = async (id, lookup = {}) => {
1351
+ const claims = await options.claims(id, lookup.tenant === void 0 ? {} : { tenant: lookup.tenant });
1352
+ if (!isRecord(claims)) return null;
1353
+ const found = subjectFromBetterSupabase({
1354
+ kind: "user",
1355
+ claims
1356
+ }, options.subject).principal;
1357
+ return found?.id === id ? freezeDeep(structuredClone(found)) : null;
607
1358
  };
1359
+ const { identities } = options;
1360
+ return Object.freeze({
1361
+ principal,
1362
+ ...identities === void 0 ? {} : { async principalByIdentity(identity) {
1363
+ const link = await identities(identity);
1364
+ if (!isRecord(link)) return null;
1365
+ if ("user" in link) return typeof link.user === "string" ? principal(link.user) : null;
1366
+ const contact = link.contact;
1367
+ return isRecord(contact) && contact.kind === "contact" ? freezeDeep(structuredClone(contact)) : null;
1368
+ } }
1369
+ });
608
1370
  }
609
- function isApiKeySession(session) {
610
- if (typeof session !== "object" || session === null) return false;
611
- const record = Object.fromEntries(Object.entries(session));
612
- return record["kind"] === "apiKey" && typeof record["keyId"] === "string" && typeof record["name"] === "string" && Array.isArray(record["scopes"]) && record["scopes"].every((scope) => typeof scope === "string");
1371
+ //#endregion
1372
+ //#region src/better-supabase/share-links.ts
1373
+ const DEFAULT_TTL = 300;
1374
+ const MAX_TTL = 3600;
1375
+ function ownEntry(record, key) {
1376
+ return Object.hasOwn(record, key) ? record[key] : void 0;
613
1377
  }
614
- function apiKeySubject(session, options) {
615
- const roles = typeof options.serviceRoles === "function" ? options.serviceRoles(session) : options.serviceRoles ?? manifestServiceRoles(options.manifest);
616
- const credential = keyCredential({
617
- id: session.keyId,
618
- name: session.name,
619
- scopes: session.scopes,
620
- ...session.organizationId === void 0 ? {} : { organizationId: session.organizationId },
621
- ...session.userId === void 0 ? {} : { userId: session.userId },
622
- ...session.createdBy === void 0 ? {} : { createdBy: session.createdBy },
623
- ...session.createdAt === void 0 ? {} : { createdAt: session.createdAt }
624
- }, roles, listPermissions(options.permissions));
625
- return credential === null ? anonymousSubject() : credentialSubject(credential, { permissions: options.permissions });
1378
+ function isRow(value) {
1379
+ if (typeof value !== "object" || value === null) return false;
1380
+ const row = Object.fromEntries(Object.entries(value));
1381
+ const expiresAt = row["expiresAt"];
1382
+ return isId(row["id"]) && isId(row["organizationId"]) && isId(row["subjectType"]) && isId(row["subjectId"]) && typeof row["role"] === "string" && (row["versionId"] === void 0 || typeof row["versionId"] === "string") && (expiresAt === void 0 || typeof expiresAt === "object" && expiresAt !== null && "epochMilliseconds" in expiresAt && typeof expiresAt.epochMilliseconds === "number" && Number.isFinite(expiresAt.epochMilliseconds));
626
1383
  }
627
1384
  /**
628
- * `subjectFromSupabaseSession` for better-supabase's `AuthSession`, reading
629
- * `memberships` for the memberships and the entitlements module's `features`
630
- * claim for `principal.plans` (`plans` names another claim). With `apiKeys`,
631
- * a verified `apiKey` session is the key's credential subject: a personal key
632
- * acts as its user within the key's scopes, a tenant key as a `service`
633
- * principal holding `serviceRoles`. Without `apiKeys`, and for `anon`,
634
- * `service` and `invalid` sessions, the subject is anonymous.
1385
+ * Resolves a better-supabase share-link token into the `link` subject it
1386
+ * acts as, for `exchangeCapability` and `--capabilities` RLS. The row is
1387
+ * read on every call, so a revoked or expired link stops resolving at
1388
+ * once. An unknown token, subject type or role, a malformed row, a broken
1389
+ * link policy, a `resolve` error and a throw are the anonymous subject. So
1390
+ * is a link pinned to a version: a capability grants the subject's current
1391
+ * rows, so a pinned link reads through `versions.getShared(token)` instead.
635
1392
  */
636
- function subjectFromBetterSupabase(session, options = {}) {
637
- const { apiKeys, plans = "features", ...rest } = options;
638
- if (isApiKeySession(session)) return apiKeys === void 0 ? anonymousSubject() : apiKeySubject(session, apiKeys);
639
- const claim = typeof plans === "string" ? plans : plans.claim ?? "features";
640
- const keys = typeof plans === "string" ? void 0 : plans.keys;
641
- return subjectFromSupabaseSession(keys === void 0 ? session : decodedFeatures(session, claim, keys), {
642
- memberships: "memberships",
643
- ...rest,
644
- plans: claim
645
- });
1393
+ async function subjectFromShareLink(token, options) {
1394
+ if (token === void 0 || token === null || token.length === 0) return anonymousSubject();
1395
+ const deny = (reason, cause) => {
1396
+ options.onAuth?.(compact({
1397
+ reason,
1398
+ cause,
1399
+ source: "share-link",
1400
+ requestId: options.requestId
1401
+ }));
1402
+ return anonymousSubject();
1403
+ };
1404
+ let row;
1405
+ try {
1406
+ const resolved = await options.shareLinks.resolve(token);
1407
+ if (!resolved.ok) return deny("source-threw", "resolve-failed");
1408
+ if (resolved.data === null) return deny("invalid-token", "share-link-unknown");
1409
+ if (!isRow(resolved.data)) return deny("schema", "share-link-malformed");
1410
+ row = resolved.data;
1411
+ } catch {
1412
+ return deny("source-threw");
1413
+ }
1414
+ if (row.versionId !== void 0) return deny("invalid-token", "share-link-pinned");
1415
+ const target = ownEntry(options.links, row.subjectType);
1416
+ if (target === void 0) return deny("invalid-token", "subject-type-unknown");
1417
+ const roles = idList(ownEntry(target.roles, row.role));
1418
+ if (roles === void 0 || roles.length === 0) return deny("unknown-role");
1419
+ const now = nowSeconds(options.now);
1420
+ const ttl = Math.min(Math.max(1, options.ttl ?? DEFAULT_TTL), MAX_TTL);
1421
+ const rowExpiry = row.expiresAt === void 0 ? Number.POSITIVE_INFINITY : Math.floor(row.expiresAt.epochMilliseconds / 1e3);
1422
+ const expiresAt = Math.min(now + ttl, rowExpiry);
1423
+ if (expiresAt <= now) return deny("invalid-token", "expired");
1424
+ let capability;
1425
+ try {
1426
+ capability = capabilityOf({
1427
+ id: row.id,
1428
+ on: {
1429
+ resource: target.resource,
1430
+ id: row.subjectId
1431
+ },
1432
+ roles,
1433
+ expiresAt
1434
+ });
1435
+ } catch {
1436
+ return deny("schema", "invalid-claims");
1437
+ }
1438
+ try {
1439
+ const policy = await options.linkPolicy?.(capability, row);
1440
+ if (policy !== void 0 && linkPolicyViolation(capability, policy, void 0) !== void 0) return deny("invalid-token", "link-policy");
1441
+ } catch {
1442
+ return deny("source-threw");
1443
+ }
1444
+ return capabilitySubject(capability);
1445
+ }
1446
+ //#endregion
1447
+ //#region src/better-supabase/workflow.ts
1448
+ /** Whether `permdock` is granted the permission `key` (a catalog key or scope) with no row. */
1449
+ function grants(permdock, key) {
1450
+ try {
1451
+ const permission = findPermission(permdock.permissions, key);
1452
+ return permission !== void 0 && permdock.decide(permission, void 0).outcome === "granted";
1453
+ } catch {
1454
+ return false;
1455
+ }
1456
+ }
1457
+ /**
1458
+ * The `can` option of better-supabase's `authorizeHook`, for the caller's
1459
+ * `permdock`: `true` only when the caller is a member of the hook's tenant
1460
+ * and `decide` grants the permission its `hookMetadata` names there. An
1461
+ * unknown key, `approval-required` and a throw are `false`.
1462
+ */
1463
+ function hookCan(permdock) {
1464
+ return (tenant, key) => {
1465
+ try {
1466
+ return permdock.tenants().includes(tenant) && grants(permdock.tenant(tenant), key);
1467
+ } catch {
1468
+ return false;
1469
+ }
1470
+ };
1471
+ }
1472
+ /**
1473
+ * The `can` option of better-supabase's `protectWebHandler`: `undefined`
1474
+ * (401) when the request has no signed-in principal or `getPermDock`
1475
+ * throws, otherwise whether `decide` grants the permission in the request's
1476
+ * active tenant. An unknown key and `approval-required` are `false` (403).
1477
+ */
1478
+ function handlerCan(source) {
1479
+ return async (request, key) => {
1480
+ let permdock;
1481
+ try {
1482
+ permdock = await source.getPermDock(request);
1483
+ } catch {
1484
+ return;
1485
+ }
1486
+ if (permdock.subject.principal === null) return;
1487
+ return grants(permdock, key);
1488
+ };
646
1489
  }
647
1490
  //#endregion
648
- export { apiKeyClaimOptions, apiKeyVerifier, authorizationProvider, bucketPolicy, credentialGuard, subjectFromBetterSupabase, toolPolicy, topicPolicy };
1491
+ export { accessReviewApplier, apiKeyClaimOptions, apiKeyVerifier, authorizationHooks, authorizationProvider, authorizer, bucketPolicy, credentialGuard, handlerCan, hookCan, principalSource, subjectFromBetterSupabase, subjectFromShareLink, subscriberPolicy, supportPolicy, toolPolicy, topicPolicy };