@symbiote-native/engine 0.1.2 → 0.1.3

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 (52) hide show
  1. package/build/accessibility-info/index.android.js +2 -3
  2. package/build/accessibility-info/index.ios.js +2 -3
  3. package/build/accessibility-info/shared.js +2 -1
  4. package/build/action-sheet-ios/index.js +2 -2
  5. package/build/alert/index.android.js +6 -6
  6. package/build/alert/index.ios.js +3 -3
  7. package/build/alert/index.js +1 -1
  8. package/build/alert/shared.js +1 -1
  9. package/build/animated/animated-component-shared.js +1 -2
  10. package/build/animated/animations/base.js +2 -2
  11. package/build/animated/animations/composition.js +1 -1
  12. package/build/animated/animations/decay.js +2 -2
  13. package/build/animated/animations/spring.js +1 -1
  14. package/build/animated/animations/timing.js +1 -1
  15. package/build/animated/graph.js +2 -3
  16. package/build/animated/index.js +5 -5
  17. package/build/animated/native/native-animated.js +3 -3
  18. package/build/animated/props.js +4 -4
  19. package/build/animated/style.js +3 -3
  20. package/build/animated/value-xy.js +1 -1
  21. package/build/animated/value.js +1 -1
  22. package/build/back-handler/index.js +2 -3
  23. package/build/commit.js +6 -6
  24. package/build/dimensions/index.js +1 -2
  25. package/build/i18n-manager/index.js +2 -3
  26. package/build/index.js +2 -2
  27. package/build/layout-animation/index.js +1 -2
  28. package/build/linking/index.android.js +1 -1
  29. package/build/linking/index.js +1 -1
  30. package/build/linking/shared.js +1 -1
  31. package/build/native-events.js +1 -1
  32. package/build/native-modules.js +1 -1
  33. package/build/node.js +3 -2
  34. package/build/permissions-android/index.js +3 -4
  35. package/build/platform/index.android.js +1 -1
  36. package/build/platform/index.ios.js +1 -1
  37. package/build/platform/index.js +0 -1
  38. package/build/platform/shared.js +0 -1
  39. package/build/settings/index.js +2 -4
  40. package/build/share/index.android.js +1 -2
  41. package/build/share/index.ios.js +1 -1
  42. package/build/share/index.js +1 -1
  43. package/build/share/shared.js +1 -1
  44. package/build/status-bar/index.android.js +1 -1
  45. package/build/status-bar/index.ios.js +1 -2
  46. package/build/status-bar/shared.js +1 -1
  47. package/build/style-registry/index.js +12 -16
  48. package/build/toast-android/index.js +3 -3
  49. package/build/vibration/index.android.js +1 -2
  50. package/build/vibration/index.js +1 -1
  51. package/build/vibration/shared.js +4 -5
  52. package/package.json +2 -2
@@ -14,9 +14,8 @@ import { dlog } from '../debug.js';
14
14
  import { isBoolean, } from './shared.js';
15
15
  // The Android native module name. This is the module the Android JS wrapper
16
16
  // (INativeAccessibilityInfoAndroid) resolves: the stock RN `AccessibilityInfo` Turbo/legacy
17
- // module. Per the symbiote invariant, a module name is only provable on a real host (a
18
- // headless fake answers to any name); this Android name is DEVICE-VERIFY-PENDING. See
19
- // .docs/native-module-platform-routing.md.
17
+ // module. A module name is only provable on a real host (a headless fake answers to any
18
+ // name); this Android name is DEVICE-VERIFY-PENDING.
20
19
  const ACCESSIBILITY_MODULE = 'AccessibilityInfo';
21
20
  // Public event name -> the Android device event the native side emits. Android renames
22
21
  // most of them; events with no Android source (iOS-only) are absent and yield an inert
@@ -15,9 +15,8 @@ import { isBoolean, } from './shared.js';
15
15
  // The iOS native module name RN registers this under. NOTE: this is the name the iOS JS
16
16
  // wrapper (INativeAccessibilityManagerIOS) resolves via
17
17
  // `TurboModuleRegistry.get('AccessibilityManager')`, NOT the spec filename
18
- // `NativeAccessibilityManager`. Per the symbiote invariant, a module name is only provable
19
- // on a real host (a headless fake answers to any name); this iOS name is device-verified
20
- // (the pre-split file shipped it). See .docs/native-module-platform-routing.md.
18
+ // `NativeAccessibilityManager`. A module name is only provable on a real host (a headless
19
+ // fake answers to any name); this iOS name is device-verified (the pre-split file shipped it).
21
20
  const ACCESSIBILITY_MODULE = 'AccessibilityManager';
22
21
  // Public event name -> the iOS device event the native side emits. iOS keeps the names
23
22
  // 1:1; the indirection exists only so the mapping stays explicit (Android renames them).
@@ -6,7 +6,8 @@
6
6
  // event maps to (iOS `screenReaderChanged` vs Android `touchExplorationDidChange`).
7
7
  // So the .ios/.android files own the native calls and per-platform event-name map;
8
8
  // the public types + the shared method surface live here. Filename selects, no
9
- // Platform.OS read (see ADR 0012 + native_module_name_is_platform_specific). Mirrors
9
+ // Platform.OS read: the native module name is resolved per platform inside those
10
+ // files, not derived from this file's name. Mirrors
10
11
  // RN's Libraries/Components/AccessibilityInfo/AccessibilityInfo.js.
