permdock 0.4.0 → 0.6.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.
- package/README.md +37 -35
- package/dist/{schema-Bol6gqXA.js → 2020-C9VN-x8c.js} +5 -130
- package/dist/a2a/index.d.ts +6 -6
- package/dist/a2a/index.js +14 -17
- package/dist/{adapter-context-mlWsVGGg.js → adapter-context-CRhqyzCk.js} +1 -1
- package/dist/adoption-scan-DCYe60Pi.js +107 -0
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.js +1 -0
- package/dist/ai-sdk/index.d.ts +18 -35
- package/dist/ai-sdk/index.js +37 -23
- package/dist/{app-data-CV8pqqMi.js → app-data-MuyzVqX8.js} +2 -1
- package/dist/approval-header-CJACtHkv.js +23 -0
- package/dist/approval-prompt-Dg5LeAcT.js +62 -0
- package/dist/approval-prompt-DvSSPQJX.d.ts +57 -0
- package/dist/approvals/index.d.ts +5 -3
- package/dist/approvals/index.js +8 -5
- package/dist/approvals-p1PGoVjb.js +995 -0
- package/dist/arazzo-41WuzHou.js +36 -0
- package/dist/{arazzo-C4BcskbX.js → arazzo-CaUVBn7D.js} +20 -14
- package/dist/args-IIolxxKA.js +197 -0
- package/dist/astro/index.d.ts +43 -0
- package/dist/astro/index.js +27 -0
- package/dist/audience-DabClFLQ.js +55 -0
- package/dist/auth-DRLEizbL.js +178 -0
- package/dist/authzen/index.d.ts +26 -6
- package/dist/authzen/index.js +94 -79
- package/dist/automation-BNM3TtDo.d.ts +114 -0
- package/dist/automation-DVjOckjV.js +157 -0
- package/dist/better-auth/index.d.ts +8 -6
- package/dist/better-auth/index.js +9 -8
- package/dist/better-supabase/index.d.ts +357 -30
- package/dist/better-supabase/index.js +936 -194
- package/dist/{bind-kk-uyCwH.js → bind-CAvgUuKs.js} +14 -10
- package/dist/{bind-BzqG8SMb.d.ts → bind-DOyrv6zi.d.ts} +12 -10
- package/dist/body-CH5of8Ne.js +10 -0
- package/dist/body-text-QcXok_SI.js +52 -0
- package/dist/{build-instance-DIIV9DbC.js → build-instance-Bu_eDqYw.js} +4 -3
- package/dist/{canonical-json-CFKyFOke.js → canonical-json-BC8_nMTw.js} +5 -3
- package/dist/{capability-CksyCWDH.d.ts → capability-Drg-1YhL.d.ts} +44 -25
- package/dist/{capability-txATtKV4.js → capability-N1K9IpqS.js} +68 -42
- package/dist/casbin-Cr7RLtA3.js +30 -0
- package/dist/casbin-Dyu2gnUD.js +234 -0
- package/dist/casl-64gpT-8x.js +24 -0
- package/dist/casl-DjEDhTCP.js +299 -0
- package/dist/catalog/index.d.ts +74 -2
- package/dist/catalog/index.js +51 -2
- package/dist/catalog-BoWglDPI.js +1 -0
- package/dist/catalog-DZVbrF-z.js +52 -0
- package/dist/{catalog-tZUyOfOv.js → catalog-HD2gf5mY.js} +51 -6
- package/dist/{catalog-doc-DWtg0gGR.js → catalog-doc-Cdn7-0pQ.js} +45 -23
- package/dist/catalog-fingerprint-CCP0-yQS.js +153 -0
- package/dist/chat/index.d.ts +2 -0
- package/dist/chat/index.js +224 -0
- package/dist/chat-sdk/index.d.ts +87 -0
- package/dist/chat-sdk/index.js +197 -0
- package/dist/claims-map-7oQ3ue4X.js +13 -0
- package/dist/claims-map-Ca-QX8C6.d.ts +32 -0
- package/dist/claude-agent/index.d.ts +7 -14
- package/dist/claude-agent/index.js +24 -27
- package/dist/clerk/index.d.ts +9 -8
- package/dist/clerk/index.js +9 -14
- package/dist/cli/bin.js +36 -17
- package/dist/cli/index.d.ts +19 -5
- package/dist/cli/index.js +164 -4
- package/dist/cloud/index.d.ts +102 -63
- package/dist/cloud/index.js +239 -340
- package/dist/cloud-ytQ3uviA.js +87 -0
- package/dist/codemod-5mpSpnUY.js +37 -0
- package/dist/codemod-ua2hHy7w.js +2084 -0
- package/dist/collect-Ci6akVwH.js +55 -0
- package/dist/collect-D7eQDffE.js +1 -0
- package/dist/{collect-2SrB7lfk.js → collect-mLTY4nwb.js} +75 -11
- package/dist/command-L8EstFNm.js +6 -0
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -2
- package/dist/{compile-CtTP7eur.d.ts → compile-1bsAezL0.d.ts} +5 -3
- package/dist/{compile-CcEu2ZQ1.js → compile-CMqMx_IN.js} +11 -8
- package/dist/completion-BTFrHZhX.js +143 -0
- package/dist/config-Bj2Ixq3d.js +26 -0
- package/dist/{config-BIjREpyE.js → config-bs_kZeYq.js} +12 -7
- package/dist/{config-report--Xb9APJS.js → config-report-DSpsLpCE.js} +15 -5
- package/dist/{connection-DUsczpB5.js → connection-DjhIJSs2.js} +6 -5
- package/dist/{context-DieZVJXu.js → context-B_u3_bDJ.js} +2 -2
- package/dist/convex/index.d.ts +11 -10
- package/dist/convex/index.js +6 -4
- package/dist/create-B30OMUOJ.d.ts +107 -0
- package/dist/create-CBMmFie-.d.ts +138 -0
- package/dist/create-CGm28gyI.js +130 -0
- package/dist/create-CQWIpUG-.js +403 -0
- package/dist/{create-BBO24vj6.js → create-DCF2SvoT.js} +31 -32
- package/dist/create-D_NmPOdH.d.ts +110 -0
- package/dist/{credential-kue0qpbm.js → credential-BcoiejpY.js} +152 -31
- package/dist/credentials-DPyHmYz5.js +74 -0
- package/dist/delegation-j6-rc37o.js +286 -0
- package/dist/delivery/index.d.ts +5 -0
- package/dist/delivery/index.js +5 -0
- package/dist/delivery-DCAgNyCn.js +499 -0
- package/dist/delivery-event-xX4obB8F.js +40 -0
- package/dist/{denied-text-DfRk15uw.d.ts → denied-text-CiFQNuMV.d.ts} +5 -5
- package/dist/{describe-C3ldA9os.d.ts → describe-B5qgDvEW.d.ts} +2 -2
- package/dist/{describe-Cgkp7eP0.js → describe-BybMRY7A.js} +6 -2
- package/dist/device-BARQ8eYG.js +193 -0
- package/dist/{diff-rpqoe7Ag.js → diff-3jbx6Rm4.js} +60 -54
- package/dist/diff-CmuSD3vc.js +37 -0
- package/dist/doctor-3ZqNHX_G.js +44 -0
- package/dist/{doctor-owlv6ddh.js → doctor-BdZw53-k.js} +245 -50
- package/dist/drizzle/index.d.ts +5 -3
- package/dist/drizzle/index.js +2 -2
- package/dist/elysia/index.d.ts +16 -13
- package/dist/elysia/index.js +13 -12
- package/dist/env-DJZrPjRi.js +8 -0
- package/dist/errors-BDUwg2qC.js +27 -0
- package/dist/{errors-drnfxYtm.js → errors-BKy0EfhN.js} +5 -9
- package/dist/errors-BWXBMaKK.d.ts +11 -0
- package/dist/errors-CWTY6uih.js +143 -0
- package/dist/{errors-OD_4c__H.d.ts → errors-DH5QEGXh.d.ts} +2 -2
- package/dist/evaluate-CNClBvcj.js +1785 -0
- package/dist/evaluations-C_1vdSZc.d.ts +32 -0
- package/dist/{evaluations-C9YJsv3H.js → evaluations-beAooV0n.js} +145 -34
- package/dist/eve/index.d.ts +12 -16
- package/dist/eve/index.js +22 -28
- package/dist/event-time-CsegxXxB.js +23 -0
- package/dist/expiry-D4_YKRKD.js +52 -0
- package/dist/express/index.d.ts +13 -10
- package/dist/express/index.js +12 -11
- package/dist/fastify/index.d.ts +25 -22
- package/dist/fastify/index.js +13 -12
- package/dist/fetch-adapter-DD_gfmrC.js +120 -0
- package/dist/fetch-adapter-Dw0MTS3D.d.ts +31 -0
- package/dist/fields-OYG_JR78.js +203 -0
- package/dist/files-C5ZI27eV.js +124 -0
- package/dist/{fixtures-h2eG2OZV.js → fixtures-B7XrLeY7.js} +8 -7
- package/dist/flags-C3DktBDY.js +86 -0
- package/dist/{from-snapshot-7REQZe0H.js → from-snapshot-CTCMsFZk.js} +55 -52
- package/dist/{grantee-conditions-MQs5NIuX.js → grantee-conditions-CcXf8hMM.js} +4 -3
- package/dist/{grantee-CXY-0C87.js → grantee-hxrUsbpM.js} +16 -6
- package/dist/{grantee-match-fxYp9Nmt.js → grantee-match-DbENBHLB.js} +5 -4
- package/dist/h3/index.d.ts +37 -0
- package/dist/h3/index.js +35 -0
- package/dist/headers-BIjfMaoC.js +8 -0
- package/dist/headers-CIbDTUTK.d.ts +7 -0
- package/dist/{helpers-C9YmyBIa.js → helpers-CBYVMjwh.js} +45 -38
- package/dist/hono/index.d.ts +12 -9
- package/dist/hono/index.js +13 -12
- package/dist/hosted-B46-5LYE.js +315 -0
- package/dist/hosted-BPk51-kX.d.ts +78 -0
- package/dist/{http-DPuDaChb.d.ts → http-B_LphE5Y.d.ts} +1 -2
- package/dist/http-DXJ37RDA.js +46 -0
- package/dist/{http-BEbdMZ05.js → http-ujKm0JXE.js} +1 -2
- package/dist/{import-UHk1N7vH.js → import-CNV7I2Dw.js} +25 -20
- package/dist/{import-BFIlhetD.js → import-wyqXUyjm.js} +190 -53
- package/dist/index-B8YjkEZ4.d.ts +210 -0
- package/dist/index-C-6Letxg.d.ts +431 -0
- package/dist/index-CaxrMD1A.d.ts +1 -0
- package/dist/{index-BwMe94Xq.d.ts → index-Ch8LpNDp.d.ts} +34 -16
- package/dist/index-Cp_ilKi1.d.ts +91 -0
- package/dist/index-CueFnxQc.d.ts +110 -0
- package/dist/index-DuWxc5dt.d.ts +909 -0
- package/dist/index.d.ts +19 -15
- package/dist/index.js +31 -21
- package/dist/init-DeMYsnYJ.js +96 -0
- package/dist/init-GG6Jh8HD.js +95 -0
- package/dist/{instance-options-plkrsAog.d.ts → instance-options-BsVpyGIk.d.ts} +2 -2
- package/dist/instance-snapshot-BOEhcqIF.js +601 -0
- package/dist/instant-BluqDvyS.js +19 -0
- package/dist/internals-Dk8A8fX3.js +17 -0
- package/dist/{json-schema-DBkdUuCO.js → json-schema-B-vAwlde.js} +45 -6
- package/dist/json-schema-CN_iLFH2.d.ts +40 -0
- package/dist/jwt/index.d.ts +18 -11
- package/dist/jwt/index.js +156 -135
- package/dist/{kernel-CB3LrIJM.js → kernel-D0NVb1cK.js} +52 -24
- package/dist/kysely/index.d.ts +5 -3
- package/dist/kysely/index.js +2 -2
- package/dist/load-BxEtwlBS.js +1 -0
- package/dist/{load-C3_oTHyO.js → load-DFS_ry0W.js} +22 -6
- package/dist/{load-jose-BL6LH4bL.js → load-jose-DZ4dnjri.js} +1 -4
- package/dist/login-Dfe5RlYk.js +68 -0
- package/dist/login-imhuwvn3.js +254 -0
- package/dist/manifest-C8IzW3Do.js +42 -0
- package/dist/manifest-dvL2XEe1.js +370 -0
- package/dist/manifest-json-CAGzls-C.js +9 -0
- package/dist/{manifest-uDS9y5de.d.ts → manifest-mx5ghSFM.d.ts} +9 -2
- package/dist/manifest-schema-C0kK_8V0.js +399 -0
- package/dist/{map-claims-AVlBaJoa.js → map-claims-D3oRmTk0.js} +13 -10
- package/dist/{map-error-BE27PYkE.js → map-error-Dr70oA_3.js} +3 -3
- package/dist/{markers-CiwAOZtO.js → markers-C6joDI3o.js} +2 -1
- package/dist/mcp/index.d.ts +22 -12
- package/dist/mcp/index.js +57 -39
- package/dist/{migrate-BwlB5-Wk.js → migrate-CIq4-E9F.js} +53 -14
- package/dist/nest/index.d.ts +12 -9
- package/dist/nest/index.js +15 -14
- package/dist/next/index.d.ts +18 -21
- package/dist/next/index.js +13 -11
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +2 -2
- package/dist/node/index.d.ts +10 -10
- package/dist/node/index.js +7 -9
- package/dist/{ocsf-uYFXGVw2.js → ocsf-BFp-PZZA.js} +22 -11
- package/dist/openai/index.d.ts +9 -16
- package/dist/openai/index.js +17 -24
- package/dist/openapi/index.d.ts +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-DBzHQbov.js → openapi-B7HZ0f-r.js} +8 -8
- package/dist/openapi-C4dDTVEd.js +138 -0
- package/dist/{openapi-BJdBmYDG.js → openapi-DD1SV-5-.js} +10 -9
- package/dist/operation-findings-DgATDpvh.js +36 -0
- package/dist/operations-X0VM7F3m.d.ts +59 -0
- package/dist/orpc/index.d.ts +13 -10
- package/dist/orpc/index.js +20 -15
- package/dist/otel/index.d.ts +3 -2
- package/dist/otel/index.js +30 -7
- package/dist/output-1im__-08.js +88 -0
- package/dist/output-files-B3IZJksC.js +1 -0
- package/dist/{output-files-CgIsvdSa.js → output-files-DhRBBE4n.js} +24 -26
- package/dist/{permission-keys-B26jeyUv.js → ownership-DqaJNJPn.js} +1045 -153
- package/dist/{package-root-DtNnpmUd.js → package-root-Dk_ETdkp.js} +1 -1
- package/dist/{parse-CVFQHk6c.js → parse-Cxq0WxrA.js} +2 -2
- package/dist/{parse-local-manifest-DvYUR0QL.d.ts → parse-local-manifest-5ODwqu8b.d.ts} +1 -1
- package/dist/{parse-local-manifest-C-q02Pal.js → parse-local-manifest-CVSs6J9I.js} +1 -1
- package/dist/parse-request-D_tUXG6M.js +26 -0
- package/dist/paths-DXSHAMIa.js +42 -0
- package/dist/pdp/index.d.ts +7 -3
- package/dist/pdp/index.js +45 -26
- package/dist/{peer-BOr_kJE1.js → peer-C8swJ2GH.js} +23 -4
- package/dist/peer-CRvC7Hg1.js +1 -0
- package/dist/permdock-BrAscV28.js +868 -0
- package/dist/permdock-SV8ZINYT.d.ts +502 -0
- package/dist/{permission-registry-BsblpB-z.js → permission-registry-Br4jSNVi.js} +3 -3
- package/dist/permissions-B-wCP5ZC.js +30 -0
- package/dist/{permissions-CCJ6lmxB.js → permissions-BPxlwVhN.js} +4 -3
- package/dist/{permissions-COH-KRgT.d.ts → permissions-u_KHg-sN.d.ts} +1 -2
- package/dist/policies-DP2nqRoF.js +295 -0
- package/dist/{policy-ur0BBK_e.d.ts → policy-77FSAq_V.d.ts} +2 -2
- package/dist/{hosted-CBHgkzDl.js → policy-DbM5YYtV.js} +17 -273
- package/dist/policy-from-catalog-C2gMa2Dj.js +450 -0
- package/dist/policy-module-CTlwMvLI.js +89 -0
- package/dist/{policy-types-sp9P1iIV.d.ts → policy-types-n0uRTsW8.d.ts} +460 -632
- package/dist/postgrest-CEgRbefE.d.ts +100 -0
- package/dist/postgrest-DAjUkF9w.js +575 -0
- package/dist/{powersync-cZe4kzhL.js → powersync-BkWWCS1K.js} +23 -24
- package/dist/powersync-DISt6SUe.js +62 -0
- package/dist/powersync-MMJ2mI2q.js +1 -0
- package/dist/preset/index.d.ts +146 -0
- package/dist/preset/index.js +1 -0
- package/dist/preset-DZdU0tne.js +1768 -0
- package/dist/principal-source-pHneukrx.js +103 -0
- package/dist/prisma/index.d.ts +6 -3
- package/dist/prisma/index.js +2 -2
- package/dist/{problem-CnWraVhC.js → problem-CTviyH5L.js} +13 -23
- package/dist/{problem-details-rIIo8B2C.d.ts → problem-details-B45V5d2b.d.ts} +1 -1
- package/dist/{problem-details-DECUnXAn.js → problem-details-cn5X30gZ.js} +43 -40
- package/dist/problem-types-CJ2325SZ.js +5 -0
- package/dist/{prompt-CXYhLfsS.js → prompt-DfZLJLIn.js} +2 -2
- package/dist/{protected-BN7BUdY1.js → protected-BlF0F6wL.js} +21 -4
- package/dist/{protected-B3_P7xuw.d.ts → protected-RFGpakN9.d.ts} +16 -8
- package/dist/{provider-Bi-0HNL3.js → provider-B1al-AZz.js} +2 -2
- package/dist/{provider-kwHC9CZD.d.ts → provider-DILL9oPK.d.ts} +1 -1
- package/dist/{provider-store-Ctt-81Ni.js → provider-store-DM6DNcta.js} +2 -2
- package/dist/pull-CvpINcMK.js +1 -0
- package/dist/pull-jEwbf80J.js +256 -0
- package/dist/push-BD4f15co.js +116 -0
- package/dist/pushed-policy-CqtxkV4G.js +238 -0
- package/dist/pushed-policy-DCW91KQU.d.ts +57 -0
- package/dist/react/index.d.ts +8 -8
- package/dist/react/index.js +6 -6
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -2
- package/dist/react-native/index.d.ts +24 -72
- package/dist/react-native/index.js +29 -25
- package/dist/{refs-f3imMjNW.js → refs-K6eWUJ0H.js} +1 -1
- package/dist/{relation-walk-BXnlLtg2.js → relation-walk-3OyDBF0S.js} +2 -2
- package/dist/{relations-CvmSpG67.js → relations-BnCWnJGI.js} +3 -3
- package/dist/renames-CGj7YwcQ.js +175 -0
- package/dist/{replay-COpmZUfx.d.ts → replay-Bkm-Ne4l.d.ts} +1 -1
- package/dist/{replay-BsOyTrdV.js → replay-Du1tJjG7.js} +2 -1
- package/dist/{request-context-BVM7aWNs.js → request-context-C81Pip9n.js} +3 -3
- package/dist/request-schema-CMreVuFM.js +148 -0
- package/dist/resend/index.d.ts +126 -0
- package/dist/resend/index.js +379 -0
- package/dist/{resource-relations-D1Un3pcz.js → resource-relations-D_jw0BFx.js} +1 -1
- package/dist/{retire-BFcxL6un.js → retire-CALjIQ7e.js} +2 -2
- package/dist/{revocations-Cy06JNx2.d.ts → revocations-R07lFBnX.d.ts} +2 -0
- package/dist/rls-BAqP4q66.js +336 -0
- package/dist/{rls-BEktUaqg.js → rls-DTvYd-lI.js} +288 -690
- package/dist/{role-builder-B4GbKM-i.js → role-builder-uWQZ4v84.js} +14 -207
- package/dist/{rpc-nYC3cR_u.js → rpc-VSl5ylaQ.js} +7 -7
- package/dist/run-CUKuz0A8.js +723 -0
- package/dist/run-DDwUaJDU.js +1 -0
- package/dist/run-ticket-DocwaTKn.d.ts +124 -0
- package/dist/run-ticket-sxFI6LAk.js +337 -0
- package/dist/{runtime-D_tMXnsq.js → runtime-D3VTAiCA.js} +1 -1
- package/dist/{saas-DtaA4JuW.js → saas-BCXRuXca.js} +16 -14
- package/dist/scan-qvOe0hS3.js +29 -0
- package/dist/{schema-DjQBcAP9.js → schema-B4v0Qrac.js} +102 -8
- package/dist/schema-yLuBWWsh.js +130 -0
- package/dist/scim/index.d.ts +2 -2
- package/dist/scim/index.js +1 -2
- package/dist/{scim-vFR1O5oG.js → scim-ClHtpyV3.js} +208 -76
- package/dist/{scopes-tvpa34ep.js → scopes-CUPSjo9q.js} +12 -7
- package/dist/server/index.d.ts +12 -143
- package/dist/server/index.js +12 -9
- package/dist/server-C3WXAHZj.js +453 -0
- package/dist/{server-types-DdqBwZN_.d.ts → server-types-CKQiCnON.d.ts} +51 -68
- package/dist/{sha256-BO0swRXL.js → sha256-D9ihqmvd.js} +8 -1
- package/dist/{shared-1uhI-e-b.js → shared-BAK8t4Uo.js} +20 -9
- package/dist/{shared-JhMeOsDF.js → shared-DvvFzYXe.js} +8 -9
- package/dist/signals-D3NcTKUS.js +32 -0
- package/dist/sink-4-eQIRWf.d.ts +225 -0
- package/dist/sink-BEZSlMS_.js +957 -0
- package/dist/{skills-BZdRQkxs.js → skills-B6lVdD_W.js} +13 -6
- package/dist/skills-CTYwkl44.js +51 -0
- package/dist/snapshot-B7DkYg6g.js +269 -0
- package/dist/{snapshot-cache-Cod40D-X.js → snapshot-cache-DZHQ0KF6.js} +11 -5
- package/dist/{snapshot-cache-J5T7t_vD.d.ts → snapshot-cache-PXI-vGpY.d.ts} +7 -5
- package/dist/solid/index.d.ts +11 -12
- package/dist/solid/index.js +7 -7
- package/dist/solid-start/index.d.ts +52 -0
- package/dist/solid-start/index.js +49 -0
- package/dist/{sources-Dljy06zL.d.ts → sources-Xc4zkszI.d.ts} +7 -13
- package/dist/{sources-B1-b-R0t.js → sources-vs5dGOsS.js} +125 -119
- package/dist/{sql-t8teWOfo.js → sql-B5T1IL83.js} +13 -1
- package/dist/src-Cu2m75uo.js +1860 -0
- package/dist/ssf/index.d.ts +4 -4
- package/dist/ssf/index.js +384 -47
- package/dist/standard-webhooks-DgFFD-Ga.js +228 -0
- package/dist/standard-webhooks-rbD7LS6h.d.ts +88 -0
- package/dist/storage-C2bWdvJH.js +132 -0
- package/dist/{store-ILkLUj_o.js → store-BDutJkDL.js} +40 -53
- package/dist/{store-options-CbUHDoSe.js → store-options-u0ycEe8s.js} +26 -21
- package/dist/{stores-Cxz_80g-.d.ts → stores-BUGNbJTt.d.ts} +7 -8
- package/dist/{stores-BHXdSbw9.js → stores-CGhM8BN_.js} +2 -2
- package/dist/{stream-Dwsg6WwE.d.ts → stream-CeCMJmBB.d.ts} +2 -2
- package/dist/{stream-BMui98KK.js → stream-CvdUS9V1.js} +9 -2
- package/dist/style-DiKWnAvn.js +1 -0
- package/dist/{style-CTm1AXDT.js → style-Uao76wO7.js} +3 -2
- package/dist/subject-BGoM5o8B.js +17 -0
- package/dist/supabase/index.d.ts +23 -106
- package/dist/supabase/index.js +5 -4
- package/dist/supabase/middleware.d.ts +7 -6
- package/dist/supabase/middleware.js +13 -11
- package/dist/supabase-By_frk3U.js +92 -0
- package/dist/{supabase-hook-Cva3ikXz.js → supabase-hook-BMoEhZir.js} +960 -64
- package/dist/supabase-hook-CCw5m35C.js +1 -0
- package/dist/{supabase-C1TWlnYt.js → supabase-x5JtR2tc.js} +99 -206
- package/dist/support-session-C6xJQYut.js +158 -0
- package/dist/svelte/Protected.svelte +4 -4
- package/dist/svelte/index.d.ts +7 -7
- package/dist/svelte/index.js +10 -10
- package/dist/svelte/runtime.d.ts +3 -3
- package/dist/svelte/runtime.js +3 -3
- package/dist/svelte/source.d.ts +7 -7
- package/dist/svelte/source.js +4 -4
- package/dist/sveltekit/index.d.ts +51 -0
- package/dist/sveltekit/index.js +27 -0
- package/dist/tanstack-start/index.d.ts +51 -0
- package/dist/tanstack-start/index.js +40 -0
- package/dist/target-Bw37TFNA.js +51 -0
- package/dist/{tenancy-DshjqY0n.js → tenancy-DN2QAx9z.js} +5 -5
- package/dist/terminal/index.d.ts +11 -9
- package/dist/terminal/index.js +23 -287
- package/dist/testing/index.d.ts +311 -38
- package/dist/testing/index.js +3475 -201
- package/dist/testing/saas/index.d.ts +9 -8
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -2
- package/dist/text-BMFRfBqc.js +8 -0
- package/dist/{text-diff-BdkkYSbi.js → text-diff-BQcUapOb.js} +1 -1
- package/dist/thenable-yD7SabqG.js +31 -0
- package/dist/time-DkWjdL98.js +71 -0
- package/dist/{token-Bx9qy30p.js → token-BLZcjQgt.js} +15 -6
- package/dist/{tool-refusal-CQ7OEia9.js → tool-refusal-CMLN5xhn.js} +2 -2
- package/dist/trpc/index.d.ts +12 -9
- package/dist/trpc/index.js +16 -14
- package/dist/types-8RnwUAvE.d.ts +107 -0
- package/dist/{store-9KO4BlkB.d.ts → types-B3_ooZgZ.d.ts} +100 -75
- package/dist/{types-Br9IXUBG.d.ts → types-BdtSDbBO.d.ts} +77 -17
- package/dist/types-BtHtcKMr.d.ts +157 -0
- package/dist/{types-CqrjB9o6.d.ts → types-BzzaHxt3.d.ts} +23 -11
- package/dist/types-C791HFu9.d.ts +332 -0
- package/dist/types-C863OQiR.d.ts +57 -0
- package/dist/{types-ByUo56un.d.ts → types-CVZSNocA.d.ts} +23 -22
- package/dist/{types-DHrmBJLH.d.ts → types-CX7AvRqP.d.ts} +7 -7
- package/dist/types-C_IiEs64.d.ts +65 -0
- package/dist/{types-7rJVLWe5.d.ts → types-Dfv0aTER.d.ts} +63 -11
- package/dist/types-DqzsnlSA.js +67 -0
- package/dist/{types-DpWfQdSh.d.ts → types-Druu9wLM.d.ts} +46 -11
- package/dist/{types-CIlTojhq.d.ts → types-_WhUP7ud.d.ts} +9 -6
- package/dist/{types-CUcFZfpU.d.ts → types-lUlG3TVd.d.ts} +2 -1
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/usage-CRVl9V3t.js +32 -0
- package/dist/usage-CZpSQP6L.js +1 -0
- package/dist/{usage-CG9nOYZw.js → usage-SveTRTyr.js} +54 -11
- package/dist/{validation-error-BSSw0l_8.d.ts → validation-error-DHUrAkku.d.ts} +1 -1
- package/dist/{validity-dVrUEPJt.js → validity-CZ7-y0Cj.js} +32 -15
- package/dist/{verifier-DMr_Zmb_.js → verifier-DHuh-okk.js} +18 -7
- package/dist/{verify-BztUB3GX.js → verify-BaAKx7t6.js} +645 -50
- package/dist/{version-BEogC-rB.js → version-CEbQ5UeM.js} +4 -3
- package/dist/version-DXkYUISx.js +1 -0
- package/dist/{views-CJsrBS1f.js → views-DTIIO-oQ.js} +22 -5
- package/dist/{permission-types-Dg39z9Kv.d.ts → vocabulary-CN8H_9UF.d.ts} +67 -2
- package/dist/vue/index.d.ts +9 -10
- package/dist/vue/index.js +8 -8
- package/dist/waiter-B986w1oK.d.ts +36 -0
- package/dist/waiter-BGUtGYoU.js +67 -0
- package/dist/watch--ovlPOc0.js +1 -0
- package/dist/{watch-zeucB7eF.js → watch-vea54PRw.js} +44 -5
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +3 -3
- package/dist/where-scope-C9vfEphl.js +306 -0
- package/dist/who-can-DWBAcaCQ.js +364 -0
- package/dist/wire-delegation-Cv3W2eaM.js +36 -0
- package/dist/wire-denial-C7PaVYEM.js +27 -0
- package/dist/workflow/index.d.ts +5 -0
- package/dist/workflow/index.js +223 -0
- package/dist/workflow-sdk/index.d.ts +56 -0
- package/dist/workflow-sdk/index.js +78 -0
- package/dist/{wrap-Cr93qqdP.d.ts → wrap-C5jC6sKI.d.ts} +2 -1
- package/dist/{wrap-DWsU_5Bd.js → wrap-gX61EtpB.js} +7 -6
- package/package.json +136 -37
- package/schemas/access-event-v1.json +118 -0
- package/schemas/approval-request-v1.json +34 -1
- package/schemas/approval-signature-v1.json +94 -0
- package/schemas/automation-event-v1.json +113 -0
- package/schemas/capability-v1.json +84 -0
- package/schemas/catalog-event-v1.json +87 -0
- package/schemas/catalog-v1.json +123 -12
- package/schemas/cli-commands-v1.json +164 -0
- package/schemas/cli-result-v1.json +96 -0
- package/schemas/cloud-api-v1.json +5407 -0
- package/schemas/connector-v1.json +408 -0
- package/schemas/credential-event-v1.json +81 -0
- package/schemas/credential-v1.json +134 -0
- package/schemas/decision-event-v1.json +288 -0
- package/schemas/delivery-event-v1.json +117 -0
- package/schemas/directory-event-v1.json +85 -0
- package/schemas/evidence-export-v1.json +54 -0
- package/schemas/membership-event-v1.json +93 -0
- package/schemas/policy-v1.json +68 -0
- package/schemas/pushed-policy-v1.json +236 -0
- package/schemas/run-v1.json +162 -0
- package/schemas/snapshot-v1.json +138 -0
- package/schemas/supabase-claims-v1.json +4 -4
- package/schemas/supabase-manifest-v1.json +5 -1
- package/schemas/support-session-v1.json +111 -0
- package/skills/permdock/SKILL.md +16 -12
- package/skills/permdock-agents/SKILL.md +40 -23
- package/skills/permdock-agents/references/operations-automations-chat.md +221 -0
- package/skills/permdock-agents/references/runtimes.md +22 -18
- package/skills/permdock-approvals/SKILL.md +29 -13
- package/skills/permdock-approvals/references/delivery-and-waiters.md +172 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +21 -7
- package/skills/permdock-audit/SKILL.md +3 -3
- package/skills/permdock-credentials/SKILL.md +5 -5
- package/skills/permdock-credentials/references/tokens-keys-links.md +14 -10
- package/skills/permdock-data/SKILL.md +6 -6
- package/skills/permdock-data/references/queries-and-rls.md +10 -9
- package/skills/permdock-data/references/relationships.md +3 -3
- package/skills/permdock-tenancy/SKILL.md +20 -7
- package/skills/permdock-tenancy/references/contacts-and-support-sessions.md +122 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +6 -4
- package/skills/permdock-wire/SKILL.md +17 -11
- package/skills/permdock-wire/references/adapters.md +139 -51
- package/skills/permdock-wire/references/policy-options.md +5 -5
- package/skills/permdock-wire/references/presets-and-recipes.md +58 -0
- package/dist/approvals-DZL76lKW.js +0 -240
- package/dist/arazzo-CD8d0WLb.js +0 -54
- package/dist/catalog-6yWAX6Ps.js +0 -2
- package/dist/catalog-WHG4kBq7.js +0 -48
- package/dist/cloud-C7bSDgX4.js +0 -50
- package/dist/cloud-PeBN3IEn.js +0 -118
- package/dist/collect-Bw2Jdq1q.js +0 -2
- package/dist/collect-CQx4hX4N.js +0 -83
- package/dist/commands-Cpip4ULx.js +0 -97
- package/dist/config-DLt6BvV7.js +0 -36
- package/dist/diff-Cnghh_Ip.js +0 -48
- package/dist/doctor-CuVJvhV_.js +0 -53
- package/dist/duration-xK2jGFVc.js +0 -25
- package/dist/errors-CWfoNg9N.js +0 -47
- package/dist/errors-DRnGZHzF.js +0 -30
- package/dist/expiry-BoBJ8Tvc.js +0 -7
- package/dist/fields-fwfXN01n.js +0 -1016
- package/dist/headers-BZSDieNT.js +0 -6
- package/dist/headers-Cf-zWvsO.d.ts +0 -6
- package/dist/ids-DC4V4CFu.js +0 -18
- package/dist/index-DgM7w96K.d.ts +0 -6
- package/dist/index-Dz7didpC.d.ts +0 -201
- package/dist/index-UdVQgq48.d.ts +0 -550
- package/dist/load-B_shHrmd.js +0 -2
- package/dist/manifest-BngWKryL.js +0 -770
- package/dist/may-use-BOn_dR_w.js +0 -78
- package/dist/openapi-DcQ1Kg40.js +0 -164
- package/dist/ownership-BgnbOJFT.js +0 -851
- package/dist/permdock-L26udHSk.js +0 -3646
- package/dist/powersync-BFSbSElQ.js +0 -62
- package/dist/powersync-DCDgZgfZ.js +0 -2
- package/dist/rls-55DQTuf5.js +0 -250
- package/dist/run-DMcNW_8T.js +0 -174
- package/dist/server-ZYEHtNHS.js +0 -268
- package/dist/sink-BTtmfPnE.js +0 -141
- package/dist/sink-CPN4zLfs.d.ts +0 -104
- package/dist/skills-Dle4ZoLF.js +0 -56
- package/dist/snapshot-Dc9CHPME.js +0 -1078
- package/dist/src-Bb7no-hS.js +0 -639
- package/dist/subject-C6dLxqHU.js +0 -24
- package/dist/supabase-Dbqr141s.js +0 -83
- package/dist/supabase-hook-BULg7Ut_.js +0 -2
- package/dist/thenable-QSmvt-vZ.js +0 -65
- package/dist/types-C0dee0Qf.d.ts +0 -197
- package/dist/types-DvupZE0w.d.ts +0 -9
- package/dist/usage-4G6t643F.js +0 -42
- package/dist/usage-iFLJ0-rr.js +0 -2
- package/dist/vocabulary-hhEFLFoL.d.ts +0 -65
- package/dist/watch-C8TpsAl4.js +0 -2
|
@@ -1,25 +1,624 @@
|
|
|
1
|
-
import { r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
|
-
import { a as hasConditionOp } from "./refs-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { c as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { a as sole, r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
|
+
import { a as hasConditionOp, c as isConditionRef } from "./refs-K6eWUJ0H.js";
|
|
3
|
+
import { E as memoryCustomRoleSource, g as memoryRelationSource } from "./src-Cu2m75uo.js";
|
|
4
|
+
import { c as listPermissions, h as formerKeys, n as findPermission } from "./permission-registry-Br4jSNVi.js";
|
|
5
|
+
import { a as expandRelation, c as isEdgeRelation, d as isSelfParented, l as isFieldRelation, o as groupEntries, p as restrictedFor, u as isPrincipalRelation } from "./resource-relations-D_jw0BFx.js";
|
|
6
|
+
import { f as nowMillis, u as isoAt } from "./time-DkWjdL98.js";
|
|
7
|
+
import { t as canonicalJson } from "./canonical-json-BC8_nMTw.js";
|
|
8
|
+
import { d as scopeList } from "./scopes-CUPSjo9q.js";
|
|
9
|
+
import { o as requiresStandIn } from "./delegation-j6-rc37o.js";
|
|
10
|
+
import { o as flattenGrantee } from "./grantee-hxrUsbpM.js";
|
|
11
|
+
import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-CcXf8hMM.js";
|
|
12
|
+
import { p as scopeField } from "./tenancy-DN2QAx9z.js";
|
|
13
|
+
import { s as policyLevels } from "./policy-DbM5YYtV.js";
|
|
14
|
+
import { t as requiresApproval } from "./approval-required-TzNB9oQA.js";
|
|
15
|
+
import { c as andWhere, m as customRoleScope, v as leveled, y as membershipsClaim } from "./evaluate-CNClBvcj.js";
|
|
16
|
+
import { t as getPermDock } from "./permdock-BrAscV28.js";
|
|
17
|
+
import { a as describeError, i as configError, u as usageResult } from "./errors-CWTY6uih.js";
|
|
11
18
|
import { t as policyRowConditionKeys } from "./row-conditions-h2HythXQ.js";
|
|
12
|
-
import {
|
|
13
|
-
import { o as loadPolicyAt } from "./load-
|
|
14
|
-
import { c as relationArmSql, u as restrictedRowsSql } from "./compile-
|
|
19
|
+
import { s as jsonSchemaOf } from "./catalog-doc-Cdn7-0pQ.js";
|
|
20
|
+
import { o as loadPolicyAt } from "./load-DFS_ry0W.js";
|
|
21
|
+
import { c as relationArmSql, u as restrictedRowsSql } from "./compile-CMqMx_IN.js";
|
|
15
22
|
import "./budget-NbEtbJC_.js";
|
|
16
|
-
import {
|
|
17
|
-
import "./sources-
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
+
import { i as qualifySqlTable, n as defaultSqlCommand } from "./sql-B5T1IL83.js";
|
|
24
|
+
import "./sources-vs5dGOsS.js";
|
|
25
|
+
import { t as beforeFirst } from "./text-BMFRfBqc.js";
|
|
26
|
+
import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-B7XrLeY7.js";
|
|
27
|
+
import { F as quoteQualified, I as quoteTable, M as qualified, N as quoteIdent, P as quoteLiteral, T as columnTypesOf, _ as subjectClaimJsonSql, a as customRolesOn, j as helperSchema, r as HELPERS, v as subjectClaimSql, w as arrayColumnsOf } from "./shared-BAK8t4Uo.js";
|
|
28
|
+
import { d as connectPgPool, f as pgConnector, l as validitySql, s as andSql, u as wrapSql } from "./policies-DP2nqRoF.js";
|
|
29
|
+
import { $ as activeUserSql, A as collectGrants, F as CLOSURE, I as graphHelper, L as graphSqlName, M as compileConditionSql, N as contextRefs, O as breakGlassHolder, P as sqlFunctionNames, R as graphSqlText, T as apiKeyAllowsCall, V as linkHelper, Y as restrictedHelper, Z as scopeTable, _ as capabilityAccessSql, k as breakGlassKey, m as accessSql, p as requiresSql } from "./ownership-DqaJNJPn.js";
|
|
30
|
+
import { s as viewName, t as FIELD_VIEWS } from "./fields-OYG_JR78.js";
|
|
31
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
32
|
+
//#region src/cli/rls/indexes.ts
|
|
33
|
+
/** Postgres truncates identifiers longer than this, in bytes. */
|
|
34
|
+
const MAX_IDENT = 63;
|
|
35
|
+
/**
|
|
36
|
+
* The row fields `condition` compares with the subject, a claim or a
|
|
37
|
+
* membership (`ownerId: principal.id`, `memberOf`), through `and` and `or`:
|
|
38
|
+
* the selective comparisons an index serves. A comparison with a constant
|
|
39
|
+
* (`status: 'sent'`) narrows rows the scope key already found, a negation
|
|
40
|
+
* scans anyway, and a dotted field is a JSON path a plain index does not serve.
|
|
41
|
+
*/
|
|
42
|
+
function indexedFields(condition) {
|
|
43
|
+
if (condition === void 0) return [];
|
|
44
|
+
switch (condition.op) {
|
|
45
|
+
case "and":
|
|
46
|
+
case "or": return condition.conditions.flatMap(indexedFields);
|
|
47
|
+
case "memberOf": return plainField(condition.field);
|
|
48
|
+
case "eq":
|
|
49
|
+
case "in": return isConditionRef(condition.value) ? plainField(condition.field) : [];
|
|
50
|
+
case "not":
|
|
51
|
+
case "sqlFunction":
|
|
52
|
+
case "opaque":
|
|
53
|
+
case "related":
|
|
54
|
+
case "ne":
|
|
55
|
+
case "gt":
|
|
56
|
+
case "gte":
|
|
57
|
+
case "lt":
|
|
58
|
+
case "lte":
|
|
59
|
+
case "contains":
|
|
60
|
+
case "notIn":
|
|
61
|
+
case "isNull":
|
|
62
|
+
case "liveSession": return [];
|
|
63
|
+
default: return condition;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function plainField(field) {
|
|
67
|
+
return field.includes(".") ? [] : [field];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Every index the generated SQL reads through: the row columns policies
|
|
71
|
+
* filter on (scope keys and selective condition fields), and the membership
|
|
72
|
+
* tables' user column, followed by the scope column the helpers select.
|
|
73
|
+
*/
|
|
74
|
+
function indexTargets(ctx, rowColumns) {
|
|
75
|
+
const targets = /* @__PURE__ */ new Map();
|
|
76
|
+
const add = (table, columns) => {
|
|
77
|
+
const target = {
|
|
78
|
+
table: qualifySqlTable(table),
|
|
79
|
+
columns
|
|
80
|
+
};
|
|
81
|
+
targets.set(`${target.table}\u0000${columns[0] ?? ""}`, target);
|
|
82
|
+
};
|
|
83
|
+
for (const { table, column } of rowColumns) add(table, [column]);
|
|
84
|
+
for (const scope of ctx.scopes) {
|
|
85
|
+
const mapped = scopeTable(ctx, scope.name);
|
|
86
|
+
if (mapped !== void 0) add(mapped.table.table, [mapped.table.user, mapped.column]);
|
|
87
|
+
}
|
|
88
|
+
for (const source of [...ctx.sources ?? [], ...ctx.memberSources ?? []]) {
|
|
89
|
+
add(source.sql.table, [source.sql.user]);
|
|
90
|
+
const users = source.sql.userThrough;
|
|
91
|
+
if (users !== void 0) add(users.table, [users.key]);
|
|
92
|
+
}
|
|
93
|
+
return [...targets.values()].toSorted((a, b) => `${a.table}.${a.columns.join(",")}` < `${b.table}.${b.columns.join(",")}` ? -1 : 1);
|
|
94
|
+
}
|
|
95
|
+
/** A stable index name under Postgres' identifier limit. */
|
|
96
|
+
function indexName(target) {
|
|
97
|
+
const name = `permdock_${target.table.slice(target.table.lastIndexOf(".") + 1)}_${target.columns.join("_")}_idx`;
|
|
98
|
+
if (Buffer.byteLength(name) <= MAX_IDENT) return name;
|
|
99
|
+
return `permdock_${createHash("sha256").update(name).digest("hex").slice(0, 8)}_idx`;
|
|
100
|
+
}
|
|
101
|
+
/** `create index if not exists` for every target, one per line. */
|
|
102
|
+
function indexesSql(targets) {
|
|
103
|
+
return targets.map((target) => `create index if not exists ${quoteIdent(indexName(target))} on ${quoteTable(target.table)} (${target.columns.map(quoteIdent).join(", ")});`).join("\n");
|
|
104
|
+
}
|
|
105
|
+
/** Every live column of the tables in `$1` (`schema.table`), as `target` and `name`. */
|
|
106
|
+
const COLUMNS_SQL = `select n.nspname || '.' || c.relname as target, a.attname as name
|
|
107
|
+
from pg_attribute a
|
|
108
|
+
join pg_class c on c.oid = a.attrelid
|
|
109
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
110
|
+
where a.attnum > 0 and not a.attisdropped
|
|
111
|
+
and n.nspname || '.' || c.relname = any($1::text[])`;
|
|
112
|
+
const INDEX_COLUMNS_SQL = `select n.nspname || '.' || c.relname as target,
|
|
113
|
+
array(
|
|
114
|
+
select a.attname::text
|
|
115
|
+
from unnest(i.indkey) with ordinality k(attnum, ord)
|
|
116
|
+
left join pg_attribute a on a.attrelid = i.indrelid and a.attnum = k.attnum and k.attnum > 0
|
|
117
|
+
where k.ord <= i.indnkeyatts
|
|
118
|
+
order by k.ord
|
|
119
|
+
) as columns
|
|
120
|
+
from pg_index i
|
|
121
|
+
join pg_class c on c.oid = i.indrelid
|
|
122
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
123
|
+
where i.indpred is null and n.nspname || '.' || c.relname = any($1::text[])`;
|
|
124
|
+
function strings(value) {
|
|
125
|
+
return Array.isArray(value) ? value.map((item) => typeof item === "string" ? item : null) : [];
|
|
126
|
+
}
|
|
127
|
+
/** Reads the columns and index key columns of `tables` (`schema.table`). */
|
|
128
|
+
async function readTableIndexFacts(db, tables, connect = (url) => connectPgPool(url, "permdock rls generate --db-url-stdin")) {
|
|
129
|
+
const client = await connect(db);
|
|
130
|
+
try {
|
|
131
|
+
const [columns, indexes] = await Promise.all([client.query(COLUMNS_SQL, [[...tables]]), client.query(INDEX_COLUMNS_SQL, [[...tables]])]);
|
|
132
|
+
const byTable = {};
|
|
133
|
+
for (const row of columns.rows) (byTable[String(row["target"])] ??= []).push(String(row["name"]));
|
|
134
|
+
const keys = {};
|
|
135
|
+
for (const row of indexes.rows) {
|
|
136
|
+
const list = strings(row["columns"]);
|
|
137
|
+
const end = list.indexOf(null);
|
|
138
|
+
const lead = end === -1 ? list : list.slice(0, end);
|
|
139
|
+
if (lead.length > 0) (keys[String(row["target"])] ??= []).push(lead);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
columns: byTable,
|
|
143
|
+
indexes: keys
|
|
144
|
+
};
|
|
145
|
+
} finally {
|
|
146
|
+
await client.end();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Drops the targets a database already serves or cannot hold: a target on a
|
|
151
|
+
* table or column the database lacks (with a warning), and one an existing
|
|
152
|
+
* index leads with (its key columns start with the target's).
|
|
153
|
+
*/
|
|
154
|
+
function pruneIndexTargets(targets, facts) {
|
|
155
|
+
const warnings = [];
|
|
156
|
+
return {
|
|
157
|
+
targets: targets.filter((target) => {
|
|
158
|
+
const columns = facts.columns[target.table];
|
|
159
|
+
if (columns === void 0) {
|
|
160
|
+
warnings.push(`no index suggested on ${target.table}: the database has no such table`);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
const absent = target.columns.filter((column) => !columns.includes(column));
|
|
164
|
+
if (absent.length > 0) {
|
|
165
|
+
warnings.push(`no index suggested on ${target.table} (${target.columns.join(", ")}): the table has no column ${absent.join(", ")}`);
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return !(facts.indexes[target.table] ?? []).some((index) => target.columns.every((column, at) => index[at] === column));
|
|
169
|
+
}),
|
|
170
|
+
warnings
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/cli/sql/compile.ts
|
|
175
|
+
/**
|
|
176
|
+
* The levels an allow reaches: every level without a row condition, else the
|
|
177
|
+
* levels whose condition equals its condition.
|
|
178
|
+
*/
|
|
179
|
+
function reachedLevels(where, levels) {
|
|
180
|
+
if (where === void 0) return levels.map(([name]) => name);
|
|
181
|
+
const held = canonicalJson(where);
|
|
182
|
+
return levels.filter(([, condition]) => canonicalJson(condition) === held).map(([name]) => name);
|
|
183
|
+
}
|
|
184
|
+
function levelReachOf(policy, keys) {
|
|
185
|
+
const reach = /* @__PURE__ */ new Map();
|
|
186
|
+
for (const [entry, grantKey] of keys) {
|
|
187
|
+
const { item } = entry;
|
|
188
|
+
const { grant } = item;
|
|
189
|
+
if (item.access.kind !== "role" || grant.effect !== "allow" || grant.permission.kind !== "instance" || grant.level !== void 0) continue;
|
|
190
|
+
const levels = policyLevels(policy, grant.permission.resource);
|
|
191
|
+
if (levels.length > 0) {
|
|
192
|
+
const set = reach.get(grantKey) ?? /* @__PURE__ */ new Set();
|
|
193
|
+
for (const name of reachedLevels(grant.where, levels)) set.add(name);
|
|
194
|
+
reach.set(grantKey, set);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const grantKey of new Set(keys.values())) {
|
|
199
|
+
const at = grantKey.lastIndexOf("@");
|
|
200
|
+
const base = at === -1 ? grantKey : grantKey.slice(0, at);
|
|
201
|
+
const level = at === -1 ? void 0 : grantKey.slice(at + 1);
|
|
202
|
+
for (const name of reach.get(base) ?? []) if (level === void 0 || level === name) out.push([grantKey, name]);
|
|
203
|
+
}
|
|
204
|
+
return out.toSorted(([a, x], [b, y]) => a === b ? x < y ? -1 : 1 : a < b ? -1 : 1);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The SQL command `action` compiles to: `rls.actions` first, then the six default verbs.
|
|
208
|
+
* Any other verb is `undefined`, as `'none'` is: no policy, and its grants are still seeded.
|
|
209
|
+
*/
|
|
210
|
+
function commandFor(action, actions) {
|
|
211
|
+
if (actions !== void 0 && Object.hasOwn(actions, action)) {
|
|
212
|
+
const configured = actions[action];
|
|
213
|
+
return configured === "none" ? void 0 : configured;
|
|
214
|
+
}
|
|
215
|
+
return defaultSqlCommand(action);
|
|
216
|
+
}
|
|
217
|
+
function tableFor$1(resource, tables) {
|
|
218
|
+
return tables?.[resource] ?? resource;
|
|
219
|
+
}
|
|
220
|
+
function ancestorsOf(policy, name) {
|
|
221
|
+
const names = [];
|
|
222
|
+
let current = policy.resources.get(name);
|
|
223
|
+
while (current?.parent !== void 0 && !names.includes(current.parent.resource)) {
|
|
224
|
+
names.push(current.parent.resource);
|
|
225
|
+
current = policy.resources.get(current.parent.resource);
|
|
226
|
+
}
|
|
227
|
+
return names;
|
|
228
|
+
}
|
|
229
|
+
function membershipField(policy, resource, holder) {
|
|
230
|
+
if (resource === void 0) return;
|
|
231
|
+
if (resource.name === holder) return resource.id ?? "id";
|
|
232
|
+
let current = resource;
|
|
233
|
+
const seen = /* @__PURE__ */ new Set();
|
|
234
|
+
while (current?.parent !== void 0 && !seen.has(current.name)) {
|
|
235
|
+
seen.add(current.name);
|
|
236
|
+
if (current.parent.resource === holder) return current.parent.field;
|
|
237
|
+
current = policy.resources.get(current.parent.resource);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function resourceCondition(item, policy, ctx) {
|
|
241
|
+
if (item.access.kind !== "resource") throw configError("PermDock CLI: resourceCondition needs a resource role");
|
|
242
|
+
const { role, resource: roleResource } = item.access;
|
|
243
|
+
const target = policy.resources.get(item.grant.permission.resource);
|
|
244
|
+
const holders = [roleResource, ...ancestorsOf(policy, roleResource)];
|
|
245
|
+
const hops = [];
|
|
246
|
+
for (const [index, holder] of holders.entries()) {
|
|
247
|
+
const field = membershipField(policy, target, holder);
|
|
248
|
+
if (field === void 0) continue;
|
|
249
|
+
if (index > 0 && ctx.memberships?.resource?.[holder] === void 0) continue;
|
|
250
|
+
hops.push({
|
|
251
|
+
op: "memberOf",
|
|
252
|
+
scope: "resource",
|
|
253
|
+
field,
|
|
254
|
+
roles: [role],
|
|
255
|
+
resource: holder
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return sole(hops) ?? {
|
|
259
|
+
op: "or",
|
|
260
|
+
conditions: hops
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* The access a link capability has to a resource-scoped grant, over the same
|
|
265
|
+
* holders as `resourceCondition`. It reads the claim, so ancestor hops need
|
|
266
|
+
* no memberships table. `undefined` when no holder is on the row's chain.
|
|
267
|
+
*/
|
|
268
|
+
function capabilityAccess(item, policy, ctx) {
|
|
269
|
+
if (item.access.kind !== "resource") return;
|
|
270
|
+
const { role, resource: roleResource } = item.access;
|
|
271
|
+
const kinds = ctx.ownership?.kinds[role];
|
|
272
|
+
if (kinds !== void 0 && !kinds.includes("link")) return;
|
|
273
|
+
const target = policy.resources.get(item.grant.permission.resource);
|
|
274
|
+
const hops = [];
|
|
275
|
+
for (const holder of [roleResource, ...ancestorsOf(policy, roleResource)]) {
|
|
276
|
+
const field = membershipField(policy, target, holder);
|
|
277
|
+
if (field !== void 0) hops.push(capabilityAccessSql(ctx, field, holder, role, item.grant.permission.key));
|
|
278
|
+
}
|
|
279
|
+
if (hops.length === 0) return;
|
|
280
|
+
return hops.length === 1 ? hops[0] : hops.map(wrapSql).join(" or ");
|
|
281
|
+
}
|
|
282
|
+
function prepare(item, tables, warnings, skipClosures, actions, rowActions = false) {
|
|
283
|
+
const { grant, label } = item;
|
|
284
|
+
if (grant.breakGlass !== void 0) {
|
|
285
|
+
warnings.push(`break-glass grant ${label}/${grant.permission.key} reads through permdock_break_glass_${grant.permission.resource}, not a policy`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (grant.closure !== void 0 || grant.portable === false) {
|
|
289
|
+
if (skipClosures) {
|
|
290
|
+
warnings.push(`skipped non-portable grant ${label}/${grant.permission.key}`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
throw configError(`PermDock CLI: closure grant ${label}/${grant.permission.key} is not portable; rewrite it or pass --skip-closures`);
|
|
294
|
+
}
|
|
295
|
+
const context = [.../* @__PURE__ */ new Set([...contextRefs(item.where), ...contextRefs(grant.check)])];
|
|
296
|
+
if (context.length > 0) {
|
|
297
|
+
if (skipClosures) {
|
|
298
|
+
warnings.push(`skipped grant ${label}/${grant.permission.key}: it reads ${context.join(", ")}, which is not in the token`);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
throw configError(`PermDock CLI: grant ${label}/${grant.permission.key} reads ${context.join(", ")}; request context is not in the token, so RLS cannot compile it. Move the value to a server-set claim (principal.claims.*) or pass --skip-closures (permdock doctor PD027)`);
|
|
302
|
+
}
|
|
303
|
+
if (requiresApproval(grant.approval)) {
|
|
304
|
+
warnings.push(`skipped approval grant ${label}/${grant.permission.key}`);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const command = commandFor(grant.permission.action, actions);
|
|
308
|
+
const table = tableFor$1(grant.permission.resource, tables);
|
|
309
|
+
if (command === void 0) {
|
|
310
|
+
if (item.access.kind !== "role" && rowActions) return {
|
|
311
|
+
item,
|
|
312
|
+
command: "none",
|
|
313
|
+
table,
|
|
314
|
+
...item.where === void 0 ? {} : { using: item.where }
|
|
315
|
+
};
|
|
316
|
+
if (item.access.kind !== "role") {
|
|
317
|
+
warnings.push(`skipped ${label}/${grant.permission.key}: action '${grant.permission.action}' has no SQL command (rls.actions) and only role grants are seeded`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (grant.validity !== void 0 && grant.effect === "allow") {
|
|
321
|
+
warnings.push(`skipped ${label}/${grant.permission.key}: a role_permissions row cannot carry validFrom / validUntil, so a time-bounded grant on an action with no SQL command is not seeded`);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
item,
|
|
326
|
+
command: "none",
|
|
327
|
+
table,
|
|
328
|
+
...item.where === void 0 ? {} : { using: item.where },
|
|
329
|
+
...grant.check === void 0 ? {} : { check: grant.check }
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const using = command === "insert" ? void 0 : item.where;
|
|
333
|
+
const check = command === "insert" ? grant.check ?? item.where : command === "update" ? grant.check ?? item.where : void 0;
|
|
334
|
+
return {
|
|
335
|
+
item,
|
|
336
|
+
command,
|
|
337
|
+
table,
|
|
338
|
+
...using === void 0 ? {} : { using },
|
|
339
|
+
...check === void 0 ? {} : { check }
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function signature(entry, byFields) {
|
|
343
|
+
const base = [
|
|
344
|
+
entry.item.grant.effect,
|
|
345
|
+
entry.using ?? null,
|
|
346
|
+
entry.check ?? null
|
|
347
|
+
];
|
|
348
|
+
return JSON.stringify(byFields ? [...base, entry.item.grant.fields ?? null] : base);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Grant keys per permission: the key is the permission, split into
|
|
352
|
+
* `permission#n` when role grants carry different portable conditions (or
|
|
353
|
+
* effects), one key per condition group; a group whose grants set `group`
|
|
354
|
+
* is `permission#<group>` instead. With field views, grants that differ
|
|
355
|
+
* only in `fields` get their own keys too, so a view can tell them apart.
|
|
356
|
+
*/
|
|
357
|
+
function assignKeys(entries, byFields) {
|
|
358
|
+
const groups = /* @__PURE__ */ new Map();
|
|
359
|
+
for (const entry of entries) {
|
|
360
|
+
if (entry.item.access.kind !== "role") continue;
|
|
361
|
+
const key = entry.item.grant.permission.key;
|
|
362
|
+
const byCondition = groups.get(key) ?? /* @__PURE__ */ new Map();
|
|
363
|
+
const sig = signature(entry, byFields);
|
|
364
|
+
byCondition.set(sig, [...byCondition.get(sig) ?? [], entry]);
|
|
365
|
+
groups.set(key, byCondition);
|
|
366
|
+
}
|
|
367
|
+
const keys = /* @__PURE__ */ new Map();
|
|
368
|
+
for (const [permission, byCondition] of groups) {
|
|
369
|
+
const lists = [...byCondition.values()];
|
|
370
|
+
const named = /* @__PURE__ */ new Set();
|
|
371
|
+
for (const [index, list] of lists.entries()) {
|
|
372
|
+
const group = groupOf(permission, list);
|
|
373
|
+
if (group !== void 0 && named.has(group)) throw configError(`PermDock CLI: grants of ${permission} with different conditions share group '${group}'; a group names one condition`);
|
|
374
|
+
if (group !== void 0) named.add(group);
|
|
375
|
+
const grantKey = group !== void 0 ? `${permission}#${group}` : lists.length === 1 ? permission : `${permission}#${index + 1}`;
|
|
376
|
+
for (const entry of list) keys.set(entry, grantKey);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return keys;
|
|
380
|
+
}
|
|
381
|
+
function groupOf(permission, list) {
|
|
382
|
+
const names = [...new Set(list.flatMap((entry) => entry.item.grant.group === void 0 ? [] : [entry.item.grant.group]))].toSorted();
|
|
383
|
+
if (names.length > 1) throw configError(`PermDock CLI: grants of ${permission} with one condition name the groups ${names.join(", ")}; give them one group`);
|
|
384
|
+
return names[0];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* One entry per level for every allow of an assignable role on a resource
|
|
388
|
+
* with levels, keyed `<grant key>@<level>`. Only `permdock_custom_keys`
|
|
389
|
+
* hands these keys out, to a custom role whose stored allow names the level.
|
|
390
|
+
*/
|
|
391
|
+
function levelEntries(policy, ctx, keys, tables, warnings, skipClosures) {
|
|
392
|
+
const custom = ctx.customRoles;
|
|
393
|
+
if (custom?.levels !== true) return [];
|
|
394
|
+
const assignable = new Set(custom.assignable);
|
|
395
|
+
const out = [];
|
|
396
|
+
for (const [entry, grantKey] of Array.from(keys)) {
|
|
397
|
+
const { item } = entry;
|
|
398
|
+
const { grant } = item;
|
|
399
|
+
if (item.access.kind !== "role" || grant.effect !== "allow" || grant.permission.kind !== "instance" || !assignable.has(item.access.role)) continue;
|
|
400
|
+
if (grant.permission.key.includes("@")) throw configError(`PermDock CLI: permission key '${grant.permission.key}' holds '@', which separates a level in custom-role grant keys`);
|
|
401
|
+
for (const [level, condition] of policyLevels(policy, grant.permission.resource)) {
|
|
402
|
+
const where = andWhere(item.where, condition);
|
|
403
|
+
const prepared = prepare({
|
|
404
|
+
...item,
|
|
405
|
+
grant: leveled(grant, level, condition),
|
|
406
|
+
...where === void 0 ? {} : { where }
|
|
407
|
+
}, tables, warnings, skipClosures, ctx.actions);
|
|
408
|
+
if (prepared !== void 0) {
|
|
409
|
+
keys.set(prepared, `${grantKey}@${level}`);
|
|
410
|
+
out.push(prepared);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return out;
|
|
415
|
+
}
|
|
416
|
+
function compileOptional(condition, ctx) {
|
|
417
|
+
return condition === void 0 ? void 0 : compileConditionSql(condition, ctx);
|
|
418
|
+
}
|
|
419
|
+
function noteConditions(entry, warnings) {
|
|
420
|
+
const { label, grant } = entry.item;
|
|
421
|
+
const names = [...sqlFunctionNames(entry.using), ...sqlFunctionNames(entry.check)];
|
|
422
|
+
if (names.length > 0) warnings.push(`sqlFunction ${[...new Set(names)].join(", ")} on ${label}/${grant.permission.key} is portable via twin`);
|
|
423
|
+
if (hasConditionOp(entry.using, "opaque") || hasConditionOp(entry.check, "opaque")) warnings.push(`opaque SQL on ${label}/${grant.permission.key} is untestable app-side`);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Compiles every grant (role and top-level) to branches whose role check is
|
|
427
|
+
* a helper call keyed by grant key. Nothing in a branch is per-row except the
|
|
428
|
+
* portable row condition and resource-membership joins.
|
|
429
|
+
*/
|
|
430
|
+
/** A Supabase token from a permanent user: anonymous sign-ins carry `is_anonymous: true`. */
|
|
431
|
+
const PERMANENT_USER = `((select auth.jwt()) ->> 'is_anonymous') is distinct from 'true'`;
|
|
432
|
+
function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions = false) {
|
|
433
|
+
const items = collectGrants(policy);
|
|
434
|
+
const entries = items.flatMap((item) => {
|
|
435
|
+
const entry = prepare(item, tables, warnings, skipClosures, ctx.actions, rowActions);
|
|
436
|
+
return entry === void 0 ? [] : [entry];
|
|
437
|
+
});
|
|
438
|
+
const keys = assignKeys(entries, ctx.fields === "views");
|
|
439
|
+
entries.push(...levelEntries(policy, ctx, keys, tables, warnings, skipClosures));
|
|
440
|
+
const conditionedKeys = /* @__PURE__ */ new Set();
|
|
441
|
+
for (const [entry, grantKey] of keys) if (entry.using !== void 0 || entry.check !== void 0 || entry.item.grant.validity !== void 0 || entry.item.grant.requires !== void 0) conditionedKeys.add(grantKey);
|
|
442
|
+
const rows = /* @__PURE__ */ new Map();
|
|
443
|
+
for (const item of items) {
|
|
444
|
+
const holder = item.grant.breakGlass === void 0 ? void 0 : breakGlassHolder(item);
|
|
445
|
+
if (holder !== void 0) {
|
|
446
|
+
const row = {
|
|
447
|
+
role: holder.role,
|
|
448
|
+
permission: item.grant.permission.key,
|
|
449
|
+
grantKey: breakGlassKey(item.grant.permission.key),
|
|
450
|
+
scope: holder.scope,
|
|
451
|
+
effect: item.grant.effect
|
|
452
|
+
};
|
|
453
|
+
rows.set(`${row.role}\u0000${row.grantKey}\u0000${row.scope}`, row);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const branches = [];
|
|
457
|
+
const actionBranches = [];
|
|
458
|
+
const rowColumns = /* @__PURE__ */ new Map();
|
|
459
|
+
const filters = (resource, table, column) => {
|
|
460
|
+
rowColumns.set(`${table}\u0000${column}`, {
|
|
461
|
+
resource,
|
|
462
|
+
table,
|
|
463
|
+
column
|
|
464
|
+
});
|
|
465
|
+
};
|
|
466
|
+
const typed = /* @__PURE__ */ new Map();
|
|
467
|
+
const contextFor = (name) => {
|
|
468
|
+
const known = typed.get(name);
|
|
469
|
+
if (known !== void 0) return known;
|
|
470
|
+
const node = policy.resources.get(name);
|
|
471
|
+
const schema = node === void 0 ? void 0 : jsonSchemaOf(node);
|
|
472
|
+
const next = {
|
|
473
|
+
...ctx,
|
|
474
|
+
columnTypes: columnTypesOf(schema),
|
|
475
|
+
arrayColumns: arrayColumnsOf(schema)
|
|
476
|
+
};
|
|
477
|
+
typed.set(name, next);
|
|
478
|
+
return next;
|
|
479
|
+
};
|
|
480
|
+
for (const entry of entries) {
|
|
481
|
+
const { item, table } = entry;
|
|
482
|
+
const { grant, access, label } = item;
|
|
483
|
+
if (entry.command === "none") {
|
|
484
|
+
const grantKey = keys.get(entry);
|
|
485
|
+
if (access.kind === "role" && grantKey !== void 0) {
|
|
486
|
+
const row = {
|
|
487
|
+
role: access.role,
|
|
488
|
+
permission: grant.permission.key,
|
|
489
|
+
grantKey,
|
|
490
|
+
scope: access.scope,
|
|
491
|
+
effect: grant.effect
|
|
492
|
+
};
|
|
493
|
+
rows.set(`${row.role}\u0000${row.grantKey}\u0000${row.scope}`, row);
|
|
494
|
+
}
|
|
495
|
+
if (!rowActions) continue;
|
|
496
|
+
}
|
|
497
|
+
const actionOnly = entry.command === "none";
|
|
498
|
+
const command = actionOnly ? "select" : entry.command;
|
|
499
|
+
const target = actionOnly ? actionBranches : branches;
|
|
500
|
+
const rowCtx = {
|
|
501
|
+
...contextFor(item.grant.permission.resource),
|
|
502
|
+
permission: grant.permission.key
|
|
503
|
+
};
|
|
504
|
+
noteConditions(entry, warnings);
|
|
505
|
+
for (const field of [...indexedFields(entry.using), ...indexedFields(entry.check)]) filters(grant.permission.resource, table, field);
|
|
506
|
+
const grantKey = keys.get(entry);
|
|
507
|
+
let accessExpr;
|
|
508
|
+
if (access.kind === "role" && grantKey !== void 0) {
|
|
509
|
+
const column = access.scope === "global" ? void 0 : scopeField(policy.resources.get(grant.permission.resource), access.scope, policy.scopes);
|
|
510
|
+
accessExpr = accessSql(ctx, access.scope, grantKey, column);
|
|
511
|
+
if (column !== void 0) filters(grant.permission.resource, table, column);
|
|
512
|
+
const row = {
|
|
513
|
+
role: access.role,
|
|
514
|
+
permission: grant.permission.key,
|
|
515
|
+
grantKey,
|
|
516
|
+
scope: access.scope,
|
|
517
|
+
effect: grant.effect
|
|
518
|
+
};
|
|
519
|
+
rows.set(`${row.role}\u0000${row.grantKey}\u0000${row.scope}`, row);
|
|
520
|
+
}
|
|
521
|
+
const linkOnly = access.kind === "resource" && ctx.capabilities === true && ctx.memberships?.resource?.[access.resource] === void 0;
|
|
522
|
+
if (access.kind === "actor") accessExpr = `${subjectClaimSql(ctx, "client_id")} is not null or ${subjectClaimJsonSql(ctx, "act")} is not null`;
|
|
523
|
+
if (access.kind === "resource" && !linkOnly) accessExpr = compileConditionSql(resourceCondition(item, policy, ctx), {
|
|
524
|
+
...ctx,
|
|
525
|
+
permission: grant.permission.key
|
|
526
|
+
});
|
|
527
|
+
if (linkOnly) warnings.push(`no ${access.resource} memberships table: only link capabilities reach ${label}/${grant.permission.key}`);
|
|
528
|
+
const validity = validitySql(grant.validity);
|
|
529
|
+
accessExpr = andSql(accessExpr, validity);
|
|
530
|
+
for (const key of grant.requires ?? []) accessExpr = andSql(accessExpr, requiresSql(ctx, key, policy.resources.get(grant.permission.resource)));
|
|
531
|
+
if (ctx.anonymousSignIns === "deny" && access.kind !== "anyone") accessExpr = andSql(accessExpr, PERMANENT_USER);
|
|
532
|
+
if (ctx.apiKeys !== void 0 && grant.effect === "allow" && access.kind !== "anyone" && access.kind !== "role") {
|
|
533
|
+
const calls = (requiresStandIn(grant) ? grant.requires ?? [] : [grant.permission.key, ...grant.requires ?? []]).map((key) => `(select ${apiKeyAllowsCall(ctx, quoteLiteral(key))})`);
|
|
534
|
+
accessExpr = andSql(accessExpr, calls.length === 1 ? calls[0] ?? "" : `(${calls.join(" and ")})`);
|
|
535
|
+
}
|
|
536
|
+
const using = compileOptional(entry.using, rowCtx);
|
|
537
|
+
const check = compileOptional(entry.check, rowCtx);
|
|
538
|
+
const fields = grant.fields === void 0 ? {} : { fields: grant.fields };
|
|
539
|
+
if (!linkOnly) target.push({
|
|
540
|
+
table,
|
|
541
|
+
command,
|
|
542
|
+
effect: grant.effect,
|
|
543
|
+
roles: access.kind === "anyone" ? ["anon", "authenticated"] : ["authenticated"],
|
|
544
|
+
label,
|
|
545
|
+
resource: grant.permission.resource,
|
|
546
|
+
permissionKey: grant.permission.key,
|
|
547
|
+
...fields,
|
|
548
|
+
...grantKey === void 0 ? {} : { grantKey },
|
|
549
|
+
...accessExpr === void 0 ? {} : { access: accessExpr },
|
|
550
|
+
...using === void 0 ? {} : { using },
|
|
551
|
+
...check === void 0 ? {} : { check }
|
|
552
|
+
});
|
|
553
|
+
const linked = ctx.capabilities === true ? capabilityAccess(item, policy, ctx) : void 0;
|
|
554
|
+
if (linked !== void 0) target.push({
|
|
555
|
+
table,
|
|
556
|
+
command,
|
|
557
|
+
effect: grant.effect,
|
|
558
|
+
roles: ["anon"],
|
|
559
|
+
label,
|
|
560
|
+
resource: grant.permission.resource,
|
|
561
|
+
permissionKey: grant.permission.key,
|
|
562
|
+
...fields,
|
|
563
|
+
access: andSql(linked, validity) ?? linked,
|
|
564
|
+
...using === void 0 ? {} : { using },
|
|
565
|
+
...check === void 0 ? {} : { check }
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
branches: ensureSelectCoverage(branches, warnings),
|
|
570
|
+
actionBranches,
|
|
571
|
+
rolePermissions: withAliasRows(policy, [...rows.values()]),
|
|
572
|
+
conditionedKeys,
|
|
573
|
+
rowColumns: [...rowColumns.values()],
|
|
574
|
+
levelReach: ctx.customRoles?.levels === true ? levelReachOf(policy, keys) : []
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Each row again under every key its permission was renamed from, so SQL that
|
|
579
|
+
* still calls `permdock_has('<old key>')` keeps the access the current key
|
|
580
|
+
* has. A split grant key (`key#n`) keeps its suffix; break-glass rows are not
|
|
581
|
+
* copied.
|
|
582
|
+
*/
|
|
583
|
+
function withAliasRows(policy, rows) {
|
|
584
|
+
const former = new Map(listPermissions(policy.permissions).map((leaf) => [leaf.key, formerKeys(leaf)]));
|
|
585
|
+
const out = [...rows];
|
|
586
|
+
for (const row of rows) {
|
|
587
|
+
const suffix = row.grantKey.slice(row.permission.length);
|
|
588
|
+
if (!row.grantKey.startsWith(row.permission) || suffix !== "" && (!/^#[a-z0-9][a-z0-9_-]*$/u.test(suffix) || suffix === "#break-glass")) continue;
|
|
589
|
+
for (const old of former.get(row.permission) ?? []) out.push({
|
|
590
|
+
...row,
|
|
591
|
+
permission: old,
|
|
592
|
+
grantKey: `${old}${suffix}`
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
return out;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Postgres needs SELECT access to find rows for UPDATE / DELETE and for
|
|
599
|
+
* RETURNING, so a table with only update or delete grants gets matching
|
|
600
|
+
* SELECT branches.
|
|
601
|
+
*/
|
|
602
|
+
function ensureSelectCoverage(branches, warnings) {
|
|
603
|
+
const extra = [];
|
|
604
|
+
const readable = new Set(branches.filter((item) => item.command === "select" && item.effect === "allow").map((item) => item.table));
|
|
605
|
+
const covered = /* @__PURE__ */ new Set();
|
|
606
|
+
for (const item of branches) {
|
|
607
|
+
if (item.effect !== "allow" || item.command !== "update" && item.command !== "delete" || readable.has(item.table)) continue;
|
|
608
|
+
const { check: _check, ...rest } = item;
|
|
609
|
+
extra.push({
|
|
610
|
+
...rest,
|
|
611
|
+
command: "select",
|
|
612
|
+
coverage: true
|
|
613
|
+
});
|
|
614
|
+
if (!covered.has(item.table)) {
|
|
615
|
+
covered.add(item.table);
|
|
616
|
+
warnings.push(`added SELECT coverage for ${item.table} (${item.permissionKey})`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return [...branches, ...extra];
|
|
620
|
+
}
|
|
621
|
+
//#endregion
|
|
23
622
|
//#region src/cli/sql/helper-calls.ts
|
|
24
623
|
/**
|
|
25
624
|
* Tables whose policies other packages write by calling the SQL helpers
|
|
@@ -46,7 +645,7 @@ function helperCallKeys(sql, blind = false) {
|
|
|
46
645
|
for (const [, name = "", literal = "", extra, falsy] of sql.matchAll(HELPER_CALL)) {
|
|
47
646
|
const byPermission = /_permission$/iu.test(name);
|
|
48
647
|
if (blind && byPermission && (extra === void 0 || falsy !== void 0)) continue;
|
|
49
|
-
const key = literal.replaceAll("''", "'")
|
|
648
|
+
const key = beforeFirst(literal.replaceAll("''", "'"), "#");
|
|
50
649
|
if (key !== "") keys.add(key);
|
|
51
650
|
}
|
|
52
651
|
return [...keys];
|
|
@@ -74,7 +673,7 @@ function helperTablePolicies(sql) {
|
|
|
74
673
|
}
|
|
75
674
|
return found;
|
|
76
675
|
}
|
|
77
|
-
/** `pg_policies` rows on {@link HELPER_TABLES}, read by `rls verify --db`. */
|
|
676
|
+
/** `pg_policies` rows on {@link HELPER_TABLES}, read by `rls verify --db-url-stdin`. */
|
|
78
677
|
const HELPER_TABLE_POLICIES_SQL = `select schemaname || '.' || tablename as target, policyname, coalesce(qual, '') || ' ' || coalesce(with_check, '') as body
|
|
79
678
|
from pg_policies
|
|
80
679
|
where (schemaname, tablename) in (('storage', 'objects'), ('realtime', 'messages'))`;
|
|
@@ -92,7 +691,7 @@ function helperTablePoliciesFromRows(rows) {
|
|
|
92
691
|
}
|
|
93
692
|
return found;
|
|
94
693
|
}
|
|
95
|
-
/** The message PD037 and `rls verify --db` report for one policy. */
|
|
694
|
+
/** The message PD037 and `rls verify --db-url-stdin` report for one policy. */
|
|
96
695
|
function rowConditionMessage(policy, keys) {
|
|
97
696
|
return `${policy.table} policy '${policy.name}' calls the SQL helpers with ${keys.join(", ")}, whose grants carry row conditions the helpers do not check: the policy grants more than the application does`;
|
|
98
697
|
}
|
|
@@ -118,10 +717,9 @@ function statementFor(fixture, action, table) {
|
|
|
118
717
|
const quoted = quoteTable(table);
|
|
119
718
|
const id = quoteIdent("id");
|
|
120
719
|
const key = rowId(fixture.row);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
case "
|
|
124
|
-
case "get": return {
|
|
720
|
+
const command = defaultSqlCommand(action) ?? "select";
|
|
721
|
+
switch (command) {
|
|
722
|
+
case "select": return {
|
|
125
723
|
sql: `select ${id} from ${quoted} where ${id} = $1`,
|
|
126
724
|
values: [key]
|
|
127
725
|
};
|
|
@@ -132,7 +730,7 @@ function statementFor(fixture, action, table) {
|
|
|
132
730
|
values: [key, ...next.map(([, value]) => value)]
|
|
133
731
|
};
|
|
134
732
|
}
|
|
135
|
-
case "
|
|
733
|
+
case "insert": {
|
|
136
734
|
const cols = columnsOf(fixture.row);
|
|
137
735
|
if (cols.length === 0) return {
|
|
138
736
|
sql: `insert into ${quoted} (${id}) values ($1) returning ${id}`,
|
|
@@ -147,10 +745,7 @@ function statementFor(fixture, action, table) {
|
|
|
147
745
|
sql: `delete from ${quoted} where ${id} = $1 returning ${id}`,
|
|
148
746
|
values: [key]
|
|
149
747
|
};
|
|
150
|
-
default: return
|
|
151
|
-
sql: `select ${id} from ${quoted} where ${id} = $1`,
|
|
152
|
-
values: [key]
|
|
153
|
-
};
|
|
748
|
+
default: return command;
|
|
154
749
|
}
|
|
155
750
|
}
|
|
156
751
|
function rowId(row) {
|
|
@@ -363,7 +958,7 @@ function closureDepths(plan) {
|
|
|
363
958
|
*/
|
|
364
959
|
function relationArm(ctx, plan, node, relation, asked, tables) {
|
|
365
960
|
const spec = node.relations[relation];
|
|
366
|
-
if (!isEdgeRelation(spec) && !isPrincipalRelation(spec) && (!isFieldRelation(spec) || spec.memberOf !== void 0)) throw
|
|
961
|
+
if (!isEdgeRelation(spec) && !isPrincipalRelation(spec) && (!isFieldRelation(spec) || spec.memberOf !== void 0)) throw configError(`PermDock CLI: relation '${relation}' on ${node.name} is not a graph relation RLS can compile`);
|
|
367
962
|
const guard = asked.length === 1 ? `(p_relation is null or p_relation = ${quoteLiteral(asked[0] ?? relation)})` : `(p_relation is null or p_relation in (${asked.map(quoteLiteral).join(", ")}))`;
|
|
368
963
|
return ` select a.id from (${graphSqlText(relationArmSql(node.name, relation, {
|
|
369
964
|
resources: new Map([...plan].map(([name, item]) => [name, item.node])),
|
|
@@ -393,7 +988,7 @@ grant execute on function ${fn}(text) to authenticated;`;
|
|
|
393
988
|
/** `permdock_link_<resource>_<link>(ids)`: ids of `resource` whose link points into `ids`, read past the table's own policies. */
|
|
394
989
|
function linkSql(ctx, node, link, tables) {
|
|
395
990
|
const spec = Object.hasOwn(node.links, link) ? node.links[link] : void 0;
|
|
396
|
-
if (spec === void 0) throw
|
|
991
|
+
if (spec === void 0) throw configError(`PermDock CLI: ${node.name} has no link '${link}'`);
|
|
397
992
|
const fn = qualified(ctx, linkHelper(node.name, link));
|
|
398
993
|
return `-- ${node.name}.${link}: rows whose ${spec.field} points at one of the given ${spec.resource} ids
|
|
399
994
|
create or replace function ${fn}(p_ids text[])
|
|
@@ -607,9 +1202,9 @@ function graphSql(ctx, plan, tables) {
|
|
|
607
1202
|
for (const name of plan.keys()) {
|
|
608
1203
|
const helper = graphHelper(name);
|
|
609
1204
|
const sql = graphSqlName(name) ?? name;
|
|
610
|
-
if (ctx.scopes.some((scope) => scope.name === sql)) throw
|
|
1205
|
+
if (ctx.scopes.some((scope) => scope.name === sql)) throw configError(`PermDock CLI: resource '${name}' has graph relations and shares its SQL name with the ${sql} scope, so ${helper} would replace the scope helper; rename the resource (permdock doctor PD032)`);
|
|
611
1206
|
const other = sqlNames.get(sql);
|
|
612
|
-
if (other !== void 0) throw
|
|
1207
|
+
if (other !== void 0) throw configError(`PermDock CLI: resources '${other}' and '${name}' both name the SQL helper ${helper}; rename one (permdock doctor PD032)`);
|
|
613
1208
|
sqlNames.set(sql, name);
|
|
614
1209
|
}
|
|
615
1210
|
const entries = dependencyOrder(plan);
|
|
@@ -628,7 +1223,7 @@ function graphSql(ctx, plan, tables) {
|
|
|
628
1223
|
//#endregion
|
|
629
1224
|
//#region src/cli/rls/verify-tree.ts
|
|
630
1225
|
const SYSTEM_ENTROPY = Object.freeze({
|
|
631
|
-
now: () =>
|
|
1226
|
+
now: () => nowMillis(),
|
|
632
1227
|
randomUUID: () => randomUUID()
|
|
633
1228
|
});
|
|
634
1229
|
const SUBJECTS = 3;
|
|
@@ -738,7 +1333,7 @@ function placeholder(column, entropy) {
|
|
|
738
1333
|
if (type === "uuid") return entropy.randomUUID();
|
|
739
1334
|
if (type === "boolean") return false;
|
|
740
1335
|
if (/^(smallint|integer|bigint|numeric|real|double precision)/u.test(type)) return 0;
|
|
741
|
-
if (type.startsWith("timestamp") || type === "date") return
|
|
1336
|
+
if (type.startsWith("timestamp") || type === "date") return isoAt(entropy.now());
|
|
742
1337
|
if (type === "json" || type === "jsonb") return {};
|
|
743
1338
|
return "permdock-tree";
|
|
744
1339
|
}
|
|
@@ -748,7 +1343,7 @@ function distinctPlaceholder(column, index, entropy) {
|
|
|
748
1343
|
if (typeof value === "number") return value + index;
|
|
749
1344
|
if (typeof value !== "string" || checkedValue(column.checks) !== void 0) return value;
|
|
750
1345
|
if (column.type === "uuid") return value;
|
|
751
|
-
if (column.type.startsWith("timestamp") || column.type === "date") return
|
|
1346
|
+
if (column.type.startsWith("timestamp") || column.type === "date") return isoAt(Date.parse(value) + index * 864e5);
|
|
752
1347
|
return `${value}-${String(index + 1)}`;
|
|
753
1348
|
}
|
|
754
1349
|
function asRequiredColumn(row) {
|
|
@@ -851,16 +1446,16 @@ async function verifyTree(input) {
|
|
|
851
1446
|
for (const [index, item] of tree.entries()) {
|
|
852
1447
|
const holder = subjects[index % SUBJECTS];
|
|
853
1448
|
if (holder === void 0 || index % 5 !== 1) continue;
|
|
854
|
-
const expiresAt = spec.expiresAt !== void 0 && index % 10 === 6 ?
|
|
1449
|
+
const expiresAt = spec.expiresAt !== void 0 && index % 10 === 6 ? now - 864e5 : void 0;
|
|
855
1450
|
edgeRows.push({
|
|
856
1451
|
[object]: item.id,
|
|
857
1452
|
[subject]: holder,
|
|
858
|
-
...spec.expiresAt === void 0 ? {} : { [spec.expiresAt]: expiresAt
|
|
1453
|
+
...spec.expiresAt === void 0 ? {} : { [spec.expiresAt]: expiresAt === void 0 ? null : isoAt(expiresAt) }
|
|
859
1454
|
});
|
|
860
1455
|
list.push({
|
|
861
1456
|
id: String(item.id),
|
|
862
1457
|
principal: holder,
|
|
863
|
-
...expiresAt === void 0 ? {} : { expiresAt: expiresAt
|
|
1458
|
+
...expiresAt === void 0 ? {} : { expiresAt: expiresAt / 1e3 }
|
|
864
1459
|
});
|
|
865
1460
|
}
|
|
866
1461
|
if (edgeRows.length > 0) await seed(spec.edge, edgeRows);
|
|
@@ -883,7 +1478,7 @@ async function verifyTree(input) {
|
|
|
883
1478
|
rows[child.name] = [...await seed(tables?.[child.name] ?? child.name, childRows)];
|
|
884
1479
|
}
|
|
885
1480
|
}
|
|
886
|
-
const relations =
|
|
1481
|
+
const relations = memoryRelationSource(policy.permissions, {
|
|
887
1482
|
rows,
|
|
888
1483
|
edges
|
|
889
1484
|
});
|
|
@@ -892,7 +1487,7 @@ async function verifyTree(input) {
|
|
|
892
1487
|
let granted = 0;
|
|
893
1488
|
const reads = listPermissions(policy.permissions).filter((leaf) => leaf.kind === "instance" && commandFor(leaf.action, input.config.rls?.actions) === "select" && rows[leaf.resource] !== void 0);
|
|
894
1489
|
for (const subject of subjects) {
|
|
895
|
-
const permdock = await
|
|
1490
|
+
const permdock = await getPermDock(policy, {
|
|
896
1491
|
principal: {
|
|
897
1492
|
id: subject,
|
|
898
1493
|
roles: []
|
|
@@ -960,7 +1555,7 @@ async function viewFields(query, table, key) {
|
|
|
960
1555
|
async function seedCustomRoles(query, schema, customRoles, scopes) {
|
|
961
1556
|
for (const row of customRoleRows(schema, customRoles, scopes)) {
|
|
962
1557
|
const result = await query(row.sql, row.values);
|
|
963
|
-
if (result.code !== void 0) throw new Error(`PermDock CLI: rls verify --db could not seed custom roles (${result.code}); connect as a role that owns the custom_role_* tables`);
|
|
1558
|
+
if (result.code !== void 0) throw new Error(`PermDock CLI: rls verify --db-url-stdin could not seed custom roles (${result.code}); connect as a role that owns the custom_role_* tables`);
|
|
964
1559
|
}
|
|
965
1560
|
}
|
|
966
1561
|
async function bindSubject(query, fixture, subject) {
|
|
@@ -1084,17 +1679,17 @@ function grantKind(policy, key) {
|
|
|
1084
1679
|
return "portable";
|
|
1085
1680
|
}
|
|
1086
1681
|
async function runRlsVerify(input) {
|
|
1087
|
-
const connect = input.connect ?? pgConnector("permdock rls verify --db");
|
|
1682
|
+
const connect = input.connect ?? pgConnector("permdock rls verify --db-url-stdin", input.cwd);
|
|
1088
1683
|
const policyPath = input.from ?? input.config.policy;
|
|
1089
1684
|
if (policyPath === void 0) return {
|
|
1090
1685
|
code: 2,
|
|
1091
|
-
output: "PermDock CLI: rls verify needs policy in the config or --
|
|
1686
|
+
output: "PermDock CLI: rls verify needs policy in the config or --policy"
|
|
1092
1687
|
};
|
|
1093
1688
|
const policy = await loadPolicyAt(input.cwd, policyPath);
|
|
1094
1689
|
if (input.tree === true) {
|
|
1095
1690
|
if (input.db === void 0) return {
|
|
1096
1691
|
code: 2,
|
|
1097
|
-
output: "PermDock CLI: rls verify --tree needs --db"
|
|
1692
|
+
output: "PermDock CLI: rls verify --tree needs a database: --db-url-stdin, DATABASE_URL or SUPABASE_DB_URL"
|
|
1098
1693
|
};
|
|
1099
1694
|
try {
|
|
1100
1695
|
return await verifyTreeAgainstDatabase(input.db, policy, input.config, connect);
|
|
@@ -1120,7 +1715,7 @@ async function runRlsVerify(input) {
|
|
|
1120
1715
|
continue;
|
|
1121
1716
|
}
|
|
1122
1717
|
const kind = grantKind(policy, fixture.action);
|
|
1123
|
-
const permdock = await
|
|
1718
|
+
const permdock = await getPermDock(policy, fixtureSubject(fixture.subject), { customRoles: memoryCustomRoleSource(customRoles) });
|
|
1124
1719
|
const granted = canFixture(permdock, permission, fixtureRow(fixture, permission.kind));
|
|
1125
1720
|
const outcome = granted ? "granted" : "denied";
|
|
1126
1721
|
if (fixture.expected !== void 0 && fixture.expected !== outcome) mismatches.push(`${fixture.action}: in-process ${outcome}, expected ${fixture.expected}`);
|
|
@@ -1172,4 +1767,4 @@ async function runRlsVerify(input) {
|
|
|
1172
1767
|
};
|
|
1173
1768
|
}
|
|
1174
1769
|
//#endregion
|
|
1175
|
-
export { graphSql as a, callsHelper as c, rowConditionMessage as d, graphPlan as i, helperCallKeys as l, runRlsVerify as n, HELPER_TABLES as o, closureDepths as r, ROW_CONDITION_FIX as s, canFixture as t, helperTablePolicies as u };
|
|
1770
|
+
export { indexesSql as _, graphSql as a, callsHelper as c, rowConditionMessage as d, commandFor as f, indexTargets as g, COLUMNS_SQL as h, graphPlan as i, helperCallKeys as l, tableFor$1 as m, runRlsVerify as n, HELPER_TABLES as o, compileGrants as p, closureDepths as r, ROW_CONDITION_FIX as s, canFixture as t, helperTablePolicies as u, pruneIndexTargets as v, readTableIndexFacts as y };
|