@symbiote-native/engine 1.2.0 → 1.3.1

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 (110) 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/accessibility-props.d.ts +0 -11
  7. package/build/accessibility-props.js +30 -68
  8. package/build/alert/shared.d.ts +1 -1
  9. package/build/alert/shared.js +3 -7
  10. package/build/animated/graph.js +1 -1
  11. package/build/animated/leaf-lifecycle.js +2 -2
  12. package/build/app-state/index.d.ts +1 -1
  13. package/build/app-state/index.js +35 -27
  14. package/build/asset-source-resolver.d.ts +2 -0
  15. package/build/asset-source-resolver.js +13 -0
  16. package/build/back-handler/index.d.ts +7 -6
  17. package/build/back-handler/index.js +19 -16
  18. package/build/debug.js +8 -22
  19. package/build/dispatch.js +3 -9
  20. package/build/events/index.js +12 -3
  21. package/build/fabric-props.js +75 -180
  22. package/build/fabric.d.ts +2 -8
  23. package/build/fabric.js +27 -33
  24. package/build/host-access.d.ts +1 -128
  25. package/build/host-access.js +96 -205
  26. package/build/host-behavior.d.ts +1 -100
  27. package/build/host-behavior.js +125 -311
  28. package/build/image-loader.d.ts +4 -2
  29. package/build/image-loader.js +24 -48
  30. package/build/image-source-resolver.js +3 -7
  31. package/build/image-source-write.d.ts +1 -12
  32. package/build/image-source-write.js +28 -36
  33. package/build/imperative.d.ts +0 -28
  34. package/build/imperative.js +42 -92
  35. package/build/index.d.ts +3 -2
  36. package/build/index.js +30 -43
  37. package/build/invariant.d.ts +1 -0
  38. package/build/invariant.js +10 -0
  39. package/build/keyboard/index.js +11 -32
  40. package/build/linking/index.android.js +5 -3
  41. package/build/linking/shared.d.ts +1 -1
  42. package/build/linking/shared.js +16 -19
  43. package/build/mutation-buffer.d.ts +0 -177
  44. package/build/mutation-buffer.js +137 -308
  45. package/build/native-engine.d.ts +0 -102
  46. package/build/native-engine.js +38 -98
  47. package/build/native-events.d.ts +5 -0
  48. package/build/native-events.js +30 -18
  49. package/build/native-tree-host.d.ts +0 -21
  50. package/build/native-tree-host.js +14 -31
  51. package/build/node.d.ts +0 -212
  52. package/build/node.js +354 -774
  53. package/build/permissions-android/index.android.d.ts +59 -0
  54. package/build/permissions-android/index.android.js +47 -0
  55. package/build/permissions-android/index.d.ts +1 -115
  56. package/build/permissions-android/index.ios.d.ts +59 -0
  57. package/build/permissions-android/index.ios.js +31 -0
  58. package/build/permissions-android/index.js +3 -184
  59. package/build/permissions-android/shared.d.ts +63 -0
  60. package/build/permissions-android/shared.js +66 -0
  61. package/build/platform/index.android.js +3 -5
  62. package/build/platform/index.ios.js +3 -3
  63. package/build/platform/shared.d.ts +4 -0
  64. package/build/platform/shared.js +9 -0
  65. package/build/platform-color/index.android.d.ts +4 -0
  66. package/build/platform-color/index.android.js +7 -0
  67. package/build/platform-color/index.d.ts +2 -20
  68. package/build/platform-color/index.js +1 -45
  69. package/build/platform-color/shared.d.ts +21 -0
  70. package/build/platform-color/shared.js +41 -0
  71. package/build/post-commit.js +3 -8
  72. package/build/process-aspect-ratio.js +3 -7
  73. package/build/process-background-longhands.js +10 -19
  74. package/build/process-filter.js +11 -19
  75. package/build/process-font-variant.js +3 -7
  76. package/build/registry.d.ts +0 -33
  77. package/build/registry.js +22 -57
  78. package/build/report-error.js +4 -18
  79. package/build/settings/index.android.d.ts +6 -0
  80. package/build/settings/index.android.js +21 -0
  81. package/build/settings/index.d.ts +1 -8
  82. package/build/settings/index.ios.d.ts +8 -0
  83. package/build/settings/index.ios.js +122 -0
  84. package/build/settings/index.js +3 -122
  85. package/build/share/index.android.js +9 -32
  86. package/build/share/index.ios.js +14 -15
  87. package/build/share/shared.d.ts +4 -2
  88. package/build/share/shared.js +7 -10
  89. package/build/status-bar/index.android.js +1 -1
  90. package/build/status-bar/index.ios.js +4 -3
  91. package/build/structured-style.d.ts +0 -9
  92. package/build/structured-style.js +16 -31
  93. package/build/styles.js +3 -6
  94. package/build/surface.d.ts +0 -26
  95. package/build/surface.js +29 -76
  96. package/build/text-input-state.js +4 -8
  97. package/build/toast-android/index.android.d.ts +10 -0
  98. package/build/toast-android/index.android.js +108 -0
  99. package/build/toast-android/index.d.ts +1 -10
  100. package/build/toast-android/index.ios.d.ts +10 -0
  101. package/build/toast-android/index.ios.js +19 -0
  102. package/build/toast-android/index.js +3 -108
  103. package/build/touch-history.js +5 -11
  104. package/build/tree-host.d.ts +0 -270
  105. package/build/tree-host.js +63 -153
  106. package/build/view-config.js +17 -37
  107. package/cpp/SymbioteFabricProps.cpp +204 -120
  108. package/cpp/SymbioteFabricProps.h +5 -0
  109. package/cpp/SymbioteTree.cpp +20 -6
  110. package/package.json +2 -2
