permdock 0.6.0 → 0.8.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 (507) hide show
  1. package/README.md +2 -3
  2. package/dist/{2020-C9VN-x8c.js → 2020-CJbO8Bvz.js} +1 -3
  3. package/dist/a2a/index.d.ts +7 -7
  4. package/dist/a2a/index.js +9 -9
  5. package/dist/action-result-CL-QCITA.d.ts +19 -0
  6. package/dist/{adapter-context-CRhqyzCk.js → adapter-context-CIQpQxbE.js} +8 -2
  7. package/dist/{adoption-scan-DCYe60Pi.js → adoption-scan-BLs67P1l.js} +5 -5
  8. package/dist/agent/index.d.ts +3 -3
  9. package/dist/agent/index.js +1 -1
  10. package/dist/ai-sdk/index.d.ts +31 -10
  11. package/dist/ai-sdk/index.js +22 -12
  12. package/dist/{app-data-MuyzVqX8.js → app-data-UNNm07jV.js} +10 -7
  13. package/dist/{approval-header-CJACtHkv.js → approval-header-8QV6MU4H.js} +6 -3
  14. package/dist/{approval-prompt-Dg5LeAcT.js → approval-prompt-B8hQY69x.js} +5 -6
  15. package/dist/{approval-prompt-DvSSPQJX.d.ts → approval-prompt-PYagyQUj.d.ts} +13 -11
  16. package/dist/approvals/index.d.ts +5 -5
  17. package/dist/approvals/index.js +7 -7
  18. package/dist/{approvals-p1PGoVjb.js → approvals-DnIBTuXE.js} +128 -78
  19. package/dist/{approvers-CqPOLD-o.js → approvers-B4r0N_Uz.js} +8 -2
  20. package/dist/{arazzo-41WuzHou.js → arazzo-3RhGfkAK.js} +2 -2
  21. package/dist/{arazzo-CaUVBn7D.js → arazzo-B5-ulWif.js} +6 -6
  22. package/dist/{args-IIolxxKA.js → args-0FrVs5EH.js} +125 -8
  23. package/dist/astro/index.d.ts +30 -12
  24. package/dist/astro/index.js +2 -2
  25. package/dist/{audience-DabClFLQ.js → audience-BAkBDMqn.js} +8 -8
  26. package/dist/{auth-DRLEizbL.js → auth--yPyZNgr.js} +21 -10
  27. package/dist/authzen/index.d.ts +171 -25
  28. package/dist/authzen/index.js +321 -149
  29. package/dist/{automation-BNM3TtDo.d.ts → automation-DqEoWHBc.d.ts} +42 -32
  30. package/dist/{automation-DVjOckjV.js → automation-x3KEUusw.js} +22 -21
  31. package/dist/base-url-BQbddZO5.js +9 -0
  32. package/dist/better-auth/index.d.ts +2 -2
  33. package/dist/better-auth/index.js +13 -7
  34. package/dist/better-supabase/index.d.ts +392 -220
  35. package/dist/better-supabase/index.js +293 -152
  36. package/dist/{bind-DOyrv6zi.d.ts → bind-Cp2hBHzP.d.ts} +27 -12
  37. package/dist/{bind-CAvgUuKs.js → bind-D_sZSVA3.js} +22 -10
  38. package/dist/{body-CH5of8Ne.js → body-CJQwYVa8.js} +3 -3
  39. package/dist/{body-text-QcXok_SI.js → body-text-DQhcmqdY.js} +8 -4
  40. package/dist/{boundary-BM8a_P6E.js → boundary-BV3O1eud.js} +8 -2
  41. package/dist/{boundary-BmHxVH1J.d.ts → boundary-C9ck-3uK.d.ts} +13 -4
  42. package/dist/{budget-NbEtbJC_.js → budget-BELja5A9.js} +6 -3
  43. package/dist/{budget-JqcRqyhK.d.ts → budget-hYsPz_yE.d.ts} +6 -3
  44. package/dist/{build-instance-Bu_eDqYw.js → build-instance-CNWaYJyr.js} +3 -3
  45. package/dist/{canonical-json-BC8_nMTw.js → canonical-json-DwilZ1iB.js} +6 -3
  46. package/dist/{capability-N1K9IpqS.js → capability-BqX94r07.js} +31 -25
  47. package/dist/{capability-Drg-1YhL.d.ts → capability-ChuiONQU.d.ts} +41 -29
  48. package/dist/{casbin-Dyu2gnUD.js → casbin-H0rKxBx2.js} +16 -8
  49. package/dist/{casbin-Cr7RLtA3.js → casbin-cbSbhT_k.js} +2 -2
  50. package/dist/{casl-64gpT-8x.js → casl-C4OINZHI.js} +2 -2
  51. package/dist/{casl-DjEDhTCP.js → casl-CHHoCldP.js} +19 -10
  52. package/dist/catalog/index.d.ts +104 -23
  53. package/dist/catalog/index.js +117 -8
  54. package/dist/{catalog-HD2gf5mY.js → catalog-BFpz2EmT.js} +57 -26
  55. package/dist/{catalog-DZVbrF-z.js → catalog-DCspHDc8.js} +3 -3
  56. package/dist/{catalog-doc-Cdn7-0pQ.js → catalog-doc-DjMKzJp9.js} +53 -16
  57. package/dist/{catalog-fingerprint-CCP0-yQS.js → catalog-fingerprint-e6aCWosn.js} +19 -15
  58. package/dist/catalog-mRQQQhNK.js +1 -0
  59. package/dist/catalog-operations-Dadrl7Z-.js +60 -0
  60. package/dist/{create-CQWIpUG-.js → challenge-DImNcR7B.js} +157 -39
  61. package/dist/chat/index.d.ts +1 -1
  62. package/dist/chat/index.js +13 -10
  63. package/dist/chat-sdk/index.d.ts +44 -26
  64. package/dist/chat-sdk/index.js +17 -17
  65. package/dist/checks-CGkP3Z7A.js +40 -0
  66. package/dist/{claims-map-7oQ3ue4X.js → claims-map-CKkoAAYF.js} +5 -2
  67. package/dist/{claims-map-Ca-QX8C6.d.ts → claims-map-D1Pb5oRq.d.ts} +8 -5
  68. package/dist/claude-agent/index.d.ts +6 -6
  69. package/dist/claude-agent/index.js +6 -3
  70. package/dist/clerk/index.d.ts +11 -7
  71. package/dist/clerk/index.js +6 -6
  72. package/dist/cli/bin.js +4 -4
  73. package/dist/cli/index.d.ts +82 -7
  74. package/dist/cli/index.js +6 -6
  75. package/dist/{clients-C7zLFuWT.d.ts → clients-C_NOET3z.d.ts} +5 -5
  76. package/dist/{clients-DjU5ABMy.js → clients-DYr3U86j.js} +3 -3
  77. package/dist/cloud/index.d.ts +186 -38
  78. package/dist/cloud/index.js +293 -31
  79. package/dist/{cloud-ytQ3uviA.js → cloud-BFlGMaak.js} +5 -6
  80. package/dist/{codemod-5mpSpnUY.js → codemod-DLQ9Zpo4.js} +3 -4
  81. package/dist/{codemod-ua2hHy7w.js → codemod-DWSN2erG.js} +523 -201
  82. package/dist/collect-B5BL3klN.js +1 -0
  83. package/dist/{collect-mLTY4nwb.js → collect-B7ZFAJU3.js} +36 -64
  84. package/dist/{collect-Ci6akVwH.js → collect-Bzv3le0A.js} +6 -6
  85. package/dist/{compare-pfyVnBjE.js → compare-DTfyUunF.js} +2 -2
  86. package/dist/compile/index.d.ts +2 -2
  87. package/dist/compile/index.js +1 -1
  88. package/dist/{compile-1bsAezL0.d.ts → compile-BFzysW3o.d.ts} +36 -12
  89. package/dist/{compile-CMqMx_IN.js → compile-EzEMB-7v.js} +77 -33
  90. package/dist/{completion-BTFrHZhX.js → completion-saoYiRFO.js} +17 -8
  91. package/dist/config-BwPBfN0t.js +108 -0
  92. package/dist/{config-Bj2Ixq3d.js → config-DBeXXmBF.js} +2 -2
  93. package/dist/{config-report-DSpsLpCE.js → config-report-BipSWA6V.js} +9 -6
  94. package/dist/config-shape-ykV0Vm2n.js +134 -0
  95. package/dist/{connection-DjhIJSs2.js → connection-B72dg5Q2.js} +23 -22
  96. package/dist/connector-schema-CYLQNMYq.js +434 -0
  97. package/dist/{context-B_u3_bDJ.js → context-BnIzxTNE.js} +4 -3
  98. package/dist/convex/index.d.ts +13 -7
  99. package/dist/convex/index.js +9 -6
  100. package/dist/core-H-mbhpZc.js +341 -0
  101. package/dist/core-hWIARPDz.d.ts +206 -0
  102. package/dist/{create-CBMmFie-.d.ts → create-BSNrVuxh.d.ts} +87 -34
  103. package/dist/{create-B30OMUOJ.d.ts → create-BVq1lNQE.d.ts} +49 -28
  104. package/dist/{create-D_NmPOdH.d.ts → create-CM1FciYG.d.ts} +37 -29
  105. package/dist/{create-CGm28gyI.js → create-Cq4Uox0f.js} +13 -9
  106. package/dist/{create-DCF2SvoT.js → create-_noSrY1T.js} +15 -21
  107. package/dist/{credential-BcoiejpY.js → credential-CyyyvCsL.js} +42 -39
  108. package/dist/{credentials-DPyHmYz5.js → credentials-CdDxp7jN.js} +6 -6
  109. package/dist/{deciding-columns-Sq12mEA6.js → deciding-columns-NB4wcyBf.js} +13 -2
  110. package/dist/{delegation-j6-rc37o.js → delegation-CDB6_keF.js} +31 -22
  111. package/dist/delivery/index.d.ts +5 -5
  112. package/dist/delivery/index.js +6 -5
  113. package/dist/{delivery-DCAgNyCn.js → delivery-BupfvJIV.js} +81 -20
  114. package/dist/{delivery-event-xX4obB8F.js → delivery-event-JioK5YbS.js} +6 -6
  115. package/dist/denied-text-CHNlVInN.d.ts +22 -0
  116. package/dist/{describe-BybMRY7A.js → describe-Bb4hGK5d.js} +6 -5
  117. package/dist/{describe-B5qgDvEW.d.ts → describe-ByS9LgHK.d.ts} +5 -5
  118. package/dist/{device-BARQ8eYG.js → device-BwbJNmb2.js} +11 -8
  119. package/dist/{diff-CmuSD3vc.js → diff-CN8IYhC_.js} +2 -2
  120. package/dist/{diff-3jbx6Rm4.js → diff-iZWV65nC.js} +23 -14
  121. package/dist/{digest-B3kc_PgQ.d.ts → digest-8Yx2CchM.d.ts} +3 -3
  122. package/dist/{doctor-BdZw53-k.js → doctor-CHD1M9R5.js} +485 -141
  123. package/dist/{doctor-3ZqNHX_G.js → doctor-D3wo-yuz.js} +4 -4
  124. package/dist/drizzle/index.d.ts +33 -19
  125. package/dist/drizzle/index.js +17 -9
  126. package/dist/elysia/index.d.ts +17 -11
  127. package/dist/elysia/index.js +5 -5
  128. package/dist/{errors-BKy0EfhN.js → errors-B6R_lg2a.js} +13 -4
  129. package/dist/{errors-BWXBMaKK.d.ts → errors-CHwoRRpB.d.ts} +4 -1
  130. package/dist/{errors-CWTY6uih.js → errors-Vpu6rKHl.js} +14 -10
  131. package/dist/{errors-DH5QEGXh.d.ts → errors-zi_4T-rx.d.ts} +22 -6
  132. package/dist/{evaluate-CNClBvcj.js → evaluate-Dd8Ta0Ie.js} +185 -80
  133. package/dist/{evaluations-beAooV0n.js → evaluations-IBceAeKT.js} +41 -20
  134. package/dist/{evaluations-C_1vdSZc.d.ts → evaluations-z8dQFDaN.d.ts} +12 -6
  135. package/dist/eve/index.d.ts +9 -6
  136. package/dist/eve/index.js +4 -4
  137. package/dist/{event-time-CsegxXxB.js → event-time-BGy48400.js} +8 -4
  138. package/dist/{expiry-D4_YKRKD.js → expiry-6XByK0dm.js} +8 -5
  139. package/dist/express/index.d.ts +9 -6
  140. package/dist/express/index.js +9 -6
  141. package/dist/fastify/index.d.ts +16 -10
  142. package/dist/fastify/index.js +8 -5
  143. package/dist/{fetch-adapter-Dw0MTS3D.d.ts → fetch-adapter-C1DCvpqV.d.ts} +9 -6
  144. package/dist/{fetch-adapter-DD_gfmrC.js → fetch-adapter-JL43jD3E.js} +14 -11
  145. package/dist/fields-D0vUI6dq.js +498 -0
  146. package/dist/{files-C5ZI27eV.js → files-B3AA05f1.js} +21 -5
  147. package/dist/{files-D_JIzUrP.js → files-BSjrtIHw.js} +11 -4
  148. package/dist/{fixtures-B7XrLeY7.js → fixtures-A1_sp_tR.js} +15 -6
  149. package/dist/{flags-C3DktBDY.js → flags-DXjoTVbS.js} +2 -2
  150. package/dist/{freeze-CrU8nQeX.js → freeze-jjan0lCv.js} +4 -1
  151. package/dist/{from-snapshot-CTCMsFZk.js → from-snapshot-DCakWRsr.js} +26 -16
  152. package/dist/gen-B5kvbAoZ.js +58 -0
  153. package/dist/gen-DWCYcMQM.js +827 -0
  154. package/dist/generate-CRVV5V7l.js +165 -0
  155. package/dist/generate-CezbCaTI.js +275 -0
  156. package/dist/{grantee-hxrUsbpM.js → grantee-Dke-9rd1.js} +4 -4
  157. package/dist/{grantee-conditions-CcXf8hMM.js → grantee-conditions-BrYPe-w0.js} +12 -12
  158. package/dist/{grantee-match-DbENBHLB.js → grantee-match-7RZZvoT_.js} +12 -6
  159. package/dist/{graph-sql-B-dsAT6j.d.ts → graph-sql-BwFC4Qjs.d.ts} +3 -3
  160. package/dist/h3/index.d.ts +10 -7
  161. package/dist/h3/index.js +3 -3
  162. package/dist/headers-B9jQxjtW.d.ts +10 -0
  163. package/dist/{helpers-CBYVMjwh.js → helpers-DJicaG6f.js} +40 -28
  164. package/dist/hono/index.d.ts +16 -10
  165. package/dist/hono/index.js +5 -5
  166. package/dist/{hosted-BPk51-kX.d.ts → hosted-Cmtxogd0.d.ts} +44 -20
  167. package/dist/{hosted-B46-5LYE.js → hosted-D0IzESu_.js} +68 -25
  168. package/dist/{http-ujKm0JXE.js → http-DH94eh6f.js} +4 -4
  169. package/dist/{http-BBoKefeS.js → http-DKLXrFGk.js} +13 -7
  170. package/dist/{http-DXJ37RDA.js → http-DOL33cO0.js} +11 -8
  171. package/dist/{import-wyqXUyjm.js → import-CST57XLp.js} +64 -51
  172. package/dist/{import-CNV7I2Dw.js → import-_YqhhwvX.js} +6 -6
  173. package/dist/{index-B8YjkEZ4.d.ts → index-B8s204GL.d.ts} +64 -52
  174. package/dist/{index-Ch8LpNDp.d.ts → index-BT5-1VsX.d.ts} +64 -25
  175. package/dist/{index-DuWxc5dt.d.ts → index-BW_0MZOS.d.ts} +388 -149
  176. package/dist/index-BhxEUY9J.d.ts +233 -0
  177. package/dist/index-Bv5iXPqU.d.ts +210 -0
  178. package/dist/{index-C-6Letxg.d.ts → index-_9UiQIkt.d.ts} +164 -77
  179. package/dist/index.d.ts +22 -21
  180. package/dist/index.js +35 -33
  181. package/dist/{init-GG6Jh8HD.js → init-Dz_PsHdv.js} +5 -5
  182. package/dist/{init-DeMYsnYJ.js → init-cE3A8aXq.js} +10 -10
  183. package/dist/{instance-options-BsVpyGIk.d.ts → instance-options-B0KXqTrJ.d.ts} +4 -4
  184. package/dist/{instance-snapshot-BOEhcqIF.js → instance-snapshot--JWiTrvO.js} +81 -56
  185. package/dist/{instant-BluqDvyS.js → instant-DK3Iz83e.js} +2 -2
  186. package/dist/{internals-Dk8A8fX3.js → internals-B3IgHrvo.js} +6 -3
  187. package/dist/{json-schema-B-vAwlde.js → json-schema-BPGa-RjQ.js} +9 -3
  188. package/dist/{json-schema-CN_iLFH2.d.ts → json-schema-BfXgnRNT.d.ts} +4 -4
  189. package/dist/jwt/index.d.ts +125 -17
  190. package/dist/jwt/index.js +157 -16
  191. package/dist/{kernel-D0NVb1cK.js → kernel-C-fYydV2.js} +151 -16
  192. package/dist/kysely/index.d.ts +49 -33
  193. package/dist/kysely/index.js +9 -7
  194. package/dist/{live-options-BaAFCIiu.js → live-options-BBIT68le.js} +2 -2
  195. package/dist/{load-DFS_ry0W.js → load-DjSU5KXO.js} +6 -6
  196. package/dist/load-Dm0pSYH3.js +1 -0
  197. package/dist/{login-Dfe5RlYk.js → login-D-8tMqzo.js} +4 -4
  198. package/dist/{login-imhuwvn3.js → login-DplQ0s84.js} +18 -15
  199. package/dist/{manifest-dvL2XEe1.js → manifest-Cgiq5oqc.js} +67 -37
  200. package/dist/{manifest-mx5ghSFM.d.ts → manifest-CzzWtnAb.d.ts} +144 -28
  201. package/dist/{manifest-schema-C0kK_8V0.js → manifest-schema-2r37XRhD.js} +80 -9
  202. package/dist/{manifest-C8IzW3Do.js → manifest-wg8WkkpD.js} +11 -7
  203. package/dist/{map-claims-D3oRmTk0.js → map-claims-BNWCJtmP.js} +27 -18
  204. package/dist/{map-error-Dr70oA_3.js → map-error-pasIGqMT.js} +7 -7
  205. package/dist/{markers-C6joDI3o.js → markers-BeHZ4x9Q.js} +20 -5
  206. package/dist/mcp/index.d.ts +154 -47
  207. package/dist/mcp/index.js +200 -41
  208. package/dist/{migrate-CIq4-E9F.js → migrate-B7oP_sQU.js} +102 -31
  209. package/dist/module-values-BJTBdo6y.js +107 -0
  210. package/dist/nest/index.d.ts +23 -13
  211. package/dist/nest/index.js +18 -11
  212. package/dist/next/client.d.ts +5 -2
  213. package/dist/next/client.js +1 -1
  214. package/dist/next/index.d.ts +72 -26
  215. package/dist/next/index.js +110 -6
  216. package/dist/next/plugin.d.ts +1 -1
  217. package/dist/next/plugin.js +4 -3
  218. package/dist/node/index.d.ts +5 -5
  219. package/dist/node/index.js +3 -3
  220. package/dist/{ocsf-BFp-PZZA.js → ocsf-TcjRBgIs.js} +15 -5
  221. package/dist/openai/index.d.ts +13 -7
  222. package/dist/openai/index.js +3 -3
  223. package/dist/openapi/index.d.ts +61 -30
  224. package/dist/openapi/index.js +2 -2
  225. package/dist/{openapi-B7HZ0f-r.js → openapi-BOsx0xn9.js} +34 -24
  226. package/dist/{openapi-C4dDTVEd.js → openapi-CBZUhA3l.js} +4 -5
  227. package/dist/{openapi-DD1SV-5-.js → openapi-VeHHU2l9.js} +23 -14
  228. package/dist/{operation-findings-DgATDpvh.js → operation-findings-CXtFzlKJ.js} +3 -3
  229. package/dist/{operations-X0VM7F3m.d.ts → operations-Ci_rT6Uo.d.ts} +10 -7
  230. package/dist/orpc/index.d.ts +68 -14
  231. package/dist/orpc/index.js +62 -13
  232. package/dist/otel/index.d.ts +7 -4
  233. package/dist/otel/index.js +8 -5
  234. package/dist/output-files-BHOIlrfD.js +1 -0
  235. package/dist/{output-files-DhRBBE4n.js → output-files-BIQ_9pIs.js} +29 -17
  236. package/dist/{output-1im__-08.js → output-tkju6U7R.js} +15 -7
  237. package/dist/ownership-B4L59T9u.js +1054 -0
  238. package/dist/parse-1_ZheTW5.js +151 -0
  239. package/dist/parse-local-manifest-BQhTlLQS.d.ts +10 -0
  240. package/dist/parse-local-manifest-DEiCF4ah.js +69 -0
  241. package/dist/{parse-request-D_tUXG6M.js → parse-request-CwCZpyjV.js} +7 -8
  242. package/dist/{paths-DXSHAMIa.js → paths-CUvURzHp.js} +4 -1
  243. package/dist/pdp/index.d.ts +9 -6
  244. package/dist/pdp/index.js +23 -12
  245. package/dist/{peer-CRvC7Hg1.js → peer-C9LvBCcb.js} +1 -1
  246. package/dist/peer-Q0fuZ6hx.js +982 -0
  247. package/dist/{permdock-SV8ZINYT.d.ts → permdock-B6Sf4T_S.d.ts} +270 -117
  248. package/dist/{permdock-BrAscV28.js → permdock-L-_I_3zP.js} +226 -74
  249. package/dist/{permission-registry-Br4jSNVi.js → permission-registry-B8tYxPIt.js} +24 -11
  250. package/dist/{vocabulary-CN8H_9UF.d.ts → permission-types-CAY-GU7e.d.ts} +130 -104
  251. package/dist/{permissions-u_KHg-sN.d.ts → permissions-Bx6UI8n1.d.ts} +6 -2
  252. package/dist/{permissions-B-wCP5ZC.js → permissions-CVXmRILq.js} +3 -3
  253. package/dist/{permissions-BPxlwVhN.js → permissions-mDcTWoBs.js} +8 -5
  254. package/dist/pg-DQTqRrou.js +60 -0
  255. package/dist/{pins-8ILTftC9.js → pins-nqarl3oE.js} +4 -1
  256. package/dist/pkce-CnmmYqUZ.js +21 -0
  257. package/dist/{policy-77FSAq_V.d.ts → policy-Ba4bDhaj.d.ts} +5 -5
  258. package/dist/{policy-DbM5YYtV.js → policy-CLQ8__GF.js} +49 -20
  259. package/dist/{policy-from-catalog-C2gMa2Dj.js → policy-from-catalog-CJgOm6aq.js} +28 -25
  260. package/dist/{policy-module-CTlwMvLI.js → policy-module-xym9fnSq.js} +12 -6
  261. package/dist/{policy-types-n0uRTsW8.d.ts → policy-types-CCiDRZBH.d.ts} +802 -334
  262. package/dist/postgres-store-DcyrPS-r.js +1028 -0
  263. package/dist/{postgrest-DAjUkF9w.js → postgrest-D4nuPXIQ.js} +88 -36
  264. package/dist/{postgrest-CEgRbefE.d.ts → postgrest-DicQyqm-.d.ts} +60 -30
  265. package/dist/powersync-DHNQX06W.js +1117 -0
  266. package/dist/{powersync-DISt6SUe.js → powersync-DXqGuq9A.js} +3 -3
  267. package/dist/powersync-Djf-zCpB.js +1 -0
  268. package/dist/preset/index.d.ts +38 -28
  269. package/dist/preset/index.js +1 -1
  270. package/dist/{preset-DZdU0tne.js → preset-BjLzeFpp.js} +58 -34
  271. package/dist/{principal-source-pHneukrx.js → principal-source-C6N60GtM.js} +10 -10
  272. package/dist/prisma/index.d.ts +69 -41
  273. package/dist/prisma/index.js +21 -17
  274. package/dist/{problem-CTviyH5L.js → problem-BVLHFK33.js} +41 -21
  275. package/dist/{problem-details-cn5X30gZ.js → problem-details-CLDgX1KV.js} +13 -4
  276. package/dist/{problem-types-CJ2325SZ.js → problem-types-DSsRfONQ.js} +4 -1
  277. package/dist/prompt-BYc6drYW.js +938 -0
  278. package/dist/protected-action-BE7N_Luo.d.ts +58 -0
  279. package/dist/protected-action-DUYqb-Ma.js +80 -0
  280. package/dist/{protected-BlF0F6wL.js → protected-action-QEA8arRB.js} +48 -14
  281. package/dist/{provider-B1al-AZz.js → provider-C55OGXJ7.js} +5 -4
  282. package/dist/{provider-DILL9oPK.d.ts → provider-DXfq5c2d.d.ts} +1 -1
  283. package/dist/{provider-store-DM6DNcta.js → provider-store-CaTv00tr.js} +13 -9
  284. package/dist/pull-B65ndFNZ.js +1 -0
  285. package/dist/{pull-jEwbf80J.js → pull-CwpGaQSi.js} +12 -11
  286. package/dist/{push-BD4f15co.js → push-DgssgzkK.js} +62 -16
  287. package/dist/{pushed-policy-DCW91KQU.d.ts → pushed-policy-D1Eoi6NM.d.ts} +12 -6
  288. package/dist/{pushed-policy-CqtxkV4G.js → pushed-policy-ITwYKvvH.js} +10 -7
  289. package/dist/react/index.d.ts +9 -8
  290. package/dist/react/index.js +7 -6
  291. package/dist/react/provider-client.d.ts +1 -1
  292. package/dist/react/provider-client.js +1 -1
  293. package/dist/react-native/index.d.ts +104 -51
  294. package/dist/react-native/index.js +394 -67
  295. package/dist/{refs-K6eWUJ0H.js → refs-C8BKxc_e.js} +17 -3
  296. package/dist/{relation-walk-3OyDBF0S.js → relation-walk-DK_gkgL7.js} +12 -9
  297. package/dist/{relations-BnCWnJGI.js → relations-BxvRvDHK.js} +3 -3
  298. package/dist/{replay-Du1tJjG7.js → replay-BU09ad4L.js} +1 -1
  299. package/dist/{replay-Bkm-Ne4l.d.ts → replay-DNftIb_R.d.ts} +1 -1
  300. package/dist/{request-context-C81Pip9n.js → request-context-C25ilqiJ.js} +8 -8
  301. package/dist/{request-schema-CMreVuFM.js → request-schema-DhX7UbfQ.js} +4 -1
  302. package/dist/resend/index.d.ts +34 -28
  303. package/dist/resend/index.js +29 -29
  304. package/dist/resolve-names-CF7xXLSG.js +201 -0
  305. package/dist/{resource-relations-D_jw0BFx.js → resource-relations-8-pCScBO.js} +18 -9
  306. package/dist/{retire-CALjIQ7e.js → retire-B2xffQgY.js} +15 -6
  307. package/dist/{revocations-R07lFBnX.d.ts → revocations-BaUXHM1N.d.ts} +6 -3
  308. package/dist/{rls-DTvYd-lI.js → rls-CNEH2_BG.js} +743 -624
  309. package/dist/{rls-BAqP4q66.js → rls-Ca8Jz9b5.js} +41 -10
  310. package/dist/{role-builder-uWQZ4v84.js → role-builder-BK2-T6oD.js} +41 -20
  311. package/dist/rolldown-runtime-C3I60J0A.js +4 -0
  312. package/dist/{row-conditions-h2HythXQ.js → row-conditions-BAGHOmVq.js} +4 -3
  313. package/dist/{rpc-VSl5ylaQ.js → rpc-fNqAIqQk.js} +8 -5
  314. package/dist/{run-CUKuz0A8.js → run-Bz26OTfO.js} +26 -23
  315. package/dist/run-D-JE8QWb.js +1 -0
  316. package/dist/{run-ticket-DocwaTKn.d.ts → run-ticket-DbWI9f99.d.ts} +33 -13
  317. package/dist/{run-ticket-sxFI6LAk.js → run-ticket-Dv7aF7Dk.js} +29 -8
  318. package/dist/{runtime-D3VTAiCA.js → runtime-BGnrOkVH.js} +5 -2
  319. package/dist/{saas-BCXRuXca.js → saas-iWnUdwCt.js} +42 -29
  320. package/dist/{scan-qvOe0hS3.js → scan-DCj1KQfn.js} +1 -1
  321. package/dist/{schema-B4v0Qrac.js → schema-BOJjqlWL.js} +3 -2
  322. package/dist/{schema-yLuBWWsh.js → schema-Dst9I9Gs.js} +9 -5
  323. package/dist/scim/index.d.ts +3 -2
  324. package/dist/scim/index.js +3 -1
  325. package/dist/scim-D8YupzX1.js +44 -0
  326. package/dist/{scim-ClHtpyV3.js → scim-jJBJ7g1Z.js} +35 -551
  327. package/dist/{scopes-CUPSjo9q.js → scopes-0Up_UA2t.js} +42 -24
  328. package/dist/server/index.d.ts +12 -12
  329. package/dist/server/index.js +13 -13
  330. package/dist/{server-C3WXAHZj.js → server-3cVlTLde.js} +31 -31
  331. package/dist/{server-types-CKQiCnON.d.ts → server-types-CIlpSA2G.d.ts} +81 -46
  332. package/dist/{sha256-D9ihqmvd.js → sha256-C-4WXKIu.js} +4 -3
  333. package/dist/{shared-BAK8t4Uo.js → shared-Afctg7B0.js} +100 -61
  334. package/dist/{shared-DvvFzYXe.js → shared-l0oGGetg.js} +9 -6
  335. package/dist/{signals-D3NcTKUS.js → signals-B1vHR3fN.js} +2 -2
  336. package/dist/{sink-BEZSlMS_.js → sink-D9_vh372.js} +32 -21
  337. package/dist/{sink-4-eQIRWf.d.ts → sink-Dvi7YZG_.d.ts} +44 -20
  338. package/dist/{skills-B6lVdD_W.js → skills-Bjj7BNEQ.js} +16 -12
  339. package/dist/{skills-CTYwkl44.js → skills-OuoMR49L.js} +3 -3
  340. package/dist/{snapshot-B7DkYg6g.js → snapshot-CXlzi69u.js} +21 -18
  341. package/dist/{snapshot-cache-PXI-vGpY.d.ts → snapshot-cache-BAXZ0KOp.d.ts} +21 -12
  342. package/dist/{snapshot-cache-DZHQ0KF6.js → snapshot-cache-Bx_4xC_6.js} +14 -11
  343. package/dist/solid/index.d.ts +55 -15
  344. package/dist/solid/index.js +46 -12
  345. package/dist/solid-start/index.d.ts +30 -12
  346. package/dist/solid-start/index.js +3 -3
  347. package/dist/sources-B1Os_YUu.d.ts +312 -0
  348. package/dist/{sources-vs5dGOsS.js → sources-CMAM-2A3.js} +156 -51
  349. package/dist/sources-qoIJQrJv.js +11 -0
  350. package/dist/{sql-B5T1IL83.js → sql-0WjxIp9g.js} +32 -7
  351. package/dist/sql-B80DFdpG.d.ts +10 -0
  352. package/dist/{src-Cu2m75uo.js → src-DZ0l9UAg.js} +305 -123
  353. package/dist/ssf/index.d.ts +4 -4
  354. package/dist/ssf/index.js +665 -224
  355. package/dist/{standard-webhooks-rbD7LS6h.d.ts → standard-webhooks-C-qX4mJW.d.ts} +7 -7
  356. package/dist/{standard-webhooks-DgFFD-Ga.js → standard-webhooks-D67a3nzk.js} +5 -5
  357. package/dist/{store-BDutJkDL.js → store-RNFvaJJv.js} +43 -29
  358. package/dist/{store-options-u0ycEe8s.js → store-options-Dil3lraY.js} +105 -15
  359. package/dist/stores-C0Kta5Ft.d.ts +59 -0
  360. package/dist/{stores-CGhM8BN_.js → stores-DxZWDXCs.js} +42 -5
  361. package/dist/{stream-CvdUS9V1.js → stream-CDvF0sJm.js} +5 -5
  362. package/dist/{stream-CeCMJmBB.d.ts → stream-ltuLdtTG.d.ts} +4 -3
  363. package/dist/{style-DiKWnAvn.js → style-DblOtSga.js} +1 -1
  364. package/dist/{style-Uao76wO7.js → style-Ds11Hmk4.js} +3 -1
  365. package/dist/{subject-settings-CkEwrw3W.js → subject-settings-8sH5HlII.js} +8 -4
  366. package/dist/{row-check-Dt35czLy.d.ts → subject-settings-vNW8qmUH.d.ts} +30 -18
  367. package/dist/supabase/index.d.ts +48 -37
  368. package/dist/supabase/index.js +6 -6
  369. package/dist/supabase/middleware.d.ts +19 -13
  370. package/dist/supabase/middleware.js +7 -7
  371. package/dist/{supabase-x5JtR2tc.js → supabase-BL1qJzzY.js} +82 -36
  372. package/dist/{supabase-By_frk3U.js → supabase-D39cuGPt.js} +3 -3
  373. package/dist/{supabase-hook-BMoEhZir.js → supabase-hook-B-yXnzUl.js} +1206 -286
  374. package/dist/supabase-hook-H-MtE9Wd.js +1 -0
  375. package/dist/supautils-BU-pAEpf.js +808 -0
  376. package/dist/{support-session-C6xJQYut.js → support-session-BsmPCbz8.js} +22 -22
  377. package/dist/svelte/PermissionBoundary.svelte +2 -6
  378. package/dist/svelte/Protected.svelte +2 -8
  379. package/dist/svelte/index.d.ts +9 -8
  380. package/dist/svelte/index.js +10 -9
  381. package/dist/svelte/runtime.d.ts +4 -4
  382. package/dist/svelte/runtime.js +4 -4
  383. package/dist/svelte/source.d.ts +9 -8
  384. package/dist/svelte/source.js +6 -5
  385. package/dist/sveltekit/index.d.ts +30 -12
  386. package/dist/sveltekit/index.js +2 -2
  387. package/dist/{ownership-DqaJNJPn.js → tables-DeMNEy6V.js} +1025 -1085
  388. package/dist/tanstack-start/index.d.ts +22 -10
  389. package/dist/tanstack-start/index.js +2 -2
  390. package/dist/{target-Bw37TFNA.js → target-qdEAnnhU.js} +6 -6
  391. package/dist/{tenancy-DN2QAx9z.js → tenancy-5d1zVBvK.js} +36 -21
  392. package/dist/terminal/index.d.ts +23 -14
  393. package/dist/terminal/index.js +17 -20
  394. package/dist/testing/core.d.ts +2 -0
  395. package/dist/testing/core.js +1 -0
  396. package/dist/testing/fixtures/index.d.ts +15 -0
  397. package/dist/testing/fixtures/index.js +831 -0
  398. package/dist/testing/index.d.ts +235 -285
  399. package/dist/testing/index.js +502 -927
  400. package/dist/testing/saas/index.d.ts +43 -27
  401. package/dist/testing/saas/index.js +2 -2
  402. package/dist/testing/saas/permissions.d.ts +1 -1
  403. package/dist/testing/saas/permissions.js +1 -1
  404. package/dist/text-diff-BtfSmdUo.js +444 -0
  405. package/dist/{thenable-yD7SabqG.js → thenable-BNSM1U2R.js} +12 -3
  406. package/dist/{time-DkWjdL98.js → time-nR1JesoH.js} +32 -8
  407. package/dist/{token-BLZcjQgt.js → token-DC9EQiet.js} +15 -13
  408. package/dist/{tool-refusal-CMLN5xhn.js → tool-refusal-Qpjs_W6M.js} +5 -2
  409. package/dist/trpc/index.d.ts +11 -8
  410. package/dist/trpc/index.js +15 -9
  411. package/dist/type-helpers-BIPIDa6a.d.ts +50 -0
  412. package/dist/{types-8RnwUAvE.d.ts → types-AMJ5qoU_.d.ts} +49 -30
  413. package/dist/{types-C_IiEs64.d.ts → types-B2mlGuhW.d.ts} +14 -11
  414. package/dist/{types-_WhUP7ud.d.ts → types-B4uqKDnp.d.ts} +25 -12
  415. package/dist/{types-CX7AvRqP.d.ts → types-BJk0BKRX.d.ts} +24 -14
  416. package/dist/types-B_d_g7T3.d.ts +262 -0
  417. package/dist/{types-BdtSDbBO.d.ts → types-B_tiYvk8.d.ts} +59 -32
  418. package/dist/{types-DqzsnlSA.js → types-CcUXciSj.js} +17 -5
  419. package/dist/{types-Dfv0aTER.d.ts → types-DC1Loeh3.d.ts} +449 -168
  420. package/dist/{types-lUlG3TVd.d.ts → types-DHKPcqzr.d.ts} +2 -2
  421. package/dist/{types-C791HFu9.d.ts → types-DjuMBNF9.d.ts} +68 -32
  422. package/dist/{types-C863OQiR.d.ts → types-HzqOtvsJ.d.ts} +23 -12
  423. package/dist/{types-Druu9wLM.d.ts → types-ZzRGoViD.d.ts} +77 -24
  424. package/dist/{types-BzzaHxt3.d.ts → types-ah9NlU6j.d.ts} +137 -48
  425. package/dist/{types-CVZSNocA.d.ts → types-djVzLlIV.d.ts} +31 -14
  426. package/dist/{types-BtHtcKMr.d.ts → types-yUuPR5Hy.d.ts} +47 -24
  427. package/dist/unplugin/index.d.ts +1 -1
  428. package/dist/unplugin/index.js +2 -2
  429. package/dist/usage-CYeXek0p.js +1 -0
  430. package/dist/{usage-SveTRTyr.js → usage-CxEzS4ey.js} +50 -26
  431. package/dist/{usage-CRVl9V3t.js → usage-ab0PdusX.js} +2 -2
  432. package/dist/{validation-error-ZfUHbKvf.js → validation-error-CYg2kJEw.js} +4 -1
  433. package/dist/{validation-error-DHUrAkku.d.ts → validation-error-YGg2mXyH.d.ts} +5 -2
  434. package/dist/{validity-CZ7-y0Cj.js → validity-Buc7w8E9.js} +13 -10
  435. package/dist/{verifier-DHuh-okk.js → verifier-DUV6pLqZ.js} +10 -4
  436. package/dist/{verify-BaAKx7t6.js → verify-DZeqoWu5.js} +417 -489
  437. package/dist/version-DMQ0dnN5.js +48 -0
  438. package/dist/version-DizhmD72.js +1 -0
  439. package/dist/{views-DTIIO-oQ.js → views-CD8wuL4Z.js} +19 -6
  440. package/dist/vocabulary-DEJ5j4mR.d.ts +70 -0
  441. package/dist/vue/index.d.ts +71 -21
  442. package/dist/vue/index.js +50 -13
  443. package/dist/{waiter-B986w1oK.d.ts → waiter-Bu9zAlE0.d.ts} +13 -10
  444. package/dist/{waiter-BGUtGYoU.js → waiter-aBLM63_f.js} +1 -1
  445. package/dist/{watch-vea54PRw.js → watch-Cx_WciLE.js} +8 -8
  446. package/dist/watch-DB42U1ib.js +1 -0
  447. package/dist/webmcp/index.d.ts +11 -11
  448. package/dist/webmcp/index.js +8 -5
  449. package/dist/{where-scope-C9vfEphl.js → where-scope-DlCdCM_8.js} +56 -30
  450. package/dist/{who-can-DWBAcaCQ.js → who-can-rI7dCR9Q.js} +23 -20
  451. package/dist/workflow/index.d.ts +3 -3
  452. package/dist/workflow/index.js +13 -13
  453. package/dist/workflow-sdk/index.d.ts +19 -13
  454. package/dist/workflow-sdk/index.js +6 -6
  455. package/dist/{wrap-gX61EtpB.js → wrap-B2wziJI_.js} +11 -5
  456. package/dist/{wrap-C5jC6sKI.d.ts → wrap-DlJE3gS_.d.ts} +8 -5
  457. package/package.json +25 -14
  458. package/schemas/catalog-v1.json +3 -0
  459. package/schemas/cli-result-v1.json +1 -0
  460. package/schemas/cloud-api-v1.json +152 -7
  461. package/schemas/connector-v1.json +8 -0
  462. package/schemas/supabase-claims-v1.json +5 -0
  463. package/schemas/supabase-manifest-v1.json +308 -75
  464. package/skills/permdock/SKILL.md +1 -0
  465. package/skills/permdock-agents/references/runtimes.md +3 -1
  466. package/skills/permdock-approvals/references/stores-and-resume.md +2 -1
  467. package/skills/permdock-credentials/references/tokens-keys-links.md +8 -6
  468. package/skills/permdock-data/references/queries-and-rls.md +6 -2
  469. package/skills/permdock-tenancy/SKILL.md +1 -1
  470. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +2 -1
  471. package/skills/permdock-wire/references/adapters.md +28 -22
  472. package/skills/permdock-wire/references/policy-options.md +1 -0
  473. package/dist/catalog-BoWglDPI.js +0 -1
  474. package/dist/collect-D7eQDffE.js +0 -1
  475. package/dist/config-bs_kZeYq.js +0 -162
  476. package/dist/denied-text-CiFQNuMV.d.ts +0 -19
  477. package/dist/fields-OYG_JR78.js +0 -203
  478. package/dist/generate-CNU1pIOo.js +0 -171
  479. package/dist/headers-BIjfMaoC.js +0 -8
  480. package/dist/headers-CIbDTUTK.d.ts +0 -7
  481. package/dist/index-Cp_ilKi1.d.ts +0 -91
  482. package/dist/index-CueFnxQc.d.ts +0 -110
  483. package/dist/load-BxEtwlBS.js +0 -1
  484. package/dist/output-files-B3IZJksC.js +0 -1
  485. package/dist/package-root-Dk_ETdkp.js +0 -31
  486. package/dist/parse-Cxq0WxrA.js +0 -53
  487. package/dist/parse-local-manifest-5ODwqu8b.d.ts +0 -10
  488. package/dist/parse-local-manifest-CVSs6J9I.js +0 -34
  489. package/dist/peer-C8swJ2GH.js +0 -50
  490. package/dist/policies-DP2nqRoF.js +0 -295
  491. package/dist/powersync-BkWWCS1K.js +0 -538
  492. package/dist/powersync-MMJ2mI2q.js +0 -1
  493. package/dist/problem-details-B45V5d2b.d.ts +0 -7
  494. package/dist/prompt-DfZLJLIn.js +0 -32
  495. package/dist/protected-RFGpakN9.d.ts +0 -38
  496. package/dist/pull-CvpINcMK.js +0 -1
  497. package/dist/run-DDwUaJDU.js +0 -1
  498. package/dist/sources-Xc4zkszI.d.ts +0 -190
  499. package/dist/stores-BUGNbJTt.d.ts +0 -30
  500. package/dist/supabase-hook-CCw5m35C.js +0 -1
  501. package/dist/supautils-B6HYL9eL.js +0 -91
  502. package/dist/text-diff-BQcUapOb.js +0 -15
  503. package/dist/types-B3_ooZgZ.d.ts +0 -172
  504. package/dist/usage-CZpSQP6L.js +0 -1
  505. package/dist/version-CEbQ5UeM.js +0 -18
  506. package/dist/version-DXkYUISx.js +0 -1
  507. package/dist/watch--ovlPOc0.js +0 -1
