permdock 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/README.md +1 -0
  2. package/dist/{2020-C9VN-x8c.js → 2020-CJbO8Bvz.js} +1 -3
  3. package/dist/a2a/index.d.ts +5 -5
  4. package/dist/a2a/index.js +4 -4
  5. package/dist/{adoption-scan-B4R5fnMU.js → adoption-scan-D-feE7BE.js} +1 -1
  6. package/dist/agent/index.d.ts +3 -3
  7. package/dist/agent/index.js +1 -1
  8. package/dist/ai-sdk/index.d.ts +21 -6
  9. package/dist/ai-sdk/index.js +19 -9
  10. package/dist/{approval-prompt-Djo_PyZc.d.ts → approval-prompt-K0faOLFp.d.ts} +2 -2
  11. package/dist/approvals/index.d.ts +4 -4
  12. package/dist/approvals/index.js +4 -4
  13. package/dist/{approvals-DJ6Yd5z1.js → approvals-CMRfsbBh.js} +74 -48
  14. package/dist/{arazzo-B6lebmiQ.js → arazzo-ClrF3PVb.js} +2 -2
  15. package/dist/{arazzo-BWhNF-k0.js → arazzo-rdeJ8n1J.js} +4 -4
  16. package/dist/{args-IIolxxKA.js → args-CC3OwyHW.js} +112 -2
  17. package/dist/astro/index.d.ts +6 -6
  18. package/dist/astro/index.js +2 -2
  19. package/dist/{audience-Dg8XsRaO.js → audience-BrMs8c4o.js} +1 -1
  20. package/dist/{auth-DRLEizbL.js → auth-CscUX8Sy.js} +3 -4
  21. package/dist/authzen/index.d.ts +162 -22
  22. package/dist/authzen/index.js +315 -146
  23. package/dist/{automation-BMZPIVUM.d.ts → automation-DOtAFWdy.d.ts} +3 -3
  24. package/dist/{automation-DVjOckjV.js → automation-ySsxp_Yp.js} +1 -1
  25. package/dist/base-url-BQbddZO5.js +9 -0
  26. package/dist/better-auth/index.d.ts +2 -2
  27. package/dist/better-auth/index.js +2 -2
  28. package/dist/better-supabase/index.d.ts +50 -8
  29. package/dist/better-supabase/index.js +94 -32
  30. package/dist/{bind-CZzhx-Vj.js → bind-D1nO6fiC.js} +5 -5
  31. package/dist/{bind--9EFVYWF.d.ts → bind-JDuvxjuW.d.ts} +7 -7
  32. package/dist/{build-instance-BMnAudHn.js → build-instance-DxE7czo9.js} +1 -1
  33. package/dist/{canonical-json-BC8_nMTw.js → canonical-json-DwilZ1iB.js} +6 -3
  34. package/dist/{capability-BSslw1QV.d.ts → capability-D4UC9TTW.d.ts} +2 -2
  35. package/dist/{capability-N1K9IpqS.js → capability-L7NnfhiE.js} +1 -1
  36. package/dist/{casbin-Cr7RLtA3.js → casbin-BHijLuHY.js} +2 -2
  37. package/dist/{casbin-Dyu2gnUD.js → casbin-BW9pS47J.js} +1 -1
  38. package/dist/{casl-64gpT-8x.js → casl-CmJlgziS.js} +2 -2
  39. package/dist/{casl-DjEDhTCP.js → casl-UbI8v-Wq.js} +1 -1
  40. package/dist/catalog/index.d.ts +70 -10
  41. package/dist/catalog/index.js +112 -6
  42. package/dist/catalog-BCCjSAdH.js +1 -0
  43. package/dist/{catalog-R_A__RsH.js → catalog-BaMQfQ6Y.js} +3 -3
  44. package/dist/{catalog-BECMPrwc.js → catalog-D69WhFii.js} +43 -13
  45. package/dist/{catalog-doc-C9wjsfNY.js → catalog-doc-CV9s6B1S.js} +28 -5
  46. package/dist/{catalog-fingerprint-CCP0-yQS.js → catalog-fingerprint-C144oW4M.js} +1 -1
  47. package/dist/{create-BWRSXgOF.js → challenge-nGWma08S.js} +122 -25
  48. package/dist/chat/index.d.ts +1 -1
  49. package/dist/chat/index.js +2 -2
  50. package/dist/chat-sdk/index.d.ts +3 -3
  51. package/dist/checks-BC_0mPIq.js +37 -0
  52. package/dist/claude-agent/index.d.ts +3 -3
  53. package/dist/claude-agent/index.js +2 -2
  54. package/dist/clerk/index.d.ts +3 -3
  55. package/dist/clerk/index.js +1 -1
  56. package/dist/cli/bin.js +2 -2
  57. package/dist/cli/index.d.ts +3 -3
  58. package/dist/cli/index.js +3 -3
  59. package/dist/cloud/index.d.ts +127 -11
  60. package/dist/cloud/index.js +251 -3
  61. package/dist/{cloud-CIoHNevm.js → cloud-CO8pYtwr.js} +5 -5
  62. package/dist/{codemod-Cxvvc70T.js → codemod-BP6n8N9L.js} +3 -3
  63. package/dist/{codemod-Bmj9dWMH.js → codemod-i4N2BhDc.js} +3 -3
  64. package/dist/{collect-CrZJJnEF.js → collect-BaWeMjwV.js} +17 -8
  65. package/dist/{collect-DloHbDVa.js → collect-DNm3oKOk.js} +6 -5
  66. package/dist/collect-k_Dulxr5.js +1 -0
  67. package/dist/compile/index.d.ts +1 -1
  68. package/dist/compile/index.js +1 -1
  69. package/dist/{compile-CMqMx_IN.js → compile-BAEO5bOk.js} +2 -2
  70. package/dist/{compile-Ckm2HqBZ.d.ts → compile-COM2VKuN.d.ts} +3 -3
  71. package/dist/{completion-BqhRBJD8.js → completion-BNdVf7X2.js} +3 -3
  72. package/dist/{config-bs_kZeYq.js → config-CoQHDrDi.js} +7 -3
  73. package/dist/{config-B46rm9sc.js → config-DGZzMV1B.js} +2 -2
  74. package/dist/{config-report-DUgj62Ai.js → config-report-Cm7UITNA.js} +3 -3
  75. package/dist/connector-schema-_VCSsdtD.js +433 -0
  76. package/dist/{context-bbFHR-0D.js → context--uM9V3Ui.js} +2 -2
  77. package/dist/convex/index.d.ts +5 -5
  78. package/dist/convex/index.js +2 -2
  79. package/dist/core-B5-w6Kw9.js +335 -0
  80. package/dist/core-CQkE8Esr.d.ts +179 -0
  81. package/dist/{create-0VZ7Pdiu.d.ts → create-B2-CQqCI.d.ts} +5 -5
  82. package/dist/{create-C3b4B5eF.js → create-Cb86Z8Im.js} +9 -8
  83. package/dist/{create-DnrmLnLW.js → create-DJrlhEp9.js} +5 -5
  84. package/dist/{create-BFfm17UA.d.ts → create-QHuuPw3q.d.ts} +7 -7
  85. package/dist/{create-BDS7wc3y.d.ts → create-UTnUVETj.d.ts} +30 -10
  86. package/dist/{credential-DM2um3ha.js → credential-CIM7wHwA.js} +4 -4
  87. package/dist/{credentials-DPyHmYz5.js → credentials-BpuTwd7a.js} +1 -1
  88. package/dist/{deciding-columns-Sq12mEA6.js → deciding-columns-DRo2QM6E.js} +13 -2
  89. package/dist/{delegation-DaXVdDhM.js → delegation-C5rOdTp5.js} +5 -5
  90. package/dist/delivery/index.d.ts +5 -5
  91. package/dist/delivery/index.js +4 -3
  92. package/dist/{delivery-DCAgNyCn.js → delivery-rwOKLWRm.js} +51 -2
  93. package/dist/{denied-text-D8jDHgsl.d.ts → denied-text-D4qkEtFz.d.ts} +2 -2
  94. package/dist/{describe-BybMRY7A.js → describe-DZlnEtLy.js} +1 -0
  95. package/dist/{describe-BZUsixiL.d.ts → describe-O0SwYIK8.d.ts} +2 -2
  96. package/dist/{diff-CxZo9pTq.js → diff-Btr1c1Ow.js} +2 -2
  97. package/dist/{diff-DCePiN6M.js → diff-DzT-xD13.js} +10 -10
  98. package/dist/{doctor-DFjUxf2f.js → doctor-BU1szn63.js} +241 -49
  99. package/dist/{doctor-CxiwSfqH.js → doctor-BoK_y0KO.js} +4 -4
  100. package/dist/drizzle/index.d.ts +3 -3
  101. package/dist/drizzle/index.js +1 -1
  102. package/dist/elysia/index.d.ts +5 -5
  103. package/dist/elysia/index.js +3 -3
  104. package/dist/{errors-C2PXIv4c.d.ts → errors-B_Qg9D3e.d.ts} +2 -2
  105. package/dist/{errors-CWTY6uih.js → errors-DpP_U6Bf.js} +1 -0
  106. package/dist/{evaluate-_R8QkWaf.js → evaluate-B_oqFbWC.js} +130 -34
  107. package/dist/{evaluations-16UqqQ6W.d.ts → evaluations-BCddEOfO.d.ts} +4 -4
  108. package/dist/{evaluations-DZ2CbV5k.js → evaluations-DReoiVBw.js} +4 -4
  109. package/dist/eve/index.d.ts +5 -5
  110. package/dist/eve/index.js +3 -3
  111. package/dist/express/index.d.ts +5 -5
  112. package/dist/express/index.js +3 -3
  113. package/dist/fastify/index.d.ts +5 -5
  114. package/dist/fastify/index.js +3 -3
  115. package/dist/{fetch-adapter-qKJBuRqy.d.ts → fetch-adapter-CuhyYwC1.d.ts} +4 -4
  116. package/dist/{fetch-adapter-g0ypLkID.js → fetch-adapter-De-u6vhJ.js} +3 -3
  117. package/dist/{fields-D9tPVUUS.js → fields-ClcQLrVc.js} +48 -13
  118. package/dist/{files-C5ZI27eV.js → files-acUbNwWt.js} +15 -2
  119. package/dist/{fixtures-B7XrLeY7.js → fixtures-BTMW6zgU.js} +2 -2
  120. package/dist/{flags-C3DktBDY.js → flags-aaeRAdiD.js} +2 -2
  121. package/dist/{from-snapshot-D2lZt4Dd.js → from-snapshot-Bz469dIh.js} +17 -10
  122. package/dist/generate-3FNO5P5Q.js +159 -0
  123. package/dist/{grantee-Cn1HyUsI.js → grantee-BrRANrpT.js} +14 -4
  124. package/dist/{grantee-conditions-CcXf8hMM.js → grantee-conditions-C6P1Tvol.js} +1 -1
  125. package/dist/{grantee-match-DHArqY82.js → grantee-match-0RtHWQDx.js} +3 -3
  126. package/dist/h3/index.d.ts +4 -4
  127. package/dist/h3/index.js +3 -3
  128. package/dist/{helpers-DTcZOzhF.js → helpers-DY3vXuGs.js} +3 -3
  129. package/dist/hono/index.d.ts +5 -5
  130. package/dist/hono/index.js +3 -3
  131. package/dist/{hosted-DBDX0M-P.d.ts → hosted-BmjzxQhp.d.ts} +14 -2
  132. package/dist/{hosted-BZIAYvon.js → hosted-XBgU6RGT.js} +34 -12
  133. package/dist/{http-DXJ37RDA.js → http-Ca3Sr5x1.js} +1 -1
  134. package/dist/{import-DMmOG27O.js → import-Bz0GefWg.js} +5 -5
  135. package/dist/{import-CNV7I2Dw.js → import-DB2EG_iy.js} +3 -3
  136. package/dist/{index-CxGUswIC.d.ts → index-BE8y3moU.d.ts} +50 -8
  137. package/dist/{index-Chrs5fQ-.d.ts → index-BSfRl0hZ.d.ts} +6 -6
  138. package/dist/{index-DOVf7RJv.d.ts → index-BTkwrK7v.d.ts} +155 -17
  139. package/dist/index-CMiPH2Tv.d.ts +206 -0
  140. package/dist/{index-SGdIQyzc.d.ts → index-DH9DUDEM.d.ts} +18 -3
  141. package/dist/{index-BcHcPyIo.d.ts → index-DLwcuCN_.d.ts} +86 -7
  142. package/dist/index.d.ts +18 -17
  143. package/dist/index.js +27 -25
  144. package/dist/{init-GG6Jh8HD.js → init-5TXNji88.js} +5 -5
  145. package/dist/{init-DeMYsnYJ.js → init-B-4t1NzC.js} +4 -4
  146. package/dist/{instance-options-Omfd793B.d.ts → instance-options-CU0YQCLH.d.ts} +1 -1
  147. package/dist/{instance-snapshot-Cxq4ygFc.js → instance-snapshot-CjcwnEiV.js} +20 -14
  148. package/dist/jwt/index.d.ts +83 -5
  149. package/dist/jwt/index.js +143 -5
  150. package/dist/{kernel-5JatXyND.js → kernel-DVExUwkp.js} +138 -12
  151. package/dist/kysely/index.d.ts +3 -3
  152. package/dist/kysely/index.js +1 -1
  153. package/dist/load-DrMHXzgN.js +1 -0
  154. package/dist/{load-DFS_ry0W.js → load-DxroEzH1.js} +2 -2
  155. package/dist/{login-Dfe5RlYk.js → login-ClSJh5t5.js} +4 -4
  156. package/dist/{login-imhuwvn3.js → login-WC9HPJpU.js} +6 -10
  157. package/dist/{manifest-C8IzW3Do.js → manifest-B79YuF2T.js} +6 -2
  158. package/dist/{manifest-mx5ghSFM.d.ts → manifest-BVhExYhw.d.ts} +23 -2
  159. package/dist/{manifest-CtKR3GXL.js → manifest-Dv6q4i5s.js} +54 -35
  160. package/dist/{manifest-schema-C0kK_8V0.js → manifest-schema-Cv492Qev.js} +37 -7
  161. package/dist/{map-claims-D3oRmTk0.js → map-claims-etGIS_Dv.js} +14 -11
  162. package/dist/{map-error-TCAdw-eW.js → map-error-BQGyDm_M.js} +1 -1
  163. package/dist/mcp/index.d.ts +64 -10
  164. package/dist/mcp/index.js +151 -22
  165. package/dist/{migrate-BShP7Vf4.js → migrate-Dodel0IJ.js} +77 -24
  166. package/dist/nest/index.d.ts +5 -5
  167. package/dist/nest/index.js +3 -3
  168. package/dist/next/index.d.ts +9 -9
  169. package/dist/next/index.js +5 -5
  170. package/dist/next/plugin.d.ts +1 -1
  171. package/dist/next/plugin.js +2 -2
  172. package/dist/node/index.d.ts +5 -5
  173. package/dist/node/index.js +2 -2
  174. package/dist/openai/index.d.ts +5 -5
  175. package/dist/openai/index.js +3 -3
  176. package/dist/openapi/index.d.ts +14 -4
  177. package/dist/openapi/index.js +1 -1
  178. package/dist/{openapi-ahr8rHu3.js → openapi-DMocl1bh.js} +4 -4
  179. package/dist/{openapi-B7HZ0f-r.js → openapi-Ddb4628T.js} +2 -7
  180. package/dist/{openapi-DTaEmTSW.js → openapi-ztVaux6j.js} +8 -8
  181. package/dist/{operation-findings-DgATDpvh.js → operation-findings-kRcEp8lE.js} +1 -1
  182. package/dist/{operations-CxybZErY.d.ts → operations-BNLes61x.d.ts} +1 -1
  183. package/dist/orpc/index.d.ts +6 -6
  184. package/dist/orpc/index.js +4 -4
  185. package/dist/otel/index.d.ts +3 -3
  186. package/dist/otel/index.js +1 -1
  187. package/dist/{output-Dyw8fyPF.js → output-DgSDLrta.js} +7 -3
  188. package/dist/{output-files-DhRBBE4n.js → output-files-CCMdcFWb.js} +2 -2
  189. package/dist/output-files-DPAY7-GX.js +1 -0
  190. package/dist/{ownership-BatFgAA4.js → ownership-Z2LpxsGa.js} +1349 -883
  191. package/dist/parse-Ddhz2xf4.js +148 -0
  192. package/dist/{parse-local-manifest-CVSs6J9I.js → parse-local-manifest-T1Q8X3A2.js} +24 -1
  193. package/dist/{parse-local-manifest-DgqtruPn.d.ts → parse-local-manifest-e4mdwAKd.d.ts} +1 -1
  194. package/dist/pdp/index.d.ts +3 -3
  195. package/dist/pdp/index.js +7 -3
  196. package/dist/peer-12Q5I_RH.js +976 -0
  197. package/dist/{peer-CRvC7Hg1.js → peer-DOzzMfZL.js} +1 -1
  198. package/dist/{permdock-B9JxkBwC.js → permdock-BfWEeMqk.js} +177 -49
  199. package/dist/{permdock-D7eeLJER.d.ts → permdock-Dco6dyCK.d.ts} +34 -5
  200. package/dist/{permission-registry-Br4jSNVi.js → permission-registry-DpPtSKNZ.js} +1 -1
  201. package/dist/{vocabulary-D6lPdb4Z.d.ts → permission-types-DhNEmoRG.d.ts} +4 -62
  202. package/dist/{permissions-oEWfl-kl.d.ts → permissions-CoqCOfJ3.d.ts} +2 -1
  203. package/dist/{permissions-B-wCP5ZC.js → permissions-CyH2MV_B.js} +1 -1
  204. package/dist/{permissions-CZKiUfFM.js → permissions-DArULThq.js} +4 -4
  205. package/dist/pkce-5h8NgMQJ.js +21 -0
  206. package/dist/{policies-CO1oOymN.js → policies-B2lZIurj.js} +3 -3
  207. package/dist/{policy-Czo4n3Hs.d.ts → policy-B7dg_yzu.d.ts} +2 -2
  208. package/dist/{policy-DLZJ5gAp.js → policy-DaJy4nK4.js} +17 -8
  209. package/dist/{policy-from-catalog-DzhEc3Y9.js → policy-from-catalog-DFWYkNdu.js} +9 -9
  210. package/dist/{policy-types-DzwivBgR.d.ts → policy-types-B-F-qAZk.d.ts} +109 -5
  211. package/dist/postgres-store-BL9M_CA1.js +1001 -0
  212. package/dist/{postgrest-NUqZT-RB.d.ts → postgrest-CYacM_u7.d.ts} +12 -1
  213. package/dist/{postgrest-CW0IqC-U.js → postgrest-r5faDIzD.js} +47 -5
  214. package/dist/{powersync-BHfuehf3.js → powersync-13odBwiM.js} +3 -3
  215. package/dist/powersync-53ryOZip.js +1059 -0
  216. package/dist/powersync-CExtTLyA.js +1 -0
  217. package/dist/preset/index.js +1 -1
  218. package/dist/{preset-DZdU0tne.js → preset-CHTbd-DQ.js} +2 -2
  219. package/dist/prisma/index.d.ts +2 -2
  220. package/dist/prisma/index.js +1 -1
  221. package/dist/{problem-details-DXlt-vks.d.ts → problem-details-Tcfmj3bV.d.ts} +1 -1
  222. package/dist/prompt-DwtVemER.js +935 -0
  223. package/dist/{protected-DEPHc2xV.d.ts → protected-BwZYL1X_.d.ts} +6 -5
  224. package/dist/{protected-DfjY5mLA.js → protected-DXE3cQYg.js} +2 -2
  225. package/dist/{provider-CC-u3bYP.js → provider-BQsTYYy0.js} +2 -2
  226. package/dist/{provider-DMVt6yYn.d.ts → provider-Csk-THAn.d.ts} +1 -1
  227. package/dist/{provider-store-B5Yrkhzr.js → provider-store-IgqQipCO.js} +2 -2
  228. package/dist/{pull-BgIfno6w.js → pull-CN5OLxrF.js} +7 -7
  229. package/dist/pull-Cty6Ovyu.js +1 -0
  230. package/dist/{push-CmFVBOeI.js → push-tVdG8dUL.js} +54 -14
  231. package/dist/{pushed-policy-BImMTTIM.d.ts → pushed-policy-BbcY9r_4.d.ts} +1 -1
  232. package/dist/{pushed-policy-DobP7AHI.js → pushed-policy-CmYeSQqq.js} +1 -1
  233. package/dist/react/index.d.ts +6 -6
  234. package/dist/react/index.js +3 -3
  235. package/dist/react/provider-client.d.ts +1 -1
  236. package/dist/react/provider-client.js +1 -1
  237. package/dist/react-native/index.d.ts +25 -11
  238. package/dist/react-native/index.js +214 -24
  239. package/dist/{refs-K6eWUJ0H.js → refs-CNJBiTFo.js} +15 -1
  240. package/dist/{replay-D7iKcLFe.d.ts → replay-DCDD_ZfL.d.ts} +1 -1
  241. package/dist/{request-context-BTRTG7OT.js → request-context-BykKLmyE.js} +2 -2
  242. package/dist/resend/index.d.ts +4 -4
  243. package/dist/{retire-CALjIQ7e.js → retire-Wj6dkyDE.js} +2 -2
  244. package/dist/{rls-BA8an5Ap.js → rls-Ck_PocSp.js} +32 -8
  245. package/dist/{rls-j6t43t7n.js → rls-CxBZb4rZ.js} +478 -534
  246. package/dist/{role-builder-CXbis5-X.js → role-builder-C1VJzvcr.js} +3 -3
  247. package/dist/rolldown-runtime-C3I60J0A.js +4 -0
  248. package/dist/{rpc-aPyNi6tA.js → rpc-Bk1bE-ch.js} +1 -1
  249. package/dist/run-Bz7DXGEv.js +1 -0
  250. package/dist/{run-ticket-sxFI6LAk.js → run-ticket-B5d_63Ma.js} +22 -1
  251. package/dist/{run-ticket-BjhTOi6u.d.ts → run-ticket-BpNi7BCi.d.ts} +8 -3
  252. package/dist/{run-DOfMtc9V.js → run-uLD2aWuO.js} +7 -7
  253. package/dist/{runtime-D3VTAiCA.js → runtime-Vif3_-zf.js} +1 -1
  254. package/dist/{saas-FO-Q8oj0.js → saas-Az_Kz-4D.js} +7 -7
  255. package/dist/{scan-BOI5tJ-6.js → scan-DSljDE1Y.js} +1 -1
  256. package/dist/{schema-yLuBWWsh.js → schema-gIcCx7zM.js} +1 -1
  257. package/dist/scim/index.d.ts +3 -2
  258. package/dist/scim/index.js +3 -1
  259. package/dist/scim-C7JYhySg.js +44 -0
  260. package/dist/{scim-ClHtpyV3.js → scim-YNieBO1U.js} +3 -537
  261. package/dist/{scopes-CUPSjo9q.js → scopes-BHsc-wp-.js} +1 -1
  262. package/dist/server/index.d.ts +11 -11
  263. package/dist/server/index.js +7 -7
  264. package/dist/{server-DqCT4CgZ.js → server-Bnga6I50.js} +3 -3
  265. package/dist/{server-types-BLd4MnBX.d.ts → server-types-C4gZUEZy.d.ts} +9 -9
  266. package/dist/{shared-DvvFzYXe.js → shared-C8LjUdVU.js} +1 -1
  267. package/dist/{shared-CD11nBbC.js → shared-fMKOv8hP.js} +46 -39
  268. package/dist/{sink-Bj_lDUDq.d.ts → sink-Cp_Eot9W.d.ts} +4 -4
  269. package/dist/{skills-B6lVdD_W.js → skills-Dcjw9_1C.js} +5 -5
  270. package/dist/{skills-CTYwkl44.js → skills-g_3BS9NC.js} +3 -3
  271. package/dist/{snapshot-CXUWfsNy.js → snapshot-CbGmYiJx.js} +4 -4
  272. package/dist/{snapshot-cache-DZHQ0KF6.js → snapshot-cache-DT2Kx1F8.js} +1 -1
  273. package/dist/{snapshot-cache-DqK9eDmM.d.ts → snapshot-cache-Pahd3IYJ.d.ts} +1 -1
  274. package/dist/solid/index.d.ts +6 -5
  275. package/dist/solid/index.js +4 -4
  276. package/dist/solid-start/index.d.ts +6 -6
  277. package/dist/solid-start/index.js +3 -3
  278. package/dist/{sources-vs5dGOsS.js → sources-B-zYB4GS.js} +111 -25
  279. package/dist/{sources-BV9j0DeZ.d.ts → sources-DEEuGb8S.d.ts} +63 -14
  280. package/dist/sources-qoIJQrJv.js +11 -0
  281. package/dist/{sql-B5T1IL83.js → sql-B96movIM.js} +5 -1
  282. package/dist/sql-CDEPCA0g.d.ts +10 -0
  283. package/dist/{src-DLfUWFAI.js → src-DLFrtrrC.js} +154 -53
  284. package/dist/ssf/index.d.ts +4 -4
  285. package/dist/ssf/index.js +640 -211
  286. package/dist/{standard-webhooks-6lg8v_is.d.ts → standard-webhooks-B_qRg0ws.d.ts} +2 -2
  287. package/dist/{store-4u_hxjWa.js → store-DSP9oGvh.js} +15 -7
  288. package/dist/{store-options-BqEakYRa.js → store-options-B_poyYvg.js} +6 -6
  289. package/dist/{stores-ClZUgeoP.d.ts → stores-BAZwSYZA.d.ts} +6 -5
  290. package/dist/{stores-BbsD4fAR.js → stores-BqEuHC05.js} +2 -2
  291. package/dist/{stream-7-xmnf3U.d.ts → stream-2Vwhl7Er.d.ts} +2 -2
  292. package/dist/{style-Uao76wO7.js → style-Cqo7-73x.js} +1 -0
  293. package/dist/{style-DiKWnAvn.js → style-Ds1gNm77.js} +1 -1
  294. package/dist/supabase/index.d.ts +8 -7
  295. package/dist/supabase/index.js +5 -5
  296. package/dist/supabase/middleware.d.ts +6 -6
  297. package/dist/supabase/middleware.js +6 -6
  298. package/dist/{supabase-Dxlp8A94.js → supabase--0aJdLEd.js} +9 -7
  299. package/dist/{supabase-9pqI57pW.js → supabase-Bqp7iIY2.js} +3 -3
  300. package/dist/supabase-hook-DJxC4PoP.js +1 -0
  301. package/dist/{supabase-hook-DdDQhMlw.js → supabase-hook-Dg0SrQWR.js} +1069 -123
  302. package/dist/supautils-MESd81bF.js +689 -0
  303. package/dist/{support-session-C6xJQYut.js → support-session-Cna49-3f.js} +2 -2
  304. package/dist/svelte/index.d.ts +5 -5
  305. package/dist/svelte/index.js +5 -5
  306. package/dist/svelte/runtime.d.ts +3 -3
  307. package/dist/svelte/runtime.js +3 -3
  308. package/dist/svelte/source.d.ts +5 -5
  309. package/dist/svelte/source.js +2 -2
  310. package/dist/sveltekit/index.d.ts +6 -6
  311. package/dist/sveltekit/index.js +2 -2
  312. package/dist/tanstack-start/index.d.ts +6 -6
  313. package/dist/tanstack-start/index.js +2 -2
  314. package/dist/{tenancy-DN2QAx9z.js → tenancy-CmeBQ6Nv.js} +12 -3
  315. package/dist/terminal/index.d.ts +6 -6
  316. package/dist/terminal/index.js +3 -3
  317. package/dist/testing/core.d.ts +2 -0
  318. package/dist/testing/core.js +1 -0
  319. package/dist/testing/fixtures/index.d.ts +12 -0
  320. package/dist/testing/fixtures/index.js +770 -0
  321. package/dist/testing/index.d.ts +41 -174
  322. package/dist/testing/index.js +241 -731
  323. package/dist/testing/saas/index.d.ts +5 -5
  324. package/dist/testing/saas/index.js +2 -2
  325. package/dist/testing/saas/permissions.d.ts +1 -1
  326. package/dist/testing/saas/permissions.js +1 -1
  327. package/dist/text-diff-DhITNCnD.js +444 -0
  328. package/dist/{token-BLZcjQgt.js → token-2qYDvXz2.js} +1 -1
  329. package/dist/{tool-refusal-CMLN5xhn.js → tool-refusal-reAhIzdl.js} +1 -1
  330. package/dist/trpc/index.d.ts +6 -6
  331. package/dist/trpc/index.js +3 -3
  332. package/dist/{types-5Ut_pbKy.d.ts → types-BWZybqs9.d.ts} +112 -8
  333. package/dist/{types-CZTnPFzM.d.ts → types-BXMYB7BY.d.ts} +2 -2
  334. package/dist/{types-CdlxsYQh.d.ts → types-BfVmNJO5.d.ts} +14 -3
  335. package/dist/{types-BqAKcRNW.d.ts → types-CIeZcG8v.d.ts} +2 -2
  336. package/dist/{types-BNORmDM8.d.ts → types-CKV71kKh.d.ts} +4 -4
  337. package/dist/{types-tIgIeIKJ.d.ts → types-CQVEQawv.d.ts} +4 -4
  338. package/dist/{types-BjV-fqcA.d.ts → types-CeIscIKp.d.ts} +4 -4
  339. package/dist/{types-_sHy8BQf.d.ts → types-CfiKC5aN.d.ts} +1 -1
  340. package/dist/{types-ClZaWzfb.d.ts → types-D3A19w4A.d.ts} +8 -4
  341. package/dist/{types-C8-KNsFf.d.ts → types-DBHftjgS.d.ts} +15 -8
  342. package/dist/{types-CCpc2Dcv.d.ts → types-DC44-hqb.d.ts} +2 -2
  343. package/dist/{types-fbZ7JEXr.d.ts → types-DXmwu78Z.d.ts} +4 -4
  344. package/dist/{types-DiQjupG2.d.ts → types-cTos-H7p.d.ts} +2 -2
  345. package/dist/{types-DX0LaKTb.d.ts → types-g7Oy6Zg4.d.ts} +3 -3
  346. package/dist/unplugin/index.d.ts +1 -1
  347. package/dist/unplugin/index.js +2 -2
  348. package/dist/{usage-T5mXq98v.js → usage-Bybmck7f.js} +7 -7
  349. package/dist/{usage-DHtE8j4E.js → usage-CDaLXCfL.js} +2 -2
  350. package/dist/usage-y-kBNdqs.js +1 -0
  351. package/dist/{validation-error-Bg5Tpluk.d.ts → validation-error-CQpoquwj.d.ts} +1 -1
  352. package/dist/{verify-DbCL9bnB.js → verify-pe8rL_Qw.js} +350 -83
  353. package/dist/{views-DTIIO-oQ.js → views-BB8E9dTP.js} +1 -1
  354. package/dist/vocabulary-Bg3eeT2H.d.ts +67 -0
  355. package/dist/vue/index.d.ts +6 -5
  356. package/dist/vue/index.js +4 -4
  357. package/dist/{waiter-CKy4MQSY.d.ts → waiter-DwEyouan.d.ts} +2 -2
  358. package/dist/watch-Bd9mx3rd.js +1 -0
  359. package/dist/{watch-CszoTJMq.js → watch-C-4VmFlL.js} +3 -3
  360. package/dist/webmcp/index.d.ts +2 -2
  361. package/dist/webmcp/index.js +2 -2
  362. package/dist/{where-scope-Dv-WTP-w.js → where-scope-wq2coopv.js} +41 -21
  363. package/dist/{who-can-D0QoYbEb.js → who-can-BwNsI_Js.js} +6 -6
  364. package/dist/workflow/index.d.ts +3 -3
  365. package/dist/workflow/index.js +5 -5
  366. package/dist/workflow-sdk/index.d.ts +5 -5
  367. package/dist/{wrap-Bd1Tejur.d.ts → wrap-B6R0cMRL.d.ts} +2 -2
  368. package/dist/{wrap-gX61EtpB.js → wrap-CiSc_KCq.js} +8 -2
  369. package/package.json +22 -11
  370. package/schemas/cli-result-v1.json +1 -0
  371. package/schemas/cloud-api-v1.json +152 -7
  372. package/schemas/connector-v1.json +8 -0
  373. package/schemas/supabase-claims-v1.json +5 -0
  374. package/schemas/supabase-manifest-v1.json +276 -75
  375. package/skills/permdock/SKILL.md +1 -0
  376. package/skills/permdock-agents/references/runtimes.md +3 -1
  377. package/skills/permdock-approvals/references/stores-and-resume.md +2 -1
  378. package/skills/permdock-data/references/queries-and-rls.md +5 -2
  379. package/skills/permdock-tenancy/SKILL.md +1 -1
  380. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +2 -0
  381. package/skills/permdock-wire/references/adapters.md +14 -0
  382. package/skills/permdock-wire/references/policy-options.md +1 -0
  383. package/dist/catalog-DjhMYYCq.js +0 -1
  384. package/dist/collect-FlXJlhus.js +0 -1
  385. package/dist/index-D8EovfsP.d.ts +0 -91
  386. package/dist/load-BxEtwlBS.js +0 -1
  387. package/dist/output-files-B3IZJksC.js +0 -1
  388. package/dist/parse-Cxq0WxrA.js +0 -53
  389. package/dist/peer-C8swJ2GH.js +0 -50
  390. package/dist/powersync-CbzkqL_M.js +0 -1
  391. package/dist/powersync-bYUDLOAO.js +0 -538
  392. package/dist/prompt-DfZLJLIn.js +0 -32
  393. package/dist/pull-ByOks_Tg.js +0 -1
  394. package/dist/run-CNsApDpd.js +0 -1
  395. package/dist/supabase-hook-CXcGpHqq.js +0 -1
  396. package/dist/supautils-B6HYL9eL.js +0 -91
  397. package/dist/text-diff-BQcUapOb.js +0 -15
  398. package/dist/usage-CHOtwL19.js +0 -1
  399. package/dist/watch-Cb-NcaUw.js +0 -1
