@pyreon/permissions 0.11.5 → 0.11.7

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 CHANGED
@@ -20,19 +20,23 @@ const can = createPermissions({
20
20
  'users.manage': false,
21
21
  })
22
22
 
23
- can('posts.read') // true — reactive in effects/JSX
24
- can('posts.update', myPost) // evaluates predicate
25
- can.not('billing.export') // true if denied
26
- can.all('posts.read', 'posts.create') // true if both granted
27
- can.any('posts.read', 'users.manage') // true if any granted
23
+ can('posts.read') // true — reactive in effects/JSX
24
+ can('posts.update', myPost) // evaluates predicate
25
+ can.not('billing.export') // true if denied
26
+ can.all('posts.read', 'posts.create') // true if both granted
27
+ can.any('posts.read', 'users.manage') // true if any granted
28
28
 
29
29
  // Update after login / role change
30
30
  can.set(fromRole(user.role))
31
31
  can.patch({ 'billing.export': true })
32
32
 
33
33
  // Reactive in JSX
34
- {() => can('posts.delete') && <DeleteButton />}
35
- {() => can('users.manage') && <AdminPanel />}
34
+ {
35
+ ;() => can('posts.delete') && <DeleteButton />
36
+ }
37
+ {
38
+ ;() => can('users.manage') && <AdminPanel />
39
+ }
36
40
  ```
37
41
 
38
42
  ## Wildcards
@@ -44,7 +48,7 @@ can.patch({ 'billing.export': true })
44
48
  ```tsx
45
49
  import { PermissionsProvider, usePermissions } from '@pyreon/permissions'
46
50
 
47
- <PermissionsProvider instance={can}>
51
+ ;<PermissionsProvider instance={can}>
48
52
  <App />
49
53
  </PermissionsProvider>
50
54
 
@@ -58,16 +62,16 @@ const can = usePermissions()
58
62
 
59
63
  Create a reactive permissions instance. Permission values are `boolean` (static) or `(context?) => boolean` (predicate).
60
64
 
61
- | Method | Description |
62
- | --- | --- |
63
- | `can(key, context?)` | Check a permission (reactive) |
64
- | `can.not(key)` | Inverse check |
65
- | `can.all(...keys)` | True if all granted |
66
- | `can.any(...keys)` | True if any granted |
67
- | `can.set(map)` | Replace all permissions |
68
- | `can.patch(map)` | Merge permissions |
69
- | `can.granted()` | `Computed<string[]>` of all granted keys |
70
- | `can.entries()` | `Computed<[key, value][]>` for introspection |
65
+ | Method | Description |
66
+ | -------------------- | -------------------------------------------- |
67
+ | `can(key, context?)` | Check a permission (reactive) |
68
+ | `can.not(key)` | Inverse check |
69
+ | `can.all(...keys)` | True if all granted |
70
+ | `can.any(...keys)` | True if any granted |
71
+ | `can.set(map)` | Replace all permissions |
72
+ | `can.patch(map)` | Merge permissions |
73
+ | `can.granted()` | `Computed<string[]>` of all granted keys |
74
+ | `can.entries()` | `Computed<[key, value][]>` for introspection |
71
75
 
72
76
  ### `PermissionsProvider` / `usePermissions()`
