permdock 0.6.0 → 0.7.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 (399) hide show
  1. package/README.md +1 -0
  2. package/dist/{2020-C9VN-x8c.js → 2020-CJbO8Bvz.js} +1 -3
  3. package/dist/a2a/index.d.ts +5 -5
  4. package/dist/a2a/index.js +4 -4
  5. package/dist/{adoption-scan-DCYe60Pi.js → adoption-scan-D-feE7BE.js} +1 -1
  6. package/dist/agent/index.d.ts +3 -3
  7. package/dist/agent/index.js +1 -1
  8. package/dist/ai-sdk/index.d.ts +21 -6
  9. package/dist/ai-sdk/index.js +19 -9
  10. package/dist/{approval-prompt-DvSSPQJX.d.ts → approval-prompt-K0faOLFp.d.ts} +2 -2
  11. package/dist/approvals/index.d.ts +4 -4
  12. package/dist/approvals/index.js +4 -4
  13. package/dist/{approvals-p1PGoVjb.js → approvals-CMRfsbBh.js} +74 -48
  14. package/dist/{arazzo-41WuzHou.js → arazzo-ClrF3PVb.js} +2 -2
  15. package/dist/{arazzo-CaUVBn7D.js → arazzo-rdeJ8n1J.js} +4 -4
  16. package/dist/{args-IIolxxKA.js → args-CC3OwyHW.js} +112 -2
  17. package/dist/astro/index.d.ts +6 -6
  18. package/dist/astro/index.js +2 -2
  19. package/dist/{audience-DabClFLQ.js → audience-BrMs8c4o.js} +1 -1
  20. package/dist/{auth-DRLEizbL.js → auth-CscUX8Sy.js} +3 -4
  21. package/dist/authzen/index.d.ts +162 -22
  22. package/dist/authzen/index.js +315 -146
  23. package/dist/{automation-BNM3TtDo.d.ts → automation-DOtAFWdy.d.ts} +3 -3
  24. package/dist/{automation-DVjOckjV.js → automation-ySsxp_Yp.js} +1 -1
  25. package/dist/base-url-BQbddZO5.js +9 -0
  26. package/dist/better-auth/index.d.ts +2 -2
  27. package/dist/better-auth/index.js +2 -2
  28. package/dist/better-supabase/index.d.ts +50 -8
  29. package/dist/better-supabase/index.js +94 -32
  30. package/dist/{bind-CAvgUuKs.js → bind-D1nO6fiC.js} +5 -5
  31. package/dist/{bind-DOyrv6zi.d.ts → bind-JDuvxjuW.d.ts} +7 -7
  32. package/dist/{build-instance-Bu_eDqYw.js → build-instance-DxE7czo9.js} +1 -1
  33. package/dist/{canonical-json-BC8_nMTw.js → canonical-json-DwilZ1iB.js} +6 -3
  34. package/dist/{capability-Drg-1YhL.d.ts → capability-D4UC9TTW.d.ts} +2 -2
  35. package/dist/{capability-N1K9IpqS.js → capability-L7NnfhiE.js} +1 -1
  36. package/dist/{casbin-Cr7RLtA3.js → casbin-BHijLuHY.js} +2 -2
  37. package/dist/{casbin-Dyu2gnUD.js → casbin-BW9pS47J.js} +1 -1
  38. package/dist/{casl-64gpT-8x.js → casl-CmJlgziS.js} +2 -2
  39. package/dist/{casl-DjEDhTCP.js → casl-UbI8v-Wq.js} +1 -1
  40. package/dist/catalog/index.d.ts +70 -10
  41. package/dist/catalog/index.js +112 -6
  42. package/dist/catalog-BCCjSAdH.js +1 -0
  43. package/dist/{catalog-DZVbrF-z.js → catalog-BaMQfQ6Y.js} +3 -3
  44. package/dist/{catalog-HD2gf5mY.js → catalog-D69WhFii.js} +43 -13
  45. package/dist/{catalog-doc-Cdn7-0pQ.js → catalog-doc-CV9s6B1S.js} +28 -5
  46. package/dist/{catalog-fingerprint-CCP0-yQS.js → catalog-fingerprint-C144oW4M.js} +1 -1
  47. package/dist/{create-CQWIpUG-.js → challenge-nGWma08S.js} +122 -25
  48. package/dist/chat/index.d.ts +1 -1
  49. package/dist/chat/index.js +2 -2
  50. package/dist/chat-sdk/index.d.ts +3 -3
  51. package/dist/checks-BC_0mPIq.js +37 -0
  52. package/dist/claude-agent/index.d.ts +3 -3
  53. package/dist/claude-agent/index.js +2 -2
  54. package/dist/clerk/index.d.ts +3 -3
  55. package/dist/clerk/index.js +1 -1
  56. package/dist/cli/bin.js +2 -2
  57. package/dist/cli/index.d.ts +3 -3
  58. package/dist/cli/index.js +3 -3
  59. package/dist/cloud/index.d.ts +127 -11
  60. package/dist/cloud/index.js +251 -3
  61. package/dist/{cloud-ytQ3uviA.js → cloud-CO8pYtwr.js} +5 -5
  62. package/dist/{codemod-ua2hHy7w.js → codemod-BP6n8N9L.js} +3 -3
  63. package/dist/{codemod-5mpSpnUY.js → codemod-i4N2BhDc.js} +3 -3
  64. package/dist/{collect-mLTY4nwb.js → collect-BaWeMjwV.js} +17 -8
  65. package/dist/{collect-Ci6akVwH.js → collect-DNm3oKOk.js} +6 -5
  66. package/dist/collect-k_Dulxr5.js +1 -0
  67. package/dist/compile/index.d.ts +1 -1
  68. package/dist/compile/index.js +1 -1
  69. package/dist/{compile-CMqMx_IN.js → compile-BAEO5bOk.js} +2 -2
  70. package/dist/{compile-1bsAezL0.d.ts → compile-COM2VKuN.d.ts} +3 -3
  71. package/dist/{completion-BTFrHZhX.js → completion-BNdVf7X2.js} +3 -3
  72. package/dist/{config-bs_kZeYq.js → config-CoQHDrDi.js} +7 -3
  73. package/dist/{config-Bj2Ixq3d.js → config-DGZzMV1B.js} +2 -2
  74. package/dist/{config-report-DSpsLpCE.js → config-report-Cm7UITNA.js} +3 -3
  75. package/dist/connector-schema-_VCSsdtD.js +433 -0
  76. package/dist/{context-B_u3_bDJ.js → context--uM9V3Ui.js} +2 -2
  77. package/dist/convex/index.d.ts +5 -5
  78. package/dist/convex/index.js +2 -2
  79. package/dist/core-B5-w6Kw9.js +335 -0
  80. package/dist/core-CQkE8Esr.d.ts +179 -0
  81. package/dist/{create-B30OMUOJ.d.ts → create-B2-CQqCI.d.ts} +5 -5
  82. package/dist/{create-CGm28gyI.js → create-Cb86Z8Im.js} +9 -8
  83. package/dist/{create-DCF2SvoT.js → create-DJrlhEp9.js} +5 -5
  84. package/dist/{create-D_NmPOdH.d.ts → create-QHuuPw3q.d.ts} +7 -7
  85. package/dist/{create-CBMmFie-.d.ts → create-UTnUVETj.d.ts} +30 -10
  86. package/dist/{credential-BcoiejpY.js → credential-CIM7wHwA.js} +4 -4
  87. package/dist/{credentials-DPyHmYz5.js → credentials-BpuTwd7a.js} +1 -1
  88. package/dist/{deciding-columns-Sq12mEA6.js → deciding-columns-DRo2QM6E.js} +13 -2
  89. package/dist/{delegation-j6-rc37o.js → delegation-C5rOdTp5.js} +5 -5
  90. package/dist/delivery/index.d.ts +5 -5
  91. package/dist/delivery/index.js +4 -3
  92. package/dist/{delivery-DCAgNyCn.js → delivery-rwOKLWRm.js} +51 -2
  93. package/dist/{denied-text-CiFQNuMV.d.ts → denied-text-D4qkEtFz.d.ts} +2 -2
  94. package/dist/{describe-BybMRY7A.js → describe-DZlnEtLy.js} +1 -0
  95. package/dist/{describe-B5qgDvEW.d.ts → describe-O0SwYIK8.d.ts} +2 -2
  96. package/dist/{diff-CmuSD3vc.js → diff-Btr1c1Ow.js} +2 -2
  97. package/dist/{diff-3jbx6Rm4.js → diff-DzT-xD13.js} +10 -10
  98. package/dist/{doctor-BdZw53-k.js → doctor-BU1szn63.js} +241 -49
  99. package/dist/{doctor-3ZqNHX_G.js → doctor-BoK_y0KO.js} +4 -4
  100. package/dist/drizzle/index.d.ts +3 -3
  101. package/dist/drizzle/index.js +1 -1
  102. package/dist/elysia/index.d.ts +5 -5
  103. package/dist/elysia/index.js +3 -3
  104. package/dist/{errors-DH5QEGXh.d.ts → errors-B_Qg9D3e.d.ts} +2 -2
  105. package/dist/{errors-CWTY6uih.js → errors-DpP_U6Bf.js} +1 -0
  106. package/dist/{evaluate-CNClBvcj.js → evaluate-B_oqFbWC.js} +55 -12
  107. package/dist/{evaluations-C_1vdSZc.d.ts → evaluations-BCddEOfO.d.ts} +4 -4
  108. package/dist/{evaluations-beAooV0n.js → evaluations-DReoiVBw.js} +4 -4
  109. package/dist/eve/index.d.ts +5 -5
  110. package/dist/eve/index.js +3 -3
  111. package/dist/express/index.d.ts +5 -5
  112. package/dist/express/index.js +3 -3
  113. package/dist/fastify/index.d.ts +5 -5
  114. package/dist/fastify/index.js +3 -3
  115. package/dist/{fetch-adapter-Dw0MTS3D.d.ts → fetch-adapter-CuhyYwC1.d.ts} +4 -4
  116. package/dist/{fetch-adapter-DD_gfmrC.js → fetch-adapter-De-u6vhJ.js} +3 -3
  117. package/dist/{fields-OYG_JR78.js → fields-ClcQLrVc.js} +48 -13
  118. package/dist/{files-C5ZI27eV.js → files-acUbNwWt.js} +15 -2
  119. package/dist/{fixtures-B7XrLeY7.js → fixtures-BTMW6zgU.js} +2 -2
  120. package/dist/{flags-C3DktBDY.js → flags-aaeRAdiD.js} +2 -2
  121. package/dist/{from-snapshot-CTCMsFZk.js → from-snapshot-Bz469dIh.js} +17 -10
  122. package/dist/generate-3FNO5P5Q.js +159 -0
  123. package/dist/{grantee-hxrUsbpM.js → grantee-BrRANrpT.js} +1 -1
  124. package/dist/{grantee-conditions-CcXf8hMM.js → grantee-conditions-C6P1Tvol.js} +1 -1
  125. package/dist/{grantee-match-DbENBHLB.js → grantee-match-0RtHWQDx.js} +3 -3
  126. package/dist/h3/index.d.ts +4 -4
  127. package/dist/h3/index.js +3 -3
  128. package/dist/{helpers-CBYVMjwh.js → helpers-DY3vXuGs.js} +3 -3
  129. package/dist/hono/index.d.ts +5 -5
  130. package/dist/hono/index.js +3 -3
  131. package/dist/{hosted-BPk51-kX.d.ts → hosted-BmjzxQhp.d.ts} +14 -2
  132. package/dist/{hosted-B46-5LYE.js → hosted-XBgU6RGT.js} +34 -12
  133. package/dist/{http-DXJ37RDA.js → http-Ca3Sr5x1.js} +1 -1
  134. package/dist/{import-wyqXUyjm.js → import-Bz0GefWg.js} +5 -5
  135. package/dist/{import-CNV7I2Dw.js → import-DB2EG_iy.js} +3 -3
  136. package/dist/{index-C-6Letxg.d.ts → index-BE8y3moU.d.ts} +50 -8
  137. package/dist/{index-B8YjkEZ4.d.ts → index-BSfRl0hZ.d.ts} +6 -6
  138. package/dist/{index-DuWxc5dt.d.ts → index-BTkwrK7v.d.ts} +143 -14
  139. package/dist/index-CMiPH2Tv.d.ts +206 -0
  140. package/dist/{index-Ch8LpNDp.d.ts → index-DH9DUDEM.d.ts} +18 -3
  141. package/dist/{index-CueFnxQc.d.ts → index-DLwcuCN_.d.ts} +86 -7
  142. package/dist/index.d.ts +18 -17
  143. package/dist/index.js +27 -25
  144. package/dist/{init-GG6Jh8HD.js → init-5TXNji88.js} +5 -5
  145. package/dist/{init-DeMYsnYJ.js → init-B-4t1NzC.js} +4 -4
  146. package/dist/{instance-options-BsVpyGIk.d.ts → instance-options-CU0YQCLH.d.ts} +1 -1
  147. package/dist/{instance-snapshot-BOEhcqIF.js → instance-snapshot-CjcwnEiV.js} +16 -11
  148. package/dist/jwt/index.d.ts +83 -5
  149. package/dist/jwt/index.js +143 -5
  150. package/dist/{kernel-D0NVb1cK.js → kernel-DVExUwkp.js} +138 -12
  151. package/dist/kysely/index.d.ts +3 -3
  152. package/dist/kysely/index.js +1 -1
  153. package/dist/load-DrMHXzgN.js +1 -0
  154. package/dist/{load-DFS_ry0W.js → load-DxroEzH1.js} +2 -2
  155. package/dist/{login-Dfe5RlYk.js → login-ClSJh5t5.js} +4 -4
  156. package/dist/{login-imhuwvn3.js → login-WC9HPJpU.js} +6 -10
  157. package/dist/{manifest-C8IzW3Do.js → manifest-B79YuF2T.js} +6 -2
  158. package/dist/{manifest-mx5ghSFM.d.ts → manifest-BVhExYhw.d.ts} +23 -2
  159. package/dist/{manifest-dvL2XEe1.js → manifest-Dv6q4i5s.js} +54 -35
  160. package/dist/{manifest-schema-C0kK_8V0.js → manifest-schema-Cv492Qev.js} +37 -7
  161. package/dist/{map-claims-D3oRmTk0.js → map-claims-etGIS_Dv.js} +14 -11
  162. package/dist/{map-error-Dr70oA_3.js → map-error-BQGyDm_M.js} +1 -1
  163. package/dist/mcp/index.d.ts +64 -10
  164. package/dist/mcp/index.js +151 -22
  165. package/dist/{migrate-CIq4-E9F.js → migrate-Dodel0IJ.js} +77 -24
  166. package/dist/nest/index.d.ts +5 -5
  167. package/dist/nest/index.js +3 -3
  168. package/dist/next/index.d.ts +9 -9
  169. package/dist/next/index.js +5 -5
  170. package/dist/next/plugin.d.ts +1 -1
  171. package/dist/next/plugin.js +2 -2
  172. package/dist/node/index.d.ts +5 -5
  173. package/dist/node/index.js +2 -2
  174. package/dist/openai/index.d.ts +5 -5
  175. package/dist/openai/index.js +3 -3
  176. package/dist/openapi/index.d.ts +14 -4
  177. package/dist/openapi/index.js +1 -1
  178. package/dist/{openapi-C4dDTVEd.js → openapi-DMocl1bh.js} +4 -4
  179. package/dist/{openapi-B7HZ0f-r.js → openapi-Ddb4628T.js} +2 -7
  180. package/dist/{openapi-DD1SV-5-.js → openapi-ztVaux6j.js} +8 -8
  181. package/dist/{operation-findings-DgATDpvh.js → operation-findings-kRcEp8lE.js} +1 -1
  182. package/dist/{operations-X0VM7F3m.d.ts → operations-BNLes61x.d.ts} +1 -1
  183. package/dist/orpc/index.d.ts +6 -6
  184. package/dist/orpc/index.js +4 -4
  185. package/dist/otel/index.d.ts +3 -3
  186. package/dist/otel/index.js +1 -1
  187. package/dist/{output-1im__-08.js → output-DgSDLrta.js} +7 -3
  188. package/dist/{output-files-DhRBBE4n.js → output-files-CCMdcFWb.js} +2 -2
  189. package/dist/output-files-DPAY7-GX.js +1 -0
  190. package/dist/{ownership-DqaJNJPn.js → ownership-Z2LpxsGa.js} +443 -40
  191. package/dist/parse-Ddhz2xf4.js +148 -0
  192. package/dist/{parse-local-manifest-CVSs6J9I.js → parse-local-manifest-T1Q8X3A2.js} +24 -1
  193. package/dist/{parse-local-manifest-5ODwqu8b.d.ts → parse-local-manifest-e4mdwAKd.d.ts} +1 -1
  194. package/dist/pdp/index.d.ts +3 -3
  195. package/dist/pdp/index.js +7 -3
  196. package/dist/peer-12Q5I_RH.js +976 -0
  197. package/dist/{peer-CRvC7Hg1.js → peer-DOzzMfZL.js} +1 -1
  198. package/dist/{permdock-BrAscV28.js → permdock-BfWEeMqk.js} +177 -49
  199. package/dist/{permdock-SV8ZINYT.d.ts → permdock-Dco6dyCK.d.ts} +34 -5
  200. package/dist/{permission-registry-Br4jSNVi.js → permission-registry-DpPtSKNZ.js} +1 -1
  201. package/dist/{vocabulary-CN8H_9UF.d.ts → permission-types-DhNEmoRG.d.ts} +4 -64
  202. package/dist/{permissions-u_KHg-sN.d.ts → permissions-CoqCOfJ3.d.ts} +2 -1
  203. package/dist/{permissions-B-wCP5ZC.js → permissions-CyH2MV_B.js} +1 -1
  204. package/dist/{permissions-BPxlwVhN.js → permissions-DArULThq.js} +4 -4
  205. package/dist/pkce-5h8NgMQJ.js +21 -0
  206. package/dist/{policies-DP2nqRoF.js → policies-B2lZIurj.js} +3 -3
  207. package/dist/{policy-77FSAq_V.d.ts → policy-B7dg_yzu.d.ts} +2 -2
  208. package/dist/{policy-DbM5YYtV.js → policy-DaJy4nK4.js} +17 -8
  209. package/dist/{policy-from-catalog-C2gMa2Dj.js → policy-from-catalog-DFWYkNdu.js} +9 -9
  210. package/dist/{policy-types-n0uRTsW8.d.ts → policy-types-B-F-qAZk.d.ts} +83 -4
  211. package/dist/postgres-store-BL9M_CA1.js +1001 -0
  212. package/dist/{postgrest-CEgRbefE.d.ts → postgrest-CYacM_u7.d.ts} +12 -1
  213. package/dist/{postgrest-DAjUkF9w.js → postgrest-r5faDIzD.js} +37 -4
  214. package/dist/{powersync-DISt6SUe.js → powersync-13odBwiM.js} +3 -3
  215. package/dist/powersync-53ryOZip.js +1059 -0
  216. package/dist/powersync-CExtTLyA.js +1 -0
  217. package/dist/preset/index.js +1 -1
  218. package/dist/{preset-DZdU0tne.js → preset-CHTbd-DQ.js} +2 -2
  219. package/dist/prisma/index.d.ts +2 -2
  220. package/dist/prisma/index.js +1 -1
  221. package/dist/{problem-details-B45V5d2b.d.ts → problem-details-Tcfmj3bV.d.ts} +1 -1
  222. package/dist/prompt-DwtVemER.js +935 -0
  223. package/dist/{protected-RFGpakN9.d.ts → protected-BwZYL1X_.d.ts} +6 -5
  224. package/dist/{protected-BlF0F6wL.js → protected-DXE3cQYg.js} +2 -2
  225. package/dist/{provider-B1al-AZz.js → provider-BQsTYYy0.js} +2 -2
  226. package/dist/{provider-DILL9oPK.d.ts → provider-Csk-THAn.d.ts} +1 -1
  227. package/dist/{provider-store-DM6DNcta.js → provider-store-IgqQipCO.js} +2 -2
  228. package/dist/{pull-jEwbf80J.js → pull-CN5OLxrF.js} +7 -7
  229. package/dist/pull-Cty6Ovyu.js +1 -0
  230. package/dist/{push-BD4f15co.js → push-tVdG8dUL.js} +54 -14
  231. package/dist/{pushed-policy-DCW91KQU.d.ts → pushed-policy-BbcY9r_4.d.ts} +1 -1
  232. package/dist/{pushed-policy-CqtxkV4G.js → pushed-policy-CmYeSQqq.js} +1 -1
  233. package/dist/react/index.d.ts +6 -6
  234. package/dist/react/index.js +3 -3
  235. package/dist/react/provider-client.d.ts +1 -1
  236. package/dist/react/provider-client.js +1 -1
  237. package/dist/react-native/index.d.ts +25 -11
  238. package/dist/react-native/index.js +209 -21
  239. package/dist/{refs-K6eWUJ0H.js → refs-CNJBiTFo.js} +15 -1
  240. package/dist/{replay-Bkm-Ne4l.d.ts → replay-DCDD_ZfL.d.ts} +1 -1
  241. package/dist/{request-context-C81Pip9n.js → request-context-BykKLmyE.js} +2 -2
  242. package/dist/resend/index.d.ts +4 -4
  243. package/dist/{retire-CALjIQ7e.js → retire-Wj6dkyDE.js} +2 -2
  244. package/dist/{rls-BAqP4q66.js → rls-Ck_PocSp.js} +32 -8
  245. package/dist/{rls-DTvYd-lI.js → rls-CxBZb4rZ.js} +478 -534
  246. package/dist/{role-builder-uWQZ4v84.js → role-builder-C1VJzvcr.js} +3 -3
  247. package/dist/rolldown-runtime-C3I60J0A.js +4 -0
  248. package/dist/{rpc-VSl5ylaQ.js → rpc-Bk1bE-ch.js} +1 -1
  249. package/dist/run-Bz7DXGEv.js +1 -0
  250. package/dist/{run-ticket-sxFI6LAk.js → run-ticket-B5d_63Ma.js} +22 -1
  251. package/dist/{run-ticket-DocwaTKn.d.ts → run-ticket-BpNi7BCi.d.ts} +8 -3
  252. package/dist/{run-CUKuz0A8.js → run-uLD2aWuO.js} +7 -7
  253. package/dist/{runtime-D3VTAiCA.js → runtime-Vif3_-zf.js} +1 -1
  254. package/dist/{saas-BCXRuXca.js → saas-Az_Kz-4D.js} +7 -7
  255. package/dist/{scan-qvOe0hS3.js → scan-DSljDE1Y.js} +1 -1
  256. package/dist/{schema-yLuBWWsh.js → schema-gIcCx7zM.js} +1 -1
  257. package/dist/scim/index.d.ts +3 -2
  258. package/dist/scim/index.js +3 -1
  259. package/dist/scim-C7JYhySg.js +44 -0
  260. package/dist/{scim-ClHtpyV3.js → scim-YNieBO1U.js} +3 -537
  261. package/dist/{scopes-CUPSjo9q.js → scopes-BHsc-wp-.js} +1 -1
  262. package/dist/server/index.d.ts +11 -11
  263. package/dist/server/index.js +7 -7
  264. package/dist/{server-C3WXAHZj.js → server-Bnga6I50.js} +3 -3
  265. package/dist/{server-types-CKQiCnON.d.ts → server-types-C4gZUEZy.d.ts} +9 -9
  266. package/dist/{shared-DvvFzYXe.js → shared-C8LjUdVU.js} +1 -1
  267. package/dist/{shared-BAK8t4Uo.js → shared-fMKOv8hP.js} +44 -38
  268. package/dist/{sink-4-eQIRWf.d.ts → sink-Cp_Eot9W.d.ts} +4 -4
  269. package/dist/{skills-B6lVdD_W.js → skills-Dcjw9_1C.js} +5 -5
  270. package/dist/{skills-CTYwkl44.js → skills-g_3BS9NC.js} +3 -3
  271. package/dist/{snapshot-B7DkYg6g.js → snapshot-CbGmYiJx.js} +4 -4
  272. package/dist/{snapshot-cache-DZHQ0KF6.js → snapshot-cache-DT2Kx1F8.js} +1 -1
  273. package/dist/{snapshot-cache-PXI-vGpY.d.ts → snapshot-cache-Pahd3IYJ.d.ts} +1 -1
  274. package/dist/solid/index.d.ts +6 -5
  275. package/dist/solid/index.js +4 -4
  276. package/dist/solid-start/index.d.ts +6 -6
  277. package/dist/solid-start/index.js +3 -3
  278. package/dist/{sources-vs5dGOsS.js → sources-B-zYB4GS.js} +111 -25
  279. package/dist/{sources-Xc4zkszI.d.ts → sources-DEEuGb8S.d.ts} +63 -14
  280. package/dist/sources-qoIJQrJv.js +11 -0
  281. package/dist/{sql-B5T1IL83.js → sql-B96movIM.js} +5 -1
  282. package/dist/sql-CDEPCA0g.d.ts +10 -0
  283. package/dist/{src-Cu2m75uo.js → src-DLFrtrrC.js} +140 -48
  284. package/dist/ssf/index.d.ts +4 -4
  285. package/dist/ssf/index.js +640 -211
  286. package/dist/{standard-webhooks-rbD7LS6h.d.ts → standard-webhooks-B_qRg0ws.d.ts} +2 -2
  287. package/dist/{store-BDutJkDL.js → store-DSP9oGvh.js} +15 -7
  288. package/dist/{store-options-u0ycEe8s.js → store-options-B_poyYvg.js} +6 -6
  289. package/dist/{stores-BUGNbJTt.d.ts → stores-BAZwSYZA.d.ts} +6 -5
  290. package/dist/{stores-CGhM8BN_.js → stores-BqEuHC05.js} +2 -2
  291. package/dist/{stream-CeCMJmBB.d.ts → stream-2Vwhl7Er.d.ts} +2 -2
  292. package/dist/{style-Uao76wO7.js → style-Cqo7-73x.js} +1 -0
  293. package/dist/{style-DiKWnAvn.js → style-Ds1gNm77.js} +1 -1
  294. package/dist/supabase/index.d.ts +8 -7
  295. package/dist/supabase/index.js +5 -5
  296. package/dist/supabase/middleware.d.ts +6 -6
  297. package/dist/supabase/middleware.js +6 -6
  298. package/dist/{supabase-x5JtR2tc.js → supabase--0aJdLEd.js} +8 -6
  299. package/dist/{supabase-By_frk3U.js → supabase-Bqp7iIY2.js} +3 -3
  300. package/dist/supabase-hook-DJxC4PoP.js +1 -0
  301. package/dist/{supabase-hook-BMoEhZir.js → supabase-hook-Dg0SrQWR.js} +1060 -119
  302. package/dist/supautils-MESd81bF.js +689 -0
  303. package/dist/{support-session-C6xJQYut.js → support-session-Cna49-3f.js} +2 -2
  304. package/dist/svelte/index.d.ts +5 -5
  305. package/dist/svelte/index.js +5 -5
  306. package/dist/svelte/runtime.d.ts +3 -3
  307. package/dist/svelte/runtime.js +3 -3
  308. package/dist/svelte/source.d.ts +5 -5
  309. package/dist/svelte/source.js +2 -2
  310. package/dist/sveltekit/index.d.ts +6 -6
  311. package/dist/sveltekit/index.js +2 -2
  312. package/dist/tanstack-start/index.d.ts +6 -6
  313. package/dist/tanstack-start/index.js +2 -2
  314. package/dist/{tenancy-DN2QAx9z.js → tenancy-CmeBQ6Nv.js} +12 -3
  315. package/dist/terminal/index.d.ts +6 -6
  316. package/dist/terminal/index.js +3 -3
  317. package/dist/testing/core.d.ts +2 -0
  318. package/dist/testing/core.js +1 -0
  319. package/dist/testing/fixtures/index.d.ts +12 -0
  320. package/dist/testing/fixtures/index.js +770 -0
  321. package/dist/testing/index.d.ts +41 -174
  322. package/dist/testing/index.js +241 -731
  323. package/dist/testing/saas/index.d.ts +5 -5
  324. package/dist/testing/saas/index.js +2 -2
  325. package/dist/testing/saas/permissions.d.ts +1 -1
  326. package/dist/testing/saas/permissions.js +1 -1
  327. package/dist/text-diff-DhITNCnD.js +444 -0
  328. package/dist/{token-BLZcjQgt.js → token-2qYDvXz2.js} +1 -1
  329. package/dist/{tool-refusal-CMLN5xhn.js → tool-refusal-reAhIzdl.js} +1 -1
  330. package/dist/trpc/index.d.ts +6 -6
  331. package/dist/trpc/index.js +3 -3
  332. package/dist/{types-Dfv0aTER.d.ts → types-BWZybqs9.d.ts} +110 -8
  333. package/dist/{types-Druu9wLM.d.ts → types-BXMYB7BY.d.ts} +2 -2
  334. package/dist/{types-BzzaHxt3.d.ts → types-BfVmNJO5.d.ts} +14 -3
  335. package/dist/{types-lUlG3TVd.d.ts → types-CIeZcG8v.d.ts} +2 -2
  336. package/dist/{types-B3_ooZgZ.d.ts → types-CKV71kKh.d.ts} +4 -4
  337. package/dist/{types-CX7AvRqP.d.ts → types-CQVEQawv.d.ts} +4 -4
  338. package/dist/{types-C863OQiR.d.ts → types-CeIscIKp.d.ts} +4 -4
  339. package/dist/{types-C_IiEs64.d.ts → types-CfiKC5aN.d.ts} +1 -1
  340. package/dist/{types-_WhUP7ud.d.ts → types-D3A19w4A.d.ts} +8 -4
  341. package/dist/{types-8RnwUAvE.d.ts → types-DBHftjgS.d.ts} +15 -8
  342. package/dist/{types-BdtSDbBO.d.ts → types-DC44-hqb.d.ts} +2 -2
  343. package/dist/{types-CVZSNocA.d.ts → types-DXmwu78Z.d.ts} +4 -4
  344. package/dist/{types-C791HFu9.d.ts → types-cTos-H7p.d.ts} +2 -2
  345. package/dist/{types-BtHtcKMr.d.ts → types-g7Oy6Zg4.d.ts} +3 -3
  346. package/dist/unplugin/index.d.ts +1 -1
  347. package/dist/unplugin/index.js +2 -2
  348. package/dist/{usage-SveTRTyr.js → usage-Bybmck7f.js} +7 -7
  349. package/dist/{usage-CRVl9V3t.js → usage-CDaLXCfL.js} +2 -2
  350. package/dist/usage-y-kBNdqs.js +1 -0
  351. package/dist/{validation-error-DHUrAkku.d.ts → validation-error-CQpoquwj.d.ts} +1 -1
  352. package/dist/{verify-BaAKx7t6.js → verify-pe8rL_Qw.js} +350 -83
  353. package/dist/{views-DTIIO-oQ.js → views-BB8E9dTP.js} +1 -1
  354. package/dist/vocabulary-Bg3eeT2H.d.ts +67 -0
  355. package/dist/vue/index.d.ts +6 -5
  356. package/dist/vue/index.js +4 -4
  357. package/dist/{waiter-B986w1oK.d.ts → waiter-DwEyouan.d.ts} +2 -2
  358. package/dist/watch-Bd9mx3rd.js +1 -0
  359. package/dist/{watch-vea54PRw.js → watch-C-4VmFlL.js} +3 -3
  360. package/dist/webmcp/index.d.ts +2 -2
  361. package/dist/webmcp/index.js +2 -2
  362. package/dist/{where-scope-C9vfEphl.js → where-scope-wq2coopv.js} +41 -21
  363. package/dist/{who-can-DWBAcaCQ.js → who-can-BwNsI_Js.js} +6 -6
  364. package/dist/workflow/index.d.ts +3 -3
  365. package/dist/workflow/index.js +5 -5
  366. package/dist/workflow-sdk/index.d.ts +5 -5
  367. package/dist/{wrap-C5jC6sKI.d.ts → wrap-B6R0cMRL.d.ts} +2 -2
  368. package/dist/{wrap-gX61EtpB.js → wrap-CiSc_KCq.js} +8 -2
  369. package/package.json +22 -11
  370. package/schemas/cli-result-v1.json +1 -0
  371. package/schemas/cloud-api-v1.json +152 -7
  372. package/schemas/connector-v1.json +8 -0
  373. package/schemas/supabase-claims-v1.json +5 -0
  374. package/schemas/supabase-manifest-v1.json +276 -75
  375. package/skills/permdock/SKILL.md +1 -0
  376. package/skills/permdock-agents/references/runtimes.md +3 -1
  377. package/skills/permdock-approvals/references/stores-and-resume.md +2 -1
  378. package/skills/permdock-data/references/queries-and-rls.md +4 -1
  379. package/skills/permdock-tenancy/SKILL.md +1 -1
  380. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
  381. package/skills/permdock-wire/references/adapters.md +14 -0
  382. package/skills/permdock-wire/references/policy-options.md +1 -0
  383. package/dist/catalog-BoWglDPI.js +0 -1
  384. package/dist/collect-D7eQDffE.js +0 -1
  385. package/dist/index-Cp_ilKi1.d.ts +0 -91
  386. package/dist/load-BxEtwlBS.js +0 -1
  387. package/dist/output-files-B3IZJksC.js +0 -1
  388. package/dist/parse-Cxq0WxrA.js +0 -53
  389. package/dist/peer-C8swJ2GH.js +0 -50
  390. package/dist/powersync-BkWWCS1K.js +0 -538
  391. package/dist/powersync-MMJ2mI2q.js +0 -1
  392. package/dist/prompt-DfZLJLIn.js +0 -32
  393. package/dist/pull-CvpINcMK.js +0 -1
  394. package/dist/run-DDwUaJDU.js +0 -1
  395. package/dist/supabase-hook-CCw5m35C.js +0 -1
  396. package/dist/supautils-B6HYL9eL.js +0 -91
  397. package/dist/text-diff-BQcUapOb.js +0 -15
  398. package/dist/usage-CZpSQP6L.js +0 -1
  399. package/dist/watch--ovlPOc0.js +0 -1
