permdock 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/README.md +37 -35
  2. package/dist/{schema-Bol6gqXA.js → 2020-C9VN-x8c.js} +5 -130
  3. package/dist/a2a/index.d.ts +6 -6
  4. package/dist/a2a/index.js +14 -17
  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 -35
  10. package/dist/ai-sdk/index.js +37 -23
  11. package/dist/{app-data-CV8pqqMi.js → app-data-MuyzVqX8.js} +2 -1
  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/arazzo-B6lebmiQ.js +36 -0
  19. package/dist/{arazzo-C4BcskbX.js → arazzo-BWhNF-k0.js} +20 -14
  20. package/dist/args-IIolxxKA.js +197 -0
  21. package/dist/astro/index.d.ts +43 -0
  22. package/dist/astro/index.js +27 -0
  23. package/dist/audience-Dg8XsRaO.js +55 -0
  24. package/dist/auth-DRLEizbL.js +178 -0
  25. package/dist/authzen/index.d.ts +26 -6
  26. package/dist/authzen/index.js +94 -79
  27. package/dist/automation-BMZPIVUM.d.ts +114 -0
  28. package/dist/automation-DVjOckjV.js +157 -0
  29. package/dist/better-auth/index.d.ts +8 -6
  30. package/dist/better-auth/index.js +9 -8
  31. package/dist/better-supabase/index.d.ts +357 -30
  32. package/dist/better-supabase/index.js +936 -194
  33. package/dist/{bind-BzqG8SMb.d.ts → bind--9EFVYWF.d.ts} +12 -10
  34. package/dist/{bind-kk-uyCwH.js → bind-CZzhx-Vj.js} +14 -10
  35. package/dist/body-CH5of8Ne.js +10 -0
  36. package/dist/body-text-QcXok_SI.js +52 -0
  37. package/dist/{build-instance-DIIV9DbC.js → build-instance-BMnAudHn.js} +4 -3
  38. package/dist/{canonical-json-CFKyFOke.js → canonical-json-BC8_nMTw.js} +5 -3
  39. package/dist/{capability-CksyCWDH.d.ts → capability-BSslw1QV.d.ts} +44 -25
  40. package/dist/{capability-txATtKV4.js → capability-N1K9IpqS.js} +68 -42
  41. package/dist/casbin-Cr7RLtA3.js +30 -0
  42. package/dist/casbin-Dyu2gnUD.js +234 -0
  43. package/dist/casl-64gpT-8x.js +24 -0
  44. package/dist/casl-DjEDhTCP.js +299 -0
  45. package/dist/catalog/index.d.ts +74 -2
  46. package/dist/catalog/index.js +51 -2
  47. package/dist/{catalog-tZUyOfOv.js → catalog-BECMPrwc.js} +51 -6
  48. package/dist/catalog-DjhMYYCq.js +1 -0
  49. package/dist/catalog-R_A__RsH.js +52 -0
  50. package/dist/{catalog-doc-DWtg0gGR.js → catalog-doc-C9wjsfNY.js} +45 -23
  51. package/dist/catalog-fingerprint-CCP0-yQS.js +153 -0
  52. package/dist/chat/index.d.ts +2 -0
  53. package/dist/chat/index.js +224 -0
  54. package/dist/chat-sdk/index.d.ts +87 -0
  55. package/dist/chat-sdk/index.js +197 -0
  56. package/dist/claims-map-7oQ3ue4X.js +13 -0
  57. package/dist/claims-map-Ca-QX8C6.d.ts +32 -0
  58. package/dist/claude-agent/index.d.ts +7 -14
  59. package/dist/claude-agent/index.js +24 -27
  60. package/dist/clerk/index.d.ts +9 -8
  61. package/dist/clerk/index.js +9 -14
  62. package/dist/cli/bin.js +36 -17
  63. package/dist/cli/index.d.ts +19 -5
  64. package/dist/cli/index.js +164 -4
  65. package/dist/cloud/index.d.ts +102 -63
  66. package/dist/cloud/index.js +239 -340
  67. package/dist/cloud-CIoHNevm.js +87 -0
  68. package/dist/codemod-Bmj9dWMH.js +37 -0
  69. package/dist/codemod-Cxvvc70T.js +2084 -0
  70. package/dist/{collect-2SrB7lfk.js → collect-CrZJJnEF.js} +75 -11
  71. package/dist/collect-DloHbDVa.js +55 -0
  72. package/dist/collect-FlXJlhus.js +1 -0
  73. package/dist/command-L8EstFNm.js +6 -0
  74. package/dist/compile/index.d.ts +1 -1
  75. package/dist/compile/index.js +1 -2
  76. package/dist/{compile-CcEu2ZQ1.js → compile-CMqMx_IN.js} +11 -8
  77. package/dist/{compile-CtTP7eur.d.ts → compile-Ckm2HqBZ.d.ts} +5 -3
  78. package/dist/completion-BqhRBJD8.js +143 -0
  79. package/dist/config-B46rm9sc.js +26 -0
  80. package/dist/{config-BIjREpyE.js → config-bs_kZeYq.js} +12 -7
  81. package/dist/{config-report--Xb9APJS.js → config-report-DUgj62Ai.js} +15 -5
  82. package/dist/{connection-DUsczpB5.js → connection-DjhIJSs2.js} +6 -5
  83. package/dist/{context-DieZVJXu.js → context-bbFHR-0D.js} +2 -2
  84. package/dist/convex/index.d.ts +11 -10
  85. package/dist/convex/index.js +6 -4
  86. package/dist/create-0VZ7Pdiu.d.ts +107 -0
  87. package/dist/create-BDS7wc3y.d.ts +138 -0
  88. package/dist/create-BFfm17UA.d.ts +110 -0
  89. package/dist/create-BWRSXgOF.js +403 -0
  90. package/dist/create-C3b4B5eF.js +130 -0
  91. package/dist/{create-BBO24vj6.js → create-DnrmLnLW.js} +31 -32
  92. package/dist/{credential-kue0qpbm.js → credential-DM2um3ha.js} +152 -31
  93. package/dist/credentials-DPyHmYz5.js +74 -0
  94. package/dist/delegation-DaXVdDhM.js +286 -0
  95. package/dist/delivery/index.d.ts +5 -0
  96. package/dist/delivery/index.js +5 -0
  97. package/dist/delivery-DCAgNyCn.js +499 -0
  98. package/dist/delivery-event-xX4obB8F.js +40 -0
  99. package/dist/{denied-text-DfRk15uw.d.ts → denied-text-D8jDHgsl.d.ts} +5 -5
  100. package/dist/{describe-C3ldA9os.d.ts → describe-BZUsixiL.d.ts} +2 -2
  101. package/dist/{describe-Cgkp7eP0.js → describe-BybMRY7A.js} +6 -2
  102. package/dist/device-BARQ8eYG.js +193 -0
  103. package/dist/diff-CxZo9pTq.js +37 -0
  104. package/dist/{diff-rpqoe7Ag.js → diff-DCePiN6M.js} +60 -54
  105. package/dist/doctor-CxiwSfqH.js +44 -0
  106. package/dist/{doctor-owlv6ddh.js → doctor-DFjUxf2f.js} +245 -50
  107. package/dist/drizzle/index.d.ts +5 -3
  108. package/dist/drizzle/index.js +2 -2
  109. package/dist/elysia/index.d.ts +16 -13
  110. package/dist/elysia/index.js +13 -12
  111. package/dist/env-DJZrPjRi.js +8 -0
  112. package/dist/errors-BDUwg2qC.js +27 -0
  113. package/dist/{errors-drnfxYtm.js → errors-BKy0EfhN.js} +5 -9
  114. package/dist/errors-BWXBMaKK.d.ts +11 -0
  115. package/dist/{errors-OD_4c__H.d.ts → errors-C2PXIv4c.d.ts} +2 -2
  116. package/dist/errors-CWTY6uih.js +143 -0
  117. package/dist/evaluate-_R8QkWaf.js +1732 -0
  118. package/dist/evaluations-16UqqQ6W.d.ts +32 -0
  119. package/dist/{evaluations-C9YJsv3H.js → evaluations-DZ2CbV5k.js} +145 -34
  120. package/dist/eve/index.d.ts +12 -16
  121. package/dist/eve/index.js +22 -28
  122. package/dist/event-time-CsegxXxB.js +23 -0
  123. package/dist/expiry-D4_YKRKD.js +52 -0
  124. package/dist/express/index.d.ts +13 -10
  125. package/dist/express/index.js +12 -11
  126. package/dist/fastify/index.d.ts +25 -22
  127. package/dist/fastify/index.js +13 -12
  128. package/dist/fetch-adapter-g0ypLkID.js +120 -0
  129. package/dist/fetch-adapter-qKJBuRqy.d.ts +31 -0
  130. package/dist/fields-D9tPVUUS.js +203 -0
  131. package/dist/files-C5ZI27eV.js +124 -0
  132. package/dist/{fixtures-h2eG2OZV.js → fixtures-B7XrLeY7.js} +8 -7
  133. package/dist/flags-C3DktBDY.js +86 -0
  134. package/dist/{from-snapshot-7REQZe0H.js → from-snapshot-D2lZt4Dd.js} +55 -52
  135. package/dist/{grantee-CXY-0C87.js → grantee-Cn1HyUsI.js} +3 -3
  136. package/dist/{grantee-conditions-MQs5NIuX.js → grantee-conditions-CcXf8hMM.js} +4 -3
  137. package/dist/{grantee-match-fxYp9Nmt.js → grantee-match-DHArqY82.js} +5 -4
  138. package/dist/h3/index.d.ts +37 -0
  139. package/dist/h3/index.js +35 -0
  140. package/dist/headers-BIjfMaoC.js +8 -0
  141. package/dist/headers-CIbDTUTK.d.ts +7 -0
  142. package/dist/{helpers-C9YmyBIa.js → helpers-DTcZOzhF.js} +45 -38
  143. package/dist/hono/index.d.ts +12 -9
  144. package/dist/hono/index.js +13 -12
  145. package/dist/hosted-BZIAYvon.js +315 -0
  146. package/dist/hosted-DBDX0M-P.d.ts +78 -0
  147. package/dist/{http-DPuDaChb.d.ts → http-B_LphE5Y.d.ts} +1 -2
  148. package/dist/http-DXJ37RDA.js +46 -0
  149. package/dist/{http-BEbdMZ05.js → http-ujKm0JXE.js} +1 -2
  150. package/dist/{import-UHk1N7vH.js → import-CNV7I2Dw.js} +25 -20
  151. package/dist/{import-BFIlhetD.js → import-DMmOG27O.js} +190 -53
  152. package/dist/index-BcHcPyIo.d.ts +110 -0
  153. package/dist/index-CaxrMD1A.d.ts +1 -0
  154. package/dist/index-Chrs5fQ-.d.ts +210 -0
  155. package/dist/index-CxGUswIC.d.ts +431 -0
  156. package/dist/index-D8EovfsP.d.ts +91 -0
  157. package/dist/{index-UdVQgq48.d.ts → index-DOVf7RJv.d.ts} +431 -81
  158. package/dist/{index-BwMe94Xq.d.ts → index-SGdIQyzc.d.ts} +34 -16
  159. package/dist/index.d.ts +19 -15
  160. package/dist/index.js +31 -21
  161. package/dist/init-DeMYsnYJ.js +96 -0
  162. package/dist/init-GG6Jh8HD.js +95 -0
  163. package/dist/{instance-options-plkrsAog.d.ts → instance-options-Omfd793B.d.ts} +2 -2
  164. package/dist/instance-snapshot-Cxq4ygFc.js +600 -0
  165. package/dist/instant-BluqDvyS.js +19 -0
  166. package/dist/internals-Dk8A8fX3.js +17 -0
  167. package/dist/{json-schema-DBkdUuCO.js → json-schema-B-vAwlde.js} +45 -6
  168. package/dist/json-schema-CN_iLFH2.d.ts +40 -0
  169. package/dist/jwt/index.d.ts +18 -11
  170. package/dist/jwt/index.js +156 -135
  171. package/dist/{kernel-CB3LrIJM.js → kernel-5JatXyND.js} +52 -24
  172. package/dist/kysely/index.d.ts +5 -3
  173. package/dist/kysely/index.js +2 -2
  174. package/dist/load-BxEtwlBS.js +1 -0
  175. package/dist/{load-C3_oTHyO.js → load-DFS_ry0W.js} +22 -6
  176. package/dist/{load-jose-BL6LH4bL.js → load-jose-DZ4dnjri.js} +1 -4
  177. package/dist/login-Dfe5RlYk.js +68 -0
  178. package/dist/login-imhuwvn3.js +254 -0
  179. package/dist/manifest-C8IzW3Do.js +42 -0
  180. package/dist/manifest-CtKR3GXL.js +370 -0
  181. package/dist/manifest-json-CAGzls-C.js +9 -0
  182. package/dist/{manifest-uDS9y5de.d.ts → manifest-mx5ghSFM.d.ts} +9 -2
  183. package/dist/manifest-schema-C0kK_8V0.js +399 -0
  184. package/dist/{map-claims-AVlBaJoa.js → map-claims-D3oRmTk0.js} +13 -10
  185. package/dist/{map-error-BE27PYkE.js → map-error-TCAdw-eW.js} +3 -3
  186. package/dist/{markers-CiwAOZtO.js → markers-C6joDI3o.js} +2 -1
  187. package/dist/mcp/index.d.ts +22 -12
  188. package/dist/mcp/index.js +57 -39
  189. package/dist/{migrate-BwlB5-Wk.js → migrate-BShP7Vf4.js} +53 -14
  190. package/dist/nest/index.d.ts +12 -9
  191. package/dist/nest/index.js +15 -14
  192. package/dist/next/index.d.ts +18 -21
  193. package/dist/next/index.js +13 -11
  194. package/dist/next/plugin.d.ts +1 -1
  195. package/dist/next/plugin.js +2 -2
  196. package/dist/node/index.d.ts +10 -10
  197. package/dist/node/index.js +7 -9
  198. package/dist/{ocsf-uYFXGVw2.js → ocsf-BFp-PZZA.js} +22 -11
  199. package/dist/openai/index.d.ts +9 -16
  200. package/dist/openai/index.js +17 -24
  201. package/dist/openapi/index.d.ts +3 -3
  202. package/dist/openapi/index.js +1 -1
  203. package/dist/{openapi-DBzHQbov.js → openapi-B7HZ0f-r.js} +8 -8
  204. package/dist/{openapi-BJdBmYDG.js → openapi-DTaEmTSW.js} +10 -9
  205. package/dist/openapi-ahr8rHu3.js +138 -0
  206. package/dist/operation-findings-DgATDpvh.js +36 -0
  207. package/dist/operations-CxybZErY.d.ts +59 -0
  208. package/dist/orpc/index.d.ts +13 -10
  209. package/dist/orpc/index.js +20 -15
  210. package/dist/otel/index.d.ts +3 -2
  211. package/dist/otel/index.js +30 -7
  212. package/dist/output-Dyw8fyPF.js +88 -0
  213. package/dist/output-files-B3IZJksC.js +1 -0
  214. package/dist/{output-files-CgIsvdSa.js → output-files-DhRBBE4n.js} +24 -26
  215. package/dist/{permission-keys-B26jeyUv.js → ownership-BatFgAA4.js} +1698 -869
  216. package/dist/{package-root-DtNnpmUd.js → package-root-Dk_ETdkp.js} +1 -1
  217. package/dist/{parse-CVFQHk6c.js → parse-Cxq0WxrA.js} +2 -2
  218. package/dist/{parse-local-manifest-C-q02Pal.js → parse-local-manifest-CVSs6J9I.js} +1 -1
  219. package/dist/{parse-local-manifest-DvYUR0QL.d.ts → parse-local-manifest-DgqtruPn.d.ts} +1 -1
  220. package/dist/parse-request-D_tUXG6M.js +26 -0
  221. package/dist/paths-DXSHAMIa.js +42 -0
  222. package/dist/pdp/index.d.ts +7 -3
  223. package/dist/pdp/index.js +45 -26
  224. package/dist/{peer-BOr_kJE1.js → peer-C8swJ2GH.js} +23 -4
  225. package/dist/peer-CRvC7Hg1.js +1 -0
  226. package/dist/permdock-B9JxkBwC.js +868 -0
  227. package/dist/permdock-D7eeLJER.d.ts +502 -0
  228. package/dist/{permission-registry-BsblpB-z.js → permission-registry-Br4jSNVi.js} +3 -3
  229. package/dist/permissions-B-wCP5ZC.js +30 -0
  230. package/dist/{permissions-CCJ6lmxB.js → permissions-CZKiUfFM.js} +4 -3
  231. package/dist/{permissions-COH-KRgT.d.ts → permissions-oEWfl-kl.d.ts} +1 -2
  232. package/dist/policies-CO1oOymN.js +295 -0
  233. package/dist/{policy-ur0BBK_e.d.ts → policy-Czo4n3Hs.d.ts} +2 -2
  234. package/dist/{hosted-CBHgkzDl.js → policy-DLZJ5gAp.js} +17 -273
  235. package/dist/policy-from-catalog-DzhEc3Y9.js +450 -0
  236. package/dist/policy-module-CTlwMvLI.js +89 -0
  237. package/dist/{policy-types-sp9P1iIV.d.ts → policy-types-DzwivBgR.d.ts} +434 -631
  238. package/dist/postgrest-CW0IqC-U.js +566 -0
  239. package/dist/postgrest-NUqZT-RB.d.ts +100 -0
  240. package/dist/powersync-BHfuehf3.js +62 -0
  241. package/dist/powersync-CbzkqL_M.js +1 -0
  242. package/dist/{powersync-cZe4kzhL.js → powersync-bYUDLOAO.js} +23 -24
  243. package/dist/preset/index.d.ts +146 -0
  244. package/dist/preset/index.js +1 -0
  245. package/dist/preset-DZdU0tne.js +1768 -0
  246. package/dist/principal-source-pHneukrx.js +103 -0
  247. package/dist/prisma/index.d.ts +6 -3
  248. package/dist/prisma/index.js +2 -2
  249. package/dist/{problem-CnWraVhC.js → problem-CTviyH5L.js} +13 -23
  250. package/dist/{problem-details-rIIo8B2C.d.ts → problem-details-DXlt-vks.d.ts} +1 -1
  251. package/dist/{problem-details-DECUnXAn.js → problem-details-cn5X30gZ.js} +43 -40
  252. package/dist/problem-types-CJ2325SZ.js +5 -0
  253. package/dist/{prompt-CXYhLfsS.js → prompt-DfZLJLIn.js} +2 -2
  254. package/dist/{protected-B3_P7xuw.d.ts → protected-DEPHc2xV.d.ts} +16 -8
  255. package/dist/{protected-BN7BUdY1.js → protected-DfjY5mLA.js} +21 -4
  256. package/dist/{provider-Bi-0HNL3.js → provider-CC-u3bYP.js} +2 -2
  257. package/dist/{provider-kwHC9CZD.d.ts → provider-DMVt6yYn.d.ts} +1 -1
  258. package/dist/{provider-store-Ctt-81Ni.js → provider-store-B5Yrkhzr.js} +2 -2
  259. package/dist/pull-BgIfno6w.js +256 -0
  260. package/dist/pull-ByOks_Tg.js +1 -0
  261. package/dist/push-CmFVBOeI.js +116 -0
  262. package/dist/pushed-policy-BImMTTIM.d.ts +57 -0
  263. package/dist/pushed-policy-DobP7AHI.js +238 -0
  264. package/dist/react/index.d.ts +8 -8
  265. package/dist/react/index.js +6 -6
  266. package/dist/react/provider-client.d.ts +1 -1
  267. package/dist/react/provider-client.js +1 -2
  268. package/dist/react-native/index.d.ts +24 -72
  269. package/dist/react-native/index.js +24 -22
  270. package/dist/{refs-f3imMjNW.js → refs-K6eWUJ0H.js} +1 -1
  271. package/dist/{relation-walk-BXnlLtg2.js → relation-walk-3OyDBF0S.js} +2 -2
  272. package/dist/{relations-CvmSpG67.js → relations-BnCWnJGI.js} +3 -3
  273. package/dist/renames-CGj7YwcQ.js +175 -0
  274. package/dist/{replay-COpmZUfx.d.ts → replay-D7iKcLFe.d.ts} +1 -1
  275. package/dist/{replay-BsOyTrdV.js → replay-Du1tJjG7.js} +2 -1
  276. package/dist/{request-context-BVM7aWNs.js → request-context-BTRTG7OT.js} +3 -3
  277. package/dist/request-schema-CMreVuFM.js +148 -0
  278. package/dist/resend/index.d.ts +126 -0
  279. package/dist/resend/index.js +379 -0
  280. package/dist/{resource-relations-D1Un3pcz.js → resource-relations-D_jw0BFx.js} +1 -1
  281. package/dist/{retire-BFcxL6un.js → retire-CALjIQ7e.js} +2 -2
  282. package/dist/{revocations-Cy06JNx2.d.ts → revocations-R07lFBnX.d.ts} +2 -0
  283. package/dist/rls-BA8an5Ap.js +336 -0
  284. package/dist/{rls-BEktUaqg.js → rls-j6t43t7n.js} +287 -689
  285. package/dist/{role-builder-B4GbKM-i.js → role-builder-CXbis5-X.js} +14 -207
  286. package/dist/{rpc-nYC3cR_u.js → rpc-aPyNi6tA.js} +7 -7
  287. package/dist/run-CNsApDpd.js +1 -0
  288. package/dist/run-DOfMtc9V.js +723 -0
  289. package/dist/run-ticket-BjhTOi6u.d.ts +124 -0
  290. package/dist/run-ticket-sxFI6LAk.js +337 -0
  291. package/dist/{runtime-D_tMXnsq.js → runtime-D3VTAiCA.js} +1 -1
  292. package/dist/{saas-DtaA4JuW.js → saas-FO-Q8oj0.js} +16 -14
  293. package/dist/scan-BOI5tJ-6.js +29 -0
  294. package/dist/{schema-DjQBcAP9.js → schema-B4v0Qrac.js} +102 -8
  295. package/dist/schema-yLuBWWsh.js +130 -0
  296. package/dist/scim/index.d.ts +2 -2
  297. package/dist/scim/index.js +1 -2
  298. package/dist/{scim-vFR1O5oG.js → scim-ClHtpyV3.js} +208 -76
  299. package/dist/{scopes-tvpa34ep.js → scopes-CUPSjo9q.js} +12 -7
  300. package/dist/server/index.d.ts +12 -143
  301. package/dist/server/index.js +12 -9
  302. package/dist/server-DqCT4CgZ.js +453 -0
  303. package/dist/{server-types-DdqBwZN_.d.ts → server-types-BLd4MnBX.d.ts} +51 -68
  304. package/dist/{sha256-BO0swRXL.js → sha256-D9ihqmvd.js} +8 -1
  305. package/dist/{shared-1uhI-e-b.js → shared-CD11nBbC.js} +18 -8
  306. package/dist/{shared-JhMeOsDF.js → shared-DvvFzYXe.js} +8 -9
  307. package/dist/signals-D3NcTKUS.js +32 -0
  308. package/dist/sink-BEZSlMS_.js +957 -0
  309. package/dist/sink-Bj_lDUDq.d.ts +225 -0
  310. package/dist/{skills-BZdRQkxs.js → skills-B6lVdD_W.js} +13 -6
  311. package/dist/skills-CTYwkl44.js +51 -0
  312. package/dist/snapshot-CXUWfsNy.js +269 -0
  313. package/dist/{snapshot-cache-Cod40D-X.js → snapshot-cache-DZHQ0KF6.js} +11 -5
  314. package/dist/{snapshot-cache-J5T7t_vD.d.ts → snapshot-cache-DqK9eDmM.d.ts} +7 -5
  315. package/dist/solid/index.d.ts +11 -12
  316. package/dist/solid/index.js +7 -7
  317. package/dist/solid-start/index.d.ts +52 -0
  318. package/dist/solid-start/index.js +49 -0
  319. package/dist/{sources-Dljy06zL.d.ts → sources-BV9j0DeZ.d.ts} +7 -13
  320. package/dist/{sources-B1-b-R0t.js → sources-vs5dGOsS.js} +125 -119
  321. package/dist/{sql-t8teWOfo.js → sql-B5T1IL83.js} +13 -1
  322. package/dist/src-DLfUWFAI.js +1851 -0
  323. package/dist/ssf/index.d.ts +4 -4
  324. package/dist/ssf/index.js +384 -47
  325. package/dist/standard-webhooks-6lg8v_is.d.ts +88 -0
  326. package/dist/standard-webhooks-DgFFD-Ga.js +228 -0
  327. package/dist/storage-C2bWdvJH.js +132 -0
  328. package/dist/{store-ILkLUj_o.js → store-4u_hxjWa.js} +40 -53
  329. package/dist/{store-options-CbUHDoSe.js → store-options-BqEakYRa.js} +26 -21
  330. package/dist/{stores-BHXdSbw9.js → stores-BbsD4fAR.js} +2 -2
  331. package/dist/{stores-Cxz_80g-.d.ts → stores-ClZUgeoP.d.ts} +7 -8
  332. package/dist/{stream-Dwsg6WwE.d.ts → stream-7-xmnf3U.d.ts} +2 -2
  333. package/dist/{stream-BMui98KK.js → stream-CvdUS9V1.js} +9 -2
  334. package/dist/style-DiKWnAvn.js +1 -0
  335. package/dist/{style-CTm1AXDT.js → style-Uao76wO7.js} +3 -2
  336. package/dist/subject-BGoM5o8B.js +17 -0
  337. package/dist/supabase/index.d.ts +23 -106
  338. package/dist/supabase/index.js +5 -4
  339. package/dist/supabase/middleware.d.ts +7 -6
  340. package/dist/supabase/middleware.js +13 -11
  341. package/dist/supabase-9pqI57pW.js +92 -0
  342. package/dist/{supabase-C1TWlnYt.js → supabase-Dxlp8A94.js} +98 -205
  343. package/dist/supabase-hook-CXcGpHqq.js +1 -0
  344. package/dist/{supabase-hook-Cva3ikXz.js → supabase-hook-DdDQhMlw.js} +951 -60
  345. package/dist/support-session-C6xJQYut.js +158 -0
  346. package/dist/svelte/Protected.svelte +4 -4
  347. package/dist/svelte/index.d.ts +7 -7
  348. package/dist/svelte/index.js +10 -10
  349. package/dist/svelte/runtime.d.ts +3 -3
  350. package/dist/svelte/runtime.js +3 -3
  351. package/dist/svelte/source.d.ts +7 -7
  352. package/dist/svelte/source.js +4 -4
  353. package/dist/sveltekit/index.d.ts +51 -0
  354. package/dist/sveltekit/index.js +27 -0
  355. package/dist/tanstack-start/index.d.ts +51 -0
  356. package/dist/tanstack-start/index.js +40 -0
  357. package/dist/target-Bw37TFNA.js +51 -0
  358. package/dist/{tenancy-DshjqY0n.js → tenancy-DN2QAx9z.js} +5 -5
  359. package/dist/terminal/index.d.ts +11 -9
  360. package/dist/terminal/index.js +23 -287
  361. package/dist/testing/index.d.ts +311 -38
  362. package/dist/testing/index.js +3475 -201
  363. package/dist/testing/saas/index.d.ts +9 -8
  364. package/dist/testing/saas/index.js +2 -2
  365. package/dist/testing/saas/permissions.d.ts +1 -1
  366. package/dist/testing/saas/permissions.js +1 -2
  367. package/dist/text-BMFRfBqc.js +8 -0
  368. package/dist/{text-diff-BdkkYSbi.js → text-diff-BQcUapOb.js} +1 -1
  369. package/dist/thenable-yD7SabqG.js +31 -0
  370. package/dist/time-DkWjdL98.js +71 -0
  371. package/dist/{token-Bx9qy30p.js → token-BLZcjQgt.js} +15 -6
  372. package/dist/{tool-refusal-CQ7OEia9.js → tool-refusal-CMLN5xhn.js} +2 -2
  373. package/dist/trpc/index.d.ts +12 -9
  374. package/dist/trpc/index.js +16 -14
  375. package/dist/{types-7rJVLWe5.d.ts → types-5Ut_pbKy.d.ts} +61 -11
  376. package/dist/{store-9KO4BlkB.d.ts → types-BNORmDM8.d.ts} +100 -75
  377. package/dist/types-BjV-fqcA.d.ts +57 -0
  378. package/dist/{types-CUcFZfpU.d.ts → types-BqAKcRNW.d.ts} +2 -1
  379. package/dist/types-C8-KNsFf.d.ts +107 -0
  380. package/dist/{types-Br9IXUBG.d.ts → types-CCpc2Dcv.d.ts} +77 -17
  381. package/dist/{types-DpWfQdSh.d.ts → types-CZTnPFzM.d.ts} +46 -11
  382. package/dist/{types-CqrjB9o6.d.ts → types-CdlxsYQh.d.ts} +23 -11
  383. package/dist/{types-CIlTojhq.d.ts → types-ClZaWzfb.d.ts} +9 -6
  384. package/dist/types-DX0LaKTb.d.ts +157 -0
  385. package/dist/types-DiQjupG2.d.ts +332 -0
  386. package/dist/types-DqzsnlSA.js +67 -0
  387. package/dist/types-_sHy8BQf.d.ts +65 -0
  388. package/dist/{types-ByUo56un.d.ts → types-fbZ7JEXr.d.ts} +23 -22
  389. package/dist/{types-DHrmBJLH.d.ts → types-tIgIeIKJ.d.ts} +7 -7
  390. package/dist/unplugin/index.d.ts +1 -1
  391. package/dist/unplugin/index.js +2 -2
  392. package/dist/usage-CHOtwL19.js +1 -0
  393. package/dist/usage-DHtE8j4E.js +32 -0
  394. package/dist/{usage-CG9nOYZw.js → usage-T5mXq98v.js} +54 -11
  395. package/dist/{validation-error-BSSw0l_8.d.ts → validation-error-Bg5Tpluk.d.ts} +1 -1
  396. package/dist/{validity-dVrUEPJt.js → validity-CZ7-y0Cj.js} +32 -15
  397. package/dist/{verifier-DMr_Zmb_.js → verifier-DHuh-okk.js} +18 -7
  398. package/dist/{verify-BztUB3GX.js → verify-DbCL9bnB.js} +645 -50
  399. package/dist/{version-BEogC-rB.js → version-CEbQ5UeM.js} +4 -3
  400. package/dist/version-DXkYUISx.js +1 -0
  401. package/dist/{views-CJsrBS1f.js → views-DTIIO-oQ.js} +22 -5
  402. package/dist/{permission-types-Dg39z9Kv.d.ts → vocabulary-D6lPdb4Z.d.ts} +65 -2
  403. package/dist/vue/index.d.ts +9 -10
  404. package/dist/vue/index.js +8 -8
  405. package/dist/waiter-BGUtGYoU.js +67 -0
  406. package/dist/waiter-CKy4MQSY.d.ts +36 -0
  407. package/dist/watch-Cb-NcaUw.js +1 -0
  408. package/dist/{watch-zeucB7eF.js → watch-CszoTJMq.js} +44 -5
  409. package/dist/webmcp/index.d.ts +2 -2
  410. package/dist/webmcp/index.js +3 -3
  411. package/dist/where-scope-Dv-WTP-w.js +306 -0
  412. package/dist/who-can-D0QoYbEb.js +364 -0
  413. package/dist/wire-delegation-Cv3W2eaM.js +36 -0
  414. package/dist/wire-denial-C7PaVYEM.js +27 -0
  415. package/dist/workflow/index.d.ts +5 -0
  416. package/dist/workflow/index.js +223 -0
  417. package/dist/workflow-sdk/index.d.ts +56 -0
  418. package/dist/workflow-sdk/index.js +78 -0
  419. package/dist/{wrap-Cr93qqdP.d.ts → wrap-Bd1Tejur.d.ts} +2 -1
  420. package/dist/{wrap-DWsU_5Bd.js → wrap-gX61EtpB.js} +7 -6
  421. package/package.json +136 -37
  422. package/schemas/access-event-v1.json +118 -0
  423. package/schemas/approval-request-v1.json +34 -1
  424. package/schemas/approval-signature-v1.json +94 -0
  425. package/schemas/automation-event-v1.json +113 -0
  426. package/schemas/capability-v1.json +84 -0
  427. package/schemas/catalog-event-v1.json +87 -0
  428. package/schemas/catalog-v1.json +123 -12
  429. package/schemas/cli-commands-v1.json +164 -0
  430. package/schemas/cli-result-v1.json +96 -0
  431. package/schemas/cloud-api-v1.json +5407 -0
  432. package/schemas/connector-v1.json +408 -0
  433. package/schemas/credential-event-v1.json +81 -0
  434. package/schemas/credential-v1.json +134 -0
  435. package/schemas/decision-event-v1.json +288 -0
  436. package/schemas/delivery-event-v1.json +117 -0
  437. package/schemas/directory-event-v1.json +85 -0
  438. package/schemas/evidence-export-v1.json +54 -0
  439. package/schemas/membership-event-v1.json +93 -0
  440. package/schemas/policy-v1.json +68 -0
  441. package/schemas/pushed-policy-v1.json +236 -0
  442. package/schemas/run-v1.json +162 -0
  443. package/schemas/snapshot-v1.json +138 -0
  444. package/schemas/supabase-claims-v1.json +4 -4
  445. package/schemas/supabase-manifest-v1.json +5 -1
  446. package/schemas/support-session-v1.json +111 -0
  447. package/skills/permdock/SKILL.md +16 -12
  448. package/skills/permdock-agents/SKILL.md +40 -23
  449. package/skills/permdock-agents/references/operations-automations-chat.md +221 -0
  450. package/skills/permdock-agents/references/runtimes.md +22 -18
  451. package/skills/permdock-approvals/SKILL.md +29 -13
  452. package/skills/permdock-approvals/references/delivery-and-waiters.md +172 -0
  453. package/skills/permdock-approvals/references/stores-and-resume.md +21 -7
  454. package/skills/permdock-audit/SKILL.md +3 -3
  455. package/skills/permdock-credentials/SKILL.md +5 -5
  456. package/skills/permdock-credentials/references/tokens-keys-links.md +14 -10
  457. package/skills/permdock-data/SKILL.md +6 -6
  458. package/skills/permdock-data/references/queries-and-rls.md +9 -8
  459. package/skills/permdock-data/references/relationships.md +3 -3
  460. package/skills/permdock-tenancy/SKILL.md +20 -7
  461. package/skills/permdock-tenancy/references/contacts-and-support-sessions.md +122 -0
  462. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +5 -4
  463. package/skills/permdock-wire/SKILL.md +17 -11
  464. package/skills/permdock-wire/references/adapters.md +139 -51
  465. package/skills/permdock-wire/references/policy-options.md +5 -5
  466. package/skills/permdock-wire/references/presets-and-recipes.md +58 -0
  467. package/dist/approvals-DZL76lKW.js +0 -240
  468. package/dist/arazzo-CD8d0WLb.js +0 -54
  469. package/dist/catalog-6yWAX6Ps.js +0 -2
  470. package/dist/catalog-WHG4kBq7.js +0 -48
  471. package/dist/cloud-C7bSDgX4.js +0 -50
  472. package/dist/cloud-PeBN3IEn.js +0 -118
  473. package/dist/collect-Bw2Jdq1q.js +0 -2
  474. package/dist/collect-CQx4hX4N.js +0 -83
  475. package/dist/commands-Cpip4ULx.js +0 -97
  476. package/dist/config-DLt6BvV7.js +0 -36
  477. package/dist/diff-Cnghh_Ip.js +0 -48
  478. package/dist/doctor-CuVJvhV_.js +0 -53
  479. package/dist/duration-xK2jGFVc.js +0 -25
  480. package/dist/errors-CWfoNg9N.js +0 -47
  481. package/dist/errors-DRnGZHzF.js +0 -30
  482. package/dist/expiry-BoBJ8Tvc.js +0 -7
  483. package/dist/fields-fwfXN01n.js +0 -1016
  484. package/dist/headers-BZSDieNT.js +0 -6
  485. package/dist/headers-Cf-zWvsO.d.ts +0 -6
  486. package/dist/ids-DC4V4CFu.js +0 -18
  487. package/dist/index-DgM7w96K.d.ts +0 -6
  488. package/dist/index-Dz7didpC.d.ts +0 -201
  489. package/dist/load-B_shHrmd.js +0 -2
  490. package/dist/manifest-BngWKryL.js +0 -770
  491. package/dist/may-use-BOn_dR_w.js +0 -78
  492. package/dist/openapi-DcQ1Kg40.js +0 -164
  493. package/dist/ownership-BgnbOJFT.js +0 -851
  494. package/dist/permdock-L26udHSk.js +0 -3646
  495. package/dist/powersync-BFSbSElQ.js +0 -62
  496. package/dist/powersync-DCDgZgfZ.js +0 -2
  497. package/dist/rls-55DQTuf5.js +0 -250
  498. package/dist/run-DMcNW_8T.js +0 -174
  499. package/dist/server-ZYEHtNHS.js +0 -268
  500. package/dist/sink-BTtmfPnE.js +0 -141
  501. package/dist/sink-CPN4zLfs.d.ts +0 -104
  502. package/dist/skills-Dle4ZoLF.js +0 -56
  503. package/dist/snapshot-Dc9CHPME.js +0 -1078
  504. package/dist/src-Bb7no-hS.js +0 -639
  505. package/dist/subject-C6dLxqHU.js +0 -24
  506. package/dist/supabase-Dbqr141s.js +0 -83
  507. package/dist/supabase-hook-BULg7Ut_.js +0 -2
  508. package/dist/thenable-QSmvt-vZ.js +0 -65
  509. package/dist/types-C0dee0Qf.d.ts +0 -197
  510. package/dist/types-DvupZE0w.d.ts +0 -9
  511. package/dist/usage-4G6t643F.js +0 -42
  512. package/dist/usage-iFLJ0-rr.js +0 -2
  513. package/dist/vocabulary-hhEFLFoL.d.ts +0 -65
  514. package/dist/watch-C8TpsAl4.js +0 -2
