permdock 0.1.0 → 0.2.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-cmvX2jDe.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 +206 -0
- package/dist/better-supabase/index.js +620 -0
- package/dist/bind-CVy3Espv.d.ts +25 -0
- package/dist/bind-VnYn2HKq.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-B68OFnZx.js → capability-4qPdO9gk.js} +7 -5
- package/dist/{capability-BDG_nkxt.d.ts → capability-Dq43tT1u.d.ts} +2 -2
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/catalog-BjZSovXQ.js +2 -0
- package/dist/{catalog-doc-BSo_Q_-6.js → catalog-doc-Dsc9y7sd.js} +23 -12
- package/dist/{catalog-B0E6bggc.js → catalog-h2tlKFkq.js} +2 -2
- package/dist/{catalog-H-vFGwWK.js → catalog-z8q6bi_r.js} +4 -4
- 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-BGhHS-lY.js} +2 -2
- package/dist/{cloud-Cuxr6Czw.js → cloud-Bo3e9Ak3.js} +7 -6
- package/dist/{collect-O-tOtbnk.js → collect-DDDv_ESs.js} +194 -42
- package/dist/collect-DLlujdHC.js +83 -0
- package/dist/collect-rG2IQ3t0.js +2 -0
- package/dist/{commands-BMh_Etm2.js → commands-DWOrSQic.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-CNnl6uIt.d.ts +94 -0
- package/dist/config-BJiM-xAm.js +157 -0
- package/dist/config-Bn00Z3ks.js +36 -0
- package/dist/config-report-Dw_JcUqT.js +44 -0
- package/dist/{connection-JvqwHODC.js → connection-CU8MQxRi.js} +1 -1
- package/dist/{context-CKEAabd7.js → context-Ttp0ekMT.js} +36 -27
- package/dist/convex/index.d.ts +9 -4
- package/dist/convex/index.js +14 -4
- package/dist/create-BWyx3jua.d.ts +245 -0
- package/dist/create-CPhDL6qx.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-PdoqESvg.d.ts} +9 -10
- package/dist/{diff-x5Il8LVa.js → diff-B8Tunc3o.js} +44 -11
- package/dist/{diff-BUzf5Poi.js → diff-CzqLxZ77.js} +2 -2
- package/dist/doctor-WpvG6p4k.js +1194 -0
- package/dist/doctor-collect-ByfYlNv1.js +664 -0
- package/dist/{doctor-Bqo6FwUa.js → doctor-rOr-4ZfS.js} +2 -2
- 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-DAqevpNk.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-C3sHpZNS.js} +65 -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-LF_hS-yn.d.ts → index-A0YNL8jZ.d.ts} +1 -1
- package/dist/index-CtE07CBF.d.ts +6 -0
- package/dist/{index-xIxJLwOQ.d.ts → index-D3vbkoSI.d.ts} +75 -7
- package/dist/{index-CltcQ4Bf.d.ts → index-D6_cSa1z.d.ts} +68 -5
- package/dist/index.d.ts +14 -9
- package/dist/index.js +22 -17
- package/dist/instance-options-BK_rNOSC.d.ts +11 -0
- package/dist/instance-options-Dhhcnm3y.js +22 -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-GSQnMz3O.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-CwePaF7C.js → openapi-Bbg4wGVL.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-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-BkGQNzAh.d.ts +10 -0
- package/dist/parse-local-manifest-BrMozJCo.js +36 -0
- package/dist/pdp/index.d.ts +2 -2
- package/dist/pdp/index.js +14 -127
- package/dist/{permdock-yoZ7EjxX.js → permdock-CzqXjCpU.js} +641 -126
- package/dist/{permissions-B8vCg_bI.d.ts → permissions-CDgvKG5L.d.ts} +2 -2
- package/dist/{permissions-CofRqesP.js → permissions-CpZrivYv.js} +3 -3
- package/dist/{permissions-DC7GROPA.js → permissions-DVoF6dMt.js} +77 -15
- package/dist/{permissions-B4apnahQ.d.ts → permissions-DttnqqxC.d.ts} +34 -5
- package/dist/{pins-CqE1jZuF.js → pins-8ILTftC9.js} +2 -2
- package/dist/{policy-CInmNGkM.d.ts → policy-CFZAr_Al.d.ts} +330 -37
- package/dist/{policy-Cq2fB8Q5.d.ts → policy-D8UFyERw.d.ts} +2 -2
- package/dist/{policy-ANp36PnI.js → policy-xyWg7QdR.js} +173 -22
- package/dist/powersync-A8x_Yyvh.js +2 -0
- package/dist/powersync-C7kLglqX.js +540 -0
- package/dist/powersync-yW1UUFzp.js +62 -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-CkL50EEf.d.ts +7 -0
- package/dist/problem-details-_AMOYz-w.js +144 -0
- package/dist/{protected-BuPAycJT.d.ts → protected-CNsMzrVd.d.ts} +5 -4
- package/dist/protected-Cs6kTaHo.js +100 -0
- package/dist/protected-bZs6rbT1.d.ts +40 -0
- package/dist/{provider-wTAUda2v.d.ts → provider-Cjr62YMV.d.ts} +1 -1
- package/dist/provider-DLzpb2Xz.js +52 -0
- package/dist/provider-store-DNyC1y48.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-sqBGMnNs.d.ts +5 -0
- package/dist/resource-ref-CaS9yVPi.js +20 -0
- package/dist/revocations-Bhr0aMIC.js +27 -0
- package/dist/rls-BMNydpHk.js +2521 -0
- package/dist/{rls-BOFU3chG.js → rls-CEvs64Kj.js} +8 -3
- package/dist/rls-fields-BMasREhR.js +3502 -0
- package/dist/{rls-graph-DxQ39PJj.js → rls-graph-pc9fuXYm.js} +100 -36
- package/dist/{rls-import-CXV6LBlS.js → rls-import-D8F7j35Q.js} +7 -6
- package/dist/{rls-migrate-DrFPbf8p.js → rls-migrate-7wikfrVI.js} +6 -5
- package/dist/rls-ownership-B5Lkq62W.js +843 -0
- package/dist/rls-shared-aOQ4hGTO.js +493 -0
- package/dist/rls-verify-D8B7dOTh.js +745 -0
- package/dist/{row-check-BajTgQZ-.d.ts → row-check-Dt35czLy.d.ts} +12 -15
- package/dist/rpc-DOK54_CS.js +89 -0
- package/dist/{run-CJ6UvS3a.js → run-YXCJDNYk.js} +31 -12
- package/dist/runtime-CB8gQFvS.js +11 -0
- package/dist/{saas-BRrZzJkM.js → saas-Bj_FFkjN.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-Gs6TTVa-.d.ts} +1 -1
- package/dist/skills-Biq5uup3.js +192 -0
- package/dist/{skills-C_lpV4Jx.js → skills-JaymNCdK.js} +10 -4
- package/dist/{snapshot-DE8dE_hz.js → snapshot-Enxd0n-s.js} +138 -214
- package/dist/snapshot-cache-D6jLelln.js +44 -0
- package/dist/snapshot-cache-DwQH8ufx.d.ts +45 -0
- package/dist/solid/index.d.ts +39 -9
- package/dist/solid/index.js +76 -85
- package/dist/sources-B0UvrJ21.js +534 -0
- package/dist/sources-BcJgw0lt.d.ts +192 -0
- package/dist/{sql-Bll1d7y0.js → sql-xdg5f4Fi.js} +6 -1
- package/dist/{src-CTNT-PBW.js → src-CSbUiozF.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-D_pjn91-.d.ts} +13 -4
- package/dist/store-options-yO0V6gRn.js +716 -0
- package/dist/{stores-CkG2HrSg.js → stores-4_sqiOA1.js} +10 -63
- package/dist/{stores-CvOnu_2T.d.ts → stores-DVR5NHLE.d.ts} +20 -9
- package/dist/{stream-Q_xlB6Ko.js → stream-DDpvynSB.js} +16 -2
- package/dist/{stream-BhMfZOwr.d.ts → stream-DlvG2hs-.d.ts} +2 -2
- package/dist/subject-settings-BwBmCdTR.js +128 -0
- package/dist/supabase/index.d.ts +122 -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-CwafwxxF.js} +3 -3
- package/dist/{supabase-DTxGtYdi.js → supabase-DTBaC20O.js} +345 -367
- package/dist/supabase-config-Bs44dare.js +472 -0
- package/dist/{supabase-hook-D_OqGb_V.js → supabase-hook-BuxLQLpU.js} +994 -708
- package/dist/supabase-hook-D5qVd_D8.js +2 -0
- package/dist/supautils-DYZnzRWV.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-9AWO9EGt.d.ts +197 -0
- package/dist/{types-D6Z6-rKJ.d.ts → types-BPxgszlT.d.ts} +260 -22
- package/dist/{types-BvBk1wgn.d.ts → types-BVyImy5I.d.ts} +5 -3
- package/dist/{types-CxquUByI.d.ts → types-Bvc4oWT6.d.ts} +1 -1
- package/dist/types-C3s9xVJG.d.ts +48 -0
- package/dist/{types-BBK-exvJ.d.ts → types-Dnemwd77.d.ts} +7 -1
- package/dist/types-kxznc7cn.d.ts +52 -0
- package/dist/{types-B8w5PFtS.d.ts → types-vKyAMtcX.d.ts} +17 -1
- package/dist/{types-B9dmiiab.d.ts → types-vNSDF7Dq.d.ts} +1 -1
- package/dist/{sources-DOpW5Ila.d.ts → types-yF5LmQVD.d.ts} +86 -127
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +10 -9
- package/dist/{usage-1ozMubLY.js → usage-D07N9QJ3.js} +2 -2
- package/dist/usage-D82l1CwK.js +2 -0
- package/dist/{usage-CUnK6Ou9.js → usage-DG1oeCca.js} +225 -62
- package/dist/validation-error-CnjZ4t92.js +33 -0
- package/dist/validation-error-CtF23SrC.d.ts +26 -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-Etoa_Mvr.js +2 -0
- package/dist/watch-UihESpsJ.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
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { t as compact } from "./compact-y6vfu2_f.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
5
|
-
import { o as
|
|
2
|
+
import { t as canonicalJson } from "./canonical-json-DemiK4Vp.js";
|
|
3
|
+
import { o as parseCapability } from "./capability-4qPdO9gk.js";
|
|
4
|
+
import { t as ApprovalError } from "./errors-DRnGZHzF.js";
|
|
5
|
+
import { a as decodeApprovalCursor, i as approvalPageSize, o as encodeApprovalCursor, t as applyApprovalVerdict } from "./store-DMAWwAJV.js";
|
|
6
6
|
import { t as loadJose } from "./load-jose-BL6LH4bL.js";
|
|
7
7
|
import "./budget-NbEtbJC_.js";
|
|
8
|
-
import { a as quoteSqlTable, i as quoteSqlLiteral, r as quoteSqlIdent } from "./sql-
|
|
9
|
-
import "./sources-
|
|
8
|
+
import { a as quoteSqlTable, i as quoteSqlLiteral, r as quoteSqlIdent } from "./sql-xdg5f4Fi.js";
|
|
9
|
+
import { l as keptKeys, s as roleColumn } from "./sources-B0UvrJ21.js";
|
|
10
|
+
import { i as readMemberships } from "./manifest-iaoBXHKc.js";
|
|
10
11
|
//#region src/supabase/capability.ts
|
|
11
12
|
const DEFAULT_TTL = 300;
|
|
12
13
|
const MAX_TTL = 3600;
|
|
@@ -62,7 +63,7 @@ const KNOWN = /* @__PURE__ */ new Set([
|
|
|
62
63
|
"act",
|
|
63
64
|
"app_metadata"
|
|
64
65
|
]);
|
|
65
|
-
function isRecord$
|
|
66
|
+
function isRecord$2(value) {
|
|
66
67
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
67
68
|
}
|
|
68
69
|
function own(record, key) {
|
|
@@ -83,7 +84,7 @@ function optional(record, key, path, issues, check, expected) {
|
|
|
83
84
|
}
|
|
84
85
|
const isString = (value) => typeof value === "string";
|
|
85
86
|
function checkMembership(item, path, issues) {
|
|
86
|
-
if (!isRecord$
|
|
87
|
+
if (!isRecord$2(item)) {
|
|
87
88
|
issue(issues, path, "Expected a membership object");
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
@@ -98,13 +99,14 @@ function checkMembership(item, path, issues) {
|
|
|
98
99
|
"grantedBy",
|
|
99
100
|
"reason"
|
|
100
101
|
]) optional(item, key, path, issues, isString, "a string");
|
|
101
|
-
optional(item, "within", path, issues, (value) => isRecord$
|
|
102
|
-
optional(item, "on", path, issues, (value) => isRecord$
|
|
102
|
+
optional(item, "within", path, issues, (value) => isRecord$2(value) && Object.values(value).every((id) => typeof id === "string"), "an object of string ids");
|
|
103
|
+
optional(item, "on", path, issues, (value) => isRecord$2(value) && typeof own(value, "resource") === "string" && typeof own(value, "id") === "string", "an object with string resource and id");
|
|
103
104
|
optional(item, "expiresAt", path, issues, (value) => typeof value === "number" && Number.isFinite(value), "a number of seconds since the epoch");
|
|
104
|
-
optional(item, "member", path, issues, (value) => isRecord$
|
|
105
|
+
optional(item, "member", path, issues, (value) => isRecord$2(value) && typeof own(value, "group") === "string" && own(value, "group") !== "", "an object with a non-empty string group");
|
|
105
106
|
optional(item, "managedBy", path, issues, (value) => value === "idp", "'idp'");
|
|
106
107
|
optional(item, "entitlements", path, issues, isStrings, "an array of strings");
|
|
107
|
-
optional(item, "
|
|
108
|
+
optional(item, "keep", path, issues, isStrings, "an array of strings");
|
|
109
|
+
optional(item, "grants", path, issues, isRecord$2, "an object");
|
|
108
110
|
if (!(own(item, "scope") !== void 0 && own(item, "id") !== void 0 || own(item, "tenant") !== void 0 || own(item, "team") !== void 0 || own(item, "on") !== void 0)) issue(issues, path, "Expected scope and id, tenant, team or on");
|
|
109
111
|
}
|
|
110
112
|
function checkPermDockClaims(record, tenantClaim, path, issues) {
|
|
@@ -121,7 +123,7 @@ function checkPermDockClaims(record, tenantClaim, path, issues) {
|
|
|
121
123
|
}
|
|
122
124
|
optional(record, "memberships_truncated", path, issues, (value) => typeof value === "boolean", "a boolean");
|
|
123
125
|
optional(record, tenantClaim, path, issues, isString, "a string");
|
|
124
|
-
optional(record, "attrs", path, issues, isRecord$
|
|
126
|
+
optional(record, "attrs", path, issues, isRecord$2, "an object");
|
|
125
127
|
optional(record, "authz_ver", path, issues, (value) => Number.isInteger(value), "an integer");
|
|
126
128
|
}
|
|
127
129
|
/** The outer level's `kind`: none, `support` with a `session_id`, or `impersonation`. */
|
|
@@ -142,7 +144,7 @@ function checkAct(value, issues) {
|
|
|
142
144
|
let current = value;
|
|
143
145
|
const path = ["act"];
|
|
144
146
|
while (current !== void 0) {
|
|
145
|
-
if (!isRecord$
|
|
147
|
+
if (!isRecord$2(current)) {
|
|
146
148
|
issue(issues, path, "Expected an object");
|
|
147
149
|
return;
|
|
148
150
|
}
|
|
@@ -155,7 +157,7 @@ function checkAct(value, issues) {
|
|
|
155
157
|
}
|
|
156
158
|
function checkClaims(value, tenantClaim) {
|
|
157
159
|
const issues = [];
|
|
158
|
-
if (!isRecord$
|
|
160
|
+
if (!isRecord$2(value)) {
|
|
159
161
|
issue(issues, [], "Expected a claims object");
|
|
160
162
|
return issues;
|
|
161
163
|
}
|
|
@@ -166,7 +168,7 @@ function checkClaims(value, tenantClaim) {
|
|
|
166
168
|
if (act !== void 0) checkAct(act, issues);
|
|
167
169
|
const meta = own(value, "app_metadata");
|
|
168
170
|
if (meta !== void 0) {
|
|
169
|
-
if (isRecord$
|
|
171
|
+
if (isRecord$2(meta)) checkPermDockClaims(meta, tenantClaim, ["app_metadata"], issues);
|
|
170
172
|
else issue(issues, ["app_metadata"], "Expected an object");
|
|
171
173
|
}
|
|
172
174
|
return issues;
|
|
@@ -176,8 +178,8 @@ function merge(base, app) {
|
|
|
176
178
|
if (issues.length > 0) return { issues };
|
|
177
179
|
const baseValue = base.value;
|
|
178
180
|
const appValue = app.value;
|
|
179
|
-
if (!isRecord$
|
|
180
|
-
return { value: isRecord$
|
|
181
|
+
if (!isRecord$2(appValue)) return { issues: [{ message: "Expected the extension to output an object" }] };
|
|
182
|
+
return { value: isRecord$2(baseValue) ? {
|
|
181
183
|
...baseValue,
|
|
182
184
|
...appValue
|
|
183
185
|
} : appValue };
|
|
@@ -190,9 +192,9 @@ function schemaOf(validate) {
|
|
|
190
192
|
validate
|
|
191
193
|
}),
|
|
192
194
|
extend(app) {
|
|
193
|
-
if (!isRecord$
|
|
195
|
+
if (!isRecord$2(app) && typeof app !== "function") throw new TypeError("PermDock: supabaseClaims().extend needs a Standard Schema");
|
|
194
196
|
const props = Reflect.get(app, "~standard");
|
|
195
|
-
if (!isRecord$
|
|
197
|
+
if (!isRecord$2(props) || typeof props["validate"] !== "function") throw new TypeError("PermDock: supabaseClaims().extend needs a Standard Schema");
|
|
196
198
|
const run = app["~standard"].validate.bind(app["~standard"]);
|
|
197
199
|
return schemaOf((value) => {
|
|
198
200
|
const base = validate(value);
|
|
@@ -223,6 +225,287 @@ function supabaseClaims(options = {}) {
|
|
|
223
225
|
});
|
|
224
226
|
}
|
|
225
227
|
//#endregion
|
|
228
|
+
//#region src/supabase/postgrest.ts
|
|
229
|
+
function callRpc(client, schema, fn, args) {
|
|
230
|
+
return client.schema(schema).rpc(fn, args);
|
|
231
|
+
}
|
|
232
|
+
function isRecord$1(value) {
|
|
233
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
234
|
+
}
|
|
235
|
+
function strings(value) {
|
|
236
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
237
|
+
}
|
|
238
|
+
function grantOf(value) {
|
|
239
|
+
if (!isRecord$1(value) || typeof value["permission"] !== "string") return;
|
|
240
|
+
return compact({
|
|
241
|
+
permission: value["permission"],
|
|
242
|
+
effect: value["effect"] === "deny" ? "deny" : "allow",
|
|
243
|
+
level: typeof value["level"] === "string" ? value["level"] : void 0
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function customRoleOf(value) {
|
|
247
|
+
if (!isRecord$1(value) || typeof value["name"] !== "string") return;
|
|
248
|
+
const grants = Array.isArray(value["grants"]) ? value["grants"].flatMap((item) => {
|
|
249
|
+
const grant = grantOf(item);
|
|
250
|
+
return grant === void 0 ? [] : [grant];
|
|
251
|
+
}) : [];
|
|
252
|
+
const includes = strings(value["includes"]);
|
|
253
|
+
const body = {
|
|
254
|
+
name: value["name"],
|
|
255
|
+
grants,
|
|
256
|
+
...includes.length === 0 ? {} : { includes }
|
|
257
|
+
};
|
|
258
|
+
if (value["scope"] === "global") return value["tenant"] === void 0 ? {
|
|
259
|
+
...body,
|
|
260
|
+
scope: "global"
|
|
261
|
+
} : void 0;
|
|
262
|
+
if (typeof value["tenant"] !== "string") return;
|
|
263
|
+
return compact({
|
|
264
|
+
...body,
|
|
265
|
+
tenant: value["tenant"],
|
|
266
|
+
scope: typeof value["scope"] === "string" ? value["scope"] : void 0,
|
|
267
|
+
id: typeof value["id"] === "string" ? value["id"] : void 0
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function readMemberEntries(value) {
|
|
271
|
+
if (!Array.isArray(value)) return [];
|
|
272
|
+
return value.flatMap((item) => {
|
|
273
|
+
if (!isRecord$1(item) || !isRecord$1(item["principal"])) return [];
|
|
274
|
+
const id = item["principal"]["id"];
|
|
275
|
+
const [membership] = readMemberships([item["membership"]]).memberships;
|
|
276
|
+
return typeof id === "string" && id !== "" && membership !== void 0 ? [{
|
|
277
|
+
principal: { id },
|
|
278
|
+
membership
|
|
279
|
+
}] : [];
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
function versionOf(value) {
|
|
283
|
+
const version = typeof value === "string" ? Number(value) : value;
|
|
284
|
+
return typeof version === "number" && Number.isFinite(version) ? version : void 0;
|
|
285
|
+
}
|
|
286
|
+
/** Reads `subject_for`'s JSON; anything it cannot read is left out, and a malformed record is no record. */
|
|
287
|
+
function readSubjectRecord(value) {
|
|
288
|
+
if (!isRecord$1(value) || typeof value["id"] !== "string") return;
|
|
289
|
+
if (value["active"] !== true) return {
|
|
290
|
+
id: value["id"],
|
|
291
|
+
active: false,
|
|
292
|
+
roles: [],
|
|
293
|
+
memberships: [],
|
|
294
|
+
customRoles: []
|
|
295
|
+
};
|
|
296
|
+
const version = value["authzVersion"];
|
|
297
|
+
return compact({
|
|
298
|
+
id: value["id"],
|
|
299
|
+
active: true,
|
|
300
|
+
roles: strings(value["roles"]),
|
|
301
|
+
memberships: readMemberships(value["memberships"]).memberships,
|
|
302
|
+
customRoles: Array.isArray(value["customRoles"]) ? value["customRoles"].flatMap((item) => {
|
|
303
|
+
const role = customRoleOf(item);
|
|
304
|
+
return role === void 0 ? [] : [role];
|
|
305
|
+
}) : [],
|
|
306
|
+
authzVersion: typeof version === "number" && Number.isFinite(version) ? version : void 0
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Membership, custom-role and subject sources over the `subject_for(p_user)`,
|
|
311
|
+
* `authz_version_for(p_user)` and `members_of(p_scope, p_id)` functions
|
|
312
|
+
* `permdock supabase hook generate` writes, for a backend that reaches
|
|
313
|
+
* Postgres only through PostgREST. `memberships.version` reads only the
|
|
314
|
+
* version, so `claimsFirst` with `onStale: 'reread'` reads the full subject
|
|
315
|
+
* only for a stale token; when the version function is missing it falls back
|
|
316
|
+
* to `subject_for`. With `policy`, a token that claims a custom role has its
|
|
317
|
+
* subject read in one `subject_for` call instead, and custom roles are read
|
|
318
|
+
* only for a subject that holds one. Each user and each instance is read once per
|
|
319
|
+
* `postgrestSources` call, so create it per request or per job.
|
|
320
|
+
*/
|
|
321
|
+
function postgrestSources(client, options = {}) {
|
|
322
|
+
const schema = options.schema ?? "permdock";
|
|
323
|
+
const fn = options.fn ?? "subject_for";
|
|
324
|
+
const membersFn = options.membersFn ?? "members_of";
|
|
325
|
+
const versionFn = options.versionFn ?? "authz_version_for";
|
|
326
|
+
const declared = options.policy === void 0 ? void 0 : options.policy.index.declaredRoles;
|
|
327
|
+
const onlyDeclared = (held) => declared !== void 0 && held?.every((name) => declared.has(name)) === true;
|
|
328
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
329
|
+
const versions = /* @__PURE__ */ new Map();
|
|
330
|
+
const listed = /* @__PURE__ */ new Map();
|
|
331
|
+
const list = (query) => {
|
|
332
|
+
const key = JSON.stringify([query.scope, query.id]);
|
|
333
|
+
const cached = listed.get(key);
|
|
334
|
+
if (cached !== void 0) return cached.then((entries) => [...entries]);
|
|
335
|
+
const pending = Promise.resolve(callRpc(client, schema, membersFn, {
|
|
336
|
+
p_scope: query.scope,
|
|
337
|
+
p_id: query.id
|
|
338
|
+
})).then((result) => {
|
|
339
|
+
if (result.error !== null) throw new Error(`PermDock: ${schema}.${membersFn} failed: ${result.error.message}`);
|
|
340
|
+
return readMemberEntries(result.data);
|
|
341
|
+
});
|
|
342
|
+
listed.set(key, pending);
|
|
343
|
+
return pending.then((entries) => [...entries]);
|
|
344
|
+
};
|
|
345
|
+
const record = (userId) => {
|
|
346
|
+
const cached = loaded.get(userId);
|
|
347
|
+
if (cached !== void 0) return cached;
|
|
348
|
+
const pending = Promise.resolve(callRpc(client, schema, fn, { p_user: userId })).then((result) => {
|
|
349
|
+
if (result.error !== null) throw new Error(`PermDock: ${schema}.${fn} failed: ${result.error.message}`);
|
|
350
|
+
return readSubjectRecord(result.data);
|
|
351
|
+
});
|
|
352
|
+
loaded.set(userId, pending);
|
|
353
|
+
return pending;
|
|
354
|
+
};
|
|
355
|
+
const version = (userId) => {
|
|
356
|
+
const read = loaded.get(userId);
|
|
357
|
+
if (read !== void 0) return read.then((found) => found?.authzVersion);
|
|
358
|
+
const cached = versions.get(userId);
|
|
359
|
+
if (cached !== void 0) return cached;
|
|
360
|
+
const pending = Promise.resolve(callRpc(client, schema, versionFn, { p_user: userId })).then((result) => result.error === null ? versionOf(result.data) : record(userId).then((found) => found?.authzVersion));
|
|
361
|
+
versions.set(userId, pending);
|
|
362
|
+
return pending;
|
|
363
|
+
};
|
|
364
|
+
const live = async (userId) => {
|
|
365
|
+
const found = await record(userId);
|
|
366
|
+
return found?.active === true ? found : void 0;
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
memberships: {
|
|
370
|
+
async membershipsFor(principal) {
|
|
371
|
+
return [...(await live(principal.id))?.memberships ?? []];
|
|
372
|
+
},
|
|
373
|
+
version(principal) {
|
|
374
|
+
return declared !== void 0 && !onlyDeclared([...principal.roles ?? [], ...(principal.memberships ?? []).flatMap((membership) => membership.roles)]) ? record(principal.id).then((found) => found?.authzVersion) : version(principal.id);
|
|
375
|
+
},
|
|
376
|
+
list
|
|
377
|
+
},
|
|
378
|
+
customRoles(principal) {
|
|
379
|
+
return {
|
|
380
|
+
async rolesFor(tenant, context) {
|
|
381
|
+
if (onlyDeclared(context?.held)) return [];
|
|
382
|
+
return ((await live(principal.id))?.customRoles ?? []).filter((role) => role.tenant === tenant);
|
|
383
|
+
},
|
|
384
|
+
async globalRoles(context) {
|
|
385
|
+
if (onlyDeclared(context?.held)) return [];
|
|
386
|
+
return ((await live(principal.id))?.customRoles ?? []).filter((role) => role.scope === "global");
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
},
|
|
390
|
+
async subject(userId) {
|
|
391
|
+
const found = await live(userId);
|
|
392
|
+
if (found === void 0) return {
|
|
393
|
+
principal: null,
|
|
394
|
+
context: {}
|
|
395
|
+
};
|
|
396
|
+
return {
|
|
397
|
+
principal: compact({
|
|
398
|
+
id: found.id,
|
|
399
|
+
roles: found.roles,
|
|
400
|
+
memberships: found.memberships,
|
|
401
|
+
authzVersion: found.authzVersion
|
|
402
|
+
}),
|
|
403
|
+
context: {}
|
|
404
|
+
};
|
|
405
|
+
},
|
|
406
|
+
record
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/supabase/approval-store.ts
|
|
411
|
+
function isRecord(value) {
|
|
412
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
413
|
+
}
|
|
414
|
+
/** A stored body as the request it is; the store wrote it from an `ApprovalRequest`. */
|
|
415
|
+
function requestOf(value) {
|
|
416
|
+
if (!isRecord(value) || value["v"] !== 1 || typeof value["token"] !== "string") return null;
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
function filterOf(filter) {
|
|
420
|
+
return compact({
|
|
421
|
+
status: filter.status,
|
|
422
|
+
tenant: filter.tenant,
|
|
423
|
+
principalId: filter.principalId,
|
|
424
|
+
actorId: filter.actorId,
|
|
425
|
+
session: filter.session
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* An `ApprovalStore` over the table and functions `rls.approvals` generates,
|
|
430
|
+
* called through supabase-js (`client.schema(schema).rpc(...)`) with a
|
|
431
|
+
* client that may execute them, such as a `service_role` client. Each method
|
|
432
|
+
* is one atomic statement in the database: a repeated ask keeps the open
|
|
433
|
+
* request, two racing approvers both count towards a quorum, and two racing
|
|
434
|
+
* resumes never both consume an approval.
|
|
435
|
+
*/
|
|
436
|
+
function supabaseApprovalStore(client, options = {}) {
|
|
437
|
+
const schema = options.schema ?? "permdock";
|
|
438
|
+
const call = async (fn, args) => {
|
|
439
|
+
const result = await callRpc(client, schema, fn, args);
|
|
440
|
+
if (result.error !== null) throw new Error(`PermDock: ${schema}.${fn} failed: ${result.error.message}`);
|
|
441
|
+
return result.data;
|
|
442
|
+
};
|
|
443
|
+
const get = async (token) => requestOf(await call("permdock_approval_get", { p_token: token }));
|
|
444
|
+
return {
|
|
445
|
+
...options.ttl === void 0 ? {} : { ttl: options.ttl },
|
|
446
|
+
async create(request) {
|
|
447
|
+
await call("permdock_approval_open", { p_request: request });
|
|
448
|
+
if (options.onOpen !== void 0) {
|
|
449
|
+
const stored = await get(request.token);
|
|
450
|
+
if (stored !== null && canonicalJson(stored) === canonicalJson(request)) await options.onOpen(stored);
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
get,
|
|
454
|
+
async resolve(token, verdict) {
|
|
455
|
+
const current = await get(token);
|
|
456
|
+
if (current === null) throw new ApprovalError("approval-not-found", "approval was not found");
|
|
457
|
+
const next = applyApprovalVerdict(current, verdict);
|
|
458
|
+
const written = requestOf(await call("permdock_approval_resolve", {
|
|
459
|
+
p_token: token,
|
|
460
|
+
p_next: next,
|
|
461
|
+
p_seen: current.approvals?.length ?? 0
|
|
462
|
+
}));
|
|
463
|
+
if (written === null) throw new ApprovalError("approval-not-pending", "approval is not pending");
|
|
464
|
+
return written;
|
|
465
|
+
},
|
|
466
|
+
async consume(token, now = /* @__PURE__ */ new Date()) {
|
|
467
|
+
return requestOf(await call("permdock_approval_consume", {
|
|
468
|
+
p_token: token,
|
|
469
|
+
p_now: now.toISOString()
|
|
470
|
+
}));
|
|
471
|
+
},
|
|
472
|
+
async list(query) {
|
|
473
|
+
const after = query.cursor === void 0 ? void 0 : decodeApprovalCursor(query.cursor);
|
|
474
|
+
if (after === null) return { items: [] };
|
|
475
|
+
const size = approvalPageSize(query.limit);
|
|
476
|
+
const data = await call("permdock_approval_list", {
|
|
477
|
+
p_filter: filterOf(query),
|
|
478
|
+
p_after_created: after?.[0] ?? null,
|
|
479
|
+
p_after_token: after?.[1] ?? null,
|
|
480
|
+
p_limit: size + 1
|
|
481
|
+
});
|
|
482
|
+
const rows = Array.isArray(data) ? data.flatMap((row) => {
|
|
483
|
+
const request = requestOf(row);
|
|
484
|
+
return request === null ? [] : [request];
|
|
485
|
+
}) : [];
|
|
486
|
+
const items = rows.slice(0, size);
|
|
487
|
+
const last = items.at(-1);
|
|
488
|
+
return rows.length > size && last !== void 0 ? {
|
|
489
|
+
items,
|
|
490
|
+
next: encodeApprovalCursor(last)
|
|
491
|
+
} : { items };
|
|
492
|
+
},
|
|
493
|
+
async expire(now = /* @__PURE__ */ new Date()) {
|
|
494
|
+
const count = await call("permdock_approval_expire", { p_now: now.toISOString() });
|
|
495
|
+
return typeof count === "number" ? count : 0;
|
|
496
|
+
},
|
|
497
|
+
async cancel(filter, meta) {
|
|
498
|
+
const count = await call("permdock_approval_cancel", {
|
|
499
|
+
p_filter: filterOf(filter),
|
|
500
|
+
p_by: meta.by,
|
|
501
|
+
p_note: meta.note ?? null,
|
|
502
|
+
p_now: (/* @__PURE__ */ new Date()).toISOString()
|
|
503
|
+
});
|
|
504
|
+
return typeof count === "number" ? count : 0;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
226
509
|
//#region src/supabase/rls.ts
|
|
227
510
|
function isTable(value) {
|
|
228
511
|
return value !== null && typeof value === "object" && "table" in value && typeof value.table === "string";
|
|
@@ -236,7 +519,9 @@ function supabaseRls(options = {}) {
|
|
|
236
519
|
tenantClaim: options.tenantClaim ?? "tenant_id",
|
|
237
520
|
tenantType: options.tenantType,
|
|
238
521
|
memberships,
|
|
239
|
-
suspension: options.suspension
|
|
522
|
+
suspension: options.suspension,
|
|
523
|
+
realtime: options.realtime,
|
|
524
|
+
storage: options.storage
|
|
240
525
|
});
|
|
241
526
|
}
|
|
242
527
|
const ident = (name) => quoteSqlIdent(name);
|
|
@@ -245,6 +530,13 @@ const literal = quoteSqlLiteral;
|
|
|
245
530
|
function qualifiedTable(name) {
|
|
246
531
|
return table(name.includes(".") ? name : `public.${name}`);
|
|
247
532
|
}
|
|
533
|
+
/** The memberships table's role key over alias `m`, joined to its roles table when it holds a reference. */
|
|
534
|
+
function tenantRole(memberships) {
|
|
535
|
+
return roleColumn(memberships.role, memberships.table.includes(".") ? memberships.table : `public.${memberships.table}`, "m", {
|
|
536
|
+
label: "authorizeSql tenant.role",
|
|
537
|
+
indent: " "
|
|
538
|
+
});
|
|
539
|
+
}
|
|
248
540
|
function textArray(values) {
|
|
249
541
|
return values.length === 0 ? `'{}'::text[]` : `array[${values.map(literal).join(", ")}]::text[]`;
|
|
250
542
|
}
|
|
@@ -258,7 +550,10 @@ function activeRow(row, id, text = false) {
|
|
|
258
550
|
if (row.disabledAt === void 0 && row.status === void 0) throw new TypeError("PermDock: a suspension table needs disabledAt or status");
|
|
259
551
|
return `exists (select 1 from ${qualifiedTable(row.table)} s where ${parts.join(" and ")})`;
|
|
260
552
|
}
|
|
261
|
-
/**
|
|
553
|
+
/**
|
|
554
|
+
* Early `return false` for a suspended user, or a tenant request for a
|
|
555
|
+
* suspended instance unless its scope keeps the requested permission.
|
|
556
|
+
*/
|
|
262
557
|
function suspendedGuards(suspension, scope, uid) {
|
|
263
558
|
const lines = [];
|
|
264
559
|
const users = suspension?.users;
|
|
@@ -266,9 +561,13 @@ function suspendedGuards(suspension, scope, uid) {
|
|
|
266
561
|
return false; -- suspended user
|
|
267
562
|
end if;`);
|
|
268
563
|
const tenant = suspension?.scopes?.[scope];
|
|
269
|
-
if (tenant !== void 0)
|
|
564
|
+
if (tenant !== void 0) {
|
|
565
|
+
const keep = keptKeys(tenant);
|
|
566
|
+
const kept = keep.length === 0 ? "" : ` and not (requested_permission::text = any(${textArray(keep)}))`;
|
|
567
|
+
lines.push(` if requested_tenant is not null and not ${activeRow(tenant, "requested_tenant", true)}${kept} then
|
|
270
568
|
return false; -- suspended ${scope}
|
|
271
569
|
end if;`);
|
|
570
|
+
}
|
|
272
571
|
return lines.map((line) => `\n${line}`).join("");
|
|
273
572
|
}
|
|
274
573
|
function claimEntries(where, value) {
|
|
@@ -289,16 +588,26 @@ function customHolds(q, scope, allows, denies, includes) {
|
|
|
289
588
|
))
|
|
290
589
|
)`;
|
|
291
590
|
}
|
|
292
|
-
function
|
|
293
|
-
|
|
591
|
+
function disabledLine(memberships, suspension) {
|
|
592
|
+
if (memberships.disabledAt === void 0) return "";
|
|
593
|
+
const column = `m.${ident(memberships.disabledAt)}`;
|
|
594
|
+
const keep = keptKeys(suspension?.memberships);
|
|
595
|
+
return keep.length === 0 ? `\n and ${column} is null` : `\n and (${column} is null or requested_permission::text = any(${textArray(keep)}))`;
|
|
596
|
+
}
|
|
597
|
+
function claimKept(suspension) {
|
|
598
|
+
return keptKeys(suspension?.memberships).length === 0 ? "" : `\n and case coalesce(jsonb_typeof(m -> 'keep'), 'null') when 'null' then true when 'array' then m -> 'keep' @> jsonb_build_array(requested_permission::text) else false end`;
|
|
599
|
+
}
|
|
600
|
+
function customDatabaseBranch(q, scope, memberships, declared, levels, suspension) {
|
|
601
|
+
const role = tenantRole(memberships);
|
|
602
|
+
const match = `c.tenant_id::text = requested_tenant and c.scope = ${literal(scope)} and c.scope_id is null and c.role = ${role.sql}`;
|
|
294
603
|
const rows = (source, value, extra) => `array(select c.${value} from ${q(source)} c where ${match}${extra})`;
|
|
295
604
|
return ` or exists (
|
|
296
605
|
select 1
|
|
297
|
-
from ${qualifiedTable(memberships.table)} m
|
|
606
|
+
from ${qualifiedTable(memberships.table)} m${role.join}
|
|
298
607
|
where m.${ident(memberships.user)} = v_member_user
|
|
299
608
|
and m.${ident(memberships.tenant)} = v_member_tenant
|
|
300
|
-
and not (
|
|
301
|
-
and ${customHolds(q, scope, rows("custom_role_permissions", "permission", " and c.effect = 'allow'"), rows("custom_role_permissions", "permission", " and c.effect = 'deny'"), rows("custom_role_includes", "include_role", ""))}
|
|
609
|
+
and not (${role.sql} = any(${textArray(declared)}))${memberships.expiresAt === void 0 ? "" : `\n and (m.${ident(memberships.expiresAt)} is null or m.${ident(memberships.expiresAt)} > now())`}${disabledLine(memberships, suspension)}
|
|
610
|
+
and ${customHolds(q, scope, rows("custom_role_permissions", levels ? "permission || coalesce('@' || c.level, '')" : "permission", " and c.effect = 'allow'"), rows("custom_role_permissions", "permission", " and c.effect = 'deny'"), rows("custom_role_includes", "include_role", ""))}
|
|
302
611
|
)`;
|
|
303
612
|
}
|
|
304
613
|
/**
|
|
@@ -324,13 +633,14 @@ function databaseBody(q, scope, memberships, custom, suspension) {
|
|
|
324
633
|
return false; -- no memberships table configured
|
|
325
634
|
end if;`;
|
|
326
635
|
if (memberships !== void 0 && tenantColumn !== void 0) {
|
|
636
|
+
const role = tenantRole(memberships);
|
|
327
637
|
const held = (effect) => `exists (
|
|
328
638
|
select 1
|
|
329
|
-
from ${qualifiedTable(memberships.table)} m
|
|
330
|
-
join ${q("role_permissions")} rp on rp.role =
|
|
639
|
+
from ${qualifiedTable(memberships.table)} m${role.join}
|
|
640
|
+
join ${q("role_permissions")} rp on rp.role = ${role.sql}
|
|
331
641
|
where m.${ident(memberships.user)} = v_member_user
|
|
332
642
|
and m.${ident(tenantColumn)} = v_member_tenant
|
|
333
|
-
and ${effectMatch(literal(scope), effect)}${memberships.expiresAt === void 0 ? "" : `\n and (m.${ident(memberships.expiresAt)} is null or m.${ident(memberships.expiresAt)} > now())`}
|
|
643
|
+
and ${effectMatch(literal(scope), effect)}${memberships.expiresAt === void 0 ? "" : `\n and (m.${ident(memberships.expiresAt)} is null or m.${ident(memberships.expiresAt)} > now())`}${disabledLine(memberships, suspension)}
|
|
334
644
|
)`;
|
|
335
645
|
tenantBranch = ` if requested_tenant is not null then
|
|
336
646
|
begin
|
|
@@ -342,7 +652,7 @@ function databaseBody(q, scope, memberships, custom, suspension) {
|
|
|
342
652
|
return (${held("allow")}${custom === void 0 ? "" : customDatabaseBranch(q, scope, {
|
|
343
653
|
...memberships,
|
|
344
654
|
tenant: tenantColumn
|
|
345
|
-
}, custom.declared)})
|
|
655
|
+
}, custom.declared, custom.levels === true, suspension)})
|
|
346
656
|
and not ${held("deny")}
|
|
347
657
|
and not ${global("deny")};
|
|
348
658
|
end if;`;
|
|
@@ -381,7 +691,7 @@ function jwtBody(q, scope, custom, suspension) {
|
|
|
381
691
|
cross join lateral (select m -> 'grants' -> r.role as g) cg
|
|
382
692
|
where m ->> 'scope' = ${literal(scope)}
|
|
383
693
|
and m ->> 'id' = requested_tenant
|
|
384
|
-
and ${CLAIM_UNEXPIRED}
|
|
694
|
+
and ${CLAIM_UNEXPIRED}${claimKept(suspension)}
|
|
385
695
|
and jsonb_typeof(cg.g) = 'array'
|
|
386
696
|
and not (r.role = any(${textArray(custom.declared)}))
|
|
387
697
|
and ${customHolds(q, scope, claimEntries("left(e, 1) not in ('-', '@')", "e"), claimEntries("left(e, 1) = '-'", "substr(e, 2)"), claimEntries("left(e, 1) = '@'", "substr(e, 2)"))}
|
|
@@ -392,7 +702,7 @@ function jwtBody(q, scope, custom, suspension) {
|
|
|
392
702
|
join ${q("role_permissions")} rp on rp.role = r.role
|
|
393
703
|
where m ->> 'scope' = ${literal(scope)}
|
|
394
704
|
and m ->> 'id' = requested_tenant
|
|
395
|
-
and ${CLAIM_UNEXPIRED}
|
|
705
|
+
and ${CLAIM_UNEXPIRED}${claimKept(suspension)}
|
|
396
706
|
and ${effectMatch(literal(scope), effect)}
|
|
397
707
|
)`;
|
|
398
708
|
const global = (effect) => `exists (
|
|
@@ -461,336 +771,4 @@ grant execute on function ${signature} to authenticated;
|
|
|
461
771
|
`;
|
|
462
772
|
}
|
|
463
773
|
//#endregion
|
|
464
|
-
|
|
465
|
-
const REGISTERED = /* @__PURE__ */ new Set([
|
|
466
|
-
"sub",
|
|
467
|
-
"role",
|
|
468
|
-
"iss",
|
|
469
|
-
"aud",
|
|
470
|
-
"exp",
|
|
471
|
-
"iat",
|
|
472
|
-
"nbf",
|
|
473
|
-
"aal",
|
|
474
|
-
"amr",
|
|
475
|
-
"acr",
|
|
476
|
-
"session_id",
|
|
477
|
-
"email",
|
|
478
|
-
"phone",
|
|
479
|
-
"is_anonymous",
|
|
480
|
-
"app_metadata",
|
|
481
|
-
"user_metadata",
|
|
482
|
-
"act",
|
|
483
|
-
"client_id",
|
|
484
|
-
"scope"
|
|
485
|
-
]);
|
|
486
|
-
function isRecord(value) {
|
|
487
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
488
|
-
}
|
|
489
|
-
/** Top-level first, then `app_metadata`. `null` counts as absent: the RBAC hook writes `null` for a user with no role row. */
|
|
490
|
-
function readClaim(claims, name) {
|
|
491
|
-
const top = Object.hasOwn(claims, name) ? claims[name] : void 0;
|
|
492
|
-
if (top !== void 0 && top !== null) return top;
|
|
493
|
-
const meta = claims["app_metadata"];
|
|
494
|
-
if (isRecord(meta) && Object.hasOwn(meta, name)) return meta[name] ?? void 0;
|
|
495
|
-
}
|
|
496
|
-
function asRoles(value, declared) {
|
|
497
|
-
const raw = typeof value === "string" ? value === "" ? [] : [value] : Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
498
|
-
if (declared === void 0) return raw;
|
|
499
|
-
const allowed = new Set(declared);
|
|
500
|
-
return raw.filter((role) => allowed.has(role));
|
|
501
|
-
}
|
|
502
|
-
/** Reads the `memberships` claim; an entry it cannot read is dropped and its index reported. */
|
|
503
|
-
function readMemberships(value) {
|
|
504
|
-
if (!Array.isArray(value)) return {
|
|
505
|
-
memberships: [],
|
|
506
|
-
dropped: []
|
|
507
|
-
};
|
|
508
|
-
const out = [];
|
|
509
|
-
const dropped = [];
|
|
510
|
-
for (const [index, item] of value.entries()) {
|
|
511
|
-
const membership = asMembership(item);
|
|
512
|
-
if (membership === void 0) dropped.push(index);
|
|
513
|
-
else out.push(membership);
|
|
514
|
-
}
|
|
515
|
-
return {
|
|
516
|
-
memberships: out,
|
|
517
|
-
dropped
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
function asMembership(item) {
|
|
521
|
-
if (!isRecord(item) || !Array.isArray(item["roles"])) return;
|
|
522
|
-
const roles = item["roles"].filter((role) => typeof role === "string");
|
|
523
|
-
if (roles.length === 0) return;
|
|
524
|
-
const scope = typeof item["scope"] === "string" ? item["scope"] : void 0;
|
|
525
|
-
const id = typeof item["id"] === "string" ? item["id"] : void 0;
|
|
526
|
-
const within = isRecord(item["within"]) ? Object.fromEntries(Object.entries(item["within"]).filter((entry) => typeof entry[1] === "string")) : void 0;
|
|
527
|
-
const tenant = typeof item["tenant"] === "string" ? item["tenant"] : void 0;
|
|
528
|
-
const team = typeof item["team"] === "string" ? item["team"] : void 0;
|
|
529
|
-
const onRecord = isRecord(item["on"]) ? item["on"] : void 0;
|
|
530
|
-
const on = onRecord !== void 0 && typeof onRecord["resource"] === "string" && typeof onRecord["id"] === "string" ? {
|
|
531
|
-
resource: onRecord["resource"],
|
|
532
|
-
id: onRecord["id"]
|
|
533
|
-
} : void 0;
|
|
534
|
-
if ((scope === void 0 || id === void 0) && tenant === void 0 && team === void 0 && on === void 0) return;
|
|
535
|
-
return compact({
|
|
536
|
-
roles,
|
|
537
|
-
scope,
|
|
538
|
-
id,
|
|
539
|
-
within,
|
|
540
|
-
tenant,
|
|
541
|
-
team,
|
|
542
|
-
on,
|
|
543
|
-
via: typeof item["via"] === "string" ? item["via"] : void 0,
|
|
544
|
-
expiresAt: typeof item["expiresAt"] === "number" ? item["expiresAt"] : void 0,
|
|
545
|
-
grantedBy: typeof item["grantedBy"] === "string" ? item["grantedBy"] : void 0,
|
|
546
|
-
reason: typeof item["reason"] === "string" ? item["reason"] : void 0,
|
|
547
|
-
member: memberOf(item["member"]),
|
|
548
|
-
managedBy: item["managedBy"] === "idp" ? "idp" : void 0,
|
|
549
|
-
entitlements: Array.isArray(item["entitlements"]) ? item["entitlements"].filter((seat) => typeof seat === "string") : void 0
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
function memberOf(value) {
|
|
553
|
-
if (value === null || typeof value !== "object" || !Object.hasOwn(value, "group")) return;
|
|
554
|
-
const group = value.group;
|
|
555
|
-
return typeof group === "string" && group !== "" ? { group } : void 0;
|
|
556
|
-
}
|
|
557
|
-
function asStrings(value) {
|
|
558
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
559
|
-
}
|
|
560
|
-
/** `claims[plans][tenant]`: only the active tenant's entry, never another tenant's. */
|
|
561
|
-
function plansFor(claims, name, tenant) {
|
|
562
|
-
if (name === void 0 || tenant === void 0) return [];
|
|
563
|
-
const byTenant = readClaim(claims, name);
|
|
564
|
-
if (!isRecord(byTenant) || !Object.hasOwn(byTenant, tenant)) return [];
|
|
565
|
-
return asStrings(byTenant[tenant]);
|
|
566
|
-
}
|
|
567
|
-
const INVALID_CHAIN = Object.freeze({
|
|
568
|
-
ok: false,
|
|
569
|
-
reason: "invalid-chain"
|
|
570
|
-
});
|
|
571
|
-
const NO_ACTOR = Object.freeze({ ok: true });
|
|
572
|
-
/** Own entries of one `act` level, with the nested `act` copied the same way. */
|
|
573
|
-
function copyAct(level) {
|
|
574
|
-
const entries = Object.entries(level).map(([key, value]) => [key, key === "act" && isRecord(value) ? copyAct(value) : value]);
|
|
575
|
-
return Object.fromEntries(entries);
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* The outer `act` level by its `kind`, the marker better-supabase writes: none is an OAuth client
|
|
579
|
-
* or agent chain, `support` a support session (with `session_id`), `impersonation` an admin acting
|
|
580
|
-
* as the user. Any other `kind`, or a support level without `session_id`, is invalid.
|
|
581
|
-
*/
|
|
582
|
-
function actingParty(outer, id) {
|
|
583
|
-
const marked = Object.hasOwn(outer, "kind") ? outer["kind"] : void 0;
|
|
584
|
-
const kind = marked === void 0 && Object.hasOwn(outer, "session_id") ? "support" : marked;
|
|
585
|
-
const chain = copyAct(outer);
|
|
586
|
-
const reason = typeof outer["reason"] === "string" ? outer["reason"] : void 0;
|
|
587
|
-
switch (kind) {
|
|
588
|
-
case void 0: return freezeDeep({
|
|
589
|
-
ok: true,
|
|
590
|
-
actor: {
|
|
591
|
-
id,
|
|
592
|
-
kind: "oauth-client",
|
|
593
|
-
chain
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
case "support": {
|
|
597
|
-
const session = outer["session_id"];
|
|
598
|
-
const readOnly = Object.hasOwn(outer, "read_only") ? outer["read_only"] : false;
|
|
599
|
-
if (typeof session !== "string" || session === "" || typeof readOnly !== "boolean") return INVALID_CHAIN;
|
|
600
|
-
return freezeDeep({
|
|
601
|
-
ok: true,
|
|
602
|
-
actor: compact({
|
|
603
|
-
id,
|
|
604
|
-
kind: "support",
|
|
605
|
-
sessionId: session,
|
|
606
|
-
readOnly,
|
|
607
|
-
reason,
|
|
608
|
-
chain
|
|
609
|
-
})
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
case "impersonation": return freezeDeep({
|
|
613
|
-
ok: true,
|
|
614
|
-
actor: compact({
|
|
615
|
-
id,
|
|
616
|
-
kind: "impersonation",
|
|
617
|
-
reason,
|
|
618
|
-
chain
|
|
619
|
-
})
|
|
620
|
-
});
|
|
621
|
-
default: return INVALID_CHAIN;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
function readActor(claims) {
|
|
625
|
-
if (Object.hasOwn(claims, "act") && claims["act"] !== void 0) {
|
|
626
|
-
let current = claims["act"];
|
|
627
|
-
while (current !== void 0) {
|
|
628
|
-
const level = isRecord(current) ? current["sub"] : void 0;
|
|
629
|
-
if (!isRecord(current) || typeof level !== "string" || level === "") return INVALID_CHAIN;
|
|
630
|
-
current = Object.hasOwn(current, "act") ? current["act"] : void 0;
|
|
631
|
-
}
|
|
632
|
-
const outer = claims["act"];
|
|
633
|
-
const sub = isRecord(outer) ? outer["sub"] : void 0;
|
|
634
|
-
if (!isRecord(outer) || typeof sub !== "string") return INVALID_CHAIN;
|
|
635
|
-
return actingParty(outer, sub);
|
|
636
|
-
}
|
|
637
|
-
const client = Object.hasOwn(claims, "client_id") ? claims["client_id"] : void 0;
|
|
638
|
-
if (typeof client === "string" && client !== "") return freezeDeep({
|
|
639
|
-
ok: true,
|
|
640
|
-
actor: {
|
|
641
|
-
id: client,
|
|
642
|
-
kind: "oauth-client"
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
return NO_ACTOR;
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Who acts for the user: the outermost `sub` of an RFC 8693 `act` chain (the current
|
|
649
|
-
* actor; nested levels are prior actors, kept on `chain` for audit), else the
|
|
650
|
-
* OAuth `client_id` of a third-party app. The outer level's `kind` names the actor:
|
|
651
|
-
* none is `oauth-client`, `support` a support session, `impersonation` an admin acting as the
|
|
652
|
-
* user. Reads only `act` and `client_id`; the caller applies the role rule first (`anon` and
|
|
653
|
-
* `service_role` are anonymous and carry no actor). An `act` that is not a chain of objects each
|
|
654
|
-
* with a non-empty `sub`, that names another `kind`, or a support level without `session_id`,
|
|
655
|
-
* is `{ ok: false }` and must deny.
|
|
656
|
-
*/
|
|
657
|
-
function actorOf(claims) {
|
|
658
|
-
try {
|
|
659
|
-
return isRecord(claims) ? readActor(claims) : NO_ACTOR;
|
|
660
|
-
} catch {
|
|
661
|
-
return INVALID_CHAIN;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
/** The OAuth `scope` claim (a space-separated string or a list) as `scopes`; `undefined` when empty. */
|
|
665
|
-
function delegationOf(claims) {
|
|
666
|
-
try {
|
|
667
|
-
if (!isRecord(claims) || !Object.hasOwn(claims, "scope")) return;
|
|
668
|
-
const scope = claims["scope"];
|
|
669
|
-
const scopes = typeof scope === "string" ? scope.split(/\s+/u).filter(Boolean) : asStrings(scope);
|
|
670
|
-
return scopes.length > 0 ? freezeDeep({ scopes }) : void 0;
|
|
671
|
-
} catch {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
function subjectDelegation(claims, chain) {
|
|
676
|
-
const delegation = compact({
|
|
677
|
-
scopes: delegationOf(claims)?.scopes,
|
|
678
|
-
chain
|
|
679
|
-
});
|
|
680
|
-
return Object.keys(delegation).length === 0 ? void 0 : delegation;
|
|
681
|
-
}
|
|
682
|
-
function emit(options, reason, cause) {
|
|
683
|
-
try {
|
|
684
|
-
options.onAuth?.({
|
|
685
|
-
reason,
|
|
686
|
-
cause,
|
|
687
|
-
source: "supabase"
|
|
688
|
-
});
|
|
689
|
-
} catch {}
|
|
690
|
-
}
|
|
691
|
-
function validateClaims(extra, schema) {
|
|
692
|
-
const result = schema["~standard"].validate(extra);
|
|
693
|
-
if (result instanceof Promise) {
|
|
694
|
-
ignoreRejection(result);
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
if ("issues" in result && result.issues !== void 0) return;
|
|
698
|
-
const value = result.value;
|
|
699
|
-
return isRecord(value) ? value : void 0;
|
|
700
|
-
}
|
|
701
|
-
function extraClaims(claims) {
|
|
702
|
-
const extra = {};
|
|
703
|
-
const meta = claims["app_metadata"];
|
|
704
|
-
if (isRecord(meta)) for (const [key, value] of Object.entries(meta)) extra[key] = value;
|
|
705
|
-
for (const [key, value] of Object.entries(claims)) {
|
|
706
|
-
if (REGISTERED.has(key) || key === "user_metadata") continue;
|
|
707
|
-
extra[key] = value;
|
|
708
|
-
}
|
|
709
|
-
return extra;
|
|
710
|
-
}
|
|
711
|
-
function mapClaims(claims, options) {
|
|
712
|
-
const role = claims["role"];
|
|
713
|
-
if (role === "anon" || role === "service_role") return anonymousSubject();
|
|
714
|
-
const id = claims["sub"];
|
|
715
|
-
if (typeof id !== "string" || id === "") return anonymousSubject();
|
|
716
|
-
const act = readActor(claims);
|
|
717
|
-
if (!act.ok) {
|
|
718
|
-
emit(options, "invalid-token", act.reason);
|
|
719
|
-
return anonymousSubject();
|
|
720
|
-
}
|
|
721
|
-
if (options.anonymousSignIns === "deny" && claims["is_anonymous"] === true) return anonymousSubject();
|
|
722
|
-
const actor = act.actor === void 0 ? void 0 : act.actor.kind === "support" ? {
|
|
723
|
-
id: act.actor.id,
|
|
724
|
-
kind: act.actor.kind,
|
|
725
|
-
sessionId: act.actor.sessionId,
|
|
726
|
-
readOnly: act.actor.readOnly
|
|
727
|
-
} : {
|
|
728
|
-
id: act.actor.id,
|
|
729
|
-
kind: act.actor.kind
|
|
730
|
-
};
|
|
731
|
-
const roleClaim = options.roles ?? "user_role";
|
|
732
|
-
const tenantClaim = options.tenant ?? "tenant_id";
|
|
733
|
-
const membershipsClaim = options.memberships ?? "memberships";
|
|
734
|
-
let extra = extraClaims(claims);
|
|
735
|
-
if (options.schema !== void 0) extra = validateClaims(extra, options.schema) ?? {};
|
|
736
|
-
const include = new Set(options.include ?? []);
|
|
737
|
-
const tenantValue = readClaim(claims, tenantClaim);
|
|
738
|
-
const version = readClaim(claims, "authz_ver");
|
|
739
|
-
const tenant = typeof tenantValue === "string" ? tenantValue : void 0;
|
|
740
|
-
const { memberships, dropped } = readMemberships(readClaim(claims, membershipsClaim));
|
|
741
|
-
if (dropped.length > 0) emit(options, "schema", "membership-dropped");
|
|
742
|
-
const plans = plansFor(claims, options.plans, tenant);
|
|
743
|
-
const principal = compact({
|
|
744
|
-
id,
|
|
745
|
-
kind: "user",
|
|
746
|
-
roles: asRoles(readClaim(claims, roleClaim), options.declared),
|
|
747
|
-
tenant,
|
|
748
|
-
plans: plans.length > 0 ? plans : void 0,
|
|
749
|
-
memberships,
|
|
750
|
-
membershipsTruncated: readClaim(claims, "memberships_truncated") === true ? true : void 0,
|
|
751
|
-
authzVersion: typeof version === "number" && Number.isInteger(version) ? version : void 0,
|
|
752
|
-
issuer: typeof claims["iss"] === "string" ? claims["iss"] : void 0,
|
|
753
|
-
assurance: typeof claims["aal"] === "string" ? { acr: claims["aal"] } : void 0,
|
|
754
|
-
claims: Object.keys(extra).length === 0 ? void 0 : extra,
|
|
755
|
-
email: include.has("email") && typeof claims["email"] === "string" ? claims["email"] : void 0,
|
|
756
|
-
phone: include.has("phone") && typeof claims["phone"] === "string" ? claims["phone"] : void 0,
|
|
757
|
-
is_anonymous: include.has("is_anonymous") && typeof claims["is_anonymous"] === "boolean" ? claims["is_anonymous"] : void 0
|
|
758
|
-
});
|
|
759
|
-
const session = typeof claims["session_id"] === "string" ? claims["session_id"] : void 0;
|
|
760
|
-
const expiresAt = typeof claims["exp"] === "number" ? claims["exp"] : void 0;
|
|
761
|
-
return freezeDeep(compact({
|
|
762
|
-
principal,
|
|
763
|
-
actor,
|
|
764
|
-
delegation: act.actor?.kind === "oauth-client" ? subjectDelegation(claims, act.actor.chain) : void 0,
|
|
765
|
-
context: {},
|
|
766
|
-
session,
|
|
767
|
-
expiresAt
|
|
768
|
-
}));
|
|
769
|
-
}
|
|
770
|
-
function subjectFromSupabase(claims, options = {}) {
|
|
771
|
-
try {
|
|
772
|
-
if (!isRecord(claims)) return anonymousSubject();
|
|
773
|
-
const rest = {};
|
|
774
|
-
for (const [key, value] of Object.entries(claims)) {
|
|
775
|
-
if (key === "user_metadata") continue;
|
|
776
|
-
rest[key] = value;
|
|
777
|
-
}
|
|
778
|
-
return mapClaims(rest, options);
|
|
779
|
-
} catch {
|
|
780
|
-
return anonymousSubject();
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* A verified session object (for example better-supabase's `AuthSession`) in, `Subject` out.
|
|
785
|
-
* Only `kind: 'user'` sessions map; `anon`, `service`, `invalid` and anything else are anonymous.
|
|
786
|
-
*/
|
|
787
|
-
function subjectFromSupabaseSession(session, options = {}) {
|
|
788
|
-
try {
|
|
789
|
-
if (session === null || session === void 0 || session.kind !== "user") return anonymousSubject();
|
|
790
|
-
return subjectFromSupabase(session.claims, options);
|
|
791
|
-
} catch {
|
|
792
|
-
return anonymousSubject();
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
//#endregion
|
|
796
|
-
export { subjectFromSupabaseSession as a, supabaseClaims as c, subjectFromSupabase as i, exchangeCapability as l, delegationOf as n, authorizeSql as o, readMemberships as r, supabaseRls as s, actorOf as t };
|
|
774
|
+
export { readSubjectRecord as a, postgrestSources as i, supabaseRls as n, supabaseClaims as o, supabaseApprovalStore as r, exchangeCapability as s, authorizeSql as t };
|