@@ -1,17 +1,19 @@
1
1
  import { a as sole, n as isReadonlyArray, r as isRecord } from "./compact-CAf557ZB.js";
2
2
  import { n as isForbiddenKey } from "./paths-DXSHAMIa.js";
3
- import { l as isSqlFunctionField } from "./refs-K6eWUJ0H.js";
3
+ import { u as isSqlFunctionField } from "./refs-CNJBiTFo.js";
4
+ import { c as listPermissions, h as formerKeys } from "./permission-registry-DpPtSKNZ.js";
4
5
  import { u as isoAt } from "./time-DkWjdL98.js";
5
- import { c as rootScope, d as scopeList, l as scopeChain, o as resolveScope } from "./scopes-CUPSjo9q.js";
6
+ import { f as scopeList, l as rootScope, s as resolveScope, u as scopeChain } from "./scopes-BHsc-wp-.js";
6
7
  import { t as isoInstant } from "./instant-BluqDvyS.js";
7
- import { m as findRole, v as listRoles } from "./grantee-hxrUsbpM.js";
8
- import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-CcXf8hMM.js";
9
- import { l as partitionsOf, p as scopeField } from "./tenancy-DN2QAx9z.js";
10
- import { i as configError } from "./errors-CWTY6uih.js";
11
- import { i as scopeMembershipTable, o as relatedSql, r as scopeColumn } from "./compile-CMqMx_IN.js";
12
- import { i as qualifySqlTable, t as SQL_IDENT, u as sqlTextArray } from "./sql-B5T1IL83.js";
13
- import { d as keptKeys, l as roleColumn, u as roleManifest } from "./sources-vs5dGOsS.js";
14
- import { A as declaredArray, D as sqlType, E as scopeTypeOf, F as quoteQualified, I as quoteTable, L as textArrayLiteral, M as qualified, N as quoteIdent, O as tenantTypeOf, P as quoteLiteral, S as CLAIM, _ as subjectClaimJsonSql, b as tenantClaimSql, d as keyTenantSql, f as kindFilterSql, h as roleKindSql, i as allowEntry, j as helperSchema, k as CLI, m as memberVia, n as GLOBAL_ROLES, o as rootName, p as liveSessionSql, r as HELPERS, s as signedIn, t as CUSTOM_ROLES, u as globalKindFilterSql, v as subjectClaimSql, x as userIdHelperSql, y as subjectIdSql } from "./shared-BAK8t4Uo.js";
8
+ import { m as findRole, v as listRoles } from "./grantee-BrRANrpT.js";
9
+ import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-C6P1Tvol.js";
10
+ import { l as partitionsOf, p as scopeField } from "./tenancy-CmeBQ6Nv.js";
11
+ import { a as delegatedScopeMap } from "./permdock-BfWEeMqk.js";
12
+ import { i as configError } from "./errors-DpP_U6Bf.js";
13
+ import { d as sqlTextArray, i as qualifySqlTable, t as SQL_IDENT } from "./sql-B96movIM.js";
14
+ import { i as scopeMembershipTable, o as relatedSql, r as scopeColumn } from "./compile-BAEO5bOk.js";
15
+ import { d as keptKeys, l as roleColumn, u as roleManifest } from "./sources-B-zYB4GS.js";
16
+ import { A as quoteQualified, D as qualified, E as helperSchema, F as scopeTypeOf, I as sqlType, L as tenantTypeOf, M as textArrayLiteral, O as quoteIdent, S as CLAIM, T as declaredArray, _ as subjectClaimJsonSql, b as tenantClaimSql, d as keyTenantSql, f as kindFilterSql, h as roleKindSql, i as allowEntry, j as quoteTable, k as quoteLiteral, m as memberVia, n as GLOBAL_ROLES, o as rootName, p as liveSessionSql, r as HELPERS, s as signedIn, t as CUSTOM_ROLES, u as globalKindFilterSql, v as subjectClaimSql, w as CLI, x as userIdHelperSql, y as subjectIdSql } from "./shared-fMKOv8hP.js";
15
17
  //#region src/cli/sql/active.ts