@@ -1,17 +1,647 @@
1
- import { r as isRecord } from "../compact-CAf557ZB.js";
1
+ import { r as isRecord, t as compact } from "../compact-CAf557ZB.js";
2
2
  import { n as freezeDeep } from "../freeze-CrU8nQeX.js";
3
- import { d as scopeList } from "../scopes-tvpa34ep.js";
4
- import { c as listPermissions, o as isPermission } from "../permission-registry-BsblpB-z.js";
5
- import { t as createPermDock } from "../permdock-L26udHSk.js";
6
- import { t as anonymousSubject } from "../subject-C6dLxqHU.js";
7
- import { t as mayUse } from "../may-use-BOn_dR_w.js";
8
- import { o as parseCredential, r as credentialSubject } from "../credential-kue0qpbm.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";
9
20
  import { t as policyRowConditionKeys } from "../row-conditions-h2HythXQ.js";
10
- import { c as sqlIdent, o as quoteSqlLiteral } from "../sql-t8teWOfo.js";
11
- import "../sources-B1-b-R0t.js";
12
- import { o as subjectFromSupabaseSession, t as parseSupabaseManifest } from "../manifest-BngWKryL.js";
13
- import { t as parseCatalog } from "../parse-CVFQHk6c.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";
14
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
15
645
  //#region src/better-supabase/provider.ts
