permdock 0.16.0 → 0.18.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/dist/a2a/index.d.ts +4 -4
- package/dist/a2a/index.js +3 -3
- package/dist/{adapter-context-CIQpQxbE.js → adapter-context-OHikyRqV.js} +1 -1
- package/dist/{adoption-scan-C2BCiUEG.js → adoption-scan-CxExBYSJ.js} +1 -1
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/ai-sdk/index.d.ts +3 -3
- package/dist/ai-sdk/index.js +3 -3
- package/dist/approval-normalize-CUjgSsOl.js +189 -0
- package/dist/approval-policies-oNnckT12.d.ts +138 -0
- package/dist/{approval-prompt-3BqWczTt.d.ts → approval-prompt-Bg7gDSqd.d.ts} +1 -1
- package/dist/approvals/index.d.ts +5 -4
- package/dist/approvals/index.js +7 -6
- package/dist/{approvals-DnIBTuXE.js → approvals-DGId_hgJ.js} +3 -3
- package/dist/{approvers-B4r0N_Uz.js → approvers-BaNwKYgd.js} +20 -1
- package/dist/{arazzo-ClYz2Z0W.js → arazzo--riLuGY0.js} +4 -4
- package/dist/{arazzo-CHoYnQXO.js → arazzo-CEDbb-uM.js} +2 -2
- package/dist/{args-0FrVs5EH.js → args-iafbHiXn.js} +2 -5
- package/dist/astro/index.d.ts +4 -4
- package/dist/astro/index.js +2 -2
- package/dist/{audience-DTjR8xet.js → audience-B4YR9UpV.js} +1 -1
- package/dist/{auth--yPyZNgr.js → auth-j3Eu-j4h.js} +1 -1
- package/dist/authz-topic-Dbc-HDOS.js +15 -0
- package/dist/authzen/index.d.ts +5 -5
- package/dist/authzen/index.js +2 -2
- package/dist/{automation-C6Jg75xq.d.ts → automation-Bfa_wD1h.d.ts} +1 -1
- package/dist/better-auth/index.d.ts +1 -1
- package/dist/better-auth/index.js +10 -8
- package/dist/better-supabase/index.d.ts +335 -38
- package/dist/better-supabase/index.js +778 -117
- package/dist/{better-supabase-Dirf_8Ec.js → better-supabase-BJtQ-kNf.js} +26 -10
- package/dist/{bind-BNQA9JK9.js → bind-B4pMH9bm.js} +6 -6
- package/dist/{bind-YYjvfpB9.d.ts → bind-DR6ku3A9.d.ts} +7 -7
- package/dist/{budget-BELja5A9.js → budget-BBCPDOGP.js} +1 -1
- package/dist/{budget-hYsPz_yE.d.ts → budget-CNLsV2SL.d.ts} +1 -1
- package/dist/{build-instance-BWXqwQYU.js → build-instance-BFMFQdE_.js} +3 -2
- package/dist/{capability-Di9tiUvi.d.ts → capability-BZ_veK9o.d.ts} +1 -1
- package/dist/{capability-BqX94r07.js → capability-zN6T9fxe.js} +2 -3
- package/dist/{casbin-D_rVQ31U.js → casbin-DCQHkkyj.js} +2 -2
- package/dist/{casbin-C9EvElBb.js → casbin-bKC1FpEx.js} +2 -2
- package/dist/{casl-C4OINZHI.js → casl-CjBVJj8R.js} +2 -2
- package/dist/{casl-CHHoCldP.js → casl-CkBssiB1.js} +1 -1
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +7 -7
- package/dist/catalog-Caqhbdix.js +1 -0
- package/dist/{catalog-CGlSwU6j.js → catalog-D1fw8qif.js} +5 -5
- package/dist/{catalog-doc-DjMKzJp9.js → catalog-doc-C4DGfJCO.js} +3 -3
- package/dist/{catalog-operations-DeHTrJSV.js → catalog-operations-BD22drQf.js} +4 -4
- package/dist/{catalog-amPDr0JQ.js → catalog-zzNuR3sY.js} +3 -3
- package/dist/{challenge-DImNcR7B.js → challenge-B2O2OLEk.js} +2 -2
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +2 -2
- package/dist/chat-sdk/index.d.ts +3 -3
- package/dist/{checks-CGkP3Z7A.js → checks-fyDOhcz0.js} +33 -1
- package/dist/claude-agent/index.d.ts +3 -3
- package/dist/claude-agent/index.js +2 -2
- package/dist/clerk/index.d.ts +2 -2
- package/dist/clerk/index.js +10 -7
- package/dist/cli/bin.js +1 -1
- package/dist/cli/index.d.ts +5 -5
- package/dist/cli/index.js +3 -3
- package/dist/client-CH7vzSAY.d.ts +32 -0
- package/dist/cloud/index.d.ts +8 -8
- package/dist/cloud/index.js +1 -1
- package/dist/{cloud-DdaJ7ILM.js → cloud-C0vXBmLG.js} +4 -4
- package/dist/{codemod-B1USJS7a.js → codemod-C-L0w-u7.js} +3 -3
- package/dist/{codemod-ammy6wv4.js → codemod-eYI1FbeJ.js} +395 -8
- package/dist/{collect-DnXL4B8c.js → collect-CU85z6Ta.js} +4 -4
- package/dist/{collect-WCl8cJQV.js → collect-D878zLDP.js} +4 -4
- package/dist/collect-KCtOigxx.js +1 -0
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -1
- package/dist/{compile-EzEMB-7v.js → compile-B_oJqmIg.js} +4 -4
- package/dist/{compile-BEklRUYo.d.ts → compile-DBm0ub4p.d.ts} +2 -2
- package/dist/{completion--Y4YItFZ.js → completion-Dauc6pVT.js} +3 -3
- package/dist/{config-CcLlvEYW.js → config-COHmivz9.js} +4 -4
- package/dist/{config-BQ77xMZk.js → config-DWp2PucY.js} +2 -2
- package/dist/{config-report-DVMOLJ7F.js → config-report-DKpFm5ID.js} +2 -2
- package/dist/{context-BnIzxTNE.js → context-DeammvFl.js} +2 -2
- package/dist/convex/index.d.ts +4 -4
- package/dist/convex/index.js +3 -3
- package/dist/{core-lxoANyai.js → core-B8sLve3z.js} +7 -7
- package/dist/{core-CFepFiDP.d.ts → core-D87h2kpR.d.ts} +2 -2
- package/dist/{create-BLPKnJKa.js → create-3A7J9KRR.js} +6 -5
- package/dist/{create-CDd9c22W.d.ts → create-BrQXL-Wt.d.ts} +6 -6
- package/dist/{create-BPUopdfL.d.ts → create-BzwjZuRS.d.ts} +7 -7
- package/dist/{create-Coq5fIrq.js → create-C-tm3KIF.js} +5 -5
- package/dist/{create-DBvUsgzK.d.ts → create-CtoT1lIQ.d.ts} +4 -4
- package/dist/{credential-CyyyvCsL.js → credential-B3Hj4puZ.js} +4 -3
- package/dist/{credentials-CdDxp7jN.js → credentials-dkycUsY3.js} +1 -1
- package/dist/decision-CaG5_eNJ.js +21 -0
- package/dist/{delegation-CDB6_keF.js → delegation-aK4T_5WU.js} +3 -3
- package/dist/delivery/index.d.ts +4 -4
- package/dist/delivery/index.js +1 -1
- package/dist/{denied-text-DHldhVUv.d.ts → denied-text-DlIxt8fh.d.ts} +1 -1
- package/dist/{describe-BbFPBSms.d.ts → describe-BKtC_8PG.d.ts} +1 -1
- package/dist/{describe-Bb4hGK5d.js → describe-BZAlPHxT.js} +2 -1
- package/dist/{diff-DfmYaIhB.js → diff-BnNHqtHj.js} +8 -8
- package/dist/{diff-CxtbOi2l.js → diff-CBCzOTWM.js} +2 -2
- package/dist/{doctor-D1Xnmy6u.js → doctor-CkYKCuX7.js} +233 -30
- package/dist/{doctor-CwYc2lJb.js → doctor-xUpR5rXg.js} +3 -3
- package/dist/drizzle/index.d.ts +3 -3
- package/dist/drizzle/index.js +1 -1
- package/dist/elysia/index.d.ts +4 -4
- package/dist/elysia/index.js +3 -3
- package/dist/{errors-Vpu6rKHl.js → errors-BhRkoNS7.js} +0 -2
- package/dist/{errors-BIM4MFsJ.d.ts → errors-TpzkEKgJ.d.ts} +1 -1
- package/dist/{evaluate-BAmf_AQd.js → evaluate-Czbj7thS.js} +18 -15
- package/dist/{evaluations-DOWuBpWv.d.ts → evaluations-CisTTURl.d.ts} +3 -3
- package/dist/{evaluations-IBceAeKT.js → evaluations-h3KoJAPa.js} +3 -22
- package/dist/eve/index.d.ts +4 -4
- package/dist/eve/index.js +3 -3
- package/dist/express/index.d.ts +4 -4
- package/dist/express/index.js +3 -3
- package/dist/fastify/index.d.ts +4 -4
- package/dist/fastify/index.js +3 -3
- package/dist/{fetch-adapter-BIbBAtPZ.d.ts → fetch-adapter-BdmfnbJM.d.ts} +2 -2
- package/dist/{fetch-adapter-dPTgJVHd.js → fetch-adapter-DH-NJPbL.js} +2 -2
- package/dist/{fields-DI8s3cPl.js → fields-CrjL6A49.js} +3 -3
- package/dist/{files-B3AA05f1.js → files-CfiRtQ-m.js} +1 -1
- package/dist/{fixtures-Bm4_K1ke.js → fixtures-Cemqurt8.js} +10 -4
- package/dist/{flags-DXjoTVbS.js → flags-BlmpmXoY.js} +2 -2
- package/dist/{from-snapshot-DCakWRsr.js → from-snapshot-DMlSSo9b.js} +14 -12
- package/dist/{gen-DwgCSC6V.js → gen-BEIN-DRM.js} +2 -2
- package/dist/{gen-DuAhghDw.js → gen-mf-MRwVr.js} +9 -9
- package/dist/{generate-CRVV5V7l.js → generate-BqkZq8Dr.js} +2 -2
- package/dist/{grantee-conditions-BrYPe-w0.js → grantee-conditions-DX4K_gGI.js} +1 -1
- package/dist/{grantee-match-7RZZvoT_.js → grantee-match-BP-k4vRJ.js} +2 -2
- package/dist/h3/index.d.ts +4 -4
- package/dist/h3/index.js +3 -3
- package/dist/{helpers-DJicaG6f.js → helpers-DmqGy63f.js} +85 -13
- package/dist/hono/index.d.ts +4 -4
- package/dist/hono/index.js +3 -3
- package/dist/{hosted-DVxlLZMe.d.ts → hosted-BFNXwcjd.d.ts} +1 -1
- package/dist/{hosted-D0IzESu_.js → hosted-waouw2V-.js} +12 -10
- package/dist/{http-DOL33cO0.js → http-DhMWeB_h.js} +1 -1
- package/dist/{import-CNBF96hB.js → import-C1XXpG_m.js} +4 -4
- package/dist/{import-Dfe2unKO.js → import-DObwEota.js} +3 -3
- package/dist/{index-BinlXeU-.d.ts → index-BhnOOnp9.d.ts} +2 -2
- package/dist/{index-ymept53A.d.ts → index-CVGio3cH.d.ts} +4 -4
- package/dist/{index-DJwEgYzA.d.ts → index-CdMOd-GY.d.ts} +17 -11
- package/dist/{index-Cay5-Kvx.d.ts → index-CdUTE6qd.d.ts} +11 -10
- package/dist/{index-Dj2qF9VC.d.ts → index-Dfv9Toct.d.ts} +1 -1
- package/dist/{index-CV-7DWeD.d.ts → index-rFp6X_QP.d.ts} +4 -4
- package/dist/index.d.ts +18 -16
- package/dist/index.js +24 -20
- package/dist/{init-BUo2G-FI.js → init-CNJ8lLjo.js} +5 -5
- package/dist/{init-C6cWP3xW.js → init-DS3PEsCG.js} +5 -5
- package/dist/{instance-options-CoHn-0e8.d.ts → instance-options-2sFGSdsf.d.ts} +2 -2
- package/dist/{instance-snapshot-DuWf1cT3.js → instance-snapshot-BWa71my8.js} +15 -12
- package/dist/jwt/index.d.ts +4 -4
- package/dist/jwt/index.js +3 -3
- package/dist/{kernel-BD8QuMnv.js → kernel-30Z2zM_d.js} +10 -10
- package/dist/kysely/index.d.ts +3 -3
- package/dist/kysely/index.js +1 -1
- package/dist/{load-DjSU5KXO.js → load-BaEamml-.js} +1 -1
- package/dist/load-CjnlPxpL.js +1 -0
- package/dist/{login-D1MHOomI.js → login-BgK5Jloo.js} +4 -4
- package/dist/{login-DCKE5pVy.js → login-DUVzBLh7.js} +4 -4
- package/dist/{manifest-BXTbHNIV.js → manifest-BdzBobNK.js} +1 -1
- package/dist/{manifest-schema-DQkBK2Mr.js → manifest-schema-CsFsYLF-.js} +1 -1
- package/dist/{manifest-Cft51TLK.js → manifest-wclLWBVH.js} +37 -37
- package/dist/{map-claims-C-nv08_n.js → map-claims-PQwHynxV.js} +25 -11
- package/dist/{map-error-pasIGqMT.js → map-error-BH16Ah8P.js} +1 -1
- package/dist/mcp/index.d.ts +8 -8
- package/dist/mcp/index.js +9 -9
- package/dist/{migrate-BJHxQqT5.js → migrate-BbY5-Khy.js} +4 -4
- package/dist/{module-values-B1EVTkHP.js → module-values-B8tNIxdd.js} +2 -2
- package/dist/nest/index.d.ts +4 -4
- package/dist/nest/index.js +3 -3
- package/dist/next/index.d.ts +45 -13
- package/dist/next/index.js +68 -30
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +2 -2
- package/dist/node/index.d.ts +4 -4
- package/dist/node/index.js +2 -2
- package/dist/openai/index.d.ts +4 -4
- package/dist/openai/index.js +3 -3
- package/dist/openapi/index.d.ts +2 -2
- package/dist/{openapi-BwWkHxvW.js → openapi-C4gFqeRz.js} +4 -4
- package/dist/{openapi-DSzQN7Zu.js → openapi-zMEB9qxn.js} +5 -5
- package/dist/{operation-findings-CXtFzlKJ.js → operation-findings-CVwJHEu_.js} +1 -1
- package/dist/operation-risks-BKOcTv8J.js +13 -0
- package/dist/operations-Bm9HFecz.js +89 -0
- package/dist/orpc/index.d.ts +5 -5
- package/dist/orpc/index.js +3 -3
- package/dist/otel/index.d.ts +3 -3
- package/dist/otel/index.js +1 -1
- package/dist/{output-DlICTjBh.js → output-BFWLER4r.js} +1 -1
- package/dist/output-files-BYscrQl-.js +1 -0
- package/dist/{output-files-BIQ_9pIs.js → output-files-CM0BnH05.js} +1 -1
- package/dist/{ownership-qqBuGJXr.js → ownership-D-mIt-w0.js} +6 -6
- package/dist/{parse-1_ZheTW5.js → parse-UTC-sDrt.js} +2 -2
- package/dist/{parse-local-manifest-DEiCF4ah.js → parse-local-manifest-CxcSMQgl.js} +2 -2
- package/dist/{parse-local-manifest-CCmbhnxP.d.ts → parse-local-manifest-DvjTQNuO.d.ts} +1 -1
- package/dist/{parse-request-CwCZpyjV.js → parse-request-DOBVaeiQ.js} +1 -1
- package/dist/pdp/index.d.ts +3 -3
- package/dist/pdp/index.js +3 -3
- package/dist/{peer-Q0fuZ6hx.js → peer-BuK9F53f.js} +1 -1
- package/dist/{peer-C9LvBCcb.js → peer-n-swIV4H.js} +1 -1
- package/dist/{permdock-BvjivVb9.js → permdock-D-_WrdKu.js} +205 -102
- package/dist/{permdock-CprAPN3w.d.ts → permdock-ffY4CMd9.d.ts} +101 -131
- package/dist/{permissions-CYcHj_w7.js → permissions-B3ggI88M.js} +2 -2
- package/dist/{permissions-CVXmRILq.js → permissions-BaoLxy66.js} +1 -13
- package/dist/{pg-DQTqRrou.js → pg-Ddxsd5ZB.js} +2 -2
- package/dist/{policy-BycVKnXv.d.ts → policy-BtKSApSt.d.ts} +1 -1
- package/dist/{policy-CLQ8__GF.js → policy-cV_wwPb8.js} +5 -4
- package/dist/{policy-from-catalog-CU3An8uP.js → policy-from-catalog-B68hGC0S.js} +9 -8
- package/dist/{policy-types-DGIO_gxq.d.ts → policy-types-D-GwfQTc.d.ts} +72 -19
- package/dist/{postgrest-DAxFltvO.js → postgrest-BrjrckDG.js} +74 -19
- package/dist/{postgrest-D5nUGm-c.d.ts → postgrest-D84p5dbA.d.ts} +12 -7
- package/dist/{powersync-CBhTBhG2.js → powersync-9PYOE1wt.js} +158 -21
- package/dist/{powersync-DAfo0EL5.js → powersync-DStC_g0B.js} +3 -3
- package/dist/powersync-xNRlC4jN.js +1 -0
- package/dist/preset/index.js +1 -1
- package/dist/{preset-BjLzeFpp.js → preset-pPrVyGU5.js} +74 -7
- package/dist/prisma/index.d.ts +2 -2
- package/dist/prisma/index.js +1 -1
- package/dist/{protected-action-QEA8arRB.js → protected-action-B40tU3yP.js} +15 -5
- package/dist/{protected-action-D8x73IGm.d.ts → protected-action-Ds_EUY8B.d.ts} +14 -7
- package/dist/{protected-action-DUYqb-Ma.js → protected-action-OpOxtTsN.js} +1 -1
- package/dist/{provider-C55OGXJ7.js → provider-CY4TSSJo.js} +2 -2
- package/dist/{provider-DfLeek9O.d.ts → provider-OtnIiFWm.d.ts} +1 -1
- package/dist/{provider-store-CaTv00tr.js → provider-store-BQP_pp8s.js} +2 -2
- package/dist/{pull-CQCGy1gv.js → pull-DLP4I1Kl.js} +7 -7
- package/dist/pull-UBjDnf4b.js +1 -0
- package/dist/{push-Dq_ca4-j.js → push-BSb8j1EW.js} +8 -8
- package/dist/{pushed-policy-CDuOL2de.js → pushed-policy-BecMsTtM.js} +1 -1
- package/dist/{pushed-policy-CryQheKq.d.ts → pushed-policy-Ck3FZcoy.d.ts} +1 -1
- 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 -1
- package/dist/react-native/index.d.ts +104 -39
- package/dist/react-native/index.js +199 -40
- package/dist/refresh-on-L9UOwATg.d.ts +49 -0
- package/dist/{replay-Bs9Df2fQ.d.ts → replay-E5X9E1lC.d.ts} +1 -1
- package/dist/{request-context-Duzdf0cd.js → request-context-DM6_jY1n.js} +2 -2
- package/dist/{request-schema-DhX7UbfQ.js → request-schema-CMP8Cmxd.js} +5 -0
- package/dist/resend/index.d.ts +4 -4
- package/dist/{resolve-names-Y1Cb22cp.js → resolve-names-CBl4r4_r.js} +6 -6
- package/dist/{rls-tHu7N_Pe.js → rls-DGBL-jDY.js} +20 -20
- package/dist/{rls-C5ZGzR_B.js → rls-pAHGYgTb.js} +4 -4
- package/dist/{role-builder-BK2-T6oD.js → role-builder-CMW4wWzn.js} +3 -157
- package/dist/{rpc-vHIwyXCp.js → rpc-KwX_Jcxz.js} +2 -2
- package/dist/{run-DUq1xU4K.js → run-B7lv3NuK.js} +8 -40
- package/dist/run-UPbacxPg.js +1 -0
- package/dist/{run-ticket-DfnZCh-u.js → run-ticket-CjvXr6wj.js} +1 -1
- package/dist/{run-ticket-ylovhyFQ.d.ts → run-ticket-DPaD27_r.d.ts} +2 -2
- package/dist/{runtime-BGnrOkVH.js → runtime-rf0ZuyF3.js} +1 -1
- package/dist/{saas-DK4V-3Is.js → saas-B8H1MsQ3.js} +13 -10
- package/dist/{scan-C3HRH9Fs.js → scan-0r3OZqqK.js} +1 -1
- package/dist/{schema-DvXvxPhQ.js → schema-B_9tXrbG.js} +1 -1
- package/dist/{schema-BOJjqlWL.js → schema-CuX70xgH.js} +15 -2
- package/dist/scim/index.d.ts +1 -1
- package/dist/scim/index.js +1 -1
- package/dist/{scim-jJBJ7g1Z.js → scim-C30QBBoF.js} +2 -1
- package/dist/{scim-D8YupzX1.js → scim-CovliQUg.js} +2 -2
- package/dist/{scopes-0Up_UA2t.js → scopes-BSdYaGHs.js} +9 -40
- package/dist/server/index.d.ts +11 -11
- package/dist/server/index.js +8 -8
- package/dist/{server-3cVlTLde.js → server-D0PW6ZCw.js} +2 -2
- package/dist/{server-types-CyRN0-Cd.d.ts → server-types-3OnXjZ2D.d.ts} +8 -8
- package/dist/{shared-l0oGGetg.js → shared-BRmCJxDK.js} +2 -2
- package/dist/{shared-DrGLwM3s.js → shared-DyYVEjKC.js} +2 -2
- package/dist/{sink-B5zoUW_O.d.ts → sink-ogbVkyeo.d.ts} +2 -2
- package/dist/{skills-Celu2W6v.js → skills-B4MPRxQd.js} +2 -2
- package/dist/{skills-B2qTbaQa.js → skills-C9sTD70r.js} +2 -2
- package/dist/{snapshot-CXlzi69u.js → snapshot-DN_8J27z.js} +6 -2
- package/dist/{snapshot-cache-BjAWkchD.d.ts → snapshot-cache-B4YvlGG_.d.ts} +20 -6
- package/dist/{snapshot-cache-Bx_4xC_6.js → snapshot-cache-B66h7FBK.js} +25 -8
- package/dist/solid/index.d.ts +7 -7
- package/dist/solid/index.js +6 -6
- package/dist/solid-start/index.d.ts +4 -4
- package/dist/solid-start/index.js +3 -3
- package/dist/{sources-oB1Hg1Fm.d.ts → sources-xdQShLDU.d.ts} +1 -1
- package/dist/{src-C_pi2Rnh.js → src-Brzq_0in.js} +193 -128
- package/dist/ssf/index.d.ts +3 -3
- package/dist/ssf/index.js +1 -1
- package/dist/{standard-webhooks-DOYEuOLx.d.ts → standard-webhooks-BfPaZ6T1.d.ts} +2 -2
- package/dist/{types-tQB2CEnY.d.ts → store-DYeB-_U3.d.ts} +114 -113
- package/dist/{store-RNFvaJJv.js → store-DmVqy74I.js} +13 -7
- package/dist/{store-options-Dil3lraY.js → store-options--zWn48PK.js} +76 -7
- package/dist/{stores-DxZWDXCs.js → stores-BYFopOm1.js} +3 -3
- package/dist/{stores-DZJvxFIZ.d.ts → stores-EPul_8MN.d.ts} +5 -6
- package/dist/{stream-9y9qw51m.d.ts → stream-W2RuUYly.d.ts} +1 -1
- package/dist/{subject-BGoM5o8B.js → subject-C7RddW0G.js} +8 -1
- package/dist/supabase/client.d.ts +2 -0
- package/dist/supabase/client.js +21 -0
- package/dist/supabase/index.d.ts +37 -6
- package/dist/supabase/index.js +6 -5
- package/dist/supabase/middleware.d.ts +8 -7
- package/dist/supabase/middleware.js +6 -6
- package/dist/{supabase-CDHMsDD0.js → supabase-BiViJpQ4.js} +31 -8
- package/dist/{supabase-DYWnT800.js → supabase-CogTyJH2.js} +3 -3
- package/dist/supabase-hook-BN46IrVI.js +1 -0
- package/dist/{supabase-hook-HPgwYb9V.js → supabase-hook-DX38Hj8h.js} +393 -32
- package/dist/{supautils-SwE642N1.js → supautils-BGf6zRvM.js} +12 -12
- package/dist/svelte/index.d.ts +7 -7
- package/dist/svelte/index.js +8 -8
- 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 +5 -5
- package/dist/sveltekit/index.d.ts +4 -4
- package/dist/sveltekit/index.js +2 -2
- package/dist/{tables-DimBcVWY.js → tables-BfOk0ymL.js} +7 -7
- package/dist/tanstack-start/index.d.ts +4 -4
- package/dist/tanstack-start/index.js +2 -2
- package/dist/{tenancy-5d1zVBvK.js → tenancy-DvOHHk3-.js} +1 -1
- package/dist/terminal/index.d.ts +5 -5
- package/dist/terminal/index.js +6 -6
- package/dist/testing/core.d.ts +1 -1
- package/dist/testing/core.js +1 -1
- package/dist/testing/fixtures/index.d.ts +1 -1
- package/dist/testing/fixtures/index.js +2 -2
- package/dist/testing/index.d.ts +29 -22
- package/dist/testing/index.js +120 -48
- package/dist/testing/saas/index.d.ts +3 -3
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.js +1 -1
- package/dist/token-D6pWn8Uj.d.ts +11 -0
- package/dist/{token-DC9EQiet.js → token-DaMnIU9O.js} +24 -8
- package/dist/{tool-refusal-Qpjs_W6M.js → tool-refusal-D4spOJ7I.js} +1 -1
- package/dist/trpc/index.d.ts +5 -5
- package/dist/trpc/index.js +3 -3
- package/dist/{type-helpers-c7Xo_2Cu.d.ts → type-helpers-i1EJ3Eb1.d.ts} +1 -1
- package/dist/{types-Cklw5hJo.d.ts → types-3Y8UpFkF.d.ts} +40 -1
- package/dist/{types-DlelvQk4.d.ts → types-B8qUkGja.d.ts} +2 -2
- package/dist/{types-CN0LsqbK.d.ts → types-CCEeSCMB.d.ts} +3 -3
- package/dist/{types-B2efzoSX.d.ts → types-CDVXR6YD.d.ts} +1 -1
- package/dist/{types-DsHGPtkR.d.ts → types-Ck1KOfug.d.ts} +2 -2
- package/dist/{types-DLvhVm1r.d.ts → types-CqcsNCaN.d.ts} +16 -7
- package/dist/{types-BB-zWDcR.d.ts → types-D-JO7lh8.d.ts} +5 -5
- package/dist/{types-CEinc15V.d.ts → types-D19MK4t-.d.ts} +3 -4
- package/dist/{types-B6ExllNo.d.ts → types-DGf_cpKF.d.ts} +2 -2
- package/dist/{types-rCRV4Ge-.d.ts → types-DmbfLu2p.d.ts} +4 -4
- package/dist/{types-ZzRGoViD.d.ts → types-NYoj8JBm.d.ts} +2 -2
- package/dist/{types-CWBsAwqn.d.ts → types-o1vu6Ks3.d.ts} +4 -4
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/usage-Cl5eb-e1.js +1 -0
- package/dist/{usage-CPXeuAdT.js → usage-DC0UKe6C.js} +6 -6
- package/dist/{usage-CfZCQd0F.js → usage-DYtRVkcN.js} +2 -2
- package/dist/{validation-error-uvp-kyfO.d.ts → validation-error-Dx8PipbO.d.ts} +1 -1
- package/dist/{verify-DfT0QYNh.js → verify-Bpj0hauG.js} +19 -19
- package/dist/{version-cache-Df11C2Qp.d.ts → version-cache-DzNKWiI6.d.ts} +9 -3
- package/dist/{views-CD8wuL4Z.js → views-MWmJ6Y4P.js} +1 -1
- package/dist/vue/index.d.ts +7 -7
- package/dist/vue/index.js +6 -6
- package/dist/{waiter-CqUKX6CY.d.ts → waiter-NK_UuZbb.d.ts} +2 -2
- package/dist/{watch-DAkjm75Y.js → watch-CBHADqBH.js} +3 -3
- package/dist/watch-OYwAPCzx.js +1 -0
- package/dist/webmcp/index.d.ts +1 -1
- package/dist/webmcp/index.js +1 -1
- package/dist/{where-scope-DlCdCM_8.js → where-scope-BKrqUDVk.js} +5 -5
- package/dist/{who-can-BoDJm0Nc.js → who-can-KhVWMxtf.js} +4 -4
- package/dist/workflow/index.d.ts +3 -3
- package/dist/workflow/index.js +5 -4
- package/dist/workflow-sdk/index.d.ts +4 -4
- package/dist/{wrap-DxXB54Wu.d.ts → wrap-BHGA-SMM.d.ts} +2 -2
- package/dist/{wrap-B2wziJI_.js → wrap-C5PWzgkq.js} +1 -1
- package/package.json +8 -5
- package/schemas/approval-request-v1.json +5 -0
- package/schemas/catalog-v1.json +18 -2
- package/schemas/cli-result-v1.json +0 -2
- package/schemas/snapshot-v1.json +9 -0
- package/schemas/supabase-manifest-v1.json +1 -1
- package/skills/permdock-approvals/SKILL.md +8 -8
- package/skills/permdock-approvals/references/stores-and-resume.md +7 -3
- package/skills/permdock-audit/SKILL.md +1 -1
- package/skills/permdock-wire/references/adapters.md +3 -1
- package/dist/action-result-BcudBQOF.d.ts +0 -19
- package/dist/catalog-D3oS2GH3.js +0 -1
- package/dist/collect-D-eroIfV.js +0 -1
- package/dist/load-Dm0pSYH3.js +0 -1
- package/dist/output-files-BHOIlrfD.js +0 -1
- package/dist/powersync-0QVP_DuR.js +0 -1
- package/dist/pull-CCAChKuy.js +0 -1
- package/dist/renames-CGj7YwcQ.js +0 -175
- package/dist/run-C5P3x1EI.js +0 -1
- package/dist/supabase-hook-qsewAVZY.js +0 -1
- package/dist/usage-DmmO_xjJ.js +0 -1
- package/dist/watch-CuPz0D2M.js +0 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { r as isRecord, t as compact } from "../compact-CAf557ZB.js";
|
|
2
2
|
import { n as freezeDeep } from "../freeze-jjan0lCv.js";
|
|
3
|
-
import {
|
|
4
|
-
import { a as holder, i as flattenApprovers, r as approverLeaves } from "../approvers-B4r0N_Uz.js";
|
|
3
|
+
import { a as flattenApprovers, i as approverManagerKey, o as holder, r as approverLeaves } from "../approvers-BaNwKYgd.js";
|
|
5
4
|
import { t as loadPrincipal } from "../principal-source-C6N60GtM.js";
|
|
6
5
|
import { a as isId, r as idList } from "../expiry-6XByK0dm.js";
|
|
7
6
|
import { c as listPermissions, n as findPermission, o as isPermission } from "../permission-registry-B8tYxPIt.js";
|
|
@@ -9,21 +8,33 @@ import { i as dateAt, m as rfc3339, p as nowSeconds, u as isoAt } from "../time-
|
|
|
9
8
|
import { t as canonicalJson } from "../canonical-json-DwilZ1iB.js";
|
|
10
9
|
import { r as sha256Digest } from "../sha256-C-4WXKIu.js";
|
|
11
10
|
import { t as decideSupportSession } from "../support-session-BsmPCbz8.js";
|
|
12
|
-
import {
|
|
11
|
+
import { d as scopeList } from "../scopes-BSdYaGHs.js";
|
|
13
12
|
import { o as flattenGrantee } from "../grantee-Dke-9rd1.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { t as anonymousSubject } from "../subject-
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
13
|
+
import { n as asApprover } from "../approval-normalize-CUjgSsOl.js";
|
|
14
|
+
import { n as isOperationEffect } from "../operations-Bm9HFecz.js";
|
|
15
|
+
import { r as isSubject, t as anonymousSubject } from "../subject-C7RddW0G.js";
|
|
16
|
+
import { I as decisionTenant } from "../evaluate-Czbj7thS.js";
|
|
17
|
+
import { _ as claimsFirst } from "../instance-snapshot-BWa71my8.js";
|
|
18
|
+
import { a as permissionStates } from "../checks-fyDOhcz0.js";
|
|
19
|
+
import { a as delegatedScopeMap, s as scopesReaching } from "../permdock-D-_WrdKu.js";
|
|
20
|
+
import { s as ApprovalError } from "../types-CcUXciSj.js";
|
|
21
|
+
import { t as applyApprovalVerdict } from "../store-DmVqy74I.js";
|
|
22
|
+
import { i as linkPolicyViolation, n as capabilityOf, r as capabilitySubject } from "../capability-zN6T9fxe.js";
|
|
23
|
+
import { r as credentialSubject, s as parseCredential } from "../credential-B3Hj4puZ.js";
|
|
24
|
+
import { h as managerKeys, s as resumeDecision } from "../helpers-DmqGy63f.js";
|
|
19
25
|
import { t as AccessReviewError } from "../errors-BDUwg2qC.js";
|
|
26
|
+
import { t as parseApprovalRequest } from "../parse-request-DOBVaeiQ.js";
|
|
27
|
+
import { n as parseApprovalHeader } from "../approval-header-8QV6MU4H.js";
|
|
28
|
+
import { i as snapshotTag } from "../snapshot-cache-B66h7FBK.js";
|
|
29
|
+
import { t as buildInstance } from "../build-instance-BFMFQdE_.js";
|
|
30
|
+
import { n as NO_GRANT } from "../decision-CaG5_eNJ.js";
|
|
20
31
|
import { t as policyRowConditionKeys } from "../row-conditions-BAGHOmVq.js";
|
|
21
|
-
import { n as subscribersFor } from "../audience-
|
|
32
|
+
import { n as subscribersFor } from "../audience-B4YR9UpV.js";
|
|
22
33
|
import { i as qualifySqlTable, l as sqlIdent, s as quoteSqlLiteral } from "../sql-0WjxIp9g.js";
|
|
23
34
|
import { o as junctionMembershipSource } from "../sources-C4vkzZJV.js";
|
|
24
|
-
import { c as subjectFromSupabaseSession, t as callRpc } from "../postgrest-
|
|
25
|
-
import { n as parseSupabaseManifest, t as hasTokenHook } from "../manifest-
|
|
26
|
-
import { t as parseCatalog } from "../parse-
|
|
35
|
+
import { c as subjectFromSupabaseSession, t as callRpc } from "../postgrest-BrjrckDG.js";
|
|
36
|
+
import { n as parseSupabaseManifest, t as hasTokenHook } from "../manifest-BdzBobNK.js";
|
|
37
|
+
import { t as parseCatalog } from "../parse-UTC-sDrt.js";
|
|
27
38
|
import { n as activeTenantMismatch, t as activeOrganizationFrom } from "../active-tenant-DVee32-T.js";
|
|
28
39
|
import { AsyncResult, dbError } from "better-supabase";
|
|
29
40
|
//#region src/better-supabase/access-reviews.ts
|
|
@@ -155,6 +166,439 @@ function accessReviewApplier(options) {
|
|
|
155
166
|
};
|
|
156
167
|
}
|
|
157
168
|
//#endregion
|
|
169
|
+
//#region src/better-supabase/approvals-block.ts
|
|
170
|
+
const PAGE = 100;
|
|
171
|
+
const MAX_SCANNED = 2e3;
|
|
172
|
+
function statusOf(status) {
|
|
173
|
+
switch (status) {
|
|
174
|
+
case "pending":
|
|
175
|
+
case "approved":
|
|
176
|
+
case "rejected":
|
|
177
|
+
case "expired": return status;
|
|
178
|
+
case "cancelled": return "rejected";
|
|
179
|
+
default: return status;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function instantOf(value) {
|
|
183
|
+
if (value === void 0) return;
|
|
184
|
+
const ms = Date.parse(value.toJSON());
|
|
185
|
+
return Number.isNaN(ms) ? void 0 : isoAt(ms);
|
|
186
|
+
}
|
|
187
|
+
/** The PermDock request a block record carries, with the block's state. */
|
|
188
|
+
function requestOf(record) {
|
|
189
|
+
const stored = parseApprovalRequest(record.payload["permdock"]);
|
|
190
|
+
if (stored === null) return null;
|
|
191
|
+
const status = statusOf(record.status);
|
|
192
|
+
return freezeDeep(compact({
|
|
193
|
+
...stored,
|
|
194
|
+
status,
|
|
195
|
+
expiresAt: instantOf(record.expiresAt) ?? stored.expiresAt,
|
|
196
|
+
resolvedAt: status === "pending" ? void 0 : instantOf(record.decidedAt),
|
|
197
|
+
consumedAt: instantOf(record.consumedAt),
|
|
198
|
+
fingerprint: record.fingerprint ?? stored.fingerprint
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
async function unwrap(result, call) {
|
|
202
|
+
const awaited = await result;
|
|
203
|
+
if (!awaited.ok) throw new Error(`PermDock: the approvals block's ${call} failed`, { cause: awaited.error });
|
|
204
|
+
return awaited.data;
|
|
205
|
+
}
|
|
206
|
+
/** Every block record carrying a PermDock request, newest first, capped. */
|
|
207
|
+
async function scan(approvals, found, tenantless) {
|
|
208
|
+
const out = [];
|
|
209
|
+
let before;
|
|
210
|
+
for (let scanned = 0; scanned < MAX_SCANNED; scanned += PAGE) {
|
|
211
|
+
const page = await unwrap(approvals.list({
|
|
212
|
+
status: null,
|
|
213
|
+
pageSize: PAGE,
|
|
214
|
+
...before === void 0 ? {} : { before },
|
|
215
|
+
...tenantless ? { tenantless } : {}
|
|
216
|
+
}), "list");
|
|
217
|
+
for (const record of page) {
|
|
218
|
+
const request = requestOf(record);
|
|
219
|
+
if (request !== null) {
|
|
220
|
+
const pair = {
|
|
221
|
+
record,
|
|
222
|
+
request
|
|
223
|
+
};
|
|
224
|
+
out.push(pair);
|
|
225
|
+
if (found(pair)) return out;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const last = page.at(-1);
|
|
229
|
+
if (page.length < PAGE || last === void 0) break;
|
|
230
|
+
before = last.createdAt;
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function actionOf(permission) {
|
|
235
|
+
const dot = permission.lastIndexOf(".");
|
|
236
|
+
return dot === -1 ? permission : permission.slice(dot + 1);
|
|
237
|
+
}
|
|
238
|
+
function matchesQuery(request, query) {
|
|
239
|
+
return (query.status === void 0 || request.status === query.status) && (query.principalId === void 0 || request.subject.principal?.id === query.principalId) && (query.actorId === void 0 || request.subject.actor?.id === query.actorId) && (query.tenant === void 0 || decisionTenant(request.subject, request.membership) === query.tenant) && (query.session === void 0 || request.subject.session === query.session);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* An `ApprovalStore` over better-supabase's approvals block, so a PermDock
|
|
243
|
+
* `approval-required` decision files a block request and is decided in the
|
|
244
|
+
* block's own approve and reject flow. `create` files the request on the
|
|
245
|
+
* resource under the permission's action, with the PermDock request in
|
|
246
|
+
* `payload.permdock` and its `fingerprint`. `get`, `getById` and `list` read
|
|
247
|
+
* the block's latest 2,000 requests in the block's tenant, and with
|
|
248
|
+
* `tenantless` the latest 2,000 without one. `consume` calls the block's
|
|
249
|
+
* `consume`, which refuses a second use and another fingerprint. `resolve`
|
|
250
|
+
* checks the verdict against the PermDock request, then calls the block's
|
|
251
|
+
* `approve` or `reject` with `decidedBy` set to the approver, so a service-role
|
|
252
|
+
* client records the user who decided; without `approve` and `reject` on the
|
|
253
|
+
* client it throws. `expire` answers 0, since the block expires requests
|
|
254
|
+
* itself.
|
|
255
|
+
*/
|
|
256
|
+
function blockApprovalStore(approvals, options = {}) {
|
|
257
|
+
const tenantless = options.tenantless === true;
|
|
258
|
+
const scanAll = async (found) => {
|
|
259
|
+
const inTenant = await scan(approvals, found, false);
|
|
260
|
+
if (!tenantless || inTenant.some(found)) return inTenant;
|
|
261
|
+
return [...inTenant, ...await scan(approvals, found, true)];
|
|
262
|
+
};
|
|
263
|
+
const byToken = async (token) => (await scanAll(({ request }) => request.token === token)).find(({ request }) => request.token === token);
|
|
264
|
+
return Object.freeze({
|
|
265
|
+
name: "better-supabase-approvals",
|
|
266
|
+
async create(request) {
|
|
267
|
+
await unwrap(approvals.request({
|
|
268
|
+
type: request.resource.type,
|
|
269
|
+
id: request.resource.id ?? request.id
|
|
270
|
+
}, compact({
|
|
271
|
+
action: actionOf(request.permission),
|
|
272
|
+
reason: request.detail,
|
|
273
|
+
payload: { permdock: request },
|
|
274
|
+
fingerprint: request.fingerprint,
|
|
275
|
+
tenantless: tenantless && decisionTenant(request.subject, request.membership) === void 0 ? true : void 0
|
|
276
|
+
})), "request");
|
|
277
|
+
},
|
|
278
|
+
async get(token) {
|
|
279
|
+
return (await byToken(token))?.request ?? null;
|
|
280
|
+
},
|
|
281
|
+
async getById(id) {
|
|
282
|
+
return (await scanAll(({ request }) => request.id === id)).find(({ request }) => request.id === id)?.request ?? null;
|
|
283
|
+
},
|
|
284
|
+
async resolve(token, verdict) {
|
|
285
|
+
const decide = verdict.status === "approved" ? approvals.approve : approvals.reject;
|
|
286
|
+
if (decide === void 0) throw new Error("PermDock: blockApprovalStore resolves only with the approvals block's approve and reject on the client");
|
|
287
|
+
const found = await byToken(token);
|
|
288
|
+
if (found === void 0) throw new ApprovalError("approval-not-found", "approval was not found");
|
|
289
|
+
applyApprovalVerdict(found.request, verdict);
|
|
290
|
+
const by = verdict.by.principal?.id;
|
|
291
|
+
const next = requestOf(await unwrap(decide(found.record.id, verdict.note, compact({ decidedBy: by })), verdict.status === "approved" ? "approve" : "reject"));
|
|
292
|
+
if (next === null) throw new ApprovalError("approval-not-found", "approval was not found");
|
|
293
|
+
return next;
|
|
294
|
+
},
|
|
295
|
+
async list(query) {
|
|
296
|
+
const limit = Math.min(Math.max(query.limit ?? 50, 1), 200);
|
|
297
|
+
const offset = query.cursor === void 0 ? 0 : Number(query.cursor);
|
|
298
|
+
if (!Number.isSafeInteger(offset) || offset < 0) return { items: [] };
|
|
299
|
+
const all = (await scanAll(() => false)).map(({ request }) => request).filter((request) => matchesQuery(request, query)).toReversed().toSorted((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
|
|
300
|
+
const items = all.slice(offset, offset + limit);
|
|
301
|
+
return offset + limit < all.length ? {
|
|
302
|
+
items,
|
|
303
|
+
next: String(offset + limit)
|
|
304
|
+
} : { items };
|
|
305
|
+
},
|
|
306
|
+
expire() {
|
|
307
|
+
return 0;
|
|
308
|
+
},
|
|
309
|
+
async consume(token) {
|
|
310
|
+
const found = await byToken(token);
|
|
311
|
+
if (found === void 0) return null;
|
|
312
|
+
const consumed = await approvals.consume(found.record.id, found.request.fingerprint);
|
|
313
|
+
return consumed.ok ? requestOf(consumed.data) : null;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
const INTERVAL_UNITS = {
|
|
318
|
+
second: 1,
|
|
319
|
+
minute: 60,
|
|
320
|
+
hour: 3600,
|
|
321
|
+
day: 86400,
|
|
322
|
+
week: 604800
|
|
323
|
+
};
|
|
324
|
+
/** Seconds in a Postgres interval such as `3 days` or `1 day 2 hours`. */
|
|
325
|
+
function intervalSeconds(interval) {
|
|
326
|
+
const parts = interval.trim().toLowerCase().split(/\s+/u);
|
|
327
|
+
let seconds = 0;
|
|
328
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
329
|
+
const amount = Number(parts[index]);
|
|
330
|
+
const unit = parts[index + 1]?.replace(/s$/u, "");
|
|
331
|
+
const size = unit === void 0 ? void 0 : INTERVAL_UNITS[unit];
|
|
332
|
+
if (!Number.isSafeInteger(amount) || amount < 0 || size === void 0) throw new TypeError(`PermDock: the approval policy interval '${interval}' is not whole seconds, minutes, hours, days or weeks`);
|
|
333
|
+
seconds += amount * size;
|
|
334
|
+
}
|
|
335
|
+
if (seconds === 0) throw new TypeError(`PermDock: the approval policy interval '${interval}' is empty`);
|
|
336
|
+
return seconds;
|
|
337
|
+
}
|
|
338
|
+
function stageApprovers(approvers, roleScope) {
|
|
339
|
+
const leaves = [
|
|
340
|
+
...approvers.permission === void 0 ? [] : [{
|
|
341
|
+
kind: "permission",
|
|
342
|
+
permission: approvers.permission
|
|
343
|
+
}],
|
|
344
|
+
...approvers.role === void 0 ? [] : [{
|
|
345
|
+
kind: "role",
|
|
346
|
+
role: approvers.role,
|
|
347
|
+
scope: roleScope
|
|
348
|
+
}],
|
|
349
|
+
...(approvers.users ?? []).map((id) => ({
|
|
350
|
+
kind: "user",
|
|
351
|
+
id
|
|
352
|
+
})),
|
|
353
|
+
...approvers.manager === true ? [{ kind: "manager" }] : []
|
|
354
|
+
];
|
|
355
|
+
if (leaves.length === 0) throw new TypeError("PermDock: an approval policy step names no approver PermDock can check (team approvers are not supported)");
|
|
356
|
+
return {
|
|
357
|
+
kind: "any-of",
|
|
358
|
+
of: leaves
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function quorumOf(step) {
|
|
362
|
+
if (step.required !== "all") return step.required;
|
|
363
|
+
const { users = [], permission, role, team, manager } = step.approvers;
|
|
364
|
+
if (users.length === 0 || permission !== void 0 || role !== void 0 || team !== void 0 || manager === true) throw new TypeError("PermDock: an approval policy step with required \"all\" needs users and no other approvers, since the block counts other holders when a request is filed");
|
|
365
|
+
return users.length;
|
|
366
|
+
}
|
|
367
|
+
const NUMERIC = /^-?\d+(?:\.\d+)?$/u;
|
|
368
|
+
function isScalar(value) {
|
|
369
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
370
|
+
}
|
|
371
|
+
function variants(value) {
|
|
372
|
+
if (typeof value === "string" && NUMERIC.test(value)) return [value, Number(value)];
|
|
373
|
+
return typeof value === "number" ? [value, String(value)] : [value];
|
|
374
|
+
}
|
|
375
|
+
function numberOf(value) {
|
|
376
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
377
|
+
return typeof value === "string" && NUMERIC.test(value) ? Number(value) : void 0;
|
|
378
|
+
}
|
|
379
|
+
const ORDER = {
|
|
380
|
+
gt: "gt",
|
|
381
|
+
gte: "gte",
|
|
382
|
+
lt: "lt",
|
|
383
|
+
lte: "lte"
|
|
384
|
+
};
|
|
385
|
+
function ruleCondition(field, operator, value) {
|
|
386
|
+
const list = Array.isArray(value) && value.every(isScalar) ? value : [];
|
|
387
|
+
const contains = (item) => ({
|
|
388
|
+
op: "contains",
|
|
389
|
+
field,
|
|
390
|
+
value: item
|
|
391
|
+
});
|
|
392
|
+
switch (operator) {
|
|
393
|
+
case "": return true;
|
|
394
|
+
case "is":
|
|
395
|
+
case "eq": return isScalar(value) ? {
|
|
396
|
+
op: "or",
|
|
397
|
+
conditions: variants(value).map((item) => ({
|
|
398
|
+
op: "eq",
|
|
399
|
+
field,
|
|
400
|
+
value: item
|
|
401
|
+
}))
|
|
402
|
+
} : void 0;
|
|
403
|
+
case "is_not":
|
|
404
|
+
case "neq": return isScalar(value) ? {
|
|
405
|
+
op: "and",
|
|
406
|
+
conditions: variants(value).map((item) => ({
|
|
407
|
+
op: "ne",
|
|
408
|
+
field,
|
|
409
|
+
value: item
|
|
410
|
+
}))
|
|
411
|
+
} : void 0;
|
|
412
|
+
case "gt":
|
|
413
|
+
case "gte":
|
|
414
|
+
case "lt":
|
|
415
|
+
case "lte": {
|
|
416
|
+
const bound = numberOf(value);
|
|
417
|
+
return bound === void 0 ? void 0 : {
|
|
418
|
+
op: ORDER[operator],
|
|
419
|
+
field,
|
|
420
|
+
value: bound
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
case "between":
|
|
424
|
+
case "not_between": {
|
|
425
|
+
const [low, high] = Array.isArray(value) ? value.map(numberOf) : [void 0, void 0];
|
|
426
|
+
if (low === void 0 || high === void 0) return;
|
|
427
|
+
const between = {
|
|
428
|
+
op: "and",
|
|
429
|
+
conditions: [{
|
|
430
|
+
op: "gte",
|
|
431
|
+
field,
|
|
432
|
+
value: low
|
|
433
|
+
}, {
|
|
434
|
+
op: "lte",
|
|
435
|
+
field,
|
|
436
|
+
value: high
|
|
437
|
+
}]
|
|
438
|
+
};
|
|
439
|
+
return operator === "between" ? between : {
|
|
440
|
+
op: "not",
|
|
441
|
+
condition: between
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
case "is_any_of":
|
|
445
|
+
case "is_none_of": return list.length === 0 ? void 0 : {
|
|
446
|
+
op: operator === "is_any_of" ? "in" : "notIn",
|
|
447
|
+
field,
|
|
448
|
+
value: list.flatMap(variants)
|
|
449
|
+
};
|
|
450
|
+
case "contains": return typeof value === "string" ? contains(value) : void 0;
|
|
451
|
+
case "not_contains": return typeof value === "string" ? {
|
|
452
|
+
op: "not",
|
|
453
|
+
condition: contains(value)
|
|
454
|
+
} : void 0;
|
|
455
|
+
case "has_any_of":
|
|
456
|
+
case "has_all_of":
|
|
457
|
+
case "has_none_of": {
|
|
458
|
+
if (list.length === 0) return;
|
|
459
|
+
const each = list.map(contains);
|
|
460
|
+
if (operator === "has_all_of") return {
|
|
461
|
+
op: "and",
|
|
462
|
+
conditions: each
|
|
463
|
+
};
|
|
464
|
+
const any = {
|
|
465
|
+
op: "or",
|
|
466
|
+
conditions: each
|
|
467
|
+
};
|
|
468
|
+
return operator === "has_any_of" ? any : {
|
|
469
|
+
op: "not",
|
|
470
|
+
condition: any
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
case "empty": return {
|
|
474
|
+
op: "isNull",
|
|
475
|
+
field,
|
|
476
|
+
value: true
|
|
477
|
+
};
|
|
478
|
+
case "not_empty": return {
|
|
479
|
+
op: "isNull",
|
|
480
|
+
field,
|
|
481
|
+
value: false
|
|
482
|
+
};
|
|
483
|
+
default: return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function treeCondition(node) {
|
|
487
|
+
if (!isRecord(node)) return;
|
|
488
|
+
let condition;
|
|
489
|
+
if (node["type"] === "group") {
|
|
490
|
+
const rules = node["rules"];
|
|
491
|
+
const combinator = node["combinator"];
|
|
492
|
+
if (!Array.isArray(rules) || combinator !== "and" && combinator !== "or") return;
|
|
493
|
+
const parts = [];
|
|
494
|
+
for (const rule of rules) {
|
|
495
|
+
const part = treeCondition(rule);
|
|
496
|
+
if (part === void 0) return;
|
|
497
|
+
if (part === true) {
|
|
498
|
+
if (combinator === "or") return true;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
parts.push(part);
|
|
502
|
+
}
|
|
503
|
+
condition = parts.length === 0 ? true : {
|
|
504
|
+
op: combinator,
|
|
505
|
+
conditions: parts
|
|
506
|
+
};
|
|
507
|
+
} else if (node["type"] === "rule") {
|
|
508
|
+
const path = node["path"];
|
|
509
|
+
const operator = node["operator"];
|
|
510
|
+
if (!Array.isArray(path) || path.length !== 1 || typeof path[0] !== "string" || typeof operator !== "string") return;
|
|
511
|
+
condition = ruleCondition(path[0], operator, node["value"]);
|
|
512
|
+
}
|
|
513
|
+
if (node["negated"] !== true || condition === void 0) return condition;
|
|
514
|
+
return condition === true ? void 0 : {
|
|
515
|
+
op: "not",
|
|
516
|
+
condition
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function effectsOf(effect, options) {
|
|
520
|
+
if (effect === void 0) return;
|
|
521
|
+
const mapped = options.effects !== void 0 && Object.hasOwn(options.effects, effect) ? options.effects[effect] : void 0;
|
|
522
|
+
if (mapped !== void 0) return mapped.length === 0 ? void 0 : mapped;
|
|
523
|
+
return isOperationEffect(effect) ? [effect] : void 0;
|
|
524
|
+
}
|
|
525
|
+
function keysOf(entry, permissions) {
|
|
526
|
+
if (permissions === void 0) throw new TypeError("PermDock: an approval policy for every subject type or action needs blockApprovalPolicySource's permissions, or an effect PermDock knows");
|
|
527
|
+
return listPermissions(permissions).map((permission) => permission.key).filter((key) => {
|
|
528
|
+
const dot = key.lastIndexOf(".");
|
|
529
|
+
return dot > 0 && (entry.subjectType === "*" || key.slice(0, dot) === entry.subjectType) && (entry.action === "*" || key.slice(dot + 1) === entry.action);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
function policiesOf(entry, options) {
|
|
533
|
+
const roleScope = options.roleScope ?? "tenant";
|
|
534
|
+
const approval = compact({
|
|
535
|
+
stages: entry.steps.length === 0 ? void 0 : entry.steps.map((step) => ({
|
|
536
|
+
by: stageApprovers(step.approvers, roleScope),
|
|
537
|
+
quorum: quorumOf(step)
|
|
538
|
+
})),
|
|
539
|
+
combine: entry.steps.length > 1 ? "sequential" : void 0,
|
|
540
|
+
mode: entry.mode === "confirm" && entry.selfApproval ? "confirm" : void 0,
|
|
541
|
+
ttl: entry.expiresIn === void 0 ? void 0 : intervalSeconds(entry.expiresIn)
|
|
542
|
+
});
|
|
543
|
+
const tree = entry.conditions === void 0 || entry.conditions === null ? true : treeCondition(entry.conditions);
|
|
544
|
+
const shared = compact({
|
|
545
|
+
tenant: entry.organizationId ?? void 0,
|
|
546
|
+
check: tree === true || tree === void 0 ? void 0 : tree,
|
|
547
|
+
approval
|
|
548
|
+
});
|
|
549
|
+
if (!(entry.subjectType === "*" || entry.action === "*")) return [{
|
|
550
|
+
...shared,
|
|
551
|
+
permission: `${entry.subjectType}.${entry.action}`
|
|
552
|
+
}];
|
|
553
|
+
const effects = effectsOf(entry.effect, options);
|
|
554
|
+
if (effects !== void 0) return [{
|
|
555
|
+
...shared,
|
|
556
|
+
effects
|
|
557
|
+
}];
|
|
558
|
+
return keysOf(entry, options.permissions).map((permission) => ({
|
|
559
|
+
...shared,
|
|
560
|
+
permission
|
|
561
|
+
}));
|
|
562
|
+
}
|
|
563
|
+
function enabledIn(tenants) {
|
|
564
|
+
return (entry) => isRecord(entry) && entry["enabled"] === true && (entry["organizationId"] === null || typeof entry["organizationId"] === "string" && tenants.includes(entry["organizationId"]));
|
|
565
|
+
}
|
|
566
|
+
async function listed(approvals, platform) {
|
|
567
|
+
const entries = await unwrap(approvals.listPolicies(platform ? {
|
|
568
|
+
pageSize: 200,
|
|
569
|
+
platform
|
|
570
|
+
} : { pageSize: 200 }), "listPolicies");
|
|
571
|
+
if (!Array.isArray(entries)) throw new TypeError("PermDock: listPolicies answered no list");
|
|
572
|
+
return entries.map((entry) => entry);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* An `ApprovalPolicySource` over the approvals block's tenant policies, and
|
|
576
|
+
* with `platform` its platform policies, so a PermDock decision asks for
|
|
577
|
+
* approval wherever the block would. A policy keys the permission
|
|
578
|
+
* `<subjectType>.<action>`; a platform policy has no tenant. Its steps become
|
|
579
|
+
* sequential stages whose approvers are any of the step's permission, role,
|
|
580
|
+
* users and manager; `required: "all"` over users needs each of them. `confirm`
|
|
581
|
+
* with `selfApproval` lets the principal approve; `expiresIn` caps the
|
|
582
|
+
* request's `ttl`. `conditions` become a `check` over the call's input, or the
|
|
583
|
+
* row without one; a tree PermDock cannot express is dropped, so the policy
|
|
584
|
+
* applies to every call: stricter, never looser. A `*` subject type or action
|
|
585
|
+
* with an effect PermDock knows matches every operation with that effect, and
|
|
586
|
+
* without one expands over `permissions`. A concrete policy's `effect` is
|
|
587
|
+
* ignored, so it applies to every call of its permission. A step with only a
|
|
588
|
+
* `team` approver, `required: "all"` over anything but users, an interval it
|
|
589
|
+
* cannot read or a failed read makes the source fail, and every call an allow
|
|
590
|
+
* would grant denies.
|
|
591
|
+
*/
|
|
592
|
+
function blockApprovalPolicySource(approvals, options = {}) {
|
|
593
|
+
return Object.freeze({
|
|
594
|
+
name: "better-supabase-approvals",
|
|
595
|
+
async approvalPoliciesFor(query) {
|
|
596
|
+
const [own, platform] = await Promise.all([listed(approvals, false), options.platform === true ? listed(approvals, true) : []]);
|
|
597
|
+
return [...platform, ...own].filter(enabledIn(query.tenants)).flatMap((entry) => policiesOf(entry, options));
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
//#endregion
|
|
158
602
|
//#region src/better-supabase/api-keys.ts
|
|
159
603
|
const EPOCH = "1970-01-01T00:00:00.000Z";
|
|
160
604
|
const rfc3339Of = (instant) => isoAt(instant.epochMilliseconds);
|
|
@@ -341,6 +785,127 @@ function subjectFromBetterSupabase(session, options = {}) {
|
|
|
341
785
|
});
|
|
342
786
|
}
|
|
343
787
|
//#endregion
|
|
788
|
+
//#region src/better-supabase/permdock-for.ts
|
|
789
|
+
/** The options a `permdockFor` resolver already holds. */
|
|
790
|
+
const RESOLVER_OWNS = [
|
|
791
|
+
"subject",
|
|
792
|
+
"memberships",
|
|
793
|
+
"version",
|
|
794
|
+
"versions",
|
|
795
|
+
"instances",
|
|
796
|
+
"options"
|
|
797
|
+
];
|
|
798
|
+
function isResult(source) {
|
|
799
|
+
return isRecord(source) && Object.hasOwn(source, "session") && Object.hasOwn(source, "authzVersion") && !Object.hasOwn(source, "kind");
|
|
800
|
+
}
|
|
801
|
+
function knownVersion(value) {
|
|
802
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
803
|
+
}
|
|
804
|
+
function subjectOf(source, result, signedOut, options) {
|
|
805
|
+
if (isSubject(source)) return source;
|
|
806
|
+
if (result !== void 0) return subjectFromBetterSupabase(signedOut ? null : result.session, options);
|
|
807
|
+
return subjectFromBetterSupabase(source, options);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* The resolver behind `permdockFor`, the authorizer and the hooks: one sources
|
|
811
|
+
* object per version cache, so every instance of one subject at one version
|
|
812
|
+
* shares an `instances` entry.
|
|
813
|
+
*/
|
|
814
|
+
function permdockResolver(options) {
|
|
815
|
+
const byCache = /* @__PURE__ */ new WeakMap();
|
|
816
|
+
let uncached;
|
|
817
|
+
const sourcesOf = (versions) => ({
|
|
818
|
+
...options.options,
|
|
819
|
+
...options.instances === void 0 ? {} : { instances: options.instances },
|
|
820
|
+
...options.memberships === void 0 ? {} : { memberships: claimsFirst(options.memberships, {
|
|
821
|
+
onStale: "reread",
|
|
822
|
+
...options.version === void 0 ? {} : { version: options.version },
|
|
823
|
+
...versions === void 0 ? {} : { versions }
|
|
824
|
+
}) }
|
|
825
|
+
});
|
|
826
|
+
const sourcesFor = (versions) => {
|
|
827
|
+
if (versions === void 0) {
|
|
828
|
+
uncached ??= sourcesOf(void 0);
|
|
829
|
+
return uncached;
|
|
830
|
+
}
|
|
831
|
+
const known = byCache.get(versions);
|
|
832
|
+
if (known !== void 0) return known;
|
|
833
|
+
const created = sourcesOf(versions);
|
|
834
|
+
byCache.set(versions, created);
|
|
835
|
+
return created;
|
|
836
|
+
};
|
|
837
|
+
const resolve = async (source, query, runtimeVersions) => {
|
|
838
|
+
const result = isResult(source) ? source : void 0;
|
|
839
|
+
const signedOut = result?.sessionActive === false;
|
|
840
|
+
const user = subjectOf(source, result, signedOut, options.subject);
|
|
841
|
+
const tenant = query.tenant ?? result?.tenant ?? void 0;
|
|
842
|
+
const known = signedOut ? void 0 : knownVersion(result?.authzVersion);
|
|
843
|
+
const sources = sourcesFor(runtimeVersions ?? options.versions);
|
|
844
|
+
const { permdock } = await buildInstance({
|
|
845
|
+
policy: options.policy,
|
|
846
|
+
user,
|
|
847
|
+
sources,
|
|
848
|
+
scope: {
|
|
849
|
+
...options.options,
|
|
850
|
+
memberships: sources.memberships,
|
|
851
|
+
intent: query.intent ?? "read",
|
|
852
|
+
tenant: tenant ?? void 0,
|
|
853
|
+
version: known === void 0 ? void 0 : { known }
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
return permdock;
|
|
857
|
+
};
|
|
858
|
+
const sources = (versions) => sourcesFor(versions ?? options.versions);
|
|
859
|
+
return Object.freeze(Object.assign(resolve, { sources }));
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* The resolver an adapter builds instances with: the app's `permdockFor`, or
|
|
863
|
+
* one built from the adapter's own options. `permdockFor` with any option it
|
|
864
|
+
* already holds throws a `TypeError`, since two sources of the same option
|
|
865
|
+
* would disagree.
|
|
866
|
+
*/
|
|
867
|
+
function resolverOf(options, adapter) {
|
|
868
|
+
const shared = options.permdockFor;
|
|
869
|
+
if (shared === void 0) return permdockResolver(options);
|
|
870
|
+
const clash = RESOLVER_OWNS.find((key) => options[key] !== void 0);
|
|
871
|
+
if (clash !== void 0) throw new TypeError(`PermDock: ${adapter} takes ${clash} from permdockFor; pass it to permdockFor instead`);
|
|
872
|
+
const resolve = (source, query, versions) => shared(source, query, versions);
|
|
873
|
+
return Object.freeze(Object.assign(resolve, { sources: shared.sources }));
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* One resolver for every instance of the app: `permdockFor(await
|
|
877
|
+
* bsb.claims())`, `permdockFor(await bsb.bootstrap(...))` or `permdockFor(await
|
|
878
|
+
* bsb.getAccess(...))`. A bootstrap or access result passes its `authzVersion`
|
|
879
|
+
* as `version.known`, so the instance reads no version, and a `sessionActive:
|
|
880
|
+
* false` bootstrap is anonymous. With `instances`, a subject at an unchanged
|
|
881
|
+
* version reuses its instance across requests. Pass the same resolver to
|
|
882
|
+
* `authorizer({ permdockFor })`, `authorizationHooks({ permdockFor })` and,
|
|
883
|
+
* through `permdockFor.sources()`, to every other adapter's options, so they
|
|
884
|
+
* share one instance per subject and version.
|
|
885
|
+
*/
|
|
886
|
+
function permdockFor(options) {
|
|
887
|
+
const resolve = permdockResolver(options);
|
|
888
|
+
const call = (source, query, versions) => resolve(source, typeof query === "string" || query === null ? { tenant: query } : query ?? {}, versions);
|
|
889
|
+
return Object.freeze(Object.assign(call, { sources: resolve.sources }));
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* One `{ allowed, outcome, reason? }` per named check for `input`, from one
|
|
893
|
+
* `permdockFor` instance: frozen plain JSON an island returns from `'use cache:
|
|
894
|
+
* private'`. A check that throws denies its entry; an instance that cannot be
|
|
895
|
+
* built denies them all.
|
|
896
|
+
*/
|
|
897
|
+
async function getPermissions(resolve, input, checks, query) {
|
|
898
|
+
let permdock;
|
|
899
|
+
try {
|
|
900
|
+
permdock = await resolve(input, query);
|
|
901
|
+
} catch {
|
|
902
|
+
permdock = void 0;
|
|
903
|
+
}
|
|
904
|
+
if (permdock === void 0) return permissionStates(() => NO_GRANT, checks);
|
|
905
|
+
const decide = permdock.decide;
|
|
906
|
+
return permissionStates(decide, checks);
|
|
907
|
+
}
|
|
908
|
+
//#endregion
|
|
344
909
|
//#region src/better-supabase/subscribers.ts
|
|
345
910
|
function permissionFor(events, type) {
|
|
346
911
|
if (Object.hasOwn(events, type)) return events[type];
|
|
@@ -356,6 +921,14 @@ function permissionFor(events, type) {
|
|
|
356
921
|
* tenant and a throw reach no one.
|
|
357
922
|
*/
|
|
358
923
|
function subscriberPolicy(options) {
|
|
924
|
+
const resolve = permdockFor({
|
|
925
|
+
policy: options.policy,
|
|
926
|
+
options: {
|
|
927
|
+
...options.options,
|
|
928
|
+
principals: options.principals
|
|
929
|
+
},
|
|
930
|
+
...options.instances === void 0 ? {} : { instances: options.instances }
|
|
931
|
+
});
|
|
359
932
|
return async (event, subscribers) => {
|
|
360
933
|
try {
|
|
361
934
|
const permission = permissionFor(options.events, event.type);
|
|
@@ -364,14 +937,10 @@ function subscriberPolicy(options) {
|
|
|
364
937
|
if (permission === void 0 || tenant === null || senderId === null || senderId === void 0) return [];
|
|
365
938
|
const sender = await loadPrincipal(options.principals, senderId, tenant);
|
|
366
939
|
if (sender === null) return [];
|
|
367
|
-
const permdock = await
|
|
940
|
+
const permdock = await resolve({
|
|
368
941
|
principal: sender,
|
|
369
942
|
context: {}
|
|
370
|
-
},
|
|
371
|
-
...options.options,
|
|
372
|
-
principals: options.principals,
|
|
373
|
-
tenant
|
|
374
|
-
});
|
|
943
|
+
}, tenant);
|
|
375
944
|
if (permdock.subject.principal?.tenant !== tenant) return [];
|
|
376
945
|
return await subscribersFor(permdock, {
|
|
377
946
|
permission,
|
|
@@ -407,12 +976,13 @@ function requestId(input) {
|
|
|
407
976
|
* is `false`.
|
|
408
977
|
*/
|
|
409
978
|
function supportPolicy(options) {
|
|
979
|
+
const resolve = permdockFor(options);
|
|
410
980
|
return async (input) => {
|
|
411
981
|
try {
|
|
412
|
-
const base = await
|
|
982
|
+
const base = await resolve({
|
|
413
983
|
kind: "user",
|
|
414
984
|
claims: input.admin.claims
|
|
415
|
-
}
|
|
985
|
+
});
|
|
416
986
|
if (base.subject.principal?.id !== input.admin.user.id) return false;
|
|
417
987
|
if (!input.readOnly && options.writable === void 0) return false;
|
|
418
988
|
const permdock = input.tenant === void 0 ? base : base.tenant(input.tenant);
|
|
@@ -468,16 +1038,17 @@ function grants$1(permdock, key) {
|
|
|
468
1038
|
return permission !== void 0 && permdock.decide(permission, void 0).outcome === "granted";
|
|
469
1039
|
}
|
|
470
1040
|
/**
|
|
471
|
-
* A list matches when every entry does, `anyOf` when one does;
|
|
472
|
-
* match nobody, as in SQL.
|
|
1041
|
+
* A list matches when every entry does, `anyOf` when one does; `manager` when
|
|
1042
|
+
* its key is in `managed`; other leaves match nobody, as in SQL.
|
|
473
1043
|
*/
|
|
474
|
-
function matches(approvers, userId, permdock) {
|
|
1044
|
+
function matches(approvers, userId, permdock, managed) {
|
|
475
1045
|
const items = flattenApprovers(approvers);
|
|
476
1046
|
return items.length > 0 && items.every((item) => {
|
|
477
1047
|
switch (item.kind) {
|
|
478
1048
|
case "user": return item.id === userId;
|
|
479
1049
|
case "permission": return grants$1(permdock, item.permission);
|
|
480
|
-
case "any-of": return item.of.some((entry) => matches(entry, userId, permdock));
|
|
1050
|
+
case "any-of": return item.of.some((entry) => matches(entry, userId, permdock, managed));
|
|
1051
|
+
case "manager": return managed.has(approverManagerKey(item));
|
|
481
1052
|
case "actor":
|
|
482
1053
|
case "anyone":
|
|
483
1054
|
case "assurance":
|
|
@@ -495,25 +1066,23 @@ function matches(approvers, userId, permdock) {
|
|
|
495
1066
|
* authorization })`), decided by `policy`. Every hook builds the principal's
|
|
496
1067
|
* instance from the claims `claims` loads now, never from the request, and
|
|
497
1068
|
* fails closed: a throw, an unknown key and `approval-required` refuse.
|
|
498
|
-
* `automation` and `
|
|
499
|
-
* `
|
|
500
|
-
* `
|
|
501
|
-
*
|
|
502
|
-
* `
|
|
1069
|
+
* `automation`, `delegation` and `sessionTags` (`snapshotTag(userId)`, so
|
|
1070
|
+
* `evictSession` and sign-out clear PermDock snapshots) are always set;
|
|
1071
|
+
* `identities`, `delivery`, `approverOf`, `support` and `tickets` only with the
|
|
1072
|
+
* options they read. `delegation.permissions` maps each scope to the keys whose
|
|
1073
|
+
* `.scope` it is, was renamed from, or that a policy `oauthScopes` entry
|
|
1074
|
+
* covers, and reads no `authorization_details`.
|
|
503
1075
|
*/
|
|
504
1076
|
function authorizationHooks(options) {
|
|
505
1077
|
const { policy } = options;
|
|
1078
|
+
const resolve = resolverOf(options, "authorizationHooks");
|
|
506
1079
|
const instance = async (claims, tenant) => {
|
|
507
|
-
const
|
|
1080
|
+
const permdock = await resolve({
|
|
508
1081
|
kind: "user",
|
|
509
1082
|
claims
|
|
510
|
-
},
|
|
511
|
-
if (subject.principal === null) return null;
|
|
512
|
-
|
|
513
|
-
...options.options,
|
|
514
|
-
...tenant === null || tenant === void 0 ? {} : { tenant }
|
|
515
|
-
});
|
|
516
|
-
return tenant === null || tenant === void 0 || permdock.subject.principal?.tenant === tenant ? permdock : null;
|
|
1083
|
+
}, { tenant: tenant ?? null });
|
|
1084
|
+
if (permdock.subject.principal === null) return null;
|
|
1085
|
+
return tenant === null || tenant === void 0 || permdock.subject.principal.tenant === tenant ? permdock : null;
|
|
517
1086
|
};
|
|
518
1087
|
const holdsAll = async (claims, automation) => {
|
|
519
1088
|
const permdock = await instance(claims, automation.tenant);
|
|
@@ -559,7 +1128,10 @@ function authorizationHooks(options) {
|
|
|
559
1128
|
const given = approvers[request.kind];
|
|
560
1129
|
if (given === void 0 || approver.claims["sub"] !== approver.userId) return false;
|
|
561
1130
|
const permdock = await instance(approver.claims, request.tenant);
|
|
562
|
-
|
|
1131
|
+
if (permdock === null) return false;
|
|
1132
|
+
const wanted = asApprover(given);
|
|
1133
|
+
const managed = request.requester === void 0 ? [] : await managerKeys(approverLeaves(wanted), request.requester, approver.userId, compact({ relations: resolve.sources().relations }));
|
|
1134
|
+
return matches(wanted, approver.userId, permdock, new Set(managed));
|
|
563
1135
|
} catch {
|
|
564
1136
|
return false;
|
|
565
1137
|
}
|
|
@@ -589,6 +1161,7 @@ function authorizationHooks(options) {
|
|
|
589
1161
|
return Object.freeze({
|
|
590
1162
|
apiVersion: 1,
|
|
591
1163
|
name: "PermDock",
|
|
1164
|
+
sessionTags: (userId) => [snapshotTag(userId)],
|
|
592
1165
|
automation: Object.freeze(automation),
|
|
593
1166
|
delegation: Object.freeze(delegation),
|
|
594
1167
|
...approverOf === void 0 ? {} : { approverOf },
|
|
@@ -620,6 +1193,8 @@ function authorizationHooks(options) {
|
|
|
620
1193
|
//#region src/better-supabase/approvers.ts
|
|
621
1194
|
/** `rls.approvals`' approver check, executable by `authenticated`. */
|
|
622
1195
|
const IS_APPROVER = "permdock_is_approver";
|
|
1196
|
+
/** The manager walk `rls.approvals.managers` writes. */
|
|
1197
|
+
const MANAGES = "permdock_manages";
|
|
623
1198
|
/**
|
|
624
1199
|
* The resource a cover's `resources` names for a request kind: `job` for
|
|
625
1200
|
* `job.update`, else the kind.
|
|
@@ -631,14 +1206,20 @@ function resourceOf(kind) {
|
|
|
631
1206
|
/**
|
|
632
1207
|
* better-supabase's `isApprover` template: one `case` over `{kind}` whose
|
|
633
1208
|
* branches call `permdock_is_approver` with the kind's approvers as JSON. A
|
|
634
|
-
* kind with no branch is `false`.
|
|
635
|
-
*
|
|
1209
|
+
* kind with no branch is `false`. With `requestApprovers`, every call is the
|
|
1210
|
+
* five-argument form, which also gets better-supabase's `{approvers}`, the
|
|
1211
|
+
* request's current approvers. Only `user`, `holder` and `anyOf` approvers, and
|
|
1212
|
+
* `manager` with `rls.approvals.managers`, match in SQL; other leaves are
|
|
1213
|
+
* reported and match nobody.
|
|
636
1214
|
*/
|
|
637
1215
|
function isApproverTemplate(input) {
|
|
638
1216
|
const problems = [];
|
|
639
1217
|
if (!input.listed) return { problems: [`approvers needs ${input.schema}.${IS_APPROVER}, which \`permdock rls generate\` writes with rls.approvals. Run it, then \`permdock supabase inspect --out\`.`] };
|
|
640
1218
|
const fn = `${sqlIdent(input.schema)}.${sqlIdent(IS_APPROVER)}`;
|
|
641
1219
|
const keys = input.catalog === void 0 ? void 0 : new Set(input.catalog.permissions.map((permission) => permission.key));
|
|
1220
|
+
const current = input.requestApprovers === true;
|
|
1221
|
+
if (current && input.covers !== true) return { problems: [`requestApprovers needs the five-argument ${IS_APPROVER}, which \`permdock rls generate\` writes with rls.approvals.delegations or rls.approvals.managers. Set one, run it, then \`permdock supabase inspect --out\`.`] };
|
|
1222
|
+
const passed = current ? ", ({approvers})::jsonb" : "";
|
|
642
1223
|
const branches = [];
|
|
643
1224
|
for (const [kind, given] of Object.entries(input.approvers)) {
|
|
644
1225
|
if (kind === "") {
|
|
@@ -646,24 +1227,28 @@ function isApproverTemplate(input) {
|
|
|
646
1227
|
continue;
|
|
647
1228
|
}
|
|
648
1229
|
const approvers = asApprover(given);
|
|
1230
|
+
const managers = input.managers === true && input.covers === true;
|
|
649
1231
|
for (const leaf of approverLeaves(approvers)) {
|
|
650
1232
|
if (leaf.kind === "user") continue;
|
|
651
|
-
if (leaf.kind
|
|
1233
|
+
if (leaf.kind === "manager") {
|
|
1234
|
+
if (!managers) problems.push(`The ${kind} approvers name a manager approver, which ${IS_APPROVER} checks only with rls.approvals.managers, so it matches nobody. Set rls.approvals.managers, run \`permdock rls generate\`, then \`permdock supabase inspect --out\`.`);
|
|
1235
|
+
} else if (leaf.kind !== "permission") problems.push(`The ${kind} approvers name a ${leaf.kind} approver, which ${IS_APPROVER} cannot check in SQL, so it matches nobody. Use user(), holder(), manager() or anyOf().`);
|
|
652
1236
|
else if (keys !== void 0 && !keys.has(leaf.permission)) problems.push(`The ${kind} approver permission ${leaf.permission} is not in the catalog. Run \`permdock catalog\` after adding it to the policy.`);
|
|
653
1237
|
}
|
|
654
1238
|
const json = `${quoteSqlLiteral(JSON.stringify(approvers))}::jsonb`;
|
|
655
|
-
branches.push(input.covers === true ? `when ${quoteSqlLiteral(kind)} then ${fn}(${json}, ({tenant})::text, ({requester})::text, ${quoteSqlLiteral(resourceOf(kind))})` : `when ${quoteSqlLiteral(kind)} then ${fn}(${json}, ({tenant})::text)`);
|
|
1239
|
+
branches.push(input.covers === true ? `when ${quoteSqlLiteral(kind)} then ${fn}(${json}, ({tenant})::text, ({requester})::text, ${quoteSqlLiteral(resourceOf(kind))}${passed})` : `when ${quoteSqlLiteral(kind)} then ${fn}(${json}, ({tenant})::text)`);
|
|
656
1240
|
}
|
|
657
|
-
if (branches.length === 0) {
|
|
1241
|
+
if (branches.length === 0 && !current) {
|
|
658
1242
|
problems.push("approvers names no kind, so isApprover is not set.");
|
|
659
1243
|
return { problems };
|
|
660
1244
|
}
|
|
661
1245
|
if (!input.rootTenant) problems.push(`The tenant scope "${input.tenantScope}" is not a root scope, and ${IS_APPROVER} checks holder() approvers at the root scope, so they match nobody.`);
|
|
1246
|
+
const otherwise = current ? `${fn}(null::jsonb, ({tenant})::text, ({requester})::text, split_part({kind}, '.', 1)${passed})` : "false";
|
|
662
1247
|
return {
|
|
663
|
-
template: `case {kind} ${branches.join(" ")} else
|
|
1248
|
+
template: branches.length === 0 ? otherwise : `case {kind} ${branches.join(" ")} else ${otherwise} end`,
|
|
664
1249
|
requires: {
|
|
665
1250
|
function: fn,
|
|
666
|
-
args: input.covers === true ? "jsonb, text, text, text" : "jsonb, text",
|
|
1251
|
+
args: current ? "jsonb, text, text, text, jsonb" : input.covers === true ? "jsonb, text, text, text" : "jsonb, text",
|
|
667
1252
|
role: "postgres"
|
|
668
1253
|
},
|
|
669
1254
|
problems
|
|
@@ -879,12 +1464,14 @@ function tokenHookOf(manifest, activeTenant) {
|
|
|
879
1464
|
const owned = manifest.claims.filter((claim) => claim.source === "permdock");
|
|
880
1465
|
const registered = manifest.claims.filter((claim) => claim.source !== "permdock");
|
|
881
1466
|
const budgeted = manifest.claims.filter((claim) => claim.budget);
|
|
1467
|
+
const truncated = owned.some((claim) => claim.name === "memberships_truncated") ? "memberships_truncated" : void 0;
|
|
882
1468
|
const activeFrom = activeTenant === "resolver" || manifest.hook.activeFrom === void 0 ? void 0 : activeOrganizationFrom(manifest.hook.activeFrom, manifest.tenantClaim);
|
|
883
1469
|
return {
|
|
884
1470
|
function: `${manifest.hook.schema}.${manifest.hook.function}`,
|
|
885
1471
|
tenantClaim: manifest.tenantClaim,
|
|
886
1472
|
...activeFrom === void 0 ? {} : { activeOrganizationFrom: activeFrom },
|
|
887
1473
|
ownedClaims: owned.map((claim) => claim.name),
|
|
1474
|
+
...truncated === void 0 ? {} : { truncatedClaim: truncated },
|
|
888
1475
|
...registered.length > 0 ? { registeredClaims: registered.map((claim) => ({
|
|
889
1476
|
name: claim.name,
|
|
890
1477
|
function: claim.source
|
|
@@ -892,7 +1479,7 @@ function tokenHookOf(manifest, activeTenant) {
|
|
|
892
1479
|
budget: {
|
|
893
1480
|
claims: budgeted.map((claim) => claim.name),
|
|
894
1481
|
bytes: manifest.budget.bytes,
|
|
895
|
-
...
|
|
1482
|
+
...truncated === void 0 ? {} : { truncatedClaim: truncated }
|
|
896
1483
|
},
|
|
897
1484
|
markers: {
|
|
898
1485
|
hook: `-- permdock:hook ${manifest.markers.hook}`,
|
|
@@ -1073,12 +1660,14 @@ function authorizationProvider(options) {
|
|
|
1073
1660
|
...options.approvers
|
|
1074
1661
|
};
|
|
1075
1662
|
let isApprover;
|
|
1076
|
-
if (approvers !== void 0) {
|
|
1663
|
+
if (approvers !== void 0 || options.requestApprovers === true) {
|
|
1077
1664
|
const built = isApproverTemplate({
|
|
1078
|
-
approvers,
|
|
1665
|
+
approvers: approvers ?? {},
|
|
1666
|
+
requestApprovers: options.requestApprovers === true,
|
|
1079
1667
|
schema: rls.schema,
|
|
1080
1668
|
listed: helpers.has(IS_APPROVER),
|
|
1081
1669
|
covers: /\bp_requester\b/u.test(helpers.get("permdock_is_approver")?.args ?? ""),
|
|
1670
|
+
managers: helpers.has(MANAGES),
|
|
1082
1671
|
tenantScope,
|
|
1083
1672
|
rootTenant,
|
|
1084
1673
|
catalog
|
|
@@ -1197,9 +1786,6 @@ const denied = (reason) => ({
|
|
|
1197
1786
|
outcome: "denied",
|
|
1198
1787
|
reason
|
|
1199
1788
|
});
|
|
1200
|
-
function isVersionCache(value) {
|
|
1201
|
-
return isRecord(value) && typeof value["maxStaleMs"] === "number" && typeof value["current"] === "function" && typeof value["evict"] === "function";
|
|
1202
|
-
}
|
|
1203
1789
|
function outcomeOf(decision, key) {
|
|
1204
1790
|
switch (decision.outcome) {
|
|
1205
1791
|
case "granted": return { outcome: "granted" };
|
|
@@ -1211,10 +1797,31 @@ function outcomeOf(decision, key) {
|
|
|
1211
1797
|
default: return decision;
|
|
1212
1798
|
}
|
|
1213
1799
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1800
|
+
/**
|
|
1801
|
+
* The row a check decides on. A write is `change`: a create checks the new
|
|
1802
|
+
* values, an update the stored row against the stored row with the new values
|
|
1803
|
+
* over it. Only a row better-supabase loaded itself, with no change, skips
|
|
1804
|
+
* schema validation.
|
|
1805
|
+
*/
|
|
1806
|
+
function dataOf(resource) {
|
|
1807
|
+
const { change } = resource;
|
|
1808
|
+
if (change !== void 0) return change.current === void 0 ? {
|
|
1809
|
+
data: change.next,
|
|
1810
|
+
trusted: false
|
|
1811
|
+
} : {
|
|
1812
|
+
data: {
|
|
1813
|
+
current: change.current,
|
|
1814
|
+
next: {
|
|
1815
|
+
...change.current,
|
|
1816
|
+
...change.next
|
|
1817
|
+
}
|
|
1818
|
+
},
|
|
1819
|
+
trusted: false
|
|
1820
|
+
};
|
|
1821
|
+
return {
|
|
1822
|
+
data: resource.properties,
|
|
1823
|
+
trusted: resource.source === "loaded"
|
|
1824
|
+
};
|
|
1218
1825
|
}
|
|
1219
1826
|
/**
|
|
1220
1827
|
* The verified claims better-supabase passes in `subject.properties.claims`,
|
|
@@ -1227,7 +1834,7 @@ function claimsOf(request) {
|
|
|
1227
1834
|
return isRecord(claims) ? claims : void 0;
|
|
1228
1835
|
}
|
|
1229
1836
|
const tenantOf = (request) => {
|
|
1230
|
-
const tenant = request.context
|
|
1837
|
+
const { tenant } = request.context;
|
|
1231
1838
|
return typeof tenant === "string" ? tenant : null;
|
|
1232
1839
|
};
|
|
1233
1840
|
const keyOf = (request) => isPermission(request.action) ? request.action.key : null;
|
|
@@ -1241,38 +1848,61 @@ const keyOf = (request) => isPermission(request.action) ? request.action.key : n
|
|
|
1241
1848
|
* request scope share an instance per claims, tenant and intent.
|
|
1242
1849
|
*/
|
|
1243
1850
|
function authorizer(options) {
|
|
1244
|
-
const { operations } = options;
|
|
1851
|
+
const { operations, store } = options;
|
|
1245
1852
|
const intentOf = (request, batch) => {
|
|
1246
1853
|
if (options.intent !== void 0) return options.intent(request);
|
|
1854
|
+
const { intent } = request.context;
|
|
1855
|
+
if (intent === "read" || intent === "write") return intent;
|
|
1247
1856
|
if (batch) return "read";
|
|
1248
1857
|
return isPermission(request.action) && READ_ACTIONS.has(request.action.action) ? "read" : "write";
|
|
1249
1858
|
};
|
|
1250
|
-
const
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
claims
|
|
1263
|
-
};
|
|
1264
|
-
const base = await getPermDock(options.policy, subjectFromBetterSupabase(session, options.subject), {
|
|
1265
|
-
...options.options,
|
|
1266
|
-
...membershipsFor(runtime),
|
|
1859
|
+
const resolve = resolverOf(options, "authorizer");
|
|
1860
|
+
const instanceFor = (claims, tenant, intent, runtime) => resolve(claims === void 0 ? null : {
|
|
1861
|
+
kind: "user",
|
|
1862
|
+
claims
|
|
1863
|
+
}, {
|
|
1864
|
+
tenant,
|
|
1865
|
+
intent
|
|
1866
|
+
}, runtime?.versions);
|
|
1867
|
+
const scoped = (claims, tenant, intent, runtime, batch) => {
|
|
1868
|
+
const key = `permdock:authorizer:${canonicalJson([
|
|
1869
|
+
claims ?? null,
|
|
1870
|
+
tenant,
|
|
1267
1871
|
intent
|
|
1872
|
+
])}`;
|
|
1873
|
+
const build = () => instanceFor(claims, tenant, intent, runtime);
|
|
1874
|
+
if (runtime?.memo !== void 0) return runtime.memo.once(key, build);
|
|
1875
|
+
if (batch === void 0) return build();
|
|
1876
|
+
const known = batch.get(key);
|
|
1877
|
+
if (known !== void 0) return known;
|
|
1878
|
+
const created = build();
|
|
1879
|
+
batch.set(key, created);
|
|
1880
|
+
return created;
|
|
1881
|
+
};
|
|
1882
|
+
const decideWith = async (permdock, request, consume) => {
|
|
1883
|
+
const { action } = request;
|
|
1884
|
+
if (!isPermission(action)) return denied("Unknown permission.");
|
|
1885
|
+
const { data, trusted } = dataOf(request.resource);
|
|
1886
|
+
const decision = permdock.decide(action, data, {
|
|
1887
|
+
trusted,
|
|
1888
|
+
adapter: "better-supabase"
|
|
1268
1889
|
});
|
|
1269
|
-
|
|
1890
|
+
if (store === void 0 || decision.outcome !== "approval-required") return outcomeOf(decision, action.key);
|
|
1891
|
+
const { id } = request.resource;
|
|
1892
|
+
return outcomeOf(await resumeDecision({
|
|
1893
|
+
decision,
|
|
1894
|
+
permission: action,
|
|
1895
|
+
subject: permdock.subject,
|
|
1896
|
+
store,
|
|
1897
|
+
resource: id === void 0 ? { type: action.resource } : {
|
|
1898
|
+
type: action.resource,
|
|
1899
|
+
id
|
|
1900
|
+
},
|
|
1901
|
+
adapter: "better-supabase",
|
|
1902
|
+
token: parseApprovalHeader(request.context.approval ?? null),
|
|
1903
|
+
consume
|
|
1904
|
+
}), action.key);
|
|
1270
1905
|
};
|
|
1271
|
-
const scoped = (claims, tenant, intent, runtime) => runtime?.memo === void 0 ? instanceFor(claims, tenant, intent, runtime) : runtime.memo.once(`permdock:authorizer:${JSON.stringify([
|
|
1272
|
-
claims ?? null,
|
|
1273
|
-
tenant,
|
|
1274
|
-
intent
|
|
1275
|
-
])}`, () => instanceFor(claims, tenant, intent, runtime));
|
|
1276
1906
|
const failed = (error, request) => {
|
|
1277
1907
|
report(options.onError, error, {
|
|
1278
1908
|
permission: keyOf(request),
|
|
@@ -1293,33 +1923,17 @@ function authorizer(options) {
|
|
|
1293
1923
|
} },
|
|
1294
1924
|
async evaluate(request, runtime) {
|
|
1295
1925
|
try {
|
|
1296
|
-
return decideWith(await scoped(claimsOf(request), tenantOf(request), intentOf(request, false), runtime), request);
|
|
1926
|
+
return await decideWith(await scoped(claimsOf(request), tenantOf(request), intentOf(request, false), runtime), request, true);
|
|
1297
1927
|
} catch (error) {
|
|
1298
1928
|
return failed(error, request);
|
|
1299
1929
|
}
|
|
1300
1930
|
},
|
|
1301
1931
|
evaluations(requests, runtime) {
|
|
1302
|
-
const
|
|
1303
|
-
const shared = (request) =>
|
|
1304
|
-
const claims = claimsOf(request);
|
|
1305
|
-
const tenant = tenantOf(request);
|
|
1306
|
-
const intent = intentOf(request, true);
|
|
1307
|
-
let byKey = cache.get(claims);
|
|
1308
|
-
if (byKey === void 0) {
|
|
1309
|
-
byKey = /* @__PURE__ */ new Map();
|
|
1310
|
-
cache.set(claims, byKey);
|
|
1311
|
-
}
|
|
1312
|
-
const key = JSON.stringify([tenant, intent]);
|
|
1313
|
-
let instance = byKey.get(key);
|
|
1314
|
-
if (instance === void 0) {
|
|
1315
|
-
instance = scoped(claims, tenant, intent, runtime);
|
|
1316
|
-
byKey.set(key, instance);
|
|
1317
|
-
}
|
|
1318
|
-
return instance;
|
|
1319
|
-
};
|
|
1932
|
+
const batch = /* @__PURE__ */ new Map();
|
|
1933
|
+
const shared = (request) => scoped(claimsOf(request), tenantOf(request), intentOf(request, true), runtime, batch);
|
|
1320
1934
|
return Promise.all(requests.map(async (request) => {
|
|
1321
1935
|
try {
|
|
1322
|
-
return decideWith(await shared(request), request);
|
|
1936
|
+
return await decideWith(await shared(request), request, false);
|
|
1323
1937
|
} catch (error) {
|
|
1324
1938
|
return failed(error, request);
|
|
1325
1939
|
}
|
|
@@ -1332,14 +1946,17 @@ function authorizer(options) {
|
|
|
1332
1946
|
function roleOf(options, columns) {
|
|
1333
1947
|
const column = columns.role;
|
|
1334
1948
|
if (options.roles !== void 0 && options.roleThrough !== void 0) throw new TypeError("PermDock: blockMembershipSource takes roles or roleThrough, not both; a junction holds role keys");
|
|
1335
|
-
if (options.roles === "many")
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1949
|
+
if (options.roles === "many") {
|
|
1950
|
+
const junction = options.tables?.membershipRoles;
|
|
1951
|
+
return {
|
|
1952
|
+
through: junction?.table ?? `${qualifySqlTable(columns.table).split(".")[0] ?? "better_supabase"}.membership_roles`,
|
|
1953
|
+
on: {
|
|
1954
|
+
[columns.tenant]: junction?.columns.tenant ?? columns.tenant,
|
|
1955
|
+
[columns.user]: junction?.columns.user ?? columns.user
|
|
1956
|
+
},
|
|
1957
|
+
column: junction?.columns.role ?? "role"
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1343
1960
|
if (options.roles !== void 0) return {
|
|
1344
1961
|
through: options.roles.table,
|
|
1345
1962
|
on: { [options.roles.id ?? "id"]: options.roles.membership },
|
|
@@ -1361,11 +1978,14 @@ function roleOf(options, columns) {
|
|
|
1361
1978
|
*/
|
|
1362
1979
|
function blockMembershipSource(options = {}) {
|
|
1363
1980
|
const c = options.columns ?? {};
|
|
1981
|
+
const block = options.tables?.memberships;
|
|
1364
1982
|
const scope = options.scope ?? "organization";
|
|
1365
|
-
const
|
|
1366
|
-
const
|
|
1367
|
-
const
|
|
1368
|
-
const
|
|
1983
|
+
const disabled = c.disabledAt === void 0 ? block?.columns.disabledAt : c.disabledAt;
|
|
1984
|
+
const disabledAt = disabled === null ? void 0 : disabled ?? "disabled_at";
|
|
1985
|
+
const table = options.table ?? block?.table ?? "better_supabase.memberships";
|
|
1986
|
+
const tenant = c.tenant ?? block?.columns.tenant ?? "organization_id";
|
|
1987
|
+
const user = c.user ?? block?.columns.user ?? "user_id";
|
|
1988
|
+
const role = c.role ?? block?.columns.role ?? "role";
|
|
1369
1989
|
return junctionMembershipSource({
|
|
1370
1990
|
table,
|
|
1371
1991
|
scope,
|
|
@@ -1373,7 +1993,7 @@ function blockMembershipSource(options = {}) {
|
|
|
1373
1993
|
user,
|
|
1374
1994
|
roles: roleOf(options, {
|
|
1375
1995
|
table,
|
|
1376
|
-
role
|
|
1996
|
+
role,
|
|
1377
1997
|
tenant,
|
|
1378
1998
|
user
|
|
1379
1999
|
}),
|
|
@@ -1515,9 +2135,10 @@ const refused = (reason) => ({
|
|
|
1515
2135
|
* The `authorize` and `visible` hooks of better-supabase's `createMcp`, for
|
|
1516
2136
|
* tools whose `meta` is a PermDock permission. A tool without a permission is
|
|
1517
2137
|
* hidden and refused. `authorize` grants only a `granted` decision: an
|
|
1518
|
-
* approval-required call is refused, because `
|
|
1519
|
-
* A
|
|
1520
|
-
*
|
|
2138
|
+
* approval-required call is refused, because `authorize` has no approval
|
|
2139
|
+
* outcome. A tool whose `permission` goes through `authorizer` with a `store`
|
|
2140
|
+
* returns the approval id and resumes it instead. A denial's text names the
|
|
2141
|
+
* first denial reason. A throw anywhere refuses and reaches `onError`.
|
|
1521
2142
|
*/
|
|
1522
2143
|
function toolPolicy(options) {
|
|
1523
2144
|
return Object.freeze({
|
|
@@ -1828,4 +2449,44 @@ function handlerCan(source, options = {}) {
|
|
|
1828
2449
|
};
|
|
1829
2450
|
}
|
|
1830
2451
|
//#endregion
|
|
1831
|
-
|
|
2452
|
+
//#region src/better-supabase/workflow-resource.ts
|
|
2453
|
+
const MAX_ID_LENGTH = 256;
|
|
2454
|
+
function invalid(detail) {
|
|
2455
|
+
return freezeDeep({
|
|
2456
|
+
ok: false,
|
|
2457
|
+
cause: "validation",
|
|
2458
|
+
decision: {
|
|
2459
|
+
outcome: "denied",
|
|
2460
|
+
denials: [{
|
|
2461
|
+
role: null,
|
|
2462
|
+
reason: "validation",
|
|
2463
|
+
detail
|
|
2464
|
+
}],
|
|
2465
|
+
alternatives: []
|
|
2466
|
+
}
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
/**
|
|
2470
|
+
* The resource a `workflow.start` check decides on for a run started on
|
|
2471
|
+
* `subject`: `{ type, id }`, with the type `types` maps the subject type to. A
|
|
2472
|
+
* subject type `types` does not name, a missing or empty id, or an id over 256
|
|
2473
|
+
* characters is `{ ok: false, cause: 'validation' }` with the denial to
|
|
2474
|
+
* return.
|
|
2475
|
+
*/
|
|
2476
|
+
function workflowResource(subject, types) {
|
|
2477
|
+
if (!isRecord(subject)) return invalid("subject is not a { type, id } record");
|
|
2478
|
+
const { type, id } = subject;
|
|
2479
|
+
if (typeof type !== "string" || !Object.hasOwn(types, type)) return invalid(`unknown subject type ${JSON.stringify(type) ?? "undefined"}`);
|
|
2480
|
+
if (typeof id !== "string" || id === "" || id.length > MAX_ID_LENGTH) return invalid("subject id is not a non-empty string of at most 256 characters");
|
|
2481
|
+
const resource = types[type];
|
|
2482
|
+
if (typeof resource !== "string" || resource === "") return invalid(`subject type ${type} maps to no resource`);
|
|
2483
|
+
return freezeDeep({
|
|
2484
|
+
ok: true,
|
|
2485
|
+
resource: {
|
|
2486
|
+
type: resource,
|
|
2487
|
+
id
|
|
2488
|
+
}
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
//#endregion
|
|
2492
|
+
export { accessReviewApplier, apiKeyClaimOptions, apiKeyVerifier, authorizationHooks, authorizationProvider, authorizer, blockApprovalPolicySource, blockApprovalStore, blockMembershipSource, bucketPolicy, credentialGuard, getPermissions, handlerCan, hookCan, permdockFor, principalFromMemberships, principalSource, roleOrderFromCatalog, subjectFromBetterSupabase, subjectFromShareLink, subscriberPolicy, supportPolicy, toolPolicy, topicPolicy, workflowResource };
|