@@ -0,0 +1,122 @@
1
+ // Settings module: reads/writes the app's persisted defaults (iOS NSUserDefaults
2
+ // via the native SettingsManager). A JS-side snapshot seeded from the native module
3
+ // constants answers `get`; `set` writes through to native and updates the snapshot;
4
+ // `watchKeys`/`clearWatch` are a pure-JS subscription registry that fires when a
5
+ // watched key's value changes. Native re-broadcasts external edits through the
6
+ // device event `settingsUpdated`, which feeds the same change->fire path. Mirrors
7
+ // RN's Libraries/Settings/Settings.ios.js, iOS surface only.
8
+ import { installDeviceEventHub, NativeEventEmitter, } from '../native-events.js';
9
+ import { getNativeModule } from '../native-modules/index.js';
10
+ import { dlog } from '../debug.js';
11
+ import { isRecord } from '../type-guards.js';
12
+ // The iOS native module name RN registers this under. NOTE: this is the name the
13
+ // iOS JS wrapper resolves via `TurboModuleRegistry.getEnforcing('SettingsManager')`.
14
+ // The spec filename is `INativeSettingsManager`. A module name is only provable on a
15
+ // real host (a headless fake answers to any name); this iOS name is device-verify-pending.
16
+ const SETTINGS_MODULE = 'SettingsManager';
17
+ // The device event native emits when the app's defaults change out from under JS
18
+ // (e.g. a Settings.bundle edit). Its payload is a record of changed key->value.
19
+ const SETTINGS_UPDATED_EVENT = 'settingsUpdated';
20
+ // watchIds are handed out as the registry index, so the first watcher is 0. A
21
+ // cleared slot keeps its index (the array never shrinks) so later ids stay stable.
22
+ const FIRST_WATCH_ID = 0;
23
+ // Lazily resolved so importing this module has no native side effect. `null` when
24
+ // the module isn't linked (headless): the snapshot then starts empty and `set`
25
+ // updates only JS state + fires watchers.
26
+ let settingsModule;
27
+ function getModule() {
28
+ if (settingsModule === undefined) {
29
+ settingsModule = getNativeModule(SETTINGS_MODULE);
30
+ dlog(`Settings: module ${settingsModule ? 'resolved' : 'NOT resolved (null)'}`);
31
+ }
32
+ return settingsModule;
33
+ }
34
+ // The JS-side mirror of native defaults: seeded once from the module constants,
35
+ // kept in sync by `set` and by the `settingsUpdated` device event.
36
+ let snapshot;
37
+ function getSnapshot() {
38
+ if (snapshot === undefined) {
39
+ const module = getModule();
40
+ const constants = module?.getConstants().settings;
41
+ snapshot = isRecord(constants) ? { ...constants } : {};
42
+ dlog(`Settings: snapshot seeded with ${Object.keys(snapshot).length} key(s)`);
43
+ }
44
+ return snapshot;
45
+ }
46
+ const subscriptions = [];
47
+ // Fire every watcher whose key set covers `key`. Called only for keys that changed.
48
+ function fireWatchers(key) {
49
+ for (const sub of subscriptions) {
50
+ if (sub.callback !== null && sub.keys.includes(key))
51
+ sub.callback();
52
+ }
53
+ }
54
+ // Fold a batch of new values into the snapshot, firing watchers for each key whose
55
+ // value actually changed. Shared by `set` (JS-originated) and the device event
56
+ // (native-originated), matching RN's `_sendObservations`.
57
+ function applyChanges(values) {
58
+ const current = getSnapshot();
59
+ for (const key of Object.keys(values)) {
60
+ const next = values[key];
61
+ const didChange = current[key] !== next;
62
+ current[key] = next;
63
+ if (didChange)
64
+ fireWatchers(key);
65
+ }
66
+ }
67
+ let emitter;
68
+ // Subscribe to native's `settingsUpdated` so external edits flow into the snapshot.
69
+ // WHY lazy + module-gated: importing this file must have no native side effect, and
70
+ // with no native module there is nothing native to observe, so a headless run that
71
+ // never installs the device hub doesn't crash. Idempotent.
72
+ function subscribeToNative() {
73
+ if (emitter !== undefined)
74
+ return;
75
+ const module = getModule();
76
+ if (module === null)
77
+ return;
78
+ installDeviceEventHub();
79
+ emitter = new NativeEventEmitter(module);
80
+ emitter.addListener(SETTINGS_UPDATED_EVENT, payload => {
81
+ if (!isRecord(payload))
82
+ return;
83
+ applyChanges(payload);
84
+ });
85
+ }
86
+ class SettingsImpl {
87
+ // Read the current value for `key` from the JS snapshot. `undefined` if unset.
88
+ get(key) {
89
+ subscribeToNative();
90
+ return getSnapshot()[key];
91
+ }
92
+ // Persist `settings` to native, update the snapshot, and fire watchers for the
93
+ // keys that changed. Without a native module (headless) only JS state updates.
94
+ set(settings) {
95
+ subscribeToNative();
96
+ const module = getModule();
97
+ if (module === null) {
98
+ dlog('Settings.set -> no module (JS snapshot + watchers only)');
99
+ }
100
+ else {
101
+ module.setValues(settings);
102
+ }
103
+ applyChanges(settings);
104
+ }
105
+ // Register a watcher for one or more keys. Returns a numeric watchId (the registry
106
+ // index) to pass to `clearWatch`.
107
+ watchKeys(keys, callback) {
108
+ subscribeToNative();
109
+ const watched = typeof keys === 'string' ? [keys] : keys;
110
+ const watchId = subscriptions.length + FIRST_WATCH_ID;
111
+ subscriptions.push({ keys: watched, callback });
112
+ return watchId;
113
+ }
114
+ // Disarm the watcher with the given id. Its slot is emptied (kept, not removed) so
115
+ // every other watchId stays valid.
116
+ clearWatch(watchId) {
117
+ if (watchId >= FIRST_WATCH_ID && watchId < subscriptions.length) {
118
+ subscriptions[watchId] = { keys: [], callback: null };
119
+ }
120
+ }
121
+ }
122
+ export const Settings = new SettingsImpl();
@@ -1,122 +1,3 @@
1
- // Settings module: reads/writes the app's persisted defaults (iOS NSUserDefaults
2
- // via the native SettingsManager). A JS-side snapshot seeded from the native module
3
- // constants answers `get`; `set` writes through to native and updates the snapshot;
4
- // `watchKeys`/`clearWatch` are a pure-JS subscription registry that fires when a
5
- // watched key's value changes. Native re-broadcasts external edits through the
6
- // device event `settingsUpdated`, which feeds the same change->fire path. Mirrors
7
- // RN's Libraries/Settings/Settings.ios.js, iOS surface only.
8
- import { installDeviceEventHub, NativeEventEmitter, } from '../native-events.js';
9
- import { getNativeModule } from '../native-modules/index.js';
10
- import { dlog } from '../debug.js';
11
- import { isRecord } from '../type-guards.js';
12
- // The iOS native module name RN registers this under. NOTE: this is the name the
13
- // iOS JS wrapper resolves via `TurboModuleRegistry.getEnforcing('SettingsManager')`.
14
- // The spec filename is `INativeSettingsManager`. A module name is only provable on a
15
- // real host (a headless fake answers to any name); this iOS name is device-verify-pending.
16
- const SETTINGS_MODULE = 'SettingsManager';
17
- // The device event native emits when the app's defaults change out from under JS
18
- // (e.g. a Settings.bundle edit). Its payload is a record of changed key->value.
19
- const SETTINGS_UPDATED_EVENT = 'settingsUpdated';
20
- // watchIds are handed out as the registry index, so the first watcher is 0. A
21
- // cleared slot keeps its index (the array never shrinks) so later ids stay stable.
22
- const FIRST_WATCH_ID = 0;
23
- // Lazily resolved so importing this module has no native side effect. `null` when
24
- // the module isn't linked (headless): the snapshot then starts empty and `set`
25
- // updates only JS state + fires watchers.
26
- let settingsModule;
27
- function getModule() {
28
- if (settingsModule === undefined) {
29
- settingsModule = getNativeModule(SETTINGS_MODULE);
30
- dlog(`Settings: module ${settingsModule ? 'resolved' : 'NOT resolved (null)'}`);
31
- }
32
- return settingsModule;
33
- }
34
- // The JS-side mirror of native defaults: seeded once from the module constants,
35
- // kept in sync by `set` and by the `settingsUpdated` device event.
36
- let snapshot;
37
- function getSnapshot() {
38
- if (snapshot === undefined) {
39
- const module = getModule();
40
- const constants = module?.getConstants().settings;
41
- snapshot = isRecord(constants) ? { ...constants } : {};
42
- dlog(`Settings: snapshot seeded with ${Object.keys(snapshot).length} key(s)`);
43
- }
44
- return snapshot;
45
- }
46
- const subscriptions = [];
47
- // Fire every watcher whose key set covers `key`. Called only for keys that changed.
48
- function fireWatchers(key) {
49
- for (const sub of subscriptions) {
50
- if (sub.callback !== null && sub.keys.includes(key))
51
- sub.callback();
52
- }
53
- }
54
- // Fold a batch of new values into the snapshot, firing watchers for each key whose
55
- // value actually changed. Shared by `set` (JS-originated) and the device event
56
- // (native-originated), matching RN's `_sendObservations`.
57
- function applyChanges(values) {
58
- const current = getSnapshot();
59
- for (const key of Object.keys(values)) {
60
- const next = values[key];
61
- const didChange = current[key] !== next;
62
- current[key] = next;
63
- if (didChange)
64
- fireWatchers(key);
65
- }
66
- }
67
- let emitter;
68
- // Subscribe to native's `settingsUpdated` so external edits flow into the snapshot.
69
- // WHY lazy + module-gated: importing this file must have no native side effect, and
70
- // with no native module there is nothing native to observe, so a headless run that
71
- // never installs the device hub doesn't crash. Idempotent.
72
- function subscribeToNative() {
73
- if (emitter !== undefined)
74
- return;
75
- const module = getModule();
76
- if (module === null)
77
- return;
78
- installDeviceEventHub();
79
- emitter = new NativeEventEmitter(module);
80
- emitter.addListener(SETTINGS_UPDATED_EVENT, payload => {
81
- if (!isRecord(payload))
82
- return;
83
- applyChanges(payload);
84
- });
85
- }
86
- class SettingsImpl {
87
- // Read the current value for `key` from the JS snapshot. `undefined` if unset.
88
- get(key) {
89
- subscribeToNative();
90
- return getSnapshot()[key];
91
- }
92
- // Persist `settings` to native, update the snapshot, and fire watchers for the
93
- // keys that changed. Without a native module (headless) only JS state updates.
94
- set(settings) {
95
- subscribeToNative();
96
- const module = getModule();
97
- if (module === null) {
98
- dlog('Settings.set -> no module (JS snapshot + watchers only)');
99
- }
100
- else {
101
- module.setValues(settings);
102
- }
103
- applyChanges(settings);
104
- }
105
- // Register a watcher for one or more keys. Returns a numeric watchId (the registry
106
- // index) to pass to `clearWatch`.
107
- watchKeys(keys, callback) {
108
- subscribeToNative();
109
- const watched = typeof keys === 'string' ? [keys] : keys;
110
- const watchId = subscriptions.length + FIRST_WATCH_ID;
111
- subscriptions.push({ keys: watched, callback });
112
- return watchId;
113
- }
114
- // Disarm the watcher with the given id. Its slot is emptied (kept, not removed) so
115
- // every other watchId stays valid.
116
- clearWatch(watchId) {
117
- if (watchId >= FIRST_WATCH_ID && watchId < subscriptions.length) {
118
- subscriptions[watchId] = { keys: [], callback: null };
119
- }
120
- }
121
- }
122
- export const Settings = new SettingsImpl();
1
+ // Settings: base / headless build. Metro picks index.ios.ts / index.android.ts on a device; off
2
+ // those, the iOS build (the SettingsManager-backed one) is the default.
3
+ export * from './index.ios.js';
@@ -9,47 +9,24 @@
9
9
  // name, so it is only proven on a real Android host (a bridgeless resolution log there).
