@symbiote-native/engine 1.2.0 → 1.3.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.
Files changed (71) hide show
  1. package/android/CMakeLists.txt +31 -1
  2. package/android/build.gradle +4 -1
  3. package/build/accessibility-info/index.android.js +21 -17
  4. package/build/accessibility-info/index.ios.js +2 -0
  5. package/build/accessibility-info/shared.d.ts +1 -1
  6. package/build/alert/shared.d.ts +1 -1
  7. package/build/alert/shared.js +3 -7
  8. package/build/app-state/index.d.ts +1 -1
  9. package/build/app-state/index.js +35 -27
  10. package/build/back-handler/index.d.ts +7 -2
  11. package/build/back-handler/index.js +19 -10
  12. package/build/events/index.js +10 -1
  13. package/build/fabric-props.js +1 -1
  14. package/build/fabric.d.ts +7 -0
  15. package/build/fabric.js +14 -0
  16. package/build/host-behavior.d.ts +1 -0
  17. package/build/image-loader.d.ts +4 -2
  18. package/build/image-loader.js +14 -25
  19. package/build/image-source-write.d.ts +1 -1
  20. package/build/image-source-write.js +14 -2
  21. package/build/index.d.ts +2 -2
  22. package/build/index.js +6 -6
  23. package/build/invariant.d.ts +1 -0
  24. package/build/invariant.js +10 -0
  25. package/build/keyboard/index.js +11 -32
  26. package/build/linking/index.android.js +5 -3
  27. package/build/linking/shared.d.ts +1 -1
  28. package/build/linking/shared.js +16 -19
  29. package/build/native-events.d.ts +5 -0
  30. package/build/native-events.js +21 -0
  31. package/build/node.js +18 -2
  32. package/build/permissions-android/index.android.d.ts +59 -0
  33. package/build/permissions-android/index.android.js +47 -0
  34. package/build/permissions-android/index.d.ts +1 -115
  35. package/build/permissions-android/index.ios.d.ts +59 -0
  36. package/build/permissions-android/index.ios.js +31 -0
  37. package/build/permissions-android/index.js +3 -184
  38. package/build/permissions-android/shared.d.ts +63 -0
  39. package/build/permissions-android/shared.js +66 -0
  40. package/build/platform/index.android.js +3 -5
  41. package/build/platform/index.ios.js +3 -3
  42. package/build/platform/shared.d.ts +4 -0
  43. package/build/platform/shared.js +9 -0
  44. package/build/platform-color/index.android.d.ts +4 -0
  45. package/build/platform-color/index.android.js +7 -0
  46. package/build/platform-color/index.d.ts +2 -20
  47. package/build/platform-color/index.js +1 -45
  48. package/build/platform-color/shared.d.ts +21 -0
  49. package/build/platform-color/shared.js +41 -0
  50. package/build/settings/index.android.d.ts +6 -0
  51. package/build/settings/index.android.js +21 -0
  52. package/build/settings/index.d.ts +1 -8
  53. package/build/settings/index.ios.d.ts +8 -0
  54. package/build/settings/index.ios.js +122 -0
  55. package/build/settings/index.js +3 -122
  56. package/build/share/index.android.js +9 -32
  57. package/build/share/index.ios.js +14 -15
  58. package/build/share/shared.d.ts +4 -2
  59. package/build/share/shared.js +7 -10
  60. package/build/status-bar/index.android.js +1 -1
  61. package/build/status-bar/index.ios.js +4 -3
  62. package/build/toast-android/index.android.d.ts +10 -0
  63. package/build/toast-android/index.android.js +108 -0
  64. package/build/toast-android/index.d.ts +1 -10
  65. package/build/toast-android/index.ios.d.ts +10 -0
  66. package/build/toast-android/index.ios.js +19 -0
  67. package/build/toast-android/index.js +3 -108
  68. package/cpp/SymbioteFabricProps.cpp +204 -120
  69. package/cpp/SymbioteFabricProps.h +5 -0
  70. package/cpp/SymbioteTree.cpp +20 -6
  71. package/package.json +2 -2
