@symbiote-native/engine 1.1.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 (74) 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-engine.d.ts +1 -0
  30. package/build/native-events.d.ts +5 -0
  31. package/build/native-events.js +21 -0
  32. package/build/node.js +34 -2
  33. package/build/permissions-android/index.android.d.ts +59 -0
  34. package/build/permissions-android/index.android.js +47 -0
  35. package/build/permissions-android/index.d.ts +1 -115
  36. package/build/permissions-android/index.ios.d.ts +59 -0
  37. package/build/permissions-android/index.ios.js +31 -0
  38. package/build/permissions-android/index.js +3 -184
  39. package/build/permissions-android/shared.d.ts +63 -0
  40. package/build/permissions-android/shared.js +66 -0
  41. package/build/platform/index.android.js +3 -5
  42. package/build/platform/index.ios.js +3 -3
  43. package/build/platform/shared.d.ts +4 -0
  44. package/build/platform/shared.js +9 -0
  45. package/build/platform-color/index.android.d.ts +4 -0
  46. package/build/platform-color/index.android.js +7 -0
  47. package/build/platform-color/index.d.ts +2 -20
  48. package/build/platform-color/index.js +1 -45
  49. package/build/platform-color/shared.d.ts +21 -0
  50. package/build/platform-color/shared.js +41 -0
  51. package/build/settings/index.android.d.ts +6 -0
  52. package/build/settings/index.android.js +21 -0
  53. package/build/settings/index.d.ts +1 -8
  54. package/build/settings/index.ios.d.ts +8 -0
  55. package/build/settings/index.ios.js +122 -0
  56. package/build/settings/index.js +3 -122
  57. package/build/share/index.android.js +9 -32
  58. package/build/share/index.ios.js +14 -15
  59. package/build/share/shared.d.ts +4 -2
  60. package/build/share/shared.js +7 -10
  61. package/build/status-bar/index.android.js +1 -1
  62. package/build/status-bar/index.ios.js +4 -3
  63. package/build/toast-android/index.android.d.ts +10 -0
  64. package/build/toast-android/index.android.js +108 -0
  65. package/build/toast-android/index.d.ts +1 -10
  66. package/build/toast-android/index.ios.d.ts +10 -0
  67. package/build/toast-android/index.ios.js +19 -0
  68. package/build/toast-android/index.js +3 -108
  69. package/build/tree-host.d.ts +47 -0
  70. package/build/tree-host.js +32 -2
  71. package/cpp/SymbioteFabricProps.cpp +218 -117
  72. package/cpp/SymbioteFabricProps.h +5 -0
  73. package/cpp/SymbioteTree.cpp +44 -6
  74. package/package.json +2 -2
