@ankhorage/permissions 0.2.2 → 0.2.4
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/CHANGELOG.md +13 -0
- package/README.md +27 -3
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/expo/adapters/mediaLibrary.d.ts.map +1 -1
- package/dist/expo/adapters/mediaLibrary.js +15 -3
- package/dist/expo/adapters/mediaLibrary.js.map +1 -1
- package/dist/expo/adapters/notifications.d.ts.map +1 -1
- package/dist/expo/adapters/notifications.js +32 -3
- package/dist/expo/adapters/notifications.js.map +1 -1
- package/dist/expo/client.d.ts +14 -0
- package/dist/expo/client.d.ts.map +1 -1
- package/dist/expo/client.js +42 -13
- package/dist/expo/client.js.map +1 -1
- package/dist/expo/client.testSupport.d.ts +54 -0
- package/dist/expo/client.testSupport.d.ts.map +1 -0
- package/dist/expo/client.testSupport.js +183 -0
- package/dist/expo/client.testSupport.js.map +1 -0
- package/dist/expo/manifest.d.ts +5 -0
- package/dist/expo/manifest.d.ts.map +1 -1
- package/dist/expo/manifest.js +5 -0
- package/dist/expo/manifest.js.map +1 -1
- package/dist/expo/permissionResponse.d.ts.map +1 -1
- package/dist/expo/permissionResponse.js +16 -9
- package/dist/expo/permissionResponse.js.map +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +16 -11
- package/dist/react/index.js.map +1 -1
- package/dist/state/permissionState.d.ts +2 -0
- package/dist/state/permissionState.d.ts.map +1 -1
- package/dist/state/permissionState.js +3 -1
- package/dist/state/permissionState.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +27 -17
- package/dist/testing/index.js.map +1 -1
- package/dist/web/index.d.ts +1 -2
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +2 -76
- package/dist/web/index.js.map +1 -1
- package/dist/web/permissionStates.d.ts +10 -0
- package/dist/web/permissionStates.d.ts.map +1 -0
- package/dist/web/permissionStates.js +64 -0
- package/dist/web/permissionStates.js.map +1 -0
- package/package.json +22 -13
- package/src/cli/index.ts +1 -0
- package/src/expo/adapters/mediaLibrary.ts +28 -4
- package/src/expo/adapters/notifications.ts +73 -4
- package/src/expo/client.notifications.test.ts +106 -0
- package/src/expo/client.test.ts +109 -349
- package/src/expo/client.testSupport.ts +188 -0
- package/src/expo/client.ts +45 -13
- package/src/expo/manifest.test.ts +49 -47
- package/src/expo/manifest.ts +7 -5
- package/src/expo/modules.d.ts +18 -4
- package/src/expo/permissionResponse.ts +26 -9
- package/src/manager/createPermissionManager.test.ts +48 -50
- package/src/react/index.tsx +21 -13
- package/src/registry/permissions.test.ts +4 -3
- package/src/state/permissionState.test.ts +8 -1
- package/src/state/permissionState.ts +4 -7
- package/src/testing/index.ts +36 -25
- package/src/web/index.test.ts +149 -151
- package/src/web/index.ts +9 -102
- package/src/web/permissionStates.ts +83 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @ankhorage/permissions
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f4b561b: Adopt the current Devtools policy and keep permission clients compatible with strict React and security linting.
|
|
8
|
+
|
|
9
|
+
## 0.2.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 8ee977d: Normalize blocked and partial Expo authorization states, project the Expo client dependency,
|
|
14
|
+
and add settings recovery with a stable failure contract.
|
|
15
|
+
|
|
3
16
|
## 0.2.2
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# PERMISSIONS
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Cross-platform permission registry and runtime helpers for Expo, React Native, and React Native Web apps, with unified request/check APIs for camera, media, location, notifications, microphone, and other web/native capabilities.
|
|
9
9
|
|
|
@@ -132,11 +132,35 @@ Fake clients make permission flows testable without native devices, browser
|
|
|
132
132
|
prompts, simulators, or network access.
|
|
133
133
|
|
|
134
134
|
Module: `src/testing/index.ts`
|
|
135
|
-
Source: `src/testing/index.ts:
|
|
135
|
+
Source: `src/testing/index.ts:61:1`
|
|
136
136
|
Related symbols: `FakePermissionClient`, `FakePermissionClientOptions`
|
|
137
137
|
|
|
138
138
|
</details>
|
|
139
139
|
|
|
140
|
+
<details>
|
|
141
|
+
<summary>createPermissionClient</summary>
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
createPermissionClient() => PermissionClient
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Creates a permission client backed by Expo SDK modules.
|
|
148
|
+
|
|
149
|
+
Denials that Expo cannot request again are normalized to `blocked`. Limited
|
|
150
|
+
media-library access and provisional or ephemeral iOS notification access
|
|
151
|
+
are normalized to usable `limited` states, where `granted` remains true.
|
|
152
|
+
The client requires `expo-linking` for settings recovery and rejects with a
|
|
153
|
+
stable error when the operating-system settings page cannot be opened.
|
|
154
|
+
|
|
155
|
+
Expo modules are loaded only when a client operation uses them. The package
|
|
156
|
+
root and Expo manifest metadata remain free of native module imports.
|
|
157
|
+
|
|
158
|
+
Module: `src/expo/client.ts`
|
|
159
|
+
Source: `src/expo/client.ts:42:1`
|
|
160
|
+
Related symbols: `PermissionClient`
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
140
164
|
<details>
|
|
141
165
|
<summary>createPermissionManager</summary>
|
|
142
166
|
|
|
@@ -168,7 +192,7 @@ Unsupported web APIs resolve to `status: 'unavailable'`. The adapter does not
|
|
|
168
192
|
import DOM types and does not assume it is running in a browser.
|
|
169
193
|
|
|
170
194
|
Module: `src/web/index.ts`
|
|
171
|
-
Source: `src/web/index.ts:
|
|
195
|
+
Source: `src/web/index.ts:80:1`
|
|
172
196
|
Related symbols: `PermissionClient`, `WebPermissionClientOptions`
|
|
173
197
|
|
|
174
198
|
</details>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export { default } from '../ankh.provider.js';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaLibrary.d.ts","sourceRoot":"","sources":["../../../src/expo/adapters/mediaLibrary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"mediaLibrary.d.ts","sourceRoot":"","sources":["../../../src/expo/adapters/mediaLibrary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAWvD,eAAO,MAAM,mBAAmB,EAAE,qBAsBjC,CAAC"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Permission as KnownPermission } from '../../registry/permissions';
|
|
2
|
-
import {
|
|
2
|
+
import { createPermissionState } from '../../state/permissionState';
|
|
3
|
+
import { createExpoPermissionState, createExpoUnavailableState, } from '../permissionResponse';
|
|
3
4
|
export const mediaLibraryAdapter = {
|
|
4
5
|
async getStatus(permission) {
|
|
5
6
|
try {
|
|
6
7
|
const mod = await import('expo-media-library');
|
|
7
8
|
const result = await mod.getPermissionsAsync(isWriteOnlyPermission(permission));
|
|
8
|
-
return
|
|
9
|
+
return createMediaLibraryPermissionState(permission, result);
|
|
9
10
|
}
|
|
10
11
|
catch (error) {
|
|
11
12
|
return createExpoUnavailableState(permission, error);
|
|
@@ -15,13 +16,24 @@ export const mediaLibraryAdapter = {
|
|
|
15
16
|
try {
|
|
16
17
|
const mod = await import('expo-media-library');
|
|
17
18
|
const result = await mod.requestPermissionsAsync(isWriteOnlyPermission(permission));
|
|
18
|
-
return
|
|
19
|
+
return createMediaLibraryPermissionState(permission, result);
|
|
19
20
|
}
|
|
20
21
|
catch (error) {
|
|
21
22
|
return createExpoUnavailableState(permission, error);
|
|
22
23
|
}
|
|
23
24
|
},
|
|
24
25
|
};
|
|
26
|
+
function createMediaLibraryPermissionState(permission, response) {
|
|
27
|
+
if (response.accessPrivileges !== 'limited') {
|
|
28
|
+
return createExpoPermissionState(permission, response);
|
|
29
|
+
}
|
|
30
|
+
return createPermissionState({
|
|
31
|
+
permission,
|
|
32
|
+
status: 'limited',
|
|
33
|
+
canAskAgain: response.canAskAgain,
|
|
34
|
+
reason: 'Media library access is limited to user-selected assets.',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
25
37
|
function isWriteOnlyPermission(permission) {
|
|
26
38
|
return permission === KnownPermission.MediaLibraryWrite;
|
|
27
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaLibrary.js","sourceRoot":"","sources":["../../../src/expo/adapters/mediaLibrary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"mediaLibrary.js","sourceRoot":"","sources":["../../../src/expo/adapters/mediaLibrary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAwB,MAAM,6BAA6B,CAAC;AAE1F,OAAO,EACL,yBAAyB,EACzB,0BAA0B,GAE3B,MAAM,uBAAuB,CAAC;AAM/B,MAAM,CAAC,MAAM,mBAAmB,GAA0B;IACxD,KAAK,CAAC,SAAS,CAAC,UAAsB;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC,CAAC;YAEhF,OAAO,iCAAiC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAsB;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,uBAAuB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC,CAAC;YAEpF,OAAO,iCAAiC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,iCAAiC,CACxC,UAAsB,EACtB,QAA4C;IAE5C,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,yBAAyB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,qBAAqB,CAAC;QAC3B,UAAU;QACV,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,MAAM,EAAE,0DAA0D;KACnE,CAAC,CAAC;AACL,CAAC;AAED,SAAS,qBAAqB,CAAC,UAAsB;IACnD,OAAO,UAAU,KAAK,eAAe,CAAC,iBAAiB,CAAC;AAC1D,CAAC","sourcesContent":["import type { Permission } from '../../registry/permissions';\nimport { Permission as KnownPermission } from '../../registry/permissions';\nimport { createPermissionState, type PermissionState } from '../../state/permissionState';\nimport type { ExpoPermissionAdapter } from '../client';\nimport {\n createExpoPermissionState,\n createExpoUnavailableState,\n type ExpoPermissionResponse,\n} from '../permissionResponse';\n\ninterface ExpoMediaLibraryPermissionResponse extends ExpoPermissionResponse {\n readonly accessPrivileges?: 'all' | 'limited' | 'none';\n}\n\nexport const mediaLibraryAdapter: ExpoPermissionAdapter = {\n async getStatus(permission: Permission): Promise<PermissionState> {\n try {\n const mod = await import('expo-media-library');\n const result = await mod.getPermissionsAsync(isWriteOnlyPermission(permission));\n\n return createMediaLibraryPermissionState(permission, result);\n } catch (error) {\n return createExpoUnavailableState(permission, error);\n }\n },\n\n async request(permission: Permission): Promise<PermissionState> {\n try {\n const mod = await import('expo-media-library');\n const result = await mod.requestPermissionsAsync(isWriteOnlyPermission(permission));\n\n return createMediaLibraryPermissionState(permission, result);\n } catch (error) {\n return createExpoUnavailableState(permission, error);\n }\n },\n};\n\nfunction createMediaLibraryPermissionState(\n permission: Permission,\n response: ExpoMediaLibraryPermissionResponse,\n): PermissionState {\n if (response.accessPrivileges !== 'limited') {\n return createExpoPermissionState(permission, response);\n }\n\n return createPermissionState({\n permission,\n status: 'limited',\n canAskAgain: response.canAskAgain,\n reason: 'Media library access is limited to user-selected assets.',\n });\n}\n\nfunction isWriteOnlyPermission(permission: Permission): boolean {\n return permission === KnownPermission.MediaLibraryWrite;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../../src/expo/adapters/notifications.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../../src/expo/adapters/notifications.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAavD,eAAO,MAAM,oBAAoB,EAAE,qBAsBlC,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createPermissionState } from '../../state/permissionState';
|
|
2
|
+
import { createExpoPermissionState, createExpoUnavailableState, } from '../permissionResponse';
|
|
2
3
|
export const notificationsAdapter = {
|
|
3
4
|
async getStatus(permission) {
|
|
4
5
|
try {
|
|
5
6
|
const mod = await import('expo-notifications');
|
|
6
7
|
const result = await mod.getPermissionsAsync();
|
|
7
|
-
return
|
|
8
|
+
return createNotificationPermissionState(permission, result);
|
|
8
9
|
}
|
|
9
10
|
catch (error) {
|
|
10
11
|
return createExpoUnavailableState(permission, error);
|
|
@@ -14,11 +15,39 @@ export const notificationsAdapter = {
|
|
|
14
15
|
try {
|
|
15
16
|
const mod = await import('expo-notifications');
|
|
16
17
|
const result = await mod.requestPermissionsAsync();
|
|
17
|
-
return
|
|
18
|
+
return createNotificationPermissionState(permission, result);
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
21
|
return createExpoUnavailableState(permission, error);
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
24
|
};
|
|
25
|
+
function createNotificationPermissionState(permission, response) {
|
|
26
|
+
switch (response.ios?.status) {
|
|
27
|
+
case 0:
|
|
28
|
+
return createNotificationState(permission, response, 'unknown', 'iOS notification authorization has not been determined.');
|
|
29
|
+
case 1:
|
|
30
|
+
return createNotificationState(permission, response, 'denied', 'iOS notification authorization is denied.');
|
|
31
|
+
case 2:
|
|
32
|
+
return createNotificationState(permission, response, 'granted');
|
|
33
|
+
case 3:
|
|
34
|
+
return createNotificationState(permission, response, 'limited', 'iOS notification authorization is provisional.');
|
|
35
|
+
case 4:
|
|
36
|
+
return createNotificationState(permission, response, 'limited', 'iOS notification authorization is ephemeral.');
|
|
37
|
+
default:
|
|
38
|
+
return createExpoPermissionState(permission, response);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function createNotificationState(permission, response, status, reason) {
|
|
42
|
+
const state = createExpoPermissionState(permission, {
|
|
43
|
+
status,
|
|
44
|
+
canAskAgain: response.canAskAgain,
|
|
45
|
+
});
|
|
46
|
+
return createPermissionState({
|
|
47
|
+
permission,
|
|
48
|
+
status: state.status,
|
|
49
|
+
canAskAgain: state.canAskAgain,
|
|
50
|
+
reason: state.reason ?? reason,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
24
53
|
//# sourceMappingURL=notifications.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/expo/adapters/notifications.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/expo/adapters/notifications.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,6BAA6B,CAAC;AAE1F,OAAO,EACL,yBAAyB,EACzB,0BAA0B,GAE3B,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,CAAC,MAAM,oBAAoB,GAA0B;IACzD,KAAK,CAAC,SAAS,CAAC,UAAsB;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,mBAAmB,EAAE,CAAC;YAE/C,OAAO,iCAAiC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAsB;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,uBAAuB,EAAE,CAAC;YAEnD,OAAO,iCAAiC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,iCAAiC,CACxC,UAAsB,EACtB,QAA4C;IAE5C,QAAQ,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;QAC7B,KAAK,CAAC;YACJ,OAAO,uBAAuB,CAC5B,UAAU,EACV,QAAQ,EACR,SAAS,EACT,yDAAyD,CAC1D,CAAC;QACJ,KAAK,CAAC;YACJ,OAAO,uBAAuB,CAC5B,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,2CAA2C,CAC5C,CAAC;QACJ,KAAK,CAAC;YACJ,OAAO,uBAAuB,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAClE,KAAK,CAAC;YACJ,OAAO,uBAAuB,CAC5B,UAAU,EACV,QAAQ,EACR,SAAS,EACT,gDAAgD,CACjD,CAAC;QACJ,KAAK,CAAC;YACJ,OAAO,uBAAuB,CAC5B,UAAU,EACV,QAAQ,EACR,SAAS,EACT,8CAA8C,CAC/C,CAAC;QACJ;YACE,OAAO,yBAAyB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAC9B,UAAsB,EACtB,QAA4C,EAC5C,MAAiC,EACjC,MAAe;IAEf,MAAM,KAAK,GAAG,yBAAyB,CAAC,UAAU,EAAE;QAClD,MAAM;QACN,WAAW,EAAE,QAAQ,CAAC,WAAW;KAClC,CAAC,CAAC;IAEH,OAAO,qBAAqB,CAAC;QAC3B,UAAU;QACV,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM;KAC/B,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { Permission } from '../../registry/permissions';\nimport { createPermissionState, type PermissionState } from '../../state/permissionState';\nimport type { ExpoPermissionAdapter } from '../client';\nimport {\n createExpoPermissionState,\n createExpoUnavailableState,\n type ExpoPermissionResponse,\n} from '../permissionResponse';\n\ninterface ExpoNotificationPermissionResponse extends ExpoPermissionResponse {\n readonly ios?: {\n readonly status?: number;\n };\n}\n\nexport const notificationsAdapter: ExpoPermissionAdapter = {\n async getStatus(permission: Permission): Promise<PermissionState> {\n try {\n const mod = await import('expo-notifications');\n const result = await mod.getPermissionsAsync();\n\n return createNotificationPermissionState(permission, result);\n } catch (error) {\n return createExpoUnavailableState(permission, error);\n }\n },\n\n async request(permission: Permission): Promise<PermissionState> {\n try {\n const mod = await import('expo-notifications');\n const result = await mod.requestPermissionsAsync();\n\n return createNotificationPermissionState(permission, result);\n } catch (error) {\n return createExpoUnavailableState(permission, error);\n }\n },\n};\n\nfunction createNotificationPermissionState(\n permission: Permission,\n response: ExpoNotificationPermissionResponse,\n): PermissionState {\n switch (response.ios?.status) {\n case 0:\n return createNotificationState(\n permission,\n response,\n 'unknown',\n 'iOS notification authorization has not been determined.',\n );\n case 1:\n return createNotificationState(\n permission,\n response,\n 'denied',\n 'iOS notification authorization is denied.',\n );\n case 2:\n return createNotificationState(permission, response, 'granted');\n case 3:\n return createNotificationState(\n permission,\n response,\n 'limited',\n 'iOS notification authorization is provisional.',\n );\n case 4:\n return createNotificationState(\n permission,\n response,\n 'limited',\n 'iOS notification authorization is ephemeral.',\n );\n default:\n return createExpoPermissionState(permission, response);\n }\n}\n\nfunction createNotificationState(\n permission: Permission,\n response: ExpoNotificationPermissionResponse,\n status: PermissionState['status'],\n reason?: string,\n): PermissionState {\n const state = createExpoPermissionState(permission, {\n status,\n canAskAgain: response.canAskAgain,\n });\n\n return createPermissionState({\n permission,\n status: state.status,\n canAskAgain: state.canAskAgain,\n reason: state.reason ?? reason,\n });\n}\n"]}
|
package/dist/expo/client.d.ts
CHANGED
|
@@ -5,5 +5,19 @@ export interface ExpoPermissionAdapter {
|
|
|
5
5
|
getStatus(permission: Permission): Promise<PermissionState>;
|
|
6
6
|
request(permission: Permission): Promise<PermissionState>;
|
|
7
7
|
}
|
|
8
|
+
/***
|
|
9
|
+
* Creates a permission client backed by Expo SDK modules.
|
|
10
|
+
*
|
|
11
|
+
* Denials that Expo cannot request again are normalized to `blocked`. Limited
|
|
12
|
+
* media-library access and provisional or ephemeral iOS notification access
|
|
13
|
+
* are normalized to usable `limited` states, where `granted` remains true.
|
|
14
|
+
* The client requires `expo-linking` for settings recovery and rejects with a
|
|
15
|
+
* stable error when the operating-system settings page cannot be opened.
|
|
16
|
+
*
|
|
17
|
+
* Expo modules are loaded only when a client operation uses them. The package
|
|
18
|
+
* root and Expo manifest metadata remain free of native module imports.
|
|
19
|
+
*
|
|
20
|
+
* @readme
|
|
21
|
+
*/
|
|
8
22
|
export declare function createPermissionClient(): PermissionClient;
|
|
9
23
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/expo/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/expo/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAUvF,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC3D;AAYD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAkDzD"}
|
package/dist/expo/client.js
CHANGED
|
@@ -6,47 +6,76 @@ import { mediaLibraryAdapter } from './adapters/mediaLibrary';
|
|
|
6
6
|
import { microphoneAdapter } from './adapters/microphone';
|
|
7
7
|
import { notificationsAdapter } from './adapters/notifications';
|
|
8
8
|
import { EXPO_PERMISSION_SUPPORT } from './manifest';
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
[Permission.
|
|
12
|
-
[Permission.
|
|
13
|
-
[Permission.
|
|
14
|
-
[Permission.
|
|
15
|
-
[Permission.
|
|
16
|
-
[Permission.
|
|
17
|
-
|
|
9
|
+
const OPEN_SETTINGS_ERROR_MESSAGE = 'Unable to open application settings.';
|
|
10
|
+
const adapters = new Map([
|
|
11
|
+
[Permission.Camera, cameraAdapter],
|
|
12
|
+
[Permission.MediaLibrary, mediaLibraryAdapter],
|
|
13
|
+
[Permission.MediaLibraryWrite, mediaLibraryAdapter],
|
|
14
|
+
[Permission.Microphone, microphoneAdapter],
|
|
15
|
+
[Permission.LocationForeground, locationAdapter],
|
|
16
|
+
[Permission.LocationBackground, locationAdapter],
|
|
17
|
+
[Permission.Notifications, notificationsAdapter],
|
|
18
|
+
]);
|
|
19
|
+
/***
|
|
20
|
+
* Creates a permission client backed by Expo SDK modules.
|
|
21
|
+
*
|
|
22
|
+
* Denials that Expo cannot request again are normalized to `blocked`. Limited
|
|
23
|
+
* media-library access and provisional or ephemeral iOS notification access
|
|
24
|
+
* are normalized to usable `limited` states, where `granted` remains true.
|
|
25
|
+
* The client requires `expo-linking` for settings recovery and rejects with a
|
|
26
|
+
* stable error when the operating-system settings page cannot be opened.
|
|
27
|
+
*
|
|
28
|
+
* Expo modules are loaded only when a client operation uses them. The package
|
|
29
|
+
* root and Expo manifest metadata remain free of native module imports.
|
|
30
|
+
*
|
|
31
|
+
* @readme
|
|
32
|
+
*/
|
|
18
33
|
export function createPermissionClient() {
|
|
19
34
|
return {
|
|
20
35
|
async getStatus(permission) {
|
|
21
|
-
const metadata =
|
|
36
|
+
const metadata = getPermissionSupport(permission);
|
|
22
37
|
if (metadata.support === 'unsupported' || metadata.support === 'notImplemented') {
|
|
23
38
|
return createUnavailableSupportState(permission);
|
|
24
39
|
}
|
|
25
40
|
if (metadata.support === 'notRequired') {
|
|
26
41
|
return createNotRequiredState(permission);
|
|
27
42
|
}
|
|
28
|
-
const adapter = adapters
|
|
43
|
+
const adapter = adapters.get(permission);
|
|
29
44
|
if (!adapter) {
|
|
30
45
|
throw new Error(`No adapter registered for permission: ${permission}`);
|
|
31
46
|
}
|
|
32
47
|
return adapter.getStatus(permission);
|
|
33
48
|
},
|
|
34
49
|
async request(permission) {
|
|
35
|
-
const metadata =
|
|
50
|
+
const metadata = getPermissionSupport(permission);
|
|
36
51
|
if (metadata.support === 'unsupported' || metadata.support === 'notImplemented') {
|
|
37
52
|
return createUnavailableSupportState(permission);
|
|
38
53
|
}
|
|
39
54
|
if (metadata.support === 'notRequired') {
|
|
40
55
|
return createNotRequiredState(permission);
|
|
41
56
|
}
|
|
42
|
-
const adapter = adapters
|
|
57
|
+
const adapter = adapters.get(permission);
|
|
43
58
|
if (!adapter) {
|
|
44
59
|
throw new Error(`No adapter registered for permission: ${permission}`);
|
|
45
60
|
}
|
|
46
61
|
return adapter.request(permission);
|
|
47
62
|
},
|
|
63
|
+
async openSettings() {
|
|
64
|
+
try {
|
|
65
|
+
const linking = await import('expo-linking');
|
|
66
|
+
await linking.openSettings();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new Error(OPEN_SETTINGS_ERROR_MESSAGE, {
|
|
70
|
+
cause: error,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
},
|
|
48
74
|
};
|
|
49
75
|
}
|
|
76
|
+
function getPermissionSupport(permission) {
|
|
77
|
+
return Reflect.get(EXPO_PERMISSION_SUPPORT, permission);
|
|
78
|
+
}
|
|
50
79
|
function createUnavailableSupportState(permission) {
|
|
51
80
|
return createPermissionState({
|
|
52
81
|
permission,
|
package/dist/expo/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/expo/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAwB,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/expo/client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PermissionClient } from '../client/types';\nimport { Permission } from '../registry/permissions';\nimport { createPermissionState, type PermissionState } from '../state/permissionState';\nimport { cameraAdapter } from './adapters/camera';\nimport { locationAdapter } from './adapters/location';\nimport { mediaLibraryAdapter } from './adapters/mediaLibrary';\nimport { microphoneAdapter } from './adapters/microphone';\nimport { notificationsAdapter } from './adapters/notifications';\nimport { EXPO_PERMISSION_SUPPORT } from './manifest';\n\nconst OPEN_SETTINGS_ERROR_MESSAGE = 'Unable to open application settings.';\n\nexport interface ExpoPermissionAdapter {\n getStatus(permission: Permission): Promise<PermissionState>;\n request(permission: Permission): Promise<PermissionState>;\n}\n\nconst adapters = new Map<Permission, ExpoPermissionAdapter>([\n [Permission.Camera, cameraAdapter],\n [Permission.MediaLibrary, mediaLibraryAdapter],\n [Permission.MediaLibraryWrite, mediaLibraryAdapter],\n [Permission.Microphone, microphoneAdapter],\n [Permission.LocationForeground, locationAdapter],\n [Permission.LocationBackground, locationAdapter],\n [Permission.Notifications, notificationsAdapter],\n]);\n\n/***\n * Creates a permission client backed by Expo SDK modules.\n *\n * Denials that Expo cannot request again are normalized to `blocked`. Limited\n * media-library access and provisional or ephemeral iOS notification access\n * are normalized to usable `limited` states, where `granted` remains true.\n * The client requires `expo-linking` for settings recovery and rejects with a\n * stable error when the operating-system settings page cannot be opened.\n *\n * Expo modules are loaded only when a client operation uses them. The package\n * root and Expo manifest metadata remain free of native module imports.\n *\n * @readme\n */\nexport function createPermissionClient(): PermissionClient {\n return {\n async getStatus(permission: Permission): Promise<PermissionState> {\n const metadata = getPermissionSupport(permission);\n if (metadata.support === 'unsupported' || metadata.support === 'notImplemented') {\n return createUnavailableSupportState(permission);\n }\n\n if (metadata.support === 'notRequired') {\n return createNotRequiredState(permission);\n }\n\n const adapter = adapters.get(permission);\n if (!adapter) {\n throw new Error(`No adapter registered for permission: ${permission}`);\n }\n\n return adapter.getStatus(permission);\n },\n\n async request(permission: Permission): Promise<PermissionState> {\n const metadata = getPermissionSupport(permission);\n if (metadata.support === 'unsupported' || metadata.support === 'notImplemented') {\n return createUnavailableSupportState(permission);\n }\n\n if (metadata.support === 'notRequired') {\n return createNotRequiredState(permission);\n }\n\n const adapter = adapters.get(permission);\n if (!adapter) {\n throw new Error(`No adapter registered for permission: ${permission}`);\n }\n\n return adapter.request(permission);\n },\n\n async openSettings(): Promise<void> {\n try {\n const linking = await import('expo-linking');\n\n await linking.openSettings();\n } catch (error) {\n throw new Error(OPEN_SETTINGS_ERROR_MESSAGE, {\n cause: error,\n });\n }\n },\n };\n}\n\nfunction getPermissionSupport(permission: Permission) {\n return Reflect.get(EXPO_PERMISSION_SUPPORT, permission);\n}\n\nfunction createUnavailableSupportState(permission: Permission): PermissionState {\n return createPermissionState({\n permission,\n status: 'unavailable',\n reason: `Permission ${permission} is not implemented or supported on Expo.`,\n });\n}\n\nfunction createNotRequiredState(permission: Permission): PermissionState {\n return createPermissionState({\n permission,\n status: 'granted',\n });\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface MockPermissionResponse {
|
|
2
|
+
readonly status: string;
|
|
3
|
+
readonly canAskAgain?: boolean;
|
|
4
|
+
readonly accessPrivileges?: 'all' | 'limited' | 'none';
|
|
5
|
+
readonly ios?: {
|
|
6
|
+
readonly status?: number;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
type CameraMockMode = 'topLevel' | 'cameraNamespace' | 'missingExports';
|
|
10
|
+
type AudioMockMode = 'topLevel' | 'audioModule' | 'missingExports';
|
|
11
|
+
export declare function permissionResponse(status: string, canAskAgain?: boolean): MockPermissionResponse;
|
|
12
|
+
export declare const cameraMock: {
|
|
13
|
+
mode: CameraMockMode;
|
|
14
|
+
getResponse: MockPermissionResponse;
|
|
15
|
+
requestResponse: MockPermissionResponse;
|
|
16
|
+
getError: string | undefined;
|
|
17
|
+
requestError: string | undefined;
|
|
18
|
+
};
|
|
19
|
+
export declare const audioMock: {
|
|
20
|
+
mode: AudioMockMode;
|
|
21
|
+
getResponse: MockPermissionResponse;
|
|
22
|
+
requestResponse: MockPermissionResponse;
|
|
23
|
+
getError: string | undefined;
|
|
24
|
+
requestError: string | undefined;
|
|
25
|
+
};
|
|
26
|
+
export declare const mediaLibraryMock: {
|
|
27
|
+
getResponse: MockPermissionResponse;
|
|
28
|
+
requestResponse: MockPermissionResponse;
|
|
29
|
+
getError: string | undefined;
|
|
30
|
+
requestError: string | undefined;
|
|
31
|
+
getCalls: boolean[];
|
|
32
|
+
requestCalls: boolean[];
|
|
33
|
+
};
|
|
34
|
+
export declare const locationMock: {
|
|
35
|
+
foregroundGetResponse: MockPermissionResponse;
|
|
36
|
+
foregroundRequestResponse: MockPermissionResponse;
|
|
37
|
+
backgroundGetResponse: MockPermissionResponse;
|
|
38
|
+
backgroundRequestResponse: MockPermissionResponse;
|
|
39
|
+
getCalls: string[];
|
|
40
|
+
requestCalls: string[];
|
|
41
|
+
};
|
|
42
|
+
export declare const notificationsMock: {
|
|
43
|
+
getResponse: MockPermissionResponse;
|
|
44
|
+
requestResponse: MockPermissionResponse;
|
|
45
|
+
getError: string | undefined;
|
|
46
|
+
requestError: string | undefined;
|
|
47
|
+
};
|
|
48
|
+
export declare const linkingMock: {
|
|
49
|
+
calls: number;
|
|
50
|
+
error: Error | undefined;
|
|
51
|
+
};
|
|
52
|
+
export declare function resetExpoMocks(): void;
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=client.testSupport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.testSupport.d.ts","sourceRoot":"","sources":["../../src/expo/client.testSupport.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,KAAK,cAAc,GAAG,UAAU,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AACxE,KAAK,aAAa,GAAG,UAAU,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAEnE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAO,GAAG,sBAAsB,CAE7F;AAED,eAAO,MAAM,UAAU;UACD,cAAc;;;cAGX,MAAM,GAAG,SAAS;kBACd,MAAM,GAAG,SAAS;CAC9C,CAAC;AACF,eAAO,MAAM,SAAS;UACA,aAAa;;;cAGV,MAAM,GAAG,SAAS;kBACd,MAAM,GAAG,SAAS;CAC9C,CAAC;AACF,eAAO,MAAM,gBAAgB;;;cAGJ,MAAM,GAAG,SAAS;kBACd,MAAM,GAAG,SAAS;cAC7B,OAAO,EAAE;kBACL,OAAO,EAAE;CAC9B,CAAC;AACF,eAAO,MAAM,YAAY;;;;;cAKP,MAAM,EAAE;kBACJ,MAAM,EAAE;CAC7B,CAAC;AACF,eAAO,MAAM,iBAAiB;;;cAGL,MAAM,GAAG,SAAS;kBACd,MAAM,GAAG,SAAS;CAC9C,CAAC;AACF,eAAO,MAAM,WAAW;;WAAmC,KAAK,GAAG,SAAS;CAAE,CAAC;AAsG/E,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { mock } from 'bun:test';
|
|
2
|
+
export function permissionResponse(status, canAskAgain = true) {
|
|
3
|
+
return { status, canAskAgain };
|
|
4
|
+
}
|
|
5
|
+
export const cameraMock = {
|
|
6
|
+
mode: 'topLevel',
|
|
7
|
+
getResponse: permissionResponse('granted'),
|
|
8
|
+
requestResponse: permissionResponse('granted'),
|
|
9
|
+
getError: undefined,
|
|
10
|
+
requestError: undefined,
|
|
11
|
+
};
|
|
12
|
+
export const audioMock = {
|
|
13
|
+
mode: 'topLevel',
|
|
14
|
+
getResponse: permissionResponse('granted'),
|
|
15
|
+
requestResponse: permissionResponse('granted'),
|
|
16
|
+
getError: undefined,
|
|
17
|
+
requestError: undefined,
|
|
18
|
+
};
|
|
19
|
+
export const mediaLibraryMock = {
|
|
20
|
+
getResponse: permissionResponse('granted'),
|
|
21
|
+
requestResponse: permissionResponse('granted'),
|
|
22
|
+
getError: undefined,
|
|
23
|
+
requestError: undefined,
|
|
24
|
+
getCalls: [],
|
|
25
|
+
requestCalls: [],
|
|
26
|
+
};
|
|
27
|
+
export const locationMock = {
|
|
28
|
+
foregroundGetResponse: permissionResponse('granted'),
|
|
29
|
+
foregroundRequestResponse: permissionResponse('granted'),
|
|
30
|
+
backgroundGetResponse: permissionResponse('granted'),
|
|
31
|
+
backgroundRequestResponse: permissionResponse('granted'),
|
|
32
|
+
getCalls: [],
|
|
33
|
+
requestCalls: [],
|
|
34
|
+
};
|
|
35
|
+
export const notificationsMock = {
|
|
36
|
+
getResponse: permissionResponse('granted'),
|
|
37
|
+
requestResponse: permissionResponse('granted'),
|
|
38
|
+
getError: undefined,
|
|
39
|
+
requestError: undefined,
|
|
40
|
+
};
|
|
41
|
+
export const linkingMock = { calls: 0, error: undefined };
|
|
42
|
+
void mock.module('expo-camera', () => ({
|
|
43
|
+
get getCameraPermissionsAsync() {
|
|
44
|
+
if (cameraMock.mode !== 'topLevel')
|
|
45
|
+
return undefined;
|
|
46
|
+
return () => {
|
|
47
|
+
if (cameraMock.getError !== undefined)
|
|
48
|
+
throw new Error(cameraMock.getError);
|
|
49
|
+
return Promise.resolve(cameraMock.getResponse);
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
get requestCameraPermissionsAsync() {
|
|
53
|
+
if (cameraMock.mode !== 'topLevel')
|
|
54
|
+
return undefined;
|
|
55
|
+
return () => {
|
|
56
|
+
if (cameraMock.requestError !== undefined)
|
|
57
|
+
throw new Error(cameraMock.requestError);
|
|
58
|
+
return Promise.resolve(cameraMock.requestResponse);
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
get Camera() {
|
|
62
|
+
if (cameraMock.mode !== 'cameraNamespace')
|
|
63
|
+
return undefined;
|
|
64
|
+
return {
|
|
65
|
+
getCameraPermissionsAsync: () => Promise.resolve(cameraMock.getResponse),
|
|
66
|
+
requestCameraPermissionsAsync: () => Promise.resolve(cameraMock.requestResponse),
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
void mock.module('expo-audio', () => ({
|
|
71
|
+
get getRecordingPermissionsAsync() {
|
|
72
|
+
if (audioMock.mode !== 'topLevel')
|
|
73
|
+
return undefined;
|
|
74
|
+
return () => {
|
|
75
|
+
if (audioMock.getError !== undefined)
|
|
76
|
+
throw new Error(audioMock.getError);
|
|
77
|
+
return Promise.resolve(audioMock.getResponse);
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
get requestRecordingPermissionsAsync() {
|
|
81
|
+
if (audioMock.mode !== 'topLevel')
|
|
82
|
+
return undefined;
|
|
83
|
+
return () => {
|
|
84
|
+
if (audioMock.requestError !== undefined)
|
|
85
|
+
throw new Error(audioMock.requestError);
|
|
86
|
+
return Promise.resolve(audioMock.requestResponse);
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
get AudioModule() {
|
|
90
|
+
if (audioMock.mode !== 'audioModule')
|
|
91
|
+
return undefined;
|
|
92
|
+
return {
|
|
93
|
+
getRecordingPermissionsAsync: () => Promise.resolve(audioMock.getResponse),
|
|
94
|
+
requestRecordingPermissionsAsync: () => Promise.resolve(audioMock.requestResponse),
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
}));
|
|
98
|
+
void mock.module('expo-media-library', () => ({
|
|
99
|
+
getPermissionsAsync: (writeOnly) => {
|
|
100
|
+
mediaLibraryMock.getCalls.push(writeOnly === true);
|
|
101
|
+
if (mediaLibraryMock.getError !== undefined)
|
|
102
|
+
throw new Error(mediaLibraryMock.getError);
|
|
103
|
+
return Promise.resolve(mediaLibraryMock.getResponse);
|
|
104
|
+
},
|
|
105
|
+
requestPermissionsAsync: (writeOnly) => {
|
|
106
|
+
mediaLibraryMock.requestCalls.push(writeOnly === true);
|
|
107
|
+
if (mediaLibraryMock.requestError !== undefined)
|
|
108
|
+
throw new Error(mediaLibraryMock.requestError);
|
|
109
|
+
return Promise.resolve(mediaLibraryMock.requestResponse);
|
|
110
|
+
},
|
|
111
|
+
}));
|
|
112
|
+
void mock.module('expo-location', () => ({
|
|
113
|
+
getForegroundPermissionsAsync: () => {
|
|
114
|
+
locationMock.getCalls.push('foreground');
|
|
115
|
+
return Promise.resolve(locationMock.foregroundGetResponse);
|
|
116
|
+
},
|
|
117
|
+
requestForegroundPermissionsAsync: () => {
|
|
118
|
+
locationMock.requestCalls.push('foreground');
|
|
119
|
+
return Promise.resolve(locationMock.foregroundRequestResponse);
|
|
120
|
+
},
|
|
121
|
+
getBackgroundPermissionsAsync: () => {
|
|
122
|
+
locationMock.getCalls.push('background');
|
|
123
|
+
return Promise.resolve(locationMock.backgroundGetResponse);
|
|
124
|
+
},
|
|
125
|
+
requestBackgroundPermissionsAsync: () => {
|
|
126
|
+
locationMock.requestCalls.push('background');
|
|
127
|
+
return Promise.resolve(locationMock.backgroundRequestResponse);
|
|
128
|
+
},
|
|
129
|
+
}));
|
|
130
|
+
void mock.module('expo-notifications', () => ({
|
|
131
|
+
getPermissionsAsync: () => {
|
|
132
|
+
if (notificationsMock.getError !== undefined)
|
|
133
|
+
throw new Error(notificationsMock.getError);
|
|
134
|
+
return Promise.resolve(notificationsMock.getResponse);
|
|
135
|
+
},
|
|
136
|
+
requestPermissionsAsync: () => {
|
|
137
|
+
if (notificationsMock.requestError !== undefined)
|
|
138
|
+
throw new Error(notificationsMock.requestError);
|
|
139
|
+
return Promise.resolve(notificationsMock.requestResponse);
|
|
140
|
+
},
|
|
141
|
+
}));
|
|
142
|
+
void mock.module('expo-linking', () => ({
|
|
143
|
+
openSettings: () => {
|
|
144
|
+
linkingMock.calls += 1;
|
|
145
|
+
if (linkingMock.error !== undefined)
|
|
146
|
+
throw linkingMock.error;
|
|
147
|
+
return Promise.resolve();
|
|
148
|
+
},
|
|
149
|
+
}));
|
|
150
|
+
export function resetExpoMocks() {
|
|
151
|
+
cameraMock.mode = 'topLevel';
|
|
152
|
+
cameraMock.getResponse = permissionResponse('granted');
|
|
153
|
+
cameraMock.requestResponse = permissionResponse('granted');
|
|
154
|
+
cameraMock.getError = undefined;
|
|
155
|
+
cameraMock.requestError = undefined;
|
|
156
|
+
audioMock.mode = 'topLevel';
|
|
157
|
+
audioMock.getResponse = permissionResponse('granted');
|
|
158
|
+
audioMock.requestResponse = permissionResponse('granted');
|
|
159
|
+
audioMock.getError = undefined;
|
|
160
|
+
audioMock.requestError = undefined;
|
|
161
|
+
resetCollectionMocks();
|
|
162
|
+
notificationsMock.getResponse = permissionResponse('granted');
|
|
163
|
+
notificationsMock.requestResponse = permissionResponse('granted');
|
|
164
|
+
notificationsMock.getError = undefined;
|
|
165
|
+
notificationsMock.requestError = undefined;
|
|
166
|
+
linkingMock.calls = 0;
|
|
167
|
+
linkingMock.error = undefined;
|
|
168
|
+
}
|
|
169
|
+
function resetCollectionMocks() {
|
|
170
|
+
mediaLibraryMock.getResponse = permissionResponse('granted');
|
|
171
|
+
mediaLibraryMock.requestResponse = permissionResponse('granted');
|
|
172
|
+
mediaLibraryMock.getError = undefined;
|
|
173
|
+
mediaLibraryMock.requestError = undefined;
|
|
174
|
+
mediaLibraryMock.getCalls.length = 0;
|
|
175
|
+
mediaLibraryMock.requestCalls.length = 0;
|
|
176
|
+
locationMock.foregroundGetResponse = permissionResponse('granted');
|
|
177
|
+
locationMock.foregroundRequestResponse = permissionResponse('granted');
|
|
178
|
+
locationMock.backgroundGetResponse = permissionResponse('granted');
|
|
179
|
+
locationMock.backgroundRequestResponse = permissionResponse('granted');
|
|
180
|
+
locationMock.getCalls.length = 0;
|
|
181
|
+
locationMock.requestCalls.length = 0;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=client.testSupport.js.map
|