@@ -0,0 +1,31 @@
1
+ // PermissionsAndroid off Android: RN's PermissionsAndroid.js `Platform.OS !== 'android'` branches.
2
+ // Each call warns that the module is Android-only (RN's own user-facing warning) and resolves a
3
+ // fixed answer; nothing reaches native.
4
+ import { ANDROID_ONLY_WARNING, CHECK_PERMISSION_DEPRECATED, PERMISSIONS, REQUEST_PERMISSION_DEPRECATED, RESULTS, } from './shared.js';
5
+ export { PERMISSIONS, RESULTS, } from './shared.js';
6
+ export const PermissionsAndroid = {
7
+ PERMISSIONS,
8
+ RESULTS,
9
+ checkPermission(_permission) {
10
+ console.warn(CHECK_PERMISSION_DEPRECATED);
11
+ console.warn(ANDROID_ONLY_WARNING);
12
+ return Promise.resolve(false);
13
+ },
14
+ check(_permission) {
15
+ console.warn(ANDROID_ONLY_WARNING);
16
+ return Promise.resolve(false);
17
+ },
18
+ requestPermission(_permission, _rationale) {
19
+ console.warn(REQUEST_PERMISSION_DEPRECATED);
20
+ console.warn(ANDROID_ONLY_WARNING);
21
+ return Promise.resolve(false);
22
+ },
23
+ request(_permission, _rationale) {
24
+ console.warn(ANDROID_ONLY_WARNING);
25
+ return Promise.resolve(RESULTS.DENIED);
26
+ },
27
+ requestMultiple(_permissions) {
28
+ console.warn(ANDROID_ONLY_WARNING);
29
+ return Promise.resolve({});
30
+ },
31
+ };
@@ -1,184 +1,3 @@
1
- // PermissionsAndroid module: JS wrapper over Android's runtime-permissions
2
- // model. Mirrors RN's Libraries/PermissionsAndroid/PermissionsAndroid.js,
3
- // exposing the modern API: check / request / requestMultiple /
4
- // shouldShowRequestPermissionRationale, plus the frozen PERMISSIONS and RESULTS
5
- // constant maps. Imperative JS->native calls with no Fabric view of their own,
6
- // like Vibration / Keyboard.
7
- //
8
- // symbiote is iOS-first and this module is Android-only, so it MUST degrade
9
- // gracefully when the native module is absent (on iOS, or headless). RN throws
10
- // "works only for Android"; we instead resolve a safe default + dlog so a
11
- // cross-platform smoke never hard-crashes the process. Never reject/throw
12
- // synchronously on a missing module.
13
- //
14
- // The native contract is confirmed from RN's TurboModule spec at
15
- // specs_DEPRECATED/modules/INativePermissionsAndroid.js (`'PermissionsAndroid'`):
16
- // checkPermission(permission): Promise<boolean>
17
- // requestPermission(permission): Promise<string>
18
- // shouldShowRequestPermissionRationale(permission): Promise<boolean>
19
- // requestMultiplePermissions(permissions): Promise<{[permission]: string}>
20
- import { getNativeModule } from '../native-modules/index.js';
21
- import { dlog } from '../debug.js';
22
- import { isBoolean } from '../type-guards.js';
23
- // The native module name RN registers this TurboModule under
24
- // (`TurboModuleRegistry.get<Spec>('PermissionsAndroid')`). NOTE: a module name is
25
- // only provable on a real host (a headless fake answers to any name); this name
26
- // is device-verify-pending.
27
- const PERMISSIONS_ANDROID_MODULE = 'PermissionsAndroid';
28
- // The runtime-permission result strings Android can return. Modeled as a frozen
29
- // `as const` map: the generated-style constant-map exception to "no magic
30
- // strings".
31
- export const RESULTS = Object.freeze({
32
- GRANTED: 'granted',
33
- DENIED: 'denied',
34
- NEVER_ASK_AGAIN: 'never_ask_again',
35
- });
36
- // The full Android permission-string map, copied verbatim from RN's source.
37
- // Frozen `as const` constant map (the same string-map exception as RESULTS).
38
- export const PERMISSIONS = Object.freeze({
39
- READ_CALENDAR: 'android.permission.READ_CALENDAR',
40
- WRITE_CALENDAR: 'android.permission.WRITE_CALENDAR',
41
- CAMERA: 'android.permission.CAMERA',
42
- READ_CONTACTS: 'android.permission.READ_CONTACTS',
43
- WRITE_CONTACTS: 'android.permission.WRITE_CONTACTS',
44
- GET_ACCOUNTS: 'android.permission.GET_ACCOUNTS',
45
- ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION',
46
- ACCESS_COARSE_LOCATION: 'android.permission.ACCESS_COARSE_LOCATION',
47
- ACCESS_BACKGROUND_LOCATION: 'android.permission.ACCESS_BACKGROUND_LOCATION',
48
- RECORD_AUDIO: 'android.permission.RECORD_AUDIO',
49
- READ_PHONE_STATE: 'android.permission.READ_PHONE_STATE',
50
- CALL_PHONE: 'android.permission.CALL_PHONE',
51
- READ_CALL_LOG: 'android.permission.READ_CALL_LOG',
52
- WRITE_CALL_LOG: 'android.permission.WRITE_CALL_LOG',
53
- ADD_VOICEMAIL: 'com.android.voicemail.permission.ADD_VOICEMAIL',
54
- READ_VOICEMAIL: 'com.android.voicemail.permission.READ_VOICEMAIL',
55
- WRITE_VOICEMAIL: 'com.android.voicemail.permission.WRITE_VOICEMAIL',
56
- USE_SIP: 'android.permission.USE_SIP',
57
- PROCESS_OUTGOING_CALLS: 'android.permission.PROCESS_OUTGOING_CALLS',
58
- BODY_SENSORS: 'android.permission.BODY_SENSORS',
59
- BODY_SENSORS_BACKGROUND: 'android.permission.BODY_SENSORS_BACKGROUND',
60
- SEND_SMS: 'android.permission.SEND_SMS',
61
- RECEIVE_SMS: 'android.permission.RECEIVE_SMS',
62
- READ_SMS: 'android.permission.READ_SMS',
63
- RECEIVE_WAP_PUSH: 'android.permission.RECEIVE_WAP_PUSH',
64
- RECEIVE_MMS: 'android.permission.RECEIVE_MMS',
65
- READ_EXTERNAL_STORAGE: 'android.permission.READ_EXTERNAL_STORAGE',
66
- READ_MEDIA_IMAGES: 'android.permission.READ_MEDIA_IMAGES',
67
- READ_MEDIA_VIDEO: 'android.permission.READ_MEDIA_VIDEO',
68
- READ_MEDIA_AUDIO: 'android.permission.READ_MEDIA_AUDIO',
69
- READ_MEDIA_VISUAL_USER_SELECTED: 'android.permission.READ_MEDIA_VISUAL_USER_SELECTED',
70
- WRITE_EXTERNAL_STORAGE: 'android.permission.WRITE_EXTERNAL_STORAGE',
71
- BLUETOOTH_CONNECT: 'android.permission.BLUETOOTH_CONNECT',
72
- BLUETOOTH_SCAN: 'android.permission.BLUETOOTH_SCAN',
73
- BLUETOOTH_ADVERTISE: 'android.permission.BLUETOOTH_ADVERTISE',
74
- ACCESS_MEDIA_LOCATION: 'android.permission.ACCESS_MEDIA_LOCATION',
75
- ACCEPT_HANDOVER: 'android.permission.ACCEPT_HANDOVER',
76
- ACTIVITY_RECOGNITION: 'android.permission.ACTIVITY_RECOGNITION',
77
- ANSWER_PHONE_CALLS: 'android.permission.ANSWER_PHONE_CALLS',
78
- READ_PHONE_NUMBERS: 'android.permission.READ_PHONE_NUMBERS',
79
- UWB_RANGING: 'android.permission.UWB_RANGING',
80
- POST_NOTIFICATIONS: 'android.permission.POST_NOTIFICATIONS',
81
- NEARBY_WIFI_DEVICES: 'android.permission.NEARBY_WIFI_DEVICES',
82
- });
83
- // Narrow the native string result into a known RESULTS value. Any unrecognized
84
- // string is passed through as a IPermissionStatus only when it matches; otherwise
85
- // we fall back to DENIED: a runtime guard at the trust boundary, not an `as`.
86
- function toPermissionStatus(value) {
87
- if (value === RESULTS.GRANTED ||
88
- value === RESULTS.DENIED ||
89
- value === RESULTS.NEVER_ASK_AGAIN) {
90
- return value;
91
- }
92
- return RESULTS.DENIED;
93
- }
94
- // Narrow the native requestMultiplePermissions() return into a per-permission
95
- // status map. Each value is run through toPermissionStatus; non-object returns
96
- // yield an empty map.
97
- function toStatusMap(value) {
98
- const result = {};
99
- if (typeof value !== 'object' || value === null)
100
- return result;
101
- for (const key of Object.keys(value)) {
102
- result[key] = toPermissionStatus(Reflect.get(value, key));
103
- }
104
- return result;
105
- }
106
- // Resolved FRESH on every call, deliberately NOT memoized at module load. The original
107
- // self-contained module resolved once at import, but moving it into the engine made it a
108
- // shared singleton; a cached null would survive a module being linked later (or the headless
109
- // smoke flipping a fake module on after its first import). getNativeModule is a cheap proxy
110
- // lookup, so per-call resolution costs nothing and stays correct. Returns null on iOS / headless.
111
- function getModule() {
112
- const module = getNativeModule(PERMISSIONS_ANDROID_MODULE);
113
- dlog(`PermissionsAndroid: module ${module ? 'resolved' : 'NOT resolved (null)'}`);
114
- return module;
115
- }
116
- export const PermissionsAndroid = {
117
- PERMISSIONS,
118
- RESULTS,
119
- // Resolve whether a permission has already been granted. Without a native
120
- // module (iOS / headless) resolve false. Never throw.
121
- async check(permission) {
122
- const permissionsModule = getModule();
123
- if (permissionsModule === null) {
124
- dlog('PermissionsAndroid.check -> module unavailable, resolving false');
125
- return false;
126
- }
127
- dlog(`PermissionsAndroid.check -> ${permission}`);
128
- const granted = await permissionsModule.checkPermission(permission);
129
- return isBoolean(granted) ? granted : false;
130
- },
131
- // Prompt the user for a permission, resolving a RESULTS status. If a rationale
132
- // is supplied and DialogManagerAndroid is present, show it first; otherwise
133
- // proceed straight to the native request (dlog the skip). Without the
134
- // PermissionsAndroid module resolve RESULTS.DENIED. Never throw.
135
- async request(permission, rationale) {
136
- const permissionsModule = getModule();
137
- if (permissionsModule === null) {
138
- dlog('PermissionsAndroid.request -> module unavailable, resolving DENIED');
139
- return RESULTS.DENIED;
140
- }
141
- dlog(`PermissionsAndroid.request -> ${permission}`);
142
- if (rationale !== undefined) {
143
- const shouldShow = await permissionsModule.shouldShowRequestPermissionRationale(permission);
144
- const dialogModule = getNativeModule('DialogManagerAndroid');
145
- if (isBoolean(shouldShow) && shouldShow && dialogModule !== null) {
146
- return new Promise((resolve, reject) => {
147
- dialogModule.showAlert(rationale, () => reject(new Error('Error showing rationale')), () => {
148
- permissionsModule
149
- .requestPermission(permission)
150
- .then(status => resolve(toPermissionStatus(status)))
151
- .catch(reject);
152
- });
153
- });
154
- }
155
- dlog('PermissionsAndroid.request -> rationale skipped (DialogManagerAndroid unavailable)');
156
- }
157
- const status = await permissionsModule.requestPermission(permission);
158
- return toPermissionStatus(status);
159
- },
160
- // Prompt for several permissions at once, resolving a per-permission status
161
- // map. Without a native module resolve an empty map. Never throw.
162
- async requestMultiple(permissions) {
163
- const permissionsModule = getModule();
164
- if (permissionsModule === null) {
165
- dlog('PermissionsAndroid.requestMultiple -> module unavailable, resolving {}');
166
- return {};
167
- }
168
- dlog(`PermissionsAndroid.requestMultiple -> ${permissions.join(', ')}`);
169
- const statuses = await permissionsModule.requestMultiplePermissions(permissions);
170
- return toStatusMap(statuses);
171
- },
172
- // Whether the OS recommends showing a rationale before re-requesting. Without
173
- // a native module resolve false. Never throw.
174
- async shouldShowRequestPermissionRationale(permission) {
175
- const permissionsModule = getModule();
176
- if (permissionsModule === null) {
177
- dlog('PermissionsAndroid.shouldShowRequestPermissionRationale -> module unavailable, resolving false');
178
- return false;
179
- }
180
- dlog(`PermissionsAndroid.shouldShowRequestPermissionRationale -> ${permission}`);
181
- const shouldShow = await permissionsModule.shouldShowRequestPermissionRationale(permission);
182
- return isBoolean(shouldShow) ? shouldShow : false;
183
- },
184
- };
1
+ // PermissionsAndroid: base / headless build. Metro picks index.android.ts / index.ios.ts on a
2
+ // device; off those, RN's non-Android branch is the default.
3
+ export * from './index.ios.js';
@@ -0,0 +1,63 @@
1
+ export declare const PERMISSIONS_ANDROID_MODULE = "PermissionsAndroid";
2
+ export declare const ANDROID_ONLY_WARNING = "\"PermissionsAndroid\" module works only for Android platform.";
3
+ export declare const CHECK_PERMISSION_DEPRECATED = "\"PermissionsAndroid.checkPermission\" is deprecated. Use \"PermissionsAndroid.check\" instead";
4
+ export declare const REQUEST_PERMISSION_DEPRECATED = "\"PermissionsAndroid.requestPermission\" is deprecated. Use \"PermissionsAndroid.request\" instead";
5
+ export declare const RESULTS: Readonly<{
6
+ readonly GRANTED: "granted";
7
+ readonly DENIED: "denied";
8
+ readonly NEVER_ASK_AGAIN: "never_ask_again";
9
+ }>;
10
+ export type IPermissionStatus = (typeof RESULTS)[keyof typeof RESULTS];
11
+ export declare const PERMISSIONS: Readonly<{
12
+ readonly READ_CALENDAR: "android.permission.READ_CALENDAR";
13
+ readonly WRITE_CALENDAR: "android.permission.WRITE_CALENDAR";
14
+ readonly CAMERA: "android.permission.CAMERA";
15
+ readonly READ_CONTACTS: "android.permission.READ_CONTACTS";
16
+ readonly WRITE_CONTACTS: "android.permission.WRITE_CONTACTS";
17
+ readonly GET_ACCOUNTS: "android.permission.GET_ACCOUNTS";
18
+ readonly ACCESS_FINE_LOCATION: "android.permission.ACCESS_FINE_LOCATION";
19
+ readonly ACCESS_COARSE_LOCATION: "android.permission.ACCESS_COARSE_LOCATION";
20
+ readonly ACCESS_BACKGROUND_LOCATION: "android.permission.ACCESS_BACKGROUND_LOCATION";
21
+ readonly RECORD_AUDIO: "android.permission.RECORD_AUDIO";
22
+ readonly READ_PHONE_STATE: "android.permission.READ_PHONE_STATE";
23
+ readonly CALL_PHONE: "android.permission.CALL_PHONE";
24
+ readonly READ_CALL_LOG: "android.permission.READ_CALL_LOG";
25
+ readonly WRITE_CALL_LOG: "android.permission.WRITE_CALL_LOG";
26
+ readonly ADD_VOICEMAIL: "com.android.voicemail.permission.ADD_VOICEMAIL";
27
+ readonly READ_VOICEMAIL: "com.android.voicemail.permission.READ_VOICEMAIL";
28
+ readonly WRITE_VOICEMAIL: "com.android.voicemail.permission.WRITE_VOICEMAIL";
29
+ readonly USE_SIP: "android.permission.USE_SIP";
30
+ readonly PROCESS_OUTGOING_CALLS: "android.permission.PROCESS_OUTGOING_CALLS";
31
+ readonly BODY_SENSORS: "android.permission.BODY_SENSORS";
32
+ readonly BODY_SENSORS_BACKGROUND: "android.permission.BODY_SENSORS_BACKGROUND";
33
+ readonly SEND_SMS: "android.permission.SEND_SMS";
34
+ readonly RECEIVE_SMS: "android.permission.RECEIVE_SMS";
35
+ readonly READ_SMS: "android.permission.READ_SMS";
36
+ readonly RECEIVE_WAP_PUSH: "android.permission.RECEIVE_WAP_PUSH";
37
+ readonly RECEIVE_MMS: "android.permission.RECEIVE_MMS";
38
+ readonly READ_EXTERNAL_STORAGE: "android.permission.READ_EXTERNAL_STORAGE";
39
+ readonly READ_MEDIA_IMAGES: "android.permission.READ_MEDIA_IMAGES";
40
+ readonly READ_MEDIA_VIDEO: "android.permission.READ_MEDIA_VIDEO";
41
+ readonly READ_MEDIA_AUDIO: "android.permission.READ_MEDIA_AUDIO";
42
+ readonly READ_MEDIA_VISUAL_USER_SELECTED: "android.permission.READ_MEDIA_VISUAL_USER_SELECTED";
43
+ readonly WRITE_EXTERNAL_STORAGE: "android.permission.WRITE_EXTERNAL_STORAGE";
44
+ readonly BLUETOOTH_CONNECT: "android.permission.BLUETOOTH_CONNECT";
45
+ readonly BLUETOOTH_SCAN: "android.permission.BLUETOOTH_SCAN";
46
+ readonly BLUETOOTH_ADVERTISE: "android.permission.BLUETOOTH_ADVERTISE";
47
+ readonly ACCESS_MEDIA_LOCATION: "android.permission.ACCESS_MEDIA_LOCATION";
48
+ readonly ACCEPT_HANDOVER: "android.permission.ACCEPT_HANDOVER";
49
+ readonly ACTIVITY_RECOGNITION: "android.permission.ACTIVITY_RECOGNITION";
50
+ readonly ANSWER_PHONE_CALLS: "android.permission.ANSWER_PHONE_CALLS";
51
+ readonly READ_PHONE_NUMBERS: "android.permission.READ_PHONE_NUMBERS";
52
+ readonly UWB_RANGING: "android.permission.UWB_RANGING";
53
+ readonly POST_NOTIFICATIONS: "android.permission.POST_NOTIFICATIONS";
54
+ readonly NEARBY_WIFI_DEVICES: "android.permission.NEARBY_WIFI_DEVICES";
55
+ }>;
56
+ export type IPermission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];
57
+ export type IRationale = {
58
+ title: string;
59
+ message: string;
60
+ buttonPositive?: string;
61
+ buttonNegative?: string;
62
+ buttonNeutral?: string;
63
+ };
@@ -0,0 +1,66 @@
1
+ // PermissionsAndroid, platform-neutral half: the frozen PERMISSIONS / RESULTS maps and the public
2
+ // types, copied verbatim from RN's Libraries/PermissionsAndroid/PermissionsAndroid.js. The behavior
3
+ // is per platform, as in RN: index.android.ts talks to the native module, index.ios.ts is RN's
4
+ // `Platform.OS !== 'android'` branch.
5
+ //
6
+ // The native contract, from RN's TurboModule spec (`'PermissionsAndroid'`):
7
+ // checkPermission(permission): Promise<boolean>
8
+ // requestPermission(permission): Promise<string>
9
+ // shouldShowRequestPermissionRationale(permission): Promise<boolean>
10
+ // requestMultiplePermissions(permissions): Promise<{[permission]: string}>
11
+ export const PERMISSIONS_ANDROID_MODULE = 'PermissionsAndroid';
12
+ export const ANDROID_ONLY_WARNING = '"PermissionsAndroid" module works only for Android platform.';
13
+ export const CHECK_PERMISSION_DEPRECATED = '"PermissionsAndroid.checkPermission" is deprecated. Use "PermissionsAndroid.check" instead';
14
+ export const REQUEST_PERMISSION_DEPRECATED = '"PermissionsAndroid.requestPermission" is deprecated. Use "PermissionsAndroid.request" instead';
15
+ // The runtime-permission result strings Android can return.
16
+ export const RESULTS = Object.freeze({
17
+ GRANTED: 'granted',
18
+ DENIED: 'denied',
19
+ NEVER_ASK_AGAIN: 'never_ask_again',
20
+ });
21
+ // The full Android permission-string map, copied verbatim from RN's source.
22
+ export const PERMISSIONS = Object.freeze({
23
+ READ_CALENDAR: 'android.permission.READ_CALENDAR',
24
+ WRITE_CALENDAR: 'android.permission.WRITE_CALENDAR',
25
+ CAMERA: 'android.permission.CAMERA',
26
+ READ_CONTACTS: 'android.permission.READ_CONTACTS',
27
+ WRITE_CONTACTS: 'android.permission.WRITE_CONTACTS',
28
+ GET_ACCOUNTS: 'android.permission.GET_ACCOUNTS',
29
+ ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION',
30
+ ACCESS_COARSE_LOCATION: 'android.permission.ACCESS_COARSE_LOCATION',
31
+ ACCESS_BACKGROUND_LOCATION: 'android.permission.ACCESS_BACKGROUND_LOCATION',
32
+ RECORD_AUDIO: 'android.permission.RECORD_AUDIO',
33
+ READ_PHONE_STATE: 'android.permission.READ_PHONE_STATE',
34
+ CALL_PHONE: 'android.permission.CALL_PHONE',
35
+ READ_CALL_LOG: 'android.permission.READ_CALL_LOG',
36
+ WRITE_CALL_LOG: 'android.permission.WRITE_CALL_LOG',
37
+ ADD_VOICEMAIL: 'com.android.voicemail.permission.ADD_VOICEMAIL',
38
+ READ_VOICEMAIL: 'com.android.voicemail.permission.READ_VOICEMAIL',
39
+ WRITE_VOICEMAIL: 'com.android.voicemail.permission.WRITE_VOICEMAIL',
40
+ USE_SIP: 'android.permission.USE_SIP',
41
+ PROCESS_OUTGOING_CALLS: 'android.permission.PROCESS_OUTGOING_CALLS',
42
+ BODY_SENSORS: 'android.permission.BODY_SENSORS',
43
+ BODY_SENSORS_BACKGROUND: 'android.permission.BODY_SENSORS_BACKGROUND',
44
+ SEND_SMS: 'android.permission.SEND_SMS',
45
+ RECEIVE_SMS: 'android.permission.RECEIVE_SMS',
46
+ READ_SMS: 'android.permission.READ_SMS',
47
+ RECEIVE_WAP_PUSH: 'android.permission.RECEIVE_WAP_PUSH',
48
+ RECEIVE_MMS: 'android.permission.RECEIVE_MMS',
49
+ READ_EXTERNAL_STORAGE: 'android.permission.READ_EXTERNAL_STORAGE',
50
+ READ_MEDIA_IMAGES: 'android.permission.READ_MEDIA_IMAGES',
51
+ READ_MEDIA_VIDEO: 'android.permission.READ_MEDIA_VIDEO',
52
+ READ_MEDIA_AUDIO: 'android.permission.READ_MEDIA_AUDIO',
53
+ READ_MEDIA_VISUAL_USER_SELECTED: 'android.permission.READ_MEDIA_VISUAL_USER_SELECTED',
54
+ WRITE_EXTERNAL_STORAGE: 'android.permission.WRITE_EXTERNAL_STORAGE',
55
+ BLUETOOTH_CONNECT: 'android.permission.BLUETOOTH_CONNECT',
56
+ BLUETOOTH_SCAN: 'android.permission.BLUETOOTH_SCAN',
57
+ BLUETOOTH_ADVERTISE: 'android.permission.BLUETOOTH_ADVERTISE',
58
+ ACCESS_MEDIA_LOCATION: 'android.permission.ACCESS_MEDIA_LOCATION',
59
+ ACCEPT_HANDOVER: 'android.permission.ACCEPT_HANDOVER',
60
+ ACTIVITY_RECOGNITION: 'android.permission.ACTIVITY_RECOGNITION',
61
+ ANSWER_PHONE_CALLS: 'android.permission.ANSWER_PHONE_CALLS',
62
+ READ_PHONE_NUMBERS: 'android.permission.READ_PHONE_NUMBERS',
63
+ UWB_RANGING: 'android.permission.UWB_RANGING',
64
+ POST_NOTIFICATIONS: 'android.permission.POST_NOTIFICATIONS',
65
+ NEARBY_WIFI_DEVICES: 'android.permission.NEARBY_WIFI_DEVICES',
66
+ });
@@ -3,7 +3,7 @@
3
3
  // from the PlatformConstants native module (RN spec NativePlatformConstantsAndroid.js,