10
10
  import { dlog } from '../debug.js';
11
11
  import { getNativeModule } from '../native-modules/index.js';
12
- import { validateContent, shareActions, SHARED_ACTION, DISMISSED_ACTION, } from './shared.js';
12
+ import { assertShareArgs, invariant, shareActions } from './shared.js';
13
13
  const SHARE_MODULE = 'ShareModule';
14
- // ShareModule.share resolves an untyped value at the native boundary; narrow it before
15
- // reading `action` (no `as`).
16
- function isShareResult(value) {
17
- return (typeof value === 'object' &&
18
- value !== null &&
19
- 'action' in value &&
20
- typeof value.action === 'string');
21
- }
22
14
  export const Share = {
23
15
  ...shareActions,
24
- // Open the Android share dialog for `content`. Resolves with the user's action
25
- // (Android always resolves sharedAction); rejects on invalid content, an unexpected
26
- // native result, or a missing module (explicit reject, never a hung Promise).
16
+ // Share.js's Android branch: every check is a synchronous invariant, and the native result
17
+ // passes through untouched under `activityType: null`.
27
18
  share(content, options = {}) {
28
- const invalid = validateContent(content);
29
- if (invalid !== null) {
30
- dlog(`Share.share -> invalid content: ${invalid.message}`);
31
- return Promise.reject(invalid);
32
- }
19
+ assertShareArgs(content, options);
33
20
  dlog('Share.share (android)');
34
21
  const shareModule = getNativeModule(SHARE_MODULE);
35
- if (shareModule === null) {
36
- dlog(`Share: "${SHARE_MODULE}" unresolved`);
37
- return Promise.reject(new Error('Share: ShareModule native module unavailable'));
38
- }
22
+ invariant(shareModule !== null, 'ShareModule should be registered on Android.');
23
+ invariant(content.title == null || typeof content.title === 'string', 'Invalid title: title should be a string.');
39
24
  const newContent = {
40
25
  title: content.title,
41
26
  message: typeof content.message === 'string' ? content.message : undefined,
42
27
  };
43
- return shareModule.share(newContent, options.dialogTitle).then(result => {
44
- if (!isShareResult(result)) {
45
- dlog('Share.share -> android result missing action');
46
- throw new Error('Share: ShareModule returned an unexpected result');
47
- }
48
- dlog(`Share.share -> android action=${result.action}`);
49
- return {
50
- action: result.action === DISMISSED_ACTION ? DISMISSED_ACTION : SHARED_ACTION,
51
- activityType: null,
52
- };
53
- });
28
+ return shareModule
29
+ .share(newContent, options.dialogTitle)
30
+ .then(result => ({ activityType: null, ...result }));
54
31
  },
55
32
  };
