permdock 0.3.0 → 0.4.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 (323) hide show
  1. package/dist/a2a/index.d.ts +7 -5
  2. package/dist/a2a/index.js +14 -21
  3. package/dist/ai-sdk/index.d.ts +18 -6
  4. package/dist/ai-sdk/index.js +8 -5
  5. package/dist/app-data-CV8pqqMi.js +59 -0
  6. package/dist/approvals/index.d.ts +2 -2
  7. package/dist/approvals/index.js +3 -3
  8. package/dist/{approvals-DbYAuNkG.js → approvals-DZL76lKW.js} +35 -19
  9. package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
  10. package/dist/{arazzo-BAgw9ap6.js → arazzo-C4BcskbX.js} +6 -5
  11. package/dist/{arazzo-CKwwqFWz.js → arazzo-CD8d0WLb.js} +2 -2
  12. package/dist/authzen/index.d.ts +3 -3
  13. package/dist/authzen/index.js +161 -23
  14. package/dist/better-auth/index.d.ts +2 -2
  15. package/dist/better-auth/index.js +6 -19
  16. package/dist/better-supabase/index.d.ts +112 -87
  17. package/dist/better-supabase/index.js +257 -156
  18. package/dist/{bind-C_ob3LKa.d.ts → bind-BzqG8SMb.d.ts} +16 -5
  19. package/dist/{bind-CuzGytRf.js → bind-kk-uyCwH.js} +14 -6
  20. package/dist/build-instance-DIIV9DbC.js +51 -0
  21. package/dist/{canonical-json-DemiK4Vp.js → canonical-json-CFKyFOke.js} +1 -3
  22. package/dist/{capability-2kYTMWDv.d.ts → capability-CksyCWDH.d.ts} +2 -2
  23. package/dist/{capability-4qPdO9gk.js → capability-txATtKV4.js} +3 -2
  24. package/dist/catalog/index.d.ts +1 -1
  25. package/dist/catalog/index.js +1 -1
  26. package/dist/catalog-6yWAX6Ps.js +2 -0
  27. package/dist/{catalog-dFZdWCGN.js → catalog-WHG4kBq7.js} +2 -2
  28. package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-DWtg0gGR.js} +17 -10
  29. package/dist/{catalog-DWF5cYqB.js → catalog-tZUyOfOv.js} +4 -4
  30. package/dist/claude-agent/index.d.ts +4 -4
  31. package/dist/claude-agent/index.js +4 -5
  32. package/dist/clerk/index.d.ts +3 -3
  33. package/dist/clerk/index.js +8 -33
  34. package/dist/cli/bin.js +1 -1
  35. package/dist/cli/index.d.ts +3 -3
  36. package/dist/cli/index.js +3 -3
  37. package/dist/cloud/index.d.ts +4 -4
  38. package/dist/cloud/index.js +9 -15
  39. package/dist/{cloud-CB_ELYuX.js → cloud-C7bSDgX4.js} +2 -2
  40. package/dist/{cloud-DWLXO2af.js → cloud-PeBN3IEn.js} +5 -5
  41. package/dist/{collect-DSTE3ml9.js → collect-2SrB7lfk.js} +6 -8
  42. package/dist/collect-Bw2Jdq1q.js +2 -0
  43. package/dist/{collect-DClJsheJ.js → collect-CQx4hX4N.js} +3 -3
  44. package/dist/{commands-DSxCqG9A.js → commands-Cpip4ULx.js} +13 -13
  45. package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
  46. package/dist/compile/index.d.ts +1 -1
  47. package/dist/compile/index.js +1 -1
  48. package/dist/{compile-Ba4blROR.js → compile-CcEu2ZQ1.js} +6 -5
  49. package/dist/{compile-BkRyBDBw.d.ts → compile-CtTP7eur.d.ts} +2 -2
  50. package/dist/{config-BJiM-xAm.js → config-BIjREpyE.js} +4 -4
  51. package/dist/{config-Dnszbv-6.js → config-DLt6BvV7.js} +2 -2
  52. package/dist/{config-report-DMvWKUxL.js → config-report--Xb9APJS.js} +5 -6
  53. package/dist/{connection-CU8MQxRi.js → connection-DUsczpB5.js} +3 -2
  54. package/dist/constants-DcJABbLW.js +20 -0
  55. package/dist/{context-Bcy85rGi.js → context-DieZVJXu.js} +16 -5
  56. package/dist/convex/index.d.ts +4 -4
  57. package/dist/convex/index.js +14 -16
  58. package/dist/create-BBO24vj6.js +324 -0
  59. package/dist/{credential-8P81ff4u.js → credential-kue0qpbm.js} +4 -4
  60. package/dist/deciding-columns-Sq12mEA6.js +123 -0
  61. package/dist/delegation-input-FTxMK3ji.js +19 -0
  62. package/dist/denied-text-DfRk15uw.d.ts +19 -0
  63. package/dist/{describe-DMYiCjJV.d.ts → describe-C3ldA9os.d.ts} +11 -2
  64. package/dist/{describe-B_gVZEVi.js → describe-Cgkp7eP0.js} +5 -4
  65. package/dist/{diff-DBIhz_f2.js → diff-Cnghh_Ip.js} +2 -2
  66. package/dist/{diff-2VaeChzX.js → diff-rpqoe7Ag.js} +9 -9
  67. package/dist/{doctor-BwCAMLx_.js → doctor-CuVJvhV_.js} +2 -2
  68. package/dist/doctor-owlv6ddh.js +2166 -0
  69. package/dist/drizzle/index.d.ts +2 -2
  70. package/dist/drizzle/index.js +6 -14
  71. package/dist/elysia/index.d.ts +4 -4
  72. package/dist/elysia/index.js +7 -7
  73. package/dist/{errors-DPdqI50v.d.ts → errors-OD_4c__H.d.ts} +2 -2
  74. package/dist/{errors-Cz5zn0mW.js → errors-drnfxYtm.js} +3 -3
  75. package/dist/{evaluations-tz6D9A2G.js → evaluations-C9YJsv3H.js} +45 -7
  76. package/dist/eve/index.d.ts +5 -5
  77. package/dist/eve/index.js +5 -5
  78. package/dist/express/index.d.ts +4 -4
  79. package/dist/express/index.js +8 -7
  80. package/dist/fastify/index.d.ts +15 -5
  81. package/dist/fastify/index.js +9 -29
  82. package/dist/fields-fwfXN01n.js +1016 -0
  83. package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
  84. package/dist/{fixtures-ht28Cplr.js → fixtures-h2eG2OZV.js} +5 -6
  85. package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-7REQZe0H.js} +32 -22
  86. package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
  87. package/dist/grantee-CXY-0C87.js +254 -0
  88. package/dist/grantee-conditions-MQs5NIuX.js +270 -0
  89. package/dist/grantee-match-fxYp9Nmt.js +141 -0
  90. package/dist/{helpers-H_KARDVC.js → helpers-C9YmyBIa.js} +7 -6
  91. package/dist/hono/index.d.ts +4 -4
  92. package/dist/hono/index.js +8 -8
  93. package/dist/hosted-CBHgkzDl.js +655 -0
  94. package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
  95. package/dist/{http-BIhmLfCc.js → http-BEbdMZ05.js} +2 -24
  96. package/dist/{rls-import-BTFVk3jH.js → import-BFIlhetD.js} +86 -80
  97. package/dist/{openapi-import-cmwetbzw.js → import-UHk1N7vH.js} +6 -7
  98. package/dist/{index-C8z4RjQV.d.ts → index-BwMe94Xq.d.ts} +1 -1
  99. package/dist/{index-0nNqHspG.d.ts → index-DgM7w96K.d.ts} +2 -2
  100. package/dist/{index-C7Tw7MaW.d.ts → index-Dz7didpC.d.ts} +3 -3
  101. package/dist/{index-BNc7Wer_.d.ts → index-UdVQgq48.d.ts} +105 -38
  102. package/dist/index.d.ts +15 -13
  103. package/dist/index.js +22 -22
  104. package/dist/{instance-options-jARdwfPN.d.ts → instance-options-plkrsAog.d.ts} +1 -1
  105. package/dist/{json-schema-BsAflUGW.js → json-schema-DBkdUuCO.js} +1 -3
  106. package/dist/jwt/index.d.ts +4 -4
  107. package/dist/jwt/index.js +13 -13
  108. package/dist/{kernel-_RRHmnMY.js → kernel-CB3LrIJM.js} +81 -32
  109. package/dist/kysely/index.d.ts +2 -2
  110. package/dist/kysely/index.js +6 -14
  111. package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
  112. package/dist/load-B_shHrmd.js +2 -0
  113. package/dist/{load-DhWEB9P1.js → load-C3_oTHyO.js} +11 -3
  114. package/dist/{manifest-iaoBXHKc.js → manifest-BngWKryL.js} +5 -18
  115. package/dist/{map-claims-Z4QZOiFF.js → map-claims-AVlBaJoa.js} +7 -16
  116. package/dist/{map-error-pgUWNNtG.js → map-error-BE27PYkE.js} +5 -5
  117. package/dist/{markers-B0hYLvMD.js → markers-CiwAOZtO.js} +6 -2
  118. package/dist/{may-use-DndQ5tQ-.js → may-use-BOn_dR_w.js} +4 -3
  119. package/dist/mcp/index.d.ts +26 -9
  120. package/dist/mcp/index.js +456 -446
  121. package/dist/{rls-migrate-CN4TlJTO.js → migrate-BwlB5-Wk.js} +75 -15
  122. package/dist/nest/index.d.ts +4 -4
  123. package/dist/nest/index.js +11 -12
  124. package/dist/next/index.d.ts +31 -8
  125. package/dist/next/index.js +29 -33
  126. package/dist/next/plugin.d.ts +1 -1
  127. package/dist/next/plugin.js +1 -1
  128. package/dist/node/index.d.ts +4 -4
  129. package/dist/node/index.js +4 -4
  130. package/dist/ocsf-uYFXGVw2.js +115 -0
  131. package/dist/openai/index.d.ts +5 -5
  132. package/dist/openai/index.js +4 -4
  133. package/dist/openapi/index.d.ts +3 -3
  134. package/dist/openapi/index.js +1 -1
  135. package/dist/{openapi-CqqEbHsL.js → openapi-BJdBmYDG.js} +19 -23
  136. package/dist/{openapi-BqRBvAng.js → openapi-DBzHQbov.js} +2 -5
  137. package/dist/{openapi-l0xYHkcv.js → openapi-DcQ1Kg40.js} +3 -3
  138. package/dist/orpc/index.d.ts +5 -5
  139. package/dist/orpc/index.js +9 -11
  140. package/dist/otel/index.d.ts +2 -2
  141. package/dist/otel/index.js +26 -23
  142. package/dist/output-files-CgIsvdSa.js +140 -0
  143. package/dist/{rls-ownership-RuFqaqGz.js → ownership-BgnbOJFT.js} +387 -379
  144. package/dist/{package-root-zGkxO3oN.js → package-root-DtNnpmUd.js} +3 -2
  145. package/dist/{parse-BuyjxhZV.js → parse-CVFQHk6c.js} +3 -3
  146. package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-C-q02Pal.js} +2 -4
  147. package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DvYUR0QL.d.ts} +1 -1
  148. package/dist/pdp/index.d.ts +2 -2
  149. package/dist/pdp/index.js +8 -9
  150. package/dist/{permdock-CbgvLnuo.js → permdock-L26udHSk.js} +1387 -1220
  151. package/dist/{rls-fields-74oZPXWL.js → permission-keys-B26jeyUv.js} +1289 -2066
  152. package/dist/permission-registry-BsblpB-z.js +385 -0
  153. package/dist/{permissions-DttnqqxC.d.ts → permission-types-Dg39z9Kv.d.ts} +59 -31
  154. package/dist/{permissions-RJrovw98.js → permissions-CCJ6lmxB.js} +3 -3
  155. package/dist/{permissions-CDgvKG5L.d.ts → permissions-COH-KRgT.d.ts} +2 -2
  156. package/dist/{policy-IB6yxpC3.d.ts → policy-types-sp9P1iIV.d.ts} +661 -582
  157. package/dist/{policy-D2W7uv19.d.ts → policy-ur0BBK_e.d.ts} +2 -2
  158. package/dist/{powersync-DliZK_-y.js → powersync-BFSbSElQ.js} +2 -2
  159. package/dist/powersync-DCDgZgfZ.js +2 -0
  160. package/dist/{powersync-BylawD_U.js → powersync-cZe4kzhL.js} +26 -27
  161. package/dist/prisma/index.d.ts +1 -1
  162. package/dist/prisma/index.js +6 -12
  163. package/dist/{problem-B43ztKIm.js → problem-CnWraVhC.js} +8 -7
  164. package/dist/{problem-details-_AMOYz-w.js → problem-details-DECUnXAn.js} +2 -2
  165. package/dist/{problem-details-DX075l9V.d.ts → problem-details-rIIo8B2C.d.ts} +1 -1
  166. package/dist/{protected-BdnIEFkY.d.ts → protected-B3_P7xuw.d.ts} +9 -6
  167. package/dist/{protected-utU-SLuy.js → protected-BN7BUdY1.js} +20 -8
  168. package/dist/{provider-CSQd7m92.js → provider-Bi-0HNL3.js} +23 -10
  169. package/dist/{provider-DZY281jO.d.ts → provider-kwHC9CZD.d.ts} +1 -1
  170. package/dist/{provider-store-Cfbvo4DE.js → provider-store-Ctt-81Ni.js} +6 -3
  171. package/dist/react/index.d.ts +7 -7
  172. package/dist/react/index.js +5 -5
  173. package/dist/react/provider-client.d.ts +1 -1
  174. package/dist/react/provider-client.js +1 -1
  175. package/dist/react-native/index.d.ts +20 -11
  176. package/dist/react-native/index.js +33 -17
  177. package/dist/{refs-KYb4ntaz.js → refs-f3imMjNW.js} +1 -1
  178. package/dist/relation-walk-BXnlLtg2.js +186 -0
  179. package/dist/relations-CvmSpG67.js +163 -0
  180. package/dist/{replay-CYyiLiwO.d.ts → replay-COpmZUfx.d.ts} +1 -1
  181. package/dist/request-context-BVM7aWNs.js +71 -0
  182. package/dist/request-context-C2_1lchh.d.ts +5 -0
  183. package/dist/resource-relations-D1Un3pcz.js +243 -0
  184. package/dist/retire-BFcxL6un.js +240 -0
  185. package/dist/{rls-B4ur8nKQ.js → rls-55DQTuf5.js} +14 -2
  186. package/dist/{rls-DQtdgTHB.js → rls-BEktUaqg.js} +884 -484
  187. package/dist/{policy-xyWg7QdR.js → role-builder-B4GbKM-i.js} +235 -541
  188. package/dist/{rpc-DL9Sqvb1.js → rpc-nYC3cR_u.js} +15 -5
  189. package/dist/{run-DKDbM4Du.js → run-DMcNW_8T.js} +3 -3
  190. package/dist/{runtime-CB8gQFvS.js → runtime-D_tMXnsq.js} +1 -1
  191. package/dist/{saas-DhpJjnBA.js → saas-DtaA4JuW.js} +9 -8
  192. package/dist/{openapi-schema--ARjyfQr.js → schema-Bol6gqXA.js} +3 -5
  193. package/dist/{schema-D3MnDMxa.js → schema-DjQBcAP9.js} +4 -2
  194. package/dist/scim/index.d.ts +1 -1
  195. package/dist/scim/index.js +1 -1
  196. package/dist/{scim-BFGBBAqR.js → scim-vFR1O5oG.js} +6 -12
  197. package/dist/{scopes-CVdPrWN-.js → scopes-tvpa34ep.js} +10 -2
  198. package/dist/server/index.d.ts +18 -8
  199. package/dist/server/index.js +8 -8
  200. package/dist/{server-DlZ28Oz5.js → server-ZYEHtNHS.js} +4 -4
  201. package/dist/{create-B1d519XK.d.ts → server-types-DdqBwZN_.d.ts} +69 -33
  202. package/dist/shared-1uhI-e-b.js +294 -0
  203. package/dist/{shared-DRu8VFVM.js → shared-JhMeOsDF.js} +1 -1
  204. package/dist/{sink-Bd9PSyfq.js → sink-BTtmfPnE.js} +1 -1
  205. package/dist/{sink-B1fZzIMf.d.ts → sink-CPN4zLfs.d.ts} +1 -1
  206. package/dist/{skills-Biq5uup3.js → skills-BZdRQkxs.js} +4 -3
  207. package/dist/{skills-Bx-U0irG.js → skills-Dle4ZoLF.js} +2 -2
  208. package/dist/{snapshot-Enxd0n-s.js → snapshot-Dc9CHPME.js} +116 -13
  209. package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-Cod40D-X.js} +1 -1
  210. package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-J5T7t_vD.d.ts} +1 -1
  211. package/dist/solid/index.d.ts +22 -22
  212. package/dist/solid/index.js +40 -10
  213. package/dist/{sources-4p0-4xQm.js → sources-B1-b-R0t.js} +25 -26
  214. package/dist/{sources-BdOYLw9j.d.ts → sources-Dljy06zL.d.ts} +6 -2
  215. package/dist/{sql-xdg5f4Fi.js → sql-t8teWOfo.js} +19 -3
  216. package/dist/{src-BDwEZWrZ.js → src-Bb7no-hS.js} +194 -123
  217. package/dist/ssf/index.d.ts +3 -3
  218. package/dist/ssf/index.js +58 -61
  219. package/dist/store-9KO4BlkB.d.ts +147 -0
  220. package/dist/{store-DMAWwAJV.js → store-ILkLUj_o.js} +4 -4
  221. package/dist/{store-options-ByhZq1jV.js → store-options-CbUHDoSe.js} +16 -7
  222. package/dist/{stores-BT2pSCub.js → stores-BHXdSbw9.js} +7 -3
  223. package/dist/stores-Cxz_80g-.d.ts +31 -0
  224. package/dist/{stream-DDpvynSB.js → stream-BMui98KK.js} +2 -1
  225. package/dist/{stream-D1RXubw2.d.ts → stream-Dwsg6WwE.d.ts} +2 -2
  226. package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
  227. package/dist/supabase/index.d.ts +4 -4
  228. package/dist/supabase/index.js +3 -3
  229. package/dist/supabase/middleware.d.ts +11 -5
  230. package/dist/supabase/middleware.js +10 -7
  231. package/dist/{supabase-C0qESOa-.js → supabase-C1TWlnYt.js} +36 -52
  232. package/dist/{supabase-Boqb9a_Y.js → supabase-Dbqr141s.js} +2 -2
  233. package/dist/supabase-hook-BULg7Ut_.js +2 -0
  234. package/dist/{supabase-hook-CSLQ4zJ3.js → supabase-hook-Cva3ikXz.js} +731 -782
  235. package/dist/{supautils-Cn4R4cJ4.js → supautils-B6HYL9eL.js} +2 -2
  236. package/dist/svelte/Protected.svelte +9 -2
  237. package/dist/svelte/index.d.ts +6 -6
  238. package/dist/svelte/index.js +25 -11
  239. package/dist/svelte/runtime.d.ts +10 -3
  240. package/dist/svelte/runtime.js +4 -3
  241. package/dist/svelte/source.d.ts +6 -6
  242. package/dist/svelte/source.js +4 -4
  243. package/dist/{validity-uOp8UpYn.js → tenancy-DshjqY0n.js} +3 -39
  244. package/dist/terminal/index.d.ts +5 -5
  245. package/dist/terminal/index.js +18 -34
  246. package/dist/testing/index.d.ts +89 -77
  247. package/dist/testing/index.js +1276 -1262
  248. package/dist/testing/saas/index.d.ts +4 -4
  249. package/dist/testing/saas/index.js +2 -2
  250. package/dist/testing/saas/permissions.d.ts +1 -1
  251. package/dist/testing/saas/permissions.js +1 -1
  252. package/dist/thenable-QSmvt-vZ.js +65 -0
  253. package/dist/{token-CIWxJOCh.js → token-Bx9qy30p.js} +1 -1
  254. package/dist/tool-refusal-CQ7OEia9.js +41 -0
  255. package/dist/trpc/index.d.ts +5 -5
  256. package/dist/trpc/index.js +7 -8
  257. package/dist/{types-DbjRN55U.d.ts → types-7rJVLWe5.d.ts} +82 -4
  258. package/dist/{types-aqOHTHcp.d.ts → types-Br9IXUBG.d.ts} +1 -1
  259. package/dist/types-ByUo56un.d.ts +88 -0
  260. package/dist/{types-Cq_H8N_X.d.ts → types-C0dee0Qf.d.ts} +2 -2
  261. package/dist/{types-DxiCGmmd.d.ts → types-CIlTojhq.d.ts} +2 -2
  262. package/dist/{types-QCuYJh4q.d.ts → types-CUcFZfpU.d.ts} +1 -1
  263. package/dist/{types-C97IskBw.d.ts → types-CqrjB9o6.d.ts} +1 -1
  264. package/dist/{types-BnIEz1sU.d.ts → types-DHrmBJLH.d.ts} +24 -6
  265. package/dist/{types-Dnemwd77.d.ts → types-DpWfQdSh.d.ts} +5 -1
  266. package/dist/{types-Bvc4oWT6.d.ts → types-DvupZE0w.d.ts} +1 -1
  267. package/dist/unplugin/index.d.ts +1 -1
  268. package/dist/unplugin/index.js +1 -1
  269. package/dist/{usage-GTixvDDU.js → usage-4G6t643F.js} +2 -2
  270. package/dist/{usage-ItS7nLcl.js → usage-CG9nOYZw.js} +7 -7
  271. package/dist/usage-iFLJ0-rr.js +2 -0
  272. package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-BSSw0l_8.d.ts} +1 -1
  273. package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
  274. package/dist/validity-dVrUEPJt.js +62 -0
  275. package/dist/{verifier-BxBo6I44.js → verifier-DMr_Zmb_.js} +51 -15
  276. package/dist/{rls-verify-CVQTHTh5.js → verify-BztUB3GX.js} +668 -239
  277. package/dist/{version-BvacVTZR.js → version-BEogC-rB.js} +1 -1
  278. package/dist/{views-Soe7bGSC.js → views-CJsrBS1f.js} +23 -1
  279. package/dist/{vocabulary-DA8a8zif.d.ts → vocabulary-hhEFLFoL.d.ts} +15 -6
  280. package/dist/vue/index.d.ts +24 -19
  281. package/dist/vue/index.js +24 -8
  282. package/dist/watch-C8TpsAl4.js +2 -0
  283. package/dist/{watch-DVBJ9cBq.js → watch-zeucB7eF.js} +6 -6
  284. package/dist/webmcp/index.d.ts +2 -2
  285. package/dist/webmcp/index.js +13 -37
  286. package/dist/wrap-Cr93qqdP.d.ts +14 -0
  287. package/dist/wrap-DWsU_5Bd.js +26 -0
  288. package/package.json +8 -8
  289. package/schemas/catalog-v1.json +6 -0
  290. package/skills/permdock-credentials/SKILL.md +1 -1
  291. package/skills/permdock-credentials/references/tokens-keys-links.md +7 -2
  292. package/skills/permdock-data/references/queries-and-rls.md +4 -0
  293. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
  294. package/skills/permdock-wire/SKILL.md +2 -2
  295. package/skills/permdock-wire/references/adapters.md +9 -1
  296. package/skills/permdock-wire/references/policy-options.md +10 -1
  297. package/dist/catalog-CRW5Mx_B.js +0 -2
  298. package/dist/collect-DSWOk19R.js +0 -2
  299. package/dist/create-CMwIl-w2.js +0 -253
  300. package/dist/delegation-D8pUEuce.js +0 -103
  301. package/dist/doctor-collect-CHk6XWOA.js +0 -665
  302. package/dist/doctor-igIiM_Hl.js +0 -1194
  303. package/dist/evaluation-items-CfCCejQd.js +0 -40
  304. package/dist/fields-BMl1RxoX.js +0 -26
  305. package/dist/grantee-Cv5Sogfv.js +0 -602
  306. package/dist/hosted-DJQw46hn.js +0 -266
  307. package/dist/instance-options-Dhhcnm3y.js +0 -22
  308. package/dist/load-DwJUafYN.js +0 -2
  309. package/dist/map-AcxK9o_D.js +0 -147
  310. package/dist/permissions-DVoF6dMt.js +0 -662
  311. package/dist/powersync-e_AZrKbg.js +0 -2
  312. package/dist/protected-xAr24FEC.d.ts +0 -40
  313. package/dist/relations-cjOL01hZ.js +0 -505
  314. package/dist/rls-graph-CHwx0V8q.js +0 -435
  315. package/dist/rls-shared-Cewd_-zI.js +0 -495
  316. package/dist/store-jOBYpiXC.d.ts +0 -36
  317. package/dist/stores-C0IMQrGI.d.ts +0 -52
  318. package/dist/supabase-config-BpPXt3x1.js +0 -472
  319. package/dist/supabase-hook-6sYLXg_u.js +0 -2
  320. package/dist/thenable-okc2xDsj.js +0 -10
  321. package/dist/types-CKNSyIf_.d.ts +0 -52
  322. package/dist/usage-DorAJiU_.js +0 -2
  323. package/dist/watch-BwxLTYMZ.js +0 -2