16
18
  function checkActiveRow(label, row) {
17
19
  quoteTable(row.table);
@@ -699,6 +701,10 @@ function collectGrants(policy) {
699
701
  };
700
702
  });
701
703
  }
704
+ /** The declared roles a custom role may draw grants from: the custom-role ceiling. */
705
+ function assignableRoles(policy) {
706
+ return [...roleNames(policy)].toSorted().filter((name) => policy.rolesByName.get(name)?.assignable ?? listRoles(policy.vocabulary.roles).some((leaf) => leaf.key === name && leaf.assignable));
707
+ }
702
708
  /** Declared role names (bindings and `defineRoles` leaves) plus any role a top-level grant names. */
703
709
  function roleNames(policy) {
704
710
  const names = new Set(policy.roles.map((item) => item.name));
@@ -1205,7 +1211,7 @@ end;`;
1205
1211
  and ${qualified(ctx, HELPERS.has)}(m.grant_key)
1206
1212
  )`;
1207
1213
  const match = `c.tenant_id is not distinct from p_tenant and c.scope = p_scope and c.scope_id is not distinct from p_scope_id and c.role = p_role`;
1208
- const allowEntryOf = custom.levels === true ? "c.permission || coalesce('@' || c.level, '')" : "c.permission";
1214
+ const allowEntryOf = `c.${allowEntry(ctx)}`;
1209
1215
  const checked = (gate) => `begin
1210
1216
  if p_role = any(${sqlTextArray(custom.declared)}) then
1211
1217
  return query
@@ -1248,7 +1254,7 @@ revoke execute on function ${reader}(text, text, ${tenantType}, text) from publi
1248
1254
  grant execute on function ${reader}(text, text, ${tenantType}, text) to authenticated;`);