11
12
  export function isBoolean(value) {
12
13
  return typeof value === 'boolean';
@@ -3,8 +3,8 @@
3
3
  // passes options straight through and the native `callback(buttonIndex)` reports
4
4
  // the tapped row. We mirror RN faithfully.
5
5
  //
6
- // The native contract is confirmed from RN's TurboModule spec at
7
- // .vendors/react-native/.../src/private/specs_DEPRECATED/modules/INativeActionSheetManager.js:
6
+ // The native contract is confirmed from RN's TurboModule spec for
7
+ // `INativeActionSheetManager`:
8
8
  // showActionSheetWithOptions(options, callback: (buttonIndex: number) => void)
9
9
  // showShareActionSheetWithOptions(options, failureCallback, successCallback)
10
10
  // dismissActionSheet?()
@@ -5,15 +5,15 @@
5
5
  // neutral, last-to-first as RN does. Everything platform-agnostic is the shared core.
6
6
  // Metro picks this file on an Android host.
7
7
  //
8
- // The native contract is confirmed from RN's TurboModule spec:
9
- // .vendors/.../specs_DEPRECATED/modules/INativeDialogManagerAndroid.js
10
- // getConstants(): { buttonClicked, dismissed, buttonPositive, buttonNegative,
11
- // buttonNeutral }
12
- // showAlert(config, onError: (msg) => void, onAction: (action, buttonKey?) => void)
8
+ // The native contract is confirmed from RN's TurboModule spec for
9
+ // `INativeDialogManagerAndroid`:
10
+ // getConstants(): { buttonClicked, dismissed, buttonPositive, buttonNegative,
11
+ // buttonNeutral }
12
+ // showAlert(config, onError: (msg) => void, onAction: (action, buttonKey?) => void)
13
13
  //
14
14
  // device-verify-pending: the `DialogManagerAndroid` name and routing are confirmed from RN
15
15
  // source but not yet exercised on a real Android host; only a bridgeless resolution log
16
- // there can prove the name. See .docs/native-module-platform-routing.md.
16
+ // there can prove the name.
17
17
  //
18
18
  // Non-throwing, like StatusBar: a missing native module is a no-op, never a crash.
19
19
  import { dlog } from '../debug.js';
@@ -5,9 +5,9 @@
5
5
  // AlertManager path RN uses. Metro picks this file on an iOS host; the base alert.ts
6
6
  // re-exports it for web/headless.
7
7
  //
8
- // The native contract is confirmed from RN's TurboModule spec:
9
- // .vendors/.../specs_DEPRECATED/modules/INativeAlertManager.js
10
- // alertWithArgs(args: Args, callback: (id: number, value: string) => void)
8
+ // The native contract is confirmed from RN's TurboModule spec for
9
+ // `INativeAlertManager`:
10
+ // alertWithArgs(args: Args, callback: (id: number, value: string) => void)
11
11
  //
12
12
  // Non-throwing, like StatusBar: a missing native module is a no-op, never a crash (on a
13
13
  // device the module may be absent).
@@ -4,5 +4,5 @@
4
4
  // → graceful no-op). The barrel imports './alert', which resolves here under tsc/tsx and to
5
5
  // the platform file under Metro. The `export *` re-exports the public type names
6
6
  // (AlertType, AlertButtonStyle, AlertButton, AlertButtons, AlertOptions) so the barrel's
7
- // `export type { ... } from '../alert/index.js'` still resolves. See ADR 0019.
7
+ // `export type { ... } from '../alert/index.js'` still resolves.
8
8
  export * from './index.ios.js';
@@ -3,7 +3,7 @@
3
3
  // constant defaults, and the button-normalization helper. The per-platform files
4
4
  // (alert.ios.ts / alert.android.ts) implement `alert()` fully against their own native
5
5
  // module (iOS keeps `prompt` too); the two native call shapes are too divergent to share
6
- // a factory. No native, no `Platform.OS` read here. See ADR 0019.
6
+ // a factory. No native, no `Platform.OS` read here.
7
7
  // The default positive label RN uses when a button carries no text.
8
8
  export const DEFAULT_POSITIVE_TEXT = 'OK';
9
9
  // Normalize the `buttons` arg into a consistent list: undefined/empty becomes a single
@@ -3,8 +3,7 @@
3
3
  // mechanism (capture the host node, build an AnimatedProps leaf, reduce animated
4
4
  // props to their current values, override with the passthrough style) is pure JS,
5
5
  // identical across frameworks. Only `assignRef` is framework-ref-specific and stays
6
- // per-adapter; everything here is shared. Extracted from the React wrapper (ADR
7
- // 0016/0017) so a new adapter reuses it verbatim.
6
+ // per-adapter; everything here is shared so a new adapter reuses it verbatim.
8
7
  import { AnimatedNode } from './graph.js';
9
8
  import { AnimatedStyle } from './style.js';
10
9
  export function isAnimatedNode(value) {
@@ -1,5 +1,5 @@
1
1
  // Minimal driver base, ported from RN's animations/Animation.js with every
2
- // native path removed (ADR 0016): no NativeAnimatedHelper, no
2
+ // native path removed: no NativeAnimatedHelper, no
3
3
  // __startAnimationIfNative, no shouldUseNativeDriver, no FeatureFlags. What
4
4
  // remains is the JS-only contract: hold the end callback, track whether the
5
5
  // animation is still active, and fire onEnd at most once.
@@ -48,7 +48,7 @@ export class BaseAnimation {
48
48
  // If useNativeDriver was requested and the module is present, mirror the value
49
49
  // graph into native and hand the curve to native. The JS rAF loop is then
50
50
  // skipped entirely. Returns true when native took over. Falls back to JS (false)
51
- // when the module is missing (ADR 0016 path), so an app without RCTAnimation
51
+ // when the module is missing, so an app without RCTAnimation
52
52
  // still animates.
53
53
  startNativeIfNeeded(animatedValue) {
54
54
  if (!this.nativeDriverRequested)
@@ -1,5 +1,5 @@
1
1
  // The driver factory + composition API, ported from RN's
2
- // AnimatedImplementation.js, JS orchestration only (ADR 0016). `timing` /
2
+ // AnimatedImplementation.js, JS orchestration only. `timing` /
3
3
  // `spring` / `decay` wrap a value with a fresh driver and return a
4
4
  // CompositeAnimation; `parallel` / `sequence` / `stagger` / `loop` / `delay`
5
5
  // orchestrate those. Vector (XY/Color) handling, tracking, AnimatedEvent and
@@ -1,5 +1,5 @@
1
- // DecayAnimation: ported from RN's animations/DecayAnimation.js, JS path only
2
- // (ADR 0016). Models momentum bleeding off under friction: an initial velocity
1
+ // DecayAnimation: ported from RN's animations/DecayAnimation.js, JS path only.
2
+ // Models momentum bleeding off under friction: an initial velocity
3
3
  // decays exponentially toward a resting value. Ends when consecutive frames
4
4
  // move less than 0.1.
5
5
  import { dlog } from '../../debug.js';
@@ -1,5 +1,5 @@
1
1
  // SpringAnimation: ported from RN's animations/SpringAnimation.js, JS path
2
- // only (ADR 0016). Integrates the closed form of a damped harmonic oscillator
2
+ // only. Integrates the closed form of a damped harmonic oscillator
3
3
  // each frame and rests once both velocity and displacement fall below their
4
4
  // thresholds. A spring chained after a previous spring inherits its
5
5
  // position/velocity/time (getInternalState) so retargeting mid-flight stays
@@ -1,5 +1,5 @@
1
1
  // TimingAnimation: ported from RN's animations/TimingAnimation.js, JS path
2
- // only (ADR 0016). Walks a value from `fromValue` to `toValue` over `duration`
2
+ // only. Walks a value from `fromValue` to `toValue` over `duration`
3
3
  // ms, shaping progress through an easing function. The native-frame export and
4
4
  // __startAnimationIfNative branch are dropped.
5
5
  import { Easing } from '../easing.js';
@@ -2,8 +2,7 @@
2
2
  // sits ABOVE symbiote's shadow tree. Ported from React Native's AnimatedNode.js
3
3
  // + AnimatedWithChildren.js, with every native-driver path
4
4
  // (NativeAnimatedHelper / __isNative / __makeNative / __getNativeConfig) removed:
5
- // this is the JS-driven engine (ADR 0016). The native driver re-introduces those
6
- // hooks separately (ADR 0017).
5
+ // this is a JS-driven engine. A native driver re-introduces those hooks separately.
7
6
  //
8
7
  // Two phases drive an update:
9
8
  // A) top-down: when a Value changes, walk children to the leaf nodes (the
@@ -28,7 +27,7 @@ export class AnimatedNode {
28
27
  // _withSuspendedCallbacks, then fires once with the final value. Ported from RN's
29
28
  // AnimatedColor._suspendCallbacks.
30
29
  suspendCallbacks = 0;
31
- // Native-driver state (ADR 0017). Off until a useNativeDriver animation marks
30
+ // Native-driver state. Off until a useNativeDriver animation marks
32
31
  // the graph native; `nativeTag` is the node's identity in the native module,
33
32
  // allocated lazily on first reference (which also creates the native node).
34
33
  isNative = false;
@@ -1,6 +1,6 @@
1
- // @symbiote-native/engine/animated: the framework-agnostic, JS-driven Animated engine
2
- // (ADR 0016). The value graph, easing, interpolation and (Phase 2) drivers are
3
- // pure JS with no React and no native dependency; every adapter re-exports them.
1
+ // @symbiote-native/engine/animated: the framework-agnostic, JS-driven Animated engine.
2
+ // The value graph, easing, interpolation and drivers are pure JS with no React
3
+ // and no native dependency; every adapter re-exports them.
4
4
  export { AnimatedNode, AnimatedWithChildren, flushValue } from './graph.js';
5
5
  export { AnimatedValue } from './value.js';
6
6
  export { AnimatedValueXY } from './value-xy.js';
@@ -15,12 +15,12 @@ export { SpringAnimation } from './animations/spring.js';
15
15
  export { DecayAnimation } from './animations/decay.js';
16
16
  export { AnimatedTracking } from './animations/tracking.js';
17
17
  export { timing, spring, decay, parallel, sequence, stagger, loop, delay, } from './animations/composition.js';
18
- // The native-driver bridge (ADR 0017). Adapters need it to connect a props leaf to
18
+ // The native-driver bridge. Adapters need it to connect a props leaf to
19
19
  // a host view tag and to restore default values on disconnect.
20
20
  export { nativeAnimated, isNativeAnimatedAvailable, } from './native/native-animated.js';
21
21
  // The pure graph leaves and the mock, framework-agnostic (extend AnimatedWithChildren,
22
22
  // no React/Vue). They live here with the rest of the graph; every adapter's
23
- // createAnimatedComponent + Animated namespace re-exports them (ADR 0016/0017).
23
+ // createAnimatedComponent + Animated namespace re-exports them.
24
24
  export { AnimatedProps } from './props.js';
25
25
  export { AnimatedStyle, AnimatedTransform } from './style.js';
26
26
  export { AnimatedMock } from './mock.js';
@@ -1,4 +1,4 @@
1
- // The native-driver bridge (ADR 0017). When an animation runs with
1
+ // The native-driver bridge. When an animation runs with
2
2
  // useNativeDriver:true, the whole value graph is mirrored into native "animated
3
3
  // nodes" and the curve is handed to the stock NativeAnimated TurboModule, which
4
4
  // then mutates the bound shadow node every frame with ZERO JS per frame.
@@ -6,7 +6,7 @@
6
6
  // We consume the module that ships in stock react-native, no native fork. On
7
7
  // iOS bridgeless it registers as `NativeAnimatedTurboModule`; we fall back to the
8
8
  // legacy `NativeAnimatedModule` name. Resolution goes through the same JSI seam as
9
- // every other native module (getNativeModule), consistent with ADR 0012. The
9
+ // every other native module (getNativeModule). The
10
10
  // module is resolved lazily on first use, so importing this file headless (no
11
11
  // native host) is inert until a native-driven animation actually starts.
12
12
  import { dlog } from '../../debug.js';
@@ -29,7 +29,7 @@ function module() {
29
29
  }
30
30
  // True when the stock native module is present in the binary: the gate the
31
31
  // drivers consult before honouring useNativeDriver:true (else they fall back to
32
- // the JS-driven path of ADR 0016).
32
+ // the JS-driven path).
33
33
  export function isNativeAnimatedAvailable() {
34
34
  return module() !== null;
35
35
  }
@@ -5,8 +5,8 @@
5
5
  // flushes to this leaf via its `update()` method, which re-pulls the current values
6
6
  // into a flat partial and pushes it through shared's scoped setNativeProps: one
7
7
  // targeted clone-on-write commit. Ported from RN's AnimatedProps.js, JS-driven path
8
- // only: native config (__makeNative / __getNativeConfig / connectAnimatedNodeToView,
9
- // ADR 0017) is stripped.
8
+ // only: native config (__makeNative / __getNativeConfig / connectAnimatedNodeToView)
9
+ // is stripped.
10
10
  //
11
11
  // `update` MUST be a method, not a class field: flushValue detects a leaf by reading
12
12
  // a `update` function off the node, and under useDefineForClassFields a field
@@ -73,7 +73,7 @@ export class AnimatedProps extends AnimatedWithChildren {
73
73
  // base component's public instance via setNativeView.
74
74
  target = null;
75
75
  // The Fabric view tag this leaf is bound to natively (null until connected).
76
- // Kept so __detach can disconnect exactly what it connected (ADR 0017).
76
+ // Kept so __detach can disconnect exactly what it connected.
77
77
  connectedViewTag = null;
78
78
  constructor(inputProps) {
79
79
  super();
@@ -139,7 +139,7 @@ export class AnimatedProps extends AnimatedWithChildren {
139
139
  this.target = null;
140
140
  super.__detach();
141
141
  }
142
- // ---- native driver (ADR 0017) -------------------------------------------
142
+ // ---- native driver -------------------------------------------------------
143
143
  // Marking the leaf native binds it to the host view. The value->...->props edges
144
144
  // are wired by the upstream walk (AnimatedWithChildren.__makeNative); the one
145
145
  // thing only the leaf can do is attach the props node to a real view tag.
@@ -2,7 +2,7 @@
2
2
  // `transform` whose entries may themselves be AnimatedNodes. Ported from RN's
3
3
  // AnimatedStyle.js + AnimatedTransform.js, NUMERIC path only: the native-driver
4
4
  // config (__makeNative / __getNativeConfig / allowlist) and the web/string/color
5
- // branches are stripped (ADR 0016). On __getValue() each node re-pulls its
5
+ // branches are stripped. On __getValue() each node re-pulls its
6
6
  // animated entries into a plain flat object the props leaf hoists onto the view.
7
7
  import { AnimatedNode, AnimatedWithChildren } from './graph.js';
8
8
  import { flattenStyle } from '../style/index.js';
@@ -88,7 +88,7 @@ export class AnimatedTransform extends AnimatedWithChildren {
88
88
  super.__detach();
89
89
  }
90
90
  // Native: one entry per transform, animated entries pointing at their value's
91
- // native tag, static ones carrying the (angle-normalized) literal (ADR 0017).
91
+ // native tag, static ones carrying the (angle-normalized) literal.
92
92
  __getNativeConfig() {
93
93
  const transforms = [];
94
94
  for (const entry of this.transforms) {
@@ -171,7 +171,7 @@ export class AnimatedStyle extends AnimatedWithChildren {
171
171
  node.__removeChild(this);
172
172
  super.__detach();
173
173
  }
174
- // Native: map each animated style key to its value's native tag (ADR 0017).
174
+ // Native: map each animated style key to its value's native tag.
175
175
  // Static keys are not in the native style node. The view already carries them.
176
176
  __getNativeConfig() {
177
177
  const style = {};
@@ -2,7 +2,7 @@
2
2
  // driving node itself: it multiplexes two ordinary AnimatedValues (x, y), so the
3
3
  // same clone-on-write and listener machinery that powers AnimatedValue applies
4
4
  // per-axis. Ported from RN's AnimatedValueXY.js with the native-driver and
5
- // platform-config branches removed (ADR 0016). The two child values carry their
5
+ // platform-config branches removed. The two child values carry their
6
6
  // own native state if they are ever made native.
7
7
  import { AnimatedValue } from './value.js';
8
8
  let nextListenerId = 1;
@@ -1,7 +1,7 @@
1
1
  // AnimatedValue: the standard driving value. One value can drive many props in
2
2
  // sync but is driven by one mechanism at a time: a new mechanism (a fresh
3
3
  // animation, or setValue) stops the previous one. Ported from RN's
4
- // AnimatedValue.js with the native-driver branches removed (ADR 0016); tracking
4
+ // AnimatedValue.js with the native-driver branches removed; tracking
5
5
  // (animating toward another animated node) is deferred.
6
6
  import { AnimatedWithChildren, flushValue } from './graph.js';
7
7
  import { AnimatedInterpolation } from './interpolation-node.js';
@@ -12,9 +12,8 @@ import { dlog } from '../debug.js';
12
12
  // The native module name RN registers this under. NOTE: this is the name the
13
13
  // INativeDeviceEventManager spec resolves via
14
14
  // `TurboModuleRegistry.get('DeviceEventManager')`, NOT the spec filename
15
- // `INativeDeviceEventManager`. Per the symbiote invariant, a module name is only
16
- // provable on a real host (a headless fake answers to any name); this name is
17
- // device-verify-pending (Android). See .docs/native-module-platform-routing.md.
15
+ // `INativeDeviceEventManager`. A module name is only provable on a real host (a
16
+ // headless fake answers to any name); this name is device-verify-pending (Android).
18
17
  const DEVICE_EVENT_MANAGER_MODULE = 'DeviceEventManager';
19
18
  // The device event native emits when the hardware back button is pressed.
20
19
  const DEVICE_BACK_EVENT = 'hardwareBackPress';
package/build/commit.js CHANGED
@@ -553,13 +553,13 @@ function commitContainer(rootTag) {
553
553
  `cloneChildren=${stats.cloneChildren} reused=${stats.reused}`);
554
554
  }
555
555
  }
556
- // Targeted per-frame prop write for the JS-driven Animated path (ADR 0016). RN
557
- // flushes an animation frame with an in-place `instance.setNativeProps(...)`; we have
558
- // no in-place mutation (Fabric is persistent), so a frame is one scoped commit: mutate
556
+ // Targeted per-frame prop write for the JS-driven Animated path. RN flushes an
557
+ // animation frame with an in-place `instance.setNativeProps(...)`; we have no
558
+ // in-place mutation (Fabric is persistent), so a frame is one scoped commit: mutate
559
559
  // the node's desired props, then re-reconcile its surface. The engine clones only this
560
560
  // node (props differ), bubbles the re-clone to the root, reuses every sibling subtree
561
561
  // by reference, and emits a single completeRoot. This is the "slow tier", viable for a
562
- // single shallow animation; the native driver (ADR 0017) is the answer for scale.
562
+ // single shallow animation; driving the animation natively is the answer for scale.
563
563
  export function setNativeProps(node, partial) {
564
564
  const record = mirror.get(node);
565
565
  if (record === undefined) {
@@ -581,8 +581,8 @@ export function setNativeProps(node, partial) {
581
581
  commitContainer(record.rootTag);
582
582
  }
583
583
  // The committed reactTag of a node (stable across clone-on-write), for binding the
584
- // Animated native driver via connectAnimatedNodeToView (ADR 0017). Undefined until the
585
- // node has been committed at least once.
584
+ // native Animated driver via connectAnimatedNodeToView. Undefined until the node
585
+ // has been committed at least once.
586
586
  export function getNativeTag(node) {
587
587
  return mirror.get(node)?.tag;
588
588
  }
@@ -5,8 +5,7 @@
5
5
  // event whose payload IS a fresh IDimensionsPayload. We cache the metrics and notify
6
6
  // 'change' listeners on each update, exactly as RN does.
7
7
  //
8
- // The native contract is confirmed from RN's TurboModule spec at
9
- // .vendors/.../specs_DEPRECATED/modules/INativeDeviceInfo.js:
8
+ // The native contract mirrors React Native's TurboModule spec for DeviceInfo:
10
9
  // getConstants(): { Dimensions: { window?, screen?, windowPhysicalPixels?,
11
10
  // screenPhysicalPixels? } }
12
11
  // We resolve it through the same generic native-module bridge as Platform
@@ -15,9 +15,8 @@
15
15
  import { dlog } from '../debug.js';
16
16
  import { getNativeModule } from '../native-modules.js';
17
17
  // The iOS native module name RN registers this under (the same name on both
18
- // platforms). NOTE: per the symbiote invariant, a module name is only provable on
19
- // a real host (a headless fake answers to any name); this name is
20
- // device-verify-pending. See .docs/native-module-platform-routing.md.
18
+ // platforms). A module name is only provable on a real host — a headless fake
19
+ // answers to any name — so this name is still pending verification on device.
21
20
  const I18N_MANAGER_MODULE = 'I18nManager';
22
21
  // RN's fallback constants when no native module is linked (headless / not yet on
23
22
  // device): not RTL, and the iOS default of swapping in RTL.
package/build/index.js CHANGED
@@ -39,8 +39,8 @@ export { AnimatedNode, AnimatedWithChildren, AnimatedValue, AnimatedValueXY, Ani
39
39
  export { getSlot } from './fabric.js';
40
40
  // Imperative runtime modules: framework-agnostic native-bridge consumers (no visual, no
41
41
  // lifecycle), moved here from @symbiote-native/react so every adapter re-exports the SAME module.
42
- // Native module names are platform-selected and device-verified, not headless (CLAUDE.md
43
- // <native_module_name_is_platform_specific>).
42
+ // The native module a JS API talks to is chosen per platform and can only be confirmed on a
43
+ // real device or simulator, not headless (a headless fake resolves any module name).
44
44
  export { Alert } from './alert/index.js';
45
45
  export { Share } from './share/index.js';
46
46
  export { ActionSheetIOS } from './action-sheet-ios/index.js';
@@ -13,8 +13,7 @@ import { dlog } from '../debug.js';
13
13
  // DEVICE-VERIFY-PENDING: on bridgeless Fabric the layout-animation configure call
14
14
  // is exposed by RN through the UIManager surface (RN's non-Fabric path calls
15
15
  // `UIManager.configureNextLayoutAnimation`; the Fabric path routes the same args
16
- // onto `global.nativeFabricUIManager.configureNextLayoutAnimation`). Per
17
- // .docs/decisions/0012 and .docs/native-module-platform-routing.md the native
16
+ // onto `global.nativeFabricUIManager.configureNextLayoutAnimation`). The native
18
17
  // MODULE NAME is platform-specific and a headless fake answers to ANY name, so
19
18
  // the name below is the most plausible bridgeless candidate, NOT proven. Only the
20
19
  // simulator/device resolution log can confirm it; the fallback list is tried in
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // device-verify-pending: the `IntentAndroid` name and routing are confirmed from RN
7
7
  // source but not yet exercised on a real Android host; only a bridgeless resolution
8
- // log there can prove the name. See .docs/native-module-platform-routing.md.
8
+ // log there can prove the name.
9
9
  import { createLinking } from './shared.js';
10
10
  export const Linking = createLinking({
11
11
  moduleName: 'IntentAndroid',
@@ -2,5 +2,5 @@
2
2
  // platform file). Metro overrides this with linking.ios.ts / linking.android.ts on a
3
3
  // real iOS/Android host; off those, the iOS build is the fallback (its LinkingManager
4
4
  // resolves null elsewhere → graceful no-op). The barrel imports './linking', which
5
- // resolves here under tsc/tsx and to the platform file under Metro. See ADR 0019.
5
+ // resolves here under tsc/tsx and to the platform file under Metro.
6
6
  export * from './index.ios.js';
@@ -6,7 +6,7 @@
6
6
  //
7
7
  // Metro selects the platform file on a real host (linking.android.ts > linking.ts);
8
8
  // the base linking.ts re-exports the iOS build for web/headless. There is no runtime
9
- // `Platform.OS` read; the filename is the selector. See ADR 0019.
9
+ // `Platform.OS` read; the filename is the selector.
10
10
  import { installDeviceEventHub, NativeEventEmitter, } from '../native-events.js';
11
11
  import { getNativeModule } from '../native-modules.js';
12
12
  import { dlog } from '../debug.js';
@@ -6,7 +6,7 @@
6
6
  // second registration is a silent no-op. So we do NOT register our own hub on a
7
7
  // real host: the app injects RN's DeviceEventEmitter (the bus native actually
8
8
  // calls) via `setDeviceEventSource`, exactly like setColorProcessor. The built-in
9
- // hub below stays as the fallback bus for headless/non-RN runs. See .docs/decisions/0012.
9
+ // hub below stays as the fallback bus for headless/non-RN runs.
10
10
  import { dlog } from './debug.js';
11
11
  import { runWrapped } from './dispatch.js';
12
12
  // The single device-event bus. Native pushes into `emit`; subscribers (wrapped by
@@ -7,7 +7,7 @@
7
7
  // This is first-party access, for symbiote's own modules (StatusBarManager,
8
8
  // KeyboardObserver, …). Third-party RN packages import `TurboModuleRegistry` from
9
9
  // `'react-native'` and read the same global themselves; they do not go through
10
- // here. See .docs/decisions/0012.
10
+ // here.
11
11
  import { dlog } from './debug.js';
12
12
  // The native module value crosses from an untyped HostObject into our types here;
13
13
  // the caller vouches for its shape via T (the single trust-boundary narrowing, no
package/build/node.js CHANGED
@@ -1,8 +1,9 @@
1
1
  // The retained shadow-tree. Adapters mutate this cheap in-memory tree through a
2
2
  // tiny API; the commit engine (commit.ts) later walks it and translates the
3
3
  // whole thing into Fabric's clone-on-write child sets. Keeping the retained
4
- // tree mutable while the Fabric mirror stays persistent is the core R2 trick,
5
- // and it lives here in shared so no adapter re-implements it.
4
+ // tree mutable while the Fabric mirror stays persistent lets every adapter mutate
5
+ // freely without touching Fabric's clone-on-write protocol directly, and it
6
+ // lives here in shared so no adapter re-implements it.
6
7
  import { isEventFor } from './view-config.js';
7
8
  import { isClassNameValue, resolveClassName } from './style-registry/index.js';
8
9
  const BRAND = Symbol('symbiote.node');
@@ -20,10 +20,9 @@
20
20
  import { getNativeModule } from '../native-modules.js';
21
21
  import { dlog } from '../debug.js';
22
22
  // The native module name RN registers this TurboModule under
23
- // (`TurboModuleRegistry.get<Spec>('PermissionsAndroid')`). NOTE: per the
24
- // symbiote invariant, a module name is only provable on a real host (a headless
25
- // fake answers to any name); this name is device-verify-pending. See
26
- // .docs/native-module-platform-routing.md.
23
+ // (`TurboModuleRegistry.get<Spec>('PermissionsAndroid')`). NOTE: a module name is
24
+ // only provable on a real host (a headless fake answers to any name); this name
25
+ // is device-verify-pending.
27
26
  const PERMISSIONS_ANDROID_MODULE = 'PermissionsAndroid';
28
27
  // The runtime-permission result strings Android can return. Modeled as a frozen
29
28
  // `as const` map: the generated-style constant-map exception to "no magic
@@ -2,7 +2,7 @@
2
2
  // `OS` is the static 'android'; `Version` is the numeric API level and the rest come
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
- // the same 'PlatformConstants'; only the shape differs. See .docs/decisions/0022.
5
+ // the same 'PlatformConstants'; only the shape differs.
6
6
  import { createConstantsResolver } from './shared.js';
7
7
  // The filename already selected this host: 'android' is a literal, not a probe.
8
8
  const OS_ANDROID = 'android';
@@ -2,7 +2,7 @@
2
2
  // is the static 'ios'; everything else derives from the PlatformConstants native module
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
- // (platform.ts) so tsc / tsx / web (no Metro) land here too. See .docs/decisions/0022.
5
+ // (platform.ts) so tsc / tsx / web (no Metro) land here too.
6
6
  import { createConstantsResolver, UNKNOWN_VERSION, } from './shared.js';
7
7
  // interfaceIdiom values RN compares against for the device-class getters.
8
8
  const IDIOM_PAD = 'pad';
@@ -1,5 +1,4 @@
1
1
  // Base / default Platform: re-exports the iOS implementation. Metro overrides this
2
2
  // with platform.ios.ts / platform.android.ts on a real host; under tsc / tsx / web
3
3
  // (no Metro) resolution lands here. The filename is the selector, no Platform.OS read.
4
- // See .docs/decisions/0022 (and 0020 for the same split on component names).
5
4
  export * from './index.ios.js';
@@ -4,7 +4,6 @@
4
4
  // owns only what genuinely differs: the OS literal, the constants shape + guard, the
5
5
  // select precedence, and the device-class getters. The filename is the selector (Metro
6
6
  // picks platform.ios / platform.android by host); no Platform.OS read lives here.
7
- // See .docs/decisions/0022 (and 0020 for the same split on component names).
8
7
  import { dlog } from '../debug.js';
9
8
  import { getNativeModule } from '../native-modules.js';
10
9
  // The native module name RN registers PlatformConstants under, identical on both
@@ -10,10 +10,8 @@ import { getNativeModule } from '../native-modules.js';
10
10
  import { dlog } from '../debug.js';
11
11
  // The iOS native module name RN registers this under. NOTE: this is the name the
12
12
  // iOS JS wrapper resolves via `TurboModuleRegistry.getEnforcing('SettingsManager')`.
13
- // The spec filename is `INativeSettingsManager`. Per the symbiote invariant, a
14
- // module name is only provable on a real host (a headless fake answers to any
15
- // name); this iOS name is device-verify-pending.
16
- // See .docs/native-module-platform-routing.md.
13
+ // The spec filename is `INativeSettingsManager`. A module name is only provable on a
14
+ // real host (a headless fake answers to any name); this iOS name is device-verify-pending.
17
15
  const SETTINGS_MODULE = 'SettingsManager';
18
16
  // The device event native emits when the app's defaults change out from under JS
19
17
  // (e.g. a Settings.bundle edit). Its payload is a record of changed key->value.
@@ -2,12 +2,11 @@
2
2
  // `share(content, dialogTitle?) -> Promise<{ action }>`. We validate content, build the
3
3
  // content dict (title/message), forward the dialog title, and map the resolved action
4
4
  // onto the shared IShareAction shape; Android has no dismiss path, so RN fills the
5
- // missing activityType with null. Metro picks this file on an Android host. See ADR 0019.
5
+ // missing activityType with null. Metro picks this file on an Android host.
6
6
  //
7
7
  // device-verify-pending: the `ShareModule` name matches NativeShareModule's
8
8
  // TurboModuleRegistry.get('ShareModule') from RN source, but headless fakes resolve any
9
9
  // name, so it is only proven on a real Android host (a bridgeless resolution log there).
10
- // See .docs/native-module-platform-routing.md, ADR 0012.
11
10
  import { dlog } from '../debug.js';
12
11
  import { getNativeModule } from '../native-modules.js';
13
12
  import { validateContent, shareActions, SHARED_ACTION, DISMISSED_ACTION } from './shared.js';
@@ -3,7 +3,7 @@
3
3
  // `showShareActionSheetWithOptions(options, failure, success)`. We validate content, map
4
4
  // options onto the native share options, and resolve the IShareAction from the success
5
5
  // callback. Metro picks this file on an iOS host; the base share.ts re-exports it for
6
- // web/headless. See ADR 0019.
6
+ // web/headless.
7
7
  import { dlog } from '../debug.js';
8
8
  import { getNativeModule } from '../native-modules.js';
9
9
  import { validateContent, shareActions, SHARED_ACTION, DISMISSED_ACTION } from './shared.js';
@@ -2,5 +2,5 @@
2
2
  // platform file). Metro overrides this with share.ios.ts / share.android.ts on a real
3
3
  // iOS/Android host; off those, the iOS build is the fallback (its ActionSheetManager
4
4
  // resolves null elsewhere → graceful reject). The barrel imports './share', which
5
- // resolves here under tsc/tsx and to the platform file under Metro. See ADR 0019.
5
+ // resolves here under tsc/tsx and to the platform file under Metro.
6
6
  export * from './index.ios.js';
@@ -6,7 +6,7 @@
6
6
  //
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
- // read: the filename is the selector. See ADR 0019.
9
+ // read: the filename is the selector.
10
10
  // RN's Share action constants (RN Share.js ~173/179). These back the documented
11
11
  // `result.action === Share.dismissedAction` pattern. True statically-known literals,
12
12
  // so CONSTANT_CASE; the public fields (Share.sharedAction / Share.dismissedAction) are
@@ -9,7 +9,7 @@
9
9
  // not installed" because RN installs global.RN$stopSurface from its own renderer, which we
10
10
  // replace. Now that render.ts installs RN$stopSurface and tears surfaces down cleanly, the
11
11
  // relayout survives and the bar updates without blanking (verified on device: show/hide +
12
- // light/dark text). See render.ts installStopSurfaceGlobal + native-module-platform-routing.
12
+ // light/dark text). See render.ts's installStopSurfaceGlobal.
13
13
  import { getNativeModule } from '../native-modules.js';
14
14
  import { processColor } from '../commit.js';
15
15
  import { dlog } from '../debug.js';
@@ -1,7 +1,6 @@
1
1
  // StatusBar on iOS drives the iOS `StatusBarManager` TurboModule from props (via
2
2
  // applyStatusBarProps) and from the static methods (statusBarImperative). The native
3
- // contract is RN's spec at
4
- // .vendors/react-native/.../src/private/specs_DEPRECATED/modules/NativeStatusBarManagerIOS.js:
3
+ // contract mirrors RN's own NativeStatusBarManagerIOS spec:
5
4
  // setStyle(statusBarStyle?: string, animated: boolean)
6
5
  // setHidden(hidden: boolean, withAnimation: 'none' | 'fade' | 'slide')
7
6
  // setNetworkActivityIndicatorVisible(visible: boolean)
@@ -6,7 +6,7 @@
6
6
  // flags from our bridgeless surface blanks it (a window-insets relayout detaches the Fabric
7
7
  // surface). So the .ios/.android files own the native calls (applyStatusBarProps +
8
8
  // statusBarImperative); the types + the framework-agnostic imperative surface live here.
9
- // Filename selects, no Platform.OS read (see ADR 0012 + native_module_name_is_platform_specific).
9
+ // Filename selects, no Platform.OS read.
10
10
  //
11
11
  // This is the engine half: pure types + the imperative API. Each adapter wraps it with a
12
12
  // per-framework declarative component (React FC + useEffect, Vue defineComponent + watchEffect)
@@ -1,20 +1,17 @@
1
- // Runtime style registry, ported from wolf-tui's internal/shared/src/styles/registry.ts.
2
- // Side-effect CSS imports (compiled by the sibling CSS-to-style build package, not
3
- // this module) call registerStyles() with camelCase keys; components look them up
4
- // by resolveClassName(). No CSS parsing happens here — this is a Map<string, ...>
5
- // lookup, nothing more.
1
+ // Runtime style registry. Side-effect CSS imports (compiled by the sibling CSS-to-style
2
+ // build package, not this module) call registerStyles() with camelCase keys; components
3
+ // look them up by resolveClassName(). No CSS parsing happens here — this is a
4
+ // Map<string, ...> lookup, nothing more.
6
5
  //
7
- // Dropped vs the wolf-tui original: all Tailwind-utility detection (registerTailwind-
8
- // Metadata / isTailwindUtility / custom prefix-static sets) — this repo's style
9
- // surface has no Tailwind layer, so compound lookup below always runs for 2-4-part
10
- // class strings instead of being gated behind "no part looks like a utility class".
6
+ // This registry has no Tailwind-utility detection layer — this repo's style surface has
7
+ // no Tailwind layer, so the compound lookup below always runs for 2-4-part class strings
8
+ // instead of being gated behind "no part looks like a utility class".
11
9
  //
12
10
  // kebab-case authoring: a CSS selector `.section-label` always registers under the
13
11
  // camelCase key `sectionLabel` (@symbiote-native/css-parser's extractClassName), so a template
14
12
  // can write EITHER `class="sectionLabel"` OR `class="section-label"` — resolveOne below
15
- // falls back to the kebab->camel form on a miss. Reinstated (wolf-tui had this, an
16
- // earlier port here dropped it on the assumption authors would always match the
17
- // camelCase key exactly) once that assumption proved wrong in practice.
13
+ // falls back to the kebab->camel form on a miss. The fallback matters because assuming
14
+ // authors would always match the camelCase key exactly proved wrong in practice.
18
15
  // Compound lookup tries every ordering of 2-4 space-separated class parts joined by
19
16
  // '.' (e.g. "btn primary" -> "btn.primary" / "primary.btn") before falling back to a
20
17
  // per-class merge, mirroring CSS compound-selector registration (`.btn.primary { }`).
@@ -93,10 +90,9 @@ function generateKPermutations(parts, size) {
93
90
  helper([], parts, 0);
94
91
  return result;
95
92
  }
96
- // wolf-tui joins a compound permutation with '.' ("btn.primary") because its CSS
97
- // pipeline registers dot-joined selector strings. This repo's CSS-to-style compiler
98
- // emits plain camelCase keys for every class, single or compound, so "btn primary"
99
- // must resolve against a registered "btnPrimary" instead.
93
+ // Compound permutations join as camelCase ("btn primary" -> "btnPrimary") because this
94
+ // repo's CSS-to-style compiler emits plain camelCase keys for every class, single or
95
+ // compound, so "btn primary" must resolve against a registered "btnPrimary".
100
96
  function toCompoundKey(parts) {
101
97
  return parts.reduce((key, part, index) => (index === 0 ? part : key + capitalize(part)), '');
102
98
  }
@@ -17,9 +17,9 @@ import { dlog } from '../debug.js';
17
17
  import { getNativeModule } from '../native-modules.js';
18
18
  // The native module name RN registers this under. NOTE: this is the name the spec
19
19
  // resolves via `TurboModuleRegistry.getEnforcing<Spec>('ToastAndroid')`, NOT the
20
- // spec filename `INativeToastAndroid`. Per the symbiote invariant, a module name is
21
- // only provable on a real host (a headless fake answers to any name); this Android
22
- // name is device-verify-pending. See .docs/native-module-platform-routing.md.
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
23
  const TOAST_MODULE = 'ToastAndroid';
24
24
  // Conventional RN values for SHORT/LONG/TOP/BOTTOM/CENTER. On a real device the
25
25
  // numbers come from native getConstants() (resolved below); these are the fallbacks
@@ -5,8 +5,7 @@
5
5
  //
6
6
  // device-verify-pending: the `Vibration` name and the `vibrateByPattern` routing are
7
7
  // confirmed from RN source but not yet exercised on a real Android host: only a
8
- // bridgeless resolution log there can prove the name. See
9
- // .docs/native-module-platform-routing.md.
8
+ // bridgeless resolution log there can prove the name.
10
9
  import { createVibration } from './shared.js';
11
10
  // RN encodes "do not repeat" as -1 and "repeat from start" as 0 in vibrateByPattern.
12
11
  const REPEAT_NONE = -1;
@@ -2,5 +2,5 @@
2
2
  // platform file). Metro overrides this with vibration.ios.ts / vibration.android.ts on a
3
3
  // real iOS/Android host; off those, the iOS build is the fallback (its Vibration module
4
4
  // resolves null elsewhere → graceful no-op). The barrel imports './vibration', which
5
- // resolves here under tsc/tsx and to the platform file under Metro. See ADR 0019.
5
+ // resolves here under tsc/tsx and to the platform file under Metro.
6
6
  export * from './index.ios.js';
@@ -5,17 +5,16 @@
5
5
  // `createVibration`. Mirrors RN's Libraries/Vibration/Vibration.js: a number is a single
6
6
  // buzz, an array is a pattern.
7
7
  //
8
- // Native module name is `Vibration` on BOTH iOS and Android (see
9
- // .docs/native-module-platform-routing.md), so unlike Linking the module name is shared,
10
- // not divergent. The TurboModule spec lives at specs_DEPRECATED/modules/INativeVibration.js:
8
+ // Native module name is `Vibration` on BOTH iOS and Android, so unlike Linking the
9
+ // module name is shared, not divergent. The TurboModule spec lives at
10
+ // specs_DEPRECATED/modules/INativeVibration.js:
11
11
  // vibrate(pattern: number)
12
12
  // vibrateByPattern(pattern: number[], repeat: number)
13
13
  // cancel()
14
14
  //
15
15
  // Metro selects the platform file on a real host (vibration.android.ts > vibration.ts);
16
16
  // the base vibration.ts re-exports the iOS build for web/headless. There is no runtime
17
- // `Platform.OS` read; the filename is the selector. See ADR 0019. No Fabric view,
18
- // pure JS->native.
17
+ // `Platform.OS` read; the filename is the selector. No Fabric view, pure JS->native.
19
18
  import { dlog } from '../debug.js';
20
19
  import { getNativeModule } from '../native-modules.js';
21
20
  const VIBRATION_MODULE = 'Vibration';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/engine",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "SymbioteNative's retained shadow-tree engine — clone-on-write commit path + event normalization over React Native Fabric, shared by every framework adapter.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,7 +32,7 @@
32
32
  "react-native": ">=0.86"
33
33
  },
34
34
  "devDependencies": {
35
- "@symbiote-native/test-utils": "0.1.1"
35
+ "@symbiote-native/test-utils": "0.1.3"
36
36
  },
37
37
  "scripts": {
38
38
  "typecheck": "tsc --build",