@@ -1,108 +1,3 @@
1
- // ToastAndroid module: pops a brief Android system toast from JS. Mirrors RN's
2
- // Libraries/Components/ToastAndroid/ToastAndroid.android.js: `show` takes a message
3
- // and a duration; `showWithGravity` adds a layout gravity; `showWithGravityAndOffset`
4
- // adds an x/y pixel offset. No Fabric view, pure JS->native.
5
- //
6
- // symbiote is iOS-first and this is an Android-only module. RN's iOS fallback
7
- // (ToastAndroid.ios.js) throws "not supported"; we prefer a no-op + dlog so a
8
- // cross-platform smoke never crashes when the native module is absent.
9
- //
10
- // The native contract is confirmed from RN's TurboModule spec at
11
- // specs_DEPRECATED/modules/INativeToastAndroid.js:
12
- // getConstants(): { SHORT, LONG, TOP, BOTTOM, CENTER }
13
- // show(message: string, duration: number)
14
- // showWithGravity(message: string, duration: number, gravity: number)
15
- // showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset)
16
- import { dlog } from '../debug.js';
17
- import { getNativeModule } from '../native-modules/index.js';
18
- // The native module name RN registers this under. NOTE: this is the name the spec
19
- // resolves via `TurboModuleRegistry.getEnforcing<Spec>('ToastAndroid')`, NOT the
20
- // spec filename `INativeToastAndroid`. A module name is only provable on a real
21
- // host (a headless fake answers to any name); this Android name is confirmed from
22
- // RN source but not yet exercised on a device.
23
- const TOAST_MODULE = 'ToastAndroid';
24
- // Conventional RN values for SHORT/LONG/TOP/BOTTOM/CENTER. On a real device the
25
- // numbers come from native getConstants() (resolved below); these are the fallbacks
26
- // that keep the constants object populated headless and on a host without the module.
27
- const FALLBACK_CONSTANTS = {
28
- SHORT: 0,
29
- LONG: 1,
30
- TOP: 48,
31
- BOTTOM: 80,
32
- CENTER: 17,
33
- };
34
- // Lazily resolved so importing this module has no native side effect.
35
- let toastModule;
36
- function getModule() {
37
- if (toastModule === undefined) {
38
- toastModule = getNativeModule(TOAST_MODULE);
39
- dlog(`ToastAndroid: ToastAndroid module ${toastModule ? 'resolved' : 'NOT resolved (null)'}`);
40
- }
41
- return toastModule;
42
- }
43
- // Read a single numeric constant from the native getConstants() payload, narrowing
44
- // with a `typeof` guard (no cast) and falling back to the conventional RN value when
45
- // the module or the key is absent.
46
- function readConstant(raw, key) {
47
- const value = raw[key];
48
- return typeof value === 'number' ? value : FALLBACK_CONSTANTS[key];
49
- }
50
- // Resolve the constants once: real numbers from native on a device, fallbacks
51
- // otherwise. Always returns a fully-populated object so the constants are never
52
- // undefined, even headless.
53
- function resolveConstants() {
54
- const module = getModule();
55
- if (module === null) {
56
- dlog('ToastAndroid: constants from fallbacks (native module unavailable)');
57
- return { ...FALLBACK_CONSTANTS };
58
- }
59
- const raw = module.getConstants();
60
- return {
61
- SHORT: readConstant(raw, 'SHORT'),
62
- LONG: readConstant(raw, 'LONG'),
63
- TOP: readConstant(raw, 'TOP'),
64
- BOTTOM: readConstant(raw, 'BOTTOM'),
65
- CENTER: readConstant(raw, 'CENTER'),
66
- };
67
- }
68
- const constants = resolveConstants();
69
- export const ToastAndroid = {
70
- // Toast duration constants.
71
- SHORT: constants.SHORT,
72
- LONG: constants.LONG,
73
- // Toast gravity constants.
74
- TOP: constants.TOP,
75
- BOTTOM: constants.BOTTOM,
76
- CENTER: constants.CENTER,
77
- // Show a toast with the given message and duration. Degrades to a no-op (logged)
78
- // when the module is absent; a missing optional native module must never throw.
79
- show(message, duration) {
80
- const module = getModule();
81
- if (module === null) {
82
- dlog('ToastAndroid.show -> ToastAndroid native module unavailable, no-op');
83
- return;
84
- }
85
- dlog(`ToastAndroid.show -> "${message}" (${duration})`);
86
- module.show(message, duration);
87
- },
88
- // Show a toast at the given layout gravity (TOP / BOTTOM / CENTER).
89
- showWithGravity(message, duration, gravity) {
90
- const module = getModule();
91
- if (module === null) {
92
- dlog('ToastAndroid.showWithGravity -> ToastAndroid native module unavailable, no-op');
93
- return;
94
- }
95
- dlog(`ToastAndroid.showWithGravity -> "${message}" (${duration}, gravity=${gravity})`);
96
- module.showWithGravity(message, duration, gravity);
97
- },
98
- // Show a toast at the given gravity, offset by xOffset/yOffset pixels.
99
- showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset) {
100
- const module = getModule();
101
- if (module === null) {
102
- dlog('ToastAndroid.showWithGravityAndOffset -> ToastAndroid native module unavailable, no-op');
103
- return;
104
- }
105
- dlog(`ToastAndroid.showWithGravityAndOffset -> "${message}" (${duration}, gravity=${gravity}, ${xOffset}, ${yOffset})`);
106
- module.showWithGravityAndOffset(message, duration, gravity, xOffset, yOffset);
107
- },
108
- };
1
+ // ToastAndroid: base / headless build. Metro picks index.android.ts / index.ios.ts on a device; off
2
+ // those, RN's non-Android fallback is the default, as RN's own ToastAndroid.js is.
3
+ export * from './index.ios.js';
@@ -148,7 +148,45 @@ export declare function commitSurfaceOps(rootTag: IRootTag | undefined, surface:
148
148
  export interface ICommitProfile {
149
149
  commits: number;
150
150
  propWrites: number;
151
+ /**
152
+ * Fresh Fabric families minted in this window — the SAME quantity a stock React Native app counts
153
+ * by wrapping `global.nativeFabricUIManager.createNode`, and the only like-for-like census left
154
+ * between the two stacks: our creates are issued from C++ (`SymbioteTree.cpp`,
155
+ * `uiManager.createNode`) and never touch that global, so a JS wrapper over it reads zero here by
156
+ * construction. Two arms whose node counts differ are not one workload, whatever their
157
+ * milliseconds say, so this belongs next to the writes rather than behind a surface id.
158
+ */
159
+ nodesCreated: number;
160
+ /**
161
+ * How many times `applyOps` was ENTERED for this window — the number of JSI crossings the buffer
162
+ * actually cost, as against the one the architecture promises.
163
+ *
164
+ * A whole create should read 2. It reads more when something READS the tree while the tree is
165
+ * being built, because a read is a batch boundary: the buffer has to drain before the answer can
166
+ * be given, so a framework navigating what it is inserting enters `applyOps` once per mutation.
167
+ * `small-batch-crossing-cost.itest.ts` prices an empty prologue at 1.5-4.4 us, so ten thousand
168
+ * boundaries is tens of milliseconds that no node count and no write count can see — which is
169
+ * exactly the shape of a cost that shows up on a device and not in a fixture.
170
+ */
171
+ applyCalls: number;
172
+ /**
173
+ * `applyOps` end to end, and the part of it that reads the buffer out of JS.
174
+ *
175
+ * THE ONE QUESTION A DEVICE HAS TO ANSWER and a fixture cannot. Our crossing is a single call
176
+ * carrying a 12 000-entry array that C++ walks element by element through JSI; stock's is ten
177
+ * thousand calls carrying scalars. On the harness's JavaScriptCore that walk is ~4 ms of a ~48 ms
178
+ * `applyOps`. Hermes is a different JSI implementation with different array-read costs and nothing
179
+ * headless can price it, so the number has to be read on a phone — near 4 ms and the buffer is
180
+ * innocent, tens of milliseconds and it is most of the gap the device reports.
181
+ */
182
+ applyMs: number;
183
+ decodeMs: number;
151
184
  }
185
+ /**
186
+ * NOTE: reading this now DRAINS the surface telemetry too — `nodesCreated` is folded in from
187
+ * `readSurfaceTelemetry`, which zeroes on read in C++. A sampler polling this on an interval
188
+ * therefore empties what a later `readSurfaceTelemetry` call would have reported.
189
+ */
152
190
  export declare function readCommitProfile(): ICommitProfile;
153
191
  export type ISurfaceTelemetry = {
154
192
  layoutMs: number;
@@ -261,6 +299,15 @@ export type ISurfaceTelemetry = {
261
299
  * `stringDecodeMs` / `structureMs` is what the op loop itself costs — the books close here.
262
300
  */
263
301
  applyMs: number;
302
+ /**
303
+ * How many nodes the C++ tree is holding right now — a LEVEL, not a total, and the one counter
304
+ * here that is not drained on read.
305
+ *
306
+ * What it is for: JS ownership anchors the C++ side (a node lives while a parent holds it or while
307
+ * JS names it through `NativeState`), so a heap reading proves the JS half was released and only
308
+ * infers the other. This is the other half as a reading.
309
+ */
310
+ liveNodes: number;
264
311
  stringDecodeMs: number;
265
312
  /** Every append / insert / remove op together. */
266
313
  structureMs: number;
@@ -106,6 +106,11 @@ export function flushOps() {
106
106
  }
107
107
  // Commits this window, for readCommitProfile below.
108
108
  let commits = 0;
109
+ // A surface to ask for this window's node counts. They live in C++ and this profile is id-less, so
110
+ // the window has to keep a tag to ask WITH, and exactly one: `walkCost_` is file-scope in
111
+ // `SymbioteTree.cpp`, shared by every surface and drained on read, so asking a second surface in the
112
+ // same window reads zeroes and a sum would be wrong rather than merely redundant.
113
+ let lastCommittedTag;
109
114
  /**
110
115
  * Record a surface's commit and drain the buffer into the host.
111
116
  *
@@ -158,13 +163,38 @@ export function commitSurfaceOps(rootTag, surface, others = []) {
158
163
  // `undefined` means this surface no longer OWNS its root — a re-mount on the same rootTag took
159
164
  // it. Its ops still drain, because a teardown is what carries the removals; completing the root
160
165
  // would hand Fabric the dead surface's emptied tree over the live one's.
161
- if (rootTag !== undefined)
166
+ if (rootTag !== undefined) {
162
167
  recordCommit(rootTag, surface);
168
+ lastCommittedTag = rootTag;
169
+ }
163
170
  host.applyOps(takeBatch());
164
171
  noteCommitDrained();
165
172
  }
173
+ /**
174
+ * NOTE: reading this now DRAINS the surface telemetry too — `nodesCreated` is folded in from
175
+ * `readSurfaceTelemetry`, which zeroes on read in C++. A sampler polling this on an interval
176
+ * therefore empties what a later `readSurfaceTelemetry` call would have reported.
177
+ */
166
178
  export function readCommitProfile() {
167
- const snapshot = { commits, propWrites: takePropStats().writes };
179
+ // ONE read, not two: `readSurfaceTelemetry` zeroes the C++ accumulator, so asking it twice hands
180
+ // the second caller zeroes and the field would read as "the buffer never crossed".
181
+ //
182
+ // Gated on a commit having LANDED in this window, and not merely on a tag being known. Fabric's
183
+ // `TransactionTelemetry::getCommitStartTime` asserts that a commit has started, so asking a
184
+ // surface that has not committed since the last read aborts the process in a debug build and
185
+ // reads an undefined time point in a release one. The window's own `commits` is the only thing
186
+ // that answers "is there anything to ask about" without asking.
187
+ const telemetry = commits === 0 || lastCommittedTag === undefined
188
+ ? undefined
189
+ : readSurfaceTelemetry(lastCommittedTag);
190
+ const snapshot = {
191
+ commits,
192
+ propWrites: takePropStats().writes,
193
+ nodesCreated: telemetry?.nodesCreated ?? 0,
194
+ applyCalls: telemetry?.applyCalls ?? 0,
195
+ applyMs: telemetry?.applyMs ?? 0,
196
+ decodeMs: telemetry?.decodeMs ?? 0,
197
+ };
168
198
  commits = 0;
169
199
  return snapshot;
170
200
  }