@codeleap/permissions 6.3.0 → 7.0.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.
@@ -0,0 +1,80 @@
1
+ import { PermissionOptions } from './types';
2
+ import { PermissionConfig, PermissionStatus } from './globals';
3
+ /**
4
+ * Represents a single named permission with persistent reactive state.
5
+ *
6
+ * State is backed by `globalState` and persisted under the key
7
+ * `"permissions-<name>"`, so the last-known status survives page reloads or
8
+ * app restarts without an extra async check.
9
+ *
10
+ * The status machine has one sticky transition: once a permission reaches
11
+ * `"blocked"` it will never regress to `"denied"` via {@link check} — because
12
+ * on most platforms a blocked permission can only be cleared through the OS
13
+ * settings screen, not by a subsequent API call.
14
+ */
15
+ export declare class Permission {
16
+ /** The name of the permission. */
17
+ name: string;
18
+ private state;
19
+ private checkStatus;
20
+ private requestStatus;
21
+ /**
22
+ * Set to `true` to enable verbose permission logs via `@codeleap/logger`.
23
+ * Applies to every `Permission` instance; toggling at runtime takes effect
24
+ * immediately.
25
+ */
26
+ static logsEnabled: boolean;
27
+ /**
28
+ * Converts a raw status string into a structured descriptor object.
29
+ * Prefer this over equality checks scattered across call sites so that
30
+ * adding a new status variant only requires updating this method.
31
+ */
32
+ static is(status: PermissionStatus): {
33
+ value: null;
34
+ isGranted: boolean;
35
+ isLimited: boolean;
36
+ isDenied: boolean;
37
+ isBlocked: boolean;
38
+ };
39
+ /** Configuration object associated with the permission. */
40
+ config: PermissionConfig;
41
+ /**
42
+ * Snapshot of the persisted status. Returns `null` until the first
43
+ * `check()` or `request()` resolves.
44
+ */
45
+ get value(): null;
46
+ get isGranted(): boolean;
47
+ get isLimited(): boolean;
48
+ get isDenied(): boolean;
49
+ get isBlocked(): boolean;
50
+ constructor(options: PermissionOptions<PermissionConfig>);
51
+ private log;
52
+ /**
53
+ * Directly overwrites the stored status without going through the native
54
+ * check or request flow. Useful when the status is already known from an
55
+ * external source (e.g. a push-notification token registration callback).
56
+ */
57
+ set(newStatus: PermissionStatus): void;
58
+ /**
59
+ * React hook that subscribes a component to this permission's reactive
60
+ * state. Re-renders whenever the status changes.
61
+ */
62
+ use(): null;
63
+ /**
64
+ * Queries the native status without showing a user-facing prompt.
65
+ *
66
+ * If the platform returns `"denied"` but the stored value is already
67
+ * `"blocked"`, the `"blocked"` value is preserved. This guards against
68
+ * platforms that report a previously-blocked permission as merely denied on
69
+ * subsequent queries, which would incorrectly allow a re-request attempt.
70
+ */
71
+ check(): Promise<null>;
72
+ /**
73
+ * Triggers an interactive permission prompt (may show a system dialog) and
74
+ * updates stored state when the result differs from the current value.
75
+ * Only call this in direct response to a user action; invoking it
76
+ * speculatively or on mount will fail silently on most platforms.
77
+ */
78
+ request(): Promise<null>;
79
+ }
80
+ //# sourceMappingURL=Permission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Permission.d.ts","sourceRoot":"","sources":["../src/Permission.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAI9D;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU;IACrB,kCAAkC;IAC3B,IAAI,EAAE,MAAM,CAAA;IAEnB,OAAO,CAAC,KAAK,CAA+B;IAE5C,OAAO,CAAC,WAAW,CAAiC;IAEpD,OAAO,CAAC,aAAa,CAAiC;IAEtD;;;;OAIG;IACH,MAAM,CAAC,WAAW,EAAE,OAAO,CAAQ;IAEnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB;;;;;;;IAUlC,2DAA2D;IACpD,MAAM,EAAE,gBAAgB,CAAA;IAE/B;;;OAGG;IACH,IAAI,KAAK,SAER;IAED,IAAI,SAAS,YAEZ;IAED,IAAI,SAAS,YAEZ;IAED,IAAI,QAAQ,YAEX;IAED,IAAI,SAAS,YAEZ;gBAEW,OAAO,EAAE,iBAAiB,CAAC,gBAAgB,CAAC;IAWxD,OAAO,CAAC,GAAG;IAKX;;;;OAIG;IACH,GAAG,CAAC,SAAS,EAAE,gBAAgB;IAI/B;;;OAGG;IACH,GAAG;IAIH;;;;;;;OAOG;IACG,KAAK;IAkBX;;;;;OAKG;IACG,OAAO;CAYd"}
@@ -0,0 +1,82 @@
1
+ import { Permission } from './Permission';
2
+ import { PermissionOptions } from './types';
3
+ import { PermissionStatus, PermissionConfig } from './globals';
4
+ /**
5
+ * Registry and coordinator for a fixed set of named permissions.
6
+ *
7
+ * Instantiating this class immediately calls `checkAll()` in the background so
8
+ * that every permission's cached status is refreshed before the first render.
9
+ * The `requester` callback is the single authoritative place to implement any
10
+ * pre-prompt logic (e.g. showing an educational interstitial before the OS
11
+ * dialog appears).
12
+ */
13
+ export declare class PermissionsManager<P extends string> {
14
+ private requester;
15
+ permissions: Record<P, Permission>;
16
+ private get keys();
17
+ /**
18
+ * Snapshot of all registered permissions' current statuses. Values reflect
19
+ * whatever is in persistent state at the moment of access — call
20
+ * `checkAll()` first if you need fresh data from the platform.
21
+ */
22
+ get values(): Record<P, null>;
23
+ private forEach;
24
+ constructor(requester: (permission: Permission) => Promise<PermissionStatus>, permissions: Record<P, Omit<PermissionOptions<PermissionConfig>, 'name'>>);
25
+ private log;
26
+ /**
27
+ * React hook that subscribes a component to the reactive state of a single
28
+ * named permission. Re-renders whenever that permission's status changes.
29
+ */
30
+ use(permissionName: P): null;
31
+ /**
32
+ * Requests a single permission through the manager's `requester` callback.
33
+ * The result passes through {@link Permission.is} so callers get a
34
+ * structured descriptor rather than a raw string.
35
+ *
36
+ * Note: this bypasses `Permission.request()` on the individual instance —
37
+ * the `requester` callback owns the full flow, including any pre-prompt UI.
38
+ */
39
+ request(permissionName: P): Promise<{
40
+ value: null;
41
+ isGranted: boolean;
42
+ isLimited: boolean;
43
+ isDenied: boolean;
44
+ isBlocked: boolean;
45
+ }>;
46
+ /**
47
+ * Silently queries a single permission's current status (no system dialog).
48
+ * Returns a structured descriptor via {@link Permission.is}.
49
+ */
50
+ check(permissionName: P): Promise<{
51
+ value: null;
52
+ isGranted: boolean;
53
+ isLimited: boolean;
54
+ isDenied: boolean;
55
+ isBlocked: boolean;
56
+ }>;
57
+ /**
58
+ * Requests multiple permissions sequentially. Requests are serial, not
59
+ * parallel, to avoid triggering concurrent system dialogs which may be
60
+ * rejected or silently dropped on some platforms.
61
+ */
62
+ requestMany<K extends P>(permissionsNames: K[]): Promise<Record<K, ReturnType<typeof Permission.is>>>;
63
+ /**
64
+ * Silently queries multiple permissions sequentially. Serial execution
65
+ * ensures consistent ordering and avoids race conditions in the underlying
66
+ * state store.
67
+ */
68
+ checkMany<K extends P>(permissionsNames: K[]): Promise<Record<K, ReturnType<typeof Permission.is>>>;
69
+ /**
70
+ * Silently refreshes every registered permission. Called automatically
71
+ * during construction so that cached statuses are up to date before the
72
+ * first render without requiring the caller to await initialization.
73
+ */
74
+ checkAll(): Promise<Record<P, {
75
+ value: null;
76
+ isGranted: boolean;
77
+ isLimited: boolean;
78
+ isDenied: boolean;
79
+ isBlocked: boolean;
80
+ }>>;
81
+ }
82
+ //# sourceMappingURL=PermissionsManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionsManager.d.ts","sourceRoot":"","sources":["../src/PermissionsManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAG9D;;;;;;;;GAQG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,MAAM;IAC9C,OAAO,CAAC,SAAS,CAAuD;IAExE,WAAW,EAAE,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,CAA8B;IAEhE,OAAO,KAAK,IAAI,GAEf;IAED;;;;OAIG;IACH,IAAI,MAAM,oBAQT;YAEa,OAAO;gBAQnB,SAAS,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,gBAAgB,CAAC,EAChE,WAAW,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAAC;IAkB3E,OAAO,CAAC,GAAG;IAKX;;;OAGG;IACH,GAAG,CAAC,cAAc,EAAE,CAAC;IAIrB;;;;;;;OAOG;IACG,OAAO,CAAC,cAAc,EAAE,CAAC;;;;;;;IAM/B;;;OAGG;IACG,KAAK,CAAC,cAAc,EAAE,CAAC;;;;;;;IAM7B;;;;OAIG;IACG,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IAU3G;;;;OAIG;IACG,SAAS,CAAC,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IAUzG;;;;OAIG;IACG,QAAQ;;;;;;;CAIf"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Extend this interface via module augmentation in the consuming app to attach
3
+ * platform-specific metadata (e.g. Android rationale strings, iOS usage
4
+ * descriptions) to every registered permission.
5
+ *
6
+ * @example
7
+ * declare module '@codeleap/permissions' {
8
+ * interface PermissionConfig {
9
+ * rationale: string
10
+ * }
11
+ * }
12
+ */
13
+ export interface PermissionConfig {
14
+ }
15
+ /**
16
+ * Extend this interface via module augmentation to register the set of valid
17
+ * permission statuses for the target platform. The keys become the union type
18
+ * used throughout the package.
19
+ *
20
+ * @example
21
+ * declare module '@codeleap/permissions' {
22
+ * interface Status {
23
+ * granted: never
24
+ * denied: never
25
+ * blocked: never
26
+ * limited: never
27
+ * }
28
+ * }
29
+ */
30
+ export interface Status {
31
+ }
32
+ /**
33
+ * Union of every key registered in {@link Status} plus `null`.
34
+ * `null` means the status has not yet been resolved (e.g. before the first
35
+ * `check()` call completes).
36
+ */
37
+ export type PermissionStatus = keyof Status | null;
38
+ //# sourceMappingURL=globals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;CAEhC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,MAAM;CAEtB;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,MAAM,GAAG,IAAI,CAAA"}
@@ -0,0 +1,5 @@
1
+ export * from './PermissionsManager';
2
+ export * from './Permission';
3
+ export * from './types';
4
+ export * from './globals';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { AnyRecord } from '@codeleap/types';
2
+ import { PermissionStatus } from './globals';
3
+ /**
4
+ * Construction options for a single {@link Permission} instance.
5
+ *
6
+ * `check` and `request` are intentionally separate callbacks because many
7
+ * platforms distinguish a silent status query (no UI) from an interactive
8
+ * prompt (may show a system dialog). Provide both even if the underlying API
9
+ * is the same — the manager calls them at different points in the flow.
10
+ */
11
+ export type PermissionOptions<Config extends AnyRecord> = {
12
+ name: string;
13
+ config: Config;
14
+ check: () => Promise<PermissionStatus>;
15
+ request: () => Promise<PermissionStatus>;
16
+ };
17
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAE5C;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,CAAC,MAAM,SAAS,SAAS,IAAI;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACtC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACzC,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,20 @@
1
1
  {
2
2
  "name": "@codeleap/permissions",
3
- "version": "6.3.0",
3
+ "version": "7.0.0",
4
4
  "main": "src/index.ts",
5
+ "types": "dist/index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "source": "./src/index.ts",
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
5
18
  "license": "UNLICENSED",
6
19
  "repository": {
7
20
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -9,18 +22,20 @@
9
22
  "directory": "packages/permissions"
10
23
  },
11
24
  "devDependencies": {
12
- "@codeleap/config": "6.3.0",
13
- "@codeleap/store": "6.3.0",
14
- "@codeleap/types": "6.3.0",
25
+ "@codeleap/config": "7.0.0",
26
+ "@codeleap/logger": "7.0.0",
27
+ "@codeleap/store": "7.0.0",
28
+ "@codeleap/types": "7.0.0",
15
29
  "ts-node-dev": "1.1.8"
16
30
  },
17
31
  "scripts": {
18
- "build": "echo 'No build needed'"
32
+ "build": "tsc --build tsconfig.build.json",
33
+ "typecheck": "bun tsc --noEmit -p ./tsconfig.json"
19
34
  },
20
35
  "peerDependencies": {
21
- "@codeleap/store": "6.3.0",
22
- "@codeleap/types": "6.3.0",
23
- "@codeleap/logger": "6.3.0",
36
+ "@codeleap/store": "7.0.0",
37
+ "@codeleap/types": "7.0.0",
38
+ "@codeleap/logger": "7.0.0",
24
39
  "typescript": "5.5.2"
25
40
  }
26
- }
41
+ }
package/src/Permission.ts CHANGED
@@ -4,6 +4,18 @@ import { PermissionConfig, PermissionStatus } from './globals'
4
4
  import { TypeGuards } from '@codeleap/types'