@@ -0,0 +1,1059 @@
1
+ import { t as compact } from "./compact-CAf557ZB.js";
2
+ import { c as isConditionDate, l as isConditionRef } from "./refs-CNJBiTFo.js";
3
+ import { A as memoryCustomRoleSource, g as localSnapshotManifest } from "./src-DLFrtrrC.js";
4
+ import { n as findPermission } from "./permission-registry-DpPtSKNZ.js";
5
+ import { f as scopeList, s as resolveScope } from "./scopes-BHsc-wp-.js";
6
+ import { c as policyLevels } from "./policy-DaJy4nK4.js";
7
+ import { t as requiresApproval } from "./approval-required-TzNB9oQA.js";
8
+ import { t as getPermDock } from "./permdock-BfWEeMqk.js";
9
+ import { u as usageResult } from "./errors-DpP_U6Bf.js";
10
+ import { c as requirePolicy } from "./load-DxroEzH1.js";
11
+ import { s as quoteSqlLiteral, t as SQL_IDENT } from "./sql-B96movIM.js";
12
+ import { i as scopeMembershipTable, r as scopeColumn } from "./compile-BAEO5bOk.js";
13
+ import "./sources-B-zYB4GS.js";
14
+ import { t as applyFiles } from "./files-acUbNwWt.js";
15
+ import { i as fileDrift } from "./output-files-CCMdcFWb.js";
16
+ import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-BTMW6zgU.js";
17
+ import { O as quoteIdent, a as customRolesOn, t as CUSTOM_ROLES } from "./shared-fMKOv8hP.js";
18
+ import { F as collectGrants, I as roleNames, L as andConditions, M as assignableRoles, t as ownershipRules, z as contextRefs } from "./ownership-Z2LpxsGa.js";
19
+ import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
20
+ import { f as pgConnector } from "./policies-B2lZIurj.js";
21
+ import { d as tableNames, t as canFixture, x as tableFor } from "./verify-pe8rL_Qw.js";
22
+ import { resolve } from "node:path";
23
+ //#region src/cli/powersync/names.ts
24
+ function ident(name) {
25
+ if (!SQL_IDENT.test(name)) throw new TypeError(`PermDock CLI: '${name}' is not a plain identifier, which Sync Streams need`);
26
+ return name;
27
+ }
28
+ function parts(table) {
29
+ const dot = table.lastIndexOf(".");
30
+ const schema = dot === -1 ? void 0 : ident(table.slice(0, dot));
31
+ const name = ident(table.slice(dot + 1));
32
+ return [schema === "public" ? void 0 : schema, name];
33
+ }
34
+ /**
35
+ * The name a stream query and the device use for the table: its own name,
36
+ * or `<schema>_<table>` for a table outside `public` that shares its name
37
+ * with another synced table, since PowerSync drops the schema on the device.
38
+ */
39
+ function bare(table, named) {
40
+ const [schema, name] = parts(table);
41
+ const key = `${schema ?? "public"}.${name}`;
42
+ named.names?.seen.add(key);
43
+ return named.names?.renamed.get(key) ?? name;
44
+ }
45
+ /** The table as a stream query reads it: Sync Streams need a schema other than `public` in double quotes. */
46
+ function quotedTable(table) {
47
+ const [schema, name] = parts(table);
48
+ return schema === void 0 ? name : `"${schema}"."${name}"`;
49
+ }
50
+ /** The table in a `FROM` or `JOIN`, aliased to its `bare` name outside `public`. */
51
+ function fromTable(table, named) {
52
+ const [schema] = parts(table);
53
+ return schema === void 0 ? bare(table, named) : `${quotedTable(table)} AS ${bare(table, named)}`;
54
+ }
55
+ /** `<schema>_<table>` for each table outside `public` whose name another referenced table shares. */
56
+ function renames(seen) {
57
+ const byName = /* @__PURE__ */ new Map();
58
+ for (const table of seen) {
59
+ const name = table.slice(table.indexOf(".") + 1);
60
+ byName.set(name, [...byName.get(name) ?? [], table]);
61
+ }
62
+ const out = /* @__PURE__ */ new Map();
63
+ for (const tables of byName.values()) {
64
+ if (tables.length < 2) continue;
65
+ for (const table of tables) if (!table.startsWith("public.")) out.set(table, table.replace(".", "_"));
66
+ }
67
+ return out;
68
+ }
69
+ /** The role label of a custom role's grants in warnings and gaps. */
70
+ const CUSTOM_ROLE = "custom role";
71
+ //#endregion
72
+ //#region src/cli/powersync/sources.ts
73
+ /** The sources that can hold scope `name`: every table source and the junction sources of that scope. */
74
+ function scopeSourcesOf(sources, name) {
75
+ return sources.filter((source) => source.sql.scope === void 0 || source.sql.scope === name);
76
+ }
77
+ /** The source's stream form, or why its rows cannot filter a stream. */
78
+ function streamOf(source) {
79
+ const { stream } = source.sql;
80
+ if (stream === void 0) return `the ${source.sql.table} membership source has no stream form: build it with tableMembershipSource or junctionMembershipSource`;
81
+ if (stream.expiresAt !== void 0) return `the ${stream.table} membership source has no stream: it has an expiry`;
82
+ if (stream.disabledAt !== void 0) return `the ${stream.table} membership source has no stream: it has disabledAt`;
83
+ return stream;
84
+ }
85
+ function pairOf(through) {
86
+ const [pair] = Object.entries(through.on);
87
+ if (pair === void 0) throw new TypeError(`PermDock CLI: the reference to ${through.through} names no column`);
88
+ return pair;
89
+ }
90
+ function isFixed(roles) {
91
+ return typeof roles === "object" && "value" in roles;
92
+ }
93
+ function isSourceList(roles) {
94
+ return Array.isArray(roles);
95
+ }
96
+ function roleSources(roles) {
97
+ return isSourceList(roles) ? roles : [roles];
98
+ }
99
+ function anyOf(clauses) {
100
+ return clauses.length === 1 ? clauses[0] ?? "" : `(${clauses.join(" OR ")})`;
101
+ }
102
+ /** The user's own rows of the source, of scope `scope` when the source has a scope column. */
103
+ function ownRows(stream, dialect, scope) {
104
+ const table = bare(stream.table, dialect);
105
+ const scoped = scope !== void 0 && "column" in stream.scope ? [`${dialect.column(table, stream.scope.column)} = ${dialect.literal(scope)}`] : [];
106
+ if (typeof stream.user === "string") return {
107
+ table,
108
+ from: fromTable(stream.table, dialect),
109
+ where: [`${dialect.column(table, stream.user)} = ${dialect.user}`, ...scoped]
110
+ };
111
+ const users = bare(stream.user.through, dialect);
112
+ const [ref, id] = pairOf(stream.user);
113
+ return {
114
+ table,
115
+ from: `${fromTable(stream.table, dialect)} INNER JOIN ${fromTable(stream.user.through, dialect)} ON ${dialect.column(users, id)} = ${dialect.column(table, ref)}`,
116
+ where: [`${dialect.column(users, stream.user.column)} = ${dialect.user}`, ...scoped]
117
+ };
118
+ }
119
+ /** The filter of one role source for role `role`; `undefined` when the source never holds it. */
120
+ function roleFilter(stream, source, role, table, dialect) {
121
+ if (typeof source === "string") return {
122
+ join: "",
123
+ where: `${dialect.column(table, source)} = ${dialect.literal(role)}`
124
+ };
125
+ if ("map" in source) {
126
+ const values = Object.entries(source.map).filter(([, key]) => key === role).map(([value]) => `${dialect.column(table, source.column)} = ${dialect.literal(value)}`);
127
+ return values.length === 0 ? void 0 : {
128
+ join: "",
129
+ where: anyOf(values)
130
+ };
131
+ }
132
+ const keys = bare(source.through, dialect);
133
+ const [ref, id] = pairOf(source);
134
+ const { guard } = stream;
135
+ const guarded = guard !== void 0 && bare(guard.table, dialect) === keys && guard.declared.includes(role) ? [anyOf([`${dialect.column(keys, guard.tenant)} IS NULL`, ...guard.skip === void 0 ? [] : [`${dialect.column(keys, guard.skip)} = ${dialect.literal(true)}`]])] : [];
136
+ return {
137
+ join: ` INNER JOIN ${fromTable(source.through, dialect)} ON ${dialect.column(keys, id)} = ${dialect.column(table, ref)}`,
138
+ where: [`${dialect.column(keys, source.column)} = ${dialect.literal(role)}`, ...guarded].join(" AND ")
139
+ };
140
+ }
141
+ /** The kinds filter of role `role`: `[]` for none, `undefined` when no row of the source has one of its kinds. */
142
+ function kindFilter(stream, role, table, ctx) {
143
+ const kinds = ctx.kinds[role];
144
+ if (kinds === void 0) return [];
145
+ const { via } = stream;
146
+ if (via === void 0) return;
147
+ if ("value" in via) return kinds.includes(via.value) ? [] : void 0;
148
+ return kinds.length === 0 ? void 0 : [anyOf(kinds.map((kind) => `${ctx.dialect.column(table, via.column)} = ${ctx.dialect.literal(kind)}`))];
149
+ }
150
+ /**
151
+ * One `IN (SELECT …)` per role source of the stream that can hold one of
152
+ * `roles` in scope `scope`: the row's `left` is an instance the user holds it
153
+ * in. A null role never equals a key, so a row without a role grants nothing.
154
+ */
155
+ function sourceIn(stream, scope, left, roles, ctx) {
156
+ const own = ownRows(stream, ctx.dialect, scope);
157
+ const select = (join, filters) => `${left} IN (SELECT ${ctx.dialect.column(own.table, stream.id)} FROM ${own.from}${join} WHERE ${[...own.where, ...filters].join(" AND ")})`;
158
+ const out = [];
159
+ for (const role of roles) {
160
+ const kinds = kindFilter(stream, role, own.table, ctx);
161
+ if (kinds === void 0) continue;
162
+ if (isFixed(stream.roles)) {
163
+ if (stream.roles.value.includes(role)) out.push([select("", kinds)]);
164
+ continue;
165
+ }
166
+ for (const source of roleSources(stream.roles)) {
167
+ const filter = roleFilter(stream, source, role, own.table, ctx.dialect);
168
+ if (filter !== void 0) out.push([select(filter.join, [...kinds, filter.where])]);
169
+ }
170
+ }
171
+ return out;
172
+ }
173
+ /** The roles tables the source joins through, each with the query of the rows the user's own rows reference. */
174
+ function roleTableRows(stream, dialect) {
175
+ if (isFixed(stream.roles)) return [];
176
+ const own = ownRows(stream, dialect);
177
+ return roleSources(stream.roles).flatMap((source) => {
178
+ if (typeof source === "string" || "map" in source) return [];
179
+ const keys = bare(source.through, dialect);
180
+ const [ref, id] = pairOf(source);
181
+ return [{
182
+ table: keys,
183
+ query: `SELECT * FROM ${fromTable(source.through, dialect)} WHERE ${dialect.column(keys, id)} IN (SELECT ${dialect.column(own.table, ref)} FROM ${own.from} WHERE ${own.where.join(" AND ")})`
184
+ }];
185
+ });
186
+ }
187
+ //#endregion
188
+ //#region src/cli/powersync/rows.ts
189
+ function throughColumn(through, device) {
190
+ const [pair] = Object.entries(through.on);
191
+ return {
192
+ column: pair?.[0] ?? "",
193
+ through: {
194
+ table: device(through.through),
195
+ id: pair?.[1] ?? "",
196
+ column: through.column
197
+ }
198
+ };
199
+ }
200
+ function roleColumn(source, device) {
201
+ if (typeof source === "string") return { column: source };
202
+ return "map" in source ? {
203
+ column: source.column,
204
+ map: source.map
205
+ } : throughColumn(source, device);
206
+ }
207
+ function roleColumns(roles, device) {
208
+ return roleSources(roles).map((source) => roleColumn(source, device));
209
+ }
210
+ function sourceRows(stream, device) {
211
+ return compact({
212
+ table: device(stream.table),
213
+ user: typeof stream.user === "string" ? { column: stream.user } : throughColumn(stream.user, device),
214
+ scope: stream.scope,
215
+ id: stream.id,
216
+ roles: isFixed(stream.roles) ? { value: [...stream.roles.value] } : roleColumns(stream.roles, device),
217
+ via: stream.via,
218
+ expiresAt: stream.expiresAt,
219
+ disabledAt: stream.disabledAt
220
+ });
221
+ }
222
+ function tableRows(table, scope, id, device) {
223
+ return compact({
224
+ table: device(table.table),
225
+ user: { column: table.user },
226
+ scope: { value: scope },
227
+ id,
228
+ roles: roleColumns(table.role, device),
229
+ via: table.via === void 0 ? void 0 : typeof table.via === "string" ? { column: table.via } : table.via,
230
+ expiresAt: table.expiresAt,
231
+ disabledAt: table.disabledAt
232
+ });
233
+ }
234
+ /**
235
+ * The manifest's `rows`: the tables the subject streams sync, as a device
236
+ * reads them. A scope's `rls.memberships` table, else its membership
237
+ * sources; the global roles table; and the custom-role tables in `database`
238
+ * mode. `devices` holds the plan's renamed tables.
239
+ */
240
+ function localRows(policy, config, devices) {
241
+ const names = { names: {
242
+ renamed: devices,
243
+ seen: /* @__PURE__ */ new Set()
244
+ } };
245
+ const device = (table) => bare(table, names);
246
+ const rls = config.rls;
247
+ const scopes = scopeList(policy.scopes);
248
+ const sources = membershipSourcesOf(config);
249
+ const memberships = [];
250
+ const seen = /* @__PURE__ */ new Set();
251
+ for (const scope of scopes) {
252
+ const table = scopeMembershipTable(rls?.memberships, scopes, scope.name);
253
+ if (table !== void 0) {
254
+ const id = scopeColumn(table, scopes, scope.name);
255
+ if (id !== void 0) memberships.push(tableRows(table, scope.name, id, device));
256
+ continue;
257
+ }
258
+ for (const source of scopeSourcesOf(sources, scope.name)) {
259
+ const { stream } = source.sql;
260
+ if (stream !== void 0 && !seen.has(stream)) {
261
+ seen.add(stream);
262
+ memberships.push(sourceRows(stream, device));
263
+ }
264
+ }
265
+ }
266
+ const schema = rls?.schema ?? "permdock";
267
+ const database = rls?.authorize === "database";
268
+ const global = rls?.roles ?? (database ? { table: `${schema}.user_roles` } : void 0);
269
+ return compact({
270
+ memberships,
271
+ roles: global === void 0 ? void 0 : {
272
+ table: device(global.table),
273
+ user: global.user ?? "user_id",
274
+ roles: [roleColumn(global.role ?? "role", device)]
275
+ },
276
+ customRoles: database && customRolesOn(rls) ? {
277
+ permissions: device(`${schema}.${CUSTOM_ROLES.permissions}`),
278
+ includes: device(`${schema}.${CUSTOM_ROLES.includes}`)
279
+ } : void 0
280
+ });
281
+ }
282
+ //#endregion
283
+ //#region src/cli/powersync/levels.ts
284
+ const ALIAS = "cr";
285
+ /** The role key column of one role source, with the join to its roles table; `undefined` for a mapped column, which holds declared roles only. */
286
+ function roleKeyOf(role, table, dialect) {
287
+ if (typeof role === "string") return {
288
+ join: "",
289
+ roleKey: dialect.column(table, role)
290
+ };
291
+ if ("map" in role) return;
292
+ const [pair] = Object.entries(role.on);
293
+ if (pair === void 0) return;
294
+ const keys = bare(role.through, dialect);
295
+ return {
296
+ join: ` INNER JOIN ${fromTable(role.through, dialect)} ON ${dialect.column(keys, pair[1])} = ${dialect.column(table, pair[0])}`,
297
+ roleKey: dialect.column(keys, role.column)
298
+ };
299
+ }
300
+ /**
301
+ * The `IN (SELECT …)` of each root-scope source: the row's `left` is a tenant
302
+ * where the user holds a custom role with an allow row of `key` at `level`
303
+ * (`null` for an allow with no level). A role column the source maps or
304
+ * fixes holds declared roles only, so it never names a custom role.
305
+ */
306
+ function customRoleIn(ctx, root, left, key, level) {
307
+ const { dialect } = ctx;
308
+ const rls = ctx.config.rls;
309
+ const table = quotedTable(`${rls?.schema ?? "permdock"}.${CUSTOM_ROLES.permissions}`);
310
+ const cr = (column) => dialect.column(ALIAS, column);
311
+ const out = [];
312
+ for (const source of scopeSourcesOf(ctx.sources, root)) {
313
+ const stream = streamOf(source);
314
+ if (typeof stream === "string") {
315
+ ctx.gaps.push(stream);
316
+ continue;
317
+ }
318
+ if (isFixed(stream.roles)) continue;
319
+ const own = ownRows(stream, dialect, root);
320
+ const id = dialect.column(own.table, stream.id);
321
+ for (const role of roleSources(stream.roles)) {
322
+ const keyed = roleKeyOf(role, own.table, dialect);
323
+ if (keyed === void 0) continue;
324
+ const { join, roleKey } = keyed;
325
+ const filters = [
326
+ ...own.where,
327
+ `${cr("tenant_id")} = ${id}`,
328
+ `${cr("scope")} = ${dialect.literal(root)}`,
329
+ `${cr("permission")} = ${dialect.literal(key)}`,
330
+ `${cr("effect")} = ${dialect.literal("allow")}`,
331
+ level === null ? `${cr("level")} IS NULL` : `${cr("level")} = ${dialect.literal(level)}`
332
+ ];
333
+ out.push([`${left} IN (SELECT ${id} FROM ${own.from}${join} INNER JOIN ${table} AS ${ALIAS} ON ${cr("role")} = ${roleKey} WHERE ${filters.join(" AND ")})`]);
334
+ }
335
+ }
336
+ return out;
337
+ }
338
+ /**
339
+ * What a custom role can reach on `resource` through `key`, in `database`
340
+ * mode when the root scope reads membership sources: each allow of `key` by
341
+ * an assignable role at the root scope (the ceiling), once for an allow with
342
+ * no level and once per level the resource declares, narrowed by that
343
+ * level's condition. `skipped` says why a root scope with custom roles
344
+ * reaching `key` has no custom-role stream.
345
+ */
346
+ function customRoleItems(ctx, grants, resource, key, table) {
347
+ const rls = ctx.config.rls;
348
+ const root = ctx.scopes[0]?.name;
349
+ const field = ctx.policy.scopes.find((scope) => scope.name === root)?.key;
350
+ if (rls?.authorize !== "database" || !customRolesOn(rls) || root === void 0 || field === void 0 || !SQL_IDENT.test(field)) return { items: [] };
351
+ const assignable = new Set(assignableRoles(ctx.policy));
352
+ const ceiling = grants.filter((item) => item.access.kind === "role" && item.access.scope === root && assignable.has(item.access.role) && item.grant.effect === "allow" && item.grant.permission.key === key);
353
+ if (ceiling.length === 0) return { items: [] };
354
+ if (scopeMembershipTable(rls.memberships, ctx.scopes, root) !== void 0) return {
355
+ items: [],
356
+ skipped: `the ${root} scope reads an rls.memberships table; custom-role streams read rls.membershipSources`
357
+ };
358
+ const left = ctx.dialect.column(table, field);
359
+ return { items: [[null, void 0], ...policyLevels(ctx.policy, resource)].flatMap(([level, condition]) => {
360
+ const access = customRoleIn(ctx, root, left, key, level);
361
+ const label = `${CUSTOM_ROLE}/${key}${level === null ? "" : `@${level}`}`;
362
+ return access.length === 0 ? [] : ceiling.map((item) => {
363
+ const where = andConditions(item.where, condition);
364
+ return {
365
+ label,
366
+ level,
367
+ access,
368
+ item: where === void 0 ? item : {
369
+ ...item,
370
+ where
371
+ }
372
+ };
373
+ });
374
+ }) };
375
+ }
376
+ //#endregion
377
+ //#region src/cli/powersync/streams.ts
378
+ const POWERSYNC = {
379
+ user: "auth.user_id()",
380
+ claim: (name) => `auth.parameter(${quoteSqlLiteral(name)})`,
381
+ claims: (name) => `(SELECT value FROM json_each(auth.parameter(${quoteSqlLiteral(name)})))`,
382
+ column: (table, name) => `${table}.${ident(name)}`,
383
+ literal: (value) => typeof value === "string" ? quoteSqlLiteral(value) : String(value)
384
+ };
385
+ const POSTGRES = {
386
+ user: "$1::text",
387
+ claim: (name) => `($2::jsonb ->> ${quoteSqlLiteral(name)})`,
388
+ claims: (name) => `(select jsonb_array_elements_text(case jsonb_typeof($2::jsonb -> ${quoteSqlLiteral(name)}) when 'array' then $2::jsonb -> ${quoteSqlLiteral(name)} else '[]'::jsonb end))`,
389
+ column: (table, name, numeric) => `${table}.${ident(name)}${numeric === true ? "" : "::text"}`,
390
+ literal: (value) => typeof value === "number" ? String(value) : quoteSqlLiteral(String(value))
391
+ };
392
+ /** At most this many queries per stream; a wider `or` product is left out rather than truncated. */
393
+ const MAX_QUERIES = 32;
394
+ /** Every combination of one clause list from each input, ANDed. */
395
+ function product(lists) {
396
+ let out = [[]];
397
+ for (const list of lists) out = out.flatMap((left) => list.map((right) => left.concat(right)));
398
+ return out;
399
+ }
400
+ function every(parts) {
401
+ const out = [];
402
+ for (const part of parts) {
403
+ if (part === void 0) return;
404
+ out.push(part);
405
+ }
406
+ return out;
407
+ }
408
+ /** A row value: a literal, or a principal ref the token carries. `undefined` when neither. */
409
+ function valueSql(value, ctx) {
410
+ if (isConditionRef(value)) {
411
+ if (value.ref === "principal.id") return ctx.dialect.user;
412
+ const claim = /^principal\.claims\.([A-Za-z_][A-Za-z0-9_]*)$/u.exec(value.ref)?.[1];
413
+ return claim === void 0 ? void 0 : ctx.dialect.claim(claim);
414
+ }
415
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return ctx.dialect.literal(value);
416
+ }
417
+ const COMPARE = {
418
+ eq: "=",
419
+ ne: "!=",
420
+ gt: ">",
421
+ gte: ">=",
422
+ lt: "<",
423
+ lte: "<="
424
+ };
425
+ /**
426
+ * A row condition as alternatives of ANDed clauses over `table`. `undefined`
427
+ * when part of it has no Sync Streams form, so the grant syncs nothing.
428
+ */
429
+ function conditionSql(condition, table, ctx) {
430
+ const column = (field) => SQL_IDENT.test(field) ? ctx.dialect.column(table, field) : void 0;
431
+ switch (condition.op) {
432
+ case "eq":
433
+ case "ne":
434
+ case "gt":
435
+ case "gte":
436
+ case "lt":
437
+ case "lte": {
438
+ const left = typeof condition.value === "number" && SQL_IDENT.test(condition.field) ? ctx.dialect.column(table, condition.field, true) : column(condition.field);
439
+ if (condition.value === null && left !== void 0) return condition.op === "eq" ? [[`${left} IS NULL`]] : condition.op === "ne" ? [[`${left} IS NOT NULL`]] : void 0;
440
+ const right = valueSql(condition.value, ctx);
441
+ return left === void 0 || right === void 0 ? void 0 : [[`${left} ${COMPARE[condition.op]} ${right}`]];
442
+ }
443
+ case "isNull": {
444
+ const left = column(condition.field);
445
+ return left === void 0 ? void 0 : [[`${left} IS ${condition.value ? "" : "NOT "}NULL`]];
446
+ }
447
+ case "in":
448
+ case "notIn": {
449
+ const left = column(condition.field);
450
+ if (left === void 0) return;
451
+ const negate = condition.op === "notIn" ? "NOT " : "";
452
+ if (isConditionRef(condition.value)) {
453
+ const claim = /^principal\.claims\.([A-Za-z_][A-Za-z0-9_]*)$/u.exec(condition.value.ref)?.[1];
454
+ return claim === void 0 || negate !== "" ? void 0 : [[`${left} IN ${ctx.dialect.claims(claim)}`]];
455
+ }
456
+ const values = condition.value.map((value) => isConditionRef(value) || isConditionDate(value) || Array.isArray(value) ? void 0 : value === null ? void 0 : valueSql(value, ctx));
457
+ if (values.some((value) => value === void 0)) return;
458
+ return values.length === 0 ? negate === "" ? [] : [[]] : [[`${left} ${negate}IN (${values.join(", ")})`]];
459
+ }
460
+ case "and":
461
+ case "or": {
462
+ const parts = every(condition.conditions.map((child) => conditionSql(child, table, ctx)));
463
+ if (parts === void 0) return;
464
+ return condition.op === "and" ? product(parts) : parts.flat();
465
+ }
466
+ case "memberOf": {
467
+ if (condition.scope === "resource" || condition.parents !== void 0) return;
468
+ const scope = resolveScope(ctx.scopes, condition.scope);
469
+ const left = column(condition.field);
470
+ if (scope === void 0 || left === void 0) return;
471
+ return membershipSql(scope, left, condition.roles, ctx);
472
+ }
473
+ case "sqlFunction": return conditionSql(condition.twin, table, ctx);
474
+ case "not":
475
+ case "contains":
476
+ case "related":
477
+ case "opaque":
478
+ case "liveSession": return;
479
+ default: return condition;
480
+ }
481
+ }
482
+ /** One `IN (SELECT …)` per role source of the membership table: the row's `left` is an instance the user holds `roles` in. */
483
+ function membershipIn(table, idColumn, left, roles, ctx) {
484
+ if (table.expiresAt !== void 0 || table.disabledAt !== void 0) return;
485
+ const name = bare(table.table, ctx.dialect);
486
+ const sources = Array.isArray(table.role) ? table.role : [table.role];
487
+ const kinds = [...new Set(roles.flatMap((role) => ctx.kinds[role] ?? []))];
488
+ const restricted = roles.some((role) => ctx.kinds[role] !== void 0);
489
+ if (restricted && (table.via === void 0 || roles.length !== 1)) return;
490
+ const via = table.via;
491
+ if (restricted && typeof via === "object" && !kinds.includes(via.value)) return [];
492
+ const filters = [`${ctx.dialect.column(name, table.user)} = ${ctx.dialect.user}`, ...restricted && typeof via === "string" ? [`${ctx.dialect.column(name, via)} IN (${kinds.map((kind) => ctx.dialect.literal(kind)).join(", ")})`] : []];
493
+ const list = roles.map((role) => ctx.dialect.literal(role)).join(", ");
494
+ return sources.map((source) => {
495
+ if (typeof source === "string") return [`${left} IN (SELECT ${ctx.dialect.column(name, idColumn)} FROM ${fromTable(table.table, ctx.dialect)} WHERE ${[...filters, `${ctx.dialect.column(name, source)} IN (${list})`].join(" AND ")})`];
496
+ if ("map" in source) {
497
+ const values = Object.entries(source.map).filter(([, key]) => key !== null && roles.includes(key)).map(([value]) => ctx.dialect.literal(value));
498
+ if (values.length === 0) return ["1 = 0"];
499
+ return [`${left} IN (SELECT ${ctx.dialect.column(name, idColumn)} FROM ${fromTable(table.table, ctx.dialect)} WHERE ${[...filters, `${ctx.dialect.column(name, source.column)} IN (${values.join(", ")})`].join(" AND ")})`];
500
+ }
501
+ const [pair] = Object.entries(source.on);
502
+ const keys = bare(source.through, ctx.dialect);
503
+ const [ref, id] = pair ?? ["", ""];
504
+ return [`${left} IN (SELECT ${ctx.dialect.column(name, idColumn)} FROM ${fromTable(table.table, ctx.dialect)} INNER JOIN ${fromTable(source.through, ctx.dialect)} ON ${ctx.dialect.column(keys, id)} = ${ctx.dialect.column(name, ref)} WHERE ${[...filters, `${ctx.dialect.column(keys, source.column)} IN (${list})`].join(" AND ")})`];
505
+ });
506
+ }
507
+ /** The membership filter of scope `scope`: its `rls.memberships` table, else the union of its membership sources. */
508
+ function membershipSql(scope, left, roles, ctx) {
509
+ const table = scopeMembershipTable(ctx.config.rls?.memberships, ctx.scopes, scope);
510
+ if (table !== void 0) {
511
+ const idColumn = scopeColumn(table, ctx.scopes, scope);
512
+ return idColumn === void 0 ? void 0 : membershipIn(table, idColumn, left, roles, ctx);
513
+ }
514
+ const sources = scopeSourcesOf(ctx.sources, scope);
515
+ if (sources.length === 0) return;
516
+ return sources.flatMap((source) => {
517
+ const stream = streamOf(source);
518
+ if (typeof stream === "string") {
519
+ ctx.gaps.push(stream);
520
+ return [];
521
+ }
522
+ return sourceIn(stream, scope, left, roles, ctx);
523
+ });
524
+ }
525
+ /** Who the grant reaches, as alternatives of clauses; `undefined` when Sync Streams cannot express it. */
526
+ function accessSql(item, table, ctx) {
527
+ const { access } = item;
528
+ switch (access.kind) {
529
+ case "anyone":
530
+ case "authenticated": return [[]];
531
+ case "role": {
532
+ if (access.scope === "global") return `global role ${access.role} is not a row filter`;
533
+ const field = ctx.policy.scopes.find((scope) => scope.name === access.scope)?.key;
534
+ if (field === void 0 || !SQL_IDENT.test(field)) return `scope ${access.scope} has no row key`;
535
+ return membershipSql(access.scope, ctx.dialect.column(table, field), [access.role], ctx) ?? `no rls.memberships table without an expiry or disabledAt and with the kinds of ${access.role}, and no membership source, for scope ${access.scope}`;
536
+ }
537
+ case "resource": {
538
+ const holder = ctx.config.rls?.memberships?.resource?.[access.resource];
539
+ const node = ctx.policy.resources.get(item.grant.permission.resource);
540
+ const field = node?.name === access.resource ? node.id : node?.parent?.resource === access.resource ? node.parent.field : void 0;
541
+ if (holder === void 0 || field === void 0 || !SQL_IDENT.test(field)) return `no rls.memberships.resource table for ${access.resource} on the row`;
542
+ return membershipIn(holder, holder.id ?? "id", ctx.dialect.column(table, field), [access.role], ctx) ?? `the ${access.resource} memberships table has an expiry or disabledAt, or lacks via`;
543
+ }
544
+ case "actor": return "actor grantees are not in the sync token";
545
+ default: return access;
546
+ }
547
+ }
548
+ /** A table outside `public` as `quotedTable` writes it. */
549
+ const QUOTED_TABLE = /"(\w+)"\."(\w+)"/gu;
550
+ /** `permdock_<table>`; a table in the `permdock` schema, `permdock_<table>` already, keeps one prefix. */
551
+ function subjectStreamName(table) {
552
+ return table.startsWith("permdock_") ? table : `permdock_${table}`;
553
+ }
554
+ function roleThroughs(role) {
555
+ return (Array.isArray(role) ? role : [role]).filter((item) => typeof item === "object" && item !== null && "through" in item);
556
+ }
557
+ /**
558
+ * The `SELECT`s of the root-scope ids the user is a member of: the root
559
+ * scope's `rls.memberships` table, else each of its sources that can filter a
560
+ * stream.
561
+ */
562
+ function userTenantIds(ctx) {
563
+ const { dialect } = ctx;
564
+ const root = ctx.scopes[0]?.name;
565
+ if (root === void 0) return [];
566
+ const tenants = scopeMembershipTable(ctx.config.rls?.memberships, ctx.scopes, root);
567
+ if (tenants !== void 0) {
568
+ const column = scopeColumn(tenants, ctx.scopes, root);
569
+ const members = bare(tenants.table, dialect);
570
+ return column === void 0 ? [] : [`SELECT ${dialect.column(members, column)} FROM ${fromTable(tenants.table, dialect)} WHERE ${dialect.column(members, tenants.user)} = ${dialect.user}`];
571
+ }
572
+ return scopeSourcesOf(ctx.sources, root).flatMap((source) => {
573
+ const stream = streamOf(source);
574
+ if (typeof stream === "string") return [];
575
+ const rows = ownRows(stream, dialect, root);
576
+ return [`SELECT ${dialect.column(rows.table, stream.id)} FROM ${rows.from} WHERE ${rows.where.join(" AND ")}`];
577
+ });
578
+ }
579
+ /** The stream forms of the sources of every scope `rls.memberships` maps no table for. */
580
+ function unmappedStreams(ctx) {
581
+ const out = /* @__PURE__ */ new Set();
582
+ for (const scope of ctx.scopes) {
583
+ if (scopeMembershipTable(ctx.config.rls?.memberships, ctx.scopes, scope.name) !== void 0) continue;
584
+ for (const source of scopeSourcesOf(ctx.sources, scope.name)) if (source.sql.stream !== void 0) out.add(source.sql.stream);
585
+ }
586
+ return [...out];
587
+ }
588
+ /**
589
+ * Streams of the rows a device builds the user's snapshot from: the user's
590
+ * own membership and global-role rows, the roles tables they reference, and
591
+ * the custom roles of the user's tenants. Each query starts from the user's
592
+ * own rows.
593
+ */
594
+ function subjectStreams(ctx) {
595
+ const rls = ctx.config.rls;
596
+ const { dialect } = ctx;
597
+ const tables = /* @__PURE__ */ new Map();
598
+ const add = (table, query, user) => {
599
+ const entry = tables.get(table) ?? {
600
+ user: void 0,
601
+ queries: /* @__PURE__ */ new Set()
602
+ };
603
+ if (entry.queries.size === 0) entry.user = user;
604
+ else if (entry.user !== user) entry.user = void 0;
605
+ entry.queries.add(query);
606
+ tables.set(table, entry);
607
+ };
608
+ const own = (table, user, role) => {
609
+ const name = bare(table, dialect);
610
+ const mine = `${dialect.column(name, user)} = ${dialect.user}`;
611
+ add(name, `SELECT * FROM ${fromTable(table, dialect)} WHERE ${mine}`, user);
612
+ for (const through of roleThroughs(role)) {
613
+ const keys = bare(through.through, dialect);
614
+ const [pair] = Object.entries(through.on);
615
+ if (pair === void 0) continue;
616
+ const [ref, id] = pair;
617
+ add(keys, `SELECT * FROM ${fromTable(through.through, dialect)} WHERE ${dialect.column(keys, id)} IN (SELECT ${dialect.column(name, ref)} FROM ${fromTable(table, dialect)} WHERE ${mine})`);
618
+ }
619
+ };
620
+ const memberships = rls?.memberships;
621
+ for (const table of [
622
+ ...Object.values(memberships?.scopes ?? {}),
623
+ ...memberships?.tenant === void 0 ? [] : [memberships.tenant],
624
+ ...memberships?.team === void 0 ? [] : [memberships.team],
625
+ ...Object.values(memberships?.resource ?? {})
626
+ ]) own(table.table, table.user, table.role);
627
+ for (const stream of unmappedStreams(ctx)) {
628
+ const rows = ownRows(stream, dialect);
629
+ add(rows.table, typeof stream.user === "string" ? `SELECT * FROM ${rows.from} WHERE ${rows.where.join(" AND ")}` : `SELECT ${rows.table}.* FROM ${rows.from} WHERE ${rows.where.join(" AND ")}`, typeof stream.user === "string" ? stream.user : void 0);
630
+ if (typeof stream.user !== "string") {
631
+ const users = bare(stream.user.through, dialect);
632
+ add(users, `SELECT * FROM ${fromTable(stream.user.through, dialect)} WHERE ${dialect.column(users, stream.user.column)} = ${dialect.user}`, stream.user.column);
633
+ }
634
+ for (const keys of roleTableRows(stream, dialect)) add(keys.table, keys.query);
635
+ }
636
+ const schema = rls?.schema ?? "permdock";
637
+ const database = rls?.authorize === "database";
638
+ const globalRoles = rls?.roles ?? (database ? { table: `${schema}.user_roles` } : void 0);
639
+ if (globalRoles !== void 0) own(globalRoles.table, globalRoles.user ?? "user_id", globalRoles.role ?? "role");
640
+ if (database && customRolesOn(rls)) {
641
+ const tenantIds = userTenantIds(ctx);
642
+ for (const name of [CUSTOM_ROLES.permissions, CUSTOM_ROLES.includes]) {
643
+ const qualified = `${schema}.${name}`;
644
+ const table = bare(qualified, dialect);
645
+ const from = fromTable(qualified, dialect);
646
+ const tenant = dialect.column(table, "tenant_id");
647
+ add(table, `SELECT * FROM ${from} WHERE ${tenant} IS NULL`);
648
+ for (const ids of tenantIds) add(table, `SELECT * FROM ${from} WHERE ${tenant} IN (${ids})`);
649
+ }
650
+ }
651
+ const out = [];
652
+ const names = /* @__PURE__ */ new Set();
653
+ for (const [table, entry] of tables) {
654
+ const short = subjectStreamName(table);
655
+ const name = names.has(short) ? `permdock_${table}` : short;
656
+ names.add(name);
657
+ const queries = [...entry.queries];
658
+ out.push(entry.user === void 0 ? {
659
+ name,
660
+ table,
661
+ queries
662
+ } : {
663
+ name,
664
+ table,
665
+ user: entry.user,
666
+ queries
667
+ });
668
+ }
669
+ return out;
670
+ }
671
+ /** The membership sources as `rls generate` reads them: a tenant row of the custom roles table named like a declared role is no role. */
672
+ function guardedSources(policy, config) {
673
+ const roles = config.rls?.customRoleWrites?.roles;
674
+ const guard = roles?.tenant === void 0 ? void 0 : {
675
+ table: roles.table,
676
+ tenant: roles.tenant,
677
+ skip: roles.skip,
678
+ declared: [...roleNames(policy)].toSorted()
679
+ };
680
+ return membershipSourcesOf(config).map((source) => guard === void 0 ? source : source.guarded?.(guard) ?? source);
681
+ }
682
+ /** Why a grant cannot become a stream query, before its conditions are compiled. */
683
+ function unsupported(item) {
684
+ const { grant } = item;
685
+ if (grant.breakGlass !== void 0) return "break-glass";
686
+ if (grant.closure !== void 0 || grant.portable === false) return "not portable";
687
+ if (requiresApproval(grant.approval)) return "needs approval";
688
+ if (grant.validity !== void 0) return "time-bounded";
689
+ const context = contextRefs(item.where);
690
+ return context.length > 0 ? `reads ${context.join(", ")}` : void 0;
691
+ }
692
+ /**
693
+ * Sync Streams for each synced resource: the union of the rows its read
694
+ * permission's allow grants reach. A grant with any part Sync Streams cannot
695
+ * express syncs nothing, and a resource with a deny grant gets no stream, so
696
+ * a stream never holds a row the policy denies.
697
+ */
698
+ function powersyncPlan(policy, config, dialect = POWERSYNC) {
699
+ const seen = /* @__PURE__ */ new Set();
700
+ const first = planWith(policy, config, {
701
+ ...dialect,
702
+ names: {
703
+ renamed: /* @__PURE__ */ new Map(),
704
+ seen
705
+ }
706
+ });
707
+ const renamed = renames(seen);
708
+ return renamed.size === 0 ? first : planWith(policy, config, {
709
+ ...dialect,
710
+ names: {
711
+ renamed,
712
+ seen: /* @__PURE__ */ new Set()
713
+ }
714
+ });
715
+ }
716
+ function planWith(policy, config, dialect) {
717
+ const scopes = scopeList(policy.scopes);
718
+ const ctx = {
719
+ policy,
720
+ config,
721
+ scopes,
722
+ kinds: ownershipRules(policy, scopes)?.kinds ?? {},
723
+ dialect,
724
+ sources: guardedSources(policy, config),
725
+ gaps: []
726
+ };
727
+ const action = config.powersync?.action ?? "read";
728
+ const grants = collectGrants(policy);
729
+ const resources = config.powersync?.resources ?? [...policy.resources.keys()].filter((name) => grants.some((item) => item.grant.permission.resource === name && item.grant.permission.action === action));
730
+ const streams = [];
731
+ const omitted = [];
732
+ const warnings = [];
733
+ const unsynced = [];
734
+ for (const resource of resources) {
735
+ const node = policy.resources.get(resource);
736
+ const key = `${resource}.${action}`;
737
+ const items = grants.filter((item) => item.grant.permission.key === key);
738
+ if (node === void 0 || items.length === 0) {
739
+ omitted.push({
740
+ resource,
741
+ reason: `no grant of ${key}`
742
+ });
743
+ continue;
744
+ }
745
+ if (items.some((item) => item.grant.effect === "deny")) {
746
+ omitted.push({
747
+ resource,
748
+ reason: `${key} has a deny grant, which Sync Streams cannot subtract`
749
+ });
750
+ continue;
751
+ }
752
+ const source = tableFor(resource, tableNames(config.rls?.tables));
753
+ const table = bare(source, ctx.dialect);
754
+ const { id } = node;
755
+ const queries = /* @__PURE__ */ new Set();
756
+ const compile = (label, gap, item, reach) => {
757
+ const access = unsupported(item) ?? reach();
758
+ const where = typeof access === "string" || item.where === void 0 ? [[]] : conditionSql(item.where, table, ctx);
759
+ const gaps = [...new Set(ctx.gaps.splice(0))];
760
+ for (const reason of gaps) warnings.push(`${label} under-syncs: ${reason}`);
761
+ if (typeof access === "string") warnings.push(`${label} does not sync: ${access}`);
762
+ else if (where === void 0) warnings.push(`${label} does not sync: its condition has no Sync Streams form`);
763
+ if (gaps.length > 0 || typeof access === "string" || where === void 0) unsynced.push(gap);
764
+ if (typeof access === "string" || where === void 0) return;
765
+ const columns = item.grant.fields === void 0 ? "*" : [.../* @__PURE__ */ new Set([id, ...item.grant.fields])].map((field) => ctx.dialect.column(table, field)).join(", ");
766
+ for (const clauses of product([access, where])) queries.add(`SELECT ${columns} FROM ${fromTable(source, ctx.dialect)}${clauses.length === 0 ? "" : ` WHERE ${clauses.join(" AND ")}`}`);
767
+ };
768
+ for (const item of items) compile(`${item.label}/${key}`, {
769
+ permission: key,
770
+ role: item.label
771
+ }, item, () => accessSql(item, table, ctx));
772
+ const { items: custom, skipped } = customRoleItems(ctx, grants, resource, key, table);
773
+ if (skipped !== void 0) ctx.gaps.push(skipped);
774
+ for (const entry of custom) compile(entry.label, {
775
+ permission: key,
776
+ role: CUSTOM_ROLE,
777
+ level: entry.level
778
+ }, entry.item, () => entry.access);
779
+ const dropped = [...new Set(ctx.gaps.splice(0))];
780
+ for (const reason of dropped) warnings.push(`${CUSTOM_ROLE}/${key} does not sync: ${reason}`);
781
+ if (dropped.length > 0) unsynced.push({
782
+ permission: key,
783
+ role: CUSTOM_ROLE
784
+ });
785
+ if (custom.length > 0) {
786
+ warnings.push(`${CUSTOM_ROLE}/${key}: a custom role's deny rows, includes and shared roles do not change its stream; the device still applies them`);
787
+ unsynced.push({
788
+ permission: key,
789
+ role: CUSTOM_ROLE,
790
+ composed: true
791
+ });
792
+ }
793
+ if (queries.size === 0) omitted.push({
794
+ resource,
795
+ reason: `no grant of ${key} compiles`
796
+ });
797
+ else if (queries.size > MAX_QUERIES) omitted.push({
798
+ resource,
799
+ reason: `${String(queries.size)} queries, over the limit of ${String(MAX_QUERIES)}`
800
+ });
801
+ else streams.push({
802
+ name: resource,
803
+ resource,
804
+ table,
805
+ id,
806
+ queries: [...queries]
807
+ });
808
+ }
809
+ const subject = subjectStreams(ctx);
810
+ const outside = [...new Set([...streams, ...subject].flatMap((stream) => stream.queries.flatMap((query) => [...query.matchAll(QUOTED_TABLE)].map(([, schema, name]) => `${schema ?? ""}.${name ?? ""}`))))].toSorted();
811
+ return {
812
+ streams,
813
+ subject,
814
+ omitted,
815
+ warnings,
816
+ unsynced,
817
+ devices: dialect.names?.renamed ?? /* @__PURE__ */ new Map(),
818
+ outside,
819
+ ...outside.length === 0 ? {} : { publication: `alter publication powersync add table ${outside.join(", ")};` }
820
+ };
821
+ }
822
+ /** A YAML scalar quoted as oxfmt and Prettier write it: single quotes around a `"`, unless a backslash needs a double-quoted escape. */
823
+ function yamlString(text) {
824
+ return text.includes("\"") && !text.includes("\\") ? `'${text.replaceAll("'", "''")}'` : JSON.stringify(text);
825
+ }
826
+ /** `sync-config.yaml`: edition 3 Sync Streams, one auto-subscribed stream per synced resource. */
827
+ function powersyncYaml(plan) {
828
+ const lines = [
829
+ "# Generated by permdock powersync generate from the policy. Do not edit.",
830
+ ...plan.publication === void 0 ? [] : ["# Tables outside public must be in the powersync publication:", `# ${plan.publication}`],
831
+ "config:",
832
+ " edition: 3",
833
+ "",
834
+ "streams:"
835
+ ];
836
+ if (plan.streams.length === 0 && plan.subject.length === 0) lines[lines.length - 1] = "streams: {}";
837
+ for (const stream of [...plan.streams, ...plan.subject]) lines.push(` ${stream.name}:`, " auto_subscribe: true", " queries:", ...stream.queries.map((query) => ` - ${yamlString(query)}`));
838
+ for (const item of plan.omitted) lines.push(`# ${item.resource}: not synced, ${item.reason}`);
839
+ return `${lines.join("\n")}\n`;
840
+ }
841
+ //#endregion
842
+ //#region src/cli/powersync/verify-gaps.ts
843
+ function heldRoles(subject) {
844
+ return /* @__PURE__ */ new Set([...subject.roles ?? [], ...(subject.memberships ?? []).flatMap((membership) => membership.roles)]);
845
+ }
846
+ function customGap(gap, roles) {
847
+ if (gap.composed === true) return roles.some((role) => (role.includes ?? []).length > 0 || role.shared === true || role.scope === "global");
848
+ return roles.some((role) => (role.grants ?? []).some((grant) => grant.permission === gap.permission && (grant.effect ?? "allow") === "allow" && (gap.level === void 0 || (grant.level ?? null) === gap.level)));
849
+ }
850
+ /**
851
+ * Whether `generate` warned for a grant the fixture's subject holds of
852
+ * `permission`: a declared role it holds, `anyone` or `authenticated`, or a
853
+ * custom role it holds at the warned level.
854
+ */
855
+ function warnedFor(unsynced, permission, subject, customRoles) {
856
+ const held = heldRoles(subject);
857
+ const custom = customRoles.filter((role) => held.has(role.name));
858
+ return unsynced.some((gap) => {
859
+ if (gap.permission !== permission) return false;
860
+ if (gap.role === "custom role") return customGap(gap, custom);
861
+ return gap.role === "anyone" || gap.role === "authenticated" || held.has(gap.role);
862
+ });
863
+ }
864
+ //#endregion
865
+ //#region src/cli/powersync/powersync.ts
866
+ const POWERSYNC_HELP = `permdock powersync generate [--out sync-config.yaml] [--check]
867
+ permdock powersync verify [--db-url-stdin] [--fixtures rls.fixtures.json]`;
868
+ const DEFAULT_OUT = "sync-config.yaml";
869
+ function powersyncOut(config, out) {
870
+ return out ?? config.powersync?.out ?? DEFAULT_OUT;
871
+ }
872
+ function loadPolicy(cwd, config, from) {
873
+ return requirePolicy(cwd, from ?? config.policy, "powersync needs policy in the config or --policy");
874
+ }
875
+ function sameJson(current, expected) {
876
+ try {
877
+ return JSON.stringify(JSON.parse(current)) === JSON.stringify(JSON.parse(expected));
878
+ } catch {
879
+ return false;
880
+ }
881
+ }
882
+ /**
883
+ * The files the policy compiles to: the Sync Streams file and, with
884
+ * `powersync.manifest`, the local-snapshot manifest. `warnings` lists the
885
+ * grants that do not sync.
886
+ */
887
+ function powersyncFiles(policy, config, out) {
888
+ const plan = powersyncPlan(policy, config);
889
+ const files = [{
890
+ path: powersyncOut(config, out),
891
+ text: powersyncYaml(plan)
892
+ }];
893
+ const manifest = config.powersync?.manifest;
894
+ if (manifest !== void 0) files.push({
895
+ path: manifest,
896
+ json: true,
897
+ text: `${JSON.stringify({
898
+ ...localSnapshotManifest(policy),
899
+ rows: localRows(policy, config, plan.devices)
900
+ }, null, 2)}\n`
901
+ });
902
+ return {
903
+ files,
904
+ warnings: plan.warnings,
905
+ ...plan.publication === void 0 ? {} : { publication: plan.publication }
906
+ };
907
+ }
908
+ /** The files whose content on disk is not what the policy compiles to, with `missing` for an absent one. */
909
+ function staleFiles(cwd, files) {
910
+ const stale = [];
911
+ for (const file of files) {
912
+ const found = fileDrift(resolve(cwd, file.path), file.text, file.json === true ? sameJson : void 0);
913
+ if (found !== void 0) stale.push({
914
+ path: file.path,
915
+ missing: found.kind === "missing"
916
+ });
917
+ }
918
+ return stale;
919
+ }
920
+ function rowValue(row, key) {
921
+ const value = key !== void 0 && row !== null && typeof row === "object" && !Array.isArray(row) ? Object.entries(row).find(([name]) => name === key)?.[1] : void 0;
922
+ return value === void 0 || value === null ? void 0 : String(value);
923
+ }
924
+ function claimsJson(fixture) {
925
+ return JSON.stringify(fixture.subject.claims ?? {});
926
+ }
927
+ /** Whether a stream of the user's own rows holds a row of another user. */
928
+ async function leaks(query, stream, user, fixture) {
929
+ for (const text of stream.queries) if ((await query(`select 1 from (${text}) q where q.${quoteIdent(user)}::text is distinct from $1::text and $2::jsonb is not null limit 1`, [fixture.subject.id, claimsJson(fixture)])).length > 0) return true;
930
+ return false;
931
+ }
932
+ /** Whether the stream holds the fixture's row for its subject; `undefined` for a row with no id. */
933
+ async function synced(query, stream, fixture) {
934
+ const value = rowValue(fixture.row, stream.id);
935
+ if (value === void 0) return;
936
+ for (const text of stream.queries) if ((await query(`select 1 from (${text}) q where q.${quoteIdent(stream.id)}::text = $3::text and $1::text is not null and $2::jsonb is not null limit 1`, [
937
+ fixture.subject.id,
938
+ claimsJson(fixture),
939
+ value
940
+ ])).length > 0) return true;
941
+ return false;
942
+ }
943
+ async function verifyStreams(input) {
944
+ const plan = powersyncPlan(input.policy, input.config, POSTGRES);
945
+ const mismatches = [];
946
+ const notes = [];
947
+ const client = await input.connect(input.db);
948
+ const query = async (sql, values) => (await client.query(sql, values)).rows;
949
+ const root = input.policy.scopes.find((scope) => scope.within === void 0)?.key;
950
+ try {
951
+ const checked = /* @__PURE__ */ new Set();
952
+ for (const fixture of input.fixtures) {
953
+ if (checked.has(fixture.subject.id)) continue;
954
+ checked.add(fixture.subject.id);
955
+ for (const stream of plan.subject) if (stream.user !== void 0 && await leaks(query, stream, stream.user, fixture)) mismatches.push(`stream ${stream.name} syncs another user's ${stream.table} rows to ${fixture.subject.id}`);
956
+ }
957
+ for (const fixture of input.fixtures) {
958
+ const permission = findPermission(input.policy.permissions, fixture.action);
959
+ const stream = plan.streams.find((item) => permission !== void 0 && item.resource === permission.resource && permission.action === (input.config.powersync?.action ?? "read"));
960
+ if (permission === void 0 || stream === void 0) continue;
961
+ const tenant = rowValue(fixture.row, root);
962
+ const permdock = await getPermDock(input.policy, fixtureSubject({
963
+ ...fixture.subject,
964
+ ...tenant === void 0 ? {} : { tenant }
965
+ }), { customRoles: memoryCustomRoleSource(input.customRoles) });
966
+ const granted = canFixture(permdock, permission, fixtureRow(fixture, permission.kind));
967
+ const holds = await synced(query, stream, fixture);
968
+ if (holds === void 0) notes.push(`${fixture.action} for ${fixture.subject.id}: not checked against stream ${stream.name}: the fixture row has no ${stream.id}`);
969
+ else if (holds && !granted) mismatches.push(`${fixture.action} for ${fixture.subject.id}: stream ${stream.name} syncs a row the policy denies`);
970
+ else if (granted && !holds && warnedFor(plan.unsynced, permission.key, fixture.subject, input.customRoles)) notes.push(`${fixture.action} for ${fixture.subject.id}: granted, not synced (the app reads it from the server)`);
971
+ else if (granted && !holds) mismatches.push(`${fixture.action} for ${fixture.subject.id}: granted, but stream ${stream.name} does not sync the row and generate gave no warning for it`);
972
+ }
973
+ await checkPublication(query, plan, mismatches, notes);
974
+ } finally {
975
+ await client.end();
976
+ }
977
+ return {
978
+ mismatches,
979
+ notes
980
+ };
981
+ }
982
+ /** PD058 when the `powersync` publication leaves out a synced table outside `public`; a note when the database has no such publication. */
983
+ async function checkPublication(query, plan, mismatches, notes) {
984
+ if (plan.outside.length === 0) return;
985
+ if ((await query("select 1 from pg_catalog.pg_publication where pubname = 'powersync'", [])).length === 0) {
986
+ notes.push(`this database has no powersync publication; PowerSync needs the synced tables in it: ${plan.publication ?? ""}`);
987
+ return;
988
+ }
989
+ const published = new Set((await query("select schemaname || '.' || tablename as name from pg_catalog.pg_publication_tables where pubname = 'powersync'", [])).flatMap((row) => {
990
+ const name = rowValue(row, "name");
991
+ return name === void 0 ? [] : [name];
992
+ }));
993
+ for (const table of plan.outside) if (!published.has(table)) mismatches.push(`PD058: ${table} is not in the powersync publication, so PowerSync syncs none of its rows: ${plan.publication ?? ""}`);
994
+ }
995
+ async function runPowerSync(input) {
996
+ const [verb] = input.rest;
997
+ if (verb !== "generate" && verb !== "verify") return {
998
+ code: 2,
999
+ output: POWERSYNC_HELP
1000
+ };
1001
+ let policy;
1002
+ let compiled;
1003
+ try {
1004
+ policy = await loadPolicy(input.cwd, input.config, input.from);
1005
+ compiled = powersyncFiles(policy, input.config, input.out);
1006
+ } catch (cause) {
1007
+ return usageResult(cause);
1008
+ }
1009
+ const { files, warnings } = compiled;
1010
+ const out = powersyncOut(input.config, input.out);
1011
+ const stale = staleFiles(input.cwd, files);
1012
+ if (verb === "generate") {
1013
+ if (input.check) return stale.length === 0 ? {
1014
+ code: 0,
1015
+ output: files.map((file) => `${file.path} is current`).join("\n")
1016
+ } : {
1017
+ code: 1,
1018
+ output: stale.map((file) => `${file.path} is stale: run permdock powersync generate`).join("\n")
1019
+ };
1020
+ applyFiles(input.cwd, files);
1021
+ return {
1022
+ code: 0,
1023
+ output: [
1024
+ ...files.map((file) => `wrote ${file.path}`),
1025
+ ...compiled.publication === void 0 ? [] : [`add the tables outside public to the powersync publication: ${compiled.publication}`],
1026
+ ...warnings
1027
+ ].join("\n")
1028
+ };
1029
+ }
1030
+ const mismatches = stale.map((file) => `${file.path} ${file.missing ? "is missing" : "differs from the policy"}: run permdock powersync generate`);
1031
+ const notes = [...warnings];
1032
+ if (input.db !== void 0) try {
1033
+ const fixturesPath = input.fixtures ?? input.config.rls?.fixtures ?? "rls.fixtures.json";
1034
+ const { fixtures, customRoles } = await loadFixtures(input.cwd, fixturesPath);
1035
+ const database = await verifyStreams({
1036
+ policy,
1037
+ config: input.config,
1038
+ fixtures,
1039
+ customRoles,
1040
+ db: input.db,
1041
+ connect: input.connect ?? pgConnector("permdock powersync verify --db-url-stdin", input.cwd)
1042
+ });
1043
+ mismatches.push(...database.mismatches);
1044
+ notes.push(...database.notes);
1045
+ } catch (cause) {
1046
+ return usageResult(cause);
1047
+ }
1048
+ if (mismatches.length > 0) return {
1049
+ code: 1,
1050
+ output: [...mismatches, ...notes].join("\n")
1051
+ };
1052
+ const verified = input.db === void 0 ? `${out} matches the policy` : `${out} matches the policy; no stream syncs a row the fixtures deny`;
1053
+ return {
1054
+ code: 0,
1055
+ output: notes.length === 0 ? verified : `${verified}\n${notes.join("\n")}`
1056
+ };
1057
+ }
1058
+ //#endregion
1059
+ export { streamOf as a, scopeSourcesOf as i, runPowerSync as n, staleFiles as r, powersyncFiles as t };