permdock 0.0.0 → 0.1.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 +268 -1
- package/dist/a2a/index.d.ts +178 -0
- package/dist/a2a/index.js +271 -0
- package/dist/ai-sdk/index.d.ts +74 -0
- package/dist/ai-sdk/index.js +137 -0
- package/dist/approval-required-TzNB9oQA.js +6 -0
- package/dist/approvals/index.d.ts +3 -0
- package/dist/approvals/index.js +4 -0
- package/dist/approvals-DaxY-Bdg.js +218 -0
- package/dist/approvers-aH0PJkmf.js +20 -0
- package/dist/arazzo-CTeVuXr5.js +70 -0
- package/dist/arazzo-aoTkqYsJ.js +54 -0
- package/dist/authzen/index.d.ts +48 -0
- package/dist/authzen/index.js +336 -0
- package/dist/better-auth/index.d.ts +92 -0
- package/dist/better-auth/index.js +324 -0
- package/dist/budget-JqcRqyhK.d.ts +11 -0
- package/dist/budget-NbEtbJC_.js +11 -0
- package/dist/capability-B68OFnZx.js +171 -0
- package/dist/capability-BDG_nkxt.d.ts +99 -0
- package/dist/catalog/index.d.ts +12 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog-B0E6bggc.js +48 -0
- package/dist/catalog-H-vFGwWK.js +55 -0
- package/dist/catalog-doc-BSo_Q_-6.js +298 -0
- package/dist/catalog-idsHc0vo.js +2 -0
- package/dist/catalog-path-CnI0XTFK.js +12 -0
- package/dist/claude-agent/index.d.ts +81 -0
- package/dist/claude-agent/index.js +83 -0
- package/dist/clerk/index.d.ts +56 -0
- package/dist/clerk/index.js +280 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.js +5 -0
- package/dist/cloud/index.d.ts +91 -0
- package/dist/cloud/index.js +390 -0
- package/dist/cloud-BNQTrmrr.js +50 -0
- package/dist/cloud-Cuxr6Czw.js +117 -0
- package/dist/collect-B7_TIfmg.js +49 -0
- package/dist/collect-O-tOtbnk.js +350 -0
- package/dist/collect-fRPAQIWb.js +2 -0
- package/dist/commands-BMh_Etm2.js +93 -0
- package/dist/compact-y6vfu2_f.js +19 -0
- package/dist/compare-pfyVnBjE.js +12 -0
- package/dist/compile-CQbvyIBr.d.ts +30 -0
- package/dist/compile-D22iwvuV.js +558 -0
- package/dist/config-BPYmjTOp.js +26 -0
- package/dist/connection-JvqwHODC.js +194 -0
- package/dist/context-CKEAabd7.js +73 -0
- package/dist/context-gtAwAILx.js +6 -0
- package/dist/convex/index.d.ts +40 -0
- package/dist/convex/index.js +55 -0
- package/dist/create-BKF3dgFk.d.ts +162 -0
- package/dist/create-CSkhDU9T.js +222 -0
- package/dist/credential-B_rkUNlY.js +326 -0
- package/dist/credentials-BgRX3WEg.js +268 -0
- package/dist/describe-Bt1YD5uS.js +99 -0
- package/dist/describe-DernyItf.d.ts +37 -0
- package/dist/diff-BUzf5Poi.js +48 -0
- package/dist/diff-x5Il8LVa.js +418 -0
- package/dist/digest-B3kc_PgQ.d.ts +19 -0
- package/dist/digest-BI-6aa6d.js +27 -0
- package/dist/doctor-BGle8Gqi.js +1314 -0
- package/dist/doctor-Bqo6FwUa.js +53 -0
- package/dist/drizzle/index.d.ts +72 -0
- package/dist/drizzle/index.js +160 -0
- package/dist/duration-xK2jGFVc.js +25 -0
- package/dist/elysia/index.d.ts +71 -0
- package/dist/elysia/index.js +103 -0
- package/dist/errors-DSMnAVNa.js +43 -0
- package/dist/errors-DbZ1YOnb.d.ts +138 -0
- package/dist/errors-Iy71s7Oj.js +177 -0
- package/dist/errors-m3u9TmDu.js +27 -0
- package/dist/evaluations-DsAU9ppl.js +479 -0
- package/dist/eve/index.d.ts +95 -0
- package/dist/eve/index.js +181 -0
- package/dist/express/index.d.ts +46 -0
- package/dist/express/index.js +108 -0
- package/dist/fastify/index.d.ts +56 -0
- package/dist/fastify/index.js +141 -0
- package/dist/files-B_wAGYmt.js +121 -0
- package/dist/fixtures-Cvaw0gDG.js +64 -0
- package/dist/freeze-y1_jQL4v.js +31 -0
- package/dist/from-snapshot-BMGnhmgv.js +552 -0
- package/dist/generate-CH7ThJ14.js +173 -0
- package/dist/grantee-Bv04_Vzf.js +521 -0
- package/dist/headers-B5RRv3Xm.js +6 -0
- package/dist/headers-C8SHQccD.d.ts +6 -0
- package/dist/helpers-DnSOrhB9.js +682 -0
- package/dist/hono/index.d.ts +70 -0
- package/dist/hono/index.js +131 -0
- package/dist/hosted-BwKQ1BTA.js +265 -0
- package/dist/http-DPuDaChb.d.ts +13 -0
- package/dist/http-GgVApX1k.js +111 -0
- package/dist/index-CltcQ4Bf.d.ts +138 -0
- package/dist/index-LF_hS-yn.d.ts +177 -0
- package/dist/index-fG6NSXlc.d.ts +9 -0
- package/dist/index-xIxJLwOQ.d.ts +415 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +21 -0
- package/dist/jwt/index.d.ts +87 -0
- package/dist/jwt/index.js +571 -0
- package/dist/kernel-YOGCWNLf.js +211 -0
- package/dist/kysely/index.d.ts +77 -0
- package/dist/kysely/index.js +129 -0
- package/dist/load-BZ24CpZP.js +69 -0
- package/dist/load-QOOm1z70.js +2 -0
- package/dist/load-jose-BL6LH4bL.js +11 -0
- package/dist/lru-8q4WZjBV.js +28 -0
- package/dist/manifest-DDQbC6K0.d.ts +118 -0
- package/dist/map-DowSO96j.js +158 -0
- package/dist/map-claims-B_6I1Rn-.js +360 -0
- package/dist/map-error-RJ3ZsUyA.js +16 -0
- package/dist/markers-B0hYLvMD.js +60 -0
- package/dist/may-use-CnFEjVEp.js +37 -0
- package/dist/mcp/index.d.ts +127 -0
- package/dist/mcp/index.js +563 -0
- package/dist/nest/index.d.ts +80 -0
- package/dist/nest/index.js +270 -0
- package/dist/next/client.d.ts +24 -0
- package/dist/next/client.js +31 -0
- package/dist/next/index.d.ts +110 -0
- package/dist/next/index.js +195 -0
- package/dist/next/plugin.d.ts +14 -0
- package/dist/next/plugin.js +2 -0
- package/dist/node/index.d.ts +43 -0
- package/dist/node/index.js +66 -0
- package/dist/openai/index.d.ts +71 -0
- package/dist/openai/index.js +105 -0
- package/dist/openapi/index.d.ts +93 -0
- package/dist/openapi/index.js +3 -0
- package/dist/openapi-CwePaF7C.js +164 -0
- package/dist/openapi-DWD0Uc_4.js +250 -0
- package/dist/openapi-DcqM8nU7.js +306 -0
- package/dist/openapi-import-BWRUNNeW.js +5959 -0
- package/dist/openapi-schema-D8j_T9tO.js +6902 -0
- package/dist/orpc/index.d.ts +64 -0
- package/dist/orpc/index.js +188 -0
- package/dist/otel/index.d.ts +13 -0
- package/dist/otel/index.js +202 -0
- package/dist/package-root-zGkxO3oN.js +30 -0
- package/dist/parse-DPfIvFMU.js +135 -0
- package/dist/pdp/index.d.ts +22 -0
- package/dist/pdp/index.js +795 -0
- package/dist/peer-BOr_kJE1.js +31 -0
- package/dist/permdock-yoZ7EjxX.js +2956 -0
- package/dist/permissions-B4apnahQ.d.ts +185 -0
- package/dist/permissions-B8vCg_bI.d.ts +52 -0
- package/dist/permissions-CofRqesP.js +142 -0
- package/dist/permissions-DC7GROPA.js +600 -0
- package/dist/pg-4GJWDUxG.js +953 -0
- package/dist/pins-CqE1jZuF.js +12 -0
- package/dist/plugin-4gr7IxFJ.js +65 -0
- package/dist/policy-ANp36PnI.js +971 -0
- package/dist/policy-CInmNGkM.d.ts +1959 -0
- package/dist/policy-Cq2fB8Q5.d.ts +20 -0
- package/dist/prisma/index.d.ts +135 -0
- package/dist/prisma/index.js +430 -0
- package/dist/problem-Cgp9-KZA.js +226 -0
- package/dist/prompt-CXYhLfsS.js +32 -0
- package/dist/protected-B4K3BKoX.js +138 -0
- package/dist/protected-BuPAycJT.d.ts +26 -0
- package/dist/protected-wY7nXhY9.d.ts +23 -0
- package/dist/provider-_wx46HpB.js +48 -0
- package/dist/provider-wTAUda2v.d.ts +6 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +6 -0
- package/dist/react/provider-client.d.ts +2 -0
- package/dist/react/provider-client.js +3 -0
- package/dist/react-native/index.d.ts +42 -0
- package/dist/react-native/index.js +174 -0
- package/dist/refs-ChLSxbST.js +90 -0
- package/dist/relations-r4soIFZB.js +426 -0
- package/dist/revocations-Cy06JNx2.d.ts +21 -0
- package/dist/rls-BOFU3chG.js +233 -0
- package/dist/rls-CJVja9Ww.js +2119 -0
- package/dist/rls-graph-DxQ39PJj.js +371 -0
- package/dist/rls-helpers-BxRuif0l.js +1346 -0
- package/dist/rls-import-CXV6LBlS.js +942 -0
- package/dist/rls-migrate-DrFPbf8p.js +366 -0
- package/dist/row-check-BajTgQZ-.d.ts +43 -0
- package/dist/run-CJ6UvS3a.js +155 -0
- package/dist/runtime-DP9ttFh1.js +27 -0
- package/dist/saas-BRrZzJkM.js +1193 -0
- package/dist/schema-P8yoma__.js +296 -0
- package/dist/scim/index.d.ts +2 -0
- package/dist/scim/index.js +2 -0
- package/dist/scim-9NFa0XVZ.js +1445 -0
- package/dist/scopes-cKVp_WTw.js +212 -0
- package/dist/server/index.d.ts +123 -0
- package/dist/server/index.js +8 -0
- package/dist/sha256-BO0swRXL.js +150 -0
- package/dist/sink-Bd9PSyfq.js +141 -0
- package/dist/sink-Cfr9-7YC.d.ts +104 -0
- package/dist/skills-C_lpV4Jx.js +50 -0
- package/dist/skills-Dx3QLnMY.js +110 -0
- package/dist/snapshot-DE8dE_hz.js +1051 -0
- package/dist/solid/index.d.ts +63 -0
- package/dist/solid/index.js +197 -0
- package/dist/source-DoBBYJi2.js +6 -0
- package/dist/sources-DOpW5Ila.d.ts +347 -0
- package/dist/sources-DVHM3Mwq.js +284 -0
- package/dist/sql-Bll1d7y0.js +22 -0
- package/dist/src-CTNT-PBW.js +430 -0
- package/dist/ssf/index.d.ts +3 -0
- package/dist/ssf/index.js +622 -0
- package/dist/store-Bq90395i.d.ts +27 -0
- package/dist/store-C6hFW6ta.js +508 -0
- package/dist/stores-CkG2HrSg.js +140 -0
- package/dist/stores-CvOnu_2T.d.ts +41 -0
- package/dist/stream-BhMfZOwr.d.ts +12 -0
- package/dist/stream-Q_xlB6Ko.js +52 -0
- package/dist/style-CTm1AXDT.js +22 -0
- package/dist/subject-C6dLxqHU.js +24 -0
- package/dist/subject-settings-xt7V845g.js +82 -0
- package/dist/supabase/index.d.ts +67 -0
- package/dist/supabase/index.js +4 -0
- package/dist/supabase/middleware.d.ts +77 -0
- package/dist/supabase/middleware.js +68 -0
- package/dist/supabase-D8UlBA7Z.js +83 -0
- package/dist/supabase-DTxGtYdi.js +796 -0
- package/dist/supabase-hook-DW-xP4hU.js +2 -0
- package/dist/supabase-hook-D_OqGb_V.js +1528 -0
- package/dist/svelte/Protected.svelte +34 -0
- package/dist/svelte/index.d.ts +10 -0
- package/dist/svelte/index.js +53 -0
- package/dist/svelte/runtime.d.ts +6 -0
- package/dist/svelte/runtime.js +3 -0
- package/dist/svelte/source.d.ts +10 -0
- package/dist/svelte/source.js +8 -0
- package/dist/terminal/index.d.ts +171 -0
- package/dist/terminal/index.js +821 -0
- package/dist/testing/index.d.ts +656 -0
- package/dist/testing/index.js +3208 -0
- package/dist/testing/saas/index.d.ts +194 -0
- package/dist/testing/saas/index.js +3 -0
- package/dist/testing/saas/permissions.d.ts +2 -0
- package/dist/testing/saas/permissions.js +2 -0
- package/dist/text-diff-BdkkYSbi.js +15 -0
- package/dist/thenable-okc2xDsj.js +10 -0
- package/dist/timeout-D51niuQP.js +8 -0
- package/dist/trpc/index.d.ts +80 -0
- package/dist/trpc/index.js +205 -0
- package/dist/types-B8w5PFtS.d.ts +141 -0
- package/dist/types-B9dmiiab.d.ts +66 -0
- package/dist/types-BBK-exvJ.d.ts +177 -0
- package/dist/types-BvBk1wgn.d.ts +118 -0
- package/dist/types-CxquUByI.d.ts +9 -0
- package/dist/types-D6Z6-rKJ.d.ts +312 -0
- package/dist/types-DbWdEfcm.d.ts +87 -0
- package/dist/types-bva3i4Gf.d.ts +98 -0
- package/dist/types-i4BAOvdb.d.ts +97 -0
- package/dist/unplugin/index.d.ts +7 -0
- package/dist/unplugin/index.js +30 -0
- package/dist/usage-1ozMubLY.js +42 -0
- package/dist/usage-CUnK6Ou9.js +530 -0
- package/dist/usage-PyEor6GA.js +2 -0
- package/dist/validity-D0uRmJKJ.js +224 -0
- package/dist/verifier-B90agnhl.js +461 -0
- package/dist/version-CnSh33Fj.js +16 -0
- package/dist/vocabulary-Dk0fs-Jk.d.ts +56 -0
- package/dist/vue/index.d.ts +57 -0
- package/dist/vue/index.js +203 -0
- package/dist/webmcp/index.d.ts +75 -0
- package/dist/webmcp/index.js +313 -0
- package/package.json +423 -4
- package/rulesets/permdock.yaml +29 -0
- package/schemas/catalog-v1.json +507 -0
- package/schemas/openapi/oas-3.1.json +1298 -0
- package/schemas/openapi/oas-3.2.json +1516 -0
- package/schemas/openapi/overlay-1.1.json +79 -0
- package/schemas/supabase-claims-v1.json +139 -0
- package/schemas/supabase-manifest-v1.json +217 -0
- package/skills/permdock/SKILL.md +66 -0
- package/skills/permdock-agents/SKILL.md +77 -0
- package/skills/permdock-agents/references/runtimes.md +211 -0
- package/skills/permdock-approvals/SKILL.md +80 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +83 -0
- package/skills/permdock-audit/SKILL.md +67 -0
- package/skills/permdock-credentials/SKILL.md +55 -0
- package/skills/permdock-credentials/references/tokens-keys-links.md +127 -0
- package/skills/permdock-data/SKILL.md +57 -0
- package/skills/permdock-data/references/queries-and-rls.md +54 -0
- package/skills/permdock-data/references/relationships.md +65 -0
- package/skills/permdock-tenancy/SKILL.md +77 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +83 -0
- package/skills/permdock-wire/SKILL.md +116 -0
- package/skills/permdock-wire/references/adapters.md +480 -0
- package/skills/permdock-wire/references/policy-options.md +43 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as compact } from "../compact-y6vfu2_f.js";
|
|
3
|
+
import { n as requiredPlans, t as describe } from "../describe-Bt1YD5uS.js";
|
|
4
|
+
import { n as parseSnapshot } from "../snapshot-DE8dE_hz.js";
|
|
5
|
+
import { t as emptySnapshot } from "../from-snapshot-BMGnhmgv.js";
|
|
6
|
+
import { t as approvalHeaders } from "../headers-B5RRv3Xm.js";
|
|
7
|
+
import { n as PermDockStoreContext } from "../context-gtAwAILx.js";
|
|
8
|
+
import { a as useFilter, c as usePermission, d as useSubject, f as useTenant, i as useAssignableRoles, l as usePermissions, n as useApproval, o as useMemberships, r as useAssignablePermissions, s as usePermDock, t as Protected, u as useRoles } from "../protected-B4K3BKoX.js";
|
|
9
|
+
import { t as createClientStore } from "../store-C6hFW6ta.js";
|
|
10
|
+
import { useEffect, useMemo } from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
//#region src/react-native/storage.ts
|
|
13
|
+
const SNAPSHOT_KEY = "permdock.snapshot";
|
|
14
|
+
const TENANT_KEY = "permdock.tenant";
|
|
15
|
+
function memoryStorage(initial = {}) {
|
|
16
|
+
const map = new Map(Object.entries(initial));
|
|
17
|
+
return {
|
|
18
|
+
getItem(key) {
|
|
19
|
+
return map.get(key) ?? null;
|
|
20
|
+
},
|
|
21
|
+
setItem(key, value) {
|
|
22
|
+
map.set(key, value);
|
|
23
|
+
},
|
|
24
|
+
removeItem(key) {
|
|
25
|
+
map.delete(key);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function isThenable(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
31
|
+
}
|
|
32
|
+
function acceptSnapshot(raw, subjectId) {
|
|
33
|
+
if (raw === null || raw.length === 0) return;
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
const snapshot = parseSnapshot(parsed);
|
|
37
|
+
const id = snapshot.subject.principal?.id;
|
|
38
|
+
if (subjectId !== void 0 && id !== subjectId) return;
|
|
39
|
+
return snapshot;
|
|
40
|
+
} catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function readStored(storage, subjectId) {
|
|
45
|
+
const raw = await Promise.resolve(storage.getItem(SNAPSHOT_KEY));
|
|
46
|
+
const tenantRaw = await Promise.resolve(storage.getItem(TENANT_KEY));
|
|
47
|
+
return {
|
|
48
|
+
snapshot: acceptSnapshot(raw, subjectId),
|
|
49
|
+
tenant: tenantRaw === null || tenantRaw.length === 0 ? void 0 : tenantRaw
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function readStoredSync(storage, subjectId) {
|
|
53
|
+
const raw = storage.getItem(SNAPSHOT_KEY);
|
|
54
|
+
const tenantRaw = storage.getItem(TENANT_KEY);
|
|
55
|
+
if (isThenable(raw) || isThenable(tenantRaw)) return;
|
|
56
|
+
return {
|
|
57
|
+
snapshot: acceptSnapshot(raw, subjectId),
|
|
58
|
+
tenant: tenantRaw === null || tenantRaw.length === 0 ? void 0 : tenantRaw
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function persistSnapshot(storage, snapshot, tenant) {
|
|
62
|
+
const write = storage.setItem(SNAPSHOT_KEY, JSON.stringify(snapshot));
|
|
63
|
+
if (isThenable(write)) write.catch(() => void 0);
|
|
64
|
+
if (tenant === void 0) return;
|
|
65
|
+
const next = storage.setItem(TENANT_KEY, tenant);
|
|
66
|
+
if (isThenable(next)) next.catch(() => void 0);
|
|
67
|
+
}
|
|
68
|
+
function clearStorage(storage) {
|
|
69
|
+
const snap = storage.removeItem(SNAPSHOT_KEY);
|
|
70
|
+
if (isThenable(snap)) snap.catch(() => void 0);
|
|
71
|
+
const tenant = storage.removeItem(TENANT_KEY);
|
|
72
|
+
if (isThenable(tenant)) tenant.catch(() => void 0);
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/react-native/store.ts
|
|
76
|
+
function isJws(value) {
|
|
77
|
+
const parts = value.split(".");
|
|
78
|
+
return parts.length === 3 && parts.every((part) => part.length > 0);
|
|
79
|
+
}
|
|
80
|
+
function createNativeStore(options) {
|
|
81
|
+
const signed = typeof options.snapshot === "string" && isJws(options.snapshot) ? options.snapshot : void 0;
|
|
82
|
+
const seeded = options.snapshot === void 0 || signed !== void 0 ? void 0 : typeof options.snapshot === "string" ? acceptSnapshot(options.snapshot, options.subjectId) : acceptSnapshot(JSON.stringify(options.snapshot), options.subjectId);
|
|
83
|
+
const sync = signed === void 0 ? readStoredSync(options.storage, options.subjectId) : void 0;
|
|
84
|
+
const snapshot = signed ?? seeded ?? sync?.snapshot ?? emptySnapshot();
|
|
85
|
+
const tenant = options.tenant ?? sync?.tenant;
|
|
86
|
+
let booted = false;
|
|
87
|
+
let touched = false;
|
|
88
|
+
const store = createClientStore(compact({
|
|
89
|
+
snapshot,
|
|
90
|
+
endpoint: options.endpoint,
|
|
91
|
+
snapshotUrl: options.snapshotUrl,
|
|
92
|
+
approvals: options.approvals,
|
|
93
|
+
tenant,
|
|
94
|
+
fetch: options.fetch,
|
|
95
|
+
headers: options.headers,
|
|
96
|
+
maxAge: options.maxAge,
|
|
97
|
+
verifier: options.verifier,
|
|
98
|
+
server: false,
|
|
99
|
+
onSnapshot: (next, nextTenant) => {
|
|
100
|
+
if (!booted) return;
|
|
101
|
+
touched = true;
|
|
102
|
+
persistSnapshot(options.storage, next, nextTenant);
|
|
103
|
+
},
|
|
104
|
+
onClear: () => {
|
|
105
|
+
touched = true;
|
|
106
|
+
clearStorage(options.storage);
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
booted = true;
|
|
110
|
+
if (seeded !== void 0) persistSnapshot(options.storage, seeded, tenant);
|
|
111
|
+
if (sync === void 0 && seeded === void 0 && signed === void 0) readStored(options.storage, options.subjectId).then((stored) => {
|
|
112
|
+
if (!touched && stored.snapshot !== void 0) store.replace(stored.snapshot);
|
|
113
|
+
});
|
|
114
|
+
return store;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/react-native/provider.tsx
|
|
118
|
+
function PermDockProvider(props) {
|
|
119
|
+
const store = useMemo(() => createNativeStore(compact({
|
|
120
|
+
storage: props.storage,
|
|
121
|
+
snapshot: props.snapshot,
|
|
122
|
+
snapshotUrl: props.snapshotUrl,
|
|
123
|
+
endpoint: props.endpoint,
|
|
124
|
+
approvals: props.approvals,
|
|
125
|
+
tenant: props.tenant,
|
|
126
|
+
subjectId: props.subjectId,
|
|
127
|
+
revalidate: props.revalidate,
|
|
128
|
+
subscribeForeground: props.subscribeForeground,
|
|
129
|
+
fetch: props.fetch,
|
|
130
|
+
headers: props.headers,
|
|
131
|
+
maxAge: props.maxAge,
|
|
132
|
+
verifier: props.verifier
|
|
133
|
+
})), [
|
|
134
|
+
props.storage,
|
|
135
|
+
props.snapshot,
|
|
136
|
+
props.snapshotUrl,
|
|
137
|
+
props.endpoint,
|
|
138
|
+
props.approvals,
|
|
139
|
+
props.tenant,
|
|
140
|
+
props.subjectId,
|
|
141
|
+
props.revalidate,
|
|
142
|
+
props.subscribeForeground,
|
|
143
|
+
props.fetch,
|
|
144
|
+
props.headers,
|
|
145
|
+
props.maxAge,
|
|
146
|
+
props.verifier
|
|
147
|
+
]);
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
const mode = props.revalidate ?? "launch";
|
|
150
|
+
const refresh = () => {
|
|
151
|
+
if (props.snapshotUrl === void 0) return;
|
|
152
|
+
store.get().refresh().catch(() => void 0);
|
|
153
|
+
};
|
|
154
|
+
if (mode === "launch" || mode === "focus" || typeof mode === "number") refresh();
|
|
155
|
+
if (typeof mode === "number") {
|
|
156
|
+
const timer = setInterval(refresh, mode * 1e3);
|
|
157
|
+
return () => {
|
|
158
|
+
clearInterval(timer);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (mode === "focus" && props.subscribeForeground !== void 0) return props.subscribeForeground(refresh);
|
|
162
|
+
}, [
|
|
163
|
+
store,
|
|
164
|
+
props.revalidate,
|
|
165
|
+
props.snapshotUrl,
|
|
166
|
+
props.subscribeForeground
|
|
167
|
+
]);
|
|
168
|
+
return /* @__PURE__ */ jsx(PermDockStoreContext, {
|
|
169
|
+
value: store,
|
|
170
|
+
children: props.children
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { PermDockProvider, Protected, approvalHeaders, createNativeStore, describe, memoryStorage, requiredPlans, useApproval, useAssignablePermissions, useAssignableRoles, useFilter, useMemberships, usePermDock, usePermission, usePermissions, useRoles, useSubject, useTenant };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { n as freezeDeep } from "./freeze-y1_jQL4v.js";
|
|
2
|
+
import { E as splitPath, T as readPath, x as assertSafeKey } from "./permissions-DC7GROPA.js";
|
|
3
|
+
//#region src/conditions/ast.ts
|
|
4
|
+
function parentHop(parent) {
|
|
5
|
+
return typeof parent === "string" ? { field: parent } : parent;
|
|
6
|
+
}
|
|
7
|
+
function isConditionRef(value) {
|
|
8
|
+
return value !== null && typeof value === "object" && "ref" in value && typeof value.ref === "string";
|
|
9
|
+
}
|
|
10
|
+
function isConditionDate(value) {
|
|
11
|
+
return value !== null && typeof value === "object" && "date" in value && typeof value.date === "string" && !("ref" in value);
|
|
12
|
+
}
|
|
13
|
+
function isCondition(value) {
|
|
14
|
+
return value !== null && typeof value === "object" && "op" in value && typeof value.op === "string";
|
|
15
|
+
}
|
|
16
|
+
function isSqlFunctionField(value) {
|
|
17
|
+
return value !== null && typeof value === "object" && "field" in value && typeof value.field === "string" && !("ref" in value) && !("date" in value) && !("op" in value);
|
|
18
|
+
}
|
|
19
|
+
function hasConditionOp(condition, op) {
|
|
20
|
+
if (condition === void 0) return false;
|
|
21
|
+
if (condition.op === op) return true;
|
|
22
|
+
switch (condition.op) {
|
|
23
|
+
case "and":
|
|
24
|
+
case "or": return condition.conditions.some((child) => hasConditionOp(child, op));
|
|
25
|
+
case "not": return hasConditionOp(condition.condition, op);
|
|
26
|
+
case "sqlFunction": return hasConditionOp(condition.twin, op);
|
|
27
|
+
case "eq":
|
|
28
|
+
case "ne":
|
|
29
|
+
case "gt":
|
|
30
|
+
case "gte":
|
|
31
|
+
case "lt":
|
|
32
|
+
case "lte":
|
|
33
|
+
case "contains":
|
|
34
|
+
case "in":
|
|
35
|
+
case "notIn":
|
|
36
|
+
case "isNull":
|
|
37
|
+
case "memberOf":
|
|
38
|
+
case "related":
|
|
39
|
+
case "opaque": return false;
|
|
40
|
+
default:
|
|
41
|
+
/* v8 ignore next */
|
|
42
|
+
return condition;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/conditions/refs.ts
|
|
47
|
+
const REF_BRAND = Symbol.for("permdock.ref");
|
|
48
|
+
function createRef(path) {
|
|
49
|
+
const segments = splitPath(path);
|
|
50
|
+
for (const segment of segments) assertSafeKey(segment, "subject path");
|
|
51
|
+
const target = freezeDeep({ ref: path });
|
|
52
|
+
return new Proxy(target, {
|
|
53
|
+
get(object, property, receiver) {
|
|
54
|
+
if (property === "ref" || property === REF_BRAND) return Reflect.get(object, property === REF_BRAND ? "ref" : property, receiver);
|
|
55
|
+
if (property === "toJSON" || property === "valueOf" || property === Symbol.toStringTag) return;
|
|
56
|
+
if (typeof property !== "string") return;
|
|
57
|
+
assertSafeKey(property, "subject path");
|
|
58
|
+
return createRef(`${path}.${property}`);
|
|
59
|
+
},
|
|
60
|
+
getOwnPropertyDescriptor(object, property) {
|
|
61
|
+
if (property === "ref") return Reflect.getOwnPropertyDescriptor(object, property);
|
|
62
|
+
},
|
|
63
|
+
ownKeys() {
|
|
64
|
+
return ["ref"];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const principal = createRef("principal");
|
|
69
|
+
const context = createRef("context");
|
|
70
|
+
function isSubjectRef(value) {
|
|
71
|
+
if (value === null || typeof value !== "object" || !("ref" in value) || typeof value.ref !== "string") return false;
|
|
72
|
+
const ref = value.ref;
|
|
73
|
+
return ref === "principal" || ref.startsWith("principal.") || ref === "context" || ref.startsWith("context.");
|
|
74
|
+
}
|
|
75
|
+
function resolveConditionRef(ref, resolved) {
|
|
76
|
+
if (resolved === void 0) return;
|
|
77
|
+
if (ref === "principal" || ref.startsWith("principal.")) {
|
|
78
|
+
const path = ref === "principal" ? "" : ref.slice(10);
|
|
79
|
+
if (path === "") return resolved.principal;
|
|
80
|
+
if (resolved.principal === null) return;
|
|
81
|
+
return readPath(resolved.principal, path);
|
|
82
|
+
}
|
|
83
|
+
if (ref === "context" || ref.startsWith("context.")) {
|
|
84
|
+
const path = ref === "context" ? "" : ref.slice(8);
|
|
85
|
+
if (path === "") return resolved.context;
|
|
86
|
+
return readPath(resolved.context, path);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { hasConditionOp as a, isConditionRef as c, resolveConditionRef as i, isSqlFunctionField as l, isSubjectRef as n, isCondition as o, principal as r, isConditionDate as s, context as t, parentHop as u };
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { C as ownGet, l as isComputedRelation, p as isPrincipalRelation, r as expandRelation, s as getRegistry, u as isEdgeRelation } from "./permissions-DC7GROPA.js";
|
|
2
|
+
import { n as isThenable } from "./thenable-okc2xDsj.js";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
}
|
|
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 asGroup(value) {
|
|
26
|
+
if (!isRecord(value) || typeof value["resource"] !== "string" || typeof value["id"] !== "string" || typeof value["relation"] !== "string" || value["id"] === "") return;
|
|
27
|
+
return {
|
|
28
|
+
resource: value["resource"],
|
|
29
|
+
id: value["id"],
|
|
30
|
+
relation: value["relation"]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function asHolders(value) {
|
|
34
|
+
if (!Array.isArray(value)) return;
|
|
35
|
+
const holders = [];
|
|
36
|
+
for (const item of value) {
|
|
37
|
+
if (!isRecord(item) || !isInstant(item["startsAt"]) || !isInstant(item["expiresAt"])) return;
|
|
38
|
+
const period = {
|
|
39
|
+
...item["startsAt"] === void 0 ? {} : { startsAt: item["startsAt"] },
|
|
40
|
+
...item["expiresAt"] === void 0 ? {} : { expiresAt: item["expiresAt"] }
|
|
41
|
+
};
|
|
42
|
+
if (isRecord(item["principal"]) && typeof item["principal"]["id"] === "string") {
|
|
43
|
+
holders.push({
|
|
44
|
+
principal: { id: item["principal"]["id"] },
|
|
45
|
+
...period
|
|
46
|
+
});
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const group = asGroup(item["group"]);
|
|
50
|
+
if (group === void 0) return;
|
|
51
|
+
holders.push({
|
|
52
|
+
group,
|
|
53
|
+
...period
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return holders;
|
|
57
|
+
}
|
|
58
|
+
function read(cache, key, call, validate) {
|
|
59
|
+
const entry = cache.get(key);
|
|
60
|
+
if (entry !== void 0) return entry.state === "ready" ? entry.value : entry.state;
|
|
61
|
+
if (call === void 0) return "failed";
|
|
62
|
+
const settle = (value) => {
|
|
63
|
+
const valid = validate(value);
|
|
64
|
+
cache.set(key, valid === void 0 ? { state: "failed" } : {
|
|
65
|
+
state: "ready",
|
|
66
|
+
value: valid
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
let result;
|
|
70
|
+
try {
|
|
71
|
+
result = call();
|
|
72
|
+
} catch {
|
|
73
|
+
cache.set(key, { state: "failed" });
|
|
74
|
+
return "failed";
|
|
75
|
+
}
|
|
76
|
+
if (isThenable(result)) {
|
|
77
|
+
const promise = Promise.resolve(result).then(settle, () => {
|
|
78
|
+
cache.set(key, { state: "failed" });
|
|
79
|
+
});
|
|
80
|
+
cache.set(key, {
|
|
81
|
+
state: "pending",
|
|
82
|
+
promise
|
|
83
|
+
});
|
|
84
|
+
return "pending";
|
|
85
|
+
}
|
|
86
|
+
settle(result);
|
|
87
|
+
const settled = cache.get(key);
|
|
88
|
+
return settled?.state === "ready" ? settled.value : "failed";
|
|
89
|
+
}
|
|
90
|
+
function relationReader(source, cache, resources) {
|
|
91
|
+
const concrete = (resource, id, relation) => read(cache, JSON.stringify([
|
|
92
|
+
"holders",
|
|
93
|
+
resource,
|
|
94
|
+
id,
|
|
95
|
+
relation
|
|
96
|
+
]), source === void 0 ? void 0 : () => source.related({
|
|
97
|
+
resource,
|
|
98
|
+
id,
|
|
99
|
+
relation
|
|
100
|
+
}), asHolders);
|
|
101
|
+
return {
|
|
102
|
+
available: source !== void 0,
|
|
103
|
+
chain(query) {
|
|
104
|
+
const through = query.through ?? "parent";
|
|
105
|
+
return read(cache, JSON.stringify([
|
|
106
|
+
"chain",
|
|
107
|
+
query.resource,
|
|
108
|
+
query.id,
|
|
109
|
+
query.depth,
|
|
110
|
+
through
|
|
111
|
+
]), source === void 0 ? void 0 : () => source.ancestors({
|
|
112
|
+
resource: query.resource,
|
|
113
|
+
id: query.id,
|
|
114
|
+
depth: query.depth,
|
|
115
|
+
through
|
|
116
|
+
}), asChain);
|
|
117
|
+
},
|
|
118
|
+
holders(query) {
|
|
119
|
+
const node = resources?.get(query.resource);
|
|
120
|
+
const names = node === void 0 ? [query.relation] : expandRelation(node, query.relation);
|
|
121
|
+
const out = [];
|
|
122
|
+
let unread;
|
|
123
|
+
for (const name of names) {
|
|
124
|
+
const answer = concrete(query.resource, query.id, name);
|
|
125
|
+
if (answer === "pending" || answer === "failed") {
|
|
126
|
+
unread = answer === "failed" || unread === "failed" ? "failed" : "pending";
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
for (const holder of answer) out.push({
|
|
130
|
+
...holder,
|
|
131
|
+
relation: name
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return unread ?? out;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** The loads still in flight, for an instance that awaits them before deciding. */
|
|
139
|
+
function pendingRelations(cache) {
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const entry of cache.values()) if (entry.state === "pending") out.push(entry.promise);
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
function relationId(value) {
|
|
145
|
+
if (typeof value === "string") return value === "" ? void 0 : value;
|
|
146
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
147
|
+
if (typeof value === "bigint") return String(value);
|
|
148
|
+
}
|
|
149
|
+
/** Whether the holder's period covers `now`. */
|
|
150
|
+
function activeNow(holder, now) {
|
|
151
|
+
return (holder.startsAt === void 0 || holder.startsAt <= now) && (holder.expiresAt === void 0 || holder.expiresAt > now);
|
|
152
|
+
}
|
|
153
|
+
function holdsNow(holder, principalId, now) {
|
|
154
|
+
return "principal" in holder && holder.principal.id === principalId && activeNow(holder, now);
|
|
155
|
+
}
|
|
156
|
+
/** Where the walk starts after following `hops`, or why it cannot. */
|
|
157
|
+
function followHops(condition, first, reader) {
|
|
158
|
+
const hops = condition.hops ?? [];
|
|
159
|
+
let id = first;
|
|
160
|
+
for (let index = 1; index < hops.length; index += 1) {
|
|
161
|
+
const from = hops[index - 1];
|
|
162
|
+
const hop = hops[index];
|
|
163
|
+
if (from === void 0 || hop === void 0)
|
|
164
|
+
/* v8 ignore next */
|
|
165
|
+
return;
|
|
166
|
+
const chain = reader.chain({
|
|
167
|
+
resource: from.resource,
|
|
168
|
+
id,
|
|
169
|
+
depth: 1,
|
|
170
|
+
through: hop.link
|
|
171
|
+
});
|
|
172
|
+
if (chain === "pending" || chain === "failed") return "relation-unavailable";
|
|
173
|
+
const next = chain.ancestors[0]?.id;
|
|
174
|
+
if (next === void 0) return;
|
|
175
|
+
id = next;
|
|
176
|
+
}
|
|
177
|
+
return { id };
|
|
178
|
+
}
|
|
179
|
+
/** The instances a graph relation reads: the start, then ancestors until a restricted one or `depth`. */
|
|
180
|
+
function relationWalk(condition, row, reader) {
|
|
181
|
+
if (!isRecord(row)) return;
|
|
182
|
+
const first = relationId(ownGet(row, condition.field));
|
|
183
|
+
if (first === void 0) return;
|
|
184
|
+
const rowRestricted = condition.restricted !== void 0 && ownGet(row, condition.restricted) === true;
|
|
185
|
+
const hopped = condition.hops !== void 0 && condition.hops.length > 0;
|
|
186
|
+
if ((condition.parent === true || hopped) && rowRestricted) return;
|
|
187
|
+
const start = hopped ? followHops(condition, first, reader) : { id: first };
|
|
188
|
+
if (start === void 0 || start === "relation-unavailable") return start;
|
|
189
|
+
const id = start.id;
|
|
190
|
+
if (condition.depth === 0 || rowRestricted) return {
|
|
191
|
+
ids: [id],
|
|
192
|
+
truncated: false
|
|
193
|
+
};
|
|
194
|
+
const chain = reader.chain({
|
|
195
|
+
resource: condition.resource,
|
|
196
|
+
id,
|
|
197
|
+
depth: condition.depth
|
|
198
|
+
});
|
|
199
|
+
if (chain === "pending" || chain === "failed") return "relation-unavailable";
|
|
200
|
+
const ids = [id];
|
|
201
|
+
let stopped = chain.restricted === true;
|
|
202
|
+
if (!stopped) for (const ancestor of chain.ancestors.slice(0, condition.depth)) {
|
|
203
|
+
ids.push(ancestor.id);
|
|
204
|
+
if (ancestor.restricted === true) {
|
|
205
|
+
stopped = true;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (new Set(ids).size !== ids.length) return "relation-depth";
|
|
210
|
+
return {
|
|
211
|
+
ids,
|
|
212
|
+
truncated: !stopped && (chain.ancestors.length > condition.depth || chain.truncated === true)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Whether the subject holds the relation on the row's instance or an
|
|
217
|
+
* ancestor within `depth`. A cycle is `relation-depth`; so is a chain that
|
|
218
|
+
* goes on past `depth` with no holder below it. Any read the instance could
|
|
219
|
+
* not answer is `relation-unavailable` unless a holder was found elsewhere.
|
|
220
|
+
*/
|
|
221
|
+
function resolveRelated(condition, row, subject, now, reader) {
|
|
222
|
+
const principal = subject.principal;
|
|
223
|
+
if (principal === null) return false;
|
|
224
|
+
const walk = relationWalk(condition, row, reader);
|
|
225
|
+
if (walk === void 0) return false;
|
|
226
|
+
if (walk === "relation-depth" || walk === "relation-unavailable") return walk;
|
|
227
|
+
if (condition.ids !== void 0) {
|
|
228
|
+
const held = new Set(condition.ids);
|
|
229
|
+
if (walk.ids.some((id) => held.has(id))) return true;
|
|
230
|
+
return walk.truncated ? "relation-depth" : false;
|
|
231
|
+
}
|
|
232
|
+
let unavailable = false;
|
|
233
|
+
let deep = false;
|
|
234
|
+
const explored = /* @__PURE__ */ new Map();
|
|
235
|
+
for (const id of walk.ids) {
|
|
236
|
+
const verdict = holdsRelation({
|
|
237
|
+
resource: condition.resource,
|
|
238
|
+
id,
|
|
239
|
+
relation: condition.relation
|
|
240
|
+
}, principal.id, now, reader, 16, /* @__PURE__ */ new Set(), explored);
|
|
241
|
+
if (verdict === true) return true;
|
|
242
|
+
if (verdict === "relation-unavailable") unavailable = true;
|
|
243
|
+
else if (verdict === "relation-depth") deep = true;
|
|
244
|
+
}
|
|
245
|
+
if (unavailable) return "relation-unavailable";
|
|
246
|
+
return walk.truncated || deep ? "relation-depth" : false;
|
|
247
|
+
}
|
|
248
|
+
function groupKey(group) {
|
|
249
|
+
return JSON.stringify([
|
|
250
|
+
group.resource,
|
|
251
|
+
group.id,
|
|
252
|
+
group.relation
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Whether the principal holds `at.relation` on one instance, directly or
|
|
257
|
+
* through a group whose members hold it. Groups of `at`'s own resource nest
|
|
258
|
+
* at most `budget` deep; a group of another resource gets a fresh budget
|
|
259
|
+
* (declarations rule out cycles across resources). A group met twice on one
|
|
260
|
+
* path, or one past the budget, is `relation-depth`. `explored` holds the
|
|
261
|
+
* budget each group was walked with across the whole walk: a group already
|
|
262
|
+
* walked with as much budget adds nothing, since its verdict already reached
|
|
263
|
+
* the caller, so shared sub-groups cost one walk rather than one per path.
|
|
264
|
+
*/
|
|
265
|
+
function holdsRelation(at, principalId, now, reader, budget, seen, explored) {
|
|
266
|
+
const key = groupKey(at);
|
|
267
|
+
const walked = explored.get(key);
|
|
268
|
+
if (walked !== void 0 && walked >= budget) return false;
|
|
269
|
+
explored.set(key, budget);
|
|
270
|
+
const holders = reader.holders(at);
|
|
271
|
+
if (holders === "pending" || holders === "failed") return "relation-unavailable";
|
|
272
|
+
if (holders.some((holder) => holdsNow(holder, principalId, now))) return true;
|
|
273
|
+
const path = new Set(seen).add(key);
|
|
274
|
+
let unavailable = false;
|
|
275
|
+
let deep = false;
|
|
276
|
+
for (const holder of holders) {
|
|
277
|
+
if (!("group" in holder) || !activeNow(holder, now)) continue;
|
|
278
|
+
const nested = holder.group.resource === at.resource;
|
|
279
|
+
if (nested && budget <= 0 || path.has(groupKey(holder.group))) {
|
|
280
|
+
deep = true;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const verdict = holdsRelation(holder.group, principalId, now, reader, nested ? budget - 1 : 16, path, explored);
|
|
284
|
+
if (verdict === true) return true;
|
|
285
|
+
if (verdict === "relation-unavailable") unavailable = true;
|
|
286
|
+
else if (verdict === "relation-depth") deep = true;
|
|
287
|
+
}
|
|
288
|
+
if (unavailable) return "relation-unavailable";
|
|
289
|
+
return deep ? "relation-depth" : false;
|
|
290
|
+
}
|
|
291
|
+
/** The holder one edge-table row names for `spec`, or `undefined` when the row does not belong to it. */
|
|
292
|
+
function tableHolder(resourceName, spec, row, id) {
|
|
293
|
+
if (relationId(ownGet(row, spec.object ?? `${resourceName}_id`)) !== id) return;
|
|
294
|
+
for (const [column, value] of Object.entries(spec.match ?? {})) if (ownGet(row, column) !== value) return;
|
|
295
|
+
const subject = relationId(ownGet(row, spec.subject ?? "user_id"));
|
|
296
|
+
if (subject === void 0) return;
|
|
297
|
+
const expiresAt = spec.expiresAt === void 0 ? void 0 : seconds(ownGet(row, spec.expiresAt));
|
|
298
|
+
const period = expiresAt === void 0 ? {} : { expiresAt };
|
|
299
|
+
const groups = spec.groups;
|
|
300
|
+
const kind = groups === void 0 ? null : ownGet(row, groups.column);
|
|
301
|
+
if (groups === void 0 || kind === null || kind === void 0 || kind === groups.direct) return {
|
|
302
|
+
principal: { id: subject },
|
|
303
|
+
...period
|
|
304
|
+
};
|
|
305
|
+
const relation = typeof kind === "string" && Object.hasOwn(groups.resources, kind) ? groups.resources[kind] : void 0;
|
|
306
|
+
return relation === void 0 ? void 0 : {
|
|
307
|
+
group: {
|
|
308
|
+
resource: kind,
|
|
309
|
+
id: subject,
|
|
310
|
+
relation
|
|
311
|
+
},
|
|
312
|
+
...period
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function seconds(value) {
|
|
316
|
+
if (value instanceof Date) {
|
|
317
|
+
const time = value.getTime();
|
|
318
|
+
return Number.isNaN(time) ? void 0 : time / 1e3;
|
|
319
|
+
}
|
|
320
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
321
|
+
if (typeof value === "string") {
|
|
322
|
+
const parsed = Date.parse(value);
|
|
323
|
+
return Number.isNaN(parsed) ? void 0 : parsed / 1e3;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function restrictedOf(node, row) {
|
|
327
|
+
return node.restricted !== void 0 && row !== void 0 && ownGet(row, node.restricted) === true;
|
|
328
|
+
}
|
|
329
|
+
function edgeHolder(edge) {
|
|
330
|
+
const holder = { principal: { id: edge.principal } };
|
|
331
|
+
if (edge.startsAt !== void 0) holder.startsAt = edge.startsAt;
|
|
332
|
+
if (edge.expiresAt !== void 0) holder.expiresAt = edge.expiresAt;
|
|
333
|
+
return holder;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* An in-process `RelationSource` over plain rows and edges, read against the
|
|
337
|
+
* resource definitions in `permissions`. For tests, fixtures and small apps.
|
|
338
|
+
*/
|
|
339
|
+
function memoryRelations(permissions, data = {}) {
|
|
340
|
+
const registry = getRegistry(permissions);
|
|
341
|
+
const byId = /* @__PURE__ */ new Map();
|
|
342
|
+
for (const [name, rows] of Object.entries(data.rows ?? {})) {
|
|
343
|
+
const node = registry.get(name);
|
|
344
|
+
const index = /* @__PURE__ */ new Map();
|
|
345
|
+
for (const row of rows) {
|
|
346
|
+
const id = relationId(ownGet(row, node?.id ?? "id"));
|
|
347
|
+
if (id !== void 0) index.set(id, row);
|
|
348
|
+
}
|
|
349
|
+
byId.set(name, index);
|
|
350
|
+
}
|
|
351
|
+
const rowOf = (resource, id) => byId.get(resource)?.get(id);
|
|
352
|
+
return {
|
|
353
|
+
ancestors({ resource, id, depth, through }) {
|
|
354
|
+
const node = registry.get(resource);
|
|
355
|
+
const start = rowOf(resource, id);
|
|
356
|
+
if (node === void 0 || start === void 0) return { ancestors: [] };
|
|
357
|
+
if (through !== "parent") {
|
|
358
|
+
const link = Object.hasOwn(node.links, through) ? node.links[through] : void 0;
|
|
359
|
+
const next = link === void 0 ? void 0 : relationId(ownGet(start, link.field));
|
|
360
|
+
if (next === void 0 || depth < 1) return { ancestors: [] };
|
|
361
|
+
return { ancestors: [{ id: next }] };
|
|
362
|
+
}
|
|
363
|
+
const restricted = restrictedOf(node, start);
|
|
364
|
+
if (restricted || node.parent === void 0 || node.parent.resource !== resource) return restricted ? {
|
|
365
|
+
restricted: true,
|
|
366
|
+
ancestors: []
|
|
367
|
+
} : { ancestors: [] };
|
|
368
|
+
const field = node.parent.field;
|
|
369
|
+
const seen = /* @__PURE__ */ new Set([id]);
|
|
370
|
+
const ancestors = [];
|
|
371
|
+
let next = relationId(ownGet(start, field));
|
|
372
|
+
while (next !== void 0 && ancestors.length < depth) {
|
|
373
|
+
const row = rowOf(resource, next);
|
|
374
|
+
if (row === void 0) {
|
|
375
|
+
next = void 0;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
const hidden = restrictedOf(node, row);
|
|
379
|
+
ancestors.push(hidden ? {
|
|
380
|
+
id: next,
|
|
381
|
+
restricted: true
|
|
382
|
+
} : { id: next });
|
|
383
|
+
if (seen.has(next) || hidden) {
|
|
384
|
+
next = void 0;
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
seen.add(next);
|
|
388
|
+
next = relationId(ownGet(row, field));
|
|
389
|
+
}
|
|
390
|
+
return next === void 0 ? { ancestors } : {
|
|
391
|
+
ancestors,
|
|
392
|
+
truncated: true
|
|
393
|
+
};
|
|
394
|
+
},
|
|
395
|
+
related({ resource, id, relation }) {
|
|
396
|
+
const spec = registry.get(resource)?.relations[relation];
|
|
397
|
+
if (spec === void 0 || isComputedRelation(spec)) return [];
|
|
398
|
+
if (isEdgeRelation(spec)) {
|
|
399
|
+
const table = data.tables?.[spec.edge];
|
|
400
|
+
if (table !== void 0) return table.flatMap((row) => {
|
|
401
|
+
const holder = tableHolder(resource, spec, row, id);
|
|
402
|
+
return holder === void 0 ? [] : [holder];
|
|
403
|
+
});
|
|
404
|
+
return (data.edges?.[resource]?.[relation] ?? []).filter((edge) => edge.id === id).map(edgeHolder);
|
|
405
|
+
}
|
|
406
|
+
const row = rowOf(resource, id);
|
|
407
|
+
if (row === void 0) return [];
|
|
408
|
+
if (isPrincipalRelation(spec)) {
|
|
409
|
+
const holder = relationId(ownGet(row, spec.principal));
|
|
410
|
+
if (holder === void 0) return [];
|
|
411
|
+
const startsAt = spec.period?.startsAt === void 0 ? void 0 : seconds(ownGet(row, spec.period.startsAt));
|
|
412
|
+
const expiresAt = spec.period?.expiresAt === void 0 ? void 0 : seconds(ownGet(row, spec.period.expiresAt));
|
|
413
|
+
return [{
|
|
414
|
+
principal: { id: holder },
|
|
415
|
+
...startsAt === void 0 ? {} : { startsAt },
|
|
416
|
+
...expiresAt === void 0 ? {} : { expiresAt }
|
|
417
|
+
}];
|
|
418
|
+
}
|
|
419
|
+
if (spec.memberOf !== void 0) return [];
|
|
420
|
+
const holder = relationId(ownGet(row, spec.field));
|
|
421
|
+
return holder === void 0 ? [] : [{ principal: { id: holder } }];
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
//#endregion
|
|
426
|
+
export { relationWalk as a, relationReader as i, pendingRelations as n, resolveRelated as o, relationId as r, memoryRelations as t };
|