@@ -47,11 +47,13 @@ const decision = await decideCredential(
47
47
  );
48
48
  if (decision.outcome === "granted") {
49
49
  const key = generateApiKey(decision.credential.id);
50
- await db.insert(apiKeys).values({
51
- id: decision.credential.id,
52
- hash: await hashApiKey(key),
53
- credential: decision.credential,
54
- });
50
+ await db
51
+ .insert(apiKeys)
52
+ .values({
53
+ id: decision.credential.id,
54
+ hash: await hashApiKey(key),
55
+ credential: decision.credential,
56
+ });
55
57
  return key; // shown once
56
58
  }
57
59
  ```
@@ -123,7 +125,7 @@ const subject = await subjectFromCapability(token, {
123
125
  - The result is a `kind: 'link'` principal with one `{ on, roles, via: 'link' }` membership. It never holds a scope or global role.
124
126
  - `linkPolicy` returns the tenant's `LinkPolicy` (`maxLifetime`, `redeemers`, `once`). Passing the same policy to `signCapability` refuses to mint a link the resolver would refuse.
125
127
  - Never an unguessable id without expiry, and never a security-definer RPC that reads the guest page's rows.
126
- - The one exception is resolving a stored link. When links live in better-supabase's share-links block, its `resolve_share_link` is `security definer` and reads only the link table. `subjectFromShareLink(token, { shareLinks, links, ttl })` from `permdock/better-supabase` turns the row into the same link subject. The page's rows are then read through `exchangeCapability` and RLS, as for a signed link. `links` maps each `subjectType` to a resource and maps the row's role to PermDock roles. An unmapped type or role, and a revoked or expired row, are anonymous. A link pinned to a version is refused, because a capability reads the current rows; read it through better-supabase's `versions.getShared(token)`. See [row-backed links](https://permdock.com/docs/concepts/capabilities#row-backed-links).
128
+ - The one exception is resolving a stored link. When links live in better-supabase's share-links block, its `resolve_share_link` is `security definer` and reads only the link table. `subjectFromShareLink(token, { shareLinks, links, ttl })` from `permdock/better-supabase` turns the row into the same link subject. The page's rows are then read through `exchangeCapability` and RLS, as for a signed link. `links` maps each subject type (`subject.type` from better-supabase 0.14, `subjectType` before it) to a resource and maps the row's role to PermDock roles. An unmapped type or role, and a revoked or expired row, are anonymous. A link pinned to a version is refused, because a capability reads the current rows; read it through better-supabase's `versions.getShared(token)`. See [row-backed links](https://permdock.com/docs/concepts/capabilities#row-backed-links).
127
129
 
128
130
  With Supabase, let RLS serve the link through an exchange, never the link token itself:
129
131
 
@@ -36,16 +36,20 @@ pnpm exec permdock rls verify --introspect
36
36
  ```