4
4
  // a different payload from iOS). Metro picks this on an Android host. The module name is
5
5
  // the same 'PlatformConstants'; only the shape differs.
6
- import { createConstantsResolver, } from './shared.js';
6
+ import { createConstantsResolver, gatedIsTesting, } from './shared.js';
7
7
  // The filename already selected this host: 'android' is a literal, not a probe.
8
8
  const OS_ANDROID = 'android';
9
9
  // RN reads isTV off the uiMode constant.
@@ -37,14 +37,12 @@ export const Platform = {
37
37
  get isVision() {
38
38
  return false;
39
39
  },
40
- // RN gates this behind __DEV__; shared has no __DEV__ flag, so we read the native
41
- // flag directly: a release build's native module reports false anyway.
42
40
  get isTesting() {
43
- return resolveConstants()?.isTesting ?? false;
41
+ return gatedIsTesting(resolveConstants());
44
42
  },
45
43
  get isDisableAnimations() {
46
44
  const constants = resolveConstants();
47
- return constants?.isDisableAnimations ?? constants?.isTesting ?? false;
45
+ return constants?.isDisableAnimations ?? gatedIsTesting(constants);
48
46
  },
49
47
  get isMacCatalyst() {
50
48
  return false;
@@ -3,7 +3,7 @@
3
3
  // (RN spec NativePlatformConstantsIOS.js), read lazily and cached via the shared
4
4
  // resolver. Metro picks this on an iOS host; it is also the base re-export target
5
5
  // (platform.ts) so tsc / tsx / web (no Metro) land here too.
6
- import { createConstantsResolver, UNKNOWN_VERSION, } from './shared.js';
6
+ import { createConstantsResolver, gatedIsTesting, UNKNOWN_VERSION, } from './shared.js';
7
7
  // interfaceIdiom values RN compares against for the device-class getters.
8
8
  const IDIOM_PAD = 'pad';
9
9
  const IDIOM_TV = 'tv';
@@ -39,13 +39,13 @@ export const Platform = {
39
39
  return idiomEquals(IDIOM_VISION);
40
40
  },
41
41
  get isTesting() {
42
- return resolveConstants()?.isTesting ?? false;
42
+ return gatedIsTesting(resolveConstants());
43
43
  },
44
44
  // RN: isDisableAnimations ?? isTesting. The native flag wins; absent, it tracks
45
45
  // isTesting (test runs disable animations by default).
46
46
  get isDisableAnimations() {
47
47
  const constants = resolveConstants();
48
- return constants?.isDisableAnimations ?? constants?.isTesting ?? false;
48
+ return constants?.isDisableAnimations ?? gatedIsTesting(constants);
49
49
  },
50
50
  get isMacCatalyst() {
51
51
  return resolveConstants()?.isMacCatalyst ?? false;
@@ -23,3 +23,7 @@ export interface IPlatformStatic<TConstants> {
23
23
  select<T>(spec: IPlatformSelectSpec<T>): T | undefined;
24
24
  }
25
25
  export declare function createConstantsResolver<TConstants>(guard: (value: unknown) => value is TConstants): () => TConstants | undefined;
26
+ export declare function isDevBuild(): boolean;
27
+ export declare function gatedIsTesting(constants: {
28
+ isTesting?: boolean;
29
+ } | undefined): boolean;
@@ -38,3 +38,12 @@ export function createConstantsResolver(guard) {
38
38
  return cached;
39
39
  };
40
40
  }
41
+ // Metro's `__DEV__` global: `true` in a dev bundle, `false` in release. Read off globalThis so a
42
+ // host that never defines it (headless tooling) counts as release, like any unset flag.
43
+ export function isDevBuild() {
44
+ return Reflect.get(globalThis, '__DEV__') === true;
45
+ }
46
+ // RN's `isTesting`: the native flag in a dev build only (`if (__DEV__) … return false`).
47
+ export function gatedIsTesting(constants) {
48
+ return isDevBuild() && (constants?.isTesting ?? false);
49
+ }
@@ -0,0 +1,4 @@
1
+ import type { IDynamicColorIOSTuple, IOpaqueColorValue } from './shared';
2
+ export * from './shared';
3
+ export declare function PlatformColor(...names: string[]): IOpaqueColorValue;
4
+ export declare function DynamicColorIOS(_tuple: IDynamicColorIOSTuple): IOpaqueColorValue;
@@ -0,0 +1,7 @@
1
+ export * from './shared.js';
2
+ export function PlatformColor(...names) {
3
+ return { resource_paths: names };
4
+ }
5
+ export function DynamicColorIOS(_tuple) {
6
+ throw new Error('DynamicColorIOS is not available on this platform.');
7
+ }
@@ -1,22 +1,4 @@
1
- export interface IDynamicColorIOSTuple {
2
- light: IColorValue;
3
- dark: IColorValue;
4
- highContrastLight?: IColorValue;
5
- highContrastDark?: IColorValue;
6
- }
7
- export interface IOpaqueColorValue {
8
- readonly semantic?: readonly string[];
9
- readonly dynamic?: {
10
- readonly light: IColorValue;
11
- readonly dark: IColorValue;
12
- readonly highContrastLight?: IColorValue;
13
- readonly highContrastDark?: IColorValue;
14
- };
15
- }
16
- export type IColorValue = string | number | IOpaqueColorValue;
1
+ import type { IDynamicColorIOSTuple, IOpaqueColorValue } from './shared';
2
+ export * from './shared';
17
3
  export declare function PlatformColor(...names: string[]): IOpaqueColorValue;
18
4
  export declare function DynamicColorIOS(tuple: IDynamicColorIOSTuple): IOpaqueColorValue;
19
- export declare function isOpaqueColorValue(value: unknown): value is IOpaqueColorValue;
20
- export declare function setColorProcessor(process: (value: IColorValue) => unknown): void;
21
- export declare function processColor(color: IColorValue): unknown;
22
- export declare function isProcessableColor(value: unknown): value is IColorValue;
@@ -1,10 +1,4 @@
1
- // PlatformColor / DynamicColorIOS: opaque color values the native side resolves
2
- // (iOS UIColor: semantic system colors and appearance-aware light/dark tuples).
3
- // These are pure object constructors with no native dependency, so they live in
4
- // shared and every adapter re-exports them. The opaque shape ({ semantic } /
5
- // { dynamic }) is exactly what RN's processColor and the iOS RCTConvert UIColor
6
- // path read, so our commit-time color seam routes these objects through the
7
- // injected processor just like CSS-string colors (see commit.ts processValue).
1
+ export * from './shared.js';
8
2
  export function PlatformColor(...names) {
9
3
  return { semantic: names };
10
4
  }
@@ -18,41 +12,3 @@ export function DynamicColorIOS(tuple) {
18
12
  },
19
13
  };
20
14
  }