@@ -7,39 +7,38 @@
7
7
  import { dlog } from '../debug.js';
8
8
  import { getNativeModule } from '../native-modules/index.js';
9
9
  import { ACTION_SHEET_MANAGER, } from '../action-sheet-ios/index.js';
10
- import { validateContent, shareActions, SHARED_ACTION, DISMISSED_ACTION, } from './shared.js';
10
+ import { isProcessableColor, processColor } from '../platform-color';
11
+ import { assertShareArgs, invariant, shareActions, SHARED_ACTION, DISMISSED_ACTION, } from './shared.js';
11
12
  export const Share = {
12
13
  ...shareActions,
13
14
  // Open the iOS share sheet for `content`. Resolves with the user's action
14
15
  // (sharedAction / dismissedAction); rejects on invalid content, a native failure,
15
16
  // or a missing module (explicit reject rather than a Promise that never settles).
16
17
  share(content, options = {}) {
17
- const invalid = validateContent(content);
18
- if (invalid !== null) {
19
- dlog(`Share.share -> invalid content: ${invalid.message}`);
20
- return Promise.reject(invalid);
21
- }
18
+ assertShareArgs(content, options);
22
19
  dlog('Share.share (ios)');
23
- const manager = getNativeModule(ACTION_SHEET_MANAGER);
24
- if (manager === null) {
25
- dlog(`Share: "${ACTION_SHEET_MANAGER}" unresolved`);
26
- return Promise.reject(new Error('Share: ActionSheetManager native module unavailable'));
27
- }
20
+ // Share.js's iOS branch: both invariants run INSIDE the executor, so they reject.
28
21
  return new Promise((resolve, reject) => {
22
+ const tintColor = isProcessableColor(options.tintColor)
23
+ ? processColor(options.tintColor)
24
+ : undefined;
25
+ invariant(tintColor == null || typeof tintColor === 'number', 'Unexpected color given for options.tintColor');
26
+ const manager = getNativeModule(ACTION_SHEET_MANAGER);
27
+ invariant(manager !== null, 'NativeActionSheetManager is not registered on iOS, but it should be.');
29
28
  manager.showShareActionSheetWithOptions({
30
29
  message: typeof content.message === 'string' ? content.message : undefined,
31
30
  url: typeof content.url === 'string' ? content.url : undefined,
32
31
  subject: options.subject,
33
- tintColor: options.tintColor,
34
- anchor: options.anchor,
32
+ tintColor: typeof tintColor === 'number' ? tintColor : undefined,
33
+ anchor: typeof options.anchor === 'number' ? options.anchor : undefined,
35
34
  excludedActivityTypes: options.excludedActivityTypes,
36
35
  }, error => {
37
36
  dlog('Share.share -> failure');
38
- reject(new Error(error.message));
37
+ reject(error);
39
38
  }, (completed, activityType) => {
40
39
  dlog(`Share.share -> success completed=${completed}`);
41
40
  resolve(completed
42
- ? { action: SHARED_ACTION, activityType: activityType ?? null }
41
+ ? { action: SHARED_ACTION, activityType }
43
42
  : { action: DISMISSED_ACTION, activityType: null });
44
43
  });
45
44
  });
@@ -1,3 +1,5 @@
1
+ import { invariant } from '../invariant';
2
+ export { invariant };
1
3
  export type IShareContent = {
2
4
  title?: string;
3
5
  url: string;
@@ -17,7 +19,7 @@ export interface IShareOptions {
17
19
  export declare const SHARED_ACTION = "sharedAction";
18
20
  export declare const DISMISSED_ACTION = "dismissedAction";
19
21
  export interface IShareAction {
20
- action: typeof SHARED_ACTION | typeof DISMISSED_ACTION;
22
+ action: string;
21
23
  activityType?: string | null;
22
24
  }
23
25
  export interface IShareStatic {
@@ -29,4 +31,4 @@ export declare const shareActions: {
29
31
  sharedAction: typeof SHARED_ACTION;
30
32
  dismissedAction: typeof DISMISSED_ACTION;
31
33
  };
32
- export declare function validateContent(content: IShareContent): Error | null;
34
+ export declare function assertShareArgs(content: IShareContent, options: IShareOptions): void;
@@ -7,6 +7,8 @@
7
7
  // Metro selects the platform file on a real host (share.android.ts > share.ts); the base
8
8
  // share.ts re-exports the iOS build for web/headless. There is no runtime `Platform.OS`
9
9
  // read: the filename is the selector.
10
+ import { invariant } from '../invariant.js';
11
+ export { invariant };
10
12
  // RN's Share action constants (RN Share.js ~173/179). These back the documented
11
13
  // `result.action === Share.dismissedAction` pattern. True statically-known literals,
12
14
  // so CONSTANT_CASE; the public fields (Share.sharedAction / Share.dismissedAction) are
@@ -19,14 +21,9 @@ export const shareActions = {
19
21
  sharedAction: SHARED_ACTION,
20
22
  dismissedAction: DISMISSED_ACTION,
21
23
  };
22
- // RN's invariant: return an Error (caller rejects rather than throws) so a bad call
23
- // can't unmount the tree on device. Shared because the rule is identical per platform.
24
- export function validateContent(content) {
25
- if (typeof content !== 'object' || content === null) {
26
- return new Error('Content to share must be a valid object');
27
- }
28
- if (typeof content.url !== 'string' && typeof content.message !== 'string') {
29
- return new Error('At least one of URL or message is required');
30
- }
31
- return null;
24
+ // Share.js's three shared invariants, checked before any promise exists on both platforms.
25
+ export function assertShareArgs(content, options) {
26
+ invariant(typeof content === 'object' && content !== null, 'Content to share must be a valid object');
27
+ invariant(typeof content.url === 'string' || typeof content.message === 'string', 'At least one of URL or message is required');
28
+ invariant(typeof options === 'object' && options !== null, 'Options must be a valid object');
32
29
  }
@@ -11,7 +11,7 @@
11
11
  // on device: show/hide + light/dark text survive the relayout without blanking.
12
12
  import { getNativeModule } from '../native-modules/index.js';
13
13
  import { dlog } from '../debug.js';
14
- import { processColor } from '../platform-color/index.js';
14
+ import { processColor } from '../platform-color';
15
15
  import { STATUS_BAR_MANAGER, } from './shared.js';
16
16
  // processColor returns `unknown` (its result is platform-dependent); narrow to the
17
17
  // number Fabric/native expects, like RN's invariant before setColor. A non-number
@@ -17,9 +17,10 @@ import { STATUS_BAR_MANAGER, STATIC_HIDE_TRANSITION, hideTransition, } from './s
17
17
  // __turboModuleProxy is installed. Non-enforcing: a declarative StatusBar must NOT crash the
18
18
  // whole render if the module can't resolve.
19
19
  //
20
- // Simplification vs RN: RN maintains a prop-merge stack so nested StatusBars compose
21
- // (deepest/last wins); we direct-apply a single component's props, which is correct for one
22
- // StatusBar and a fine first cut.
20
+ // TODO(rn-parity): each StatusBar applies its props directly. RN keeps a stack instead:
21
+ // `_propsStack` over `_defaultProps`, unmount restores the previous entry, one batched native
22
+ // update, public push/pop/replaceStackEntry, `showHideTransition`, and static setters that warn
23
+ // on the wrong platform. Port it for both platforms and all 5 adapters.
23
24
  export function applyStatusBarProps(props) {
24
25
  const { barStyle, hidden, animated = false, networkActivityIndicatorVisible, } = props;
25
26
  const manager = getNativeModule(STATUS_BAR_MANAGER);
@@ -1,10 +1 @@
1
- /**
2
- * A style value with its structured keys resolved — the same value by identity when there was
3
- * nothing to resolve, which is nearly always.
4
- *
5
- * Accepts what a style slot can hold: one object, or a (nested) array of them. An array is not
6
- * memoized — `pushClassStyle` mints a fresh one on every publish, so a cache keyed on it could
7
- * never hit; its ENTRIES carry the memo instead, and the original array comes back untouched when
8
- * none of them moved.
9
- */
10
1
  export declare function resolveStructuredStyle(style: unknown): unknown;
@@ -1,22 +1,12 @@
1
1
  // The ten style keys RN parses in JS before native, and the one place they are resolved.
2
- //
3
- // WHY THEY RUN AT WRITE TIME AND NOT AT PAYLOAD-BUILD TIME. On a device the payload is built by
4
- // `core/engine/cpp/SymbioteFabricProps.cpp`, which does not carry these — they are pure JS. So a
5
- // `boxShadow: '0 2px 4px #000'` resolved only inside `fabric-props.ts` is resolved only headless,
6
- // and the device gets the raw CSS string. Fabric's C++ parses a style string ONLY under
7
- // `enableNativeCSSParsing()`, which defaults to FALSE, so the declaration is dropped in silence:
8
- // no warning, no wrong value, just a gradient or a shadow that is not there. Resolving on the way
9
- // IN puts the structured value in `node.props` itself, which is the one thing both payload builders
10
- // read.
11
- //
12
- // This is the same move `configPayloadFold` makes for a third-party view's own processors, one
13
- // layer down: anything the C++ half cannot do has to happen before the C++ half sees the value.
14
- //
15
- // IDENTITY IS PART OF THE CONTRACT. A style object that needs nothing comes back BY IDENTITY, and
16
- // so does an array whose every entry did. The host's `OP_SET_PROP` skips a same-identity write, and
17
- // `pushClassStyle` compares what it is about to publish against what it published last — hand
18
- // either of them a fresh object per write and an unchanged style becomes a write and a dirty node
19
- // on every render.
2
+ // They run at write time, not payload-build time: SymbioteFabricProps.cpp doesn't carry these
3
+ // processors, so resolving only in fabric-props.ts leaves the device its raw CSS string, silently
4
+ // dropped (C++ parses a style string only under enableNativeCSSParsing, default false).
5
+ // Same move `configPayloadFold` makes for a third-party view's own processors: anything the C++
6
+ // half can't do has to happen before the C++ half sees the value.
7
+ // Identity is part of the contract: an object/array needing no resolution comes back by identity,
8
+ // since OP_SET_PROP skips a same-identity write and pushClassStyle diffs by reference — a fresh
9
+ // object per call would turn an unchanged style into a write and a dirty node every render.
20
10
  import { processAspectRatio } from './process-aspect-ratio.js';
21
11
  import { processBackgroundImage } from './process-background-image/index.js';
22
12
  import { processBackgroundPosition, processBackgroundRepeat, processBackgroundSize, } from './process-background-longhands.js';
@@ -80,10 +70,9 @@ function asFontVariantInput(value) {
80
70
  return value.filter(isString);
81
71
  return '';
82
72
  }
83
- // transform accepts a CSS string (processTransform parses it) or an array of single-key
84
- // transform records (the hot animated / sticky-header path). A non-string non-array value is NOT
85
- // dropped: it may already be processed, so it passes through verbatim rather than being coerced to
86
- // [] (which would erase a valid transform).
73
+ // transform accepts a CSS string or an array of single-key records (the hot animated /
74
+ // sticky-header path). A non-string non-array value passes through verbatim — it may already be
75
+ // processed, so coercing it to [] would erase a valid transform.
87
76
  function processTransformValue(value) {
88
77
  if (typeof value === 'string')
89
78
  return processTransform(value);
@@ -154,15 +143,11 @@ function resolveRecord(style) {
154
143
  resolvedStyles.set(style, answer);
155
144
  return answer;
156
145
  }
157
- /**
158
- * A style value with its structured keys resolved — the same value by identity when there was
159
- * nothing to resolve, which is nearly always.
160
- *
161
- * Accepts what a style slot can hold: one object, or a (nested) array of them. An array is not
162
- * memoized — `pushClassStyle` mints a fresh one on every publish, so a cache keyed on it could
163
- * never hit; its ENTRIES carry the memo instead, and the original array comes back untouched when
164
- * none of them moved.
165
- */
146
+ // A style value with its structured keys resolved — same value by identity when nothing needed
147
+ // resolving, which is nearly always.
148
+ // Accepts one object or a (nested) array of them. An array itself isn't memoized (pushClassStyle
149
+ // mints a fresh one per publish, so a cache keyed on it would never hit); its entries carry the
150
+ // memo instead.
166
151
  export function resolveStructuredStyle(style) {
167
152
  if (Array.isArray(style)) {
168
153
  let out;
package/build/styles.js CHANGED
@@ -1,7 +1,4 @@
1
- // The typed style surface (ViewStyle / TextStyle and friends). Maps onto Yoga layout
2
- // props and RN's view/text props, which Fabric's C++ reads off the props payload.
3
- // A correctly-typed subset of RN's StyleSheet surface (see
4
- // react-native/Libraries/StyleSheet/StyleSheetTypes): the load-bearing layout / box /
5
- // shadow / transform / text props, not the full surface. Agnostic types, so they
6
- // live in the engine next to the style processors; every adapter re-exports them.
1
+ // The typed style surface (ViewStyle/TextStyle and friends). Maps onto Yoga layout props and RN's
2
+ // view/text props, which Fabric's C++ reads off the props payload. A correctly-typed subset of
3
+ // RN's StyleSheet surface — agnostic types, so every adapter re-exports them from the engine.
7
4
  export {};
@@ -6,38 +6,12 @@ export declare class SymbioteSurface {
6
6
  private readonly node;
7
7
  private commitScheduled;
8
8
  constructor(rootTag: IRootTag);
9
- /**
10
- * Every OTHER live surface, so one commit names every root — see `commitSurfaceOps`.
11
- *
12
- * Static because it reads a sibling instance's private handle, which only a member of this class
13
- * may do. One surface — the universal case — allocates nothing.
14
- *
15
- * `self` may already be OUT of the registry: a teardown unregisters and then commits, and that
16
- * commit is the one carrying the removals. So the fast path cannot be a size check — with one
17
- * live surface left, `size === 1` means either "only me" or "only the other one", and taking the
18
- * shortcut on the second reading is how a surface loses the very batch that empties it.
19
- */
20
9
  private static others;
21
- /**
22
- * The top-level nodes, asked of the host.
23
- *
24
- * A getter rather than an array this class maintains: a second copy of a child list is the thing
25
- * this design removes, and `nextSiblingOf` (host-access.ts) needs the same answer the host gives
26
- * for a parented node.
27
- */
28
10
  get children(): readonly ISymbioteNode[];
29
11
  appendChild(child: ISymbioteNode): void;
30
12
  insertBefore(child: ISymbioteNode, beforeChild: ISymbioteNode): void;
31
13
  removeChild(child: ISymbioteNode): void;
32
14
  clear(): void;
33
- /**
34
- * Release every host behavior still standing under this surface, at unmount.
35
- *
36
- * The sweep above cannot answer this: it only sees nodes a `removeChild` NOMINATED, and an
37
- * unmount removes nothing — the adapter drops the whole surface. Without it every node keeps its
38
- * `afterCommit` registration and its timers, and a restarted surface's commits drain the dead
39
- * one's hooks forever.
40
- */
41
15
  teardown(): void;
42
16
  commit(): void;
43
17
  requestCommit(): void;