1249
1255
  const trusted = qualified(ctx, READERS.trusted);
1250
1256
  const signature = `text, text, ${tenantType}, text`;
1251
- const grants = trustedReaders === void 0 || trustedReaders.length === 0 ? "" : `\ngrant execute on function ${trusted}(${signature}) to ${trustedReaders.map(quoteIdent).join(", ")};`;
1257
+ const grants = trustedReaders === void 0 || trustedReaders.length === 0 ? "" : `\n${trustedReaderGrants(ctx, `${trusted}(${signature})`, trustedReaders)}`;
1252
1258
  chunks.push(`create or replace function ${trusted}(p_role text, p_scope text, p_tenant ${tenantType} default null, p_scope_id text default null)
1253
1259
  returns table (permission text, effect text)
1254
1260
  language plpgsql
@@ -1301,6 +1307,16 @@ ${indent} hint = ${quoteLiteral(hint)};`;
1301
1307
  function hasCustomRoleChecksFor(ctx) {
1302
1308
  return ctx.authorize === "database" && ctx.customRoles !== void 0 && ctx.scopes.length > 0 && ctx.scopes.every((scope) => hasMemberFor(ctx, scope.name));
1303
1309
  }
1310
+ /**
1311
+ * The grants a `rls.trustedReaders` role needs for one reader: usage on the
1312
+ * helper schema, which no client role reaches through these lines, and
1313
+ * execute on the function.
1314
+ */
1315
+ function trustedReaderGrants(ctx, fn, readers) {
1316
+ const to = readers.map(quoteIdent).join(", ");
1317
+ return `grant usage on schema ${quoteIdent(helperSchema(ctx))} to ${to};
1318
+ grant execute on function ${fn} to ${to};`;
1319
+ }
1304
1320
  //#endregion
1305
1321
  //#region src/cli/sql/global-roles.ts
1306
1322
  function qualify(name, schema) {
@@ -1328,6 +1344,7 @@ function globalRoleSource(source, schema, alias, roles) {
1328
1344
  from: `${quoteTable(table)} ${alias}${role.join}${global}`,
1329
1345
  userSql: `${alias}.${quoteIdent(user)}`,
1330
1346
  roleSql: role.sql,
1347
+ nullable: role.nullable,
1331
1348
  role: roleManifest(role),
1332
1349
  ...role.through === void 0 ? {} : { through: role.through },
1333
1350
  ...tenant === void 0 ? {} : { tenant },
@@ -1738,7 +1755,7 @@ ${rows}
1738
1755
  union
1739
1756
  select (ms.id)::${type}, ck.grant_key
1740
1757
  ${rows}
1741
- ${customKeysJoin(ctx, scope, entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'allow'"), entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'deny'"), entries(CUSTOM_ROLES.includes, "include_role", ""))}
1758
+ ${customKeysJoin(ctx, scope, entries(CUSTOM_ROLES.permissions, allowEntry(ctx), " and c.effect = 'allow'"), entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'deny'"), entries(CUSTOM_ROLES.includes, "include_role", ""))}
1742
1759
  where ${signedIn(ctx)}
1743
1760
  and ms.scope = ${quoteLiteral(scope)}
1744
1761
  and not (r.role = any(${sqlTextArray(custom.declared)}))${narrow}${sourceFilters(ctx, scope, subjectIdSql(ctx), grantPermissionSql("ck.grant_key"), sourcesKeep(sources))}`, sources, subjectIdSql(ctx));
@@ -1749,7 +1766,7 @@ ${rows}
1749
1766
  where ${signedIn(ctx)}
1750
1767
  and ms.scope = ${quoteLiteral(scope)}
1751
1768
  and not (r.role = any(${sqlTextArray(custom.declared)}))${narrow}${sourceFilters(ctx, scope, subjectIdSql(ctx), grantPermissionSql("p_grant"), sourcesKeep(sources))}
1752
- ${customKeysSql(ctx, scope, entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'allow'"), entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'deny'"), entries(CUSTOM_ROLES.includes, "include_role", ""))}`, sources, subjectIdSql(ctx));
1769
+ ${customKeysSql(ctx, scope, entries(CUSTOM_ROLES.permissions, allowEntry(ctx), " and c.effect = 'allow'"), entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'deny'"), entries(CUSTOM_ROLES.includes, "include_role", ""))}`, sources, subjectIdSql(ctx));
1753
1770
  }
1754
1771
  function scopedBody(ctx, scope, type) {
1755
1772
  if (ctx.authorize !== "database") return sqlBody(claimBody(ctx, scope, type));
@@ -1799,6 +1816,137 @@ revoke execute on function ${fn}(text, text, text) from public;
1799
1816
  grant execute on function ${fn}(text, text, text) to anon, authenticated;`;
1800
1817
  }
1801
1818
  //#endregion
1819
+ //#region src/cli/rls/custom-role-by-id.ts
1820
+ const PREFIX = "PermDock CLI";
1821
+ const col = (name) => `r.${quoteIdent(name)}`;
1822
+ /**
1823
+ * Custom-role functions keyed by the application's role row
1824
+ * (`rls.customRoleWrites.roles.primaryKey`): the tenant, scope, instance and
1825
+ * name come from the row, never from the caller. The writers delegate to
1826
+ * `permdock_replace_custom_role_grants` (or its trusted variant), so every
1827
+ * caller and definition check applies, and keep the stored denies and
1828
+ * includes. The reader returns declared allow keys only and is executable by
1829
+ * `rls.trustedReaders` alone.
1830
+ */
1831
+ function customRoleByIdSql(ctx, trustedReaders) {
1832
+ const custom = ctx.customRoles;
1833
+ const roles = custom?.table;
1834
+ const root = ctx.scopes[0]?.name;
1835
+ if (custom === void 0 || roles?.primaryKey === void 0 || ctx.authorize !== "database" || root === void 0) return "";
1836
+ const pk = roles.primaryKey;
1837
+ if (pk === "") throw configError(`${PREFIX}: rls.customRoleWrites.roles.primaryKey names no column`);
1838
+ const leveled = custom.levels === true;
1839
+ const tenantType = tenantTypeOf(ctx);
1840
+ const table = quoteQualified(roles.table);
1841
+ const idType = `${table}.${quoteIdent(pk)}%type`;
1842
+ const perms = qualified(ctx, CUSTOM_ROLES.permissions);
1843
+ const includes = qualified(ctx, CUSTOM_ROLES.includes);
1844
+ const replace = qualified(ctx, CUSTOM_ROLES.replace);
1845
+ const trustedReplace = qualified(ctx, CUSTOM_ROLES.trustedReplace);
1846
+ const byId = qualified(ctx, CUSTOM_ROLES.replaceById);
1847
+ const trustedById = qualified(ctx, CUSTOM_ROLES.trustedReplaceById);
1848
+ const reader = qualified(ctx, CUSTOM_ROLES.trustedReadById);
1849
+ const renames = Object.entries(custom.renamed ?? {}).toSorted(byKey);
1850
+ const current = (expr) => currentKeyExpr(renames, expr);
1851
+ const declaredKeys = sqlTextArray(custom.permissions ?? []);
1852
+ const declaredRoles = sqlTextArray(custom.declared);
1853
+ const tenant = roles.tenant === void 0 ? `null::${tenantType}` : `${col(roles.tenant)}::${tenantType}`;
1854
+ const scope = rolesTableScope(roles, tenant, root, col);
1855
+ const scopeId = roles.id === void 0 || roles.tenant === void 0 ? "null::text" : `case when ${tenant} is null then null else ${col(roles.id)}::text end`;
1856
+ const skipped = roles.skip === void 0 || roles.tenant === void 0 ? "false" : `(${tenant} is not null and coalesce(${col(roles.skip)}, false))`;
1857
+ const match = "c.tenant_id is not distinct from v_tenant and c.scope = v_scope and c.scope_id is not distinct from v_scope_id and c.role = v_key";
1858
+ const audit = custom.audit;
1859
+ const auditCall = audit === void 0 ? "" : `
1860
+ perform ${quoteQualified(audit.function)}(${[
1861
+ "event_type => 'permdock.custom_role.replaced'",
1862
+ "target_type => 'role'",
1863
+ "record_id => p_role_id::text",
1864
+ "tenant => v_tenant",
1865
+ "metadata => jsonb_build_object('operation', 'replace', 'role', v_key, 'permissions', p_permissions)",
1866
+ ...Object.entries(audit.args ?? {}).toSorted(byKey).map(([name, value]) => `${quoteIdent(name)} => ${quoteLiteral(value)}`)
1867
+ ].join(", ")});`;
1868
+ const body = (write) => `declare
1869
+ v_tenant ${tenantType};
1870
+ v_scope text;
1871
+ v_scope_id text;
1872
+ v_key text;
1873
+ v_skipped boolean;
1874
+ v_deny text[];
1875
+ v_include text[];
1876
+ begin
1877
+ select ${tenant}, ${scope}, ${scopeId}, ${col(roles.key)}::text, ${skipped}
1878
+ into v_tenant, v_scope, v_scope_id, v_key, v_skipped
1879
+ from ${table} r
1880
+ where ${col(pk)} = p_role_id;
1881
+ if not found then
1882
+ ${raiseSql(" ", "P0002", "'permdock: no role row has id ' || coalesce(p_role_id::text, 'null')", "unknown-role")}
1883
+ end if;
1884
+ if v_skipped or v_key = any(${declaredRoles}) then
1885
+ ${raiseSql(" ", "22023", "'permdock: ' || coalesce(v_key, 'null') || ' is a declared role, not a custom role'", "declared-role")}
1886
+ end if;
1887
+ select array_agg(c.permission) into v_deny
1888
+ from ${perms} c
1889
+ where ${match} and c.effect = 'deny';
1890
+ select array_agg(c.include_role) into v_include
1891
+ from ${includes} c
1892
+ where ${match};
1893
+ perform ${write}(v_tenant, v_scope, v_scope_id, v_key, p_permissions, v_deny, v_include);${auditCall}
1894
+ end;`;
1895
+ const writer = (comment, name, write, member) => `-- ${comment}
1896
+ create or replace function ${name}(p_role_id ${idType}, p_permissions text[])
1897
+ returns void
1898
+ language plpgsql
1899
+ volatile
1900
+ security definer
1901
+ set search_path = ''
1902
+ as $$
1903
+ ${body(write)}
1904
+ $$;
1905
+ ${member ? `revoke execute on function ${name}(${idType}, text[]) from public, anon;
1906
+ grant execute on function ${name}(${idType}, text[]) to authenticated;` : `revoke execute on function ${name}(${idType}, text[]) from public, anon, authenticated;`}`;
1907
+ const readerEntry = leveled ? `${current("c.permission")} || coalesce('@' || c.level, '')` : current("c.permission");
1908
+ const readerGrant = trustedReaders === void 0 || trustedReaders.length === 0 ? "" : `\n${trustedReaderGrants(ctx, `${reader}(text[], ${tenantType})`, trustedReaders)}`;
1909
+ return `${writer("save a custom role by its row id: the row supplies tenant, scope, instance and name; the caller checks of permdock_replace_custom_role_grants apply, and stored denies and includes stay", byId, replace, true)}
1910
+
1911
+ ${writer("save a custom role by its row id for a trusted caller: the definition checks without the caller checks", trustedById, trustedReplace, false)}
1912
+
1913
+ -- the declared allow keys of custom roles by their row ids as text, for a trusted reader; with p_tenant, an id of another tenant is refused
1914
+ create or replace function ${reader}(p_role_ids text[], p_tenant ${tenantType} default null)
1915
+ returns table (role_id text, permission text)
1916
+ language plpgsql
1917
+ stable
1918
+ security definer
1919
+ set search_path = ''
1920
+ as $$
1921
+ begin
1922
+ if p_tenant is not null and exists (
1923
+ select 1 from ${table} r
1924
+ where ${col(pk)}::text = any(p_role_ids) and ${tenant} is distinct from p_tenant
1925
+ ) then
1926
+ ${raiseSql(" ", "42501", "'permdock: a role id belongs to another tenant than ' || p_tenant::text", "not-member")}
1927
+ end if;
1928
+ return query
1929
+ select distinct v.role_id, ${readerEntry}
1930
+ from (
1931
+ select ${col(pk)}::text as role_id, ${tenant} as tenant_id, ${scope} as scope, ${scopeId} as scope_id, ${col(roles.key)}::text as role
1932
+ from ${table} r
1933
+ where ${col(pk)}::text = any(p_role_ids)
1934
+ and not ${skipped}
1935
+ and not (${col(roles.key)}::text = any(${declaredRoles}))
1936
+ ) v
1937
+ join ${perms} c
1938
+ on c.tenant_id is not distinct from v.tenant_id
1939
+ and c.scope = v.scope
1940
+ and c.scope_id is not distinct from v.scope_id
1941
+ and c.role = v.role
1942
+ where c.effect = 'allow'
1943
+ and ${current("c.permission")} = any(${declaredKeys})
1944
+ order by 1, 2;
1945
+ end;
1946
+ $$;
1947
+ revoke execute on function ${reader}(text[], ${tenantType}) from public, anon, authenticated;${readerGrant}`;
1948
+ }
1949
+ //#endregion
1802
1950
  //#region src/cli/rls/custom-role-cascade.ts
1803
1951
  /** `row.column`, for a trigger's `old` or `new`. */
1804
1952
  function rowColumn(row, name) {
@@ -1830,11 +1978,11 @@ function customRoleCascadeSql(ctx) {
1830
1978
  const target = quoteTable(qualifySqlTable(table.table));
1831
1979
  const tenant = (row) => table.tenant === void 0 ? `null::${tenantType}` : `${rowColumn(row, table.tenant)}::${tenantType}`;
1832
1980
  const scope = (row) => rolesTableScope(table, tenant(row), root, (name) => rowColumn(row, name));
1833
- const id = (row) => table.id === void 0 ? "null::text" : `${rowColumn(row, table.id)}::text`;
1834
- const skipped = (row) => table.skip === void 0 ? "false" : `coalesce(${rowColumn(row, table.skip)}, false)`;
1981
+ const id = (row) => table.id === void 0 || table.tenant === void 0 ? "null::text" : `case when ${tenant(row)} is null then null else ${rowColumn(row, table.id)}::text end`;
1982
+ const skipped = (row) => table.skip === void 0 || table.tenant === void 0 ? "false" : `(${tenant(row)} is not null and coalesce(${rowColumn(row, table.skip)}, false))`;
1835
1983
  const guard = qualified(ctx, CUSTOM_ROLES.guard);
1836
1984
  const entries = qualified(ctx, CUSTOM_ROLES.entries);
1837
- const allowEntryOf = custom.levels === true ? "c.permission || coalesce('@' || c.level, '')" : "c.permission";
1985
+ const allowEntryOf = `c.${allowEntry(ctx)}`;
1838
1986
  return `-- the application's roles table: renames, moves and deletes of a custom role carry its grants and includes
1839
1987
  create or replace function ${fn}()
1840
1988
  returns trigger
@@ -1913,6 +2061,58 @@ create trigger "permdock_custom_role_cascade"
1913
2061
  `;
1914
2062
  }
1915
2063
  //#endregion
2064
+ //#region src/cli/rls/custom-role-guard.ts
2065
+ /** A column of the roles-table row a trigger sees as `new`. */
2066
+ function column(name) {
2067
+ return `new.${quoteIdent(name)}`;
2068
+ }
2069
+ /**
2070
+ * The trigger on the application's roles table (`rls.customRoleWrites.roles`)
2071
+ * that refuses a tenant row named like a declared role (`declared-role`) or
2072
+ * like a shared custom role (`name-taken`). A row the `skip` column marks is
2073
+ * a system role of the tenant and passes. Empty without a tenant column: then
2074
+ * no row is a tenant row.
2075
+ */
2076
+ function customRoleNameGuardSql(ctx) {
2077
+ const custom = ctx.customRoles;
2078
+ const table = custom?.table;
2079
+ const root = ctx.scopes[0]?.name;
2080
+ if (custom === void 0 || table?.tenant === void 0 || root === void 0) return "";
2081
+ const tenant = `${column(table.tenant)}::${tenantTypeOf(ctx)}`;
2082
+ const skipped = table.skip === void 0 ? "false" : `coalesce(${column(table.skip)}, false)`;
2083
+ const fn = qualified(ctx, CUSTOM_ROLES.nameGuard);
2084
+ const target = quoteTable(qualifySqlTable(table.table));
2085
+ const key = `${column(table.key)}::text`;
2086
+ const scope = rolesTableScope(table, tenant, root, column);
2087
+ return `-- the application's roles table: a tenant row cannot take the name of a declared or a shared role
2088
+ create or replace function ${fn}()
2089
+ returns trigger
2090
+ language plpgsql
2091
+ volatile
2092
+ security definer
2093
+ set search_path = ''
2094
+ as $$
2095
+ begin
2096
+ if ${tenant} is null or ${skipped} then
2097
+ return new;
2098
+ end if;
2099
+ if ${key} = any(${sqlTextArray(custom.declared)}) then
2100
+ ${raiseSql(" ", "22023", `'permdock: ' || ${key} || ' is a declared role'`, "declared-role")}
2101
+ end if;
2102
+ if ${qualified(ctx, CUSTOM_ROLES.shared)}(${scope}, ${key}) then
2103
+ ${raiseSql(" ", "22023", `'permdock: ' || ${key} || ' is a shared role'`, "name-taken")}
2104
+ end if;
2105
+ return new;
2106
+ end;
2107
+ $$;
2108
+ revoke execute on function ${fn}() from public, anon, authenticated;
2109
+ drop trigger if exists "permdock_custom_role_name_guard" on ${target};
2110
+ create trigger "permdock_custom_role_name_guard"
2111
+ before insert or update on ${target}
2112
+ for each row execute function ${fn}();
2113
+ `;
2114
+ }
2115
+ //#endregion
1916
2116
  //#region src/cli/rls/custom-role-writes.ts
1917
2117
  function callerFunctionsSql(ctx, custom, root, levelReach, forUser) {
1918
2118
  const leveled = custom.levels === true;
@@ -2063,6 +2263,7 @@ function customRoleWritesSql(ctx, levelReach) {
2063
2263
  const guard = qualified(ctx, CUSTOM_ROLES.guard);
2064
2264
  const shape = qualified(ctx, CUSTOM_ROLES.shape);
2065
2265
  const entries = qualified(ctx, CUSTOM_ROLES.entries);
2266
+ const problem = qualified(ctx, CUSTOM_ROLES.entryProblem);
2066
2267
  const renames = Object.entries(custom.renamed ?? {}).toSorted(byKey);
2067
2268
  const current = (expr) => currentKeyExpr(renames, expr);
2068
2269
  const known = sqlTextArray([...custom.permissions ?? [], ...renames.map(([former]) => former)]);
@@ -2150,6 +2351,38 @@ ${callerFunctionsSql(ctx, custom, root, levelReach, true).beyond}
2150
2351
 
2151
2352
  ${callerFunctionsSql(ctx, custom, root, levelReach, true).guard}
2152
2353
  ` : ""}
2354
+ -- null when an allow entry is a declared permission inside the ceiling of the scope${leveled ? ", at a level the resource declares" : ""}; else unknown-permission, outside-ceiling${leveled ? " or unknown-level" : ""}
2355
+ create or replace function ${problem}(p_scope text, p_entry text)
2356
+ returns text
2357
+ language plpgsql
2358
+ stable
2359
+ security definer
2360
+ set search_path = ''
2361
+ as $$
2362
+ declare
2363
+ v_key text := ${current(leveled ? "split_part(p_entry, '@', 1)" : "p_entry")};
2364
+ begin
2365
+ if p_entry is null or not (v_key = any(${known})) then
2366
+ return 'unknown-permission';
2367
+ end if;
2368
+ if not exists (
2369
+ select 1 from ${ceiling} c
2370
+ where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
2371
+ ) then
2372
+ return 'outside-ceiling';
2373
+ end if;${leveled ? `
2374
+ if strpos(p_entry, '@') > 0 and not exists (
2375
+ select 1 from ${ceiling} c
2376
+ where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
2377
+ and split_part(c.grant_key, '@', 2) = split_part(p_entry, '@', 2)
2378
+ ) then
2379
+ return 'unknown-level';
2380
+ end if;` : ""}
2381
+ return null;
2382
+ end;
2383
+ $$;
2384
+ revoke execute on function ${problem}(text, text) from public, anon, authenticated;
2385
+
2153
2386
  -- the entries of a definition: declared keys and levels inside the ceiling of the scope, and declared includes
2154
2387
  create or replace function ${entries}(p_scope text, p_allow text[], p_deny text[], p_include text[])
2155
2388
  returns void
@@ -2161,23 +2394,18 @@ as $$
2161
2394
  declare
2162
2395
  v_entry text;
2163
2396
  v_key text;
2397
+ v_problem text;
2164
2398
  begin
2165
2399
  foreach v_entry in array coalesce(p_allow, '{}'::text[]) loop
2166
2400
  v_key := ${current(leveled ? "split_part(v_entry, '@', 1)" : "v_entry")};
2167
- if not (v_key = any(${known})) then
2401
+ v_problem := ${problem}(p_scope, v_entry);
2402
+ if v_problem = 'unknown-permission' then
2168
2403
  ${raiseSql(" ", "22023", `'permdock: ' || v_entry || ' is not a declared permission'`, "unknown-permission")}
2169
2404
  end if;
2170
- if not exists (
2171
- select 1 from ${ceiling} c
2172
- where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
2173
- ) then
2405
+ if v_problem = 'outside-ceiling' then
2174
2406
  ${raiseSql(" ", "22023", `'permdock: ' || v_key || ' is outside the ceiling of ' || p_scope`, "outside-ceiling")}
2175
2407
  end if;${leveled ? `
2176
- if strpos(v_entry, '@') > 0 and not exists (
2177
- select 1 from ${ceiling} c
2178
- where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
2179
- and split_part(c.grant_key, '@', 2) = split_part(v_entry, '@', 2)
2180
- ) then
2408
+ if v_problem = 'unknown-level' then
2181
2409
  ${raiseSql(" ", "22023", `'permdock: ' || v_entry || ' names a level the resource does not declare'`, "unknown-level")}
2182
2410
  end if;` : ""}
2183
2411
  end loop;
@@ -2438,17 +2666,103 @@ ${functionBody(memberRowsBody(ctx, scope, type, "p_user", memberForSources(ctx,
2438
2666
  revoke execute on function ${fn}(uuid) from public, anon, authenticated;`;
2439
2667
  }
2440
2668
  //#endregion
2669
+ //#region src/cli/rls/token-scopes.ts
2670
+ const TOKEN_SCOPE_ALLOWS = "permdock_token_scope_allows";
2671
+ /** `rls.tokenScopes` as the SQL generator needs it, or `undefined` when it is off. */
2672
+ function tokenScopesPlan(setting, policy) {
2673
+ if (setting !== true) return;
2674
+ const permissions = listPermissions(policy.permissions).map((leaf) => ({
2675
+ key: leaf.key,
2676
+ scope: leaf.scope,
2677
+ renamedFrom: formerKeys(leaf)
2678
+ }));
2679
+ return { scopes: delegatedScopeMap(permissions, policy.oauthScopes) };
2680
+ }
2681
+ function tokenScopeAllowsCall(ctx, grant) {
2682
+ return `${qualified(ctx, TOKEN_SCOPE_ALLOWS)}(${grant})`;
2683
+ }
2684
+ /** The token's scopes as rows of `s(scope)`: a space-separated string or, outside `guc`, a JSON array. */
2685
+ function tokenScopeRows(ctx) {
2686
+ if (ctx.dialect === "guc") return `regexp_split_to_table(btrim(coalesce(${subjectClaimSql(ctx, "scope")}, '')), '\\s+') s(scope)`;
2687
+ const scope = subjectClaimJsonSql(ctx, "scope");
2688
+ return `unnest(case jsonb_typeof(${scope})
2689
+ when 'string' then regexp_split_to_array(btrim(${scope} #>> '{}'), '\\s+')
2690
+ when 'array' then array(select jsonb_array_elements_text(${scope}))
2691
+ else '{}'::text[]
2692
+ end) s(scope)`;
2693
+ }
2694
+ /**
2695
+ * `permdock_token_scope_allows(p_grant)`: a delegated token (a `client_id`, or
2696
+ * an `act` that is not a support or impersonation session) reaches a grant key
2697
+ * only when one of its scopes maps to the key's permission. better-supabase's
2698
+ * `cap_allows` reads the same tokens the same way.
2699
+ */
2700
+ function tokenScopeAllowsSql(ctx, plan, anonExecute) {
2701
+ const fn = qualified(ctx, TOKEN_SCOPE_ALLOWS);
2702
+ const act = subjectClaimJsonSql(ctx, "act");
2703
+ const grants = anonExecute ? `revoke execute on function ${fn}(text) from public;
2704
+ grant execute on function ${fn}(text) to anon, authenticated;` : `revoke execute on function ${fn}(text) from public, anon;
2705
+ grant execute on function ${fn}(text) to authenticated;`;
2706
+ return `-- Token scopes: a delegated token holds only the permissions its scope claim maps to; denies always apply, and a token without client_id or an agent act is not limited
2707
+ create or replace function ${fn}(p_grant text)
2708
+ returns boolean
2709
+ language sql
2710
+ stable
2711
+ security definer
2712
+ set search_path = ''
2713
+ as $$
2714
+ select case
2715
+ when coalesce(${subjectClaimSql(ctx, "client_id")}, '') = ''
2716
+ and (${act} is null
2717
+ or ${act} = 'null'::jsonb
2718
+ or (jsonb_typeof(${act}) = 'object'
2719
+ and ${act} ->> 'kind' in ('support', 'impersonation'))) then true
2720
+ when exists (
2721
+ select 1 from ${qualified(ctx, "role_permissions")} rp
2722
+ where rp.grant_key = p_grant and rp.effect = 'deny'
2723
+ ) then true
2724
+ else exists (
2725
+ select 1
2726
+ from ${tokenScopeRows(ctx)}
2727
+ where s.scope <> ''
2728
+ and coalesce(${quoteLiteral(JSON.stringify(plan.scopes))}::jsonb -> s.scope, '[]'::jsonb)
2729
+ ? split_part(split_part(p_grant, '#', 1), '@', 1)
2730
+ )
2731
+ end
2732
+ $$;
2733
+ ${grants}`;
2734
+ }
2735
+ //#endregion
2441
2736
  //#region src/cli/rls/helpers.ts
2442
2737
  /**
2443
2738
  * The policy-side call for a grant key. Every form is uncorrelated, so
2444
2739
  * Postgres runs the helper once per statement (an InitPlan, or a hashed
2445
2740
  * SubPlan for `in`), never once per row.
2446
2741
  */
2447
- function accessSql(ctx, scope, grantKey, column) {
2742
+ function accessSql(ctx, scope, grantKey, column, helper) {
2448
2743
  const key = quoteLiteral(grantKey);
2449
2744
  if (scope === "global") return `(select ${qualified(ctx, HELPERS.has)}(${key}))`;
2450
2745
  if (column === void 0) throw configError(`PermDock CLI: ${scope}-scoped grant needs definePolicy({ scopes.${scope} })`);
2451
- return `${quoteIdent(column)} in (select ${qualified(ctx, permittedIdsHelper(scope))}(${key}))`;
2746
+ return `${quoteIdent(column)} in (select ${qualified(ctx, helper ?? permittedIdsHelper(scope))}(${key}))`;
2747
+ }
2748
+ /**
2749
+ * `permitted_<parent>_ids(p_grant)` for each \`rls.tables\` \`via\`: the parent
2750
+ * rows whose scope instance the grant reaches. \`security definer\`, so the
2751
+ * caller needs no read on the parent, and called uncorrelated like
2752
+ * \`permitted_<scope>_ids\`.
2753
+ */
2754
+ function viaHelpersSql(ctx, anonExecute) {
2755
+ const done = /* @__PURE__ */ new Set();
2756
+ const chunks = [];
2757
+ for (const via of Object.values(ctx.vias ?? {})) {
2758
+ if (done.has(via.helper)) continue;
2759
+ done.add(via.helper);
2760
+ chunks.push(`-- rows of ${via.table} whose ${via.column} is a ${via.scope} the grant reaches
2761
+ ${helperFunction(ctx, via.helper, `setof ${via.type}`, sqlBody(` select p.${quoteIdent(via.id)}
2762
+ from ${quoteQualified(via.table)} p
2763
+ where p.${quoteIdent(via.column)} in (select ${qualified(ctx, permittedIdsHelper(via.scope))}(p_grant))`), anonExecute)}`);
2764
+ }
2765
+ return chunks;
2452
2766
  }
2453
2767
  function userIdSql(ctx, anonExecute) {
2454
2768
  const fn = userIdHelperSql(ctx);
@@ -2536,6 +2850,40 @@ ${rows.map((row) => ` (${[
2536
2850
  );`;
2537
2851
  }
2538
2852
  /**
2853
+ * `role_permissions` as a view over the compiled rows (`rls.seeds: 'view'`).
2854
+ * `security_invoker` makes the view read as its caller, so it adds no
2855
+ * privilege; the table's revoke applies to the view the same way.
2856
+ */
2857
+ function seedViewSql(ctx, rows) {
2858
+ const view = qualified(ctx, "role_permissions");
2859
+ const columns = "role, permission, grant_key, scope, effect";
2860
+ return `-- rls.seeds: 'view': role_permissions is a view, so the rows travel with the schema
2861
+ create or replace view ${view} (${columns}) with (security_invoker = true) as
2862
+ ${rows.length === 0 ? `select null::text, null::text, null::text, null::text, null::text where false` : `select * from (values\n${rows.map((row) => ` (${[
2863
+ row.role,
2864
+ row.permission,
2865
+ row.grantKey,
2866
+ row.scope,
2867
+ row.effect
2868
+ ].map(quoteLiteral).join(", ")})`).join(",\n")}\n) as v(${columns})`};
2869
+ revoke all on table ${view} from anon, authenticated, public;`;
2870
+ }
2871
+ /** `<schema>.seed()`: the same upsert and delete as `seedSql`, for a declarative schema that carries no rows. */
2872
+ function seedFunctionSql(ctx, rows) {
2873
+ const fn = qualified(ctx, "seed");
2874
+ return `-- rls.seeds: 'function': the role_permissions rows are data, so a declarative diff leaves them out; run select ${fn}(); after each deploy
2875
+ create or replace function ${fn}()
2876
+ returns void
2877
+ language sql
2878
+ volatile
2879
+ security definer
2880
+ set search_path = ''
2881
+ as $permdock$
2882
+ ${seedSql(ctx, rows)}
2883
+ $permdock$;
2884
+ revoke execute on function ${fn}() from public, anon, authenticated;`;
2885
+ }
2886
+ /**
2539
2887
  * `role_permissions`, its seeds, and the three `security definer` helpers
2540
2888
  * with `search_path = ''`. `execute` goes to `authenticated` only.
2541
2889
  */
@@ -2545,9 +2893,13 @@ function helpersSql(ctx, rows, options) {
2545
2893
  const rp = qualified(ctx, "role_permissions");
2546
2894
  const chunks = [`-- permdock helpers (${ctx.authorize === "database" ? "database: reads the membership and user_roles tables" : "jwt: reads the role and memberships claims"})
2547
2895
  -- policies call them uncorrelated, so Postgres evaluates each once per statement`];
2548
- if (schema !== "public") chunks.push(`create schema if not exists ${s};\nrevoke all on schema ${s} from public;\ngrant usage on schema ${s} to ${options.anonExecute === true ? "anon, authenticated" : "authenticated"};`);
2896
+ if (schema !== "public") {
2897
+ const anonUsage = options.anonExecute === true || ctx.capabilities === true;
2898
+ chunks.push(`create schema if not exists ${s};\nrevoke all on schema ${s} from public;\ngrant usage on schema ${s} to ${anonUsage ? "anon, authenticated" : "authenticated"};`);
2899
+ }
2549
2900
  if (ctx.dialect === "supabase") chunks.push(userIdSql(ctx, options.anonExecute === true));
2550
- chunks.push(`create table if not exists ${rp} (
2901
+ if (ctx.seeds === "view") chunks.push(seedViewSql(ctx, rows));
2902
+ else chunks.push(`create table if not exists ${rp} (
2551
2903
  role text not null,
2552
2904
  permission text not null,
2553
2905
  grant_key text not null,
@@ -2557,7 +2909,8 @@ function helpersSql(ctx, rows, options) {
2557
2909
  );
2558
2910
  alter table ${rp} enable row level security;
2559
2911
  revoke all on table ${rp} from anon, authenticated, public;`);
2560
- if (options.withoutSeeds !== true) chunks.push(seedSql(ctx, rows));
2912
+ if (ctx.seeds === "function") chunks.push(seedFunctionSql(ctx, rows));
2913
+ else if (options.withoutSeeds !== true && ctx.seeds !== "view") chunks.push(seedSql(ctx, rows));
2561
2914
  if (ctx.authorize === "database" && options.userRoles && ctx.roles === void 0) {
2562
2915
  const ur = qualified(ctx, "user_roles");
2563
2916
  chunks.push(`create table if not exists ${ur} (
@@ -2573,6 +2926,7 @@ revoke all on table ${ur} from anon, authenticated, public;`);
2573
2926
  const anon = options.anonExecute === true;
2574
2927
  const keys = ctx.apiKeys;
2575
2928
  if (keys !== void 0) chunks.push(apiKeyAllowsSql(ctx, keys, anon));
2929
+ if (ctx.tokenScopes !== void 0) chunks.push(tokenScopeAllowsSql(ctx, ctx.tokenScopes, anon));
2576
2930
  const bodies = grantBodies(ctx);
2577
2931
  chunks.push(helperFunction(ctx, HELPERS.has, "boolean", bodies.has, anon));
2578
2932
  const capabilities = capabilitiesSql(ctx);
@@ -2584,6 +2938,7 @@ revoke all on table ${ur} from anon, authenticated, public;`);
2584
2938
  if (ctx.roleMemberScopes?.includes(scope.name) === true) chunks.push(memberRolesFunction(ctx, scope.name, type, anon));
2585
2939
  if (hasMemberFor(ctx, scope.name)) chunks.push(memberForFunction(ctx, scope.name, type));
2586
2940
  }
2941
+ chunks.push(...viaHelpersSql(ctx, anon));
2587
2942
  const forUser = forUserSql(ctx);
2588
2943
  if (forUser !== "") chunks.push(forUser);
2589
2944
  const writes = customRoleWritesSql(ctx, options.levelReach ?? []);
@@ -2591,6 +2946,12 @@ revoke all on table ${ur} from anon, authenticated, public;`);
2591
2946
  chunks.push(writes);
2592
2947
  const cascade = customRoleCascadeSql(ctx);
2593
2948
  if (cascade !== "") chunks.push(cascade);
2949
+ const sync = ctx.customRoles?.sync;
2950
+ if (sync !== void 0) chunks.push(sync);
2951
+ const byId = customRoleByIdSql(ctx, options.trustedReaders);
2952
+ if (byId !== "") chunks.push(byId);
2953
+ const nameGuard = customRoleNameGuardSql(ctx);
2954
+ if (nameGuard !== "") chunks.push(nameGuard);
2594
2955
  }
2595
2956
  const globalRoles = globalRolesSql(ctx);
2596
2957
  if (globalRoles !== "") chunks.push(globalRoles);
@@ -2600,20 +2961,26 @@ revoke all on table ${ur} from anon, authenticated, public;`);
2600
2961
  }
2601
2962
  function grantBodies(ctx) {
2602
2963
  const keys = ctx.apiKeys;
2964
+ const scoped = ctx.tokenScopes !== void 0;
2603
2965
  const set = ctx.grantSet === true;
2604
- const allows = apiKeyAllowsCall(ctx, set ? "ids.grant_key" : "p_grant");
2605
- const keyTenant = keyTenantSql(ctx, void 0) ?? "true";
2966
+ const ceiling = (grant) => {
2967
+ const calls = [...keys === void 0 ? [] : [apiKeyAllowsCall(ctx, grant)], ...scoped ? [tokenScopeAllowsCall(ctx, grant)] : []];
2968
+ return calls.length === 0 ? void 0 : calls.join(" and ");
2969
+ };
2970
+ const allows = ceiling(set ? "ids.grant_key" : "p_grant");
2971
+ const keyTenant = keys === void 0 ? "" : ` and ${keyTenantSql(ctx, void 0) ?? "true"}`;
2972
+ const hasAllows = ceiling(set ? "g.grant_key" : "p_grant");
2606
2973
  return {
2607
- has: sqlBody(keys === void 0 ? hasBody(ctx) : set ? ` select g.grant_key
2974
+ has: sqlBody(hasAllows === void 0 ? hasBody(ctx) : set ? ` select g.grant_key
2608
2975
  from (
2609
2976
  ${hasBody(ctx)}
2610
2977
  ) g(grant_key)
2611
- where ${apiKeyAllowsCall(ctx, "g.grant_key")} and ${keyTenant}` : ceilingHasSql(hasBody(ctx), `${allows} and ${keyTenant}`)),
2978
+ where ${hasAllows}${keyTenant}` : ceilingHasSql(hasBody(ctx), `${hasAllows}${keyTenant}`)),
2612
2979
  ids: (scope) => {
2613
2980
  const type = scopeTypeOf(ctx, scope);
2614
2981
  const body = scopedBody(ctx, scope, type);
2615
- if (keys === void 0) return body;
2616
- const sql = scope === rootName(ctx) ? `${body.sql}\n union\n${serviceKeyIdsSql(ctx, keys, scope, type)}` : body.sql;
2982
+ if (allows === void 0) return body;
2983
+ const sql = keys !== void 0 && scope === rootName(ctx) ? `${body.sql}\n union\n${serviceKeyIdsSql(ctx, keys, scope, type)}` : body.sql;
2617
2984
  return {
2618
2985
  sql: set ? ` select ids.id, ids.grant_key
2619
2986
  from (
@@ -2626,7 +2993,7 @@ ${sql}
2626
2993
  };
2627
2994
  }
2628
2995
  function forUserContext(ctx) {
2629
- const { apiKeys: _apiKeys, ...unkeyed } = ctx;
2996
+ const { apiKeys: _apiKeys, tokenScopes: _tokenScopes, ...unkeyed } = ctx;
2630
2997
  return {
2631
2998
  ...unkeyed,
2632
2999
  subjectId: "p_user",
@@ -3649,6 +4016,42 @@ function ownershipRules(policy, scopes) {
3649
4016
  };
3650
4017
  }
3651
4018
  /**
4019
+ * The roles whose `for` lists kinds that no membership source of the role's
4020
+ * scope can hold: such a role resolves to no one. A `via` read from a column
4021
+ * can hold any kind, and so does a source with no `via` (it is not
4022
+ * compared) and a scope with no table or source.
4023
+ */
4024
+ function unsatisfiedKinds(ctx, policy) {
4025
+ const out = [];
4026
+ for (const [role, kinds] of Object.entries(ctx.ownership?.kinds ?? {})) {
4027
+ const scope = roleScope(policy, ctx.scopes, role);
4028
+ if (scope === void 0) continue;
4029
+ const held = [];
4030
+ const mapped = scopeTable(ctx, scope)?.table;
4031
+ if (mapped !== void 0) {
4032
+ const via = memberVia(mapped);
4033
+ held.push({
4034
+ label: mapped.table,
4035
+ via: typeof via === "string" ? void 0 : via.value ?? void 0
4036
+ });
4037
+ } else for (const source of ctx.memberSources ?? ctx.sources ?? []) {
4038
+ if (source.sql.scope !== void 0 && source.sql.scope !== scope) continue;
4039
+ const via = source.sql.manifest.via;
4040
+ held.push({
4041
+ label: source.sql.table,
4042
+ via: via === void 0 ? void 0 : "value" in via && typeof via.value === "string" ? via.value : void 0
4043
+ });
4044
+ }
4045
+ if (held.length === 0 || held.some((entry) => entry.via === void 0 || kinds.includes(entry.via ?? ""))) continue;
4046
+ out.push({
4047
+ role,
4048
+ for: kinds,
4049
+ sources: held.map((entry) => `${entry.label} (via ${entry.via ?? ""})`)
4050
+ });
4051
+ }
4052
+ return out;
4053
+ }
4054
+ /**
3652
4055
  * The ownership objects: holder-count and transfer-only triggers on each
3653
4056
  * scope's membership table, and `permdock_can_assign` for the application's
3654
4057
  * own policies on those tables. Empty when no role declares a rule.
@@ -3694,4 +4097,4 @@ function ownershipSql(ctx) {
3694
4097
  return chunks.length === 0 ? "" : `${chunks.join("\n\n")}\n`;
3695
4098
  }
3696
4099
  //#endregion
3697
- export { activeUserSql as $, collectGrants as A, hasPermittedUsers as B, byKey as C, apiKeysPlan as D, apiKeyFields as E, CLOSURE as F, memberIdsHelper as G, memberForHelper as H, graphHelper as I, permittedIdsHelper as J, memberRoleOf as K, graphSqlName as L, compileConditionSql as M, contextRefs as N, breakGlassHolder as O, sqlFunctionNames as P, activeRowSql as Q, graphSqlText as R, byCodeUnit as S, apiKeyAllowsCall as T, memberForSources as U, linkHelper as V, memberForTable as W, scopeSources as X, restrictedHelper as Y, scopeTable as Z, capabilityAccessSql as _, guardedTables as a, currentKeyExpr as b, PERMISSION_HELPERS as c, permissionKeysForHelper as d, checkSuspension as et, permittedByPermissionHelper as f, seedSql as g, helpersSql as h, assignmentSql as i, roleNames as j, breakGlassKey as k, checkName as l, accessSql as m, ownershipSql as n, ASSIGNMENTS as o, requiresSql as p, permittedForHelper as q, holderSources as r, OWNERSHIP as s, ownershipRules as t, permissionHelpersSql as u, globalRoleRows as v, API_KEY_ALLOWS as w, rolesTableScope as x, globalRoleSource as y, hasMemberFor as z };
4100
+ export { permittedForHelper as $, apiKeyFields as A, sqlFunctionNames as B, globalRoleSource as C, byKey as D, byCodeUnit as E, collectGrants as F, hasMemberFor as G, graphHelper as H, roleNames as I, memberForHelper as J, hasPermittedUsers as K, andConditions as L, assignableRoles as M, breakGlassHolder as N, API_KEY_ALLOWS as O, breakGlassKey as P, memberRoleOf as Q, compileConditionSql as R, globalRoleRows as S, rolesTableScope as T, graphSqlName as U, CLOSURE as V, graphSqlText as W, memberForTable as X, memberForSources as Y, memberIdsHelper as Z, seedSql as _, assignmentSql as a, activeUserSql as at, tokenScopesPlan as b, OWNERSHIP as c, permissionHelpersSql as d, permittedIdsHelper as et, permissionKeysForHelper as f, helpersSql as g, accessSql as h, holderSources as i, activeRowSql as it, apiKeysPlan as j, apiKeyAllowsCall as k, PERMISSION_HELPERS as l, requiresSql as m, ownershipSql as n, scopeSources as nt, guardedTables as o, checkSuspension as ot, permittedByPermissionHelper as p, linkHelper as q, unsatisfiedKinds as r, scopeTable as rt, ASSIGNMENTS as s, ownershipRules as t, restrictedHelper as tt, checkName as u, TOKEN_SCOPE_ALLOWS as v, currentKeyExpr as w, capabilityAccessSql as x, tokenScopeAllowsCall as y, contextRefs as z };