5
5
  import { logger } from '@codeleap/logger'
6
6
 
7
+ /**
8
+ * Represents a single named permission with persistent reactive state.
9
+ *
10
+ * State is backed by `globalState` and persisted under the key
11
+ * `"permissions-<name>"`, so the last-known status survives page reloads or
12
+ * app restarts without an extra async check.
13
+ *
14
+ * The status machine has one sticky transition: once a permission reaches
15
+ * `"blocked"` it will never regress to `"denied"` via {@link check} — because
16
+ * on most platforms a blocked permission can only be cleared through the OS
17
+ * settings screen, not by a subsequent API call.
18
+ */
7
19
  export class Permission {
8
20
  /** The name of the permission. */
9
21
  public name: string
@@ -14,8 +26,18 @@ export class Permission {
14
26
 
15
27
  private requestStatus: () => Promise<PermissionStatus>
16
28
 
29
+ /**
30
+ * Set to `true` to enable verbose permission logs via `@codeleap/logger`.
31
+ * Applies to every `Permission` instance; toggling at runtime takes effect
32
+ * immediately.
33
+ */
17
34
  static logsEnabled: boolean = false
18
35
 
36
+ /**
37
+ * Converts a raw status string into a structured descriptor object.
38
+ * Prefer this over equality checks scattered across call sites so that
39
+ * adding a new status variant only requires updating this method.
40
+ */
19
41
  static is(status: PermissionStatus) {
20
42
  return {
21
43
  value: status,
@@ -30,8 +52,8 @@ export class Permission {
30
52
  public config: PermissionConfig
31
53
 
32
54
  /**
33
- * Gets the current permission status.
34
- * @returns The current permission status.
55
+ * Snapshot of the persisted status. Returns `null` until the first
56
+ * `check()` or `request()` resolves.
35
57
  */
36
58
  get value() {
37
59
  return this.state.get()
@@ -70,24 +92,29 @@ export class Permission {
70
92
  }
71
93
 
72
94
  /**
73
- * Updates the permission status.
74
- * @param newStatus - The new permission status.
95
+ * Directly overwrites the stored status without going through the native
96
+ * check or request flow. Useful when the status is already known from an
97
+ * external source (e.g. a push-notification token registration callback).
75
98
  */
76
99
  set(newStatus: PermissionStatus) {
77
100
  this.state.set(newStatus)
78
101
  }
79
102
 
80
103
  /**
81
- * React hook for consuming the permission state.
82
- * @returns The current permission status state.
104
+ * React hook that subscribes a component to this permission's reactive
105
+ * state. Re-renders whenever the status changes.
83
106
  */
84
107
  use() {
85
108
  return this.state.use()
86
109
  }
87
110
 
88
111
  /**
89
- * Checks the current permission status and updates the state if it has changed.
90
- * @returns The checked permission status.
112
+ * Queries the native status without showing a user-facing prompt.
113
+ *
114
+ * If the platform returns `"denied"` but the stored value is already
115
+ * `"blocked"`, the `"blocked"` value is preserved. This guards against
116
+ * platforms that report a previously-blocked permission as merely denied on
117
+ * subsequent queries, which would incorrectly allow a re-request attempt.
91
118
  */
92
119
  async check() {
93
120
  let status = await this.checkStatus()
@@ -108,8 +135,10 @@ export class Permission {
108
135
  }
109
136
 
110
137
  /**
111
- * Requests the permission and updates the state if it has changed.
112
- * @returns The requested permission status.
138
+ * Triggers an interactive permission prompt (may show a system dialog) and
139
+ * updates stored state when the result differs from the current value.
140
+ * Only call this in direct response to a user action; invoking it
141
+ * speculatively or on mount will fail silently on most platforms.
113
142
  */
114
143
  async request() {
115
144
  const status = await this.requestStatus()
@@ -3,6 +3,15 @@ import { PermissionOptions } from './types'
3
3
  import { PermissionStatus, PermissionConfig } from './globals'
4
4
  import { logger } from '@codeleap/logger'
5
5
 
6
+ /**
7
+ * Registry and coordinator for a fixed set of named permissions.
8
+ *
9
+ * Instantiating this class immediately calls `checkAll()` in the background so
10
+ * that every permission's cached status is refreshed before the first render.
11
+ * The `requester` callback is the single authoritative place to implement any
12
+ * pre-prompt logic (e.g. showing an educational interstitial before the OS
13
+ * dialog appears).
14
+ */
6
15
  export class PermissionsManager<P extends string> {
7
16
  private requester: (permission: Permission) => Promise<PermissionStatus>
8
17
 
@@ -13,14 +22,15 @@ export class PermissionsManager<P extends string> {
13
22
  }
14
23
 
15
24
  /**
16
- * Retrieves the current status values of all permissions.
17
- * @returns An object mapping permission names to their statuses.
25
+ * Snapshot of all registered permissions' current statuses. Values reflect
26
+ * whatever is in persistent state at the moment of access — call
27
+ * `checkAll()` first if you need fresh data from the platform.
18
28
  */
19
29
  get values() {
20
30
  const values = {} as Record<P, PermissionStatus>
21
31
 
22
32
  this.forEach(permission => {
23
- values[permission.name] = permission.value
33
+ values[permission.name as P] = permission.value
24
34
  })
25
35
 
26
36
  return values
@@ -59,17 +69,20 @@ export class PermissionsManager<P extends string> {
59
69
  }
60
70
 
61
71
  /**
62
- * React hook for consuming the permission state.
63
- * @returns The current permission status state.
72
+ * React hook that subscribes a component to the reactive state of a single
73
+ * named permission. Re-renders whenever that permission's status changes.
64
74
  */
65
75
  use(permissionName: P) {
66
76
  return this.permissions[permissionName].use()
67
77
  }
68
78
 
69
79
  /**
70
- * Requests a specific permission.
71
- * @param {P} permissionName - The name of the permission to request.
72
- * @returns The updated permission status.
80
+ * Requests a single permission through the manager's `requester` callback.
81
+ * The result passes through {@link Permission.is} so callers get a
82
+ * structured descriptor rather than a raw string.
83
+ *
84
+ * Note: this bypasses `Permission.request()` on the individual instance —
85
+ * the `requester` callback owns the full flow, including any pre-prompt UI.
73
86
  */
74
87
  async request(permissionName: P) {
75
88
  const permission = this.permissions[permissionName]
@@ -78,9 +91,8 @@ export class PermissionsManager<P extends string> {
78
91
  }
79
92
 
80
93
  /**
81
- * Checks the status of a specific permission.
82
- * @param {P} permissionName - The name of the permission to check.
83
- * @returns The current permission status.
94
+ * Silently queries a single permission's current status (no system dialog).
95
+ * Returns a structured descriptor via {@link Permission.is}.
84
96
  */
85
97
  async check(permissionName: P) {
86
98
  const permission = this.permissions[permissionName]
@@ -89,9 +101,9 @@ export class PermissionsManager<P extends string> {
89
101
  }
90
102
 
91
103
  /**
92
- * Requests multiple permissions at once.
93
- * @param {P[]} permissionsNames - An array of permission names to request.
94
- * @returns A record of updated permission statuses.
104
+ * Requests multiple permissions sequentially. Requests are serial, not
105
+ * parallel, to avoid triggering concurrent system dialogs which may be
106
+ * rejected or silently dropped on some platforms.
95
107
  */
96
108
  async requestMany<K extends P>(permissionsNames: K[]): Promise<Record<K, ReturnType<typeof Permission.is>>> {
97
109
  const status = {} as Record<K, ReturnType<typeof Permission.is>>
@@ -104,9 +116,9 @@ export class PermissionsManager<P extends string> {
104
116
  }
105
117
 
106
118
  /**
107
- * Checks the status of multiple permissions at once.
108
- * @param {P[]} permissionsNames - An array of permission names to check.
109
- * @returns A record of current permission statuses.
119
+ * Silently queries multiple permissions sequentially. Serial execution
120
+ * ensures consistent ordering and avoids race conditions in the underlying
121
+ * state store.
110
122
  */
111
123
  async checkMany<K extends P>(permissionsNames: K[]): Promise<Record<K, ReturnType<typeof Permission.is>>> {
112
124
  const status = {} as Record<K, ReturnType<typeof Permission.is>>
@@ -119,8 +131,9 @@ export class PermissionsManager<P extends string> {
119
131
  }
120
132
 
121
133
  /**
122
- * Checks the status of all managed permissions.
123
- * @returns A record of all current permission statuses.
134
+ * Silently refreshes every registered permission. Called automatically
135
+ * during construction so that cached statuses are up to date before the
136
+ * first render without requiring the caller to await initialization.
124
137
  */
125
138
  async checkAll() {
126
139
  this.log('checkAll')
package/src/globals.ts CHANGED
@@ -1,10 +1,42 @@
1
1
 
2
+ /**
3
+ * Extend this interface via module augmentation in the consuming app to attach
4
+ * platform-specific metadata (e.g. Android rationale strings, iOS usage
5
+ * descriptions) to every registered permission.
6
+ *
7
+ * @example
8
+ * declare module '@codeleap/permissions' {
9
+ * interface PermissionConfig {
10
+ * rationale: string
11
+ * }
12
+ * }
13
+ */
2
14
  export interface PermissionConfig {
3
15
 
4
16
  }
5
17
 
18
+ /**
19
+ * Extend this interface via module augmentation to register the set of valid
20
+ * permission statuses for the target platform. The keys become the union type
21
+ * used throughout the package.
22
+ *
23
+ * @example
24
+ * declare module '@codeleap/permissions' {
25
+ * interface Status {
26
+ * granted: never
27
+ * denied: never
28
+ * blocked: never
29
+ * limited: never
30
+ * }
31
+ * }
32
+ */
6
33
  export interface Status {
7
34
 
8
35
  }
9
36
 
37
+ /**
38
+ * Union of every key registered in {@link Status} plus `null`.
39
+ * `null` means the status has not yet been resolved (e.g. before the first
40
+ * `check()` call completes).
41
+ */
10
42
  export type PermissionStatus = keyof Status | null
package/src/types.ts CHANGED
@@ -1,6 +1,14 @@
1
1
  import { AnyRecord } from '@codeleap/types'
2
2
  import { PermissionStatus } from './globals'
3
3
 
4
+ /**
5
+ * Construction options for a single {@link Permission} instance.
6
+ *
7
+ * `check` and `request` are intentionally separate callbacks because many
8
+ * platforms distinguish a silent status query (no UI) from an interactive
9
+ * prompt (may show a system dialog). Provide both even if the underlying API
10
+ * is the same — the manager calls them at different points in the flow.
11
+ */
4
12
  export type PermissionOptions<Config extends AnyRecord> = {
5
13
  name: string
6
14
  config: Config
package/package.json.bak DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "name": "@codeleap/permissions",
3
- "version": "6.3.0",
4
- "main": "src/index.ts",
5
- "license": "UNLICENSED",
6
- "repository": {
7
- "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
8
- "type": "git",
9
- "directory": "packages/permissions"
10
- },
11
- "devDependencies": {
12
- "@codeleap/config": "workspace:*",
13
- "@codeleap/store": "workspace:*",
14
- "@codeleap/types": "workspace:*",
15
- "ts-node-dev": "1.1.8"
16
- },
17
- "scripts": {
18
- "build": "echo 'No build needed'"
19
- },
20
- "peerDependencies": {
21
- "@codeleap/store": "workspace:*",
22
- "@codeleap/types": "workspace:*",
23
- "@codeleap/logger": "workspace:*",
24
- "typescript": "5.5.2"
25
- }
26
- }