permdock 0.1.0 → 0.3.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 +106 -47
- package/dist/a2a/index.d.ts +7 -16
- package/dist/a2a/index.js +59 -98
- package/dist/adapter-context-mlWsVGGg.js +23 -0
- package/dist/ai-sdk/index.d.ts +37 -16
- package/dist/ai-sdk/index.js +30 -13
- package/dist/approvals/index.d.ts +3 -3
- package/dist/approvals/index.js +5 -4
- package/dist/{approvals-DaxY-Bdg.js → approvals-DbYAuNkG.js} +17 -11
- package/dist/approvers-DT4AVyIg.js +52 -0
- package/dist/{arazzo-CTeVuXr5.js → arazzo-BAgw9ap6.js} +2 -2
- package/dist/{arazzo-aoTkqYsJ.js → arazzo-CKwwqFWz.js} +2 -2
- package/dist/authzen/index.d.ts +4 -13
- package/dist/authzen/index.js +16 -50
- package/dist/better-auth/index.d.ts +20 -3
- package/dist/better-auth/index.js +27 -7
- package/dist/better-supabase/index.d.ts +223 -0
- package/dist/better-supabase/index.js +648 -0
- package/dist/bind-C_ob3LKa.d.ts +25 -0
- package/dist/bind-CuzGytRf.js +97 -0
- package/dist/boundary-BM8a_P6E.js +13 -0
- package/dist/boundary-BmHxVH1J.d.ts +15 -0
- package/dist/canonical-json-DemiK4Vp.js +26 -0
- package/dist/{capability-BDG_nkxt.d.ts → capability-2kYTMWDv.d.ts} +2 -2
- package/dist/{capability-B68OFnZx.js → capability-4qPdO9gk.js} +7 -5
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/catalog-CRW5Mx_B.js +2 -0
- package/dist/{catalog-H-vFGwWK.js → catalog-DWF5cYqB.js} +4 -4
- package/dist/{catalog-B0E6bggc.js → catalog-dFZdWCGN.js} +2 -2
- package/dist/{catalog-doc-BSo_Q_-6.js → catalog-doc-DdGm82XT.js} +20 -32
- package/dist/claude-agent/index.d.ts +5 -14
- package/dist/claude-agent/index.js +4 -10
- package/dist/clerk/index.d.ts +12 -3
- package/dist/clerk/index.js +26 -5
- package/dist/cli/bin.js +1 -1
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +2 -2
- package/dist/clients-C7zLFuWT.d.ts +12 -0
- package/dist/clients-DjU5ABMy.js +23 -0
- package/dist/cloud/index.d.ts +4 -5
- package/dist/cloud/index.js +20 -6
- package/dist/{cloud-BNQTrmrr.js → cloud-CB_ELYuX.js} +2 -2
- package/dist/{cloud-Cuxr6Czw.js → cloud-DWLXO2af.js} +7 -6
- package/dist/collect-DClJsheJ.js +83 -0
- package/dist/{collect-O-tOtbnk.js → collect-DSTE3ml9.js} +194 -42
- package/dist/collect-DSWOk19R.js +2 -0
- package/dist/{commands-BMh_Etm2.js → commands-DSxCqG9A.js} +16 -12
- package/dist/compile/index.d.ts +3 -0
- package/dist/compile/index.js +2 -0
- package/dist/{compile-D22iwvuV.js → compile-Ba4blROR.js} +85 -30
- package/dist/compile-BkRyBDBw.d.ts +94 -0
- package/dist/config-BJiM-xAm.js +157 -0
- package/dist/config-Dnszbv-6.js +36 -0
- package/dist/config-report-DMvWKUxL.js +44 -0
- package/dist/{connection-JvqwHODC.js → connection-CU8MQxRi.js} +1 -1
- package/dist/{context-CKEAabd7.js → context-Bcy85rGi.js} +36 -27
- package/dist/convex/index.d.ts +9 -4
- package/dist/convex/index.js +14 -4
- package/dist/create-B1d519XK.d.ts +245 -0
- package/dist/create-CMwIl-w2.js +253 -0
- package/dist/{credential-B_rkUNlY.js → credential-8P81ff4u.js} +26 -46
- package/dist/delegation-D8pUEuce.js +103 -0
- package/dist/{describe-Bt1YD5uS.js → describe-B_gVZEVi.js} +10 -15
- package/dist/{describe-DernyItf.d.ts → describe-DMYiCjJV.d.ts} +9 -10
- package/dist/{diff-x5Il8LVa.js → diff-2VaeChzX.js} +44 -11
- package/dist/{diff-BUzf5Poi.js → diff-DBIhz_f2.js} +2 -2
- package/dist/{doctor-Bqo6FwUa.js → doctor-BwCAMLx_.js} +2 -2
- package/dist/doctor-collect-CHk6XWOA.js +665 -0
- package/dist/doctor-igIiM_Hl.js +1194 -0
- package/dist/drizzle/index.d.ts +4 -3
- package/dist/drizzle/index.js +3 -3
- package/dist/elysia/index.d.ts +6 -28
- package/dist/elysia/index.js +16 -60
- package/dist/{errors-DSMnAVNa.js → errors-CWfoNg9N.js} +8 -4
- package/dist/{errors-Iy71s7Oj.js → errors-Cz5zn0mW.js} +17 -71
- package/dist/{errors-DbZ1YOnb.d.ts → errors-DPdqI50v.d.ts} +8 -21
- package/dist/{errors-m3u9TmDu.js → errors-DRnGZHzF.js} +4 -1
- package/dist/evaluation-items-CfCCejQd.js +40 -0
- package/dist/{evaluations-DsAU9ppl.js → evaluations-tz6D9A2G.js} +28 -46
- package/dist/eve/index.d.ts +6 -15
- package/dist/eve/index.js +7 -12
- package/dist/expiry-BoBJ8Tvc.js +7 -0
- package/dist/express/index.d.ts +6 -29
- package/dist/express/index.js +28 -58
- package/dist/fastify/index.d.ts +6 -29
- package/dist/fastify/index.js +16 -65
- package/dist/fields-BMl1RxoX.js +26 -0
- package/dist/{files-B_wAGYmt.js → files-DWUN9PzI.js} +5 -1
- package/dist/{fixtures-Cvaw0gDG.js → fixtures-ht28Cplr.js} +5 -2
- package/dist/{freeze-y1_jQL4v.js → freeze-CrU8nQeX.js} +31 -2
- package/dist/{from-snapshot-BMGnhmgv.js → from-snapshot-CTLijvdm.js} +66 -48
- package/dist/{grantee-Bv04_Vzf.js → grantee-Cv5Sogfv.js} +88 -7
- package/dist/graph-sql-B-dsAT6j.d.ts +29 -0
- package/dist/{headers-B5RRv3Xm.js → headers-BZSDieNT.js} +1 -1
- package/dist/{headers-C8SHQccD.d.ts → headers-Cf-zWvsO.d.ts} +1 -1
- package/dist/helpers-H_KARDVC.js +421 -0
- package/dist/hono/index.d.ts +6 -28
- package/dist/hono/index.js +11 -43
- package/dist/{hosted-BwKQ1BTA.js → hosted-DJQw46hn.js} +6 -5
- package/dist/{http-GgVApX1k.js → http-BIhmLfCc.js} +4 -20
- package/dist/http-SO7qGttf.js +44 -0
- package/dist/ids-DC4V4CFu.js +18 -0
- package/dist/index-0nNqHspG.d.ts +6 -0
- package/dist/{index-xIxJLwOQ.d.ts → index-BNc7Wer_.d.ts} +75 -7
- package/dist/{index-CltcQ4Bf.d.ts → index-C7Tw7MaW.d.ts} +68 -5
- package/dist/{index-LF_hS-yn.d.ts → index-C8z4RjQV.d.ts} +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +22 -17
- package/dist/instance-options-Dhhcnm3y.js +22 -0
- package/dist/instance-options-jARdwfPN.d.ts +11 -0
- package/dist/{parse-DPfIvFMU.js → json-schema-BsAflUGW.js} +28 -57
- package/dist/jwt/index.d.ts +10 -6
- package/dist/jwt/index.js +32 -13
- package/dist/kernel-_RRHmnMY.js +208 -0
- package/dist/kysely/index.d.ts +4 -3
- package/dist/kysely/index.js +3 -3
- package/dist/live-options-H2eG8dfJ.js +60 -0
- package/dist/{load-BZ24CpZP.js → load-DhWEB9P1.js} +16 -10
- package/dist/load-DwJUafYN.js +2 -0
- package/dist/manifest-iaoBXHKc.js +783 -0
- package/dist/manifest-uDS9y5de.d.ts +206 -0
- package/dist/{map-DowSO96j.js → map-AcxK9o_D.js} +4 -15
- package/dist/{map-claims-B_6I1Rn-.js → map-claims-Z4QZOiFF.js} +13 -4
- package/dist/map-error-pgUWNNtG.js +23 -0
- package/dist/may-use-DndQ5tQ-.js +77 -0
- package/dist/mcp/index.d.ts +54 -22
- package/dist/mcp/index.js +218 -178
- package/dist/nest/index.d.ts +26 -31
- package/dist/nest/index.js +75 -84
- package/dist/next/client.d.ts +7 -8
- package/dist/next/client.js +11 -9
- package/dist/next/index.d.ts +30 -47
- package/dist/next/index.js +45 -60
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +32 -1
- package/dist/node/index.d.ts +9 -28
- package/dist/node/index.js +11 -43
- package/dist/openai/index.d.ts +6 -15
- package/dist/openai/index.js +7 -20
- package/dist/openapi/index.d.ts +87 -4
- package/dist/openapi/index.js +3 -3
- package/dist/{openapi-DcqM8nU7.js → openapi-BqRBvAng.js} +237 -5
- package/dist/{openapi-DWD0Uc_4.js → openapi-CqqEbHsL.js} +5 -5
- package/dist/{openapi-import-BWRUNNeW.js → openapi-import-cmwetbzw.js} +1 -1
- package/dist/{openapi-CwePaF7C.js → openapi-l0xYHkcv.js} +3 -3
- package/dist/{openapi-schema-D8j_T9tO.js → openapi-schema--ARjyfQr.js} +2 -2
- package/dist/orpc/index.d.ts +15 -28
- package/dist/orpc/index.js +74 -117
- package/dist/otel/index.d.ts +2 -2
- package/dist/parse-BuyjxhZV.js +53 -0
- package/dist/parse-local-manifest-BrMozJCo.js +36 -0
- package/dist/parse-local-manifest-JLnaBH0S.d.ts +10 -0
- package/dist/pdp/index.d.ts +2 -2
- package/dist/pdp/index.js +14 -127
- package/dist/{permdock-yoZ7EjxX.js → permdock-CbgvLnuo.js} +672 -149
- package/dist/{permissions-B8vCg_bI.d.ts → permissions-CDgvKG5L.d.ts} +2 -2
- package/dist/{permissions-DC7GROPA.js → permissions-DVoF6dMt.js} +77 -15
- package/dist/{permissions-B4apnahQ.d.ts → permissions-DttnqqxC.d.ts} +34 -5
- package/dist/{permissions-CofRqesP.js → permissions-RJrovw98.js} +3 -3
- package/dist/{pins-CqE1jZuF.js → pins-8ILTftC9.js} +2 -2
- package/dist/{policy-Cq2fB8Q5.d.ts → policy-D2W7uv19.d.ts} +2 -2
- package/dist/{policy-CInmNGkM.d.ts → policy-IB6yxpC3.d.ts} +351 -42
- package/dist/{policy-ANp36PnI.js → policy-xyWg7QdR.js} +173 -22
- package/dist/powersync-BylawD_U.js +540 -0
- package/dist/powersync-DliZK_-y.js +62 -0
- package/dist/powersync-e_AZrKbg.js +2 -0
- package/dist/prisma/index.d.ts +3 -2
- package/dist/prisma/index.js +45 -20
- package/dist/{problem-Cgp9-KZA.js → problem-B43ztKIm.js} +74 -109
- package/dist/problem-details-DX075l9V.d.ts +7 -0
- package/dist/problem-details-_AMOYz-w.js +144 -0
- package/dist/{protected-BuPAycJT.d.ts → protected-BdnIEFkY.d.ts} +5 -4
- package/dist/protected-utU-SLuy.js +100 -0
- package/dist/protected-xAr24FEC.d.ts +40 -0
- package/dist/provider-CSQd7m92.js +52 -0
- package/dist/{provider-wTAUda2v.d.ts → provider-DZY281jO.d.ts} +1 -1
- package/dist/provider-store-Cfbvo4DE.js +43 -0
- package/dist/react/index.d.ts +7 -5
- package/dist/react/index.js +5 -4
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +166 -10
- package/dist/react-native/index.js +607 -85
- package/dist/{refs-ChLSxbST.js → refs-KYb4ntaz.js} +4 -3
- package/dist/{relations-r4soIFZB.js → relations-cjOL01hZ.js} +110 -31
- package/dist/replay-BsOyTrdV.js +28 -0
- package/dist/replay-CYyiLiwO.d.ts +5 -0
- package/dist/resource-ref-CaS9yVPi.js +20 -0
- package/dist/revocations-Bhr0aMIC.js +27 -0
- package/dist/{rls-BOFU3chG.js → rls-B4ur8nKQ.js} +8 -3
- package/dist/rls-DQtdgTHB.js +2521 -0
- package/dist/rls-fields-74oZPXWL.js +3582 -0
- package/dist/{rls-graph-DxQ39PJj.js → rls-graph-CHwx0V8q.js} +100 -36
- package/dist/{rls-import-CXV6LBlS.js → rls-import-BTFVk3jH.js} +7 -6
- package/dist/{rls-migrate-DrFPbf8p.js → rls-migrate-CN4TlJTO.js} +6 -5
- package/dist/rls-ownership-RuFqaqGz.js +843 -0
- package/dist/rls-shared-Cewd_-zI.js +495 -0
- package/dist/rls-verify-CVQTHTh5.js +746 -0
- package/dist/{row-check-BajTgQZ-.d.ts → row-check-Dt35czLy.d.ts} +12 -15
- package/dist/row-conditions-h2HythXQ.js +27 -0
- package/dist/rpc-DL9Sqvb1.js +89 -0
- package/dist/{run-CJ6UvS3a.js → run-DKDbM4Du.js} +31 -12
- package/dist/runtime-CB8gQFvS.js +11 -0
- package/dist/{saas-BRrZzJkM.js → saas-DhpJjnBA.js} +17 -7
- package/dist/{schema-P8yoma__.js → schema-D3MnDMxa.js} +8 -4
- package/dist/scim/index.d.ts +1 -1
- package/dist/scim/index.js +1 -1
- package/dist/{scim-9NFa0XVZ.js → scim-BFGBBAqR.js} +1 -1
- package/dist/{scopes-cKVp_WTw.js → scopes-CVdPrWN-.js} +35 -8
- package/dist/server/index.d.ts +22 -11
- package/dist/server/index.js +11 -8
- package/dist/{credentials-BgRX3WEg.js → server-DlZ28Oz5.js} +1 -1
- package/dist/shared-DRu8VFVM.js +123 -0
- package/dist/{sink-Cfr9-7YC.d.ts → sink-B1fZzIMf.d.ts} +1 -1
- package/dist/skills-Biq5uup3.js +192 -0
- package/dist/{skills-C_lpV4Jx.js → skills-Bx-U0irG.js} +10 -4
- package/dist/{snapshot-DE8dE_hz.js → snapshot-Enxd0n-s.js} +138 -214
- package/dist/snapshot-cache-B-LxUahj.d.ts +45 -0
- package/dist/snapshot-cache-D6jLelln.js +44 -0
- package/dist/solid/index.d.ts +39 -9
- package/dist/solid/index.js +76 -85
- package/dist/sources-4p0-4xQm.js +545 -0
- package/dist/sources-BdOYLw9j.d.ts +192 -0
- package/dist/{sql-Bll1d7y0.js → sql-xdg5f4Fi.js} +6 -1
- package/dist/{src-CTNT-PBW.js → src-BDwEZWrZ.js} +149 -11
- package/dist/ssf/index.d.ts +3 -2
- package/dist/ssf/index.js +3 -29
- package/dist/store-DMAWwAJV.js +371 -0
- package/dist/{store-Bq90395i.d.ts → store-jOBYpiXC.d.ts} +13 -4
- package/dist/store-options-ByhZq1jV.js +716 -0
- package/dist/{stores-CkG2HrSg.js → stores-BT2pSCub.js} +10 -63
- package/dist/{stores-CvOnu_2T.d.ts → stores-C0IMQrGI.d.ts} +20 -9
- package/dist/{stream-BhMfZOwr.d.ts → stream-D1RXubw2.d.ts} +2 -2
- package/dist/{stream-Q_xlB6Ko.js → stream-DDpvynSB.js} +16 -2
- package/dist/subject-settings-BwBmCdTR.js +128 -0
- package/dist/supabase/index.d.ts +133 -6
- package/dist/supabase/index.js +4 -3
- package/dist/supabase/middleware.d.ts +32 -27
- package/dist/supabase/middleware.js +56 -22
- package/dist/{supabase-D8UlBA7Z.js → supabase-Boqb9a_Y.js} +3 -3
- package/dist/{supabase-DTxGtYdi.js → supabase-C0qESOa-.js} +347 -367
- package/dist/supabase-config-BpPXt3x1.js +472 -0
- package/dist/supabase-hook-6sYLXg_u.js +2 -0
- package/dist/{supabase-hook-D_OqGb_V.js → supabase-hook-CSLQ4zJ3.js} +1037 -705
- package/dist/supautils-Cn4R4cJ4.js +91 -0
- package/dist/svelte/PermissionBoundary.svelte +37 -0
- package/dist/svelte/Protected.svelte +8 -2
- package/dist/svelte/index.d.ts +9 -6
- package/dist/svelte/index.js +61 -7
- package/dist/svelte/runtime.d.ts +4 -3
- package/dist/svelte/runtime.js +4 -3
- package/dist/svelte/source.d.ts +9 -6
- package/dist/svelte/source.js +7 -4
- package/dist/terminal/index.d.ts +6 -14
- package/dist/terminal/index.js +44 -32
- package/dist/testing/index.d.ts +105 -16
- package/dist/testing/index.js +504 -40
- package/dist/testing/saas/index.d.ts +4 -4
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -1
- package/dist/token-CIWxJOCh.js +50 -0
- package/dist/trpc/index.d.ts +7 -26
- package/dist/trpc/index.js +17 -100
- package/dist/types-BnIEz1sU.d.ts +48 -0
- package/dist/{types-CxquUByI.d.ts → types-Bvc4oWT6.d.ts} +1 -1
- package/dist/{sources-DOpW5Ila.d.ts → types-C97IskBw.d.ts} +86 -127
- package/dist/types-CKNSyIf_.d.ts +52 -0
- package/dist/types-Cq_H8N_X.d.ts +197 -0
- package/dist/{types-D6Z6-rKJ.d.ts → types-DbjRN55U.d.ts} +273 -22
- package/dist/{types-BBK-exvJ.d.ts → types-Dnemwd77.d.ts} +7 -1
- package/dist/{types-BvBk1wgn.d.ts → types-DxiCGmmd.d.ts} +5 -3
- package/dist/{types-B9dmiiab.d.ts → types-QCuYJh4q.d.ts} +1 -1
- package/dist/{types-B8w5PFtS.d.ts → types-aqOHTHcp.d.ts} +17 -1
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +10 -9
- package/dist/usage-DorAJiU_.js +2 -0
- package/dist/{usage-1ozMubLY.js → usage-GTixvDDU.js} +2 -2
- package/dist/{usage-CUnK6Ou9.js → usage-ItS7nLcl.js} +225 -62
- package/dist/validation-error-Ba264OaJ.d.ts +26 -0
- package/dist/validation-error-CnjZ4t92.js +33 -0
- package/dist/{validity-D0uRmJKJ.js → validity-uOp8UpYn.js} +57 -36
- package/dist/{verifier-B90agnhl.js → verifier-BxBo6I44.js} +10 -5
- package/dist/version-BvacVTZR.js +17 -0
- package/dist/views-Soe7bGSC.js +95 -0
- package/dist/{vocabulary-Dk0fs-Jk.d.ts → vocabulary-DA8a8zif.d.ts} +1 -1
- package/dist/vue/index.d.ts +35 -13
- package/dist/vue/index.js +86 -93
- package/dist/watch-BwxLTYMZ.js +2 -0
- package/dist/watch-DVBJ9cBq.js +128 -0
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +7 -13
- package/package.json +62 -36
- package/schemas/approval-request-v1.json +175 -0
- package/schemas/catalog-v1.json +29 -1
- package/schemas/supabase-claims-v1.json +6 -1
- package/schemas/supabase-manifest-v1.json +150 -6
- package/skills/permdock/SKILL.md +7 -7
- package/skills/permdock-agents/SKILL.md +5 -5
- package/skills/permdock-agents/references/runtimes.md +12 -10
- package/skills/permdock-approvals/SKILL.md +4 -4
- package/skills/permdock-approvals/references/stores-and-resume.md +15 -4
- package/skills/permdock-audit/SKILL.md +3 -3
- package/skills/permdock-credentials/SKILL.md +4 -4
- package/skills/permdock-credentials/references/tokens-keys-links.md +7 -2
- package/skills/permdock-data/SKILL.md +4 -4
- package/skills/permdock-data/references/queries-and-rls.md +14 -6
- package/skills/permdock-data/references/relationships.md +5 -3
- package/skills/permdock-tenancy/SKILL.md +9 -6
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +14 -8
- package/skills/permdock-wire/SKILL.md +4 -4
- package/skills/permdock-wire/references/adapters.md +42 -30
- package/skills/permdock-wire/references/policy-options.md +5 -3
- package/dist/approvers-aH0PJkmf.js +0 -20
- package/dist/catalog-idsHc0vo.js +0 -2
- package/dist/collect-B7_TIfmg.js +0 -49
- package/dist/collect-fRPAQIWb.js +0 -2
- package/dist/compile-CQbvyIBr.d.ts +0 -30
- package/dist/config-BPYmjTOp.js +0 -26
- package/dist/context-gtAwAILx.js +0 -6
- package/dist/create-BKF3dgFk.d.ts +0 -162
- package/dist/create-CSkhDU9T.js +0 -222
- package/dist/doctor-BGle8Gqi.js +0 -1314
- package/dist/helpers-DnSOrhB9.js +0 -682
- package/dist/index-fG6NSXlc.d.ts +0 -9
- package/dist/kernel-YOGCWNLf.js +0 -211
- package/dist/load-QOOm1z70.js +0 -2
- package/dist/manifest-DDQbC6K0.d.ts +0 -118
- package/dist/map-error-RJ3ZsUyA.js +0 -16
- package/dist/may-use-CnFEjVEp.js +0 -37
- package/dist/pg-4GJWDUxG.js +0 -953
- package/dist/plugin-4gr7IxFJ.js +0 -65
- package/dist/protected-B4K3BKoX.js +0 -138
- package/dist/protected-wY7nXhY9.d.ts +0 -23
- package/dist/provider-_wx46HpB.js +0 -48
- package/dist/rls-CJVja9Ww.js +0 -2119
- package/dist/rls-helpers-BxRuif0l.js +0 -1346
- package/dist/runtime-DP9ttFh1.js +0 -27
- package/dist/skills-Dx3QLnMY.js +0 -110
- package/dist/source-DoBBYJi2.js +0 -6
- package/dist/sources-DVHM3Mwq.js +0 -284
- package/dist/store-C6hFW6ta.js +0 -508
- package/dist/subject-settings-xt7V845g.js +0 -82
- package/dist/supabase-hook-DW-xP4hU.js +0 -2
- package/dist/types-DbWdEfcm.d.ts +0 -87
- package/dist/types-bva3i4Gf.d.ts +0 -98
- package/dist/types-i4BAOvdb.d.ts +0 -97
- package/dist/usage-PyEor6GA.js +0 -2
- package/dist/version-CnSh33Fj.js +0 -16
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { T as isForbiddenKey } from "./permissions-DVoF6dMt.js";
|
|
2
|
+
import { c as rootScope, l as scopeChain, o as resolveScope } from "./scopes-CVdPrWN-.js";
|
|
3
|
+
import { t as byCodePoint } from "./compare-pfyVnBjE.js";
|
|
4
|
+
import { i as scopeMembershipTable, r as scopeColumn } from "./compile-Ba4blROR.js";
|
|
5
|
+
import { a as quoteSqlTable, i as quoteSqlLiteral, r as quoteSqlIdent, t as SQL_IDENT } from "./sql-xdg5f4Fi.js";
|
|
6
|
+
import { d as keptKeys, l as roleColumn } from "./sources-4p0-4xQm.js";
|
|
7
|
+
//#region src/cli/rls-sql.ts
|
|
8
|
+
const CLI = "PermDock CLI";
|
|
9
|
+
const CLAIM = SQL_IDENT;
|
|
10
|
+
function checkActiveRow(label, row) {
|
|
11
|
+
quoteTable(row.table);
|
|
12
|
+
quoteIdent(row.id);
|
|
13
|
+
if (row.disabledAt !== void 0) quoteIdent(row.disabledAt);
|
|
14
|
+
if (row.status !== void 0) {
|
|
15
|
+
quoteIdent(row.status);
|
|
16
|
+
if (row.active === void 0 || row.active.length === 0) throw new Error(`PermDock CLI: ${label}.status needs the active values in ${label}.active`);
|
|
17
|
+
}
|
|
18
|
+
if (row.disabledAt === void 0 && row.status === void 0) throw new Error(`PermDock CLI: ${label} needs disabledAt or status to tell an active row`);
|
|
19
|
+
}
|
|
20
|
+
/** Validates `rls.suspension` and keys its scopes by declared name. */
|
|
21
|
+
function checkSuspension(suspension, scopes) {
|
|
22
|
+
if (suspension === void 0) return;
|
|
23
|
+
if (suspension.users !== void 0) checkActiveRow("rls.suspension.users", suspension.users);
|
|
24
|
+
const byName = {};
|
|
25
|
+
for (const [key, row] of Object.entries(suspension.scopes ?? {})) {
|
|
26
|
+
const name = resolveScope(scopes, key);
|
|
27
|
+
if (name === void 0) throw new Error(`PermDock CLI: rls.suspension.scopes.${key} names a scope the policy does not declare`);
|
|
28
|
+
checkActiveRow(`rls.suspension.scopes.${key}`, row);
|
|
29
|
+
if (row.keep !== void 0 && !Array.isArray(row.keep)) throw new Error(`PermDock CLI: rls.suspension.scopes.${key}.keep is a list of permissions or permission keys`);
|
|
30
|
+
const keep = keptKeys(row);
|
|
31
|
+
const { keep: _given, ...active } = row;
|
|
32
|
+
byName[name] = keep.length === 0 ? active : {
|
|
33
|
+
...active,
|
|
34
|
+
keep
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const memberships = suspension.memberships;
|
|
38
|
+
if (memberships?.keep !== void 0 && !Array.isArray(memberships.keep)) throw new Error("PermDock CLI: rls.suspension.memberships.keep is a list of permissions or permission keys");
|
|
39
|
+
const keep = keptKeys(memberships);
|
|
40
|
+
return {
|
|
41
|
+
...suspension.users === void 0 ? {} : { users: suspension.users },
|
|
42
|
+
...Object.keys(byName).length === 0 ? {} : { scopes: byName },
|
|
43
|
+
...keep.length === 0 ? {} : { memberships: { keep } }
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function disabledKeep(ctx) {
|
|
47
|
+
return keptKeys(ctx.suspension?.memberships);
|
|
48
|
+
}
|
|
49
|
+
function activeMembershipSql(ctx, disabledAt, permission = ctx.permission === void 0 ? void 0 : { key: ctx.permission }) {
|
|
50
|
+
if (disabledAt === void 0) return [];
|
|
51
|
+
const keep = disabledKeep(ctx);
|
|
52
|
+
if (permission !== void 0 && "key" in permission && keep.includes(permission.key)) return [];
|
|
53
|
+
return permission !== void 0 && "sql" in permission && keep.length > 0 ? [`(${disabledAt} is null or ${permission.sql} = any(array[${keep.map(quoteLiteral).join(", ")}]::text[]))`] : [`${disabledAt} is null`];
|
|
54
|
+
}
|
|
55
|
+
function keptRowSql(keep, permission) {
|
|
56
|
+
const kind = `coalesce(jsonb_typeof(${keep}), 'null')`;
|
|
57
|
+
if (permission === void 0) return `${kind} = 'null'`;
|
|
58
|
+
return `case ${kind} when 'null' then true when 'array' then ${keep} @> jsonb_build_array(${"key" in permission ? quoteLiteral(permission.key) : permission.sql}) else false end`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `exists` over an active row whose id column equals `id`: a missing row is
|
|
62
|
+
* suspended, so the check fails closed.
|
|
63
|
+
*/
|
|
64
|
+
function activeRowSql(row, id) {
|
|
65
|
+
const table = quoteTable(row.table.includes(".") ? row.table : `public.${row.table}`);
|
|
66
|
+
const parts = [`s.${quoteIdent(row.id)} = ${id}`];
|
|
67
|
+
if (row.disabledAt !== void 0) parts.push(`s.${quoteIdent(row.disabledAt)} is null`);
|
|
68
|
+
if (row.status !== void 0) {
|
|
69
|
+
const values = (row.active ?? []).map(quoteLiteral).join(", ");
|
|
70
|
+
parts.push(`s.${quoteIdent(row.status)}::text = any(array[${values}]::text[])`);
|
|
71
|
+
}
|
|
72
|
+
return `exists (select 1 from ${table} s where ${parts.join(" and ")})`;
|
|
73
|
+
}
|
|
74
|
+
/** The active-user check for `user`; empty without `rls.suspension.users`. */
|
|
75
|
+
function activeUserSql(ctx, user = subjectIdSql(ctx)) {
|
|
76
|
+
const row = ctx.suspension?.users;
|
|
77
|
+
return row === void 0 ? [] : [activeRowSql(row, user)];
|
|
78
|
+
}
|
|
79
|
+
/** The permission of a grant key held in `grant`: `key#group` and `key@level` name `key`. */
|
|
80
|
+
function grantPermissionSql(grant) {
|
|
81
|
+
return { sql: `split_part(split_part(${grant}, '#', 1), '@', 1)` };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The active-instance check for every suspendable scope on `scope`'s chain:
|
|
85
|
+
* its own and each ancestor's. `idOf` gives the SQL for the id the membership
|
|
86
|
+
* holds for a scope on that chain. A scope's `keep` lets `permission` through
|
|
87
|
+
* a suspended instance: a kept key known now drops its check, and one read
|
|
88
|
+
* at run time is compared with the kept keys.
|
|
89
|
+
*/
|
|
90
|
+
function activeInstancesSql(ctx, scope, idOf, permission = ctx.permission === void 0 ? void 0 : { key: ctx.permission }) {
|
|
91
|
+
const parts = [];
|
|
92
|
+
for (const name of scopeChain(ctx.scopes, scope)) {
|
|
93
|
+
const row = ctx.suspension?.scopes?.[name];
|
|
94
|
+
if (row === void 0) continue;
|
|
95
|
+
const id = idOf(name);
|
|
96
|
+
if (id === void 0) throw new Error(`PermDock CLI: rls.suspension.scopes.${name} needs the ${name} id on ${scope} memberships: add columns.${name} to the ${scope} memberships table`);
|
|
97
|
+
const keep = keptKeys(row);
|
|
98
|
+
if (permission !== void 0 && "key" in permission && keep.includes(permission.key)) continue;
|
|
99
|
+
const active = activeRowSql(row, `(${id})::${scopeTypeOf(ctx, name)}`);
|
|
100
|
+
parts.push(permission !== void 0 && "sql" in permission && keep.length > 0 ? `(${active} or ${permission.sql} = any(array[${keep.map(quoteLiteral).join(", ")}]::text[]))` : active);
|
|
101
|
+
}
|
|
102
|
+
return parts;
|
|
103
|
+
}
|
|
104
|
+
const SQL_TYPE = /^[A-Za-z_][A-Za-z0-9_]*( [A-Za-z_][A-Za-z0-9_]*)*(\[\])?$/u;
|
|
105
|
+
function sqlType(name) {
|
|
106
|
+
if (!SQL_TYPE.test(name)) throw new Error(`PermDock CLI: unsafe SQL type '${name}'`);
|
|
107
|
+
return name;
|
|
108
|
+
}
|
|
109
|
+
function tenantTypeOf(ctx) {
|
|
110
|
+
return sqlType(ctx.tenantType ?? "uuid");
|
|
111
|
+
}
|
|
112
|
+
function teamTypeOf(ctx) {
|
|
113
|
+
return sqlType(ctx.teamType ?? ctx.tenantType ?? "uuid");
|
|
114
|
+
}
|
|
115
|
+
/** Postgres type of scope `name`'s id: `scopeTypes`, then the tenant / team type by position. */
|
|
116
|
+
function scopeTypeOf(ctx, name) {
|
|
117
|
+
const declared = ctx.scopeTypes?.[name];
|
|
118
|
+
if (declared !== void 0) return sqlType(declared);
|
|
119
|
+
return name === ctx.scopes[1]?.name ? teamTypeOf(ctx) : tenantTypeOf(ctx);
|
|
120
|
+
}
|
|
121
|
+
function viaAllowed(value, allowed) {
|
|
122
|
+
return allowed.includes(value ?? "");
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A role with `for` counts only on a membership of one of those kinds:
|
|
126
|
+
* `case role when 'admin' then via = any(...) ... else true end`. A missing
|
|
127
|
+
* kind (`viaExpr` null) holds none of them. A constant kind folds into the
|
|
128
|
+
* roles it excludes, or no filter. `undefined` when nothing is filtered.
|
|
129
|
+
*/
|
|
130
|
+
function kindFilterSql(ctx, roleExpr, viaExpr) {
|
|
131
|
+
const kinds = Object.entries(ctx.ownership?.kinds ?? {}).toSorted(([a], [b]) => byCodePoint(a, b));
|
|
132
|
+
if (kinds.length === 0) return;
|
|
133
|
+
if (typeof viaExpr !== "string") {
|
|
134
|
+
const excluded = kinds.filter(([, allowed]) => !viaAllowed(viaExpr.value, allowed)).map(([role]) => role);
|
|
135
|
+
return excluded.length === 0 ? void 0 : `not (${roleExpr} = any(array[${excluded.map(quoteLiteral).join(", ")}]::text[]))`;
|
|
136
|
+
}
|
|
137
|
+
return `case ${roleExpr} ${kinds.map(([role, allowed]) => `when ${quoteLiteral(role)} then coalesce(${viaExpr}, '') = any(array[${allowed.map(quoteLiteral).join(", ")}]::text[])`).join(" ")} else true end`;
|
|
138
|
+
}
|
|
139
|
+
/** The kind check for one known role; `undefined` when it has no `for` or a constant kind it allows. */
|
|
140
|
+
function roleKindSql(ctx, role, viaExpr) {
|
|
141
|
+
const allowed = ctx.ownership?.kinds[role];
|
|
142
|
+
if (allowed === void 0) return;
|
|
143
|
+
if (typeof viaExpr !== "string") return viaAllowed(viaExpr.value, allowed) ? void 0 : "false";
|
|
144
|
+
return `coalesce(${viaExpr}, '') = any(array[${allowed.map(quoteLiteral).join(", ")}]::text[])`;
|
|
145
|
+
}
|
|
146
|
+
/** Roles with `for` held globally (no membership, so no kind) grant nothing. */
|
|
147
|
+
function globalKindFilterSql(ctx, roleExpr) {
|
|
148
|
+
const roles = Object.keys(ctx.ownership?.kinds ?? {}).toSorted();
|
|
149
|
+
return roles.length === 0 ? void 0 : `not (${roleExpr} = any(array[${roles.map(quoteLiteral).join(", ")}]::text[]))`;
|
|
150
|
+
}
|
|
151
|
+
/** The closure table graph grants read. Part of the SQL contract. */
|
|
152
|
+
const CLOSURE = { table: "permdock_closure" };
|
|
153
|
+
/**
|
|
154
|
+
* The snake_case SQL name of a resource or link (`chatThread` becomes
|
|
155
|
+
* `chat_thread`), or `undefined` when it is not made of ASCII letters,
|
|
156
|
+
* digits and underscores starting with a letter.
|
|
157
|
+
*/
|
|
158
|
+
function graphSqlName(name) {
|
|
159
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/u.test(name)) return;
|
|
160
|
+
return name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1_$2").replaceAll(/([A-Z]+)([A-Z][a-z])/gu, "$1_$2").toLowerCase();
|
|
161
|
+
}
|
|
162
|
+
function sqlNameOf(name, label) {
|
|
163
|
+
const sql = graphSqlName(name);
|
|
164
|
+
if (sql === void 0) throw new Error(`PermDock CLI: ${label} is not a SQL name (letters, digits and underscores, starting with a letter), so rls generate cannot name its helper`);
|
|
165
|
+
return sql;
|
|
166
|
+
}
|
|
167
|
+
/** The helper returning the ids of `resource` the subject holds a relation on: `permitted_<snake_case resource>_ids`. */
|
|
168
|
+
function graphHelper(resource) {
|
|
169
|
+
return permittedIdsHelper(sqlNameOf(resource, `resource '${resource}'`));
|
|
170
|
+
}
|
|
171
|
+
function inheritedRowsHelper(resource) {
|
|
172
|
+
return `permitted_${sqlNameOf(resource, `resource '${resource}'`)}_rows`;
|
|
173
|
+
}
|
|
174
|
+
/** The helper returning the ids of `resource` whose `link` points into the ids it is given. */
|
|
175
|
+
function linkHelper(resource, link) {
|
|
176
|
+
return `permdock_link_${sqlNameOf(resource, `resource '${resource}'`)}_${sqlNameOf(link, `link '${link}' on ${resource}`)}`;
|
|
177
|
+
}
|
|
178
|
+
function restrictedHelper(resource) {
|
|
179
|
+
return `permdock_restricted_${sqlNameOf(resource, `resource '${resource}'`)}`;
|
|
180
|
+
}
|
|
181
|
+
/** Graph SQL parts as RLS text: values inline as literals, the subject from the dialect's claim. */
|
|
182
|
+
function graphSqlText(parts, ctx) {
|
|
183
|
+
return parts.map((part) => {
|
|
184
|
+
if ("text" in part) return part.text;
|
|
185
|
+
if ("column" in part) return quoteIdent(part.column);
|
|
186
|
+
if ("subject" in part) return subjectIdSql(ctx);
|
|
187
|
+
return typeof part.value === "string" ? quoteLiteral(part.value) : String(part.value);
|
|
188
|
+
}).join("");
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The role column of an `rls.memberships` table aliased `alias`; a `through`
|
|
192
|
+
* column joins its roles table as `<alias>k`, after a newline and `indent`
|
|
193
|
+
* (inline without one).
|
|
194
|
+
*/
|
|
195
|
+
function memberRoleOf(table, alias = "m", indent) {
|
|
196
|
+
return roleColumn(table.role, qualifiedTable(table.table), alias, {
|
|
197
|
+
label: `rls.memberships ${table.table} role`,
|
|
198
|
+
prefix: CLI,
|
|
199
|
+
...indent === void 0 ? {} : { indent }
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/** The schema-qualified name of a table under `search_path = ''`. */
|
|
203
|
+
function qualifiedTable(name) {
|
|
204
|
+
return name.includes(".") ? name : `public.${name}`;
|
|
205
|
+
}
|
|
206
|
+
/** The helper returning the ids of scope `name` a grant key reaches. */
|
|
207
|
+
function permittedIdsHelper(name) {
|
|
208
|
+
if (!/^[a-z][a-z0-9_]*$/u.test(name)) throw new Error(`PermDock CLI: unsafe scope name '${name}'`);
|
|
209
|
+
return `permitted_${name}_ids`;
|
|
210
|
+
}
|
|
211
|
+
/** `permitted_<scope>_ids_for(p_user, p_grant)`: the same ids for a user the caller names. */
|
|
212
|
+
function permittedForHelper(name) {
|
|
213
|
+
return `${permittedIdsHelper(name)}_for`;
|
|
214
|
+
}
|
|
215
|
+
/** The helper returning the ids of scope `name` the subject holds any live membership of. */
|
|
216
|
+
function memberIdsHelper(name) {
|
|
217
|
+
if (!/^[a-z][a-z0-9_]*$/u.test(name)) throw new Error(`PermDock CLI: unsafe scope name '${name}'`);
|
|
218
|
+
return `member_${name}_ids`;
|
|
219
|
+
}
|
|
220
|
+
/** `member_<scope>_ids_for(p_user uuid)`: the same rows for a user the caller names; the token hook's helper. */
|
|
221
|
+
function memberForHelper(name) {
|
|
222
|
+
return `${memberIdsHelper(name)}_for`;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Whether `member_<scope>_ids_for` is generated for `name`: Supabase only
|
|
226
|
+
* (its one caller is `supabase_auth_admin`), and only where `memberships` maps
|
|
227
|
+
* a table for the scope or a membership source can hold it.
|
|
228
|
+
*/
|
|
229
|
+
function hasMemberFor(input, name) {
|
|
230
|
+
if (input.dialect !== "supabase") return false;
|
|
231
|
+
return memberForTable(input, name) !== void 0 || memberForSources(input, name).length > 0;
|
|
232
|
+
}
|
|
233
|
+
/** The table `member_<scope>_ids_for` reads for scope `name`, when `memberships` maps one with its id column. */
|
|
234
|
+
function memberForTable(input, name) {
|
|
235
|
+
const table = scopeMembershipTable(input.memberships, input.scopes, name);
|
|
236
|
+
return table !== void 0 && scopeColumn(table, input.scopes, name) !== void 0 ? table : void 0;
|
|
237
|
+
}
|
|
238
|
+
/** The sources `member_<scope>_ids_for` reads: none when a table is mapped, as in `database` mode. */
|
|
239
|
+
function memberForSources(input, name) {
|
|
240
|
+
if (memberForTable(input, name) !== void 0) return [];
|
|
241
|
+
return (input.memberSources ?? []).filter((source) => source.sql.scope === void 0 || source.sql.scope === name);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The membership sources that can hold scope `name` when `memberships` maps
|
|
245
|
+
* no table for it: every `fromTable` source and the `fromJunction` sources of
|
|
246
|
+
* that scope. Empty outside `database` mode.
|
|
247
|
+
*/
|
|
248
|
+
function scopeSources(ctx, name) {
|
|
249
|
+
if (ctx.authorize !== "database" || scopeTable(ctx, name) !== void 0) return [];
|
|
250
|
+
return (ctx.sources ?? []).filter((source) => source.sql.scope === void 0 || source.sql.scope === name);
|
|
251
|
+
}
|
|
252
|
+
/** The membership table mapped for scope `name`, with the column of its id and of the first scope's id. */
|
|
253
|
+
function scopeTable(ctx, name) {
|
|
254
|
+
const table = scopeMembershipTable(ctx.memberships, ctx.scopes, name);
|
|
255
|
+
if (table === void 0) return;
|
|
256
|
+
const column = scopeColumn(table, ctx.scopes, name);
|
|
257
|
+
if (column === void 0) return;
|
|
258
|
+
const root = rootScope(ctx.scopes);
|
|
259
|
+
const tenantColumn = root !== void 0 && scopeChain(ctx.scopes, name).includes(root) ? scopeColumn(table, ctx.scopes, root) : void 0;
|
|
260
|
+
return tenantColumn === void 0 ? {
|
|
261
|
+
table,
|
|
262
|
+
column
|
|
263
|
+
} : {
|
|
264
|
+
table,
|
|
265
|
+
column,
|
|
266
|
+
tenantColumn
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function keyTenantSql(ctx, tenant) {
|
|
270
|
+
const keys = ctx.apiKeys;
|
|
271
|
+
if (keys === void 0) return;
|
|
272
|
+
const named = `nullif(${subjectClaimJsonSql(ctx, keys.claim)} ->> ${quoteLiteral(keys.tenant)}, '')`;
|
|
273
|
+
return tenant === void 0 ? `${named} is null` : `(${named} is null or (${tenant})::text = ${named})`;
|
|
274
|
+
}
|
|
275
|
+
/** The active-tenant claim cast to the tenant column's type, so the comparison uses the column's index. */
|
|
276
|
+
function tenantClaimSql(ctx) {
|
|
277
|
+
return `${subjectClaimSql(ctx, ctx.tenantClaim)}::${tenantTypeOf(ctx)}`;
|
|
278
|
+
}
|
|
279
|
+
function quoteIdent(name) {
|
|
280
|
+
return quoteSqlIdent(name, CLI);
|
|
281
|
+
}
|
|
282
|
+
function quoteTable(name) {
|
|
283
|
+
return quoteSqlTable(name, CLI);
|
|
284
|
+
}
|
|
285
|
+
/** The membership kind of a row `m` of `table`: its column as text, or its constant (`null` without one). */
|
|
286
|
+
function memberVia(table) {
|
|
287
|
+
const via = table.via;
|
|
288
|
+
if (via === void 0) return { value: null };
|
|
289
|
+
return typeof via === "string" ? `m.${quoteIdent(via)}::text` : { value: via.value };
|
|
290
|
+
}
|
|
291
|
+
function quoteLiteral(value) {
|
|
292
|
+
return quoteSqlLiteral(value);
|
|
293
|
+
}
|
|
294
|
+
const USER_ID_HELPER = "permdock_user_id";
|
|
295
|
+
function userIdHelperSql(ctx) {
|
|
296
|
+
return `${quoteIdent(ctx.schema ?? "permdock")}.${USER_ID_HELPER}`;
|
|
297
|
+
}
|
|
298
|
+
const SESSION_LIVE_HELPER = "permdock_session_live";
|
|
299
|
+
/** `{ subject: { session: { live: true } } }` in a policy; only Supabase has `auth.sessions`. */
|
|
300
|
+
function liveSessionSql(ctx) {
|
|
301
|
+
if (ctx.dialect !== "supabase") throw new Error(`PermDock CLI: { subject: { session: { live: true } } } reads auth.sessions, which only the supabase dialect has (got ${ctx.dialect})`);
|
|
302
|
+
return `(select ${quoteIdent(ctx.schema ?? "permdock")}.${SESSION_LIVE_HELPER}())`;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* `permdock_session_live()`: the token's `session_id` names a session of its
|
|
306
|
+
* user that is still in `auth.sessions`, so a signed-out or revoked session
|
|
307
|
+
* stops passing before the token's `exp`. A token without one, such as a
|
|
308
|
+
* service key's, is never live.
|
|
309
|
+
*/
|
|
310
|
+
function sessionLiveHelperSql(ctx) {
|
|
311
|
+
const name = `${quoteIdent(ctx.schema ?? "permdock")}.${SESSION_LIVE_HELPER}`;
|
|
312
|
+
return `create or replace function ${name}()
|
|
313
|
+
returns boolean
|
|
314
|
+
language plpgsql
|
|
315
|
+
stable
|
|
316
|
+
security definer
|
|
317
|
+
set search_path = ''
|
|
318
|
+
as $$
|
|
319
|
+
declare
|
|
320
|
+
v_session uuid;
|
|
321
|
+
begin
|
|
322
|
+
begin
|
|
323
|
+
v_session := nullif((select auth.jwt()) ->> 'session_id', '')::uuid;
|
|
324
|
+
exception when invalid_text_representation then
|
|
325
|
+
return false;
|
|
326
|
+
end;
|
|
327
|
+
if v_session is null then
|
|
328
|
+
return false;
|
|
329
|
+
end if;
|
|
330
|
+
return exists (
|
|
331
|
+
select 1
|
|
332
|
+
from auth.sessions s
|
|
333
|
+
where s.id = v_session
|
|
334
|
+
and s.user_id::text = ${subjectIdSql(ctx)}::text
|
|
335
|
+
and (s.not_after is null or s.not_after > now())
|
|
336
|
+
);
|
|
337
|
+
end;
|
|
338
|
+
$$;
|
|
339
|
+
revoke execute on function ${name}() from public, anon;
|
|
340
|
+
grant execute on function ${name}() to authenticated;
|
|
341
|
+
`;
|
|
342
|
+
}
|
|
343
|
+
function subjectIdSql(ctx) {
|
|
344
|
+
if (ctx.subjectId !== void 0) return ctx.subjectId;
|
|
345
|
+
switch (ctx.dialect) {
|
|
346
|
+
case "supabase": return `(select ${userIdHelperSql(ctx)}())`;
|
|
347
|
+
case "neon": return "(select auth.user_id())";
|
|
348
|
+
case "guc": return `(select current_setting(${quoteLiteral(`${ctx.gucPrefix}.user_id`)}, true))`;
|
|
349
|
+
default: return ctx.dialect;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function subjectClaimSql(ctx, claim) {
|
|
353
|
+
if (!CLAIM.test(claim)) throw new Error(`PermDock CLI: unsafe claim name '${claim}'`);
|
|
354
|
+
switch (ctx.dialect) {
|
|
355
|
+
case "supabase": return `((select auth.jwt()) ->> ${quoteLiteral(claim)})`;
|
|
356
|
+
case "neon": return `((select auth.session()) ->> ${quoteLiteral(claim)})`;
|
|
357
|
+
case "guc": return `(select current_setting(${quoteLiteral(`${ctx.gucPrefix}.${claim}`)}, true))`;
|
|
358
|
+
default: return ctx.dialect;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/** A claim as `jsonb`; the `guc` dialect stores JSON text in `<prefix>.<claim>`. */
|
|
362
|
+
function subjectClaimJsonSql(ctx, claim) {
|
|
363
|
+
if (!CLAIM.test(claim)) throw new Error(`PermDock CLI: unsafe claim name '${claim}'`);
|
|
364
|
+
switch (ctx.dialect) {
|
|
365
|
+
case "supabase": return `((select auth.jwt()) -> ${quoteLiteral(claim)})`;
|
|
366
|
+
case "neon": return `((select auth.session()) -> ${quoteLiteral(claim)})`;
|
|
367
|
+
case "guc": return `nullif((select current_setting(${quoteLiteral(`${ctx.gucPrefix}.${claim}`)}, true)), '')::jsonb`;
|
|
368
|
+
default: return ctx.dialect;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function isRecord(value) {
|
|
372
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
373
|
+
}
|
|
374
|
+
/** The Postgres type a JSON Schema property compares as; `undefined` for text or an unknown shape. */
|
|
375
|
+
function columnTypeOf(property) {
|
|
376
|
+
if (!isRecord(property)) return;
|
|
377
|
+
const variants = [property["anyOf"], property["oneOf"]].find(Array.isArray);
|
|
378
|
+
if (variants !== void 0) {
|
|
379
|
+
const present = variants.filter((item) => !(isRecord(item) && item["type"] === "null"));
|
|
380
|
+
return present.length === 1 ? columnTypeOf(present[0]) : void 0;
|
|
381
|
+
}
|
|
382
|
+
const types = (Array.isArray(property["type"]) ? property["type"] : [property["type"]]).filter((item) => item !== "null");
|
|
383
|
+
if (types.length !== 1) return;
|
|
384
|
+
switch (types[0]) {
|
|
385
|
+
case "integer":
|
|
386
|
+
case "number": return "numeric";
|
|
387
|
+
case "boolean": return "boolean";
|
|
388
|
+
case "string": switch (property["format"]) {
|
|
389
|
+
case "date-time": return "timestamptz";
|
|
390
|
+
case "date": return "date";
|
|
391
|
+
case "uuid": return "uuid";
|
|
392
|
+
default: return;
|
|
393
|
+
}
|
|
394
|
+
default: return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Column types from a resource's JSON Schema (`~standard.jsonSchema`): numbers
|
|
399
|
+
* compare as `numeric`, booleans as `boolean`, and `date-time`, `date` and
|
|
400
|
+
* `uuid` strings as `timestamptz`, `date` and `uuid`. Other columns are left
|
|
401
|
+
* out and compare as text.
|
|
402
|
+
*/
|
|
403
|
+
function columnTypesOf(schema) {
|
|
404
|
+
const properties = isRecord(schema) ? schema["properties"] : void 0;
|
|
405
|
+
if (!isRecord(properties)) return {};
|
|
406
|
+
const types = {};
|
|
407
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
408
|
+
const type = isForbiddenKey(name) ? void 0 : columnTypeOf(property);
|
|
409
|
+
if (type !== void 0) types[name] = type;
|
|
410
|
+
}
|
|
411
|
+
return types;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Array columns from a resource's JSON Schema, each with the type its items
|
|
415
|
+
* compare as (`text` when `columnTypesOf` would leave the item out).
|
|
416
|
+
*/
|
|
417
|
+
function arrayColumnsOf(schema) {
|
|
418
|
+
const properties = isRecord(schema) ? schema["properties"] : void 0;
|
|
419
|
+
if (!isRecord(properties)) return {};
|
|
420
|
+
const arrays = {};
|
|
421
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
422
|
+
if (isForbiddenKey(name) || !isRecord(property)) continue;
|
|
423
|
+
const types = (Array.isArray(property["type"]) ? property["type"] : [property["type"]]).filter((item) => item !== "null");
|
|
424
|
+
if (types.length === 1 && types[0] === "array") arrays[name] = columnTypeOf(property["items"]) ?? "text";
|
|
425
|
+
}
|
|
426
|
+
return arrays;
|
|
427
|
+
}
|
|
428
|
+
function parseMembershipsFlag(raw) {
|
|
429
|
+
if (raw === void 0 || raw === "") return;
|
|
430
|
+
const colon = raw.indexOf(":");
|
|
431
|
+
const table = colon === -1 ? raw : raw.slice(0, colon);
|
|
432
|
+
const cols = (colon === -1 ? "" : raw.slice(colon + 1)).split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
433
|
+
return { tenant: {
|
|
434
|
+
table,
|
|
435
|
+
tenant: cols[0] ?? "tenant_id",
|
|
436
|
+
user: cols[1] ?? "user_id",
|
|
437
|
+
role: cols[2] ?? "role",
|
|
438
|
+
...cols[3] === void 0 ? {} : { expiresAt: cols[3] }
|
|
439
|
+
} };
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/cli/rls-shared.ts
|
|
443
|
+
/**
|
|
444
|
+
* The per-statement helpers every generated policy calls: `permdock_has` for
|
|
445
|
+
* global roles and one `permitted_<scope>_ids` per scope
|
|
446
|
+
* (`permittedIdsHelper`). Names are part of the SQL contract.
|
|
447
|
+
*/
|
|
448
|
+
const HELPERS = {
|
|
449
|
+
has: "permdock_has",
|
|
450
|
+
hasFor: "permdock_has_for"
|
|
451
|
+
};
|
|
452
|
+
/** Objects `--custom-roles` adds next to the helpers. Names are part of the SQL contract. */
|
|
453
|
+
const CUSTOM_ROLES = {
|
|
454
|
+
permissions: "custom_role_permissions",
|
|
455
|
+
includes: "custom_role_includes",
|
|
456
|
+
ceiling: "permdock_ceiling",
|
|
457
|
+
keys: "permdock_custom_keys",
|
|
458
|
+
beyond: "permdock_custom_role_beyond",
|
|
459
|
+
guard: "permdock_custom_role_guard",
|
|
460
|
+
beyondFor: "permdock_custom_role_beyond_for",
|
|
461
|
+
guardFor: "permdock_custom_role_guard_for",
|
|
462
|
+
shape: "permdock_custom_role_shape",
|
|
463
|
+
entries: "permdock_custom_role_entries",
|
|
464
|
+
replace: "permdock_replace_custom_role_grants",
|
|
465
|
+
rename: "permdock_rename_custom_role_grants",
|
|
466
|
+
remove: "permdock_delete_custom_role_grants",
|
|
467
|
+
trustedReplace: "permdock_trusted_replace_custom_role_grants",
|
|
468
|
+
trustedRename: "permdock_trusted_rename_custom_role_grants",
|
|
469
|
+
trustedRemove: "permdock_trusted_delete_custom_role_grants",
|
|
470
|
+
cascade: "permdock_cascade_custom_role"
|
|
471
|
+
};
|
|
472
|
+
/** The function `rls.roles` adds. The name is part of the SQL contract. */
|
|
473
|
+
const GLOBAL_ROLES = { replace: "permdock_replace_global_roles" };
|
|
474
|
+
function helperSchema(ctx) {
|
|
475
|
+
return ctx.schema ?? "permdock";
|
|
476
|
+
}
|
|
477
|
+
function qualified(ctx, name) {
|
|
478
|
+
return `${quoteIdent(helperSchema(ctx))}.${name}`;
|
|
479
|
+
}
|
|
480
|
+
/** `user` is the SQL for the user id: the dialect's (the default) or the `p_user` parameter of a `_for` helper. */
|
|
481
|
+
function signedIn(ctx, user = subjectIdSql(ctx)) {
|
|
482
|
+
return `coalesce(${user}::text, '') <> ''`;
|
|
483
|
+
}
|
|
484
|
+
function rootName(ctx) {
|
|
485
|
+
return ctx.scopes[0]?.name ?? "tenant";
|
|
486
|
+
}
|
|
487
|
+
/** The stored allow as `permissions` hands it to `permdock_custom_keys`: `key`, or `key@level` with levels. */
|
|
488
|
+
function allowEntry(ctx) {
|
|
489
|
+
return ctx.customRoles?.levels === true ? "permission || coalesce('@' || c.level, '')" : "permission";
|
|
490
|
+
}
|
|
491
|
+
function textArray(values) {
|
|
492
|
+
return values.length === 0 ? `'{}'::text[]` : `array[${values.map(quoteLiteral).join(", ")}]::text[]`;
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
export { sessionLiveHelperSql as $, keptRowSql as A, memberVia as B, grantPermissionSql as C, hasMemberFor as D, graphSqlText as E, memberForHelper as F, quoteIdent as G, permittedForHelper as H, memberForSources as I, restrictedHelper as J, quoteLiteral as K, memberForTable as L, kindFilterSql as M, linkHelper as N, inheritedRowsHelper as O, liveSessionSql as P, scopeTypeOf as Q, memberIdsHelper as R, globalKindFilterSql as S, graphSqlName as T, permittedIdsHelper as U, parseMembershipsFlag as V, qualifiedTable as W, scopeSources as X, roleKindSql as Y, scopeTable as Z, activeUserSql as _, helperSchema as a, tenantTypeOf as at, columnTypesOf as b, signedIn as c, CLOSURE as d, sqlType as et, SESSION_LIVE_HELPER as f, activeRowSql as g, activeMembershipSql as h, allowEntry as i, tenantClaimSql as it, keyTenantSql as j, isRecord as k, textArray as l, activeInstancesSql as m, GLOBAL_ROLES as n, subjectClaimSql as nt, qualified as o, userIdHelperSql as ot, USER_ID_HELPER as p, quoteTable as q, HELPERS as r, subjectIdSql as rt, rootName as s, CUSTOM_ROLES as t, subjectClaimJsonSql as tt, CLAIM as u, arrayColumnsOf as v, graphHelper as w, disabledKeep as x, checkSuspension as y, memberRoleOf as z };
|