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,656 @@
|
|
|
1
|
+
import { $ as DecisionSink, C as Policy, Et as TokenVerifier, Qt as PermDockOptions, St as SubjectResolver, Tt as TokenSigner, X as CredentialVerifier, Zt as PermDock, at as LimitStore, cr as CustomRole, ct as MembershipSource, ft as RelationSource, gt as Snapshot, hr as Subject, kn as ApprovalPolicySource, kt as WhereCompiler, mr as Principal, mt as SettingsSource, pr as Membership, pt as RoleSource, sn as PolicySource, tn as WhereResult, tt as EntitlementSource, xt as SnapshotSource } from "../policy-CInmNGkM.js";
|
|
2
|
+
import { d as Permission, h as ResourceInit, l as InferPermissionTree } from "../permissions-B4apnahQ.js";
|
|
3
|
+
import { r as Role } from "../vocabulary-Dk0fs-Jk.js";
|
|
4
|
+
import { f as ApprovalStore } from "../types-B8w5PFtS.js";
|
|
5
|
+
import { n as SupabaseHookManifest } from "../manifest-DDQbC6K0.js";
|
|
6
|
+
import { t as supabaseMembershipsBudget } from "../budget-JqcRqyhK.js";
|
|
7
|
+
import { n as RevocationFeed } from "../revocations-Cy06JNx2.js";
|
|
8
|
+
import { a as ReplayStore } from "../types-i4BAOvdb.js";
|
|
9
|
+
import { v as DirectoryStore } from "../index-LF_hS-yn.js";
|
|
10
|
+
import { a as SaasProject } from "../permissions-B8vCg_bI.js";
|
|
11
|
+
import { n as saasPolicy } from "../policy-Cq2fB8Q5.js";
|
|
12
|
+
import { expectTypeOf } from "vitest";
|
|
13
|
+
//#region src/testing/describe-policy.d.ts
|
|
14
|
+
type MatrixOutcome = "granted" | "denied" | "approval-required";
|
|
15
|
+
type MatrixCell = MatrixOutcome | {
|
|
16
|
+
readonly outcome?: MatrixOutcome;
|
|
17
|
+
readonly denials?: readonly {
|
|
18
|
+
readonly role?: string | null;
|
|
19
|
+
readonly reason: string;
|
|
20
|
+
}[];
|
|
21
|
+
readonly alternatives?: readonly string[];
|
|
22
|
+
/** Obligation kinds a granted decision carries, in order; `[]` asserts none. */
|
|
23
|
+
readonly obligations?: readonly string[];
|
|
24
|
+
/** The `name` of the deny grant `explain` reports as the one that won. */
|
|
25
|
+
readonly deniedBy?: string;
|
|
26
|
+
};
|
|
27
|
+
type DescribePolicyConfig<TSubject> = {
|
|
28
|
+
readonly subjects: Record<string, TSubject>;
|
|
29
|
+
readonly fixtures?: Record<string, unknown>;
|
|
30
|
+
readonly matrix: Record<string, Record<string, MatrixCell | Record<string, MatrixCell>>>;
|
|
31
|
+
readonly exhaustive?: boolean;
|
|
32
|
+
readonly options?: PermDockOptions;
|
|
33
|
+
/**
|
|
34
|
+
* Also assert every granted or denied cell against `fromSnapshot(permdock.snapshot())`,
|
|
35
|
+
* the client a Server Component hands down. Closures cannot cross a snapshot and deny on the client.
|
|
36
|
+
*/
|
|
37
|
+
readonly snapshot?: boolean;
|
|
38
|
+
};
|
|
39
|
+
export declare function describePolicy<TSubject>(policy: Policy, config: DescribePolicyConfig<TSubject>): void;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/testing/rls-parity.d.ts
|
|
42
|
+
type RlsDbOutcome = "allowed" | "filtered" | "rejected";
|
|
43
|
+
type RlsParitySubject = {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly roles?: readonly string[];
|
|
46
|
+
readonly tenant?: string;
|
|
47
|
+
readonly memberships?: readonly Membership[];
|
|
48
|
+
/**
|
|
49
|
+
* Extra token claims, read in memory as `principal.claims.*`. They join the
|
|
50
|
+
* JWT claims (`supabase`, `neon`); under `guc` each top-level claim is its
|
|
51
|
+
* own `<prefix>.<name>` setting, strings as is and anything else as JSON.
|
|
52
|
+
*/
|
|
53
|
+
readonly claims?: Readonly<Record<string, unknown>>;
|
|
54
|
+
};
|
|
55
|
+
type RlsParityFixture = {
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly subject: RlsParitySubject;
|
|
58
|
+
readonly permission: Permission;
|
|
59
|
+
readonly row: Readonly<Record<string, unknown>>;
|
|
60
|
+
readonly table: string;
|
|
61
|
+
};
|
|
62
|
+
type RlsQueryResult = {
|
|
63
|
+
readonly rows: readonly Record<string, unknown>[];
|
|
64
|
+
readonly rowCount?: number;
|
|
65
|
+
readonly code?: string;
|
|
66
|
+
};
|
|
67
|
+
type RlsQueryFn = (sql: string, values?: readonly unknown[]) => Promise<RlsQueryResult>;
|
|
68
|
+
type RlsParityOptions = {
|
|
69
|
+
readonly query: RlsQueryFn;
|
|
70
|
+
readonly fixtures: readonly RlsParityFixture[];
|
|
71
|
+
/**
|
|
72
|
+
* `supabase` and `neon` set `request.jwt.claims`; a test database stubs
|
|
73
|
+
* `auth.jwt()` / `auth.uid()` or `auth.session()` / `auth.user_id()` over it.
|
|
74
|
+
*/
|
|
75
|
+
readonly dialect?: "supabase" | "neon" | "guc";
|
|
76
|
+
readonly gucPrefix?: string;
|
|
77
|
+
readonly tenantClaim?: string;
|
|
78
|
+
/** Claim (or `guc` setting) the RLS helpers read global roles from. Default `user_role`. */
|
|
79
|
+
readonly roleClaim?: string;
|
|
80
|
+
readonly role?: "authenticated" | "anon";
|
|
81
|
+
/**
|
|
82
|
+
* Tenant-defined roles the subjects' memberships may hold. `decide` resolves them through a
|
|
83
|
+
* `RoleSource`; each membership's claim carries them as the compact `grants` map, which the
|
|
84
|
+
* helpers read in `jwt` mode. Seeding the `database`-mode tables is the caller's job.
|
|
85
|
+
*/
|
|
86
|
+
readonly customRoles?: readonly CustomRole[];
|
|
87
|
+
/** Also decide each case from the subject's serialized snapshot (`fromSnapshot`); it must agree. */
|
|
88
|
+
readonly snapshot?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* With `rls generate --fields views`: for each instance read, the row of
|
|
91
|
+
* `<table>_visible` must hold exactly the columns `pick` keeps (plus the key,
|
|
92
|
+
* which always passes through). A table with no view is read directly.
|
|
93
|
+
*/
|
|
94
|
+
readonly fieldViews?: boolean;
|
|
95
|
+
};
|
|
96
|
+
/** Columns with a value in the view's row and in `pick`'s result, sorted. */
|
|
97
|
+
type RlsFieldsOutcome = {
|
|
98
|
+
readonly app: readonly string[];
|
|
99
|
+
readonly database: readonly string[] | string;
|
|
100
|
+
};
|
|
101
|
+
type RlsParityCase = {
|
|
102
|
+
readonly name: string;
|
|
103
|
+
readonly granted: boolean;
|
|
104
|
+
readonly database: RlsDbOutcome;
|
|
105
|
+
/** The snapshot's decision, with `snapshot: true`. */
|
|
106
|
+
readonly snapshot?: boolean;
|
|
107
|
+
/** The field view check, with `fieldViews: true`; `database` is an error code when the read failed. */
|
|
108
|
+
readonly fields?: RlsFieldsOutcome;
|
|
109
|
+
readonly ok: boolean;
|
|
110
|
+
};
|
|
111
|
+
type RlsParityReport = {
|
|
112
|
+
readonly ok: boolean;
|
|
113
|
+
readonly results: readonly RlsParityCase[];
|
|
114
|
+
};
|
|
115
|
+
export declare function rlsParity<TUser>(policy: Policy<TUser>, options: RlsParityOptions): Promise<RlsParityReport>;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/testing/orm-parity.d.ts
|
|
118
|
+
type OrmParityScenario<TUser = unknown> = {
|
|
119
|
+
readonly name: string;
|
|
120
|
+
/** What the policy's `subject` mapper receives, as in `createPermDock(policy, user)`. */
|
|
121
|
+
readonly user: TUser;
|
|
122
|
+
/** The active tenant and role sources, as in `createPermDock(policy, user, options)`. */
|
|
123
|
+
readonly options?: PermDockOptions;
|
|
124
|
+
/** An instance permission; its `where()` filters the table. */
|
|
125
|
+
readonly permission: Permission<string, unknown, "instance">;
|
|
126
|
+
/** Every row of the table, exactly as the database holds them. */
|
|
127
|
+
readonly rows: readonly Readonly<Record<string, unknown>>[];
|
|
128
|
+
};
|
|
129
|
+
type OrmParityRunInput<TUser = unknown> = {
|
|
130
|
+
readonly scenario: OrmParityScenario<TUser>;
|
|
131
|
+
readonly where: WhereResult;
|
|
132
|
+
};
|
|
133
|
+
type OrmParityOptions<TUser = unknown> = {
|
|
134
|
+
/** Runs `toWhere(where)` against the database and returns the matching row ids. */
|
|
135
|
+
readonly run: (input: OrmParityRunInput<TUser>) => Promise<readonly unknown[]>;
|
|
136
|
+
/** The row id field; default `id`. */
|
|
137
|
+
readonly id?: string;
|
|
138
|
+
};
|
|
139
|
+
type OrmParityCase = {
|
|
140
|
+
readonly name: string;
|
|
141
|
+
readonly expected: readonly string[];
|
|
142
|
+
readonly actual: readonly string[];
|
|
143
|
+
/**
|
|
144
|
+
* `where()` left out a non-portable grant. `toWhere` refusing it is
|
|
145
|
+
* fail-closed and passes; rows it does return must be a subset.
|
|
146
|
+
*/
|
|
147
|
+
readonly partial: boolean;
|
|
148
|
+
readonly error?: string;
|
|
149
|
+
readonly ok: boolean;
|
|
150
|
+
};
|
|
151
|
+
type OrmParityReport = {
|
|
152
|
+
readonly ok: boolean;
|
|
153
|
+
readonly results: readonly OrmParityCase[];
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* ORM parity: for each scenario, the rows `permdock.filter()` keeps in memory
|
|
157
|
+
* must be exactly the rows the database returns for `toWhere(permdock.where())`.
|
|
158
|
+
* With a `relations` option, the graph is loaded for the rows first, so the
|
|
159
|
+
* in-memory side walks the same graph the database query does.
|
|
160
|
+
* A partial `where()` (a closure grant it cannot compile) may be refused or
|
|
161
|
+
* return fewer rows, never more.
|
|
162
|
+
*/
|
|
163
|
+
export declare function ormParity<TUser>(policy: Policy<TUser>, scenarios: readonly OrmParityScenario<TUser>[], options: OrmParityOptions<TUser>): Promise<OrmParityReport>;
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/testing/client-parity.d.ts
|
|
166
|
+
type ClientParityCase = {
|
|
167
|
+
readonly name: string;
|
|
168
|
+
/** The value the policy's `subject` function receives. */
|
|
169
|
+
readonly user: unknown;
|
|
170
|
+
readonly tenant?: string | undefined;
|
|
171
|
+
readonly permission: Permission;
|
|
172
|
+
readonly row?: unknown;
|
|
173
|
+
/**
|
|
174
|
+
* `true` when the client may deny what the server grants, because the
|
|
175
|
+
* outcome depends on a closure, a quota or an approval the snapshot omits.
|
|
176
|
+
*/
|
|
177
|
+
readonly stricter?: boolean;
|
|
178
|
+
};
|
|
179
|
+
type ClientParityOptions = {
|
|
180
|
+
readonly customRoles?: RoleSource;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Asserts that `fromSnapshot(permdock.snapshot())` never grants what the
|
|
184
|
+
* server denies, and matches it exactly unless a case is marked `stricter`.
|
|
185
|
+
*/
|
|
186
|
+
export declare function testClientParity(policy: Policy, cases: readonly ClientParityCase[], options?: ClientParityOptions): void;
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/testing/client-store.d.ts
|
|
189
|
+
type ClientStoreStatus = "ready" | "pending" | "stale" | "server-only";
|
|
190
|
+
/** The client surface every UI adapter hands to components. */
|
|
191
|
+
type ClientStorePermDock = PermDock & {
|
|
192
|
+
status(permission?: Permission, data?: unknown): ClientStoreStatus;
|
|
193
|
+
refresh(query?: {
|
|
194
|
+
readonly tenant?: string;
|
|
195
|
+
}): Promise<void>;
|
|
196
|
+
clear(): void;
|
|
197
|
+
subscribe(listener: () => void): () => void;
|
|
198
|
+
};
|
|
199
|
+
type ClientStoreHandle = {
|
|
200
|
+
get(): ClientStorePermDock;
|
|
201
|
+
/** Hydrate from a server-pushed snapshot (a Server Action result, a socket message). */
|
|
202
|
+
replace(value: unknown): void;
|
|
203
|
+
};
|
|
204
|
+
type ClientStoreFactoryOptions = {
|
|
205
|
+
readonly snapshot: Snapshot | string;
|
|
206
|
+
readonly snapshotUrl?: string;
|
|
207
|
+
readonly endpoint?: string;
|
|
208
|
+
readonly tenant?: string;
|
|
209
|
+
readonly fetch: typeof fetch;
|
|
210
|
+
readonly verifier?: TokenVerifier;
|
|
211
|
+
};
|
|
212
|
+
type ClientStoreFactory = (options: ClientStoreFactoryOptions) => ClientStoreHandle;
|
|
213
|
+
/**
|
|
214
|
+
* Race and cache scenarios for a client snapshot store: tenant switches,
|
|
215
|
+
* `replace` and `refresh`, logout and a second user during a refresh, stale
|
|
216
|
+
* and signed snapshots, and endpoint answers cached per tenant. Runs against
|
|
217
|
+
* the saas domain; `createStore` wraps the store an adapter ships.
|
|
218
|
+
*/
|
|
219
|
+
export declare function testClientStore(name: string, createStore: ClientStoreFactory): void;
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/testing/snapshot-fixture.d.ts
|
|
222
|
+
export declare function snapshotFixture(policy: Policy, subject: unknown, options?: {
|
|
223
|
+
readonly include?: readonly (Permission | {
|
|
224
|
+
readonly [key: string]: unknown;
|
|
225
|
+
})[];
|
|
226
|
+
readonly tenants?: "all";
|
|
227
|
+
readonly tenant?: string;
|
|
228
|
+
readonly simulated?: boolean;
|
|
229
|
+
}): Promise<Snapshot>;
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/testing/http-adapter.d.ts
|
|
232
|
+
/**
|
|
233
|
+
* What a mount wires into its framework. Every value is server-side: the
|
|
234
|
+
* subject comes from a verified bearer token, memberships and plans from the
|
|
235
|
+
* seed, the tenant from the `:org` path segment.
|
|
236
|
+
*/
|
|
237
|
+
type HttpScenarioDomain = {
|
|
238
|
+
readonly policy: typeof saasPolicy;
|
|
239
|
+
/**
|
|
240
|
+
* The verified principal for an `Authorization` header, with the plans of
|
|
241
|
+
* the org in the first segment of `path`. The path, not a route param, so
|
|
242
|
+
* the answer is the same in a global middleware that runs before routing.
|
|
243
|
+
*/
|
|
244
|
+
readonly subject: (authorization: string | null | undefined, path: string) => Promise<Principal | null>;
|
|
245
|
+
/** `subject` as a full `Subject` whose `expiresAt` is the token's `exp`. */
|
|
246
|
+
readonly session: (authorization: string | null | undefined, path: string) => Promise<Subject | null>;
|
|
247
|
+
/** The feed stream mounts pass as `revocations`. */
|
|
248
|
+
readonly revocations: RevocationFeed;
|
|
249
|
+
/**
|
|
250
|
+
* The seed projects of an org, one every 20 ms, round and round until
|
|
251
|
+
* `signal` aborts: the source of the events stream.
|
|
252
|
+
*/
|
|
253
|
+
readonly ticks: (org: string | undefined, signal: AbortSignal) => AsyncIterable<SaasProject>;
|
|
254
|
+
/** The `:org` segment of a path; mounts may read their route param instead. */
|
|
255
|
+
readonly org: (path: string) => string | undefined;
|
|
256
|
+
readonly customRoles: RoleSource;
|
|
257
|
+
readonly store: ApprovalStore;
|
|
258
|
+
readonly limits: LimitStore;
|
|
259
|
+
/** A project row by id, whatever its org: tenant checks belong to the policy. */
|
|
260
|
+
readonly project: (id: string | undefined) => SaasProject | null;
|
|
261
|
+
};
|
|
262
|
+
type HttpOp = {
|
|
263
|
+
readonly op: "project.get";
|
|
264
|
+
readonly org: string;
|
|
265
|
+
readonly id: string;
|
|
266
|
+
} | {
|
|
267
|
+
readonly op: "project.update";
|
|
268
|
+
readonly org: string;
|
|
269
|
+
readonly id: string;
|
|
270
|
+
} | {
|
|
271
|
+
readonly op: "project.create";
|
|
272
|
+
readonly org: string;
|
|
273
|
+
readonly body: unknown;
|
|
274
|
+
} | {
|
|
275
|
+
readonly op: "project.delete";
|
|
276
|
+
readonly org: string;
|
|
277
|
+
readonly id: string;
|
|
278
|
+
} | {
|
|
279
|
+
readonly op: "project.upload";
|
|
280
|
+
readonly org: string;
|
|
281
|
+
readonly id: string;
|
|
282
|
+
readonly file: {
|
|
283
|
+
readonly name: string;
|
|
284
|
+
readonly content: string;
|
|
285
|
+
};
|
|
286
|
+
} | {
|
|
287
|
+
readonly op: "analytics.read";
|
|
288
|
+
readonly org: string;
|
|
289
|
+
} | {
|
|
290
|
+
readonly op: "apiKey.create";
|
|
291
|
+
readonly org: string;
|
|
292
|
+
} | {
|
|
293
|
+
readonly op: "apiKey.revokeAll";
|
|
294
|
+
readonly org: string;
|
|
295
|
+
} | {
|
|
296
|
+
readonly op: "admin.members";
|
|
297
|
+
readonly org: string;
|
|
298
|
+
} | {
|
|
299
|
+
readonly op: "evaluations";
|
|
300
|
+
readonly org: string;
|
|
301
|
+
readonly body: unknown;
|
|
302
|
+
};
|
|
303
|
+
type HttpCall = HttpOp & {
|
|
304
|
+
/** `Bearer <token>` or `null` for an anonymous call. */
|
|
305
|
+
readonly authorization: string | null;
|
|
306
|
+
/** Sent as the `PermDock-Approval` header. */
|
|
307
|
+
readonly approval?: string;
|
|
308
|
+
};
|
|
309
|
+
type HttpResult = {
|
|
310
|
+
readonly status: number;
|
|
311
|
+
readonly body: unknown;
|
|
312
|
+
};
|
|
313
|
+
type HttpMounted = {
|
|
314
|
+
/**
|
|
315
|
+
* A fetch-shaped entry for REST adapters. It must serve:
|
|
316
|
+
*
|
|
317
|
+
* - `GET /:org/projects/:id`: `protect(project.read, row)`, 200 with the row
|
|
318
|
+
* - `PATCH /:org/projects/:id`: `protect(project.update, row)`, 200
|
|
319
|
+
* - `POST /:org/projects`: `protect(project.create, body, { trusted: false })`, 201 with the body
|
|
320
|
+
* - `DELETE /:org/projects/:id`: `protect(project.read, row)`, then the handler
|
|
321
|
+
* calls `permdock.assert(project.delete, row)`; 204
|
|
322
|
+
* - `POST /:org/projects/:id/files`: `protect(project.update, row)`, then the
|
|
323
|
+
* framework's own multipart parser reads field `file`; 201 `{ name, size }`
|
|
324
|
+
* - `GET /:org/analytics`: `protect(analytics.read)`, 200
|
|
325
|
+
* - `POST /:org/api-keys`: `protect(apiKey.create)`, 201
|
|
326
|
+
* - `POST /:org/api-keys/revoke-all`: `protect(apiKey.revokeAll)`, 204
|
|
327
|
+
* - `GET /:org/admin/members`: in a sub-app, router or group; `protect(member.list)`, 200
|
|
328
|
+
* - `POST /:org/permdock/access/v1/evaluations`: the adapter's decision endpoint
|
|
329
|
+
*
|
|
330
|
+
* A global `permdock()` middleware runs before every route.
|
|
331
|
+
*
|
|
332
|
+
* With `streams: true` it also serves `GET /:org/projects/:id/events`:
|
|
333
|
+
* `protect(project.read, row)` with the subject from `domain.session`,
|
|
334
|
+
* then a connection opened for `project.read` on the row with
|
|
335
|
+
* `revocations: domain.revocations`, streaming `domain.ticks(org)` as
|
|
336
|
+
* `text/event-stream` `data:` frames of JSON rows, dropping rows the
|
|
337
|
+
* subscriber cannot `project.update`, and ending with an
|
|
338
|
+
* `event: permdock` frame carrying the Problem Details when the connection aborts.
|
|
339
|
+
*/
|
|
340
|
+
readonly fetch?: (request: Request) => Promise<Response>;
|
|
341
|
+
/** RPC adapters map a call to a procedure and its error to an HTTP status. */
|
|
342
|
+
readonly call?: (call: HttpCall) => Promise<HttpResult>;
|
|
343
|
+
readonly close?: () => Promise<void> | void;
|
|
344
|
+
};
|
|
345
|
+
type HttpScenarioName = "tenant" | "roles" | "custom-roles" | "plans" | "expired" | "limits" | "validation" | "assert" | "approval" | "stale-approval" | "evaluations" | "upload" | "sub-app" | "parallel" | "stream-revoked" | "stream-demoted" | "stream-filter" | "stream-expired";
|
|
346
|
+
type HttpAdapterOptions = {
|
|
347
|
+
readonly name: string;
|
|
348
|
+
readonly mount: (domain: HttpScenarioDomain) => HttpMounted | Promise<HttpMounted>;
|
|
349
|
+
/** Scenarios the transport cannot express (multipart over RPC), each with a reason. */
|
|
350
|
+
readonly skip?: Readonly<Partial<Record<HttpScenarioName, string>>>;
|
|
351
|
+
/** The mount serves the events stream; the `stream-*` scenarios run only then. */
|
|
352
|
+
readonly streams?: boolean;
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Real-life HTTP scenarios over the shared SaaS domain. `mount` builds the
|
|
356
|
+
* application once with the adapter under test; every expectation below is
|
|
357
|
+
* written by hand, never computed with PermDock.
|
|
358
|
+
*/
|
|
359
|
+
export declare function testHttpAdapter(options: HttpAdapterOptions): void;
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/testing/authzen.d.ts
|
|
362
|
+
type Entity = {
|
|
363
|
+
readonly type: string;
|
|
364
|
+
readonly id?: string;
|
|
365
|
+
readonly properties?: Readonly<Record<string, unknown>>;
|
|
366
|
+
};
|
|
367
|
+
type AuthZenEvaluationVector = {
|
|
368
|
+
readonly request: {
|
|
369
|
+
readonly subject: Entity;
|
|
370
|
+
readonly action: {
|
|
371
|
+
readonly name: string;
|
|
372
|
+
};
|
|
373
|
+
readonly resource: Entity;
|
|
374
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
375
|
+
};
|
|
376
|
+
readonly expected: boolean;
|
|
377
|
+
};
|
|
378
|
+
type AuthZenEvaluationsVector = {
|
|
379
|
+
readonly request: {
|
|
380
|
+
readonly subject?: Entity;
|
|
381
|
+
readonly action?: {
|
|
382
|
+
readonly name: string;
|
|
383
|
+
};
|
|
384
|
+
readonly resource?: Entity;
|
|
385
|
+
readonly evaluations: readonly {
|
|
386
|
+
readonly subject?: Entity;
|
|
387
|
+
readonly action?: {
|
|
388
|
+
readonly name: string;
|
|
389
|
+
};
|
|
390
|
+
readonly resource?: Entity;
|
|
391
|
+
}[];
|
|
392
|
+
};
|
|
393
|
+
readonly expected: readonly {
|
|
394
|
+
readonly decision: boolean;
|
|
395
|
+
}[];
|
|
396
|
+
};
|
|
397
|
+
type AuthZenSearchVector = {
|
|
398
|
+
readonly request: Readonly<Record<string, unknown>>;
|
|
399
|
+
readonly expected: {
|
|
400
|
+
readonly results: readonly Readonly<Record<string, unknown>>[];
|
|
401
|
+
};
|
|
402
|
+
};
|
|
403
|
+
/**
|
|
404
|
+
* Vectors in the layout of the OpenID AuthZEN interop harness
|
|
405
|
+
* (`decisions-*.json` for Basic and Batch, `search/{subject,resource,action}`
|
|
406
|
+
* `results.json` for Search), so the official files load unchanged.
|
|
407
|
+
*/
|
|
408
|
+
type AuthZenVectors = {
|
|
409
|
+
readonly evaluation?: readonly AuthZenEvaluationVector[];
|
|
410
|
+
readonly evaluations?: readonly AuthZenEvaluationsVector[];
|
|
411
|
+
readonly search?: {
|
|
412
|
+
readonly subject?: readonly AuthZenSearchVector[];
|
|
413
|
+
readonly resource?: readonly AuthZenSearchVector[];
|
|
414
|
+
readonly action?: readonly AuthZenSearchVector[];
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
type TestAuthZenOptions = {
|
|
418
|
+
readonly vectors: AuthZenVectors;
|
|
419
|
+
/** Origin the requests are addressed to; the discovery document must name it. */
|
|
420
|
+
readonly origin?: string;
|
|
421
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
422
|
+
/** Check `/.well-known/authzen-configuration`; on by default. */
|
|
423
|
+
readonly discovery?: boolean;
|
|
424
|
+
};
|
|
425
|
+
/** Runs AuthZEN 1.0 interop vectors against an Authorization API endpoint. */
|
|
426
|
+
export declare function testAuthZen(handle: (request: Request) => Promise<Response>, options: TestAuthZenOptions): void;
|
|
427
|
+
type TodoUser = {
|
|
428
|
+
readonly id: string;
|
|
429
|
+
readonly identity: string;
|
|
430
|
+
readonly roles: readonly ("admin" | "editor" | "viewer" | "evil_genius")[];
|
|
431
|
+
};
|
|
432
|
+
/** The AuthZEN interop Todo directory: each user is addressable by email or by identity. */
|
|
433
|
+
export declare const authzenTodoUsers: readonly TodoUser[];
|
|
434
|
+
type AuthzenTodoDefinition = {
|
|
435
|
+
readonly user: ResourceInit<unknown, readonly ["can_read_user"], readonly []>;
|
|
436
|
+
readonly todo: ResourceInit<unknown, readonly ["can_read_todos", "can_create_todo", "can_update_todo", "can_delete_todo"], readonly []>;
|
|
437
|
+
};
|
|
438
|
+
export declare const authzenTodoPermissions: InferPermissionTree<AuthzenTodoDefinition>;
|
|
439
|
+
/** The interop Todo rules: viewers read, editors write their own, admins delete and evil geniuses update anything. */
|
|
440
|
+
export declare const authzenTodoPolicy: Policy;
|
|
441
|
+
/** Rows for `resources.todo.list` and records for `subjects.list` in the Todo domain. */
|
|
442
|
+
export declare const authzenTodoData: {
|
|
443
|
+
readonly todos: readonly {
|
|
444
|
+
readonly id: string;
|
|
445
|
+
readonly ownerID: string;
|
|
446
|
+
}[];
|
|
447
|
+
readonly users: readonly {
|
|
448
|
+
readonly id: string;
|
|
449
|
+
}[];
|
|
450
|
+
};
|
|
451
|
+
/**
|
|
452
|
+
* PermDock's vectors over the interop Todo domain, in the interop layout:
|
|
453
|
+
* every user against every action on their own and on Rick's todo, one batch
|
|
454
|
+
* per user, and the three searches.
|
|
455
|
+
*/
|
|
456
|
+
export declare function authzenTodoVectors(): AuthZenVectors;
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/testing/conformance.d.ts
|
|
459
|
+
export declare function testSubjectResolver<TInput>(resolver: SubjectResolver<TInput>, options: {
|
|
460
|
+
readonly invalid: TInput;
|
|
461
|
+
}): void;
|
|
462
|
+
export declare function testMembershipSource(source: MembershipSource, options: {
|
|
463
|
+
readonly principals: readonly {
|
|
464
|
+
readonly id: string;
|
|
465
|
+
readonly kind?: string;
|
|
466
|
+
}[];
|
|
467
|
+
readonly expect?: Record<string, readonly Membership[]>;
|
|
468
|
+
/** With the policy, every membership must name one of its scopes and carry its parent ids. */
|
|
469
|
+
readonly policy?: Policy;
|
|
470
|
+
}): void;
|
|
471
|
+
/**
|
|
472
|
+
* A `RelationSource` answers facts only: well-formed chains nearest first and
|
|
473
|
+
* no longer than `depth`, holders with principal ids or group references and
|
|
474
|
+
* numeric instants, a `truncated` chain only when more ancestors exist, and
|
|
475
|
+
* empty answers (never a throw) for an object it does not know. `expect` pins
|
|
476
|
+
* the chains, holders and links for `objects`, keyed `resource:id`,
|
|
477
|
+
* `resource:id#relation` and `resource:id>link`; a group holder is written
|
|
478
|
+
* `resource:id#relation`.
|
|
479
|
+
*/
|
|
480
|
+
export declare function testRelationSource(source: RelationSource, options: {
|
|
481
|
+
readonly objects: readonly {
|
|
482
|
+
readonly resource: string;
|
|
483
|
+
readonly id: string;
|
|
484
|
+
readonly relation?: string;
|
|
485
|
+
}[];
|
|
486
|
+
readonly expect?: {
|
|
487
|
+
readonly ancestors?: Readonly<Record<string, readonly string[]>>;
|
|
488
|
+
readonly holders?: Readonly<Record<string, readonly string[]>>;
|
|
489
|
+
/** Keyed `resource:id>link`, the id the link leads to. */
|
|
490
|
+
readonly links?: Readonly<Record<string, string>>;
|
|
491
|
+
};
|
|
492
|
+
}): void;
|
|
493
|
+
export declare function testEntitlementSource(source: EntitlementSource, options: {
|
|
494
|
+
readonly principal: {
|
|
495
|
+
readonly id: string;
|
|
496
|
+
};
|
|
497
|
+
readonly tenant: string;
|
|
498
|
+
readonly expect?: readonly string[];
|
|
499
|
+
}): void;
|
|
500
|
+
export declare function testRoleSource(source: RoleSource, options: {
|
|
501
|
+
readonly tenant: string;
|
|
502
|
+
readonly declared: readonly (string | Role)[];
|
|
503
|
+
/** When set, every custom role's `grants` must resolve inside the ceiling. */
|
|
504
|
+
readonly policy?: Policy;
|
|
505
|
+
}): void;
|
|
506
|
+
export declare function testLimitStore(store: LimitStore): void;
|
|
507
|
+
export declare function testDecisionSink(sink: DecisionSink): void;
|
|
508
|
+
export declare function testSnapshotSource(source: SnapshotSource): void;
|
|
509
|
+
export declare function testPolicySource(source: PolicySource, options?: {
|
|
510
|
+
readonly policy?: Policy;
|
|
511
|
+
}): void;
|
|
512
|
+
export declare function testReplayStore(store: ReplayStore): void;
|
|
513
|
+
export declare function testRevocationFeed(feed: RevocationFeed): void;
|
|
514
|
+
export declare function testDirectoryStore(store: DirectoryStore, options: {
|
|
515
|
+
readonly tenants: readonly [string, string];
|
|
516
|
+
}): void;
|
|
517
|
+
type ApprovalStoreOptions = {
|
|
518
|
+
/**
|
|
519
|
+
* Opens a second store over the same backing storage, as a restarted process
|
|
520
|
+
* would. Omit it for stores that do not persist (the resume-after-restart
|
|
521
|
+
* case is then skipped).
|
|
522
|
+
*/
|
|
523
|
+
readonly reopen?: () => ApprovalStore | Promise<ApprovalStore>;
|
|
524
|
+
};
|
|
525
|
+
export declare function testApprovalStore(store: ApprovalStore, options?: ApprovalStoreOptions): void;
|
|
526
|
+
export declare function testTokenVerifier(verifier: TokenVerifier, options?: {
|
|
527
|
+
readonly audience?: string;
|
|
528
|
+
readonly issuer?: string;
|
|
529
|
+
}): void;
|
|
530
|
+
/** Settings for `tenant`, or `undefined`; a thrown source fails the runner. */
|
|
531
|
+
export declare function testSettingsSource(source: SettingsSource, options: {
|
|
532
|
+
readonly tenant: string;
|
|
533
|
+
readonly unknown?: string;
|
|
534
|
+
}): void;
|
|
535
|
+
/**
|
|
536
|
+
* Checks an `ApprovalPolicySource`: entries are plain JSON, belong to the
|
|
537
|
+
* queried tenant or to none, and every entry loads against `policy` (an
|
|
538
|
+
* entry that does not would deny every call).
|
|
539
|
+
*/
|
|
540
|
+
export declare function testApprovalPolicySource(source: ApprovalPolicySource, options: {
|
|
541
|
+
readonly policy: Policy;
|
|
542
|
+
readonly tenant: string;
|
|
543
|
+
readonly unknown?: string;
|
|
544
|
+
}): void;
|
|
545
|
+
/**
|
|
546
|
+
* `key` is a live key the verifier knows, or a function issuing one (called
|
|
547
|
+
* once). `revoke`, when given, revokes it; the runner then expects the key
|
|
548
|
+
* to stop verifying.
|
|
549
|
+
*/
|
|
550
|
+
export declare function testCredentialVerifier(verifier: CredentialVerifier, options: {
|
|
551
|
+
readonly key: string | (() => string | Promise<string>);
|
|
552
|
+
readonly revoke?: () => void | Promise<void>;
|
|
553
|
+
}): void;
|
|
554
|
+
export declare function testTokenSigner(signer: TokenSigner, options: {
|
|
555
|
+
readonly verifier: TokenVerifier;
|
|
556
|
+
}): void;
|
|
557
|
+
export declare function testWhereCompiler<TTarget>(compiler: WhereCompiler<TTarget>, options: {
|
|
558
|
+
readonly target: TTarget;
|
|
559
|
+
readonly isFailClosed?: (compiled: unknown) => boolean;
|
|
560
|
+
}): void;
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/testing/jwt-fixtures.d.ts
|
|
563
|
+
export declare const jwtFixtureJwks: {
|
|
564
|
+
readonly keys: readonly [{
|
|
565
|
+
readonly crv: "Ed25519";
|
|
566
|
+
readonly x: "79ab4WR6Eb9LkefWpmh5ZlvjXg7wqVGNMwIEHQqduIQ";
|
|
567
|
+
readonly kty: "OKP";
|
|
568
|
+
readonly kid: "2026-09";
|
|
569
|
+
readonly alg: "Ed25519";
|
|
570
|
+
readonly use: "sig";
|
|
571
|
+
}];
|
|
572
|
+
};
|
|
573
|
+
export declare const jwtFixtureIssuer: "https://login.example.com";
|
|
574
|
+
export declare const jwtFixtureAudience: "https://api.example.com";
|
|
575
|
+
export declare const jwtFixtureTokens: {
|
|
576
|
+
readonly valid: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6ImF0K2p3dCJ9.eyJzdWIiOiJ1XzEiLCJjbGllbnRfaWQiOiJhcHAiLCJyb2xlcyI6WyJtZW1iZXIiXSwianRpIjoianRpLXZhbGlkIiwiaXNzIjoiaHR0cHM6Ly9sb2dpbi5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjIwMDAwMDAwMDB9.ENFMmLSyVde-2s4Gdz4iP7srZvU0Xuy_ZLz-OJGHD_-W1lEayEBeSPTnab0L_TBdj8p5BmkFrT-7VE7o20GbAw";
|
|
577
|
+
readonly expired: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6ImF0K2p3dCJ9.eyJzdWIiOiJ1XzEiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4YW1wbGUuY29tIiwiYXVkIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20iLCJpYXQiOjEwMDAwMDAwMDAsImV4cCI6MTEwMDAwMDAwMH0.9X3d6kH3_2nmrbqMxuoxzFW-wsWJVCe-lR-aQ-MmsGGXlEUBMgQ-4MtAq1UR9lLE0h2b5JrE_GfjzuC04h5xCg";
|
|
578
|
+
readonly wrongAud: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6ImF0K2p3dCJ9.eyJzdWIiOiJ1XzEiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4YW1wbGUuY29tIiwiYXVkIjoiaHR0cHM6Ly9vdGhlci5leGFtcGxlLmNvbSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwfQ.PgWdzi56J31OwoMqH4ysHzVKSKUDhi-AtHFUp1ewFzi-qGE-tknzrNKnKhDOncVzj0okb_flyy3vQgfzJwG5AQ";
|
|
579
|
+
readonly wrongIss: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6ImF0K2p3dCJ9.eyJzdWIiOiJ1XzEiLCJpc3MiOiJodHRwczovL2V2aWwuZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwfQ.tIcLHLa9JLtKz1gqjRGdr5DFbUzRDs5KsoPsJCxspvbBcsFfRFG2G0XJnd1h4QHyZ_5B4OhY3W_jIIsnOPD9Bg";
|
|
580
|
+
readonly unknownKid: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoibm9wZSIsInR5cCI6ImF0K2p3dCJ9.eyJzdWIiOiJ1XzEiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4YW1wbGUuY29tIiwiYXVkIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20iLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MjAwMDAwMDAwMH0.walgG5GvpCVBnkMtkdw-epi5c93JqfvAM_N4EWR9Tqhiw_cnzITnlQyoVfihECmV1fqtFrhwM1RCIRJvd38zAQ";
|
|
581
|
+
readonly none: "eyJhbGciOiJub25lIn0.eyJzdWIiOiJ1XzEiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4YW1wbGUuY29tIiwiYXVkIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20iLCJleHAiOjIwMDAwMDAwMDAsImlhdCI6MTcwMDAwMDAwMH0.";
|
|
582
|
+
readonly snapshot: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6InBlcm1kb2NrLXNuYXBzaG90K2p3dCJ9.eyJzbmFwc2hvdCI6eyJ2IjoyLCJpc3N1ZWRBdCI6MTcwMDAwMDAwMCwic3ViamVjdCI6eyJwcmluY2lwYWwiOnsiaWQiOiJ1XzEiLCJyb2xlcyI6WyJtZW1iZXIiXX0sImNvbnRleHQiOnt9fSwicm9sZXMiOlsibWVtYmVyIl0sImdyYW50cyI6W10sInRlbmFudHMiOltdfSwic3ViIjoidV8xIiwiaXNzIjoiaHR0cHM6Ly9hcHAuZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwczovL2FwcC5leGFtcGxlLmNvbSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwLCJqdGkiOiJzbmFwX2ZpeHR1cmUifQ.4itaYctluoCwU-syytUxArtS_FRyNAti2SZxuSH-nKIMr7-Sb3h1Q3xugn_vPR1eupV4fVNIe6dH27_EiyDvCA";
|
|
583
|
+
readonly approval: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6InBlcm1kb2NrLWFwcHJvdmFsK2p3dCJ9.eyJhcHByb3ZhbCI6eyJ0b2tlbiI6InBkMS5hYmMiLCJwZXJtaXNzaW9uIjoicG9zdC5kZWxldGUiLCJyZXNvdXJjZSI6eyJ0eXBlIjoicG9zdCIsImlkIjoiNDIifSwic3RhdHVzIjoiYXBwcm92ZWQifSwic3ViIjoidV8xIiwiaXNzIjoiaHR0cHM6Ly9hcHAuZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwczovL2FwcC5leGFtcGxlLmNvbSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwLCJqdGkiOiJhcHJfZml4dHVyZSJ9.UHsoSzQCo68G7ee-EFqdzMLvmqaKlFIMdQKFuqZEM4yUQnJsJJTXbld_aCXcyfN-IdjF9cj2F5EHzsV4nLKbBg";
|
|
584
|
+
readonly decisions: "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNi0wOSIsInR5cCI6InBlcm1kb2NrLWRlY2lzaW9ucytqd3QifQ.eyJldmVudHMiOltdLCJpc3MiOiJodHRwczovL2FwcC5leGFtcGxlLmNvbSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoyMDAwMDAwMDAwLCJqdGkiOiJkZWNfZml4dHVyZSJ9.UZhJd72yFGxHOdjYbzcGYUPgYjvLwOPCtHKxEbLu56SdKSKUDCREXmEBI9WaCPKWjlTBnnIKMYwjG8I8RATJCg";
|
|
585
|
+
};
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/testing/supabase-fixtures.d.ts
|
|
588
|
+
/**
|
|
589
|
+
* Claim sets in the shape the Supabase custom access token hook produces (the RBAC guide's
|
|
590
|
+
* `user_role` claim, optionally mirrored into `app_metadata`, plus a `memberships` array for
|
|
591
|
+
* multi-org apps). `betterSupabase` is the canonical shape better-supabase 0.5 emits: scoped
|
|
592
|
+
* memberships, `tenant_id` and per-tenant plans in `features`. `full` sets every field of the
|
|
593
|
+
* claim contract plus a `hook.claims` extra claim; `portalContact`, `oauthClient` and
|
|
594
|
+
* `actChain` cover a customer contact, a Supabase OAuth server token and an RFC 8693 chain.
|
|
595
|
+
* `supportSession`, `supportSessionReadOnly` and `impersonation` are the `act.kind` tokens
|
|
596
|
+
* better-supabase mints; `anonymousSignIn` is a `signInAnonymously()` token read with
|
|
597
|
+
* `anonymousSignIns: 'deny'`. Every fixture passes `supabaseClaims()` and
|
|
598
|
+
* `schemas/supabase-claims-v1.json`. Plain data: no Supabase or better-supabase types.
|
|
599
|
+
*/
|
|
600
|
+
type SupabaseClaimFixture = {
|
|
601
|
+
readonly claims: Readonly<Record<string, unknown>>;
|
|
602
|
+
/** `subjectFromSupabase` options the case needs. */
|
|
603
|
+
readonly options?: {
|
|
604
|
+
readonly plans?: string;
|
|
605
|
+
readonly anonymousSignIns?: "deny";
|
|
606
|
+
};
|
|
607
|
+
readonly expect: {
|
|
608
|
+
readonly id: string | null;
|
|
609
|
+
readonly roles: readonly string[];
|
|
610
|
+
readonly memberships: readonly {
|
|
611
|
+
readonly tenant?: string;
|
|
612
|
+
readonly scope?: string;
|
|
613
|
+
readonly id?: string;
|
|
614
|
+
readonly within?: Readonly<Record<string, string>>;
|
|
615
|
+
readonly on?: {
|
|
616
|
+
readonly resource: string;
|
|
617
|
+
readonly id: string;
|
|
618
|
+
};
|
|
619
|
+
readonly roles: readonly string[];
|
|
620
|
+
readonly via?: string;
|
|
621
|
+
readonly expiresAt?: number;
|
|
622
|
+
readonly grantedBy?: string;
|
|
623
|
+
readonly reason?: string;
|
|
624
|
+
readonly managedBy?: "idp";
|
|
625
|
+
readonly entitlements?: readonly string[];
|
|
626
|
+
}[];
|
|
627
|
+
readonly tenant?: string;
|
|
628
|
+
readonly plans?: readonly string[];
|
|
629
|
+
/** `subject.actor`, absent when the token has neither `act` nor `client_id`. */
|
|
630
|
+
readonly actor?: {
|
|
631
|
+
readonly id: string;
|
|
632
|
+
readonly kind: "oauth-client" | "impersonation";
|
|
633
|
+
} | {
|
|
634
|
+
readonly id: string;
|
|
635
|
+
readonly kind: "support";
|
|
636
|
+
readonly sessionId: string;
|
|
637
|
+
readonly readOnly: boolean;
|
|
638
|
+
};
|
|
639
|
+
/** `subject.delegation`: the `scope` claim and the `act` chain of an `oauth-client` actor. */
|
|
640
|
+
readonly delegation?: {
|
|
641
|
+
readonly scopes?: readonly string[];
|
|
642
|
+
readonly chain?: Readonly<Record<string, unknown>>;
|
|
643
|
+
};
|
|
644
|
+
};
|
|
645
|
+
};
|
|
646
|
+
type SupabaseClaimFixtureName = "topLevelRole" | "appMetadataRole" | "nullTopLevelFallsBack" | "arrayRoles" | "missingRole" | "nullRole" | "userMetadataIgnored" | "multiOrg" | "betterSupabase" | "full" | "portalContact" | "oauthClient" | "actChain" | "supportSession" | "supportSessionReadOnly" | "impersonation" | "anonymousSignIn" | "anon" | "serviceRole";
|
|
647
|
+
export declare const supabaseClaimFixtures: Readonly<Record<SupabaseClaimFixtureName, SupabaseClaimFixture>>;
|
|
648
|
+
/**
|
|
649
|
+
* The `permdock supabase inspect --json` manifest for a policy with one
|
|
650
|
+
* `tenant` scope, the default `supabase.hook` and a `features` claim from
|
|
651
|
+
* `better_supabase.feature_claims`. A package that reads the manifest tests its
|
|
652
|
+
* parser against this value.
|
|
653
|
+
*/
|
|
654
|
+
export declare const supabaseHookManifestFixture: SupabaseHookManifest;
|
|
655
|
+
//#endregion
|
|
656
|
+
export { type ApprovalStoreOptions, type AuthZenEvaluationVector, type AuthZenEvaluationsVector, type AuthZenSearchVector, type AuthZenVectors, type ClientParityCase, type ClientParityOptions, type ClientStoreFactory, type ClientStoreFactoryOptions, type ClientStoreHandle, type ClientStorePermDock, type ClientStoreStatus, type DescribePolicyConfig, type HttpAdapterOptions, type HttpCall, type HttpMounted, type HttpOp, type HttpResult, type HttpScenarioDomain, type HttpScenarioName, type MatrixCell, type MatrixOutcome, type OrmParityCase, type OrmParityOptions, type OrmParityReport, type OrmParityRunInput, type OrmParityScenario, type RlsDbOutcome, type RlsFieldsOutcome, type RlsParityCase, type RlsParityFixture, type RlsParityOptions, type RlsParityReport, type RlsParitySubject, type RlsQueryFn, type RlsQueryResult, type SupabaseClaimFixture, type SupabaseClaimFixtureName, type TestAuthZenOptions, expectTypeOf, supabaseMembershipsBudget };
|