16
646
  /**
17
647
  * The helper each template calls (with `{scope}` unfilled), its default
@@ -93,6 +723,13 @@ const ASSIGN_ANY = {
93
723
  }
94
724
  };
95
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";
96
733
  const OPTIONAL = [
97
734
  "idsWithFor",
98
735
  "isPlatformFor",
@@ -102,10 +739,16 @@ const OPTIONAL = [
102
739
  "canAssignFor",
103
740
  "permissionsFor"
104
741
  ];
105
- /** 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
+ */
106
747
  const ID_TYPES = {
107
748
  uuid: "uuid",
108
749
  text: "text",
750
+ varchar: "text",
751
+ "character varying": "text",
109
752
  bigint: "bigint",
110
753
  int8: "bigint",
111
754
  integer: "integer",
@@ -200,17 +843,97 @@ function tokenHookOf(manifest) {
200
843
  hook: `-- permdock:hook ${manifest.markers.hook}`,
201
844
  grants: `-- permdock:grants ${manifest.markers.grants}`
202
845
  },
203
- grantsCommand: `permdock supabase hook generate --grants-out supabase/migrations/<timestamp>_permdock_hook_grants.sql`
846
+ grantsCommand: grantsCommandOf(manifest.hook.schemas ?? "pg-delta")
204
847
  };