@@ -1,9 +1,10 @@
1
- import { l as scopeChain, o as resolveScope } from "./scopes-CVdPrWN-.js";
2
- import { S as findRole } from "./grantee-Cv5Sogfv.js";
3
- import { l as roleColumn } from "./sources-4p0-4xQm.js";
4
- import { B as memberVia, G as quoteIdent, I as memberForSources, K as quoteLiteral, L as memberForTable, M as kindFilterSql, S as globalKindFilterSql, W as qualifiedTable, X as scopeSources, Y as roleKindSql, Z as scopeTable, at as tenantTypeOf, c as signedIn, o as qualified, q as quoteTable, rt as subjectIdSql, t as CUSTOM_ROLES, z as memberRoleOf } from "./rls-shared-Cewd_-zI.js";
5
- import { D as claimKeptLines, I as sourceFilters, L as sourcesKeep, M as membershipRows, N as membershipTable, O as disabledColumn, P as roleRows, R as hasCustomRoleChecksFor, j as memberColumn, k as globalRoleRows } from "./rls-fields-74oZPXWL.js";
6
- //#region src/cli/rls-ownership.ts
1
+ import { l as scopeChain, o as resolveScope } from "./scopes-tvpa34ep.js";
2
+ import { m as findRole } from "./grantee-CXY-0C87.js";
3
+ import { r as qualifySqlTable } from "./sql-t8teWOfo.js";
4
+ import { l as roleColumn } from "./sources-B1-b-R0t.js";
5
+ import { A as declaredArray, F as quoteQualified, M as qualified, N as quoteIdent, O as tenantTypeOf, P as quoteLiteral, f as kindFilterSql, h as roleKindSql, m as memberVia, s as signedIn, t as CUSTOM_ROLES, u as globalKindFilterSql, y as subjectIdSql } from "./shared-1uhI-e-b.js";
6
+ import { G as scopeSources, K as scopeTable, R as memberForSources, V as memberRoleOf, d as memberColumn, f as sourceFilters, g as roleRows, h as membershipRows, l as claimKeptLines, m as globalRoleRows, p as sourcesKeep, u as disabledColumn, y as hasCustomRoleChecksFor, z as memberForTable } from "./permission-keys-B26jeyUv.js";
7
+ //#region src/cli/rls/ownership-names.ts
7
8
  /** Objects the ownership rules add next to the helpers. Names are part of the SQL contract. */
