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
package/dist/helpers-DnSOrhB9.js
DELETED
|
@@ -1,682 +0,0 @@
|
|
|
1
|
-
import { t as compact } from "./compact-y6vfu2_f.js";
|
|
2
|
-
import { n as freezeDeep } from "./freeze-y1_jQL4v.js";
|
|
3
|
-
import { s as getRegistry } from "./permissions-DC7GROPA.js";
|
|
4
|
-
import { t as flattenApprovers } from "./approvers-aH0PJkmf.js";
|
|
5
|
-
import { o as rootMembershipId } from "./scopes-cKVp_WTw.js";
|
|
6
|
-
import { l as matchGrantee } from "./grantee-Bv04_Vzf.js";
|
|
7
|
-
import { t as describe } from "./describe-Bt1YD5uS.js";
|
|
8
|
-
import { t as parseDuration } from "./duration-xK2jGFVc.js";
|
|
9
|
-
import { i as relationReader, n as pendingRelations, o as resolveRelated } from "./relations-r4soIFZB.js";
|
|
10
|
-
import { t as ApprovalError } from "./errors-m3u9TmDu.js";
|
|
11
|
-
//#region src/approvals/types.ts
|
|
12
|
-
const APPROVAL_HEADER = "PermDock-Approval";
|
|
13
|
-
const DEFAULT_APPROVAL_TTL_MS = 36e5;
|
|
14
|
-
/** The approvals a request needs in total: `approvers.quorum` (or 1), or the sum of its stages' quorums. */
|
|
15
|
-
function approvalQuorum(request) {
|
|
16
|
-
const stages = request.approvers?.stages;
|
|
17
|
-
if (stages !== void 0) return stages.reduce((sum, stage) => sum + (stage.quorum ?? 1), 0);
|
|
18
|
-
return request.approvers?.quorum ?? 1;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The instant from which `approvers.escalation.to` may approve, in epoch
|
|
22
|
-
* milliseconds, or `undefined` when the request has no escalation or its
|
|
23
|
-
* `after` does not parse (then nobody escalates: fail-closed).
|
|
24
|
-
*/
|
|
25
|
-
function escalationOpenAt(request) {
|
|
26
|
-
const after = request.approvers?.escalation?.after;
|
|
27
|
-
const seconds = parseDuration(after);
|
|
28
|
-
if (seconds === void 0) return;
|
|
29
|
-
return Date.parse(request.createdAt) + seconds * 1e3;
|
|
30
|
-
}
|
|
31
|
-
/** The key a verdict's `relations` lists a relation approver under. */
|
|
32
|
-
function approverRelationKey(item) {
|
|
33
|
-
return JSON.stringify([
|
|
34
|
-
item.resource,
|
|
35
|
-
item.relation,
|
|
36
|
-
item.through ?? null,
|
|
37
|
-
item.depth ?? null
|
|
38
|
-
]);
|
|
39
|
-
}
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/approvals/relations.ts
|
|
42
|
-
const DEFAULT_PARENT_DEPTH = 16;
|
|
43
|
-
/** Reads until nothing is in flight: each round loads one more layer of facts. */
|
|
44
|
-
async function settled(read, unread, cache) {
|
|
45
|
-
for (;;) {
|
|
46
|
-
const value = read();
|
|
47
|
-
if (!unread(value)) return value;
|
|
48
|
-
const pending = pendingRelations(cache);
|
|
49
|
-
if (pending.length === 0) return value;
|
|
50
|
-
await Promise.all(pending);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/** The id of `item.resource` the walk starts from, after following `through` links. */
|
|
54
|
-
async function startOf(item, resource, id, reader, cache, registry) {
|
|
55
|
-
if (!Array.isArray(item.through)) return resource === item.resource ? id : void 0;
|
|
56
|
-
let current = resource;
|
|
57
|
-
let at = id;
|
|
58
|
-
for (const link of item.through) {
|
|
59
|
-
const node = registry.get(current);
|
|
60
|
-
const target = node !== void 0 && Object.hasOwn(node.links, link) ? node.links[link] : void 0;
|
|
61
|
-
if (target === void 0) return;
|
|
62
|
-
const query = {
|
|
63
|
-
resource: current,
|
|
64
|
-
id: at,
|
|
65
|
-
depth: 1,
|
|
66
|
-
through: link
|
|
67
|
-
};
|
|
68
|
-
const chain = await settled(() => reader.chain(query), (value) => value === "pending", cache);
|
|
69
|
-
const next = chain === "pending" || chain === "failed" ? void 0 : chain.ancestors[0]?.id;
|
|
70
|
-
if (next === void 0) return;
|
|
71
|
-
current = target.resource;
|
|
72
|
-
at = next;
|
|
73
|
-
}
|
|
74
|
-
return current === item.resource ? at : void 0;
|
|
75
|
-
}
|
|
76
|
-
function depthOf(item) {
|
|
77
|
-
if (item.through === "parent") return item.depth ?? DEFAULT_PARENT_DEPTH;
|
|
78
|
-
return item.depth ?? 0;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* The relation approvers of `request` that `subject` holds on its resource,
|
|
82
|
-
* keyed by `approverRelationKey`, for `ApprovalVerdict.relations`. Reads go
|
|
83
|
-
* through `relations` by the request's resource id; a request without an id,
|
|
84
|
-
* a missing source or a read that fails holds nothing.
|
|
85
|
-
*/
|
|
86
|
-
async function approverRelations(request, subject, options) {
|
|
87
|
-
const approvers = request.approvers;
|
|
88
|
-
const id = request.resource.id;
|
|
89
|
-
if (approvers === void 0 || id === void 0 || options.relations === void 0 || options.permissions === void 0 || subject.principal === null) return [];
|
|
90
|
-
const items = /* @__PURE__ */ new Map();
|
|
91
|
-
for (const item of [
|
|
92
|
-
...flattenApprovers(approvers.by),
|
|
93
|
-
...(approvers.stages ?? []).flatMap((stage) => flattenApprovers(stage.by)),
|
|
94
|
-
...flattenApprovers(approvers.escalation?.to)
|
|
95
|
-
]) if (item.kind === "relation") items.set(approverRelationKey(item), item);
|
|
96
|
-
if (items.size === 0) return [];
|
|
97
|
-
const registry = getRegistry(options.permissions);
|
|
98
|
-
const cache = /* @__PURE__ */ new Map();
|
|
99
|
-
const reader = relationReader(options.relations, cache, registry);
|
|
100
|
-
const now = (options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
101
|
-
const holds = async (item) => {
|
|
102
|
-
const start = await startOf(item, request.resource.type, id, reader, cache, registry);
|
|
103
|
-
if (start === void 0) return false;
|
|
104
|
-
const condition = {
|
|
105
|
-
op: "related",
|
|
106
|
-
resource: item.resource,
|
|
107
|
-
relation: item.relation,
|
|
108
|
-
field: "id",
|
|
109
|
-
depth: depthOf(item)
|
|
110
|
-
};
|
|
111
|
-
return await settled(() => resolveRelated(condition, { id: start }, subject, now, reader), (value) => value === "relation-unavailable", cache) === true;
|
|
112
|
-
};
|
|
113
|
-
const entries = [...items];
|
|
114
|
-
const verdicts = await Promise.all(entries.map(([, item]) => holds(item)));
|
|
115
|
-
return entries.flatMap(([key], index) => verdicts[index] === true ? [key] : []);
|
|
116
|
-
}
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region src/approvals/page.ts
|
|
119
|
-
const DEFAULT_PAGE_SIZE = 50;
|
|
120
|
-
const MAX_PAGE_SIZE = 200;
|
|
121
|
-
const MAX_PAGES = 1e3;
|
|
122
|
-
function pageSizeOf(limit) {
|
|
123
|
-
if (limit === void 0 || !Number.isFinite(limit)) return DEFAULT_PAGE_SIZE;
|
|
124
|
-
return Math.min(Math.max(Math.trunc(limit), 1), MAX_PAGE_SIZE);
|
|
125
|
-
}
|
|
126
|
-
function positionOf(request) {
|
|
127
|
-
return [request.createdAt, request.token];
|
|
128
|
-
}
|
|
129
|
-
function compare(a, b) {
|
|
130
|
-
if (a[0] !== b[0]) return a[0] < b[0] ? -1 : 1;
|
|
131
|
-
if (a[1] === b[1]) return 0;
|
|
132
|
-
return a[1] < b[1] ? -1 : 1;
|
|
133
|
-
}
|
|
134
|
-
function encodeCursor(request) {
|
|
135
|
-
return JSON.stringify(positionOf(request));
|
|
136
|
-
}
|
|
137
|
-
function decodeCursor(cursor) {
|
|
138
|
-
try {
|
|
139
|
-
const parsed = JSON.parse(cursor);
|
|
140
|
-
return Array.isArray(parsed) && parsed.length === 2 && typeof parsed[0] === "string" && typeof parsed[1] === "string" ? [parsed[0], parsed[1]] : null;
|
|
141
|
-
} catch {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
/** Pages already-filtered requests in `(createdAt, token)` order. */
|
|
146
|
-
function pageOf(matching, query) {
|
|
147
|
-
const after = query.cursor === void 0 ? void 0 : decodeCursor(query.cursor);
|
|
148
|
-
if (after === null) return { items: [] };
|
|
149
|
-
const sorted = matching.filter((request) => after === void 0 || compare(positionOf(request), after) > 0).toSorted((a, b) => compare(positionOf(a), positionOf(b)));
|
|
150
|
-
const size = pageSizeOf(query.limit);
|
|
151
|
-
const items = sorted.slice(0, size);
|
|
152
|
-
const last = items.at(-1);
|
|
153
|
-
return sorted.length > size && last !== void 0 ? {
|
|
154
|
-
items,
|
|
155
|
-
next: encodeCursor(last)
|
|
156
|
-
} : { items };
|
|
157
|
-
}
|
|
158
|
-
/** Every matching request, following `next` until the last page. */
|
|
159
|
-
function listAll(store, filter) {
|
|
160
|
-
const collect = async (cursor, pages, items) => {
|
|
161
|
-
const page = await store.list(cursor === void 0 ? {
|
|
162
|
-
...filter,
|
|
163
|
-
limit: MAX_PAGE_SIZE
|
|
164
|
-
} : {
|
|
165
|
-
...filter,
|
|
166
|
-
limit: MAX_PAGE_SIZE,
|
|
167
|
-
cursor
|
|
168
|
-
});
|
|
169
|
-
const next = [...items, ...page.items];
|
|
170
|
-
return page.next === void 0 || pages + 1 >= MAX_PAGES ? next : collect(page.next, pages + 1, next);
|
|
171
|
-
};
|
|
172
|
-
return collect(void 0, 0, []);
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
|
-
//#region src/approvals/store.ts
|
|
176
|
-
function tenantOf(request) {
|
|
177
|
-
return request.subject.principal?.tenant;
|
|
178
|
-
}
|
|
179
|
-
function membershipTenants(subject) {
|
|
180
|
-
const principal = subject.principal;
|
|
181
|
-
if (principal === null) return [];
|
|
182
|
-
const tenants = /* @__PURE__ */ new Set();
|
|
183
|
-
if (principal.tenant !== void 0) tenants.add(principal.tenant);
|
|
184
|
-
for (const membership of principal.memberships ?? []) {
|
|
185
|
-
const tenant = rootMembershipId(membership);
|
|
186
|
-
if (tenant !== void 0) tenants.add(tenant);
|
|
187
|
-
}
|
|
188
|
-
return [...tenants];
|
|
189
|
-
}
|
|
190
|
-
function belongsToTenant(subject, tenant) {
|
|
191
|
-
return membershipTenants(subject).includes(tenant);
|
|
192
|
-
}
|
|
193
|
-
function holdsRole(subject, role, tenant) {
|
|
194
|
-
const principal = subject.principal;
|
|
195
|
-
if (principal === null) return false;
|
|
196
|
-
if ((principal.roles ?? []).includes(role)) return true;
|
|
197
|
-
for (const membership of principal.memberships ?? []) {
|
|
198
|
-
if (!membership.roles.includes(role)) continue;
|
|
199
|
-
if (tenant !== void 0 && rootMembershipId(membership) !== tenant) continue;
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
function matchesApprovers(by, subject, tenant, now, relations) {
|
|
205
|
-
const items = flattenApprovers(by);
|
|
206
|
-
if (items.length === 0) return false;
|
|
207
|
-
for (const item of items) {
|
|
208
|
-
if (item.kind === "role") {
|
|
209
|
-
if (!holdsRole(subject, item.role, tenant)) return false;
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (item.kind === "user") {
|
|
213
|
-
if (subject.principal?.id !== item.id) return false;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (item.kind === "relation") {
|
|
217
|
-
if (!relations.has(approverRelationKey(item))) return false;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
const result = matchGrantee(item, subject, now, void 0);
|
|
221
|
-
if (!result.matched || result.where !== void 0) return false;
|
|
222
|
-
}
|
|
223
|
-
return true;
|
|
224
|
-
}
|
|
225
|
-
/** Approvals recorded per stage index. */
|
|
226
|
-
function stageCounts(approvals, stages) {
|
|
227
|
-
const counts = stages.map(() => 0);
|
|
228
|
-
for (const item of approvals ?? []) if (item.stage !== void 0 && item.stage < counts.length) counts[item.stage] = (counts[item.stage] ?? 0) + 1;
|
|
229
|
-
return counts;
|
|
230
|
-
}
|
|
231
|
-
function isComplete(request, approvals) {
|
|
232
|
-
const stages = request.approvers?.stages;
|
|
233
|
-
if (stages === void 0) return approvals.length >= approvalQuorum(request);
|
|
234
|
-
const counts = stageCounts(approvals, stages);
|
|
235
|
-
return stages.every((stage, index) => (counts[index] ?? 0) >= (stage.quorum ?? 1));
|
|
236
|
-
}
|
|
237
|
-
function matchesFilter(request, filter) {
|
|
238
|
-
if (filter.status !== void 0 && request.status !== filter.status) return false;
|
|
239
|
-
if (filter.principalId !== void 0 && request.subject.principal?.id !== filter.principalId) return false;
|
|
240
|
-
if (filter.actorId !== void 0 && request.subject.actor?.id !== filter.actorId) return false;
|
|
241
|
-
if (filter.tenant !== void 0 && tenantOf(request) !== filter.tenant) return false;
|
|
242
|
-
if (filter.session !== void 0 && request.subject.session !== filter.session) return false;
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Refuses `by` as an approver of `request`, or returns the index of the
|
|
247
|
-
* stage the approval counts for (`undefined` without `stages`). Eligibility
|
|
248
|
-
* is `approvers.by` or, under `stages`, the first incomplete stage
|
|
249
|
-
* (`sequential`) or any incomplete stage (`all`); `approvers.escalation.to`
|
|
250
|
-
* is eligible too once the request has waited `escalation.after`. A
|
|
251
|
-
* principal who already approved is refused, so one person never completes
|
|
252
|
-
* two stages. `relations` are the relation approvers `by` holds on the
|
|
253
|
-
* request's resource (see `ApprovalVerdict.relations`).
|
|
254
|
-
*/
|
|
255
|
-
function assertApprover(request, by, requireDistinctApprover, now = /* @__PURE__ */ new Date(), relations = []) {
|
|
256
|
-
const principal = by.principal;
|
|
257
|
-
if (principal === null) throw new ApprovalError("approver-unauthenticated", "approver must be authenticated");
|
|
258
|
-
if (request.subject.actor !== void 0 && principal.id === request.subject.actor.id) throw new ApprovalError("approver-is-actor", "approver is the actor of this request");
|
|
259
|
-
if ((requireDistinctApprover || request.approvers?.distinct !== false) && request.subject.principal !== null && principal.id === request.subject.principal.id) throw new ApprovalError("approver-is-principal", "approver is the principal of this request");
|
|
260
|
-
const tenant = request.subject.principal?.tenant;
|
|
261
|
-
if (tenant !== void 0 && !belongsToTenant(by, tenant)) throw new ApprovalError("approver-not-eligible", "approver does not belong to the request tenant");
|
|
262
|
-
if ((request.approvals ?? []).some((item) => item.by === principal.id)) throw new ApprovalError("approver-repeated", "approver has already approved this request");
|
|
263
|
-
const approvers = request.approvers;
|
|
264
|
-
if (approvers === void 0) return;
|
|
265
|
-
const held = new Set(relations);
|
|
266
|
-
const instant = now.getTime() / 1e3;
|
|
267
|
-
const eligible = (target) => matchesApprovers(target, by, tenant, instant, held);
|
|
268
|
-
const openAt = escalationOpenAt(request);
|
|
269
|
-
const escalated = approvers.escalation !== void 0 && openAt !== void 0 && now.getTime() >= openAt && eligible(approvers.escalation.to);
|
|
270
|
-
const stages = approvers.stages;
|
|
271
|
-
if (stages === void 0) {
|
|
272
|
-
if (escalated || approvers.by !== void 0 && eligible(approvers.by)) return;
|
|
273
|
-
throw new ApprovalError("approver-not-eligible", "approver does not hold an eligible role");
|
|
274
|
-
}
|
|
275
|
-
const counts = stageCounts(request.approvals, stages);
|
|
276
|
-
const open = stages.flatMap((stage, index) => (counts[index] ?? 0) < (stage.quorum ?? 1) ? [index] : []);
|
|
277
|
-
const candidates = approvers.mode === "sequential" ? open.slice(0, 1) : open;
|
|
278
|
-
for (const index of candidates) {
|
|
279
|
-
const stage = stages[index];
|
|
280
|
-
if (stage !== void 0 && (escalated || eligible(stage.by))) return index;
|
|
281
|
-
}
|
|
282
|
-
throw new ApprovalError("approver-not-eligible", approvers.mode === "sequential" ? "approver is not eligible for the current stage" : "approver is not eligible for an open stage");
|
|
283
|
-
}
|
|
284
|
-
function isSystemSubject(by) {
|
|
285
|
-
return by.actor?.kind === "system";
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* The request after `verdict`, for a store to persist: a rejection resolves
|
|
289
|
-
* it; an approval is appended to `approvals` and resolves it once there are
|
|
290
|
-
* `approvers.quorum` (default 1). Throws an `ApprovalError` when the request
|
|
291
|
-
* is not pending, has expired or `verdict.by` may not approve it. Stores
|
|
292
|
-
* should write the result only when the stored row is still pending with the
|
|
293
|
-
* same number of approvals, so two approvers racing both count.
|
|
294
|
-
*/
|
|
295
|
-
function applyApprovalVerdict(request, verdict, now = /* @__PURE__ */ new Date()) {
|
|
296
|
-
const principal = verdict.by.principal;
|
|
297
|
-
if (principal === null) throw new ApprovalError("approver-unauthenticated", "approver must be authenticated");
|
|
298
|
-
if (request.status !== "pending") throw new ApprovalError("approval-not-pending", "approval is not pending");
|
|
299
|
-
if (Date.parse(request.expiresAt) <= now.getTime()) throw new ApprovalError("approval-expired", "approval has expired");
|
|
300
|
-
const stage = verdict.status === "rejected" && isSystemSubject(verdict.by) ? void 0 : assertApprover(request, verdict.by, false, now, verdict.relations);
|
|
301
|
-
if (verdict.status === "rejected") return freezeDeep(compact({
|
|
302
|
-
...request,
|
|
303
|
-
status: "rejected",
|
|
304
|
-
resolvedAt: now.toISOString(),
|
|
305
|
-
resolvedBy: principal.id,
|
|
306
|
-
note: verdict.note ?? request.note
|
|
307
|
-
}));
|
|
308
|
-
const approvals = [...request.approvals ?? [], compact({
|
|
309
|
-
by: principal.id,
|
|
310
|
-
at: now.toISOString(),
|
|
311
|
-
stage
|
|
312
|
-
})];
|
|
313
|
-
if (!isComplete(request, approvals)) return freezeDeep(compact({
|
|
314
|
-
...request,
|
|
315
|
-
approvals,
|
|
316
|
-
note: verdict.note ?? request.note
|
|
317
|
-
}));
|
|
318
|
-
return freezeDeep(compact({
|
|
319
|
-
...request,
|
|
320
|
-
status: "approved",
|
|
321
|
-
approvals,
|
|
322
|
-
resolvedAt: now.toISOString(),
|
|
323
|
-
resolvedBy: principal.id,
|
|
324
|
-
note: verdict.note ?? request.note
|
|
325
|
-
}));
|
|
326
|
-
}
|
|
327
|
-
function rejectPending(request, meta, now) {
|
|
328
|
-
return freezeDeep(compact({
|
|
329
|
-
...request,
|
|
330
|
-
status: "rejected",
|
|
331
|
-
resolvedAt: now.toISOString(),
|
|
332
|
-
resolvedBy: `system:${meta.by}`,
|
|
333
|
-
note: meta.note
|
|
334
|
-
}));
|
|
335
|
-
}
|
|
336
|
-
/** A request a new ask may replace: expired, or past its deadline. */
|
|
337
|
-
function isStale(request, now) {
|
|
338
|
-
return request.status === "expired" || Date.parse(request.expiresAt) <= now.getTime();
|
|
339
|
-
}
|
|
340
|
-
function memoryApprovalStore(options = {}) {
|
|
341
|
-
const ttl = options.ttl ?? 36e5;
|
|
342
|
-
const records = /* @__PURE__ */ new Map();
|
|
343
|
-
return {
|
|
344
|
-
ttl,
|
|
345
|
-
create(request) {
|
|
346
|
-
const current = records.get(request.token);
|
|
347
|
-
if (current !== void 0 && !isStale(current, /* @__PURE__ */ new Date())) return;
|
|
348
|
-
records.set(request.token, freezeDeep(request));
|
|
349
|
-
},
|
|
350
|
-
get(token) {
|
|
351
|
-
return records.get(token) ?? null;
|
|
352
|
-
},
|
|
353
|
-
resolve(token, verdict) {
|
|
354
|
-
const current = records.get(token);
|
|
355
|
-
if (current === void 0) throw new ApprovalError("approval-not-found", "approval was not found");
|
|
356
|
-
const next = applyApprovalVerdict(current, verdict, /* @__PURE__ */ new Date());
|
|
357
|
-
records.set(token, next);
|
|
358
|
-
return next;
|
|
359
|
-
},
|
|
360
|
-
consume(token, now = /* @__PURE__ */ new Date()) {
|
|
361
|
-
const current = records.get(token);
|
|
362
|
-
if (current?.status !== "approved" || current.consumedAt !== void 0 || Date.parse(current.expiresAt) <= now.getTime()) return null;
|
|
363
|
-
const next = freezeDeep({
|
|
364
|
-
...current,
|
|
365
|
-
consumedAt: now.toISOString()
|
|
366
|
-
});
|
|
367
|
-
records.set(token, next);
|
|
368
|
-
return next;
|
|
369
|
-
},
|
|
370
|
-
list(query) {
|
|
371
|
-
const matching = [];
|
|
372
|
-
for (const request of records.values()) if (matchesFilter(request, query)) matching.push(request);
|
|
373
|
-
return pageOf(matching, query);
|
|
374
|
-
},
|
|
375
|
-
expire(now = /* @__PURE__ */ new Date()) {
|
|
376
|
-
let count = 0;
|
|
377
|
-
const instant = now.getTime();
|
|
378
|
-
for (const [token, request] of records) {
|
|
379
|
-
const deadline = Date.parse(request.expiresAt);
|
|
380
|
-
if (request.status !== "pending" && deadline + ttl <= instant) {
|
|
381
|
-
records.delete(token);
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
if (request.status === "pending" && deadline <= instant) {
|
|
385
|
-
records.set(token, freezeDeep(compact({
|
|
386
|
-
...request,
|
|
387
|
-
status: "expired"
|
|
388
|
-
})));
|
|
389
|
-
count += 1;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return count;
|
|
393
|
-
},
|
|
394
|
-
cancel(filter, meta) {
|
|
395
|
-
const now = /* @__PURE__ */ new Date();
|
|
396
|
-
let count = 0;
|
|
397
|
-
for (const [token, request] of records) {
|
|
398
|
-
if (request.status !== "pending" || !matchesFilter(request, filter)) continue;
|
|
399
|
-
records.set(token, rejectPending(request, meta, now));
|
|
400
|
-
count += 1;
|
|
401
|
-
}
|
|
402
|
-
return count;
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
//#endregion
|
|
407
|
-
//#region src/approvals/helpers.ts
|
|
408
|
-
function permissionMeta(permission) {
|
|
409
|
-
return {
|
|
410
|
-
key: permission.key,
|
|
411
|
-
scope: permission.scope,
|
|
412
|
-
resource: permission.resource
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
function summariseSubject(subject) {
|
|
416
|
-
const principal = subject.principal;
|
|
417
|
-
return compact({
|
|
418
|
-
principal: principal === null ? null : compact({
|
|
419
|
-
id: principal.id,
|
|
420
|
-
roles: principal.roles ?? [],
|
|
421
|
-
tenant: principal.tenant
|
|
422
|
-
}),
|
|
423
|
-
actor: subject.actor === void 0 ? void 0 : {
|
|
424
|
-
id: subject.actor.id,
|
|
425
|
-
kind: subject.actor.kind
|
|
426
|
-
},
|
|
427
|
-
session: subject.session,
|
|
428
|
-
delegation: subject.delegation === void 0 ? void 0 : compact({
|
|
429
|
-
scopes: subject.delegation.scopes,
|
|
430
|
-
authorizationDetails: subject.delegation.authorizationDetails
|
|
431
|
-
})
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* The store's window (`meta.ttl`, else the default) capped by the grant's
|
|
436
|
-
* `approval.ttl`: a grant shortens how long a request stays open, never
|
|
437
|
-
* extends it.
|
|
438
|
-
*/
|
|
439
|
-
function approvalTtl(storeTtl, approval) {
|
|
440
|
-
const base = storeTtl ?? 36e5;
|
|
441
|
-
const grant = approval === void 0 || approval === "human" ? void 0 : parseDuration(approval.ttl);
|
|
442
|
-
return grant === void 0 ? base : Math.min(base, grant * 1e3);
|
|
443
|
-
}
|
|
444
|
-
async function requestApproval(store, decision, meta) {
|
|
445
|
-
const now = meta.now ?? /* @__PURE__ */ new Date();
|
|
446
|
-
const leaf = permissionMeta(meta.permission);
|
|
447
|
-
const approval = decision.grant.approval;
|
|
448
|
-
const approvers = approval === void 0 || approval === "human" ? void 0 : compact({
|
|
449
|
-
by: approval.by,
|
|
450
|
-
mode: approval.mode,
|
|
451
|
-
stages: approval.stages,
|
|
452
|
-
distinct: approval.distinct,
|
|
453
|
-
staleOn: approval.staleOn,
|
|
454
|
-
quorum: approval.quorum,
|
|
455
|
-
escalation: approval.escalation
|
|
456
|
-
});
|
|
457
|
-
const ttl = approvalTtl(meta.ttl, approval);
|
|
458
|
-
const request = freezeDeep(compact({
|
|
459
|
-
v: 1,
|
|
460
|
-
token: decision.token,
|
|
461
|
-
permission: leaf.key,
|
|
462
|
-
scope: leaf.scope,
|
|
463
|
-
resource: meta.resource ?? { type: leaf.resource },
|
|
464
|
-
subject: summariseSubject(meta.subject),
|
|
465
|
-
membership: meta.membership,
|
|
466
|
-
approvers,
|
|
467
|
-
detail: meta.detail ?? describe(decision).detail,
|
|
468
|
-
adapter: meta.adapter,
|
|
469
|
-
createdAt: now.toISOString(),
|
|
470
|
-
expiresAt: new Date(now.getTime() + ttl).toISOString(),
|
|
471
|
-
status: "pending"
|
|
472
|
-
}));
|
|
473
|
-
await store.create(request);
|
|
474
|
-
return request;
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Checks `verdict.by` and resolves the request. Relation approvers are read
|
|
478
|
-
* through `options.relations` unless `verdict.relations` is already set.
|
|
479
|
-
*/
|
|
480
|
-
async function resolveApproval(store, token, verdict, options = {}) {
|
|
481
|
-
const current = await store.get(token);
|
|
482
|
-
if (current === null) throw new ApprovalError("approval-not-found", "approval was not found");
|
|
483
|
-
const relations = verdict.relations ?? await approverRelations(current, verdict.by, options);
|
|
484
|
-
assertApprover(current, verdict.by, options.requireDistinctApprover === true, options.now, relations);
|
|
485
|
-
return store.resolve(token, {
|
|
486
|
-
...verdict,
|
|
487
|
-
relations
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
async function inspectApproval(store, token, now = /* @__PURE__ */ new Date()) {
|
|
491
|
-
await store.expire(now);
|
|
492
|
-
const request = await store.get(token);
|
|
493
|
-
if (request === null) return {
|
|
494
|
-
ok: false,
|
|
495
|
-
detail: "approval-not-found"
|
|
496
|
-
};
|
|
497
|
-
if (request.status === "pending") return {
|
|
498
|
-
ok: false,
|
|
499
|
-
detail: "approval-pending"
|
|
500
|
-
};
|
|
501
|
-
if (request.status === "rejected") return {
|
|
502
|
-
ok: false,
|
|
503
|
-
detail: "approval-rejected"
|
|
504
|
-
};
|
|
505
|
-
if (request.consumedAt !== void 0) return {
|
|
506
|
-
ok: false,
|
|
507
|
-
detail: "approval-consumed"
|
|
508
|
-
};
|
|
509
|
-
if (request.status === "expired" || Date.parse(request.expiresAt) <= now.getTime()) return {
|
|
510
|
-
ok: false,
|
|
511
|
-
detail: "approval-expired"
|
|
512
|
-
};
|
|
513
|
-
return {
|
|
514
|
-
ok: true,
|
|
515
|
-
request
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
/** Inspects `token` and, when it is approved, consumes it so it resumes once. */
|
|
519
|
-
async function consumeApproval(store, token, now = /* @__PURE__ */ new Date()) {
|
|
520
|
-
const inspected = await inspectApproval(store, token, now);
|
|
521
|
-
if (!inspected.ok) return inspected;
|
|
522
|
-
if (typeof store.consume !== "function") return {
|
|
523
|
-
ok: false,
|
|
524
|
-
detail: "approval-not-found"
|
|
525
|
-
};
|
|
526
|
-
const consumed = await store.consume(token, now);
|
|
527
|
-
if (consumed === null) return {
|
|
528
|
-
ok: false,
|
|
529
|
-
detail: "approval-consumed"
|
|
530
|
-
};
|
|
531
|
-
return {
|
|
532
|
-
ok: true,
|
|
533
|
-
request: consumed
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
function approvalDenied(detail) {
|
|
537
|
-
return {
|
|
538
|
-
outcome: "denied",
|
|
539
|
-
denials: [{
|
|
540
|
-
role: null,
|
|
541
|
-
reason: "approval",
|
|
542
|
-
detail
|
|
543
|
-
}],
|
|
544
|
-
alternatives: []
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
function staleOnChange(decision) {
|
|
548
|
-
const approval = decision.grant.approval;
|
|
549
|
-
return approval !== void 0 && approval !== "human" && approval.staleOn === "resource-change";
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* True when `token` names a pending or approved request for this permission,
|
|
553
|
-
* resource and principal: the same request, issued while the row had another
|
|
554
|
-
* `version`.
|
|
555
|
-
*/
|
|
556
|
-
async function isStaleToken(store, token, input) {
|
|
557
|
-
const principal = input.subject.principal;
|
|
558
|
-
if (principal === null) return false;
|
|
559
|
-
let request;
|
|
560
|
-
try {
|
|
561
|
-
request = await store.get(token);
|
|
562
|
-
} catch {
|
|
563
|
-
return false;
|
|
564
|
-
}
|
|
565
|
-
return request !== null && (request.status === "approved" || request.status === "pending") && request.permission === input.permission.key && request.resource.type === input.resource.type && request.resource.id === input.resource.id && request.subject.principal?.id === principal.id;
|
|
566
|
-
}
|
|
567
|
-
/**
|
|
568
|
-
* Applies a resume token to a decision. The token only matters when the
|
|
569
|
-
* decision is `approval-required` and the token is the one this call was
|
|
570
|
-
* issued; otherwise a new approval is requested. A matching token is consumed
|
|
571
|
-
* unless `consume` is `false`, so an approval resumes exactly one call. Under
|
|
572
|
-
* `staleOn: 'resource-change'`, a token issued for an earlier version of the
|
|
573
|
-
* same row denies with `stale-approval`; the next call without it asks again.
|
|
574
|
-
*/
|
|
575
|
-
async function resumeDecision(input) {
|
|
576
|
-
const { decision, store, token } = input;
|
|
577
|
-
if (decision.outcome !== "approval-required") return decision;
|
|
578
|
-
if (token === void 0 || token !== decision.token) {
|
|
579
|
-
if (store !== void 0 && token !== void 0 && staleOnChange(decision) && await isStaleToken(store, token, input)) return {
|
|
580
|
-
outcome: "denied",
|
|
581
|
-
denials: [{
|
|
582
|
-
role: null,
|
|
583
|
-
reason: "stale-approval"
|
|
584
|
-
}],
|
|
585
|
-
alternatives: []
|
|
586
|
-
};
|
|
587
|
-
if (store !== void 0) await requestApproval(store, decision, compact({
|
|
588
|
-
permission: input.permission,
|
|
589
|
-
resource: input.resource,
|
|
590
|
-
subject: input.subject,
|
|
591
|
-
adapter: input.adapter
|
|
592
|
-
}));
|
|
593
|
-
return decision;
|
|
594
|
-
}
|
|
595
|
-
if (store === void 0) return approvalDenied("approval-not-found");
|
|
596
|
-
let inspected;
|
|
597
|
-
try {
|
|
598
|
-
inspected = await inspectApproval(store, token, input.now);
|
|
599
|
-
if (inspected.ok) {
|
|
600
|
-
const { request } = inspected;
|
|
601
|
-
if (request.permission !== input.permission.key || request.resource.id !== void 0 && request.resource.id !== input.resource.id) return approvalDenied("approval-mismatch");
|
|
602
|
-
if (input.consume !== false) inspected = await consumeApproval(store, token, input.now);
|
|
603
|
-
}
|
|
604
|
-
} catch {
|
|
605
|
-
return approvalDenied("approval-not-found");
|
|
606
|
-
}
|
|
607
|
-
if (!inspected.ok) return approvalDenied(inspected.detail);
|
|
608
|
-
const principal = input.subject.principal;
|
|
609
|
-
if (principal === null) return approvalDenied("approval-mismatch");
|
|
610
|
-
return {
|
|
611
|
-
outcome: "granted",
|
|
612
|
-
subject: {
|
|
613
|
-
...input.subject,
|
|
614
|
-
principal
|
|
615
|
-
},
|
|
616
|
-
matched: decision.grant,
|
|
617
|
-
token: decision.token
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
const SYSTEM_KIND = "system";
|
|
621
|
-
async function cancelApprovals(store, filter, meta) {
|
|
622
|
-
if (store.cancel !== void 0) return store.cancel(filter, meta);
|
|
623
|
-
const pending = await listAll(store, {
|
|
624
|
-
...filter,
|
|
625
|
-
status: "pending"
|
|
626
|
-
});
|
|
627
|
-
const by = {
|
|
628
|
-
principal: {
|
|
629
|
-
id: `system:${meta.by}`,
|
|
630
|
-
kind: "service",
|
|
631
|
-
roles: []
|
|
632
|
-
},
|
|
633
|
-
actor: {
|
|
634
|
-
id: `system:${meta.by}`,
|
|
635
|
-
kind: SYSTEM_KIND
|
|
636
|
-
},
|
|
637
|
-
context: {}
|
|
638
|
-
};
|
|
639
|
-
return (await Promise.all(pending.map(async (request) => {
|
|
640
|
-
try {
|
|
641
|
-
await store.resolve(request.token, compact({
|
|
642
|
-
status: "rejected",
|
|
643
|
-
by,
|
|
644
|
-
note: meta.note
|
|
645
|
-
}));
|
|
646
|
-
return true;
|
|
647
|
-
} catch {
|
|
648
|
-
return false;
|
|
649
|
-
}
|
|
650
|
-
}))).filter(Boolean).length;
|
|
651
|
-
}
|
|
652
|
-
function readApprovalHeader(headers) {
|
|
653
|
-
const value = headers.get(APPROVAL_HEADER);
|
|
654
|
-
if (value === null || value.trim() === "") return;
|
|
655
|
-
return value.trim();
|
|
656
|
-
}
|
|
657
|
-
function resumeFromHeader(store, headers, now) {
|
|
658
|
-
const token = readApprovalHeader(headers);
|
|
659
|
-
if (token === void 0) return Promise.resolve({
|
|
660
|
-
ok: false,
|
|
661
|
-
detail: "approval-not-found"
|
|
662
|
-
});
|
|
663
|
-
return inspectApproval(store, token, now);
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* The stored token for a decision that is waiting on approval: an approved or
|
|
667
|
-
* rejected record for the recomputed token resumes without the caller
|
|
668
|
-
* carrying it, since the token already binds permission, resource (or the
|
|
669
|
-
* call's data when there is no row id), subject and actor.
|
|
670
|
-
*/
|
|
671
|
-
async function storedApprovalToken(store, decision, denyPending) {
|
|
672
|
-
if (store === void 0 || decision.outcome !== "approval-required") return;
|
|
673
|
-
try {
|
|
674
|
-
const record = await store.get(decision.token);
|
|
675
|
-
if (record === null || record.status === "pending" && !denyPending || record.status === "expired" || Date.parse(record.expiresAt) <= Date.now()) return;
|
|
676
|
-
return decision.token;
|
|
677
|
-
} catch {
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
//#endregion
|
|
682
|
-
export { approvalQuorum as _, requestApproval as a, resumeFromHeader as c, applyApprovalVerdict as d, assertApprover as f, DEFAULT_APPROVAL_TTL_MS as g, APPROVAL_HEADER as h, readApprovalHeader as i, storedApprovalToken as l, approverRelations as m, consumeApproval as n, resolveApproval as o, memoryApprovalStore as p, inspectApproval as r, resumeDecision as s, cancelApprovals as t, summariseSubject as u, approverRelationKey as v, escalationOpenAt as y };
|
package/dist/index-fG6NSXlc.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { C as Policy, mr as Principal } from "./policy-CInmNGkM.js";
|
|
2
|
-
import { a as ReplayStore, f as SsfPermDock, p as SsfPermDockOptions } from "./types-i4BAOvdb.js";
|
|
3
|
-
//#region src/ssf/create.d.ts
|
|
4
|
-
declare function createPermDock<TUser, TPrincipal extends Principal = Principal>(policy: Policy<TUser, TPrincipal>, options: SsfPermDockOptions): SsfPermDock;
|
|
5
|
-
//#endregion
|
|
6
|
-
//#region src/ssf/replay.d.ts
|
|
7
|
-
declare function memoryReplayStore(): ReplayStore;
|
|
8
|
-
//#endregion
|
|
9
|
-
export { createPermDock as n, memoryReplayStore as t };
|