73
77
 
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/context.ts","../src/permissions.ts"],"sourcesContent":["import type { VNodeChild } from \"@pyreon/core\"\nimport { createContext, provide, useContext } from \"@pyreon/core\"\nimport type { Permissions } from \"./types\"\n\nconst PermissionsContext = createContext<Permissions | null>(null)\n\n/**\n * Provide a permissions instance to descendant components.\n * Use this for SSR isolation or testing — each request/test gets its own instance.\n *\n * @example\n * ```tsx\n * const can = createPermissions({ ... })\n *\n * <PermissionsProvider instance={can}>\n * <App />\n * </PermissionsProvider>\n * ```\n */\nexport function PermissionsProvider(props: {\n instance: Permissions\n children?: VNodeChild\n}): VNodeChild {\n provide(PermissionsContext, props.instance)\n\n return props.children ?? null\n}\n\n/**\n * Access the nearest permissions instance from context.\n * Must be used within a `<PermissionsProvider>`.\n *\n * @example\n * ```tsx\n * const can = usePermissions()\n * {() => can('posts.read') && <PostList />}\n * ```\n */\nexport function usePermissions(): Permissions {\n const instance = useContext(PermissionsContext)\n if (!instance) {\n throw new Error(\n \"[@pyreon/permissions] usePermissions() must be used within <PermissionsProvider>.\",\n )\n }\n return instance\n}\n","import { batch, computed, signal } from \"@pyreon/reactivity\"\nimport type { PermissionMap, Permissions, PermissionValue } from \"./types\"\n\n/**\n * Resolve a permission key against the map.\n * Resolution order: exact match → wildcard (e.g., 'posts.*') → global wildcard ('*') → false.\n */\n/**\n * Safely evaluate a permission value. Predicates that throw are treated as denied.\n */\nfunction evaluate(value: PermissionValue, context?: unknown): boolean {\n if (typeof value === \"function\") {\n try {\n return value(context)\n } catch {\n return false\n }\n }\n return value\n}\n\nfunction resolve(map: Map<string, PermissionValue>, key: string, context?: unknown): boolean {\n // 1. Exact match\n const exact = map.get(key)\n if (exact !== undefined) {\n return evaluate(exact, context)\n }\n\n // 2. Wildcard match — 'posts.read' matches 'posts.*'\n const dotIndex = key.lastIndexOf(\".\")\n if (dotIndex !== -1) {\n const prefix = key.slice(0, dotIndex)\n const wildcard = map.get(`${prefix}.*`)\n if (wildcard !== undefined) {\n return evaluate(wildcard, context)\n }\n }\n\n // 3. Global wildcard\n const global = map.get(\"*\")\n if (global !== undefined) {\n return evaluate(global, context)\n }\n\n // 4. No match → denied\n return false\n}\n\n/**\n * Create a reactive permissions instance.\n *\n * The returned `can` function checks permissions reactively —\n * reads update automatically when permissions change via `set()` or `patch()`.\n *\n * @param initial - Optional initial permission map\n * @returns A callable `Permissions` instance\n *\n * @example\n * ```tsx\n * const can = createPermissions({\n * 'posts.read': true,\n * 'posts.update': (post: Post) => post.authorId === userId(),\n * 'users.manage': false,\n * })\n *\n * // Check (reactive in effects/computeds/JSX)\n * can('posts.read') // true\n * can('posts.update', myPost) // evaluates predicate\n *\n * // JSX\n * {() => can('posts.delete') && <DeleteButton />}\n *\n * // Update\n * can.set({ 'posts.read': true, 'admin': true })\n * can.patch({ 'users.manage': true })\n * ```\n */\nexport function createPermissions(initial?: PermissionMap): Permissions {\n // Internal reactive state — a signal holding the permission map\n const store = signal(toMap(initial))\n // Version counter — incremented on every set/patch to trigger reactive updates\n const version = signal(0)\n\n function toMap(obj?: PermissionMap): Map<string, PermissionValue> {\n if (!obj) return new Map()\n return new Map(Object.entries(obj))\n }\n\n // The main check function — reads `version` to subscribe in reactive contexts\n function can(key: string, context?: unknown): boolean {\n // Reading version subscribes this call to reactive updates\n version()\n return resolve(store.peek(), key, context)\n }\n\n can.not = (key: string, context?: unknown): boolean => {\n return !can(key, context)\n }\n\n can.all = (...keys: string[]): boolean => {\n return keys.every((key) => can(key))\n }\n\n can.any = (...keys: string[]): boolean => {\n return keys.some((key) => can(key))\n }\n\n can.set = (permissions: PermissionMap): void => {\n batch(() => {\n store.set(toMap(permissions))\n version.update((v) => v + 1)\n })\n }\n\n can.patch = (permissions: PermissionMap): void => {\n batch(() => {\n const current = store.peek()\n for (const [key, value] of Object.entries(permissions)) {\n current.set(key, value)\n }\n store.set(current)\n version.update((v) => v + 1)\n })\n }\n\n can.granted = computed(() => {\n version()\n const keys: string[] = []\n for (const [key, value] of store.peek()) {\n // Static true or predicate (capability exists)\n if (value === true || typeof value === \"function\") {\n keys.push(key)\n }\n }\n return keys\n })\n\n can.entries = computed(() => {\n version()\n return [...store.peek().entries()]\n })\n\n return can as Permissions\n}\n"],"mappings":";;;;AAIA,MAAM,qBAAqB,cAAkC,KAAK;;;;;;;;;;;;;;AAelE,SAAgB,oBAAoB,OAGrB;AACb,SAAQ,oBAAoB,MAAM,SAAS;AAE3C,QAAO,MAAM,YAAY;;;;;;;;;;;;AAa3B,SAAgB,iBAA8B;CAC5C,MAAM,WAAW,WAAW,mBAAmB;AAC/C,KAAI,CAAC,SACH,OAAM,IAAI,MACR,oFACD;AAEH,QAAO;;;;;;;;;;;;ACnCT,SAAS,SAAS,OAAwB,SAA4B;AACpE,KAAI,OAAO,UAAU,WACnB,KAAI;AACF,SAAO,MAAM,QAAQ;SACf;AACN,SAAO;;AAGX,QAAO;;AAGT,SAAS,QAAQ,KAAmC,KAAa,SAA4B;CAE3F,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC1B,KAAI,UAAU,OACZ,QAAO,SAAS,OAAO,QAAQ;CAIjC,MAAM,WAAW,IAAI,YAAY,IAAI;AACrC,KAAI,aAAa,IAAI;EACnB,MAAM,SAAS,IAAI,MAAM,GAAG,SAAS;EACrC,MAAM,WAAW,IAAI,IAAI,GAAG,OAAO,IAAI;AACvC,MAAI,aAAa,OACf,QAAO,SAAS,UAAU,QAAQ;;CAKtC,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,KAAI,WAAW,OACb,QAAO,SAAS,QAAQ,QAAQ;AAIlC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCT,SAAgB,kBAAkB,SAAsC;CAEtE,MAAM,QAAQ,OAAO,MAAM,QAAQ,CAAC;CAEpC,MAAM,UAAU,OAAO,EAAE;CAEzB,SAAS,MAAM,KAAmD;AAChE,MAAI,CAAC,IAAK,wBAAO,IAAI,KAAK;AAC1B,SAAO,IAAI,IAAI,OAAO,QAAQ,IAAI,CAAC;;CAIrC,SAAS,IAAI,KAAa,SAA4B;AAEpD,WAAS;AACT,SAAO,QAAQ,MAAM,MAAM,EAAE,KAAK,QAAQ;;AAG5C,KAAI,OAAO,KAAa,YAA+B;AACrD,SAAO,CAAC,IAAI,KAAK,QAAQ;;AAG3B,KAAI,OAAO,GAAG,SAA4B;AACxC,SAAO,KAAK,OAAO,QAAQ,IAAI,IAAI,CAAC;;AAGtC,KAAI,OAAO,GAAG,SAA4B;AACxC,SAAO,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC;;AAGrC,KAAI,OAAO,gBAAqC;AAC9C,cAAY;AACV,SAAM,IAAI,MAAM,YAAY,CAAC;AAC7B,WAAQ,QAAQ,MAAM,IAAI,EAAE;IAC5B;;AAGJ,KAAI,SAAS,gBAAqC;AAChD,cAAY;GACV,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,CACpD,SAAQ,IAAI,KAAK,MAAM;AAEzB,SAAM,IAAI,QAAQ;AAClB,WAAQ,QAAQ,MAAM,IAAI,EAAE;IAC5B;;AAGJ,KAAI,UAAU,eAAe;AAC3B,WAAS;EACT,MAAM,OAAiB,EAAE;AACzB,OAAK,MAAM,CAAC,KAAK,UAAU,MAAM,MAAM,CAErC,KAAI,UAAU,QAAQ,OAAO,UAAU,WACrC,MAAK,KAAK,IAAI;AAGlB,SAAO;GACP;AAEF,KAAI,UAAU,eAAe;AAC3B,WAAS;AACT,SAAO,CAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC;GAClC;AAEF,QAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/context.ts","../src/permissions.ts"],"sourcesContent":["import type { VNodeChild } from '@pyreon/core'\nimport { createContext, provide, useContext } from '@pyreon/core'\nimport type { Permissions } from './types'\n\nconst PermissionsContext = createContext<Permissions | null>(null)\n\n/**\n * Provide a permissions instance to descendant components.\n * Use this for SSR isolation or testing — each request/test gets its own instance.\n *\n * @example\n * ```tsx\n * const can = createPermissions({ ... })\n *\n * <PermissionsProvider instance={can}>\n * <App />\n * </PermissionsProvider>\n * ```\n */\nexport function PermissionsProvider(props: {\n instance: Permissions\n children?: VNodeChild\n}): VNodeChild {\n provide(PermissionsContext, props.instance)\n\n return props.children ?? null\n}\n\n/**\n * Access the nearest permissions instance from context.\n * Must be used within a `<PermissionsProvider>`.\n *\n * @example\n * ```tsx\n * const can = usePermissions()\n * {() => can('posts.read') && <PostList />}\n * ```\n */\nexport function usePermissions(): Permissions {\n const instance = useContext(PermissionsContext)\n if (!instance) {\n throw new Error(\n '[@pyreon/permissions] usePermissions() must be used within <PermissionsProvider>.',\n )\n }\n return instance\n}\n","import { batch, computed, signal } from '@pyreon/reactivity'\nimport type { PermissionMap, Permissions, PermissionValue } from './types'\n\n/**\n * Resolve a permission key against the map.\n * Resolution order: exact match → wildcard (e.g., 'posts.*') → global wildcard ('*') → false.\n */\n/**\n * Safely evaluate a permission value. Predicates that throw are treated as denied.\n */\nfunction evaluate(value: PermissionValue, context?: unknown): boolean {\n if (typeof value === 'function') {\n try {\n return value(context)\n } catch {\n return false\n }\n }\n return value\n}\n\nfunction resolve(map: Map<string, PermissionValue>, key: string, context?: unknown): boolean {\n // 1. Exact match\n const exact = map.get(key)\n if (exact !== undefined) {\n return evaluate(exact, context)\n }\n\n // 2. Wildcard match — 'posts.read' matches 'posts.*'\n const dotIndex = key.lastIndexOf('.')\n if (dotIndex !== -1) {\n const prefix = key.slice(0, dotIndex)\n const wildcard = map.get(`${prefix}.*`)\n if (wildcard !== undefined) {\n return evaluate(wildcard, context)\n }\n }\n\n // 3. Global wildcard\n const global = map.get('*')\n if (global !== undefined) {\n return evaluate(global, context)\n }\n\n // 4. No match → denied\n return false\n}\n\n/**\n * Create a reactive permissions instance.\n *\n * The returned `can` function checks permissions reactively —\n * reads update automatically when permissions change via `set()` or `patch()`.\n *\n * @param initial - Optional initial permission map\n * @returns A callable `Permissions` instance\n *\n * @example\n * ```tsx\n * const can = createPermissions({\n * 'posts.read': true,\n * 'posts.update': (post: Post) => post.authorId === userId(),\n * 'users.manage': false,\n * })\n *\n * // Check (reactive in effects/computeds/JSX)\n * can('posts.read') // true\n * can('posts.update', myPost) // evaluates predicate\n *\n * // JSX\n * {() => can('posts.delete') && <DeleteButton />}\n *\n * // Update\n * can.set({ 'posts.read': true, 'admin': true })\n * can.patch({ 'users.manage': true })\n * ```\n */\nexport function createPermissions(initial?: PermissionMap): Permissions {\n // Internal reactive state — a signal holding the permission map\n const store = signal(toMap(initial))\n // Version counter — incremented on every set/patch to trigger reactive updates\n const version = signal(0)\n\n function toMap(obj?: PermissionMap): Map<string, PermissionValue> {\n if (!obj) return new Map()\n return new Map(Object.entries(obj))\n }\n\n // The main check function — reads `version` to subscribe in reactive contexts\n function can(key: string, context?: unknown): boolean {\n // Reading version subscribes this call to reactive updates\n version()\n return resolve(store.peek(), key, context)\n }\n\n can.not = (key: string, context?: unknown): boolean => {\n return !can(key, context)\n }\n\n can.all = (...keys: string[]): boolean => {\n return keys.every((key) => can(key))\n }\n\n can.any = (...keys: string[]): boolean => {\n return keys.some((key) => can(key))\n }\n\n can.set = (permissions: PermissionMap): void => {\n batch(() => {\n store.set(toMap(permissions))\n version.update((v) => v + 1)\n })\n }\n\n can.patch = (permissions: PermissionMap): void => {\n batch(() => {\n const current = store.peek()\n for (const [key, value] of Object.entries(permissions)) {\n current.set(key, value)\n }\n store.set(current)\n version.update((v) => v + 1)\n })\n }\n\n can.granted = computed(() => {\n version()\n const keys: string[] = []\n for (const [key, value] of store.peek()) {\n // Static true or predicate (capability exists)\n if (value === true || typeof value === 'function') {\n keys.push(key)\n }\n }\n return keys\n })\n\n can.entries = computed(() => {\n version()\n return [...store.peek().entries()]\n })\n\n return can as Permissions\n}\n"],"mappings":";;;;AAIA,MAAM,qBAAqB,cAAkC,KAAK;;;;;;;;;;;;;;AAelE,SAAgB,oBAAoB,OAGrB;AACb,SAAQ,oBAAoB,MAAM,SAAS;AAE3C,QAAO,MAAM,YAAY;;;;;;;;;;;;AAa3B,SAAgB,iBAA8B;CAC5C,MAAM,WAAW,WAAW,mBAAmB;AAC/C,KAAI,CAAC,SACH,OAAM,IAAI,MACR,oFACD;AAEH,QAAO;;;;;;;;;;;;ACnCT,SAAS,SAAS,OAAwB,SAA4B;AACpE,KAAI,OAAO,UAAU,WACnB,KAAI;AACF,SAAO,MAAM,QAAQ;SACf;AACN,SAAO;;AAGX,QAAO;;AAGT,SAAS,QAAQ,KAAmC,KAAa,SAA4B;CAE3F,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC1B,KAAI,UAAU,OACZ,QAAO,SAAS,OAAO,QAAQ;CAIjC,MAAM,WAAW,IAAI,YAAY,IAAI;AACrC,KAAI,aAAa,IAAI;EACnB,MAAM,SAAS,IAAI,MAAM,GAAG,SAAS;EACrC,MAAM,WAAW,IAAI,IAAI,GAAG,OAAO,IAAI;AACvC,MAAI,aAAa,OACf,QAAO,SAAS,UAAU,QAAQ;;CAKtC,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,KAAI,WAAW,OACb,QAAO,SAAS,QAAQ,QAAQ;AAIlC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCT,SAAgB,kBAAkB,SAAsC;CAEtE,MAAM,QAAQ,OAAO,MAAM,QAAQ,CAAC;CAEpC,MAAM,UAAU,OAAO,EAAE;CAEzB,SAAS,MAAM,KAAmD;AAChE,MAAI,CAAC,IAAK,wBAAO,IAAI,KAAK;AAC1B,SAAO,IAAI,IAAI,OAAO,QAAQ,IAAI,CAAC;;CAIrC,SAAS,IAAI,KAAa,SAA4B;AAEpD,WAAS;AACT,SAAO,QAAQ,MAAM,MAAM,EAAE,KAAK,QAAQ;;AAG5C,KAAI,OAAO,KAAa,YAA+B;AACrD,SAAO,CAAC,IAAI,KAAK,QAAQ;;AAG3B,KAAI,OAAO,GAAG,SAA4B;AACxC,SAAO,KAAK,OAAO,QAAQ,IAAI,IAAI,CAAC;;AAGtC,KAAI,OAAO,GAAG,SAA4B;AACxC,SAAO,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC;;AAGrC,KAAI,OAAO,gBAAqC;AAC9C,cAAY;AACV,SAAM,IAAI,MAAM,YAAY,CAAC;AAC7B,WAAQ,QAAQ,MAAM,IAAI,EAAE;IAC5B;;AAGJ,KAAI,SAAS,gBAAqC;AAChD,cAAY;GACV,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,CACpD,SAAQ,IAAI,KAAK,MAAM;AAEzB,SAAM,IAAI,QAAQ;AAClB,WAAQ,QAAQ,MAAM,IAAI,EAAE;IAC5B;;AAGJ,KAAI,UAAU,eAAe;AAC3B,WAAS;EACT,MAAM,OAAiB,EAAE;AACzB,OAAK,MAAM,CAAC,KAAK,UAAU,MAAM,MAAM,CAErC,KAAI,UAAU,QAAQ,OAAO,UAAU,WACrC,MAAK,KAAK,IAAI;AAGlB,SAAO;GACP;AAEF,KAAI,UAAU,eAAe;AAC3B,WAAS;AACT,SAAO,CAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC;GAClC;AAEF,QAAO"}
package/package.json CHANGED
@@ -1,20 +1,17 @@
1
1
  {
2
2
  "name": "@pyreon/permissions",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "description": "Reactive permissions for Pyreon — type-safe, signal-driven, universal",
5
+ "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/permissions#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/pyreon/pyreon/issues"
8
+ },
5
9
  "license": "MIT",
