@bizapps/design-system-testing 1.9.0 → 1.10.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 +1 -0
- package/dist/editor.cjs +1484 -0
- package/dist/editor.cjs.map +1 -0
- package/dist/editor.d.cts +428 -0
- package/dist/editor.d.ts +428 -0
- package/dist/editor.js +1467 -0
- package/dist/editor.js.map +1 -0
- package/dist/main.cjs +622 -130
- package/dist/main.cjs.map +1 -1
- package/dist/main.css +1 -1
- package/dist/main.d.cts +207 -50
- package/dist/main.d.ts +207 -50
- package/dist/main.js +617 -133
- package/dist/main.js.map +1 -1
- package/dist/policy-DPpMNLiO.d.cts +75 -0
- package/dist/policy-DPpMNLiO.d.ts +75 -0
- package/dist/policy.cjs +33 -0
- package/dist/policy.cjs.map +1 -0
- package/dist/policy.d.cts +1 -0
- package/dist/policy.d.ts +1 -0
- package/dist/policy.js +29 -0
- package/dist/policy.js.map +1 -0
- package/package.json +228 -175
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED — do not edit by hand.
|
|
3
|
+
*
|
|
4
|
+
* Regenerate with `pnpm generate-permissions`, which reads the catalog from the
|
|
5
|
+
* Nexus backend. The list is committed so the library build never depends on a
|
|
6
|
+
* live backend call.
|
|
7
|
+
*
|
|
8
|
+
* While this list is empty, `Permission` degrades to plain `string` and the
|
|
9
|
+
* development-time unknown-permission warning is skipped entirely — an empty
|
|
10
|
+
* catalog must never produce false positives.
|
|
11
|
+
*/
|
|
12
|
+
declare const KNOWN_PERMISSIONS: readonly [];
|
|
13
|
+
type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A permission string in `resource.action` or `module.resource.action`
|
|
17
|
+
* dot-notation, lowercase, with `*` as a segment wildcard and a bare `*` as the
|
|
18
|
+
* super-grant.
|
|
19
|
+
*
|
|
20
|
+
* Known catalog entries are suggested by autocomplete; any other string is still
|
|
21
|
+
* accepted, so a permission the catalog has not caught up with never blocks a
|
|
22
|
+
* build. The `(string & {})` half is the TypeScript idiom that keeps the literal
|
|
23
|
+
* suggestions from collapsing into plain `string`.
|
|
24
|
+
*/
|
|
25
|
+
type Permission = KnownPermission | (string & {});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The server-safe half of the authorization system.
|
|
29
|
+
*
|
|
30
|
+
* Everything in this module is a pure function over plain data: no React, no hooks, no
|
|
31
|
+
* context, no module-level mutable state. That is not incidental — it is the contract.
|
|
32
|
+
* `src/policy.ts` re-exports this file as the `@bizapps/ui/policy` entry point, which
|
|
33
|
+
* tsup deliberately does NOT stamp with `"use client"`, so a Next.js Server Component,
|
|
34
|
+
* Route Handler or Server Action can import it and evaluate permissions on the server.
|
|
35
|
+
*
|
|
36
|
+
* **Never import React (or anything that transitively does) into this file.** The build
|
|
37
|
+
* asserts it: `tsup.config.ts` fails if `dist/policy.js` contains a react import or a
|
|
38
|
+
* `"use client"` directive. Breaking that turns every server-side caller into a client
|
|
39
|
+
* reference, silently.
|
|
40
|
+
*
|
|
41
|
+
* The client half — `AbilityProvider`, `useAbility`, `useCan`, `Can` — consumes these
|
|
42
|
+
* same functions, so there is exactly one implementation of the matching rules. Two
|
|
43
|
+
* implementations of an authorization primitive drift, and drift on a security boundary
|
|
44
|
+
* is invisible until it matters.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Tests one entry of the user's grant set against one requested permission.
|
|
49
|
+
*
|
|
50
|
+
* @param pattern - an entry from the user's grant set; may contain `*` as a segment
|
|
51
|
+
* wildcard, or be a bare `*` meaning "everything"
|
|
52
|
+
* @param permission - the concrete permission being requested
|
|
53
|
+
*
|
|
54
|
+
* Matching is arity-strict: a grant of `users.*` covers `users.delete`, but a grant of
|
|
55
|
+
* `users` does NOT, and neither covers `users.delete.confirm`. That rules out the
|
|
56
|
+
* ambiguity of whether `users` names the resource or the namespace.
|
|
57
|
+
*/
|
|
58
|
+
declare function matchPermission(pattern: string, permission: string): boolean;
|
|
59
|
+
/** Whether any entry in the grant set covers the requested permission. */
|
|
60
|
+
declare function matchesAny(permissionSet: Set<string>, permission: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Evaluates a permission requirement against a grant set.
|
|
63
|
+
*
|
|
64
|
+
* The order of the first two checks is load-bearing and must not be swapped: an empty
|
|
65
|
+
* required list denies **even for a super-grant holder**, because an empty list means
|
|
66
|
+
* "nothing satisfies this", not "anything does".
|
|
67
|
+
*
|
|
68
|
+
* @param permissionSet - the user's grants
|
|
69
|
+
* @param perms - the permission(s) required
|
|
70
|
+
* @param mode - with an array, `'any'` (default) passes when at least one matches,
|
|
71
|
+
* `'all'` requires every one
|
|
72
|
+
*/
|
|
73
|
+
declare function can(permissionSet: Set<string>, perms: Permission | Permission[], mode?: 'any' | 'all'): boolean;
|
|
74
|
+
|
|
75
|
+
export { KNOWN_PERMISSIONS as K, type Permission as P, type KnownPermission as a, matchesAny as b, can as c, matchPermission as m };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED — do not edit by hand.
|
|
3
|
+
*
|
|
4
|
+
* Regenerate with `pnpm generate-permissions`, which reads the catalog from the
|
|
5
|
+
* Nexus backend. The list is committed so the library build never depends on a
|
|
6
|
+
* live backend call.
|
|
7
|
+
*
|
|
8
|
+
* While this list is empty, `Permission` degrades to plain `string` and the
|
|
9
|
+
* development-time unknown-permission warning is skipped entirely — an empty
|
|
10
|
+
* catalog must never produce false positives.
|
|
11
|
+
*/
|
|
12
|
+
declare const KNOWN_PERMISSIONS: readonly [];
|
|
13
|
+
type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A permission string in `resource.action` or `module.resource.action`
|
|
17
|
+
* dot-notation, lowercase, with `*` as a segment wildcard and a bare `*` as the
|
|
18
|
+
* super-grant.
|
|
19
|
+
*
|
|
20
|
+
* Known catalog entries are suggested by autocomplete; any other string is still
|
|
21
|
+
* accepted, so a permission the catalog has not caught up with never blocks a
|
|
22
|
+
* build. The `(string & {})` half is the TypeScript idiom that keeps the literal
|
|
23
|
+
* suggestions from collapsing into plain `string`.
|
|
24
|
+
*/
|
|
25
|
+
type Permission = KnownPermission | (string & {});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The server-safe half of the authorization system.
|
|
29
|
+
*
|
|
30
|
+
* Everything in this module is a pure function over plain data: no React, no hooks, no
|
|
31
|
+
* context, no module-level mutable state. That is not incidental — it is the contract.
|
|
32
|
+
* `src/policy.ts` re-exports this file as the `@bizapps/ui/policy` entry point, which
|
|
33
|
+
* tsup deliberately does NOT stamp with `"use client"`, so a Next.js Server Component,
|
|
34
|
+
* Route Handler or Server Action can import it and evaluate permissions on the server.
|
|
35
|
+
*
|
|
36
|
+
* **Never import React (or anything that transitively does) into this file.** The build
|
|
37
|
+
* asserts it: `tsup.config.ts` fails if `dist/policy.js` contains a react import or a
|
|
38
|
+
* `"use client"` directive. Breaking that turns every server-side caller into a client
|
|
39
|
+
* reference, silently.
|
|
40
|
+
*
|
|
41
|
+
* The client half — `AbilityProvider`, `useAbility`, `useCan`, `Can` — consumes these
|
|
42
|
+
* same functions, so there is exactly one implementation of the matching rules. Two
|
|
43
|
+
* implementations of an authorization primitive drift, and drift on a security boundary
|
|
44
|
+
* is invisible until it matters.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Tests one entry of the user's grant set against one requested permission.
|
|
49
|
+
*
|
|
50
|
+
* @param pattern - an entry from the user's grant set; may contain `*` as a segment
|
|
51
|
+
* wildcard, or be a bare `*` meaning "everything"
|
|
52
|
+
* @param permission - the concrete permission being requested
|
|
53
|
+
*
|
|
54
|
+
* Matching is arity-strict: a grant of `users.*` covers `users.delete`, but a grant of
|
|
55
|
+
* `users` does NOT, and neither covers `users.delete.confirm`. That rules out the
|
|
56
|
+
* ambiguity of whether `users` names the resource or the namespace.
|
|
57
|
+
*/
|
|
58
|
+
declare function matchPermission(pattern: string, permission: string): boolean;
|
|
59
|
+
/** Whether any entry in the grant set covers the requested permission. */
|
|
60
|
+
declare function matchesAny(permissionSet: Set<string>, permission: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Evaluates a permission requirement against a grant set.
|
|
63
|
+
*
|
|
64
|
+
* The order of the first two checks is load-bearing and must not be swapped: an empty
|
|
65
|
+
* required list denies **even for a super-grant holder**, because an empty list means
|
|
66
|
+
* "nothing satisfies this", not "anything does".
|
|
67
|
+
*
|
|
68
|
+
* @param permissionSet - the user's grants
|
|
69
|
+
* @param perms - the permission(s) required
|
|
70
|
+
* @param mode - with an array, `'any'` (default) passes when at least one matches,
|
|
71
|
+
* `'all'` requires every one
|
|
72
|
+
*/
|
|
73
|
+
declare function can(permissionSet: Set<string>, perms: Permission | Permission[], mode?: 'any' | 'all'): boolean;
|
|
74
|
+
|
|
75
|
+
export { KNOWN_PERMISSIONS as K, type Permission as P, type KnownPermission as a, matchesAny as b, can as c, matchPermission as m };
|
package/dist/policy.cjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/components/authorization/policy.ts
|
|
4
|
+
function matchPermission(pattern, permission) {
|
|
5
|
+
if (pattern === "*") return true;
|
|
6
|
+
if (pattern === permission) return true;
|
|
7
|
+
const patternParts = pattern.split(".");
|
|
8
|
+
const permParts = permission.split(".");
|
|
9
|
+
if (patternParts.length !== permParts.length) return false;
|
|
10
|
+
return patternParts.every((part, i) => part === "*" || part === permParts[i]);
|
|
11
|
+
}
|
|
12
|
+
function matchesAny(permissionSet, permission) {
|
|
13
|
+
if (permissionSet.has(permission)) return true;
|
|
14
|
+
for (const pattern of permissionSet) {
|
|
15
|
+
if (matchPermission(pattern, permission)) return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function can(permissionSet, perms, mode = "any") {
|
|
20
|
+
const list = Array.isArray(perms) ? perms : [perms];
|
|
21
|
+
if (list.length === 0) return false;
|
|
22
|
+
if (permissionSet.has("*")) return true;
|
|
23
|
+
if (mode === "all") {
|
|
24
|
+
return list.every((p) => matchesAny(permissionSet, p));
|
|
25
|
+
}
|
|
26
|
+
return list.some((p) => matchesAny(permissionSet, p));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
exports.can = can;
|
|
30
|
+
exports.matchPermission = matchPermission;
|
|
31
|
+
exports.matchesAny = matchesAny;
|
|
32
|
+
//# sourceMappingURL=policy.cjs.map
|
|
33
|
+
//# sourceMappingURL=policy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/authorization/policy.ts"],"names":[],"mappings":";;;AAgCA,SAAS,eAAA,CAAgB,SAAiB,UAAA,EAA6B;AACnE,EAAA,IAAI,OAAA,KAAY,KAAK,OAAO,IAAA;AAC5B,EAAA,IAAI,OAAA,KAAY,YAAY,OAAO,IAAA;AACnC,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,IAAI,YAAA,CAAa,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ,OAAO,KAAA;AACrD,EAAA,OAAO,YAAA,CAAa,KAAA,CAAM,CAAC,IAAA,EAAM,CAAA,KAAM,SAAS,GAAA,IAAO,IAAA,KAAS,SAAA,CAAU,CAAC,CAAC,CAAA;AAChF;AAGA,SAAS,UAAA,CAAW,eAA4B,UAAA,EAA6B;AACzE,EAAA,IAAI,aAAA,CAAc,GAAA,CAAI,UAAU,CAAA,EAAG,OAAO,IAAA;AAC1C,EAAA,KAAA,MAAW,WAAW,aAAA,EAAe;AACjC,IAAA,IAAI,eAAA,CAAgB,OAAA,EAAS,UAAU,CAAA,EAAG,OAAO,IAAA;AAAA,EACrD;AACA,EAAA,OAAO,KAAA;AACX;AAcA,SAAS,GAAA,CACL,aAAA,EACA,KAAA,EACA,IAAA,GAAsB,KAAA,EACf;AACP,EAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AAClD,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAC9B,EAAA,IAAI,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG,OAAO,IAAA;AACnC,EAAA,IAAI,SAAS,KAAA,EAAO;AAChB,IAAA,OAAO,KAAK,KAAA,CAAM,CAAC,MAAM,UAAA,CAAW,aAAA,EAAe,CAAC,CAAC,CAAA;AAAA,EACzD;AACA,EAAA,OAAO,KAAK,IAAA,CAAK,CAAC,MAAM,UAAA,CAAW,aAAA,EAAe,CAAC,CAAC,CAAA;AACxD","file":"policy.cjs","sourcesContent":["/**\n * The server-safe half of the authorization system.\n *\n * Everything in this module is a pure function over plain data: no React, no hooks, no\n * context, no module-level mutable state. That is not incidental — it is the contract.\n * `src/policy.ts` re-exports this file as the `@bizapps/ui/policy` entry point, which\n * tsup deliberately does NOT stamp with `\"use client\"`, so a Next.js Server Component,\n * Route Handler or Server Action can import it and evaluate permissions on the server.\n *\n * **Never import React (or anything that transitively does) into this file.** The build\n * asserts it: `tsup.config.ts` fails if `dist/policy.js` contains a react import or a\n * `\"use client\"` directive. Breaking that turns every server-side caller into a client\n * reference, silently.\n *\n * The client half — `AbilityProvider`, `useAbility`, `useCan`, `Can` — consumes these\n * same functions, so there is exactly one implementation of the matching rules. Two\n * implementations of an authorization primitive drift, and drift on a security boundary\n * is invisible until it matters.\n */\nimport type { Permission } from './permission-catalog';\n\n/**\n * Tests one entry of the user's grant set against one requested permission.\n *\n * @param pattern - an entry from the user's grant set; may contain `*` as a segment\n * wildcard, or be a bare `*` meaning \"everything\"\n * @param permission - the concrete permission being requested\n *\n * Matching is arity-strict: a grant of `users.*` covers `users.delete`, but a grant of\n * `users` does NOT, and neither covers `users.delete.confirm`. That rules out the\n * ambiguity of whether `users` names the resource or the namespace.\n */\nfunction matchPermission(pattern: string, permission: string): boolean {\n if (pattern === '*') return true;\n if (pattern === permission) return true;\n const patternParts = pattern.split('.');\n const permParts = permission.split('.');\n if (patternParts.length !== permParts.length) return false;\n return patternParts.every((part, i) => part === '*' || part === permParts[i]);\n}\n\n/** Whether any entry in the grant set covers the requested permission. */\nfunction matchesAny(permissionSet: Set<string>, permission: string): boolean {\n if (permissionSet.has(permission)) return true;\n for (const pattern of permissionSet) {\n if (matchPermission(pattern, permission)) return true;\n }\n return false;\n}\n\n/**\n * Evaluates a permission requirement against a grant set.\n *\n * The order of the first two checks is load-bearing and must not be swapped: an empty\n * required list denies **even for a super-grant holder**, because an empty list means\n * \"nothing satisfies this\", not \"anything does\".\n *\n * @param permissionSet - the user's grants\n * @param perms - the permission(s) required\n * @param mode - with an array, `'any'` (default) passes when at least one matches,\n * `'all'` requires every one\n */\nfunction can(\n permissionSet: Set<string>,\n perms: Permission | Permission[],\n mode: 'any' | 'all' = 'any',\n): boolean {\n const list = Array.isArray(perms) ? perms : [perms];\n if (list.length === 0) return false;\n if (permissionSet.has('*')) return true;\n if (mode === 'all') {\n return list.every((p) => matchesAny(permissionSet, p));\n }\n return list.some((p) => matchesAny(permissionSet, p));\n}\n\nexport { can, matchPermission, matchesAny };\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { P as Permission, c as can, m as matchPermission, b as matchesAny } from './policy-DPpMNLiO.cjs';
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { P as Permission, c as can, m as matchPermission, b as matchesAny } from './policy-DPpMNLiO.js';
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/components/authorization/policy.ts
|
|
2
|
+
function matchPermission(pattern, permission) {
|
|
3
|
+
if (pattern === "*") return true;
|
|
4
|
+
if (pattern === permission) return true;
|
|
5
|
+
const patternParts = pattern.split(".");
|
|
6
|
+
const permParts = permission.split(".");
|
|
7
|
+
if (patternParts.length !== permParts.length) return false;
|
|
8
|
+
return patternParts.every((part, i) => part === "*" || part === permParts[i]);
|
|
9
|
+
}
|
|
10
|
+
function matchesAny(permissionSet, permission) {
|
|
11
|
+
if (permissionSet.has(permission)) return true;
|
|
12
|
+
for (const pattern of permissionSet) {
|
|
13
|
+
if (matchPermission(pattern, permission)) return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function can(permissionSet, perms, mode = "any") {
|
|
18
|
+
const list = Array.isArray(perms) ? perms : [perms];
|
|
19
|
+
if (list.length === 0) return false;
|
|
20
|
+
if (permissionSet.has("*")) return true;
|
|
21
|
+
if (mode === "all") {
|
|
22
|
+
return list.every((p) => matchesAny(permissionSet, p));
|
|
23
|
+
}
|
|
24
|
+
return list.some((p) => matchesAny(permissionSet, p));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { can, matchPermission, matchesAny };
|
|
28
|
+
//# sourceMappingURL=policy.js.map
|
|
29
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/authorization/policy.ts"],"names":[],"mappings":";AAgCA,SAAS,eAAA,CAAgB,SAAiB,UAAA,EAA6B;AACnE,EAAA,IAAI,OAAA,KAAY,KAAK,OAAO,IAAA;AAC5B,EAAA,IAAI,OAAA,KAAY,YAAY,OAAO,IAAA;AACnC,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,IAAI,YAAA,CAAa,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ,OAAO,KAAA;AACrD,EAAA,OAAO,YAAA,CAAa,KAAA,CAAM,CAAC,IAAA,EAAM,CAAA,KAAM,SAAS,GAAA,IAAO,IAAA,KAAS,SAAA,CAAU,CAAC,CAAC,CAAA;AAChF;AAGA,SAAS,UAAA,CAAW,eAA4B,UAAA,EAA6B;AACzE,EAAA,IAAI,aAAA,CAAc,GAAA,CAAI,UAAU,CAAA,EAAG,OAAO,IAAA;AAC1C,EAAA,KAAA,MAAW,WAAW,aAAA,EAAe;AACjC,IAAA,IAAI,eAAA,CAAgB,OAAA,EAAS,UAAU,CAAA,EAAG,OAAO,IAAA;AAAA,EACrD;AACA,EAAA,OAAO,KAAA;AACX;AAcA,SAAS,GAAA,CACL,aAAA,EACA,KAAA,EACA,IAAA,GAAsB,KAAA,EACf;AACP,EAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AAClD,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAC9B,EAAA,IAAI,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG,OAAO,IAAA;AACnC,EAAA,IAAI,SAAS,KAAA,EAAO;AAChB,IAAA,OAAO,KAAK,KAAA,CAAM,CAAC,MAAM,UAAA,CAAW,aAAA,EAAe,CAAC,CAAC,CAAA;AAAA,EACzD;AACA,EAAA,OAAO,KAAK,IAAA,CAAK,CAAC,MAAM,UAAA,CAAW,aAAA,EAAe,CAAC,CAAC,CAAA;AACxD","file":"policy.js","sourcesContent":["/**\n * The server-safe half of the authorization system.\n *\n * Everything in this module is a pure function over plain data: no React, no hooks, no\n * context, no module-level mutable state. That is not incidental — it is the contract.\n * `src/policy.ts` re-exports this file as the `@bizapps/ui/policy` entry point, which\n * tsup deliberately does NOT stamp with `\"use client\"`, so a Next.js Server Component,\n * Route Handler or Server Action can import it and evaluate permissions on the server.\n *\n * **Never import React (or anything that transitively does) into this file.** The build\n * asserts it: `tsup.config.ts` fails if `dist/policy.js` contains a react import or a\n * `\"use client\"` directive. Breaking that turns every server-side caller into a client\n * reference, silently.\n *\n * The client half — `AbilityProvider`, `useAbility`, `useCan`, `Can` — consumes these\n * same functions, so there is exactly one implementation of the matching rules. Two\n * implementations of an authorization primitive drift, and drift on a security boundary\n * is invisible until it matters.\n */\nimport type { Permission } from './permission-catalog';\n\n/**\n * Tests one entry of the user's grant set against one requested permission.\n *\n * @param pattern - an entry from the user's grant set; may contain `*` as a segment\n * wildcard, or be a bare `*` meaning \"everything\"\n * @param permission - the concrete permission being requested\n *\n * Matching is arity-strict: a grant of `users.*` covers `users.delete`, but a grant of\n * `users` does NOT, and neither covers `users.delete.confirm`. That rules out the\n * ambiguity of whether `users` names the resource or the namespace.\n */\nfunction matchPermission(pattern: string, permission: string): boolean {\n if (pattern === '*') return true;\n if (pattern === permission) return true;\n const patternParts = pattern.split('.');\n const permParts = permission.split('.');\n if (patternParts.length !== permParts.length) return false;\n return patternParts.every((part, i) => part === '*' || part === permParts[i]);\n}\n\n/** Whether any entry in the grant set covers the requested permission. */\nfunction matchesAny(permissionSet: Set<string>, permission: string): boolean {\n if (permissionSet.has(permission)) return true;\n for (const pattern of permissionSet) {\n if (matchPermission(pattern, permission)) return true;\n }\n return false;\n}\n\n/**\n * Evaluates a permission requirement against a grant set.\n *\n * The order of the first two checks is load-bearing and must not be swapped: an empty\n * required list denies **even for a super-grant holder**, because an empty list means\n * \"nothing satisfies this\", not \"anything does\".\n *\n * @param permissionSet - the user's grants\n * @param perms - the permission(s) required\n * @param mode - with an array, `'any'` (default) passes when at least one matches,\n * `'all'` requires every one\n */\nfunction can(\n permissionSet: Set<string>,\n perms: Permission | Permission[],\n mode: 'any' | 'all' = 'any',\n): boolean {\n const list = Array.isArray(perms) ? perms : [perms];\n if (list.length === 0) return false;\n if (permissionSet.has('*')) return true;\n if (mode === 'all') {\n return list.every((p) => matchesAny(permissionSet, p));\n }\n return list.some((p) => matchesAny(permissionSet, p));\n}\n\nexport { can, matchPermission, matchesAny };\n"]}
|