205
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
+ }
206
926
  /**
207
927
  * better-supabase's `authorization` config value, built from PermDock's
208
928
  * manifest and catalog: the scopes, the `rls generate` helpers as SQL
209
929
  * templates, the membership tables, suspension rows, the token hook and
210
930
  * every catalog key with `sqlComplete` set from its `rowConditions`. A
211
931
  * `_for` template and `permissionsFor` are set only when the manifest lists
212
- * their helper, which `rls.mode: 'database'` writes; `canApprove` only with
213
- * `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
214
937
  * tenant scope is ambiguous.
215
938
  */
216
939
  function authorizationProvider(options) {
@@ -266,25 +989,60 @@ function authorizationProvider(options) {
266
989
  const users = rls.suspension?.users;
267
990
  const tenants = rls.suspension?.scopes?.[tenantScope];
268
991
  const approver = options.approver;
269
- let canApprove;
270
- if (approver !== void 0) {
271
- 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.`);
272
- const name = `permitted_${tenantScope}_ids_by_permission`;
273
- 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})`;
274
1017
  requires.push({
275
- function: `${schema}.${sqlIdent(name)}`,
276
- args: "text",
1018
+ function: fn,
1019
+ args: argTypes(holders.args),
277
1020
  role: "postgres"
278
1021
  });
279
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;
280
1033
  const functions = {
281
1034
  ...optional,
282
1035
  idsWith: `${schema}.${TEMPLATES.idsWith.sql}`,
283
1036
  isPlatform: `${schema}.${TEMPLATES.isPlatform.sql}`,
284
- ...canApprove === void 0 ? {} : { canApprove }
1037
+ ...holdersOf === void 0 ? {} : { holdersOf },
1038
+ ...actorMayWrite === void 0 ? {} : { actorMayWrite },
1039
+ ...isApprover === void 0 ? {} : { isApprover },
1040
+ ...permitted === void 0 ? {} : { permitted }
285
1041
  };
1042
+ const operations = catalog === void 0 ? {} : operationsOf(catalog);
1043
+ const delegated = delegatedScopes(catalog);
286
1044
  const scopes = rls.scopes.map((scope) => {
287
- const idType = ID_TYPES[scope.type.trim().toLowerCase()];
1045
+ const idType = ID_TYPES[scope.type.trim().toLowerCase().replaceAll(/\s+/gu, " ")];
288
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.`);
289
1047
  return scope.within === void 0 ? {
290
1048
  name: scope.name,
@@ -309,179 +1067,14 @@ function authorizationProvider(options) {
309
1067
  ...tenants ? { tenant: disabledRow(tenants) } : {}
310
1068
  } } : {},
311
1069
  ...roleSources.length > 0 ? { roleSources } : {},
312
- ...canApprove === void 0 ? {} : { approvals: { distinctApprover: true } },
1070
+ ...Object.keys(operations).length > 0 ? { operations } : {},
1071
+ ...Object.keys(delegated).length > 0 ? { delegation: { scopes: delegated } } : {},
313
1072
  decidingColumns: manifest.decidingColumns,
314
1073
  tokenHook: tokenHookOf(manifest),
315
1074
  ...problems.length > 0 ? { problems } : {}
316
1075
  });
317
1076
  }
318
1077
  //#endregion
319
- //#region src/better-supabase/api-keys.ts
320
- const seconds = (instant) => Math.floor(instant.epochMilliseconds / 1e3);
321
- /** When a key stops verifying: its expiry, or the end of a rotated key's grace period. */
322
- function endOf(key) {
323
- const { expiresAt, revokedAt } = key;
324
- if (revokedAt === void 0) return expiresAt;
325
- if (expiresAt === void 0) return revokedAt;
326
- return revokedAt.epochMilliseconds < expiresAt.epochMilliseconds ? revokedAt : expiresAt;
327
- }
328
- /** `rls.apiKeys.serviceRoles` from the manifest, when it has `rls.apiKeys`. */
329
- function manifestServiceRoles(manifest) {
330
- return manifest === void 0 ? void 0 : parseSupabaseManifest(manifest).rls.apiKeys?.serviceRoles;
331
- }
332
- /**
333
- * A personal key as a `user` credential acting as its user, a tenant key as
334
- * a `service` credential holding `roles` in its tenant; `null` when `*`
335
- * has no expansion, a tenant key has no roles, or the record is invalid.
336
- */
337
- function keyCredential(key, roles, allPermissions) {
338
- const scopes = key.scopes.includes("*") ? allPermissions?.map((permission) => permission.key) : key.scopes;
339
- if (scopes === void 0) return null;
340
- const owner = key.userId;
341
- const base = {
342
- v: 1,
343
- id: key.id,
344
- permissions: scopes.map((permission) => ({ permission })),
345
- createdBy: key.createdBy ?? owner ?? key.id,
346
- createdAt: key.createdAt ?? 0,
347
- ...key.expiresAt === void 0 ? {} : { expiresAt: key.expiresAt },
348
- name: key.name
349
- };
350
- if (owner !== void 0) return parseCredential({
351
- ...base,
352
- kind: "user",
353
- principal: owner,
354
- ...key.organizationId === void 0 ? {} : { tenant: key.organizationId }
355
- }) ?? null;
356
- if (roles === void 0 || key.organizationId === void 0) return null;
357
- return parseCredential({
358
- ...base,
359
- kind: "service",
360
- principal: key.id,
361
- tenant: key.organizationId,
362
- roles
363
- }) ?? null;
364
- }
365
- /**
366
- * better-supabase's API keys as a `CredentialVerifier`, for
367
- * `subjectFromApiKey` from `permdock/server`, which reads PermDock's
368
- * `pdk_<id>_<secret>` format: create the keys with `prefix: "pdk"`. The
369
- * credential id is the key's `publicId`, the id in the token. A personal key
370
- * is a `user` credential acting as its user (inside its tenant when the key
371
- * is limited to one), a tenant key a `service` credential holding
372
- * `serviceRoles` in its tenant, and the key's scopes are the credential's
373
- * permissions. better-supabase records the use when it verifies, so there is
374
- * no `touch`. A rotated key in its grace period verifies, and its credential
375
- * expires when the grace period ends. An invalid, revoked, expired or
376
- * rate-limited key, and a failed lookup, are `null`. `permdock/server` exports a different
377
- * `apiKeyVerifier`, over the application's own key records.
378
- */
379
- function apiKeyVerifier(options) {
380
- const fallback = manifestServiceRoles(options.manifest);
381
- return freezeDeep({ async verify(secret) {
382
- try {
383
- const checked = await options.keys.verify(secret);
384
- if (!checked.ok || checked.data.status !== "ok") return null;
385
- const { key } = checked.data;
386
- if (key.state !== "active" && key.state !== "grace") return null;
387
- const end = endOf(key);
388
- const roles = typeof options.serviceRoles === "function" ? options.serviceRoles(key) : options.serviceRoles ?? fallback;
389
- return keyCredential({
390
- id: key.publicId,
391
- name: key.name,
392
- scopes: key.scopes,
393
- createdAt: seconds(key.createdAt),
394
- ...key.organizationId === void 0 ? {} : { organizationId: key.organizationId },
395
- ...key.userId === void 0 ? {} : { userId: key.userId },
396
- ...key.createdBy === void 0 ? {} : { createdBy: key.createdBy },
397
- ...end === void 0 ? {} : { expiresAt: seconds(end) }
398
- }, roles, options.allPermissions);
399
- } catch {
400
- return null;
401
- }
402
- } });
403
- }
404
- /**
405
- * The `claim` and `tenantClaim` options of better-supabase's
406
- * `apiKeyClaims()`, from the manifest's `rls.apiKeys`, so a key's token
407
- * carries the scopes ceiling, tenant and roles the `rls generate` helpers
408
- * read. Throws when the manifest has no `rls.apiKeys`.
409
- */
410
- function apiKeyClaimOptions(manifest) {
411
- const rls = parseSupabaseManifest(manifest).rls;
412
- const apiKeys = rls.apiKeys;
413
- 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`.");
414
- return freezeDeep({
415
- claim: {
416
- name: apiKeys.claim,
417
- scopes: apiKeys.scopes,
418
- tenant: apiKeys.tenant,
419
- roles: apiKeys.roles,
420
- serviceRoles: apiKeys.serviceRoles
421
- },
422
- tenantClaim: rls.tenantClaim
423
- });
424
- }
425
- //#endregion
426
- //#region src/better-supabase/subject.ts
427
- /** `session` with the short codes in `claim` turned back into feature keys. */
428
- function decodedFeatures(session, claim, keys) {
429
- const claims = session?.claims;
430
- if (session === null || session === void 0 || !isRecord(claims)) return session;
431
- const coded = claims[claim];
432
- if (!isRecord(coded)) return session;
433
- const byCode = new Map(Object.entries(keys).map(([key, code]) => [code, key]));
434
- const decoded = Object.fromEntries(Object.entries(coded).map(([tenant, codes]) => [tenant, Array.isArray(codes) ? codes.flatMap((code) => {
435
- const key = typeof code === "string" ? byCode.get(code) : void 0;
436
- return key === void 0 ? [] : [key];
437
- }) : codes]));
438
- return {
439
- ...session,
440
- claims: {
441
- ...claims,
442
- [claim]: decoded
443
- }
444
- };
445
- }
446
- function isApiKeySession(session) {
447
- if (typeof session !== "object" || session === null) return false;
448
- const record = Object.fromEntries(Object.entries(session));
449
- return record["kind"] === "apiKey" && typeof record["keyId"] === "string" && typeof record["name"] === "string" && Array.isArray(record["scopes"]) && record["scopes"].every((scope) => typeof scope === "string");
450
- }
451
- function apiKeySubject(session, options) {
452
- const roles = typeof options.serviceRoles === "function" ? options.serviceRoles(session) : options.serviceRoles ?? manifestServiceRoles(options.manifest);
453
- const credential = keyCredential({
454
- id: session.keyId,
455
- name: session.name,
456
- scopes: session.scopes,
457
- ...session.organizationId === void 0 ? {} : { organizationId: session.organizationId },
458
- ...session.userId === void 0 ? {} : { userId: session.userId },
459
- ...session.createdBy === void 0 ? {} : { createdBy: session.createdBy },
460
- ...session.createdAt === void 0 ? {} : { createdAt: session.createdAt }
461
- }, roles, listPermissions(options.permissions));
462
- return credential === null ? anonymousSubject() : credentialSubject(credential, { permissions: options.permissions });
463
- }
464
- /**
465
- * `subjectFromSupabaseSession` for better-supabase's `AuthSession`, reading
466
- * `memberships` for the memberships and the entitlements module's `features`
467
- * claim for `principal.plans` (`plans` names another claim). With `apiKeys`,
468
- * a verified `apiKey` session is the key's credential subject: a personal key
469
- * acts as its user within the key's scopes, a tenant key as a `service`
470
- * principal holding `serviceRoles`. Without `apiKeys`, and for `anon`,
471
- * `service` and `invalid` sessions, the subject is anonymous.
472
- */
473
- function subjectFromBetterSupabase(session, options = {}) {
474
- const { apiKeys, plans = "features", ...rest } = options;
475
- if (isApiKeySession(session)) return apiKeys === void 0 ? anonymousSubject() : apiKeySubject(session, apiKeys);
476
- const claim = typeof plans === "string" ? plans : plans.claim ?? "features";
477
- const keys = typeof plans === "string" ? void 0 : plans.keys;
478
- return subjectFromSupabaseSession(keys === void 0 ? session : decodedFeatures(session, claim, keys), {
479
- memberships: "memberships",
480
- ...rest,
481
- plans: claim
482
- });
483
- }
484
- //#endregion
485
1078
  //#region src/better-supabase/authorizer.ts
486
1079
  const OPERATIONS = /* @__PURE__ */ new Set([
487
1080
  "list",
@@ -535,7 +1128,7 @@ function authorizer(options) {
535
1128
  kind: "user",
536
1129
  claims
537
1130
  };
538
- const base = await createPermDock(options.policy, subjectFromBetterSupabase(session, options.subject), options.options);
1131
+ const base = await getPermDock(options.policy, subjectFromBetterSupabase(session, options.subject), options.options);
539
1132
  return typeof tenant === "string" ? base.tenant(tenant) : base;
540
1133
  };
541
1134
  return Object.freeze({
@@ -738,7 +1331,7 @@ function toolPolicy(options) {
738
1331
  const permission = tool.meta;
739
1332
  if (!isPermission(permission)) return false;
740
1333
  try {
741
- return mayUse(await options.permdock(ctx), permission);
1334
+ return (await options.permdock(ctx)).mayUse(permission);
742
1335
  } catch {
743
1336
  return false;
744
1337
  }
@@ -746,4 +1339,153 @@ function toolPolicy(options) {
746
1339
  });
747
1340
  }
748
1341
  //#endregion
749
- export { apiKeyClaimOptions, apiKeyVerifier, authorizationProvider, authorizer, bucketPolicy, credentialGuard, subjectFromBetterSupabase, toolPolicy, topicPolicy };
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;
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
+ });
1370
+ }
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;
1377
+ }
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));
1383
+ }
1384
+ /**
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.
1392
+ */
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
+ };
1489
+ }
1490
+ //#endregion
1491
+ export { accessReviewApplier, apiKeyClaimOptions, apiKeyVerifier, authorizationHooks, authorizationProvider, authorizer, bucketPolicy, credentialGuard, handlerCan, hookCan, principalSource, subjectFromBetterSupabase, subjectFromShareLink, subscriberPolicy, supportPolicy, toolPolicy, topicPolicy };