37
37
 
38
38
  - List the scope instances a permission reaches in process with `permdock.permittedIds(permission, scope, { within })`, never a loop of `can` over memberships with a made-up row.
39
- - Generated policies call `permdock_has('<key>')` and one `permitted_<scope>_ids('<key>')` per declared scope, which Postgres runs once per statement. Hand-written policies that only need membership (an organization switcher) call `member_<scope>_ids()` instead of inventing a permission key. Trusted SQL that acts for a stored user (a job, a trigger, a deferred approval) calls `permdock_has_for(p_user, '<key>')` and `permitted_<scope>_ids_for(p_user, '<key>')` in `database` mode, or the generated `permitted_<resource>_rows_for`, never a hand-written function that rewrites `request.jwt.claims`. With `rls.rowHelpers`, a hand-written policy or function that needs the rows a permission reaches through the graph calls `permitted_<resource>_rows('<permission key>')` instead of restating closure walks, link hops and `includes`. A table's own `select` or `insert` policy calls the row-valued `permitted_<resource>_row(<table>, '<permission key>')` instead, so `insert ... returning` sees the new row. Hand-written policies and functions that apply no row condition call `permitted_<scope>_ids_by_permission('<permission key>')` or `permdock_has_permission('<permission key>')` instead of spelling a positional `#n` grant key. A screen or RPC that checks many permissions on one organization calls `permitted_<scope>_permission_keys(<id>)` once (or `(<id>, array['<key>', ...])` for a few keys) instead of one helper call per key. A role editor or admin screen lists a role's keys with `permdock_role_permissions(p_role, p_scope, p_tenant, p_scope_id)` (declared roles, and custom roles of the caller's tenant in `database` mode) and the catalog with `permdock_permission_keys()` (`permdock_permission_keys('<scope>')` for the keys a role on that scope can hold), instead of joining `role_permissions` and the custom-role tables by hand; server code reads any tenant's roles with `permdock_trusted_role_permissions`, granted through `rls.trustedReaders`. Set `--tenant-type` (or `rls.tenantType`, `rls.scopeTypes`) to the scope columns' types.
39
+ - Generated policies call `permdock_has('<key>')` and one `permitted_<scope>_ids('<key>')` per declared scope, which Postgres runs once per statement. Hand-written policies that only need membership (an organization switcher) call `member_<scope>_ids()` instead of inventing a permission key. A policy that passes a user id, such as better-supabase's team sharing, calls `member_team_ids_of(<user>)` on Supabase: it answers only when the id is the caller's. Trusted SQL that acts for a stored user (a job, a trigger, a deferred approval) calls `permdock_has_for(p_user, '<key>')` and `permitted_<scope>_ids_for(p_user, '<key>')` in `database` mode, or the generated `permitted_<resource>_rows_for`, never a hand-written function that rewrites `request.jwt.claims`. With `rls.rowHelpers`, a hand-written policy or function that needs the rows a permission reaches through the graph calls `permitted_<resource>_rows('<permission key>')` instead of restating closure walks, link hops and `includes`. A table's own `select` or `insert` policy calls the row-valued `permitted_<resource>_row(<table>, '<permission key>')` instead, so `insert ... returning` sees the new row. Hand-written policies and functions that apply no row condition call `permitted_<scope>_ids_by_permission('<permission key>')` or `permdock_has_permission('<permission key>')` instead of spelling a positional `#n` grant key. A screen or RPC that checks many permissions on one organization calls `permitted_<scope>_permission_keys(<id>)` once (or `(<id>, array['<key>', ...])` for a few keys) instead of one helper call per key. A role editor or admin screen lists a role's keys with `permdock_role_permissions(p_role, p_scope, p_tenant, p_scope_id)` (declared roles, and custom roles of the caller's tenant in `database` mode) and the catalog with `permdock_permission_keys()` (`permdock_permission_keys('<scope>')` for the keys a role on that scope can hold), instead of joining `role_permissions` and the custom-role tables by hand; server code reads any tenant's roles with `permdock_trusted_role_permissions`, granted through `rls.trustedReaders`. Set `--tenant-type` (or `rls.scopeTypes`, keyed by scope name or `tenant` and `team`) to the scope columns' types.
40
40
  - `--authorize database` (default) reads the membership tables per statement; map each under `rls.memberships.scopes.<scope>` with `columns` for its id and its ancestors', and `via` so roles with `for` apply: a column name, or `{ value: 'staff' }` when every row has that kind (never add a generated column for it). `--authorize jwt` reads token claims and stays stale until the token refreshes; keep the token lifetime at 3600 seconds or less (PD019).
41
+ - When the role column holds values that are not role keys, give the membership source `role: { column, map: { value: 'role' } }`; a value mapped to `null` grants nothing. `permdock rls explain` prints the tables, membership sources, custom roles table and triggers a generate run binds to, without writing a file. `rls.seeds: 'function'` writes the `role_permissions` rows as `<schema>.seed()` instead of inserts; run `select <schema>.seed();` after each deploy, and PD054 checks the function body.
42
+ - `rls verify --introspect` checks each `security definer` helper by its full signature, so every overload is checked on its own. A helper finding id names the signature (`helper-definer:permdock.permdock_has(text):helper`); write `rls.verify.ignore` entries with it.
43
+ - `permdock rls verify --kind pgtap` writes `supabase/tests/permdock_rls.test.sql` (or `--out`) and, in `database` mode, inserts each fixture's memberships before it switches role.
41
44
  - The helpers narrow to the tenant claim (`rls.tenantClaim`) whenever the token carries one; the Supabase hook writes it from `activeFrom`. When the app takes the tenant from the URL and one session spans tenants, set `rls.tenants: 'all'` so the database admits every member tenant, and keep filtering each query by the URL's tenant.
42
45
  - `--policy-per-role` keeps one policy per role for review. `--custom-roles` (or `rls.customRoles: true`) resolves tenant-defined roles inside the `permdock_ceiling` view. In `database` mode, save a custom role with `permdock_replace_custom_role_grants(tenant, scope, scope_id, role, allow, deny, include)` and rename or delete it with `permdock_rename_custom_role_grants` and `permdock_delete_custom_role_grants`, never with hand-written `security definer` functions or direct writes; failures raise `22023` or `42501` with the reason as the `hint`. A platform custom role passes a null tenant and scope `global`; a shared custom role passes a null tenant and scope id at its scope. Seeds, jobs and backends with no member caller use the `permdock_trusted_*` variants, which keep the definition checks; grant `execute` on them to a backend role in a migration of your own, never to `authenticated`. Set `rls.customRoleWrites.requires` to the permission your role editor asserts (or `'manageRoles'`) so the database refuses members who may not manage roles (`manage-roles`).
43
46
  - `--force` (or `rls.force: true`) only when the app connects as the table owner. Create views over RLS tables `with (security_invoker = true)` (PD022).
44
- - When read grants set `fields` and clients read tables directly, add `--fields views --revoke-columns`: clients read `<table>_visible`, whose restricted columns are null unless a grant covers them. PD030 names columns still readable.
47
+ - When read grants set `fields` and clients read tables directly, add `--fields views --revoke-columns`: clients read `<table>_visible`, whose restricted columns are null unless a grant covers them. PD030 names columns still readable. When a `security definer` RPC or other owner-rights SQL returns those rows, use `--fields mask` and select through `cross join lateral permdock.<table>_mask(t) m`, never a hand-written `case` per column.
45
48
  - `rls.suspension` drops suspended users' roles and memberships of suspended scope instances, live in both modes. `scopes.<scope>.keep: [permissions.org.restore]` lets members of a suspended instance still hold those permissions (restore, cancel deletion, export) in the helpers, `authorize()`, the token hook and in process, where the membership carries `keep`; `doctor` PD061 lists them.
46
49
  - When support or impersonation tokens (an `act` claim) reach the Data API, set `rls.readOnlyActors: true`: each table and write command the policies grant gets a restrictive `{table}_{op}_read_only_actors` policy that refuses the write unless `act.read_only` is `false`. Never hand-write that policy per table. With `--helpers-only`, every hand-written insert, update and delete policy calls `(select permdock.permdock_actor_may_write())`, and `rls verify --introspect` names the ones that do not.
47
50
  - To list who holds a permission in one organization (notification fan-out, an approver list), server SQL calls `permitted_<scope>_users('<permission key>', <id>)` in `database` mode, never a hand-written join of memberships and `role_permissions`. No client role may execute it; grant it to the backend's Postgres role.
48
51
  - When a backend queries Postgres for a verified API key, set `rls.apiKeys` and put the key in the `api_key` claim (`scopes` as permission keys, `tenant` and `roles` for a key that belongs to a tenant): the helpers cap every allow at the scopes and treat a tenant key without `sub` as a service principal of that tenant. A user key whose claim names a `tenant` is held to that tenant even with `rls.tenants: 'all'`, and `permdock_has` is false for it. Never write a `has_scope` check per policy.
52
+ - When OAuth clients, MCP clients or agents (a `client_id` or an agent `act` claim) reach the Data API, set `rls.tokenScopes: true`: the helpers and helper-free allows then pass a grant only when the token's `scope` claim reaches its permission, through the same map better-supabase's `delegation.scopes` reads. A policy delegation does not compile into SQL, so such a client needs permission scopes or `oauthScopes` in its token. Never hand-write a scope check per policy.
49
53
  - On Supabase every generated helper and policy reads the subject through `permdock_user_id()`: the `sub` in `request.jwt.claims`, and null for the empty `sub` of a tenant service key. It ignores the legacy `request.jwt.claim.sub` setting; SQL that impersonates a user sets `request.jwt.claims`. A hand-written policy such a token reaches calls `(select permdock.permdock_user_id())`, never `auth.uid()`, which fails the uuid cast.
50
54
  - `rls.assignments` puts a `permdock_assignment` trigger on each mapped membership table, the global-roles table `rls.roles` and each table in `tables`; `ownRole: 'refuse'` (or a map of tables) also refuses a client write to the caller's own row. `doctor` PD064 names a guarded table no migration puts the trigger on.
51
55
  - SQL that assigns roles calls `permdock_can_assign_any(role, tenant, scope, scope_id)`, which covers declared and custom roles; SQL acting later for a stored user (an invitation accept re-checking the inviter) calls `permdock_can_assign_any_for(user, ...)`. `permdock supabase inspect` lists both in the manifest's `rls.helpers`.
@@ -60,7 +60,7 @@ A policy declares its scopes in order (an organization, the customers inside it)
60
60
  3. **Load memberships.** Return memberships as `{ scope, id, within, roles, via, expiresAt }` from the subject resolver, or pass a `MembershipSource` (one, or an array) as `memberships` on `createPermDock`. A person's access ending is the membership's `expiresAt`.
61
61
  ✓ `permdock doctor` reports no PD025 for the `doctor.memberships` fixture.
62
62
 
63
- An organisation role that reaches only the principal's teams is an organisation role with `where: { team_id: { in: principal.claims.team_ids } }`, not a team role. On Supabase, write the claim with `supabase.hook.claims` ([team reach](https://permdock.com/docs/concepts/tenancy#organisation-roles-with-team-reach)).
63
+ An organisation role that reaches only the principal's teams is an organisation role with `where: { team_id: { in: claim("team_ids") } }` (`claim` from `permdock` is `principal.claims.team_ids`, typed under `noUncheckedIndexedAccess`), not a team role. On Supabase, write the claim with `supabase.hook.claims` ([team reach](https://permdock.com/docs/concepts/tenancy#organisation-roles-with-team-reach)).
64
64
 
65
65
  4. **Put ownership on the roles.** Set `min: 1` on the role that manages each scope, `assigns` for who hands out what, `for: ['staff']` on admin-like roles, and `meta.audience` for the surface. Every server action that changes a membership calls `decideRoleChange(permdock, ...)` with the target's current membership and the holder count before it writes. -> [references/ownership-and-custom-roles.md](references/ownership-and-custom-roles.md)
66
66
  ✓ PD026 is clean, and removing the last owner is denied with `last-holder`.
@@ -79,7 +79,7 @@ type CustomRole = {
79
79
 
80
80
  - The ceiling of a scope is the code allows of the declared roles marked `assignable` in that scope. A custom role never exceeds it, and hosted grants never widen it.
81
81
  - An own allow inherits the conditions of the ceiling grants for that permission. To keep a narrower condition ("own posts only"), include the declared role that has it.
82
- - A resource may declare `levels: { own: { ownerId: principal.id }, team: { teamId: { in: principal.teamIds } }, all: {} }`. A grant `{ permission: 'job.read', level: 'team' }` ANDs that condition into the ceiling grant. An undeclared level, or a level on a collection action, is dropped as `unknown-level` and removes the permission; a level on a deny is `condition-not-allowed`. `permdock.assignableLevels(leaf)` lists the levels the subject may hand out.
82
+ - A resource may declare `levels: { own: { ownerId: principal.id }, team: { teamId: { in: principal.teamIds } }, all: {} }`. A grant `{ permission: 'job.read', level: 'team' }` ANDs that condition into the ceiling grant. An undeclared level, or a level on a collection action, is dropped as `unknown-level` and removes the permission; a level on a deny is `condition-not-allowed`. `permdock.assignableLevels(leaf)` lists the levels the subject may hand out. Levels are unordered: holding `team` does not hand out `own`. Add `orderedLevels: true` to the resource when the levels nest from narrowest to widest in declaration order; a held level then also hands out every earlier one, and evaluation is unchanged.
83
83
  - An own deny removes the permission from this custom role only. Deny it in code when it must win across roles.
84
84
  - Pass a `CustomRoleSource` (`memoryCustomRoleSource(roles)` in tests), or a `CustomRoleSourceFactory` `(subject) => CustomRoleSource` when the source reads one principal's roles, as `customRoles` on `createPermDock` or any adapter. To add every role of a tenant on a role-management request, `await permdock.derive({ customRoles })` instead of building a second instance. Build it with `customRoleSource({ rolesFor })`, where `rolesFor(tenant)` reads every custom role of the tenant, not only the held ones: `assignableRoles` and `decideRoleChange` see only what the source returns. `{ read: 'held', policy }` skips the read on requests that never manage roles. Check it with `testCustomRoleSource(source, { every })`.
85
85
  - `permdock.assignableRoles({ tenant })` lists declared roles, then the tenant's custom roles whose every permission and level the subject may hand out at the custom role's scope. `decideRoleChange` assigns and revokes a custom role like a declared one, with no holder count; it inherits `for` and `exclusiveWith` from its included roles.
@@ -90,3 +90,4 @@ type CustomRole = {
90
90
  - A stored grant under a key renamed with `definePermissions(..., { renamed })` still resolves; `validateCustomRole` lists it in `renamed` as `{ from, to }`, and `permdock doctor` PD055 prints the `update` that rewrites it.
91
91
  - For generated RLS, add `--custom-roles` (or `rls.customRoles: true`) to `permdock rls generate`. Platform roles are rows with `scope = 'global'` and a null `tenant_id`, or the top-level `role_grants` claim in `jwt` mode. With levels, `custom_role_permissions` gains a `level` column and the claim writes `key@level`.
92
92
  - To adopt custom roles the app already stores in its own tables, set `rls.customRoles.from` and `rls.customRoleWrites.roles`, then run `permdock rls generate --backfill-out <migrations dir>/`. The migration saves each role through the trusted replace function, drops entries outside the ceiling with a warning, and skips roles that already have rows; never write the copy by hand ([backfill](https://permdock.com/docs/cli/rls#backfill-from-existing-tables)).
93
+ - To keep the copy current instead of copying once, set `rls.customRoles.from.sync: 'trigger'`: after-row triggers on the app's permission and include tables rewrite the copy through the trusted replace function. Before-row triggers hold a signed-in write to the checks of `permdock_replace_custom_role_grants` against the copy as it stood before the statement, so a member who manages roles cannot add a permission they lack, even to a role they hold. A write path of your own calls the stable `permdock_custom_role_guard(p_tenant, p_scope, p_scope_id, p_role)` first, then `permdock_custom_role_beyond(p_tenant, p_scope, p_scope_id, p_allow, p_deny, p_include)` on the new entries only, from a `security definer` trigger that runs before the copy changes. The generated name guard on `rls.customRoleWrites.roles` refuses a tenant row named like a declared role (`declared-role`) or a shared role (`name-taken`). A stored entry is `<permission>[@<level>]`; check one with `parseCustomRoleEntry(policy, entry, { scope })` in process or `permdock_custom_role_entry_problem(p_scope, p_entry)` in SQL, which answer `unknown-permission`, `outside-ceiling` or `unknown-level`.
@@ -17,9 +17,7 @@ export const {
17
17
  requirePermission,
18
18
  PermDockProvider,
19
19
  permdockHandler,
20
- } = createPermDock(policy, {
21
- subject: async () => getUser(),
22
- });
20
+ } = createPermDock(policy, { subject: async () => getUser() });
23
21
  ```
24
22
 
25
23
  Needs Next.js 16.3 or later. Guard with `getPermission(permissions.post.update, post)` or `assert`. For a page or Server Action that must stop on a denial, `await requirePermission(permission, data, { tenant })` calls `forbidden()` (or `unauthorized()` for a signed-out user); enable `experimental.authInterrupts` and add `app/forbidden.tsx` and `app/unauthorized.tsx`. When only part of a page is gated, let a Server Component `assert` inside `<PermissionBoundary denied={...} approval={...}>` from `permdock/next/client` (with a `Suspense` inside it); `usePermissionBoundary()` in the fallback gives the permission, the approval `token` and `retry()`. Client components use `permdock/react` inside the server `PermDockProvider`, which never awaits: it streams a `snapshotPromise`, and permission hooks answer `pending` until it lands (add `suspend` to make them suspend).
@@ -104,9 +102,7 @@ import { createPermDock } from "permdock/authzen";
104
102
 
105
103
  export const { permdockHandler } = createPermDock(policy, {
106
104
  subject: fromBearer,
107
- resources: {
108
- post: { data: (id) => loadPost(id), list: () => listPosts() },
109
- },
105
+ resources: { post: { data: (id) => loadPost(id), list: () => listPosts() } },
110
106
  });
111
107
  ```
112
108
 
@@ -137,7 +133,7 @@ import { PermDockProvider, usePermission } from "permdock/react-native";
137
133
 
138
134
  `subjectId` is required: the signed-in user's id, or `null` when signed out. A stored snapshot of another principal is cleared. `storage` is `{ getItem, setItem, removeItem }`; `secureStoreStorage(SecureStore)` and `mmkvStorage(mmkv)` wrap Expo SecureStore and MMKV. `snapshotUrl` revalidates in the background; `subscribeForeground={appStateForeground(AppState)}` revalidates on return to the foreground. For Expo Router guards use `usePermissionGuard(permission, data?)`, which is `false` while `pending`, and `useSnapshotReady()` for a splash screen. `permdock.clear()` drops the persisted snapshot on sign-out. Do not import `policy.ts` on the client.
139
135
 
140
- To answer guards from synced rows, write `localSnapshotManifest(policy)` (from `permdock`) to a JSON file at build time and pass `source={localSnapshot({ manifest, read, subscribe })}`. `read()` returns `{ principal: { id, tenant, roles, memberships, attributes }, customRoles }` from the local database; `subscribe` re-reads on row changes. It decides like the server snapshot except relation grantees (server-only) and the assignable lists (empty). Never import `policy.ts` into the app for this. Docs: [local snapshot](https://permdock.com/docs/adapters/react-native#local-snapshot).
136
+ To answer guards from synced rows, write `localSnapshotManifest(policy)` (from `permdock`) to a JSON file at build time and pass `source={localSnapshot({ manifest, read, subscribe })}`. `read()` returns `{ principal: { id, tenant, roles, memberships, attributes }, customRoles }` from the local database; `subscribe` re-reads on row changes. It decides like the server snapshot except relation grantees (server-only). With a `tenant`, the assignable roles, permissions and levels match the server's for the same rows. Never import `policy.ts` into the app for this. Docs: [local snapshot](https://permdock.com/docs/adapters/react-native#local-snapshot).
141
137
 
142
138
  For an offline app on PowerSync, set `powersync: { out: 'sync-config.yaml' }` in `permdock.config.ts` and run `permdock powersync generate`: one edition 3 Sync Stream per resource, from the `rls.tables` and `rls.memberships` the RLS generator reads. It under-syncs, never over-syncs: a resource with a deny grant gets no stream, and a grant with a global role, approval, break-glass, validity, request context or a condition without a Sync Streams form syncs nothing, each with a warning. The app reads what does not sync from the server. It also writes `permdock_*` streams for the user's own membership, global-role and custom-role rows; with `powersync.manifest` set it writes the manifest too, and `powerSyncSource(db, { manifest, queries, read })` from `permdock/react-native` builds the local snapshot from `db.watch` queries. `permdock powersync verify` fails when a stream holds a fixture row the policy denies; doctor PD058 flags a stale file or manifest. Doctor PD065 lists server-only grants read from React Native code, which the device denies offline. Docs: [`permdock powersync`](https://permdock.com/docs/cli/powersync).
143
139
 
@@ -193,11 +189,13 @@ export const { middleware, protect } = createPermDock(policy, {
193
189
  subject: ({ store }) => store.user ?? null,
194
190
  });
195
191
 
196
- const app = new Elysia().use(middleware()).delete("/posts/:id", handler, {
197
- beforeHandle: protect(permissions.post.delete, ({ params }) =>
198
- loadPost(params.id),
199
- ),
200
- });
192
+ const app = new Elysia()
193
+ .use(middleware())
194
+ .delete("/posts/:id", handler, {
195
+ beforeHandle: protect(permissions.post.delete, ({ params }) =>
196
+ loadPost(params.id),
197
+ ),
198
+ });
201
199
  ```
202
200
 
203
201
  Fetch-native plugin via `derive`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. For `.ws` routes, `connection(ws, { permission })` in `open` returns the socket's connection (memoised); it closes the socket with `1008` when revoked.
@@ -278,9 +276,7 @@ import { createPermDock } from "permdock/node";
278
276
 
279
277
  export const { getPermDock, protect, send, permdockHandler } = createPermDock(
280
278
  policy,
281
- {
282
- subject: (req) => userFromCookie(req.headers.cookie),
283
- },
279
+ { subject: (req) => userFromCookie(req.headers.cookie) },
284
280
  );
285
281
  ```
286
282
 
@@ -360,9 +356,7 @@ import { PermDockProvider, Protected, usePermission } from 'permdock/solid';
360
356
  ```ts
361
357
  import { instrument } from "permdock/otel";
362
358
 
363
- instrument(permdock, {
364
- logger: { info: console.info, warn: console.warn },
365
- });
359
+ instrument(permdock, { logger: { info: console.info, warn: console.warn } });
366
360
  ```
367
361
 
368
362
  HTTP adapters accept the same options as `otel`. `@opentelemetry/api` is optional; without it the adapter writes only through `logger`.
@@ -397,6 +391,20 @@ export const scim = scimHandler({
397
391
 
398
392
  The handler writes users and groups. It never decides. Unknown or non-assignable role names are stored and dropped when memberships are read. Pass the app's `revocations` feed so a deprovisioned member's open streams revalidate and close.
399
393
 
394
+ In production, keep the directory in Postgres: `permdock scim generate` writes the tenant-keyed tables (`supabase/schemas/permdock/scim.sql` by default; `--dialect`, `--schema`), and `postgresDirectoryStore(query)` reads and writes them through the same `SqlQuery` function `tableMembershipSource` takes:
395
+
396
+ ```ts
397
+ import { postgresDirectoryStore } from "permdock/scim";
398
+ import pg from "pg";
399
+
400
+ const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
401
+ const directory = postgresDirectoryStore((text, values) =>
402
+ pool.query(text, [...values]),
403
+ );
404
+ ```
405
+
406
+ Read memberships through `directoryMembershipSource(directory)`, never through a `tableMembershipSource` over the SCIM tables: the source applies the `externalId` / `userName` match, drops inactive users and enforces `assignable`. The tables are revoked from `anon` and `authenticated`; the store's connection must own them or be granted on them, never `service_role` in generated SQL.
407
+
400
408
  ## Cloud — `permdock/cloud`
401
409
 
402
410
  No factory for a `PermDock`. `cloud({ url, key, environment })` returns `approvals`, `sink` and `policies` to pass into any adapter, `snapshotsFor(token)` (a `SnapshotSource` for the subject an `at+jwt` proves, sent as `PermDock-Subject`), `catalog.push`, plus the environment `issuer` (`<url>/v1/environments/<environment>`) and its `jwks` URL; `cloudEndpoints({ url, environment })` computes the same two without a key. It never implements `MembershipSource` or `CustomRoleSource` and never decides.
@@ -493,7 +501,7 @@ const subject = subjectFromSupabase(claims, {
493
501
 
494
502
  Write the claims with `permdock supabase hook generate`; `permdock supabase inspect --out` writes the manifest (helper names, tenant claim, budget, claims written) that `authorizationProvider` from `permdock/better-supabase` turns into better-supabase's `authorization` config. No `@supabase/supabase-js` peer. Pair with `permdock rls generate`, or keep SQL as authority and run `permdock rls verify --db-url-stdin` against `sqlFunction` twins ([database-first](https://permdock.com/docs/adapters/rls)); the generic RLS workflow is in the `permdock-data` skill.
495
503
 
496
- With better-supabase, set `authorization: authorizationProvider({ manifest, catalog })` in `better-supabase.config.ts` (with `rls.customRoles` its `canAssign` and `canAssignFor` call `permdock_can_assign_any` and its `_for` form, so custom roles are assignable through better-supabase; regenerate the manifest after turning custom roles on), pass `authorizer({ policy })` to the server (`createNext`, `createHono`, `createMcp`) so every `permission` a resource, action, route guard or tool declares is decided by PermDock from the verified claims (`operations` maps tables for `permissions: true`; a missing or anonymous claim denies), build bucket and topic `access` policies with `bucketPolicy` and `topicPolicy`, and map its session with `subjectFromBetterSupabase(session)` (all from `permdock/better-supabase`): the active tenant's `features` become `principal.plans`. Pass `plans: { keys }` when better-supabase's `entitlements.claim.keys` shortens feature keys, and `apiKeys: { permissions, manifest }` so an `apiKey` session maps to the key's credential subject instead of the anonymous one. For `createMcp` tools, put the permission in `meta` and spread `toolPolicy({ permdock })` into the options: tools without one are hidden and refused, and approval-required calls are refused because `createMcp` collects no approvals. Wrap a `CredentialProvider` with `credentialGuard(provider, { permdock, use, revoke? })` so PermDock decides each token use. `authorizationProvider({ ..., approver })` lets holders of that permission approve AI tool calls, never their own. `approvers: { 'ai-tool': holder(p), 'support-session': ..., 'invoice.send': ... }` fills `isApprover` through `permdock_is_approver` (needs `rls.approvals`); only `user()`, `holder()` and `anyOf()` approvers, and a kind it does not name has no approver. `accessReviewApplier({ client, reviews, policy, demoteTo?, email?, permdockFor? })` applies a review's revoked items: `update_member_role` to the `demoteTo` role when that role lacks the permission, else `remove_member`, plus `revoke_invitation` for the member's matching invitations, then `markApplied`; pass the reviewer's client so the blocks' own checks run. better-supabase's `createAgentRuntime({ toolApproval })` takes `toolApproval` from `permdock/ai-sdk` as it is. Validate its claims with `betterSupabase.claims(supabaseClaims().extend(appSchema))` (`supabaseClaims` from `permdock/supabase`, any Standard Schema as `appSchema`) instead of a hand-written copy of the claim shape. A `supabase.hook.claims` function that needs the user's organizations calls `<schema>.member_organization_ids_for(user_id)` (generated by `rls generate`, executable by `supabase_auth_admin` only) instead of querying the membership tables itself. A token Supabase's OAuth server issued to a third-party app (`client_id`) becomes an `oauth-client` actor limited to its `scope`; `deny(permission, { to: actor('oauth-client') })` keeps such apps out, in the app and, through `rls generate`, in Postgres. A better-supabase support session (`act.kind: 'support'`) is actor kind `support` with `sessionId` and `readOnly`, and `actingAs` (`act.kind: 'impersonation'`) is kind `impersonation`; neither has a delegation, so they reach nothing until a policy delegation names `actor('support')` or `actor('impersonation')`, and `readOnly: true` (also when `read_only` is absent) narrows that to read-only permissions. Generated RLS reads memberships, not `act`, so a support token sent to the Data API writes as the user: add a restrictive `for update` (and `insert` / `delete`) policy per writable table that requires `act ->> 'read_only' = 'false'` when `act` is a support or session level ([support access](https://permdock.com/docs/guides/support-access)). With `rls.anonymousSignIns: 'deny'`, pass `anonymousSignIns: 'deny'` to the subject mapper too (doctor PD057). Check a deployed database against the generated policies with `permdock rls verify --introspect`.
504
+ With better-supabase, set `authorization: authorizationProvider({ manifest, catalog })` in `better-supabase.config.ts` (with `rls.customRoles` its `canAssign` and `canAssignFor` call `permdock_can_assign_any` and its `_for` form, so custom roles are assignable through better-supabase; regenerate the manifest after turning custom roles on), pass `authorizer({ policy })` to the server (`createNext`, `createHono`, `createMcp`) so every `permission` a resource, action, route guard or tool declares is decided by PermDock from the verified claims (`operations` maps tables for `permissions: true`; a missing or anonymous claim denies), build bucket and topic `access` policies with `bucketPolicy` and `topicPolicy`, and map its session with `subjectFromBetterSupabase(session)` (all from `permdock/better-supabase`): the active tenant's `features` become `principal.plans`. Pass `plans: { keys }` when better-supabase's `entitlements.claim.keys` shortens feature keys, and `apiKeys: { permissions, manifest }` so an `apiKey` session maps to the key's credential subject instead of the anonymous one. For `createMcp` tools, put the permission in `meta` and spread `toolPolicy({ permdock })` into the options: tools without one are hidden and refused, and approval-required calls are refused because `createMcp` collects no approvals. Wrap a `CredentialProvider` with `credentialGuard(provider, { permdock, use, revoke? })` so PermDock decides each token use. `authorizationProvider({ ..., approver })` lets holders of that permission approve AI tool calls, never their own. `approvers: { 'ai-tool': holder(p), 'support-session': ..., 'invoice.send': ... }` fills `isApprover` through `permdock_is_approver` (needs `rls.approvals`); only `user()`, `holder()` and `anyOf()` approvers, and a kind it does not name has no approver. `accessReviewApplier({ client, reviews, policy, demoteTo?, email?, permdockFor? })` applies a review's revoked items: `update_member_role` to the `demoteTo` role when that role lacks the permission, else `remove_member`, plus `revoke_invitation` for the member's matching invitations, then `markApplied`; pass the reviewer's client so the blocks' own checks run. better-supabase's `createAgentRuntime({ toolApproval })` takes `toolApproval` from `permdock/ai-sdk` as it is. Validate its claims with `betterSupabase.claims(supabaseClaims().extend(appSchema))` (`supabaseClaims` from `permdock/supabase`, any Standard Schema as `appSchema`) instead of a hand-written copy of the claim shape. A `supabase.hook.claims` function that needs the user's organizations calls `<schema>.member_organization_ids_for(user_id)` (generated by `rls generate`, executable by `supabase_auth_admin` only) instead of querying the membership tables itself. With a scope named `team` or `teams`, `authorizationProvider` fills better-supabase's `teamIdsOf` with `<schema>.member_team_ids_of(user)`, which answers only for the caller, so with `blocks.access.model: 'provider'` better-supabase's `team_ids_of()` and team-sharing policies read PermDock's memberships. A token Supabase's OAuth server issued to a third-party app (`client_id`) becomes an `oauth-client` actor limited to its `scope`; `deny(permission, { to: actor('oauth-client') })` keeps such apps out, in the app and, through `rls generate`, in Postgres. A better-supabase support session (`act.kind: 'support'`) is actor kind `support` with `sessionId` and `readOnly`, and `actingAs` (`act.kind: 'impersonation'`) is kind `impersonation`; neither has a delegation, so they reach nothing until a policy delegation names `actor('support')` or `actor('impersonation')`, and `readOnly: true` (also when `read_only` is absent) narrows that to read-only permissions. Generated RLS reads memberships, not `act`, so a support token sent to the Data API writes as the user: add a restrictive `for update` (and `insert` / `delete`) policy per writable table that requires `act ->> 'read_only' = 'false'` when `act` is a support or session level ([support access](https://permdock.com/docs/guides/support-access)). With `rls.anonymousSignIns: 'deny'`, pass `anonymousSignIns: 'deny'` to the subject mapper too (doctor PD057). Check a deployed database against the generated policies with `permdock rls verify --introspect`.
497
505
 
498
506
  The other better-supabase slots, all from `permdock/better-supabase` ([better-supabase](https://permdock.com/docs/adapters/better-supabase)): `hookCan(permdock)` and `handlerCan({ getPermDock })` are the `can` options of `authorizeHook` and `protectWebHandler` in `better-supabase/workflow-sdk`; they decide with no row, so a permission with a row condition denies there and is checked inside the handler, and `handlerCan` answers `undefined` (401) without a signed-in principal. `supportPolicy({ policy, options?, writable?, approval?, approved? })` is the `authorize` option of `supportSessions`; it decides each start with `decideSupportSession` as the admin, and an `approval-required` start passes only once `approved(input, pending)` returns `{ approvedBy }` for an approver other than the admin. `subscriberPolicy({ policy, principals, events, row?, sender? })` filters outbox, webhook or realtime subscribers to those who may read the event (exact type, then the longest `prefix.*`, then `*`; a type with no entry reaches no one). `principalSource({ claims, identities? })` is a `PrincipalSource` over the claims better-supabase mints, for `decideFor`, `subscribersFor` and delivery; only links the app created through a signed-in flow or a verified address go in `identities`. `authorizationHooks({ policy, claims, service?, principals?, events?, approvers?, support?, tickets? })` is the `authorization` option of `createServer`: it answers automation publish and run-as, delegated OAuth scopes, `approverOf`, identities, delivery audiences, support sessions and run tickets from the claims better-supabase mints now, never from the request.
499
507
 
@@ -526,9 +534,7 @@ import { createPermDock } from "permdock/supabase/middleware";
526
534
  const { withPermDock, permdockHandler } = createPermDock(policy, {
527
535
  subject: (ctx) =>
528
536
  subjectFromSupabase(ctx.jwtClaims, {
529
- claims: {
530
- roles: "user_role",
531
- },
537
+ claims: { roles: "user_role" },
532
538
  declared: ["member", "admin"],
533
539
  }),
534
540
  });
@@ -36,6 +36,7 @@ Adopting PermDock in an app with its own keys, SQL helpers and tokens is a step-
36
36
  - Put application-owned metadata in `meta.x`; PermDock carries it and never reads it (see App data below).
37
37
  - Rename a key later with `definePermissions(tree, { renamed: { "old.key": "new.key" } })`. Remove the alias only when `permdock doctor` PD055 and PD056 are quiet; `permdock diff` reports the removal as the breaking `alias-removed`.
38
38
  - Generate helpers beside legacy SQL with `permdock rls generate --helpers-only --shims`, then rewrite policies with `permdock rls migrate`.
39
+ - `permdock rls migrate` and `rls import` need `pgsql-parser` 18 or later; the CLI names the installed version when it is older. A hook that should carry team ids sets `supabase.hook.teams: { table, user, team, tenant? }` and writes the `team_ids` claim within the same size budget as `memberships`.
39
40
 
40
41
  ## App data and app obligations
41
42
 
@@ -1 +0,0 @@
1
- export { t as catalogFile, n as runCatalog } from "./catalog-HD2gf5mY.js";
@@ -1 +0,0 @@
1
- export { r as runCollect } from "./collect-mLTY4nwb.js";
@@ -1,162 +0,0 @@
1
- import { r as isRecord } from "./compact-CAf557ZB.js";
2
- import { n as CliError } from "./errors-CWTY6uih.js";
3
- import { existsSync } from "node:fs";
4
- import { relative, resolve } from "node:path";
5
- //#region src/cli/project/config-schema.ts
6
- const SECTIONS = {
7
- collect: {
8
- srcPath: true,
9
- out: true,
10
- barrel: true
11
- },
12
- catalog: { out: true },
13
- openapi: { doc: true },
14
- rls: {
15
- tables: true,
16
- dialect: true,
17
- memberships: true,
18
- membershipSources: true,
19
- suspension: true,
20
- functions: true,
21
- inlineFunctions: true,
22
- force: true,
23
- fixtures: true,
24
- tenantClaim: true,
25
- tenantType: true,
26
- teamType: true,
27
- scopeTypes: true,
28
- roleClaim: true,
29
- roleValues: true,
30
- gucPrefix: true,
31
- out: true,
32
- drizzle: true,
33
- prisma: true,
34
- schema: true,
35
- authorize: true,
36
- policyPerRole: true,
37
- customRoles: true,
38
- triggers: true,
39
- audit: true,
40
- capabilities: true,
41
- anonymousSignIns: true,
42
- fields: true,
43
- revokeColumns: true,
44
- policyName: true,
45
- rbac: true,
46
- roles: true,
47
- actions: true,
48
- helpersOnly: true,
49
- rowHelpers: true,
50
- treeValues: true,
51
- trustedReaders: true,
52
- migrate: true,
53
- shims: true,
54
- tenants: true,
55
- customRoleWrites: true,
56
- anonExecute: true,
57
- assignments: true,
58
- approvals: true,
59
- jsonSchema: true,
60
- ownershipTriggers: true,
61
- readOnlyActors: true,
62
- apiKeys: true,
63
- realtime: true,
64
- storage: true
65
- },
66
- supabase: {
67
- hook: true,
68
- schemas: true
69
- },
70
- powersync: {
71
- out: true,
72
- resources: true,
73
- action: true,
74
- manifest: true
75
- },
76
- doctor: {
77
- sensitiveActions: true,
78
- memberships: true,
79
- credentials: true,
80
- clientEntries: true,
81
- migrations: true,
82
- claims: true,
83
- srcPath: true
84
- }
85
- };
86
- const TOP_LEVEL = /* @__PURE__ */ new Set([
87
- "permissions",
88
- "policy",
89
- "operations",
90
- ...Object.keys(SECTIONS)
91
- ]);
92
- function isSection(key) {
93
- return Object.hasOwn(SECTIONS, key);
94
- }
95
- /**
96
- * Checks the shape a command relies on before it reads the config: module
97
- * paths are strings and each section is an object. A key PermDock does not
98
- * read is a warning, so a config written for a newer version still loads.
99
- */
100
- function parseConfig(value, file) {
101
- if (value === void 0) return {
102
- config: {},
103
- warnings: []
104
- };
105
- if (!isRecord(value)) throw new CliError("usage", `PermDock CLI: ${file} must export a config object as default (use defineConfig)`);
106
- const warnings = [];
107
- const unknown = (path, known) => `${file}: unknown key ${path}; PermDock reads ${[...known].join(", ")}`;
108
- for (const [key, entry] of Object.entries(value)) {
109
- if (key === "permissions" || key === "policy" || key === "operations") {
110
- if (entry !== void 0 && typeof entry !== "string") throw new CliError("usage", `PermDock CLI: ${file}: ${key} must be a module path string`);
111
- continue;
112
- }
113
- if (!isSection(key)) {
114
- warnings.push(unknown(key, TOP_LEVEL));
115
- continue;
116
- }
117
- if (entry === void 0) continue;
118
- if (!isRecord(entry)) throw new CliError("usage", `PermDock CLI: ${file}: ${key} must be an object`);
119
- const known = SECTIONS[key];
120
- for (const child of Object.keys(entry)) if (!Object.hasOwn(known, child)) warnings.push(unknown(`${key}.${child}`, Object.keys(known)));
121
- }
122
- return {
123
- config: value,
124
- warnings
125
- };
126
- }
127
- //#endregion
128
- //#region src/cli/project/config.ts
129
- const CONFIG_FILES = [
130
- "permdock.config.ts",
131
- "permdock.config.mts",
132
- "permdock.config.js",
133
- "permdock.config.mjs"
134
- ];
135
- function defineConfig(config) {
136
- return config;
137
- }
138
- function isConfigFile(name) {
139
- return CONFIG_FILES.some((file) => file === name);
140
- }
141
- async function readConfig(cwd, fromFlag, options) {
142
- const path = fromFlag ? resolve(cwd, fromFlag) : CONFIG_FILES.map((name) => resolve(cwd, name)).find((file) => existsSync(file));
143
- if (path === void 0) return {
144
- config: {},
145
- warnings: [],
146
- file: void 0
147
- };
148
- if (!existsSync(path)) throw new CliError("usage", `PermDock CLI: config file not found: ${path}`, { code: "config_not_found" });
149
- const { loadModule, pickNamed } = await import("./load-BxEtwlBS.js");
150
- return {
151
- ...parseConfig(pickNamed(await loadModule(path, options), ["default"]), relative(cwd, path).replaceAll("\\", "/")),
152
- file: path
153
- };
154
- }
155
- async function loadConfig(cwd, fromFlag, options) {
156
- return (await readConfig(cwd, fromFlag, options)).config;
157
- }
158
- function resolveCwd(cwd, fallback) {
159
- return cwd === void 0 ? fallback : resolve(fallback, cwd);
160
- }
161
- //#endregion
162
- export { resolveCwd as a, readConfig as i, isConfigFile as n, loadConfig as r, defineConfig as t };
@@ -1,19 +0,0 @@
1
- import { Nn as Decision } from "./policy-types-n0uRTsW8.js";
2
- import { w as Permission } from "./vocabulary-CN8H_9UF.js";
3
- //#region src/agent/denied-text.d.ts
4
- /** What an agent adapter's `deniedText` receives: the refusal and the text PermDock wrote for it. */
5
- type DeniedTextEvent = {
6
- readonly decision: Exclude<Decision, {
7
- readonly outcome: "granted";
8
- }> | null;
9
- readonly permission: Permission | undefined;
10
- readonly text: string;
11
- };
12
- /**
13
- * Replaces the human-readable text of a refusal, and nothing else: the
14
- * outcome, `isError`, the structured content and the approval token stay.
15
- * `undefined` keeps PermDock's text.
16
- */
17
- type DeniedText = (event: DeniedTextEvent) => string | undefined;
18
- //#endregion
19
- export { DeniedTextEvent as n, DeniedText as t };
@@ -1,203 +0,0 @@
1
- import { i as configError } from "./errors-CWTY6uih.js";
2
- import { s as jsonSchemaOf } from "./catalog-doc-Cdn7-0pQ.js";
3
- import { F as quoteQualified, N as quoteIdent, P as quoteLiteral, j as helperSchema, s as signedIn } from "./shared-BAK8t4Uo.js";
4
- import { c as branchClauses, i as orSql, s as andSql, u as wrapSql } from "./policies-DP2nqRoF.js";
5
- //#region src/cli/rls/fields.ts
6
- /** Names of the field-view objects. Part of the SQL contract. */
7
- const FIELD_VIEWS = {
8
- /** `<table>_visible`: the `security_invoker` view clients read. */
9
- view: "_visible",
10
- /** `<table>_visible_fields`: the owner-rights companion `--revoke-columns` adds. */
11
- companion: "_visible_fields",
12
- /** The companion's row-key column, joined to the base table's key. */
13
- key: "permdock_key",
14
- /** Marks the companion so doctor PD022 knows it reads as its owner on purpose. */
15
- comment: "permdock:field-companion"
16
- };
17
- function covers(branch, column) {
18
- return branch.fields === void 0 || branch.fields.includes(column);
19
- }
20
- /**
21
- * A read grant that only shapes columns: a field-limited deny (it never
22
- * denies the row) or an allow with an empty list (it grants nothing). With
23
- * field views it leaves the row policy and lives in the view's masks.
24
- */
25
- function fieldOnly(branch) {
26
- if (branch.command !== "select" || branch.fields === void 0) return false;
27
- return branch.effect === "deny" || branch.fields.length === 0;
28
- }
29
- /** Branches the row policies keep when field views compile. */
30
- function rowBranches(branches) {
31
- return branches.filter((branch) => !fieldOnly(branch));
32
- }
33
- /**
34
- * Whether the reader is signed in, for branches with no helper call: a grant
35
- * to any signed-in user relies on `to authenticated` in its policy, which a
36
- * view does not have.
37
- */
38
- function audience(ctx, branch) {
39
- if (branch.access !== void 0 || branch.roles.length !== 1) return;
40
- if (branch.roles[0] === "authenticated") return signedIn(ctx);
41
- return branch.roles[0] === "anon" ? `not (${signedIn(ctx)})` : void 0;
42
- }
43
- function clause(ctx, branch) {
44
- return andSql(audience(ctx, branch), branchClauses(branch).using) ?? "true";
45
- }
46
- /**
47
- * `can(permission, row, { field })` in SQL: an allow that covers the column
48
- * holds for the row and no covering deny does. Every access term is the
49
- * uncorrelated helper call the row policy uses, so it runs once per statement.
50
- */
51
- function maskSql(ctx, allows, denies, column) {
52
- const allowed = allows.filter((branch) => covers(branch, column)).map((branch) => clause(ctx, branch));
53
- if (allowed.length === 0) return "false";
54
- const denied = denies.filter((branch) => covers(branch, column)).map((branch) => clause(ctx, branch));
55
- const allow = orSql(allowed);
56
- if (denied.length === 0) return allow;
57
- const deny = orSql(denied);
58
- return allow === "true" ? `not ${wrapSql(deny)}` : `${wrapSql(allow)} and not ${wrapSql(deny)}`;
59
- }
60
- function schemaColumns(policy, resource) {
61
- const node = policy.resources.get(resource);
62
- const schema = node === void 0 ? null : jsonSchemaOf(node);
63
- const properties = schema !== null && typeof schema === "object" ? schema.properties : void 0;
64
- return properties !== null && typeof properties === "object" ? Object.keys(properties) : void 0;
65
- }
66
- function viewName(table, suffix) {
67
- return `${table}${suffix}`;
68
- }
69
- /**
70
- * `<helper schema>.<table>_visible_fields`: outside the Data API's schemas,
71
- * so its owner rights reach clients only through the `_visible` join.
72
- */
73
- function companionName(ctx, table) {
74
- const base = table.startsWith("public.") ? table.slice(7) : table.replaceAll(".", "_");
75
- return `${helperSchema(ctx)}.${viewName(base, FIELD_VIEWS.companion)}`;
76
- }
77
- /**
78
- * One view per table whose read grants limit fields. A column is restricted
79
- * when some read allow lists fields without it or some read deny lists it;
80
- * every other column, and the row key, passes through unchanged.
81
- */
82
- function fieldViews(policy, ctx, branches, options, warnings) {
83
- const byTable = /* @__PURE__ */ new Map();
84
- for (const branch of branches) {
85
- if (branch.command !== "select" || branch.coverage === true) continue;
86
- byTable.set(branch.table, [...byTable.get(branch.table) ?? [], branch]);
87
- }
88
- const views = [];
89
- for (const [table, reads] of byTable) {
90
- const allows = reads.filter((branch) => branch.effect === "allow" && branch.fields?.length !== 0);
91
- const denies = reads.filter((branch) => branch.effect === "deny");
92
- const limited = reads.some((branch) => branch.fields !== void 0);
93
- const resource = reads.find((branch) => branch.resource !== void 0)?.resource;
94
- if (!limited || allows.length === 0 || resource === void 0) continue;
95
- const names = schemaColumns(policy, resource);
96
- if (names === void 0) throw configError(`PermDock CLI: --fields views needs the ${resource} schema's JSON Schema (Standard JSON Schema) to list the columns of ${table}`);
97
- const key = policy.resources.get(resource)?.id ?? "id";
98
- const restricted = (column) => column !== key && (allows.some((branch) => branch.fields !== void 0 && !branch.fields.includes(column)) || denies.some((branch) => branch.fields?.includes(column) === true));
99
- const columns = names.map((name) => {
100
- quoteIdent(name);
101
- return restricted(name) ? {
102
- name,
103
- mask: maskSql(ctx, allows, denies, name)
104
- } : { name };
105
- });
106
- if (columns.every((column) => column.mask === void 0)) continue;
107
- const view = viewName(table, FIELD_VIEWS.view);
108
- if (allows.some((branch) => branch.fields !== void 0 && !branch.fields.includes(key))) warnings.push(`field view ${view}: the key ${key} passes through, although a read grant on ${resource} omits it`);
109
- const hidden = columns.filter((column) => column.mask !== void 0).map((column) => column.name);
110
- if (options.helpersOnly === true) warnings.push(`field view ${view}: ${table} still returns ${hidden.join(", ")} to direct reads; revoke select on those columns from client roles in your own grants, or read them through a function`);
111
- else if (!options.revokeColumns) warnings.push(`field view ${view}: ${table} still returns ${hidden.join(", ")} to direct reads; add --revoke-columns so clients read through the view`);
112
- views.push({
113
- resource,
114
- table,
115
- view,
116
- key,
117
- columns,
118
- roles: reads.some((branch) => branch.roles.includes("anon")) ? ["anon", "authenticated"] : ["authenticated"],
119
- ...options.revokeColumns ? { companion: companionName(ctx, table) } : {}
120
- });
121
- }
122
- return views;
123
- }
124
- /** Columns of `view`'s table that `anon` and `authenticated` keep on the base table with `--revoke-columns`. */
125
- function readableColumns(view) {
126
- return view.columns.filter((column) => column.mask === void 0).map((column) => column.name);
127
- }
128
- function restrictedColumns(view) {
129
- return view.columns.filter((column) => column.mask !== void 0).map((column) => column.name);
130
- }
131
- function columnList(names) {
132
- return names.map(quoteIdent).join(", ");
133
- }
134
- function selectList(lines) {
135
- return lines.map((line) => ` ${line}`).join(",\n");
136
- }
137
- function grantView(name, roles) {
138
- return [`revoke all on table ${quoteQualified(name)} from anon, authenticated, public;`, `grant select on table ${quoteQualified(name)} to ${roles.join(", ")};`].join("\n");
139
- }
140
- function inlineViewSql(view) {
141
- const targets = view.columns.map((column) => column.mask === void 0 ? quoteIdent(column.name) : `case when ${column.mask} then ${quoteIdent(column.name)} end as ${quoteIdent(column.name)}`);
142
- return `create or replace view ${quoteQualified(view.view)} with (security_invoker = true) as
143
- select
144
- ${selectList(targets)}
145
- from ${quoteQualified(view.table)};`;
146
- }
147
- function companionSql(view, companion) {
148
- const restricted = view.columns.filter((column) => column.mask !== void 0);
149
- const targets = [`${quoteIdent(view.key)} as ${quoteIdent(FIELD_VIEWS.key)}`, ...restricted.map((column) => `case when ${column.mask} then ${quoteIdent(column.name)} end as ${quoteIdent(column.name)}`)];
150
- const any = orSql(restricted.map((column) => column.mask ?? "false"));
151
- const name = companion.slice(0, companion.indexOf("."));
152
- const schema = quoteIdent(name);
153
- return `${name === "public" ? "" : `create schema if not exists ${schema};
154
- revoke all on schema ${schema} from public;
155
- grant usage on schema ${schema} to ${view.roles.join(", ")};
156
- `}create or replace view ${quoteQualified(companion)} with (security_barrier = true) as
157
- select
158
- ${selectList(targets)}
159
- from ${quoteQualified(view.table)}
160
- where ${any};
161
- comment on view ${quoteQualified(companion)} is ${quoteLiteral(`${FIELD_VIEWS.comment} ${view.view}`)};`;
162
- }
163
- function joinedViewSql(view, companion) {
164
- const targets = view.columns.map((column) => column.mask === void 0 ? `t.${quoteIdent(column.name)}` : `f.${quoteIdent(column.name)}`);
165
- return `create or replace view ${quoteQualified(view.view)} with (security_invoker = true) as
166
- select
167
- ${selectList(targets)}
168
- from ${quoteQualified(view.table)} t
169
- left join ${quoteQualified(companion)} f on f.${quoteIdent(FIELD_VIEWS.key)} = t.${quoteIdent(view.key)};`;
170
- }
171
- /** `select` on only the unrestricted columns (and the key) of the base table, for `--revoke-columns`. */
172
- function columnGrantSql(view, role) {
173
- return `grant select (${columnList(readableColumns(view))}) on table ${quoteQualified(view.table)} to ${role};`;
174
- }
175
- function columnRevokeSql(view) {
176
- return `revoke select (${columnList(restrictedColumns(view))}) on table ${quoteQualified(view.table)} from anon, authenticated;`;
177
- }
178
- /**
179
- * The view SQL: the `security_invoker` view, and with `--revoke-columns` the
180
- * owner-rights companion it joins for the restricted columns. The companion
181
- * masks every column with the full field decision and keeps only rows where
182
- * one is readable, so reading it directly shows nothing `pick` would not.
183
- */
184
- function fieldViewsSql(views) {
185
- return views.map((view) => {
186
- const head = `-- field view over ${view.table}: ${restrictedColumns(view).join(", ")} read as null unless a read grant covers the column for the row`;
187
- if (view.companion === void 0) return [
188
- head,
189
- inlineViewSql(view),
190
- grantView(view.view, view.roles)
191
- ].join("\n");
192
- return [
193
- head,
194
- `-- ${view.companion} reads ${view.table} as its owner; ${view.view} reads the rows as the caller and joins it`,
195
- companionSql(view, view.companion),
196
- grantView(view.companion, view.roles),
197
- joinedViewSql(view, view.companion),
198
- grantView(view.view, view.roles)
199
- ].join("\n");
200
- }).join("\n\n");
201
- }
202
- //#endregion
203
- export { fieldViewsSql as a, fieldViews as i, columnGrantSql as n, rowBranches as o, columnRevokeSql as r, viewName as s, FIELD_VIEWS as t };