8
9
  const OWNERSHIP = {
9
10
  canAssign: "permdock_can_assign",
@@ -13,62 +14,234 @@ const OWNERSHIP = {
13
14
  holders: "permdock_holders",
14
15
  transferOnly: "permdock_transfer_only"
15
16
  };
16
- function roleScope(policy, scopes, name) {
17
- const on = policy.rolesByName.get(name)?.on ?? findRole(policy.vocabulary.roles, name)?.on;
18
- return typeof on === "string" ? resolveScope(scopes, on) : void 0;
19
- }
20
- function isGlobalRole(policy, name) {
21
- const binding = policy.rolesByName.get(name);
22
- if (binding !== void 0) return binding.on === void 0;
23
- const leaf = findRole(policy.vocabulary.roles, name);
24
- return leaf !== void 0 && leaf.on === void 0;
25
- }
26
- /** The rules `rls generate` enforces; `undefined` when no role declares one. */
27
- function ownershipRules(policy, scopes) {
28
- const kinds = {};
29
- const assigns = [];
30
- const counted = [];
31
- for (const binding of policy.roles) {
32
- const scope = roleScope(policy, scopes, binding.name);
33
- if (binding.for !== void 0) kinds[binding.name] = binding.for;
34
- const assigner = scope ?? (binding.on === void 0 ? "global" : void 0);
35
- for (const target of binding.assigns ?? []) {
36
- const at = roleScope(policy, scopes, target);
37
- if (assigner !== void 0 && at !== void 0 && (assigner === "global" || scopeChain(scopes, at).includes(assigner))) assigns.push({
38
- assigner: binding.name,
39
- scope: assigner,
40
- role: target,
41
- at
42
- });
43
- else if (assigner === "global" && isGlobalRole(policy, target)) assigns.push({
44
- assigner: binding.name,
45
- scope: "global",
46
- role: target,
47
- at: "global"
48
- });
49
- }
50
- const min = binding.min ?? 0;
51
- const transferOnly = binding.transferOnly === true;
52
- if (scope !== void 0 && (min > 0 || binding.max !== void 0 || transferOnly)) counted.push(binding.max === void 0 ? {
53
- role: binding.name,
54
- scope,
55
- min,
56
- transferOnly
57
- } : {
58
- role: binding.name,
17
+ /** Objects `rls.assignments` adds. Names are part of the SQL contract. */
18
+ const ASSIGNMENTS = {
19
+ custom: "permdock_can_assign_custom_role",
20
+ customFor: "permdock_can_assign_custom_role_for",
21
+ trigger: "permdock_assignment"
22
+ };
23
+ //#endregion
24
+ //#region src/cli/rls/assignments.ts
25
+ /** The client roles whose writes the assignment triggers check; any other role is a trusted path. */
26
+ const CLIENT_ROLES = [
27
+ "anon",
28
+ "anonymous",
29
+ "authenticated"
30
+ ];
31
+ /** The mapped membership tables, `rls.assignments.tables` and the global-roles table `rls.roles`. */
32
+ function assignedTables(ctx) {
33
+ const out = [];
34
+ for (const { name } of ctx.scopes) {
35
+ const mapped = scopeTable(ctx, name);
36
+ if (mapped === void 0) continue;
37
+ out.push({
38
+ table: mapped.table.table,
39
+ scope: name,
40
+ id: mapped.column,
41
+ tenant: mapped.tenantColumn ?? mapped.column,
42
+ role: mapped.table.role,
43
+ user: mapped.table.user
44
+ });
45
+ }
46
+ if (ctx.roles !== void 0) out.push({
47
+ table: ctx.roles.table,
48
+ scope: "global",
49
+ role: ctx.roles.role ?? "role",
50
+ user: ctx.roles.user ?? "user_id"
51
+ });
52
+ for (const extra of ctx.assignments?.tables ?? []) {
53
+ const scope = resolveScope(ctx.scopes, extra.scope);
54
+ if (scope === void 0) throw new Error(`PermDock CLI: rls.assignments.tables names scope '${extra.scope}', which the policy does not declare`);
55
+ if (scope !== ctx.scopes[0]?.name && extra.tenant === void 0) throw new Error(`PermDock CLI: rls.assignments.tables entry ${extra.table} assigns at ${scope}, below the first scope: name its tenant column`);
56
+ out.push({
57
+ table: extra.table,
59
58
  scope,
60
- min,
61
- max: binding.max,
62
- transferOnly
59
+ id: extra.id,
60
+ tenant: extra.tenant ?? extra.id,
61
+ role: extra.role,
62
+ ...extra.user === void 0 ? {} : { user: extra.user }
63
63
  });
64
64
  }
65
- if (Object.keys(kinds).length === 0 && assigns.length === 0 && counted.length === 0) return;
66
- return {
67
- kinds,
68
- assigns,
69
- counted
70
- };
65
+ return out;
66
+ }
67
+ /** The tables `rls.assignments.ownRole` refuses a caller's own rows on, by table key. */
68
+ function ownRoleTables(ctx, tables) {
69
+ const own = ctx.assignments?.ownRole;
70
+ if (own === void 0) return /* @__PURE__ */ new Set();
71
+ if (own === "refuse") return new Set(tables.filter((entry) => entry.user !== void 0).map((entry) => qualifySqlTable(entry.table)));
72
+ const out = /* @__PURE__ */ new Set();
73
+ for (const [name, value] of Object.entries(own)) {
74
+ const entry = tables.find((table) => qualifySqlTable(table.table) === qualifySqlTable(name));
75
+ if (entry === void 0) throw new Error(`PermDock CLI: rls.assignments.ownRole names ${name}, which no assignment trigger guards`);
76
+ if (value !== "refuse") throw new Error(`PermDock CLI: rls.assignments.ownRole.${name} must be 'refuse'`);
77
+ if (entry.user === void 0) throw new Error(`PermDock CLI: rls.assignments.ownRole names ${name}, which has no user column: set its user in rls.assignments.tables`);
78
+ out.add(qualifySqlTable(entry.table));
79
+ }
80
+ return out;
81
+ }
82
+ /**
83
+ * The tables `rls.assignments` guards, schema-qualified: each scope's mapped
84
+ * membership table, the global-roles table (`rls.roles`, else
85
+ * `supabase.hook.roles`) and `rls.assignments.tables`. Empty without
86
+ * `rls.assignments`.
87
+ */
88
+ function guardedTables(config, scopes) {
89
+ const rls = config.rls;
90
+ if (rls?.assignments === void 0) return [];
91
+ const hook = config.supabase?.hook?.roles;
92
+ const roles = rls.roles ?? (hook === false ? void 0 : hook);
93
+ const tables = [
94
+ ...scopes.flatMap((scope) => {
95
+ const mapped = memberForTable({
96
+ scopes,
97
+ ...rls.memberships === void 0 ? {} : { memberships: rls.memberships }
98
+ }, scope.name);
99
+ return mapped === void 0 ? [] : [mapped.table];
100
+ }),
101
+ ...roles === void 0 ? [] : [roles.table],
102
+ ...(rls.assignments === true ? [] : rls.assignments.tables ?? []).map((entry) => entry.table)
103
+ ];
104
+ return [...new Set(tables.map(qualifySqlTable))];
105
+ }
106
+ function isRoleList(role) {
107
+ return Array.isArray(role);
108
+ }
109
+ /** The role keys a row variable holds, one scalar per role source. */
110
+ function rowRoles(entry, row) {
111
+ return (isRoleList(entry.role) ? entry.role : [entry.role]).map((part) => roleColumn(part, qualifySqlTable(entry.table), row, {
112
+ label: `rls.assignments ${entry.table} role`,
113
+ prefix: "PermDock CLI"
114
+ }).lookup);
115
+ }
116
+ /**
117
+ * `permdock_can_assign_custom_role`: whether the signed-in caller may assign a
118
+ * custom role of the tenant at the instance, by the checks the custom-role
119
+ * write functions run on its stored definition.
120
+ */
121
+ function canAssignCustomSql(ctx, forUser = false) {
122
+ const tenantType = tenantTypeOf(ctx);
123
+ const fn = qualified(ctx, forUser ? ASSIGNMENTS.customFor : ASSIGNMENTS.custom);
124
+ const perms = qualified(ctx, CUSTOM_ROLES.permissions);
125
+ const includes = qualified(ctx, CUSTOM_ROLES.includes);
126
+ const guard = qualified(ctx, forUser ? CUSTOM_ROLES.guardFor : CUSTOM_ROLES.guard);
127
+ const userParam = forUser ? "p_user uuid, " : "";
128
+ const userArg = forUser ? "uuid, " : "";
129
+ const stored = `c.tenant_id is not distinct from p_tenant and c.scope = p_scope and c.role = p_role and (c.scope_id is null or c.scope_id = p_scope_id)`;
130
+ const grants = forUser ? `revoke execute on function ${fn}(${userArg}${tenantType}, text, text, text) from public, anon, authenticated;` : `revoke execute on function ${fn}(${tenantType}, text, text, text) from public, anon;
131
+ grant execute on function ${fn}(${tenantType}, text, text, text) to authenticated;`;
132
+ return `-- whether ${forUser ? "user p_user" : "the caller"} may assign custom role p_role at instance p_scope_id (a platform role: null tenant, scope global): the custom-role write checks on its stored definition${forUser ? "; no client role may execute it" : ""}
133
+ create or replace function ${fn}(${userParam}p_tenant ${tenantType}, p_scope text, p_scope_id text, p_role text)
134
+ returns boolean
135
+ language plpgsql
136
+ stable
137
+ security definer
138
+ set search_path = ''
139
+ as $$
140
+ declare
141
+ v_scope_id text;
142
+ v_found boolean := false;
143
+ begin
144
+ select true, c.scope_id into v_found, v_scope_id
145
+ from (
146
+ select c.scope_id from ${perms} c where ${stored}
147
+ union all
148
+ select c.scope_id from ${includes} c where ${stored}
149
+ ) c
150
+ order by c.scope_id nulls first
151
+ limit 1;
152
+ if not coalesce(v_found, false) then
153
+ return false;
154
+ end if;
155
+ perform ${guard}(${forUser ? "p_user, " : ""}p_tenant, p_scope, v_scope_id, p_role);
156
+ return true;
157
+ exception
158
+ when insufficient_privilege or invalid_parameter_value then
159
+ return false;
160
+ end;
161
+ $$;
162
+ ${grants}`;
163
+ }
164
+ /**
165
+ * The assignment triggers (\`rls.assignments\`): before a client role inserts,
166
+ * updates or deletes a row that assigns a role, every role the old and new
167
+ * row hold must be one the caller may assign at that instance, by
168
+ * \`permdock_can_assign\` for a declared role and
169
+ * \`permdock_can_assign_custom_role\` for a custom one. The functions run as
170
+ * the invoker, so \`current_user\` tells a client write from a trusted one.
171
+ */
172
+ function assignmentSql(ctx) {
173
+ const own = ctx.ownership;
174
+ if (ctx.assignments === void 0 || own === void 0 || own.assigns.length === 0) return "";
175
+ const canAssign = qualified(ctx, OWNERSHIP.canAssign);
176
+ const custom = ctx.customRoles !== void 0 && ctx.authorize === "database";
177
+ const declared = ctx.customRoles?.declared;
178
+ const tenantType = tenantTypeOf(ctx);
179
+ const chunks = [];
180
+ const clients = `current_user::text = any(array[${CLIENT_ROLES.map(quoteLiteral).join(", ")}])`;
181
+ const tables = assignedTables(ctx);
182
+ const ownRole = ownRoleTables(ctx, tables);
183
+ for (const entry of tables) {
184
+ const table = quoteQualified(entry.table);
185
+ const suffix = entry.table.replaceAll(/[^a-z0-9_]/gu, "_");
186
+ const fn = qualified(ctx, quoteIdent(`${ASSIGNMENTS.trigger}_${suffix}`));
187
+ const ownUser = entry.user !== void 0 && ownRole.has(qualifySqlTable(entry.table)) ? entry.user : void 0;
188
+ const ownCheck = (row, reason) => ownUser === void 0 ? "" : ` if ${row}.${quoteIdent(ownUser)}::text = (${subjectIdSql(ctx)})::text then
189
+ raise exception using
190
+ errcode = '42501',
191
+ message = 'permdock: the caller may not change their own roles',
192
+ hint = '${reason}';
193
+ end if;
194
+ `;
195
+ const check = (row) => {
196
+ const id = entry.id === void 0 ? "null::text" : `${row}.${quoteIdent(entry.id)}::text`;
197
+ const customFn = qualified(ctx, ASSIGNMENTS.custom);
198
+ let allowed = `${canAssign}(v_role, ${id})`;
199
+ if (custom && declared !== void 0) {
200
+ const platform = `${customFn}(null, 'global', null, v_role)`;
201
+ allowed = entry.id === void 0 || entry.tenant === void 0 ? `case when v_role = any(${declaredArray(declared)}) then ${canAssign}(v_role, ${id}) else ${platform} end` : `case when v_role = any(${declaredArray(declared)}) then ${canAssign}(v_role, ${id}) when ${id} is null then ${platform} else ${customFn}(${row}.${quoteIdent(entry.tenant)}::${tenantType}, ${quoteLiteral(entry.scope)}, ${id}, v_role) end`;
202
+ }
203
+ return ` foreach v_role in array array[${rowRoles(entry, row).join(", ")}]::text[] loop
204
+ if v_role is not null and not coalesce(${allowed}, false) then
205
+ raise exception using
206
+ errcode = '42501',
207
+ message = 'permdock: the caller may not assign ' || v_role || ' in ' || coalesce(${id}, 'null'),
208
+ hint = 'not-assignable-by';
209
+ end if;
210
+ end loop;`;
211
+ };
212
+ const what = entry.id === void 0 ? "global roles" : `${entry.scope} roles`;
213
+ chunks.push(`-- ${entry.table}: a client role may write only the ${what} it may assign${ownUser === void 0 ? "" : ", and none on its own rows"}
214
+ create or replace function ${fn}()
215
+ returns trigger
216
+ language plpgsql
217
+ volatile
218
+ set search_path = ''
219
+ as $$
220
+ declare
221
+ v_role text;
222
+ begin
223
+ if not (${clients}) then
224
+ return coalesce(new, old);
225
+ end if;
226
+ if tg_op in ('UPDATE', 'DELETE') then
227
+ ${ownCheck("old", "self-demotion")}${check("old")}
228
+ end if;
229
+ if tg_op in ('INSERT', 'UPDATE') then
230
+ ${ownCheck("new", "not-assignable-by")}${check("new")}
231
+ end if;
232
+ return coalesce(new, old);
233
+ end;
234
+ $$;
235
+ revoke execute on function ${fn}() from public, anon, authenticated;
236
+ drop trigger if exists "permdock_assignment" on ${table};
237
+ create trigger "permdock_assignment"
238
+ before insert or update or delete on ${table}
239
+ for each row execute function ${fn}();`);
240
+ }
241
+ return `${chunks.join("\n\n")}\n`;
71
242
  }
243
+ //#endregion
244
+ //#region src/cli/rls/ownership-triggers.ts
72
245
  function objectName(prefix, scope) {
73
246
  return quoteIdent(`${prefix}_${scope}`);
74
247
  }
@@ -87,7 +260,7 @@ function holdersSql(ctx, table, column, idExpr, role, into) {
87
260
  const kind = typeof role === "string" ? roleKindSql(ctx, role, via) : kindFilterSql(ctx, roleExpr, via);
88
261
  if (kind !== void 0) filters.push(kind);
89
262
  return `select count(distinct m.${quoteIdent(table.user)}) into ${into}
90
- from ${membershipTable(table.table)} m${held.join}
263
+ from ${quoteQualified(table.table)} m${held.join}
91
264
  where ${filters.join("\n and ")}`;
92
265
  }
93
266
  /**
@@ -99,7 +272,7 @@ function holdersTriggerSql(ctx, scope, table, column, counted) {
99
272
  const fn = qualified(ctx, `${OWNERSHIP.holders}_${scope}`);
100
273
  const col = quoteIdent(column);
101
274
  const checks = counted.map((rule) => {
102
- const lines = [` select count(*) into v_total from ${membershipTable(table.table)} m where m.${col} = v_key;`, ` ${holdersSql(ctx, table, column, "v_key", rule.role, "v_count")};`];
275
+ const lines = [` select count(*) into v_total from ${quoteQualified(table.table)} m where m.${col} = v_key;`, ` ${holdersSql(ctx, table, column, "v_key", rule.role, "v_count")};`];
103
276
  if (rule.min > 0) lines.push(` if v_total > 0 and v_count < ${String(rule.min)} then
104
277
  raise exception using
105
278
  errcode = '23514',
@@ -124,7 +297,7 @@ as $$
124
297
  declare
125
298
  v_ids text[] := '{}';
126
299
  v_id text;
127
- v_key ${membershipTable(table.table)}.${col}%type;
300
+ v_key ${quoteQualified(table.table)}.${col}%type;
128
301
  v_total bigint;
129
302
  v_count bigint;
130
303
  begin
@@ -142,9 +315,9 @@ ${checks.join("\n")}
142
315
  return null;
143
316
  end;
144
317
  $$;
145
- drop trigger if exists ${objectName(OWNERSHIP.holders, scope)} on ${membershipTable(table.table)};
318
+ drop trigger if exists ${objectName(OWNERSHIP.holders, scope)} on ${quoteQualified(table.table)};
146
319
  create constraint trigger ${objectName(OWNERSHIP.holders, scope)}
147
- after insert or update or delete on ${membershipTable(table.table)}
320
+ after insert or update or delete on ${quoteQualified(table.table)}
148
321
  deferrable initially deferred
149
322
  for each row execute function ${fn}();`;
150
323
  }
@@ -178,7 +351,7 @@ function transferTriggerSql(ctx, scope, table, column, roles) {
178
351
  end loop;`;
179
352
  const inserted = rows("n", "permdock_new", "1");
180
353
  const deleted = rows("o", "permdock_old", "-1");
181
- const target = membershipTable(table.table);
354
+ const target = quoteQualified(table.table);
182
355
  const trigger = (op) => {
183
356
  const name = quoteIdent(`${OWNERSHIP.transferOnly}_${scope}_${op}`);
184
357
  return `drop trigger if exists ${name} on ${target};
@@ -212,6 +385,8 @@ ${trigger("insert")}
212
385
  ${trigger("update")}
213
386
  ${trigger("delete")}`;
214
387
  }
388
+ //#endregion
389
+ //#region src/cli/rls/ownership-sources.ts
215
390
  /**
216
391
  * The membership sources holding scope `name`: `undefined` when there are
217
392
  * none, `'not-tables'` when one of them carries no table to put a trigger on.
@@ -243,10 +418,10 @@ function sourceKeys(sources) {
243
418
  };
244
419
  }
245
420
  function regclassOf(table) {
246
- return `${quoteLiteral(membershipTable(table))}::regclass`;
421
+ return `${quoteLiteral(quoteQualified(table))}::regclass`;
247
422
  }
248
423
  function sourceTables(sources) {
249
- return [...new Set(sources.map((source) => membershipTable(source.table)))];
424
+ return [...new Set(sources.map((source) => quoteQualified(source.table)))];
250
425
  }
251
426
  /** `min` and `max` over membership sources: the same commit-time check as on a mapped table, counted over every source of the scope. */
252
427
  function sourceHoldersTriggerSql(ctx, scope, sources, counted) {
@@ -398,6 +573,8 @@ function sourceRowsSql(sources, user) {
398
573
  const keep = sources.some((source) => source.sql.keeps);
399
574
  return sources.map((source) => source.sql.select(user(source), keep).replaceAll(/^/gmu, " ")).join("\n union all\n");
400
575
  }
576
+ //#endregion
577
+ //#region src/cli/rls/can-assign.ts
401
578
  /** Whether the signed-in user (or `p_user` in the `_for` form) holds, in instance `p_scope_id`, a role whose `assigns` lists `p_role`. */
402
579
  function canAssignSql(base, own, forUser = false) {
403
580
  const ctx = forUser ? {
@@ -470,7 +647,7 @@ ${sourceRowsSql(sources, userOf)}
470
647
  const kind = kindFilterSql(ctx, role.sql, memberVia(table));
471
648
  if (kind !== void 0) filters.push(kind);
472
649
  parts.push(`exists (
473
- select 1 from ${membershipTable(table.table)} m${role.join}
650
+ select 1 from ${quoteQualified(table.table)} m${role.join}
474
651
  where ${filters.join("\n and ")}
475
652
  )`);
476
653
  } else {
@@ -507,337 +684,168 @@ begin
507
684
  return ${signedIn(ctx)} and (
508
685
  ${body}
509
686
  );
510
- end;
511
- $$;`;
512
- if (forUser) {
513
- const user = ctx.dialect === "supabase" ? "uuid" : "text";
514
- return `-- who may assign, for a user the caller names: trusted SQL acting later for a stored user; no client role may execute it
515
- create or replace function ${fn}(p_user ${user}, p_role text, p_scope_id text)
516
- returns boolean
517
- ${language}
518
- revoke execute on function ${fn}(${user}, text, text) from public, anon, authenticated;`;
519
- }
520
- return `-- who may assign: a held role whose assigns lists p_role, in instance p_scope_id (its scope or an ancestor's); a null p_scope_id assigns a global role, which only global assigners may
521
- create or replace function ${fn}(p_role text, p_scope_id text)
522
- returns boolean
523
- ${language}
524
- revoke execute on function ${fn}(text, text) from public, anon;
525
- grant execute on function ${fn}(text, text) to authenticated;`;
526
- }
527
- /**
528
- * The ownership objects: holder-count and transfer-only triggers on each
529
- * scope's membership table, and `permdock_can_assign` for the application's
530
- * own policies on those tables. Empty when no role declares a rule.
531
- */
532
- function ownershipSql(ctx) {
533
- const own = ctx.ownership;
534
- if (own === void 0) return "";
535
- const chunks = [];
536
- const skipped = ctx.skipOwnershipTriggers;
537
- for (const { name } of ctx.scopes) {
538
- const counted = own.counted.filter((rule) => rule.scope === name);
539
- if (counted.length === 0) continue;
540
- if (skipped === "all" || skipped?.includes(name) === true) {
541
- chunks.push(`-- ${name}: rls.ownershipTriggers leaves out the triggers, so min, max and transferOnly are checked only by decideRoleChange`);
542
- continue;
543
- }
544
- const transfer = counted.filter((rule) => rule.transferOnly).map((rule) => rule.role);
545
- const mapped = scopeTable(ctx, name);
546
- if (mapped === void 0) {
547
- const sources = holderSources(ctx, name);
548
- if (sources === void 0) chunks.push(`-- ${name}: no memberships table configured, so min, max and transferOnly are checked only by decideRoleChange`);
549
- else if (sources === "not-tables") chunks.push(`-- ${name}: a membership source is not a table, so min, max and transferOnly are checked only by decideRoleChange`);
550
- else {
551
- chunks.push(sourceHoldersTriggerSql(ctx, name, sources, counted));
552
- if (transfer.length > 0) chunks.push(sourceTransferTriggerSql(ctx, name, sources, transfer));
553
- }
554
- continue;
555
- }
556
- chunks.push(holdersTriggerSql(ctx, name, mapped.table, mapped.column, counted));
557
- if (transfer.length > 0) chunks.push(transferTriggerSql(ctx, name, mapped.table, mapped.column, transfer));
558
- }
559
- if (own.assigns.length > 0) {
560
- chunks.push(canAssignSql(ctx, own));
561
- if (ctx.authorize === "database") chunks.push(canAssignSql(ctx, own, true));
562
- const custom = assignsCustomRoles(ctx);
563
- if (custom) {
564
- chunks.push(canAssignCustomSql(ctx));
565
- if (hasCustomRoleChecksFor(ctx)) chunks.push(canAssignCustomSql(ctx, true));
566
- }
567
- chunks.push(canAssignAnySql(ctx, custom));
568
- if (hasCanAssignAnyFor(ctx)) chunks.push(canAssignAnySql(ctx, custom, true));
569
- }
570
- return chunks.length === 0 ? "" : `${chunks.join("\n\n")}\n`;
571
- }
572
- /** Whether the helpers keep custom roles in tables, so assigning one is checked against its stored definition. */
573
- function assignsCustomRoles(ctx) {
574
- return ctx.customRoles !== void 0 && ctx.authorize === "database";
575
- }
576
- /** Whether `permdock_can_assign_any_for` is written: every function it calls has a `_for` form. */
577
- function hasCanAssignAnyFor(ctx) {
578
- return ctx.authorize === "database" && (!assignsCustomRoles(ctx) || hasCustomRoleChecksFor(ctx));
579
- }
580
- /**
581
- * `permdock_can_assign_any`: one check for a declared or a custom role, so
582
- * SQL that assigns roles calls one function: `permdock_can_assign` for a
583
- * declared role, `permdock_can_assign_custom_role` for a custom one. `p_scope`
584
- * `'global'` (no tenant, no instance) assigns a platform role.
585
- */
586
- function canAssignAnySql(ctx, custom, forUser = false) {
587
- const tenantType = tenantTypeOf(ctx);
588
- const fn = qualified(ctx, forUser ? OWNERSHIP.canAssignAnyFor : OWNERSHIP.canAssignAny);
589
- const declaredFn = qualified(ctx, forUser ? OWNERSHIP.canAssignFor : OWNERSHIP.canAssign);
590
- const customFn = qualified(ctx, forUser ? ASSIGNMENTS.customFor : ASSIGNMENTS.custom);
591
- const userType = ctx.dialect === "supabase" ? "uuid" : "text";
592
- const userParam = forUser ? `p_user ${userType}, ` : "";
593
- const userArg = forUser ? `${userType}, ` : "";
594
- const user = forUser ? "p_user, " : "";
595
- const declared = `${declaredFn}(${user}p_role, case when p_scope = 'global' then null else p_scope_id end)`;
596
- const body = custom && ctx.customRoles !== void 0 ? `case
597
- when p_role = any(${declaredArray(ctx.customRoles.declared)}) then ${declared}
598
- when p_scope = 'global' then ${customFn}(${user}null, 'global', null, p_role)
599
- else ${customFn}(${user}p_tenant, p_scope, p_scope_id, p_role)
600
- end` : declared;
601
- const grants = forUser ? `revoke execute on function ${fn}(${userArg}text, ${tenantType}, text, text) from public, anon, authenticated;` : `revoke execute on function ${fn}(text, ${tenantType}, text, text) from public, anon;
602
- grant execute on function ${fn}(text, ${tenantType}, text, text) to authenticated;`;
603
- return `-- whether ${forUser ? "user p_user" : "the caller"} may assign p_role at instance p_scope_id of p_scope in tenant p_tenant, declared or custom (p_scope 'global' for a platform role)${forUser ? "; no client role may execute it" : ""}
604
- create or replace function ${fn}(${userParam}p_role text, p_tenant ${tenantType}, p_scope text, p_scope_id text)
605
- returns boolean
606
- language sql
607
- stable
608
- security definer
609
- set search_path = ''
610
- as $$
611
- select coalesce(${body}, false)
612
- $$;
613
- ${grants}`;
614
- }
615
- /** Objects `rls.assignments` adds. Names are part of the SQL contract. */
616
- const ASSIGNMENTS = {
617
- custom: "permdock_can_assign_custom_role",
618
- customFor: "permdock_can_assign_custom_role_for",
619
- trigger: "permdock_assignment"
620
- };
621
- /** The client roles whose writes the assignment triggers check; any other role is a trusted path. */
622
- const CLIENT_ROLES = [
623
- "anon",
624
- "anonymous",
625
- "authenticated"
626
- ];
627
- /** The mapped membership tables, `rls.assignments.tables` and the global-roles table `rls.roles`. */
628
- function assignedTables(ctx) {
629
- const out = [];
630
- for (const { name } of ctx.scopes) {
631
- const mapped = scopeTable(ctx, name);
632
- if (mapped === void 0) continue;
633
- out.push({
634
- table: mapped.table.table,
635
- scope: name,
636
- id: mapped.column,
637
- tenant: mapped.tenantColumn ?? mapped.column,
638
- role: mapped.table.role,
639
- user: mapped.table.user
640
- });
641
- }
642
- if (ctx.roles !== void 0) out.push({
643
- table: ctx.roles.table,
644
- scope: "global",
645
- role: ctx.roles.role ?? "role",
646
- user: ctx.roles.user ?? "user_id"
647
- });
648
- for (const extra of ctx.assignments?.tables ?? []) {
649
- const scope = resolveScope(ctx.scopes, extra.scope);
650
- if (scope === void 0) throw new Error(`PermDock CLI: rls.assignments.tables names scope '${extra.scope}', which the policy does not declare`);
651
- if (scope !== ctx.scopes[0]?.name && extra.tenant === void 0) throw new Error(`PermDock CLI: rls.assignments.tables entry ${extra.table} assigns at ${scope}, below the first scope: name its tenant column`);
652
- out.push({
653
- table: extra.table,
654
- scope,
655
- id: extra.id,
656
- tenant: extra.tenant ?? extra.id,
657
- role: extra.role,
658
- ...extra.user === void 0 ? {} : { user: extra.user }
659
- });
660
- }
661
- return out;
662
- }
663
- /** The tables `rls.assignments.ownRole` refuses a caller's own rows on, by table key. */
664
- function ownRoleTables(ctx, tables) {
665
- const own = ctx.assignments?.ownRole;
666
- if (own === void 0) return /* @__PURE__ */ new Set();
667
- if (own === "refuse") return new Set(tables.filter((entry) => entry.user !== void 0).map((entry) => qualifiedTable(entry.table)));
668
- const out = /* @__PURE__ */ new Set();
669
- for (const [name, value] of Object.entries(own)) {
670
- const entry = tables.find((table) => qualifiedTable(table.table) === qualifiedTable(name));
671
- if (entry === void 0) throw new Error(`PermDock CLI: rls.assignments.ownRole names ${name}, which no assignment trigger guards`);
672
- if (value !== "refuse") throw new Error(`PermDock CLI: rls.assignments.ownRole.${name} must be 'refuse'`);
673
- if (entry.user === void 0) throw new Error(`PermDock CLI: rls.assignments.ownRole names ${name}, which has no user column: set its user in rls.assignments.tables`);
674
- out.add(qualifiedTable(entry.table));
675
- }
676
- return out;
677
- }
678
- /**
679
- * The tables `rls.assignments` guards, schema-qualified: each scope's mapped
680
- * membership table, the global-roles table (`rls.roles`, else
681
- * `supabase.hook.roles`) and `rls.assignments.tables`. Empty without
682
- * `rls.assignments`.
683
- */
684
- function guardedTables(config, scopes) {
685
- const rls = config.rls;
686
- if (rls?.assignments === void 0) return [];
687
- const hook = config.supabase?.hook?.roles;
688
- const roles = rls.roles ?? (hook === false ? void 0 : hook);
689
- const tables = [
690
- ...scopes.flatMap((scope) => {
691
- const mapped = memberForTable({
692
- scopes,
693
- ...rls.memberships === void 0 ? {} : { memberships: rls.memberships }
694
- }, scope.name);
695
- return mapped === void 0 ? [] : [mapped.table];
696
- }),
697
- ...roles === void 0 ? [] : [roles.table],
698
- ...(rls.assignments === true ? [] : rls.assignments.tables ?? []).map((entry) => entry.table)
699
- ];
700
- return [...new Set(tables.map(qualifiedTable))];
687
+ end;
688
+ $$;`;
689
+ if (forUser) {
690
+ const user = ctx.dialect === "supabase" ? "uuid" : "text";
691
+ return `-- who may assign, for a user the caller names: trusted SQL acting later for a stored user; no client role may execute it
692
+ create or replace function ${fn}(p_user ${user}, p_role text, p_scope_id text)
693
+ returns boolean
694
+ ${language}
695
+ revoke execute on function ${fn}(${user}, text, text) from public, anon, authenticated;`;
696
+ }
697
+ return `-- who may assign: a held role whose assigns lists p_role, in instance p_scope_id (its scope or an ancestor's); a null p_scope_id assigns a global role, which only global assigners may
698
+ create or replace function ${fn}(p_role text, p_scope_id text)
699
+ returns boolean
700
+ ${language}
701
+ revoke execute on function ${fn}(text, text) from public, anon;
702
+ grant execute on function ${fn}(text, text) to authenticated;`;
701
703
  }
702
- function isRoleList(role) {
703
- return Array.isArray(role);
704
+ /** Whether the helpers keep custom roles in tables, so assigning one is checked against its stored definition. */
705
+ function assignsCustomRoles(ctx) {
706
+ return ctx.customRoles !== void 0 && ctx.authorize === "database";
704
707
  }
705
- /** The role keys a row variable holds, one scalar per role source. */
706
- function rowRoles(entry, row) {
707
- return (isRoleList(entry.role) ? entry.role : [entry.role]).map((part) => roleColumn(part, qualifiedTable(entry.table), row, {
708
- label: `rls.assignments ${entry.table} role`,
709
- prefix: "PermDock CLI"
710
- }).lookup);
708
+ /** Whether `permdock_can_assign_any_for` is written: every function it calls has a `_for` form. */
709
+ function hasCanAssignAnyFor(ctx) {
710
+ return ctx.authorize === "database" && (!assignsCustomRoles(ctx) || hasCustomRoleChecksFor(ctx));
711
711
  }
712
712
  /**
713
- * `permdock_can_assign_custom_role`: whether the signed-in caller may assign a
714
- * custom role of the tenant at the instance, by the checks the custom-role
715
- * write functions run on its stored definition.
713
+ * `permdock_can_assign_any`: one check for a declared or a custom role, so
714
+ * SQL that assigns roles calls one function: `permdock_can_assign` for a
715
+ * declared role, `permdock_can_assign_custom_role` for a custom one. `p_scope`
716
+ * `'global'` (no tenant, no instance) assigns a platform role.
716
717
  */
717
- function canAssignCustomSql(ctx, forUser = false) {
718
+ function canAssignAnySql(ctx, custom, forUser = false) {
718
719
  const tenantType = tenantTypeOf(ctx);
719
- const fn = qualified(ctx, forUser ? ASSIGNMENTS.customFor : ASSIGNMENTS.custom);
720
- const perms = qualified(ctx, CUSTOM_ROLES.permissions);
721
- const includes = qualified(ctx, CUSTOM_ROLES.includes);
722
- const guard = qualified(ctx, forUser ? CUSTOM_ROLES.guardFor : CUSTOM_ROLES.guard);
723
- const userParam = forUser ? "p_user uuid, " : "";
724
- const userArg = forUser ? "uuid, " : "";
725
- const stored = `c.tenant_id is not distinct from p_tenant and c.scope = p_scope and c.role = p_role and (c.scope_id is null or c.scope_id = p_scope_id)`;
726
- const grants = forUser ? `revoke execute on function ${fn}(${userArg}${tenantType}, text, text, text) from public, anon, authenticated;` : `revoke execute on function ${fn}(${tenantType}, text, text, text) from public, anon;
727
- grant execute on function ${fn}(${tenantType}, text, text, text) to authenticated;`;
728
- return `-- whether ${forUser ? "user p_user" : "the caller"} may assign custom role p_role at instance p_scope_id (a platform role: null tenant, scope global): the custom-role write checks on its stored definition${forUser ? "; no client role may execute it" : ""}
729
- create or replace function ${fn}(${userParam}p_tenant ${tenantType}, p_scope text, p_scope_id text, p_role text)
720
+ const fn = qualified(ctx, forUser ? OWNERSHIP.canAssignAnyFor : OWNERSHIP.canAssignAny);
721
+ const declaredFn = qualified(ctx, forUser ? OWNERSHIP.canAssignFor : OWNERSHIP.canAssign);
722
+ const customFn = qualified(ctx, forUser ? ASSIGNMENTS.customFor : ASSIGNMENTS.custom);
723
+ const userType = ctx.dialect === "supabase" ? "uuid" : "text";
724
+ const userParam = forUser ? `p_user ${userType}, ` : "";
725
+ const userArg = forUser ? `${userType}, ` : "";
726
+ const user = forUser ? "p_user, " : "";
727
+ const declared = `${declaredFn}(${user}p_role, case when p_scope = 'global' then null else p_scope_id end)`;
728
+ const body = custom && ctx.customRoles !== void 0 ? `case
729
+ when p_role = any(${declaredArray(ctx.customRoles.declared)}) then ${declared}
730
+ when p_scope = 'global' then ${customFn}(${user}null, 'global', null, p_role)
731
+ else ${customFn}(${user}p_tenant, p_scope, p_scope_id, p_role)
732
+ end` : declared;
733
+ const grants = forUser ? `revoke execute on function ${fn}(${userArg}text, ${tenantType}, text, text) from public, anon, authenticated;` : `revoke execute on function ${fn}(text, ${tenantType}, text, text) from public, anon;
734
+ grant execute on function ${fn}(text, ${tenantType}, text, text) to authenticated;`;
735
+ return `-- whether ${forUser ? "user p_user" : "the caller"} may assign p_role at instance p_scope_id of p_scope in tenant p_tenant, declared or custom (p_scope 'global' for a platform role)${forUser ? "; no client role may execute it" : ""}
736
+ create or replace function ${fn}(${userParam}p_role text, p_tenant ${tenantType}, p_scope text, p_scope_id text)
730
737
  returns boolean
731
- language plpgsql
738
+ language sql
732
739
  stable
733
740
  security definer
734
741
  set search_path = ''
735
742
  as $$
736
- declare
737
- v_scope_id text;
738
- v_found boolean := false;
739
- begin
740
- select true, c.scope_id into v_found, v_scope_id
741
- from (
742
- select c.scope_id from ${perms} c where ${stored}
743
- union all
744
- select c.scope_id from ${includes} c where ${stored}
745
- ) c
746
- order by c.scope_id nulls first
747
- limit 1;
748
- if not coalesce(v_found, false) then
749
- return false;
750
- end if;
751
- perform ${guard}(${forUser ? "p_user, " : ""}p_tenant, p_scope, v_scope_id, p_role);
752
- return true;
753
- exception
754
- when insufficient_privilege or invalid_parameter_value then
755
- return false;
756
- end;
743
+ select coalesce(${body}, false)
757
744
  $$;
758
745
  ${grants}`;
759
746
  }
747
+ //#endregion
748
+ //#region src/cli/rls/ownership.ts
749
+ function roleScope(policy, scopes, name) {
750
+ const on = policy.rolesByName.get(name)?.on ?? findRole(policy.vocabulary.roles, name)?.on;
751
+ return typeof on === "string" ? resolveScope(scopes, on) : void 0;
752
+ }
753
+ function isGlobalRole(policy, name) {
754
+ const binding = policy.rolesByName.get(name);
755
+ if (binding !== void 0) return binding.on === void 0;
756
+ const leaf = findRole(policy.vocabulary.roles, name);
757
+ return leaf !== void 0 && leaf.on === void 0;
758
+ }
759
+ /** The rules `rls generate` enforces; `undefined` when no role declares one. */
760
+ function ownershipRules(policy, scopes) {
761
+ const kinds = {};
762
+ const assigns = [];
763
+ const counted = [];
764
+ for (const binding of policy.roles) {
765
+ const scope = roleScope(policy, scopes, binding.name);
766
+ if (binding.for !== void 0) kinds[binding.name] = binding.for;
767
+ const assigner = scope ?? (binding.on === void 0 ? "global" : void 0);
768
+ for (const target of binding.assigns ?? []) {
769
+ const at = roleScope(policy, scopes, target);
770
+ if (assigner !== void 0 && at !== void 0 && (assigner === "global" || scopeChain(scopes, at).includes(assigner))) assigns.push({
771
+ assigner: binding.name,
772
+ scope: assigner,
773
+ role: target,
774
+ at
775
+ });
776
+ else if (assigner === "global" && isGlobalRole(policy, target)) assigns.push({
777
+ assigner: binding.name,
778
+ scope: "global",
779
+ role: target,
780
+ at: "global"
781
+ });
782
+ }
783
+ const min = binding.min ?? 0;
784
+ const transferOnly = binding.transferOnly === true;
785
+ if (scope !== void 0 && (min > 0 || binding.max !== void 0 || transferOnly)) counted.push(binding.max === void 0 ? {
786
+ role: binding.name,
787
+ scope,
788
+ min,
789
+ transferOnly
790
+ } : {
791
+ role: binding.name,
792
+ scope,
793
+ min,
794
+ max: binding.max,
795
+ transferOnly
796
+ });
797
+ }
798
+ if (Object.keys(kinds).length === 0 && assigns.length === 0 && counted.length === 0) return;
799
+ return {
800
+ kinds,
801
+ assigns,
802
+ counted
803
+ };
804
+ }
760
805
  /**
761
- * The assignment triggers (\`rls.assignments\`): before a client role inserts,
762
- * updates or deletes a row that assigns a role, every role the old and new
763
- * row hold must be one the caller may assign at that instance, by
764
- * \`permdock_can_assign\` for a declared role and
765
- * \`permdock_can_assign_custom_role\` for a custom one. The functions run as
766
- * the invoker, so \`current_user\` tells a client write from a trusted one.
806
+ * The ownership objects: holder-count and transfer-only triggers on each
807
+ * scope's membership table, and `permdock_can_assign` for the application's
808
+ * own policies on those tables. Empty when no role declares a rule.
767
809
  */
768
- function assignmentSql(ctx) {
810
+ function ownershipSql(ctx) {
769
811
  const own = ctx.ownership;
770
- if (ctx.assignments === void 0 || own === void 0 || own.assigns.length === 0) return "";
771
- const canAssign = qualified(ctx, OWNERSHIP.canAssign);
772
- const custom = ctx.customRoles !== void 0 && ctx.authorize === "database";
773
- const declared = ctx.customRoles?.declared;
774
- const tenantType = tenantTypeOf(ctx);
812
+ if (own === void 0) return "";
775
813
  const chunks = [];
776
- const clients = `current_user::text = any(array[${CLIENT_ROLES.map(quoteLiteral).join(", ")}])`;
777
- const tables = assignedTables(ctx);
778
- const ownRole = ownRoleTables(ctx, tables);
779
- for (const entry of tables) {
780
- const table = quoteTable(qualifiedTable(entry.table));
781
- const suffix = entry.table.replaceAll(/[^a-z0-9_]/gu, "_");
782
- const fn = qualified(ctx, quoteIdent(`${ASSIGNMENTS.trigger}_${suffix}`));
783
- const ownUser = entry.user !== void 0 && ownRole.has(qualifiedTable(entry.table)) ? entry.user : void 0;
784
- const ownCheck = (row, reason) => ownUser === void 0 ? "" : ` if ${row}.${quoteIdent(ownUser)}::text = (${subjectIdSql(ctx)})::text then
785
- raise exception using
786
- errcode = '42501',
787
- message = 'permdock: the caller may not change their own roles',
788
- hint = '${reason}';
789
- end if;
790
- `;
791
- const check = (row) => {
792
- const id = entry.id === void 0 ? "null::text" : `${row}.${quoteIdent(entry.id)}::text`;
793
- const customFn = qualified(ctx, ASSIGNMENTS.custom);
794
- let allowed = `${canAssign}(v_role, ${id})`;
795
- if (custom && declared !== void 0) {
796
- const platform = `${customFn}(null, 'global', null, v_role)`;
797
- allowed = entry.id === void 0 || entry.tenant === void 0 ? `case when v_role = any(${declaredArray(declared)}) then ${canAssign}(v_role, ${id}) else ${platform} end` : `case when v_role = any(${declaredArray(declared)}) then ${canAssign}(v_role, ${id}) when ${id} is null then ${platform} else ${customFn}(${row}.${quoteIdent(entry.tenant)}::${tenantType}, ${quoteLiteral(entry.scope)}, ${id}, v_role) end`;
814
+ const skipped = ctx.skipOwnershipTriggers;
815
+ for (const { name } of ctx.scopes) {
816
+ const counted = own.counted.filter((rule) => rule.scope === name);
817
+ if (counted.length === 0) continue;
818
+ if (skipped === "all" || skipped?.includes(name) === true) {
819
+ chunks.push(`-- ${name}: rls.ownershipTriggers leaves out the triggers, so min, max and transferOnly are checked only by decideRoleChange`);
820
+ continue;
821
+ }
822
+ const transfer = counted.filter((rule) => rule.transferOnly).map((rule) => rule.role);
823
+ const mapped = scopeTable(ctx, name);
824
+ if (mapped === void 0) {
825
+ const sources = holderSources(ctx, name);
826
+ if (sources === void 0) chunks.push(`-- ${name}: no memberships table configured, so min, max and transferOnly are checked only by decideRoleChange`);
827
+ else if (sources === "not-tables") chunks.push(`-- ${name}: a membership source is not a table, so min, max and transferOnly are checked only by decideRoleChange`);
828
+ else {
829
+ chunks.push(sourceHoldersTriggerSql(ctx, name, sources, counted));
830
+ if (transfer.length > 0) chunks.push(sourceTransferTriggerSql(ctx, name, sources, transfer));
798
831
  }
799
- return ` foreach v_role in array array[${rowRoles(entry, row).join(", ")}]::text[] loop
800
- if v_role is not null and not coalesce(${allowed}, false) then
801
- raise exception using
802
- errcode = '42501',
803
- message = 'permdock: the caller may not assign ' || v_role || ' in ' || coalesce(${id}, 'null'),
804
- hint = 'not-assignable-by';
805
- end if;
806
- end loop;`;
807
- };
808
- const what = entry.id === void 0 ? "global roles" : `${entry.scope} roles`;
809
- chunks.push(`-- ${entry.table}: a client role may write only the ${what} it may assign${ownUser === void 0 ? "" : ", and none on its own rows"}
810
- create or replace function ${fn}()
811
- returns trigger
812
- language plpgsql
813
- volatile
814
- set search_path = ''
815
- as $$
816
- declare
817
- v_role text;
818
- begin
819
- if not (${clients}) then
820
- return coalesce(new, old);
821
- end if;
822
- if tg_op in ('UPDATE', 'DELETE') then
823
- ${ownCheck("old", "self-demotion")}${check("old")}
824
- end if;
825
- if tg_op in ('INSERT', 'UPDATE') then
826
- ${ownCheck("new", "not-assignable-by")}${check("new")}
827
- end if;
828
- return coalesce(new, old);
829
- end;
830
- $$;
831
- revoke execute on function ${fn}() from public, anon, authenticated;
832
- drop trigger if exists "permdock_assignment" on ${table};
833
- create trigger "permdock_assignment"
834
- before insert or update or delete on ${table}
835
- for each row execute function ${fn}();`);
832
+ continue;
833
+ }
834
+ chunks.push(holdersTriggerSql(ctx, name, mapped.table, mapped.column, counted));
835
+ if (transfer.length > 0) chunks.push(transferTriggerSql(ctx, name, mapped.table, mapped.column, transfer));
836
836
  }
837
- return `${chunks.join("\n\n")}\n`;
838
- }
839
- function declaredArray(values) {
840
- return values.length === 0 ? `'{}'::text[]` : `array[${values.map(quoteLiteral).join(", ")}]::text[]`;
837
+ if (own.assigns.length > 0) {
838
+ chunks.push(canAssignSql(ctx, own));
839
+ if (ctx.authorize === "database") chunks.push(canAssignSql(ctx, own, true));
840
+ const custom = assignsCustomRoles(ctx);
841
+ if (custom) {
842
+ chunks.push(canAssignCustomSql(ctx));
843
+ if (hasCustomRoleChecksFor(ctx)) chunks.push(canAssignCustomSql(ctx, true));
844
+ }
845
+ chunks.push(canAssignAnySql(ctx, custom));
846
+ if (hasCanAssignAnyFor(ctx)) chunks.push(canAssignAnySql(ctx, custom, true));
847
+ }
848
+ return chunks.length === 0 ? "" : `${chunks.join("\n\n")}\n`;
841
849
  }
842
850
  //#endregion
843
- export { ownershipRules as a, guardedTables as i, OWNERSHIP as n, ownershipSql as o, assignmentSql as r, ASSIGNMENTS as t };
851
+ export { ASSIGNMENTS as a, guardedTables as i, ownershipSql as n, OWNERSHIP as o, assignmentSql as r, ownershipRules as t };