6
10
  "repository": {
7
11
  "type": "git",
8
12
  "url": "https://github.com/pyreon/pyreon.git",
9
13
  "directory": "packages/fundamentals/permissions"
10
14
  },
11
- "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/permissions#readme",
12
- "bugs": {
13
- "url": "https://github.com/pyreon/pyreon/issues"
14
- },
15
- "publishConfig": {
16
- "access": "public"
17
- },
18
15
  "files": [
19
16
  "lib",
20
17
  "src",
@@ -22,6 +19,7 @@
22
19
  "LICENSE"
23
20
  ],
24
21
  "type": "module",
22
+ "sideEffects": false,
25
23
  "main": "./lib/index.js",
26
24
  "module": "./lib/index.js",
27
25
  "types": "./lib/types/index.d.ts",
@@ -32,22 +30,24 @@
32
30
  "types": "./lib/types/index.d.ts"
33
31
  }
34
32
  },
35
- "sideEffects": false,
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
36
  "scripts": {
37
37
  "build": "vl_rolldown_build",
38
38
  "dev": "vl_rolldown_build-watch",
39
39
  "test": "vitest run",
40
40
  "typecheck": "tsc --noEmit",
41
- "lint": "biome check ."
42
- },
43
- "peerDependencies": {
44
- "@pyreon/core": "^0.11.5",
45
- "@pyreon/reactivity": "^0.11.5"
41
+ "lint": "oxlint ."
46
42
  },
