permdock 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/a2a/index.d.ts +7 -5
- package/dist/a2a/index.js +14 -21
- package/dist/ai-sdk/index.d.ts +18 -6
- package/dist/ai-sdk/index.js +8 -5
- package/dist/app-data-CV8pqqMi.js +59 -0
- package/dist/approvals/index.d.ts +2 -2
- package/dist/approvals/index.js +3 -3
- package/dist/{approvals-DbYAuNkG.js → approvals-DZL76lKW.js} +35 -19
- package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
- package/dist/{arazzo-BAgw9ap6.js → arazzo-C4BcskbX.js} +6 -5
- package/dist/{arazzo-CKwwqFWz.js → arazzo-CD8d0WLb.js} +2 -2
- package/dist/authzen/index.d.ts +3 -3
- package/dist/authzen/index.js +161 -23
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +6 -19
- package/dist/better-supabase/index.d.ts +112 -87
- package/dist/better-supabase/index.js +257 -156
- package/dist/{bind-C_ob3LKa.d.ts → bind-BzqG8SMb.d.ts} +16 -5
- package/dist/{bind-CuzGytRf.js → bind-kk-uyCwH.js} +14 -6
- package/dist/build-instance-DIIV9DbC.js +51 -0
- package/dist/{canonical-json-DemiK4Vp.js → canonical-json-CFKyFOke.js} +1 -3
- package/dist/{capability-2kYTMWDv.d.ts → capability-CksyCWDH.d.ts} +2 -2
- package/dist/{capability-4qPdO9gk.js → capability-txATtKV4.js} +3 -2
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/catalog-6yWAX6Ps.js +2 -0
- package/dist/{catalog-dFZdWCGN.js → catalog-WHG4kBq7.js} +2 -2
- package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-DWtg0gGR.js} +17 -10
- package/dist/{catalog-DWF5cYqB.js → catalog-tZUyOfOv.js} +4 -4
- package/dist/claude-agent/index.d.ts +4 -4
- package/dist/claude-agent/index.js +4 -5
- package/dist/clerk/index.d.ts +3 -3
- package/dist/clerk/index.js +8 -33
- package/dist/cli/bin.js +1 -1
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +3 -3
- package/dist/cloud/index.d.ts +4 -4
- package/dist/cloud/index.js +9 -15
- package/dist/{cloud-CB_ELYuX.js → cloud-C7bSDgX4.js} +2 -2
- package/dist/{cloud-DWLXO2af.js → cloud-PeBN3IEn.js} +5 -5
- package/dist/{collect-DSTE3ml9.js → collect-2SrB7lfk.js} +6 -8
- package/dist/collect-Bw2Jdq1q.js +2 -0
- package/dist/{collect-DClJsheJ.js → collect-CQx4hX4N.js} +3 -3
- package/dist/{commands-DSxCqG9A.js → commands-Cpip4ULx.js} +13 -13
- package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -1
- package/dist/{compile-Ba4blROR.js → compile-CcEu2ZQ1.js} +6 -5
- package/dist/{compile-BkRyBDBw.d.ts → compile-CtTP7eur.d.ts} +2 -2
- package/dist/{config-BJiM-xAm.js → config-BIjREpyE.js} +4 -4
- package/dist/{config-Dnszbv-6.js → config-DLt6BvV7.js} +2 -2
- package/dist/{config-report-DMvWKUxL.js → config-report--Xb9APJS.js} +5 -6
- package/dist/{connection-CU8MQxRi.js → connection-DUsczpB5.js} +3 -2
- package/dist/constants-DcJABbLW.js +20 -0
- package/dist/{context-Bcy85rGi.js → context-DieZVJXu.js} +16 -5
- package/dist/convex/index.d.ts +4 -4
- package/dist/convex/index.js +14 -16
- package/dist/create-BBO24vj6.js +324 -0
- package/dist/{credential-8P81ff4u.js → credential-kue0qpbm.js} +4 -4
- package/dist/deciding-columns-Sq12mEA6.js +123 -0
- package/dist/delegation-input-FTxMK3ji.js +19 -0
- package/dist/denied-text-DfRk15uw.d.ts +19 -0
- package/dist/{describe-DMYiCjJV.d.ts → describe-C3ldA9os.d.ts} +11 -2
- package/dist/{describe-B_gVZEVi.js → describe-Cgkp7eP0.js} +5 -4
- package/dist/{diff-DBIhz_f2.js → diff-Cnghh_Ip.js} +2 -2
- package/dist/{diff-2VaeChzX.js → diff-rpqoe7Ag.js} +9 -9
- package/dist/{doctor-BwCAMLx_.js → doctor-CuVJvhV_.js} +2 -2
- package/dist/doctor-owlv6ddh.js +2166 -0
- package/dist/drizzle/index.d.ts +2 -2
- package/dist/drizzle/index.js +6 -14
- package/dist/elysia/index.d.ts +4 -4
- package/dist/elysia/index.js +7 -7
- package/dist/{errors-DPdqI50v.d.ts → errors-OD_4c__H.d.ts} +2 -2
- package/dist/{errors-Cz5zn0mW.js → errors-drnfxYtm.js} +3 -3
- package/dist/{evaluations-tz6D9A2G.js → evaluations-C9YJsv3H.js} +45 -7
- package/dist/eve/index.d.ts +5 -5
- package/dist/eve/index.js +5 -5
- package/dist/express/index.d.ts +4 -4
- package/dist/express/index.js +8 -7
- package/dist/fastify/index.d.ts +15 -5
- package/dist/fastify/index.js +9 -29
- package/dist/fields-fwfXN01n.js +1016 -0
- package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
- package/dist/{fixtures-ht28Cplr.js → fixtures-h2eG2OZV.js} +5 -6
- package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-7REQZe0H.js} +32 -22
- package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
- package/dist/grantee-CXY-0C87.js +254 -0
- package/dist/grantee-conditions-MQs5NIuX.js +270 -0
- package/dist/grantee-match-fxYp9Nmt.js +141 -0
- package/dist/{helpers-H_KARDVC.js → helpers-C9YmyBIa.js} +7 -6
- package/dist/hono/index.d.ts +4 -4
- package/dist/hono/index.js +8 -8
- package/dist/hosted-CBHgkzDl.js +655 -0
- package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
- package/dist/{http-BIhmLfCc.js → http-BEbdMZ05.js} +2 -24
- package/dist/{rls-import-BTFVk3jH.js → import-BFIlhetD.js} +86 -80
- package/dist/{openapi-import-cmwetbzw.js → import-UHk1N7vH.js} +6 -7
- package/dist/{index-C8z4RjQV.d.ts → index-BwMe94Xq.d.ts} +1 -1
- package/dist/{index-0nNqHspG.d.ts → index-DgM7w96K.d.ts} +2 -2
- package/dist/{index-C7Tw7MaW.d.ts → index-Dz7didpC.d.ts} +3 -3
- package/dist/{index-BNc7Wer_.d.ts → index-UdVQgq48.d.ts} +105 -38
- package/dist/index.d.ts +15 -13
- package/dist/index.js +22 -22
- package/dist/{instance-options-jARdwfPN.d.ts → instance-options-plkrsAog.d.ts} +1 -1
- package/dist/{json-schema-BsAflUGW.js → json-schema-DBkdUuCO.js} +1 -3
- package/dist/jwt/index.d.ts +4 -4
- package/dist/jwt/index.js +13 -13
- package/dist/{kernel-_RRHmnMY.js → kernel-CB3LrIJM.js} +81 -32
- package/dist/kysely/index.d.ts +2 -2
- package/dist/kysely/index.js +6 -14
- package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
- package/dist/load-B_shHrmd.js +2 -0
- package/dist/{load-DhWEB9P1.js → load-C3_oTHyO.js} +11 -3
- package/dist/{manifest-iaoBXHKc.js → manifest-BngWKryL.js} +5 -18
- package/dist/{map-claims-Z4QZOiFF.js → map-claims-AVlBaJoa.js} +7 -16
- package/dist/{map-error-pgUWNNtG.js → map-error-BE27PYkE.js} +5 -5
- package/dist/{markers-B0hYLvMD.js → markers-CiwAOZtO.js} +6 -2
- package/dist/{may-use-DndQ5tQ-.js → may-use-BOn_dR_w.js} +4 -3
- package/dist/mcp/index.d.ts +26 -9
- package/dist/mcp/index.js +456 -446
- package/dist/{rls-migrate-CN4TlJTO.js → migrate-BwlB5-Wk.js} +75 -15
- package/dist/nest/index.d.ts +4 -4
- package/dist/nest/index.js +11 -12
- package/dist/next/index.d.ts +31 -8
- package/dist/next/index.js +29 -33
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +1 -1
- package/dist/node/index.d.ts +4 -4
- package/dist/node/index.js +4 -4
- package/dist/ocsf-uYFXGVw2.js +115 -0
- package/dist/openai/index.d.ts +5 -5
- package/dist/openai/index.js +4 -4
- package/dist/openapi/index.d.ts +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-CqqEbHsL.js → openapi-BJdBmYDG.js} +19 -23
- package/dist/{openapi-BqRBvAng.js → openapi-DBzHQbov.js} +2 -5
- package/dist/{openapi-l0xYHkcv.js → openapi-DcQ1Kg40.js} +3 -3
- package/dist/orpc/index.d.ts +5 -5
- package/dist/orpc/index.js +9 -11
- package/dist/otel/index.d.ts +2 -2
- package/dist/otel/index.js +26 -23
- package/dist/output-files-CgIsvdSa.js +140 -0
- package/dist/{rls-ownership-RuFqaqGz.js → ownership-BgnbOJFT.js} +387 -379
- package/dist/{package-root-zGkxO3oN.js → package-root-DtNnpmUd.js} +3 -2
- package/dist/{parse-BuyjxhZV.js → parse-CVFQHk6c.js} +3 -3
- package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-C-q02Pal.js} +2 -4
- package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DvYUR0QL.d.ts} +1 -1
- package/dist/pdp/index.d.ts +2 -2
- package/dist/pdp/index.js +8 -9
- package/dist/{permdock-CbgvLnuo.js → permdock-L26udHSk.js} +1387 -1220
- package/dist/{rls-fields-74oZPXWL.js → permission-keys-B26jeyUv.js} +1289 -2066
- package/dist/permission-registry-BsblpB-z.js +385 -0
- package/dist/{permissions-DttnqqxC.d.ts → permission-types-Dg39z9Kv.d.ts} +59 -31
- package/dist/{permissions-RJrovw98.js → permissions-CCJ6lmxB.js} +3 -3
- package/dist/{permissions-CDgvKG5L.d.ts → permissions-COH-KRgT.d.ts} +2 -2
- package/dist/{policy-IB6yxpC3.d.ts → policy-types-sp9P1iIV.d.ts} +661 -582
- package/dist/{policy-D2W7uv19.d.ts → policy-ur0BBK_e.d.ts} +2 -2
- package/dist/{powersync-DliZK_-y.js → powersync-BFSbSElQ.js} +2 -2
- package/dist/powersync-DCDgZgfZ.js +2 -0
- package/dist/{powersync-BylawD_U.js → powersync-cZe4kzhL.js} +26 -27
- package/dist/prisma/index.d.ts +1 -1
- package/dist/prisma/index.js +6 -12
- package/dist/{problem-B43ztKIm.js → problem-CnWraVhC.js} +8 -7
- package/dist/{problem-details-_AMOYz-w.js → problem-details-DECUnXAn.js} +2 -2
- package/dist/{problem-details-DX075l9V.d.ts → problem-details-rIIo8B2C.d.ts} +1 -1
- package/dist/{protected-BdnIEFkY.d.ts → protected-B3_P7xuw.d.ts} +9 -6
- package/dist/{protected-utU-SLuy.js → protected-BN7BUdY1.js} +20 -8
- package/dist/{provider-CSQd7m92.js → provider-Bi-0HNL3.js} +23 -10
- package/dist/{provider-DZY281jO.d.ts → provider-kwHC9CZD.d.ts} +1 -1
- package/dist/{provider-store-Cfbvo4DE.js → provider-store-Ctt-81Ni.js} +6 -3
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +5 -5
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +20 -11
- package/dist/react-native/index.js +33 -17
- package/dist/{refs-KYb4ntaz.js → refs-f3imMjNW.js} +1 -1
- package/dist/relation-walk-BXnlLtg2.js +186 -0
- package/dist/relations-CvmSpG67.js +163 -0
- package/dist/{replay-CYyiLiwO.d.ts → replay-COpmZUfx.d.ts} +1 -1
- package/dist/request-context-BVM7aWNs.js +71 -0
- package/dist/request-context-C2_1lchh.d.ts +5 -0
- package/dist/resource-relations-D1Un3pcz.js +243 -0
- package/dist/retire-BFcxL6un.js +240 -0
- package/dist/{rls-B4ur8nKQ.js → rls-55DQTuf5.js} +14 -2
- package/dist/{rls-DQtdgTHB.js → rls-BEktUaqg.js} +884 -484
- package/dist/{policy-xyWg7QdR.js → role-builder-B4GbKM-i.js} +235 -541
- package/dist/{rpc-DL9Sqvb1.js → rpc-nYC3cR_u.js} +15 -5
- package/dist/{run-DKDbM4Du.js → run-DMcNW_8T.js} +3 -3
- package/dist/{runtime-CB8gQFvS.js → runtime-D_tMXnsq.js} +1 -1
- package/dist/{saas-DhpJjnBA.js → saas-DtaA4JuW.js} +9 -8
- package/dist/{openapi-schema--ARjyfQr.js → schema-Bol6gqXA.js} +3 -5
- package/dist/{schema-D3MnDMxa.js → schema-DjQBcAP9.js} +4 -2
- package/dist/scim/index.d.ts +1 -1
- package/dist/scim/index.js +1 -1
- package/dist/{scim-BFGBBAqR.js → scim-vFR1O5oG.js} +6 -12
- package/dist/{scopes-CVdPrWN-.js → scopes-tvpa34ep.js} +10 -2
- package/dist/server/index.d.ts +18 -8
- package/dist/server/index.js +8 -8
- package/dist/{server-DlZ28Oz5.js → server-ZYEHtNHS.js} +4 -4
- package/dist/{create-B1d519XK.d.ts → server-types-DdqBwZN_.d.ts} +69 -33
- package/dist/shared-1uhI-e-b.js +294 -0
- package/dist/{shared-DRu8VFVM.js → shared-JhMeOsDF.js} +1 -1
- package/dist/{sink-Bd9PSyfq.js → sink-BTtmfPnE.js} +1 -1
- package/dist/{sink-B1fZzIMf.d.ts → sink-CPN4zLfs.d.ts} +1 -1
- package/dist/{skills-Biq5uup3.js → skills-BZdRQkxs.js} +4 -3
- package/dist/{skills-Bx-U0irG.js → skills-Dle4ZoLF.js} +2 -2
- package/dist/{snapshot-Enxd0n-s.js → snapshot-Dc9CHPME.js} +116 -13
- package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-Cod40D-X.js} +1 -1
- package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-J5T7t_vD.d.ts} +1 -1
- package/dist/solid/index.d.ts +22 -22
- package/dist/solid/index.js +40 -10
- package/dist/{sources-4p0-4xQm.js → sources-B1-b-R0t.js} +25 -26
- package/dist/{sources-BdOYLw9j.d.ts → sources-Dljy06zL.d.ts} +6 -2
- package/dist/{sql-xdg5f4Fi.js → sql-t8teWOfo.js} +19 -3
- package/dist/{src-BDwEZWrZ.js → src-Bb7no-hS.js} +194 -123
- package/dist/ssf/index.d.ts +3 -3
- package/dist/ssf/index.js +58 -61
- package/dist/store-9KO4BlkB.d.ts +147 -0
- package/dist/{store-DMAWwAJV.js → store-ILkLUj_o.js} +4 -4
- package/dist/{store-options-ByhZq1jV.js → store-options-CbUHDoSe.js} +16 -7
- package/dist/{stores-BT2pSCub.js → stores-BHXdSbw9.js} +7 -3
- package/dist/stores-Cxz_80g-.d.ts +31 -0
- package/dist/{stream-DDpvynSB.js → stream-BMui98KK.js} +2 -1
- package/dist/{stream-D1RXubw2.d.ts → stream-Dwsg6WwE.d.ts} +2 -2
- package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
- package/dist/supabase/index.d.ts +4 -4
- package/dist/supabase/index.js +3 -3
- package/dist/supabase/middleware.d.ts +11 -5
- package/dist/supabase/middleware.js +10 -7
- package/dist/{supabase-C0qESOa-.js → supabase-C1TWlnYt.js} +36 -52
- package/dist/{supabase-Boqb9a_Y.js → supabase-Dbqr141s.js} +2 -2
- package/dist/supabase-hook-BULg7Ut_.js +2 -0
- package/dist/{supabase-hook-CSLQ4zJ3.js → supabase-hook-Cva3ikXz.js} +731 -782
- package/dist/{supautils-Cn4R4cJ4.js → supautils-B6HYL9eL.js} +2 -2
- package/dist/svelte/Protected.svelte +9 -2
- package/dist/svelte/index.d.ts +6 -6
- package/dist/svelte/index.js +25 -11
- package/dist/svelte/runtime.d.ts +10 -3
- package/dist/svelte/runtime.js +4 -3
- package/dist/svelte/source.d.ts +6 -6
- package/dist/svelte/source.js +4 -4
- package/dist/{validity-uOp8UpYn.js → tenancy-DshjqY0n.js} +3 -39
- package/dist/terminal/index.d.ts +5 -5
- package/dist/terminal/index.js +18 -34
- package/dist/testing/index.d.ts +89 -77
- package/dist/testing/index.js +1276 -1262
- 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/thenable-QSmvt-vZ.js +65 -0
- package/dist/{token-CIWxJOCh.js → token-Bx9qy30p.js} +1 -1
- package/dist/tool-refusal-CQ7OEia9.js +41 -0
- package/dist/trpc/index.d.ts +5 -5
- package/dist/trpc/index.js +7 -8
- package/dist/{types-DbjRN55U.d.ts → types-7rJVLWe5.d.ts} +82 -4
- package/dist/{types-aqOHTHcp.d.ts → types-Br9IXUBG.d.ts} +1 -1
- package/dist/types-ByUo56un.d.ts +88 -0
- package/dist/{types-Cq_H8N_X.d.ts → types-C0dee0Qf.d.ts} +2 -2
- package/dist/{types-DxiCGmmd.d.ts → types-CIlTojhq.d.ts} +2 -2
- package/dist/{types-QCuYJh4q.d.ts → types-CUcFZfpU.d.ts} +1 -1
- package/dist/{types-C97IskBw.d.ts → types-CqrjB9o6.d.ts} +1 -1
- package/dist/{types-BnIEz1sU.d.ts → types-DHrmBJLH.d.ts} +24 -6
- package/dist/{types-Dnemwd77.d.ts → types-DpWfQdSh.d.ts} +5 -1
- package/dist/{types-Bvc4oWT6.d.ts → types-DvupZE0w.d.ts} +1 -1
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +1 -1
- package/dist/{usage-GTixvDDU.js → usage-4G6t643F.js} +2 -2
- package/dist/{usage-ItS7nLcl.js → usage-CG9nOYZw.js} +7 -7
- package/dist/usage-iFLJ0-rr.js +2 -0
- package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-BSSw0l_8.d.ts} +1 -1
- package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
- package/dist/validity-dVrUEPJt.js +62 -0
- package/dist/{verifier-BxBo6I44.js → verifier-DMr_Zmb_.js} +51 -15
- package/dist/{rls-verify-CVQTHTh5.js → verify-BztUB3GX.js} +668 -239
- package/dist/{version-BvacVTZR.js → version-BEogC-rB.js} +1 -1
- package/dist/{views-Soe7bGSC.js → views-CJsrBS1f.js} +23 -1
- package/dist/{vocabulary-DA8a8zif.d.ts → vocabulary-hhEFLFoL.d.ts} +15 -6
- package/dist/vue/index.d.ts +24 -19
- package/dist/vue/index.js +24 -8
- package/dist/watch-C8TpsAl4.js +2 -0
- package/dist/{watch-DVBJ9cBq.js → watch-zeucB7eF.js} +6 -6
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +13 -37
- package/dist/wrap-Cr93qqdP.d.ts +14 -0
- package/dist/wrap-DWsU_5Bd.js +26 -0
- package/package.json +8 -8
- package/schemas/catalog-v1.json +6 -0
- package/skills/permdock-credentials/SKILL.md +1 -1
- package/skills/permdock-credentials/references/tokens-keys-links.md +7 -2
- package/skills/permdock-data/references/queries-and-rls.md +4 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
- package/skills/permdock-wire/SKILL.md +2 -2
- package/skills/permdock-wire/references/adapters.md +9 -1
- package/skills/permdock-wire/references/policy-options.md +10 -1
- package/dist/catalog-CRW5Mx_B.js +0 -2
- package/dist/collect-DSWOk19R.js +0 -2
- package/dist/create-CMwIl-w2.js +0 -253
- package/dist/delegation-D8pUEuce.js +0 -103
- package/dist/doctor-collect-CHk6XWOA.js +0 -665
- package/dist/doctor-igIiM_Hl.js +0 -1194
- package/dist/evaluation-items-CfCCejQd.js +0 -40
- package/dist/fields-BMl1RxoX.js +0 -26
- package/dist/grantee-Cv5Sogfv.js +0 -602
- package/dist/hosted-DJQw46hn.js +0 -266
- package/dist/instance-options-Dhhcnm3y.js +0 -22
- package/dist/load-DwJUafYN.js +0 -2
- package/dist/map-AcxK9o_D.js +0 -147
- package/dist/permissions-DVoF6dMt.js +0 -662
- package/dist/powersync-e_AZrKbg.js +0 -2
- package/dist/protected-xAr24FEC.d.ts +0 -40
- package/dist/relations-cjOL01hZ.js +0 -505
- package/dist/rls-graph-CHwx0V8q.js +0 -435
- package/dist/rls-shared-Cewd_-zI.js +0 -495
- package/dist/store-jOBYpiXC.d.ts +0 -36
- package/dist/stores-C0IMQrGI.d.ts +0 -52
- package/dist/supabase-config-BpPXt3x1.js +0 -472
- package/dist/supabase-hook-6sYLXg_u.js +0 -2
- package/dist/thenable-okc2xDsj.js +0 -10
- package/dist/types-CKNSyIf_.d.ts +0 -52
- package/dist/usage-DorAJiU_.js +0 -2
- package/dist/watch-BwxLTYMZ.js +0 -2
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
|
+
import { s as ownGet } from "./thenable-QSmvt-vZ.js";
|
|
3
|
+
import { n as relationId } from "./relations-CvmSpG67.js";
|
|
4
|
+
//#region src/core/relation-walk.ts
|
|
5
|
+
/** Whether the holder's period covers `now`. */
|
|
6
|
+
function activeNow(holder, now) {
|
|
7
|
+
return (holder.startsAt === void 0 || holder.startsAt <= now) && (holder.expiresAt === void 0 || holder.expiresAt > now);
|
|
8
|
+
}
|
|
9
|
+
function holdsNow(holder, principalId, now) {
|
|
10
|
+
return "principal" in holder && holder.principal.id === principalId && activeNow(holder, now);
|
|
11
|
+
}
|
|
12
|
+
/** Where the walk starts after following `hops`, or why it cannot. */
|
|
13
|
+
function followHops(condition, first, reader) {
|
|
14
|
+
const hops = condition.hops ?? [];
|
|
15
|
+
let id = first;
|
|
16
|
+
for (let index = 1; index < hops.length; index += 1) {
|
|
17
|
+
const from = hops[index - 1];
|
|
18
|
+
const hop = hops[index];
|
|
19
|
+
if (from === void 0 || hop === void 0)
|
|
20
|
+
/* v8 ignore next */
|
|
21
|
+
return;
|
|
22
|
+
const chain = reader.chain({
|
|
23
|
+
resource: from.resource,
|
|
24
|
+
id,
|
|
25
|
+
depth: 1,
|
|
26
|
+
through: hop.link
|
|
27
|
+
});
|
|
28
|
+
if (chain === "pending" || chain === "failed") return "relation-unavailable";
|
|
29
|
+
const next = chain.ancestors[0]?.id;
|
|
30
|
+
if (next === void 0) return;
|
|
31
|
+
id = next;
|
|
32
|
+
}
|
|
33
|
+
return { id };
|
|
34
|
+
}
|
|
35
|
+
function restrictedAbove(condition, row, reader) {
|
|
36
|
+
const above = condition.restrictedAncestors;
|
|
37
|
+
if (above === void 0) return false;
|
|
38
|
+
const parent = relationId(ownGet(row, above.parent));
|
|
39
|
+
if (parent === void 0) return false;
|
|
40
|
+
const chain = reader.chain({
|
|
41
|
+
resource: above.resource,
|
|
42
|
+
id: parent,
|
|
43
|
+
depth: above.depth - 1
|
|
44
|
+
});
|
|
45
|
+
if (chain === "pending" || chain === "failed") return "relation-unavailable";
|
|
46
|
+
if (chain.restricted === true || chain.ancestors.some((ancestor) => ancestor.restricted === true)) return;
|
|
47
|
+
const own = relationId(ownGet(row, above.id));
|
|
48
|
+
const ids = [
|
|
49
|
+
...own === void 0 ? [] : [own],
|
|
50
|
+
parent,
|
|
51
|
+
...chain.ancestors.map((ancestor) => ancestor.id)
|
|
52
|
+
];
|
|
53
|
+
if (new Set(ids).size !== ids.length || chain.truncated === true || chain.ancestors.length > above.depth - 1) return "relation-depth";
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/** The instances a graph relation reads: the start, then ancestors until a restricted one or `depth`. */
|
|
57
|
+
function relationWalk(condition, row, reader) {
|
|
58
|
+
if (!isRecord(row)) return;
|
|
59
|
+
const first = relationId(ownGet(row, condition.field));
|
|
60
|
+
if (first === void 0) return;
|
|
61
|
+
const rowRestricted = condition.restricted !== void 0 && ownGet(row, condition.restricted) === true;
|
|
62
|
+
const hopped = condition.hops !== void 0 && condition.hops.length > 0;
|
|
63
|
+
if ((condition.parent === true || hopped) && rowRestricted) return;
|
|
64
|
+
const above = restrictedAbove(condition, row, reader);
|
|
65
|
+
if (above !== false) return above;
|
|
66
|
+
const start = hopped ? followHops(condition, first, reader) : { id: first };
|
|
67
|
+
if (start === void 0 || start === "relation-unavailable") return start;
|
|
68
|
+
const id = start.id;
|
|
69
|
+
if (condition.depth === 0 || rowRestricted) return {
|
|
70
|
+
ids: [id],
|
|
71
|
+
truncated: false
|
|
72
|
+
};
|
|
73
|
+
const chain = reader.chain({
|
|
74
|
+
resource: condition.resource,
|
|
75
|
+
id,
|
|
76
|
+
depth: condition.depth
|
|
77
|
+
});
|
|
78
|
+
if (chain === "pending" || chain === "failed") return "relation-unavailable";
|
|
79
|
+
const ids = [id];
|
|
80
|
+
const stops = condition.passRestricted !== true;
|
|
81
|
+
let stopped = stops && chain.restricted === true;
|
|
82
|
+
if (!stopped) for (const ancestor of chain.ancestors.slice(0, condition.depth)) {
|
|
83
|
+
ids.push(ancestor.id);
|
|
84
|
+
if (stops && ancestor.restricted === true) {
|
|
85
|
+
stopped = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (new Set(ids).size !== ids.length) return "relation-depth";
|
|
90
|
+
return {
|
|
91
|
+
ids,
|
|
92
|
+
truncated: !stopped && (chain.ancestors.length > condition.depth || chain.truncated === true)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Whether the subject holds the relation on the row's instance or an
|
|
97
|
+
* ancestor within `depth`. A cycle is `relation-depth`; so is a chain that
|
|
98
|
+
* goes on past `depth` with no holder below it. Any read the instance could
|
|
99
|
+
* not answer is `relation-unavailable` unless a holder was found elsewhere.
|
|
100
|
+
*/
|
|
101
|
+
function resolveRelated(condition, row, subject, now, reader, permitted) {
|
|
102
|
+
if (condition.permission !== void 0) return resolveInherited(condition, condition.permission, row, reader, permitted);
|
|
103
|
+
const principal = subject.principal;
|
|
104
|
+
if (principal === null) return false;
|
|
105
|
+
const walk = relationWalk(condition, row, reader);
|
|
106
|
+
if (walk === void 0) return false;
|
|
107
|
+
if (walk === "relation-depth" || walk === "relation-unavailable") return walk;
|
|
108
|
+
if (condition.ids !== void 0) {
|
|
109
|
+
const held = new Set(condition.ids);
|
|
110
|
+
if (walk.ids.some((id) => held.has(id))) return true;
|
|
111
|
+
return walk.truncated ? "relation-depth" : false;
|
|
112
|
+
}
|
|
113
|
+
let unavailable = false;
|
|
114
|
+
let deep = false;
|
|
115
|
+
const explored = /* @__PURE__ */ new Map();
|
|
116
|
+
for (const id of walk.ids) {
|
|
117
|
+
const verdict = holdsRelation({
|
|
118
|
+
resource: condition.resource,
|
|
119
|
+
id,
|
|
120
|
+
relation: condition.relation
|
|
121
|
+
}, principal.id, now, reader, 16, /* @__PURE__ */ new Set(), explored);
|
|
122
|
+
if (verdict === true) return true;
|
|
123
|
+
if (verdict === "relation-unavailable") unavailable = true;
|
|
124
|
+
else if (verdict === "relation-depth") deep = true;
|
|
125
|
+
}
|
|
126
|
+
if (unavailable) return "relation-unavailable";
|
|
127
|
+
return walk.truncated || deep ? "relation-depth" : false;
|
|
128
|
+
}
|
|
129
|
+
function resolveInherited(condition, permission, row, reader, permitted) {
|
|
130
|
+
const walk = relationWalk(condition, row, reader);
|
|
131
|
+
if (walk === void 0) return false;
|
|
132
|
+
if (walk === "relation-depth" || walk === "relation-unavailable") return walk;
|
|
133
|
+
if (permitted === void 0) return "relation-unavailable";
|
|
134
|
+
let unknown = false;
|
|
135
|
+
for (const id of walk.ids) {
|
|
136
|
+
const verdict = permitted(condition.resource, id, permission);
|
|
137
|
+
if (verdict === true) return true;
|
|
138
|
+
if (verdict !== false && unknown === false) unknown = verdict;
|
|
139
|
+
}
|
|
140
|
+
return unknown;
|
|
141
|
+
}
|
|
142
|
+
function groupKey(group) {
|
|
143
|
+
return JSON.stringify([
|
|
144
|
+
group.resource,
|
|
145
|
+
group.id,
|
|
146
|
+
group.relation
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Whether the principal holds `at.relation` on one instance, directly or
|
|
151
|
+
* through a group whose members hold it. Groups of `at`'s own resource nest
|
|
152
|
+
* at most `budget` deep; a group of another resource gets a fresh budget
|
|
153
|
+
* (declarations rule out cycles across resources). A group met twice on one
|
|
154
|
+
* path, or one past the budget, is `relation-depth`. `explored` holds the
|
|
155
|
+
* budget each group was walked with across the whole walk: a group already
|
|
156
|
+
* walked with as much budget adds nothing, since its verdict already reached
|
|
157
|
+
* the caller, so shared sub-groups cost one walk rather than one per path.
|
|
158
|
+
*/
|
|
159
|
+
function holdsRelation(at, principalId, now, reader, budget, seen, explored) {
|
|
160
|
+
const key = groupKey(at);
|
|
161
|
+
const walked = explored.get(key);
|
|
162
|
+
if (walked !== void 0 && walked >= budget) return false;
|
|
163
|
+
explored.set(key, budget);
|
|
164
|
+
const holders = reader.holders(at);
|
|
165
|
+
if (holders === "pending" || holders === "failed") return "relation-unavailable";
|
|
166
|
+
if (holders.some((holder) => holdsNow(holder, principalId, now))) return true;
|
|
167
|
+
const path = new Set(seen).add(key);
|
|
168
|
+
let unavailable = false;
|
|
169
|
+
let deep = false;
|
|
170
|
+
for (const holder of holders) {
|
|
171
|
+
if (!("group" in holder) || !activeNow(holder, now)) continue;
|
|
172
|
+
const nested = holder.group.resource === at.resource;
|
|
173
|
+
if (nested && budget <= 0 || path.has(groupKey(holder.group))) {
|
|
174
|
+
deep = true;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const verdict = holdsRelation(holder.group, principalId, now, reader, nested ? budget - 1 : 16, path, explored);
|
|
178
|
+
if (verdict === true) return true;
|
|
179
|
+
if (verdict === "relation-unavailable") unavailable = true;
|
|
180
|
+
else if (verdict === "relation-depth") deep = true;
|
|
181
|
+
}
|
|
182
|
+
if (unavailable) return "relation-unavailable";
|
|
183
|
+
return deep ? "relation-depth" : false;
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
export { resolveRelated as n, relationWalk as t };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
|
+
import { n as isThenable } from "./thenable-QSmvt-vZ.js";
|
|
3
|
+
import { t as idText } from "./ids-DC4V4CFu.js";
|
|
4
|
+
import { a as expandRelation } from "./resource-relations-D1Un3pcz.js";
|
|
5
|
+
//#region src/core/relations.ts
|
|
6
|
+
function isInstant(value) {
|
|
7
|
+
return value === void 0 || typeof value === "number" && Number.isFinite(value);
|
|
8
|
+
}
|
|
9
|
+
function asChain(value) {
|
|
10
|
+
if (!isRecord(value) || !Array.isArray(value["ancestors"])) return;
|
|
11
|
+
const ancestors = [];
|
|
12
|
+
for (const item of value["ancestors"]) {
|
|
13
|
+
if (!isRecord(item) || typeof item["id"] !== "string" || item["id"] === "") return;
|
|
14
|
+
ancestors.push(item["restricted"] === true ? {
|
|
15
|
+
id: item["id"],
|
|
16
|
+
restricted: true
|
|
17
|
+
} : { id: item["id"] });
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
ancestors,
|
|
21
|
+
...value["restricted"] === true ? { restricted: true } : {},
|
|
22
|
+
...value["truncated"] === true ? { truncated: true } : {}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function asRow(value) {
|
|
26
|
+
if (value === null || value === void 0) return { row: null };
|
|
27
|
+
return isRecord(value) ? { row: value } : void 0;
|
|
28
|
+
}
|
|
29
|
+
function asGroup(value) {
|
|
30
|
+
if (!isRecord(value) || typeof value["resource"] !== "string" || typeof value["id"] !== "string" || typeof value["relation"] !== "string" || value["id"] === "") return;
|
|
31
|
+
return {
|
|
32
|
+
resource: value["resource"],
|
|
33
|
+
id: value["id"],
|
|
34
|
+
relation: value["relation"]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function asHolders(value) {
|
|
38
|
+
if (!Array.isArray(value)) return;
|
|
39
|
+
const holders = [];
|
|
40
|
+
for (const item of value) {
|
|
41
|
+
if (!isRecord(item) || !isInstant(item["startsAt"]) || !isInstant(item["expiresAt"])) return;
|
|
42
|
+
const period = {
|
|
43
|
+
...item["startsAt"] === void 0 ? {} : { startsAt: item["startsAt"] },
|
|
44
|
+
...item["expiresAt"] === void 0 ? {} : { expiresAt: item["expiresAt"] }
|
|
45
|
+
};
|
|
46
|
+
if (isRecord(item["principal"]) && typeof item["principal"]["id"] === "string") {
|
|
47
|
+
holders.push({
|
|
48
|
+
principal: { id: item["principal"]["id"] },
|
|
49
|
+
...period
|
|
50
|
+
});
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const group = asGroup(item["group"]);
|
|
54
|
+
if (group === void 0) return;
|
|
55
|
+
holders.push({
|
|
56
|
+
group,
|
|
57
|
+
...period
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return holders;
|
|
61
|
+
}
|
|
62
|
+
function read(cache, key, call, validate) {
|
|
63
|
+
const entry = cache.get(key);
|
|
64
|
+
if (entry !== void 0) return entry.state === "ready" ? entry.value : entry.state;
|
|
65
|
+
if (call === void 0) return "failed";
|
|
66
|
+
const settle = (value) => {
|
|
67
|
+
const valid = validate(value);
|
|
68
|
+
cache.set(key, valid === void 0 ? { state: "failed" } : {
|
|
69
|
+
state: "ready",
|
|
70
|
+
value: valid
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
let result;
|
|
74
|
+
try {
|
|
75
|
+
result = call();
|
|
76
|
+
} catch {
|
|
77
|
+
cache.set(key, { state: "failed" });
|
|
78
|
+
return "failed";
|
|
79
|
+
}
|
|
80
|
+
if (isThenable(result)) {
|
|
81
|
+
const promise = Promise.resolve(result).then(settle, () => {
|
|
82
|
+
cache.set(key, { state: "failed" });
|
|
83
|
+
});
|
|
84
|
+
cache.set(key, {
|
|
85
|
+
state: "pending",
|
|
86
|
+
promise
|
|
87
|
+
});
|
|
88
|
+
return "pending";
|
|
89
|
+
}
|
|
90
|
+
settle(result);
|
|
91
|
+
const settled = cache.get(key);
|
|
92
|
+
return settled?.state === "ready" ? settled.value : "failed";
|
|
93
|
+
}
|
|
94
|
+
function relationReader(source, cache, resources) {
|
|
95
|
+
const concrete = (resource, id, relation) => read(cache, JSON.stringify([
|
|
96
|
+
"holders",
|
|
97
|
+
resource,
|
|
98
|
+
id,
|
|
99
|
+
relation
|
|
100
|
+
]), source === void 0 ? void 0 : () => source.related({
|
|
101
|
+
resource,
|
|
102
|
+
id,
|
|
103
|
+
relation
|
|
104
|
+
}), asHolders);
|
|
105
|
+
return {
|
|
106
|
+
available: source !== void 0,
|
|
107
|
+
row(query) {
|
|
108
|
+
const load = source?.row?.bind(source);
|
|
109
|
+
return read(cache, JSON.stringify([
|
|
110
|
+
"row",
|
|
111
|
+
query.resource,
|
|
112
|
+
query.id
|
|
113
|
+
]), load === void 0 ? void 0 : () => load({
|
|
114
|
+
resource: query.resource,
|
|
115
|
+
id: query.id
|
|
116
|
+
}), asRow);
|
|
117
|
+
},
|
|
118
|
+
chain(query) {
|
|
119
|
+
const through = query.through ?? "parent";
|
|
120
|
+
return read(cache, JSON.stringify([
|
|
121
|
+
"chain",
|
|
122
|
+
query.resource,
|
|
123
|
+
query.id,
|
|
124
|
+
query.depth,
|
|
125
|
+
through
|
|
126
|
+
]), source === void 0 ? void 0 : () => source.ancestors({
|
|
127
|
+
resource: query.resource,
|
|
128
|
+
id: query.id,
|
|
129
|
+
depth: query.depth,
|
|
130
|
+
through
|
|
131
|
+
}), asChain);
|
|
132
|
+
},
|
|
133
|
+
holders(query) {
|
|
134
|
+
const node = resources?.get(query.resource);
|
|
135
|
+
const names = node === void 0 ? [query.relation] : expandRelation(node, query.relation);
|
|
136
|
+
const out = [];
|
|
137
|
+
let unread;
|
|
138
|
+
for (const name of names) {
|
|
139
|
+
const answer = concrete(query.resource, query.id, name);
|
|
140
|
+
if (answer === "pending" || answer === "failed") {
|
|
141
|
+
unread = answer === "failed" || unread === "failed" ? "failed" : "pending";
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
for (const holder of answer) out.push({
|
|
145
|
+
...holder,
|
|
146
|
+
relation: name
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return unread ?? out;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** The loads still in flight, for an instance that awaits them before deciding. */
|
|
154
|
+
function pendingRelations(cache) {
|
|
155
|
+
const out = [];
|
|
156
|
+
for (const entry of cache.values()) if (entry.state === "pending") out.push(entry.promise);
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
function relationId(value) {
|
|
160
|
+
return idText(value);
|
|
161
|
+
}
|
|
162
|
+
//#endregion
|
|
163
|
+
export { relationId as n, relationReader as r, pendingRelations as t };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { t as assertAppData } from "./app-data-CV8pqqMi.js";
|
|
2
|
+
import { n as wrapPermDock } from "./wrap-DWsU_5Bd.js";
|
|
3
|
+
import { t as buildInstance } from "./build-instance-DIIV9DbC.js";
|
|
4
|
+
//#region src/core/request-context.ts
|
|
5
|
+
/**
|
|
6
|
+
* Errors an adapter raises around one instance (a `context` hook that threw,
|
|
7
|
+
* an `onDenied` that failed), fed to the instance's `on('error')` handlers and
|
|
8
|
+
* replayed to later ones, as core replays its own.
|
|
9
|
+
*/
|
|
10
|
+
function deliver(handler, error) {
|
|
11
|
+
try {
|
|
12
|
+
handler(error);
|
|
13
|
+
} catch {}
|
|
14
|
+
}
|
|
15
|
+
function errorChannel(queued) {
|
|
16
|
+
const errors = [...queued];
|
|
17
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
18
|
+
return {
|
|
19
|
+
report: (error) => {
|
|
20
|
+
errors.push(error);
|
|
21
|
+
for (const handler of handlers) deliver(handler, error);
|
|
22
|
+
},
|
|
23
|
+
attach: (permdock) => wrapPermDock(permdock, (inner) => ({ on: (event, handler) => {
|
|
24
|
+
const off = inner.on(event, handler);
|
|
25
|
+
if (event !== "error") return off;
|
|
26
|
+
handlers.add(handler);
|
|
27
|
+
for (const error of errors) deliver(handler, error);
|
|
28
|
+
return () => {
|
|
29
|
+
off();
|
|
30
|
+
handlers.delete(handler);
|
|
31
|
+
};
|
|
32
|
+
} }))
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `buildInstance` with an error channel: the instance's `on('error')`
|
|
37
|
+
* replays `errors` raised while resolving the request, and `report` adds
|
|
38
|
+
* more. Kept beside `errorChannel`, so adapters without one do not bundle it.
|
|
39
|
+
*/
|
|
40
|
+
async function buildReportingInstance(input) {
|
|
41
|
+
const channel = errorChannel(input.errors);
|
|
42
|
+
const { wrap } = input;
|
|
43
|
+
return {
|
|
44
|
+
...await buildInstance({
|
|
45
|
+
...input,
|
|
46
|
+
wrap: (created) => {
|
|
47
|
+
const attached = channel.attach(created);
|
|
48
|
+
return wrap === void 0 ? attached : wrap(attached);
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
report: channel.report
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Runs an adapter's `context` hook. A throw, or a value that is not a JSON
|
|
56
|
+
* object, adds no context and lands in `errors`.
|
|
57
|
+
*/
|
|
58
|
+
async function requestContext(hook, input, errors) {
|
|
59
|
+
if (hook === void 0) return;
|
|
60
|
+
try {
|
|
61
|
+
const value = await hook(input);
|
|
62
|
+
if (value === void 0) return;
|
|
63
|
+
assertAppData(value, "context");
|
|
64
|
+
return value;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
errors.push(error);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { requestContext as n, buildReportingInstance as t };
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { a as assertSafeKey } from "./thenable-QSmvt-vZ.js";
|
|
2
|
+
//#region src/core/resource-relations.ts
|
|
3
|
+
/** The edge's group resources with their relation and id column. */
|
|
4
|
+
function groupEntries(spec) {
|
|
5
|
+
const groups = spec.groups;
|
|
6
|
+
if (groups === void 0) return [];
|
|
7
|
+
return Object.entries(groups.resources).map(([resource, group]) => typeof group === "string" ? {
|
|
8
|
+
resource,
|
|
9
|
+
relation: group,
|
|
10
|
+
subject: spec.subject ?? "user_id",
|
|
11
|
+
typed: false
|
|
12
|
+
} : {
|
|
13
|
+
resource,
|
|
14
|
+
relation: group.relation,
|
|
15
|
+
subject: group.subject ?? spec.subject ?? "user_id",
|
|
16
|
+
typed: group.subject !== void 0
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function restrictedStops(node, path) {
|
|
20
|
+
return node?.restricted !== void 0 && (node.restrictedStops === void 0 || node.restrictedStops.includes(path));
|
|
21
|
+
}
|
|
22
|
+
function restrictedFor(node, path) {
|
|
23
|
+
return restrictedStops(node, path) ? node?.restricted : void 0;
|
|
24
|
+
}
|
|
25
|
+
function isFieldRelation(relation) {
|
|
26
|
+
return relation !== void 0 && "field" in relation;
|
|
27
|
+
}
|
|
28
|
+
function isEdgeRelation(relation) {
|
|
29
|
+
return relation !== void 0 && "edge" in relation;
|
|
30
|
+
}
|
|
31
|
+
function isPrincipalRelation(relation) {
|
|
32
|
+
return relation !== void 0 && "principal" in relation;
|
|
33
|
+
}
|
|
34
|
+
function isComputedRelation(relation) {
|
|
35
|
+
return relation !== void 0 && !("field" in relation) && !("edge" in relation) && !("principal" in relation);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The relations whose holders hold `name` on `node`: `name` itself when it
|
|
39
|
+
* has a source, then everything it includes, transitively, each once.
|
|
40
|
+
* `definePermissions` rejects cycles and unknown names, so this terminates.
|
|
41
|
+
*/
|
|
42
|
+
function expandRelation(node, name) {
|
|
43
|
+
const out = [];
|
|
44
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45
|
+
const visit = (current) => {
|
|
46
|
+
if (seen.has(current)) return;
|
|
47
|
+
seen.add(current);
|
|
48
|
+
const spec = node?.relations[current];
|
|
49
|
+
if (spec === void 0) return;
|
|
50
|
+
if (!isComputedRelation(spec)) out.push(current);
|
|
51
|
+
for (const included of spec.includes ?? []) visit(included);
|
|
52
|
+
};
|
|
53
|
+
visit(name);
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/** Whether `node` names itself as its parent (nested folders, sub-teams, reporting lines). */
|
|
57
|
+
function isSelfParented(node) {
|
|
58
|
+
return node?.parent !== void 0 && node.parent.resource === node.name;
|
|
59
|
+
}
|
|
60
|
+
const RESOURCE_NAME = /^[A-Za-z][A-Za-z0-9_-]*$/u;
|
|
61
|
+
/** Level names ride claims and grant keys (`key@level`), so they stay SQL- and claim-safe. */
|
|
62
|
+
const LEVEL_NAME = /^[a-z][a-z0-9_]*$/u;
|
|
63
|
+
const EDGE_TABLE = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/u;
|
|
64
|
+
function restrictedStopsOf(name, option, parent, links) {
|
|
65
|
+
if (option.stops === void 0) return;
|
|
66
|
+
if (!Array.isArray(option.stops) || option.stops.length === 0) throw new Error(`PermDock: resource "${name}" restricted.stops must list 'parent' or link names`);
|
|
67
|
+
const stops = /* @__PURE__ */ new Set();
|
|
68
|
+
for (const path of option.stops) {
|
|
69
|
+
if (typeof path !== "string") throw new TypeError(`PermDock: resource "${name}" restricted.stops must list 'parent' or link names`);
|
|
70
|
+
if (path === "parent" ? parent === void 0 : !Object.hasOwn(links, path)) throw new Error(`PermDock: resource "${name}" restricted.stops names '${path}', which it does not declare`);
|
|
71
|
+
stops.add(path);
|
|
72
|
+
}
|
|
73
|
+
return Object.freeze([...stops].toSorted());
|
|
74
|
+
}
|
|
75
|
+
function normaliseRelation(resourceName, relationName, spec) {
|
|
76
|
+
if (typeof spec === "string") {
|
|
77
|
+
assertSafeKey(spec, "relation field");
|
|
78
|
+
return { field: spec };
|
|
79
|
+
}
|
|
80
|
+
const label = `relation '${relationName}' on '${resourceName}'`;
|
|
81
|
+
const includes = normaliseIncludes(label, spec.includes);
|
|
82
|
+
const kinds = [
|
|
83
|
+
"field",
|
|
84
|
+
"edge",
|
|
85
|
+
"principal"
|
|
86
|
+
].filter((kind) => kind in spec);
|
|
87
|
+
if (kinds.length === 0 && includes !== void 0) return { includes };
|
|
88
|
+
if (kinds.length !== 1) throw new Error(`PermDock: ${label} needs exactly one of field, edge, principal or includes`);
|
|
89
|
+
const withIncludes = includes === void 0 ? {} : { includes };
|
|
90
|
+
if (isEdgeRelation(spec)) {
|
|
91
|
+
if (!EDGE_TABLE.test(spec.edge)) throw new Error(`PermDock: ${label} has an unsafe edge table '${spec.edge}'`);
|
|
92
|
+
for (const column of [
|
|
93
|
+
spec.object,
|
|
94
|
+
spec.subject,
|
|
95
|
+
spec.expiresAt
|
|
96
|
+
]) if (column !== void 0) assertSafeKey(column, "edge column");
|
|
97
|
+
return {
|
|
98
|
+
edge: spec.edge,
|
|
99
|
+
...spec.object === void 0 ? {} : { object: spec.object },
|
|
100
|
+
...spec.subject === void 0 ? {} : { subject: spec.subject },
|
|
101
|
+
...spec.expiresAt === void 0 ? {} : { expiresAt: spec.expiresAt },
|
|
102
|
+
...spec.match === void 0 ? {} : { match: normaliseMatch(label, spec.match) },
|
|
103
|
+
...spec.groups === void 0 ? {} : { groups: normaliseGroups(label, spec.groups) },
|
|
104
|
+
...withIncludes
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (isPrincipalRelation(spec)) {
|
|
108
|
+
assertSafeKey(spec.principal, "relation principal");
|
|
109
|
+
for (const column of [spec.period?.startsAt, spec.period?.expiresAt]) if (column !== void 0) assertSafeKey(column, "relation period");
|
|
110
|
+
return spec.period === void 0 ? {
|
|
111
|
+
principal: spec.principal,
|
|
112
|
+
...withIncludes
|
|
113
|
+
} : {
|
|
114
|
+
principal: spec.principal,
|
|
115
|
+
period: { ...spec.period },
|
|
116
|
+
...withIncludes
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const field = spec;
|
|
120
|
+
assertSafeKey(field.field, "relation field");
|
|
121
|
+
if (field.memberOf !== void 0 && includes !== void 0) throw new Error(`PermDock: ${label} is a memberOf relation and cannot include others`);
|
|
122
|
+
return field.memberOf === void 0 ? {
|
|
123
|
+
field: field.field,
|
|
124
|
+
...withIncludes
|
|
125
|
+
} : {
|
|
126
|
+
field: field.field,
|
|
127
|
+
memberOf: field.memberOf
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function normaliseIncludes(label, includes) {
|
|
131
|
+
if (includes === void 0) return;
|
|
132
|
+
if (!Array.isArray(includes) || includes.length === 0) throw new Error(`PermDock: ${label} includes must be a non-empty list of relation names`);
|
|
133
|
+
for (const name of includes) {
|
|
134
|
+
if (typeof name !== "string") throw new TypeError(`PermDock: ${label} includes a non-string name`);
|
|
135
|
+
assertSafeKey(name, "included relation");
|
|
136
|
+
}
|
|
137
|
+
return [...new Set(includes)];
|
|
138
|
+
}
|
|
139
|
+
function normaliseMatch(label, match) {
|
|
140
|
+
const out = {};
|
|
141
|
+
const columns = Object.keys(match);
|
|
142
|
+
if (columns.length === 0) throw new Error(`PermDock: ${label} match needs at least one column`);
|
|
143
|
+
for (const column of columns) {
|
|
144
|
+
assertSafeKey(column, "edge match column");
|
|
145
|
+
const value = match[column];
|
|
146
|
+
if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) throw new Error(`PermDock: ${label} match '${column}' must be a string, a finite number or a boolean`);
|
|
147
|
+
out[column] = value;
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
function normaliseGroups(label, groups) {
|
|
152
|
+
if (groups.column !== void 0) assertSafeKey(groups.column, "edge groups column");
|
|
153
|
+
const resources = {};
|
|
154
|
+
const names = Object.keys(groups.resources);
|
|
155
|
+
if (names.length === 0) throw new Error(`PermDock: ${label} groups needs at least one resource`);
|
|
156
|
+
for (const name of names) {
|
|
157
|
+
assertSafeKey(name, "group resource");
|
|
158
|
+
const group = groups.resources[name];
|
|
159
|
+
if (typeof group === "string") {
|
|
160
|
+
assertSafeKey(group, "group relation");
|
|
161
|
+
if (groups.column === void 0) throw new Error(`PermDock: ${label} groups '${name}' needs its own subject column, or groups needs a column naming the resource`);
|
|
162
|
+
resources[name] = group;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (group === null || typeof group !== "object") throw new TypeError(`PermDock: ${label} groups '${name}' must name a relation`);
|
|
166
|
+
const relationName = "relation" in group ? group.relation : void 0;
|
|
167
|
+
if (typeof relationName !== "string") throw new TypeError(`PermDock: ${label} groups '${name}' must name a relation`);
|
|
168
|
+
assertSafeKey(relationName, "group relation");
|
|
169
|
+
const subject = "subject" in group ? group.subject : void 0;
|
|
170
|
+
if (subject !== void 0 && typeof subject !== "string") throw new TypeError(`PermDock: ${label} groups '${name}' subject must be a column name`);
|
|
171
|
+
if (subject !== void 0) assertSafeKey(subject, "group subject column");
|
|
172
|
+
else if (groups.column === void 0) throw new Error(`PermDock: ${label} groups '${name}' needs its own subject column, or groups needs a column naming the resource`);
|
|
173
|
+
resources[name] = subject === void 0 ? { relation: relationName } : {
|
|
174
|
+
relation: relationName,
|
|
175
|
+
subject
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (groups.direct !== void 0 && groups.column === void 0) throw new Error(`PermDock: ${label} groups direct needs a column to compare it with`);
|
|
179
|
+
if (groups.direct !== void 0 && groups.direct in resources) throw new Error(`PermDock: ${label} groups direct '${groups.direct}' is also a group resource`);
|
|
180
|
+
return {
|
|
181
|
+
...groups.column === void 0 ? {} : { column: groups.column },
|
|
182
|
+
resources,
|
|
183
|
+
...groups.direct === void 0 ? {} : { direct: groups.direct }
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** An included relation must exist on the same resource, be tenancy-free and never include itself back. */
|
|
187
|
+
function assertIncludes(node) {
|
|
188
|
+
const state = /* @__PURE__ */ new Map();
|
|
189
|
+
const visit = (name, path) => {
|
|
190
|
+
if (state.get(name) === "done") return;
|
|
191
|
+
if (state.get(name) === "visiting") throw new Error(`PermDock: relation includes on '${node.name}' form a cycle: ${[...path, name].join(" -> ")}`);
|
|
192
|
+
state.set(name, "visiting");
|
|
193
|
+
for (const included of node.relations[name]?.includes ?? []) {
|
|
194
|
+
const target = node.relations[included];
|
|
195
|
+
if (target === void 0) throw new Error(`PermDock: relation '${name}' on '${node.name}' includes '${included}', which '${node.name}' does not declare`);
|
|
196
|
+
if (isFieldRelation(target) && target.memberOf !== void 0) throw new Error(`PermDock: relation '${name}' on '${node.name}' includes the memberOf relation '${included}'; scopes are tenancy, not the object graph`);
|
|
197
|
+
visit(included, [...path, name]);
|
|
198
|
+
}
|
|
199
|
+
state.set(name, "done");
|
|
200
|
+
};
|
|
201
|
+
for (const name of Object.keys(node.relations)) visit(name, []);
|
|
202
|
+
}
|
|
203
|
+
function assertGroupTarget(registry, node, relationName, spec, target, targetRelation) {
|
|
204
|
+
const label = `relation '${relationName}' on '${node.name}' groups '${target}'`;
|
|
205
|
+
const targetSpec = registry.get(target)?.relations[targetRelation];
|
|
206
|
+
if (targetSpec === void 0) throw new Error(`PermDock: ${label} names '${target}#${targetRelation}', which is not declared`);
|
|
207
|
+
if (isFieldRelation(targetSpec) && targetSpec.memberOf !== void 0) throw new Error(`PermDock: ${label} names a memberOf relation; scopes are tenancy, not the object graph`);
|
|
208
|
+
if (target === node.name && (targetRelation !== relationName || spec.includes !== void 0)) throw new Error(`PermDock: ${label}: a group on its own resource must name the same relation, which includes nothing`);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Links and group targets name declared resources and relations. Groups may
|
|
212
|
+
* nest within one resource (teams in teams) through the same relation, which
|
|
213
|
+
* then includes nothing, so SQL can walk it with one bounded recursive query;
|
|
214
|
+
* a group cycle across resources is rejected.
|
|
215
|
+
*/
|
|
216
|
+
function assertGraphTargets(registry) {
|
|
217
|
+
const edges = /* @__PURE__ */ new Map();
|
|
218
|
+
for (const node of registry.values()) {
|
|
219
|
+
for (const [linkName, link] of Object.entries(node.links)) if (!registry.has(link.resource)) throw new Error(`PermDock: link '${linkName}' on '${node.name}' names the undeclared resource '${link.resource}'`);
|
|
220
|
+
for (const [relationName, spec] of Object.entries(node.relations)) {
|
|
221
|
+
if (!isEdgeRelation(spec) || spec.groups === void 0) continue;
|
|
222
|
+
for (const { resource: target, relation: targetRelation } of groupEntries(spec)) {
|
|
223
|
+
assertGroupTarget(registry, node, relationName, spec, target, targetRelation);
|
|
224
|
+
if (target !== node.name) {
|
|
225
|
+
const out = edges.get(node.name) ?? /* @__PURE__ */ new Set();
|
|
226
|
+
out.add(target);
|
|
227
|
+
edges.set(node.name, out);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const state = /* @__PURE__ */ new Map();
|
|
233
|
+
const visit = (name) => {
|
|
234
|
+
if (state.get(name) === "done") return;
|
|
235
|
+
if (state.get(name) === "visiting") throw new Error(`PermDock: edge groups form a cycle across resources through '${name}'`);
|
|
236
|
+
state.set(name, "visiting");
|
|
237
|
+
for (const next of edges.get(name) ?? []) visit(next);
|
|
238
|
+
state.set(name, "done");
|
|
239
|
+
};
|
|
240
|
+
for (const name of edges.keys()) visit(name);
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
export { expandRelation as a, isEdgeRelation as c, isSelfParented as d, normaliseRelation as f, restrictedStopsOf as h, assertIncludes as i, isFieldRelation as l, restrictedStops as m, RESOURCE_NAME as n, groupEntries as o, restrictedFor as p, assertGraphTargets as r, isComputedRelation as s, LEVEL_NAME as t, isPrincipalRelation as u };
|