21
- // True for the opaque objects above: the color seam uses this to route them
22
- // through the platform processor alongside CSS-string colors.
23
- export function isOpaqueColorValue(value) {
24
- return (typeof value === 'object' &&
25
- value !== null &&
26
- ('semantic' in value || 'dynamic' in value));
27
- }
28
- // Color props must reach Fabric as platform ints, not CSS strings. Fabric's C++ color
29
- // parser silently drops strings. The actual conversion (processColor) is RN-platform-specific,
30
- // so it is injected here rather than imported, keeping shared free of a react-native dependency
31
- // (and the headless harness working). This module is the sole owner of "run a value through the
32
- // platform color processor" - every color-touching consumer (commit's fabricProps,
33
- // process-box-shadow, process-filter, process-background-image, StatusBar android) imports
34
- // processColor/isProcessableColor from here, never from commit.ts.
35
- let colorProcessor = value => value;
36
- export function setColorProcessor(process) {
37
- colorProcessor = process;
38
- }
39
- // Public mirror of RN's processColor: run a color through the injected platform processor (the
40
- // canary wires RN's own). Off a real host it resolves CSS strings and opaque PlatformColor
41
- // objects to the platform ints Fabric expects; headless (no processor wired) it is the identity,
42
- // so smokes see the input unchanged.
43
- export function processColor(color) {
44
- return colorProcessor(color);
45
- }
46
- // A color-keyed value the platform processor must convert before Fabric: a CSS string, a numeric
47
- // literal, or an opaque PlatformColor / DynamicColorIOS object. `undefined` is left untouched.
48
- //
49
- // A number used to be excluded here on the reasoning that it was "already a resolved platform
50
- // int". Nothing in the pipeline produces one: the CSS parser emits hex STRINGS, and fabricProps
51
- // builds a fresh payload object instead of writing the resolved value back into node.props, so a
52
- // number reaching this guard is always the author's own literal. Excluding it committed
53
- // `color: 0xff0000ff` (opaque red, rrggbbaa) unrotated, and native reads that as aarrggbb: blue.
54
- export function isProcessableColor(value) {
55
- return (typeof value === 'string' ||
56
- typeof value === 'number' ||
57
- isOpaqueColorValue(value));
58
- }
@@ -0,0 +1,21 @@
1
+ export interface IDynamicColorIOSTuple {
2
+ light: IColorValue;
3
+ dark: IColorValue;
4
+ highContrastLight?: IColorValue;
5
+ highContrastDark?: IColorValue;
6
+ }
7
+ export interface IOpaqueColorValue {
8
+ readonly semantic?: readonly string[];
9
+ readonly resource_paths?: readonly string[];
10
+ readonly dynamic?: {
11
+ readonly light: IColorValue;
12
+ readonly dark: IColorValue;
13
+ readonly highContrastLight?: IColorValue;
14
+ readonly highContrastDark?: IColorValue;
15
+ };
16
+ }
17
+ export type IColorValue = string | number | IOpaqueColorValue;
18
+ export declare function isOpaqueColorValue(value: unknown): value is IOpaqueColorValue;
19
+ export declare function setColorProcessor(process: (value: IColorValue) => unknown): void;
20
+ export declare function processColor(color: IColorValue): unknown;
21
+ export declare function isProcessableColor(value: unknown): value is IColorValue;
@@ -0,0 +1,41 @@
1
+ // PlatformColor / DynamicColorIOS: opaque colors native resolves. Constructors are per platform
2
+ // because the shapes differ: iOS reads { semantic } / { dynamic } (PlatformColorParser.mm),
3
+ // Android only { resource_paths } (ColorPropConverter). The C++ seam forwards the object as-is.
4
+ // True for the opaque objects above: the color seam uses this to route them
5
+ // through the platform processor alongside CSS-string colors.
6
+ export function isOpaqueColorValue(value) {
7
+ return (typeof value === 'object' &&
8
+ value !== null &&
9
+ ('semantic' in value || 'dynamic' in value || 'resource_paths' in value));
10
+ }
11
+ // Color props must reach Fabric as platform ints, not CSS strings. Fabric's C++ color
12
+ // parser silently drops strings. The actual conversion (processColor) is RN-platform-specific,
13
+ // so it is injected here rather than imported, keeping shared free of a react-native dependency
14
+ // (and the headless harness working). This module is the sole owner of "run a value through the
15
+ // platform color processor" - every color-touching consumer (commit's fabricProps,
16
+ // process-box-shadow, process-filter, process-background-image, StatusBar android) imports
17
+ // processColor/isProcessableColor from here, never from commit.ts.
18
+ let colorProcessor = value => value;
19
+ export function setColorProcessor(process) {
20
+ colorProcessor = process;
21
+ }
22
+ // Public mirror of RN's processColor: run a color through the injected platform processor (the
23
+ // canary wires RN's own). Off a real host it resolves CSS strings and opaque PlatformColor
24
+ // objects to the platform ints Fabric expects; headless (no processor wired) it is the identity,
25
+ // so smokes see the input unchanged.
26
+ export function processColor(color) {
27
+ return colorProcessor(color);
28
+ }
29
+ // A color-keyed value the platform processor must convert before Fabric: a CSS string, a numeric
30
+ // literal, or an opaque PlatformColor / DynamicColorIOS object. `undefined` is left untouched.
31
+ //
32
+ // A number used to be excluded here on the reasoning that it was "already a resolved platform
33
+ // int". Nothing in the pipeline produces one: the CSS parser emits hex STRINGS, and fabricProps
34
+ // builds a fresh payload object instead of writing the resolved value back into node.props, so a
35
+ // number reaching this guard is always the author's own literal. Excluding it committed
36
+ // `color: 0xff0000ff` (opaque red, rrggbbaa) unrotated, and native reads that as aarrggbb: blue.
37
+ export function isProcessableColor(value) {
38
+ return (typeof value === 'string' ||
39
+ typeof value === 'number' ||
40
+ isOpaqueColorValue(value));
41
+ }
@@ -0,0 +1,6 @@
1
+ export declare const Settings: {
2
+ get(_key: string): unknown;
3
+ set(_settings: Record<string, unknown>): void;
4
+ watchKeys(_keys: string | string[], _callback: () => void): number;
5
+ clearWatch(_watchId: number): void;
6
+ };
@@ -0,0 +1,21 @@
1
+ // Settings, Android build: RN's SettingsFallback (Settings.js resolves to it off iOS). Android has
2
+ // no SettingsManager, so every call warns and does nothing — no JS snapshot, no watchers. The
3
+ // warning is RN's own user-facing one, not a diagnostic log.
4
+ const UNSUPPORTED = 'Settings is not yet supported on this platform.';
5
+ const NO_WATCH_ID = -1;
6
+ export const Settings = {
7
+ get(_key) {
8
+ console.warn(UNSUPPORTED);
9
+ return null;
10
+ },
11
+ set(_settings) {
12
+ console.warn(UNSUPPORTED);
13
+ },
14
+ watchKeys(_keys, _callback) {
15
+ console.warn(UNSUPPORTED);
16
+ return NO_WATCH_ID;
17
+ },
18
+ clearWatch(_watchId) {
19
+ console.warn(UNSUPPORTED);
20
+ },
21
+ };
@@ -1,8 +1 @@
1
- declare class SettingsImpl {
2
- get(key: string): unknown;
3
- set(settings: Record<string, unknown>): void;
4
- watchKeys(keys: string | string[], callback: () => void): number;
5
- clearWatch(watchId: number): void;
6
- }
7
- export declare const Settings: SettingsImpl;
8
- export {};
1
+ export * from './index.ios';
@@ -0,0 +1,8 @@
1
+ declare class SettingsImpl {
2
+ get(key: string): unknown;
3
+ set(settings: Record<string, unknown>): void;
4
+ watchKeys(keys: string | string[], callback: () => void): number;
5
+ clearWatch(watchId: number): void;
6
+ }
7
+ export declare const Settings: SettingsImpl;
8
+ export {};