47
43
  "devDependencies": {
48
44
  "@happy-dom/global-registrator": "^20.8.3",
49
- "@pyreon/core": "^0.11.5",
50
- "@pyreon/reactivity": "^0.11.5",
45
+ "@pyreon/core": "^0.11.7",
46
+ "@pyreon/reactivity": "^0.11.7",
51
47
  "@vitus-labs/tools-lint": "^1.11.0"
48
+ },
49
+ "peerDependencies": {
50
+ "@pyreon/core": "^0.11.7",
51
+ "@pyreon/reactivity": "^0.11.7"
52
52
  }
53
53
  }
package/src/context.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { VNodeChild } from "@pyreon/core"
2
- import { createContext, provide, useContext } from "@pyreon/core"
3
- import type { Permissions } from "./types"
1
+ import type { VNodeChild } from '@pyreon/core'
2
+ import { createContext, provide, useContext } from '@pyreon/core'
3
+ import type { Permissions } from './types'
4
4
 
5
5
  const PermissionsContext = createContext<Permissions | null>(null)
6
6
 
@@ -40,7 +40,7 @@ export function usePermissions(): Permissions {
40
40
  const instance = useContext(PermissionsContext)
41
41
  if (!instance) {
42
42
  throw new Error(
43
- "[@pyreon/permissions] usePermissions() must be used within <PermissionsProvider>.",
43
+ '[@pyreon/permissions] usePermissions() must be used within <PermissionsProvider>.',
44
44
  )
45
45
  }
46
46
  return instance
package/src/index.ts CHANGED
@@ -23,13 +23,8 @@
23
23
  * ```
24
24
  */
25
25
 
26
- export { PermissionsProvider, usePermissions } from "./context"
27
- export { createPermissions } from "./permissions"
26
+ export { PermissionsProvider, usePermissions } from './context'
27
+ export { createPermissions } from './permissions'
28
28
 
29
29
  // Types
30
- export type {
31
- PermissionMap,
32
- PermissionPredicate,
33
- Permissions,
34
- PermissionValue,
35
- } from "./types"
30
+ export type { PermissionMap, PermissionPredicate, Permissions, PermissionValue } from './types'
@@ -1,5 +1,5 @@
1
- import { batch, computed, signal } from "@pyreon/reactivity"
2
- import type { PermissionMap, Permissions, PermissionValue } from "./types"
1
+ import { batch, computed, signal } from '@pyreon/reactivity'
2
+ import type { PermissionMap, Permissions, PermissionValue } from './types'
3
3
 
4
4
  /**
5
5
  * Resolve a permission key against the map.
@@ -9,7 +9,7 @@ import type { PermissionMap, Permissions, PermissionValue } from "./types"
9
9
  * Safely evaluate a permission value. Predicates that throw are treated as denied.
10
10
  */
11
11
  function evaluate(value: PermissionValue, context?: unknown): boolean {
12
- if (typeof value === "function") {
12
+ if (typeof value === 'function') {
13
13
  try {
14
14
  return value(context)
15
15
  } catch {
@@ -27,7 +27,7 @@ function resolve(map: Map<string, PermissionValue>, key: string, context?: unkno
27
27
  }
28
28
 
29
29
  // 2. Wildcard match — 'posts.read' matches 'posts.*'
30
- const dotIndex = key.lastIndexOf(".")
30
+ const dotIndex = key.lastIndexOf('.')
31
31
  if (dotIndex !== -1) {
32
32
  const prefix = key.slice(0, dotIndex)
33
33
  const wildcard = map.get(`${prefix}.*`)
@@ -37,7 +37,7 @@ function resolve(map: Map<string, PermissionValue>, key: string, context?: unkno
37
37
  }
38
38
 
39
39
  // 3. Global wildcard
40
- const global = map.get("*")
40
+ const global = map.get('*')
41
41
  if (global !== undefined) {
42
42
  return evaluate(global, context)
43
43
  }
@@ -128,7 +128,7 @@ export function createPermissions(initial?: PermissionMap): Permissions {
128
128
  const keys: string[] = []
129
129
  for (const [key, value] of store.peek()) {
130
130
  // Static true or predicate (capability exists)
131
- if (value === true || typeof value === "function") {
131
+ if (value === true || typeof value === 'function') {
132
132
  keys.push(key)
133
133
  }
134
134
  }