@symbiote-native/angular 1.0.0 → 2.0.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 (248) hide show
  1. package/README.md +13 -10
  2. package/babel-register-composed.cjs +41 -19
  3. package/bin/symbiote-angular-dev.cjs +16 -4
  4. package/build/angular/anchor-host-registry.js +1 -17
  5. package/build/angular/anchor-host-registry.js.map +1 -1
  6. package/build/angular/change-detection-flush.d.ts +31 -0
  7. package/build/angular/change-detection-flush.js +110 -0
  8. package/build/angular/change-detection-flush.js.map +1 -0
  9. package/build/angular/components/activity-indicator-props.d.ts +2 -0
  10. package/build/angular/components/activity-indicator-props.js +2 -0
  11. package/build/angular/components/activity-indicator-props.js.map +1 -0
  12. package/build/angular/components/button-props.d.ts +2 -0
  13. package/build/angular/components/button-props.js +2 -0
  14. package/build/angular/components/button-props.js.map +1 -0
  15. package/build/angular/components/flat-list/index.d.ts +2 -1
  16. package/build/angular/components/flat-list/index.js +22 -10
  17. package/build/angular/components/flat-list/index.js.map +1 -1
  18. package/build/angular/components/image-background-props.d.ts +6 -0
  19. package/build/angular/components/image-background-props.js +2 -0
  20. package/build/angular/components/image-background-props.js.map +1 -0
  21. package/build/angular/components/{image/shared.d.ts → image-shared.d.ts} +1 -1
  22. package/build/angular/components/{image/shared.js → image-shared.js} +7 -2
  23. package/build/angular/components/image-shared.js.map +1 -0
  24. package/build/angular/components/input-accessory-view-props.d.ts +7 -0
  25. package/build/angular/components/input-accessory-view-props.js +9 -0
  26. package/build/angular/components/input-accessory-view-props.js.map +1 -0
  27. package/build/angular/components/keyboard-avoiding-view/index.js +11 -17
  28. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
  29. package/build/angular/components/modal/index.d.ts +1 -0
  30. package/build/angular/components/modal/index.js +19 -12
  31. package/build/angular/components/modal/index.js.map +1 -1
  32. package/build/angular/components/pressable-props.d.ts +32 -0
  33. package/build/angular/components/pressable-props.js +11 -0
  34. package/build/angular/components/pressable-props.js.map +1 -0
  35. package/build/angular/components/refresh-control-props.d.ts +16 -0
  36. package/build/angular/components/refresh-control-props.js +14 -0
  37. package/build/angular/components/refresh-control-props.js.map +1 -0
  38. package/build/angular/components/safe-area-view-props.d.ts +7 -0
  39. package/build/angular/components/safe-area-view-props.js +11 -0
  40. package/build/angular/components/safe-area-view-props.js.map +1 -0
  41. package/build/angular/components/scroll-view-props.d.ts +76 -0
  42. package/build/angular/components/scroll-view-props.js +18 -0
  43. package/build/angular/components/scroll-view-props.js.map +1 -0
  44. package/build/angular/components/text-input-props.d.ts +45 -0
  45. package/build/angular/components/text-input-props.js +18 -0
  46. package/build/angular/components/text-input-props.js.map +1 -0
  47. package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +6 -0
  48. package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js +2 -0
  49. package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js.map +1 -0
  50. package/build/angular/components/touchable-props.d.ts +14 -0
  51. package/build/angular/components/touchable-props.js +13 -0
  52. package/build/angular/components/touchable-props.js.map +1 -0
  53. package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +3 -0
  54. package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js +2 -0
  55. package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js.map +1 -0
  56. package/build/angular/components/virtualized-list/index.d.ts +5 -1
  57. package/build/angular/components/virtualized-list/index.js +366 -280
  58. package/build/angular/components/virtualized-list/index.js.map +1 -1
  59. package/build/angular/components.d.ts +20 -26
  60. package/build/angular/components.js +12 -14
  61. package/build/angular/components.js.map +1 -1
  62. package/build/angular/create-portal/index.d.ts +2 -2
  63. package/build/angular/create-portal/index.js +5 -5
  64. package/build/angular/create-tunnel/index.js +1 -1
  65. package/build/angular/element-props.d.ts +53 -0
  66. package/build/angular/element-props.js +2 -0
  67. package/build/angular/element-props.js.map +1 -0
  68. package/build/angular/elements.d.ts +528 -0
  69. package/build/angular/elements.js +1444 -0
  70. package/build/angular/elements.js.map +1 -0
  71. package/build/angular/host-instance/index.js +1 -1
  72. package/build/angular/index.d.ts +4 -2
  73. package/build/angular/index.js +15 -1
  74. package/build/angular/index.js.map +1 -1
  75. package/build/angular/modules/animated/create-animated-component.d.ts +5 -6
  76. package/build/angular/modules/animated/create-animated-component.js +60 -73
  77. package/build/angular/modules/animated/create-animated-component.js.map +1 -1
  78. package/build/angular/modules/animated/index.js +1 -1
  79. package/build/angular/modules/animated/index.js.map +1 -1
  80. package/build/angular/modules/image/index.d.ts +3 -0
  81. package/build/angular/modules/image/index.js +16 -0
  82. package/build/angular/modules/image/index.js.map +1 -0
  83. package/build/angular/primitives/index.d.ts +18 -18
  84. package/build/angular/primitives/index.js +40 -36
  85. package/build/angular/primitives/index.js.map +1 -1
  86. package/build/angular/primitives/shared.d.ts +2 -3
  87. package/build/angular/primitives/shared.js +8 -53
  88. package/build/angular/primitives/shared.js.map +1 -1
  89. package/build/angular/register.d.ts +1 -1
  90. package/build/angular/register.js +12 -31
  91. package/build/angular/register.js.map +1 -1
  92. package/build/angular/render/index.js +3 -3
  93. package/build/angular/render/index.js.map +1 -1
  94. package/build/angular/renderer/index.js +113 -67
  95. package/build/angular/renderer/index.js.map +1 -1
  96. package/build/angular/renderer/value-change.d.ts +2 -0
  97. package/build/angular/renderer/value-change.js +9 -0
  98. package/build/angular/renderer/value-change.js.map +1 -0
  99. package/metro-config.cjs +23 -5
  100. package/metro-css-parser.cjs +2 -1
  101. package/package.json +5 -14
  102. package/src/anchor-host-registry.ts +1 -17
  103. package/src/change-detection-flush.ts +120 -0
  104. package/src/components/activity-indicator-props.ts +21 -0
  105. package/src/components/button-props.ts +26 -0
  106. package/src/components/flat-list/index.ts +12 -4
  107. package/src/components/image-background-props.ts +24 -0
  108. package/src/components/{image/shared.ts → image-shared.ts} +6 -1
  109. package/src/components/input-accessory-view-props.ts +21 -0
  110. package/src/components/keyboard-avoiding-view/index.ts +6 -9
  111. package/src/components/modal/index.ts +15 -7
  112. package/src/components/pressable-props.ts +73 -0
  113. package/src/components/refresh-control-props.ts +43 -0
  114. package/src/components/safe-area-view-props.ts +28 -0
  115. package/src/components/scroll-view-props.ts +109 -0
  116. package/src/components/text-input-props.ts +72 -0
  117. package/src/components/touchable-native-feedback/touchable-native-feedback-props.ts +23 -0
  118. package/src/components/touchable-props.ts +30 -0
  119. package/src/components/touchable-without-feedback/touchable-without-feedback-props.ts +21 -0
  120. package/src/components/virtualized-list/index.ts +239 -156
  121. package/src/components.ts +84 -35
  122. package/src/create-portal/index.ts +5 -5
  123. package/src/create-tunnel/index.ts +1 -1
  124. package/src/diagnostics.ts +1 -1
  125. package/src/element-props.ts +90 -0
  126. package/src/elements.ts +919 -0
  127. package/src/host-instance/index.ts +1 -1
  128. package/src/index.ts +52 -15
  129. package/src/modules/animated/create-animated-component.ts +50 -55
  130. package/src/modules/animated/index.ts +1 -1
  131. package/src/modules/image/index.ts +18 -0
  132. package/src/primitives/index.ts +22 -18
  133. package/src/primitives/shared.ts +11 -44
  134. package/src/register.ts +12 -40
  135. package/src/render/index.ts +3 -3
  136. package/src/renderer/index.ts +122 -73
  137. package/src/renderer/value-change.ts +8 -0
  138. package/babel-lower-host-primitives.cjs +0 -636
  139. package/build/angular/components/activity-indicator/index.android.d.ts +0 -13
  140. package/build/angular/components/activity-indicator/index.android.js +0 -29
  141. package/build/angular/components/activity-indicator/index.android.js.map +0 -1
  142. package/build/angular/components/activity-indicator/index.d.ts +0 -1
  143. package/build/angular/components/activity-indicator/index.ios.d.ts +0 -9
  144. package/build/angular/components/activity-indicator/index.ios.js +0 -26
  145. package/build/angular/components/activity-indicator/index.ios.js.map +0 -1
  146. package/build/angular/components/activity-indicator/index.js +0 -4
  147. package/build/angular/components/activity-indicator/index.js.map +0 -1
  148. package/build/angular/components/activity-indicator/shared.d.ts +0 -67
  149. package/build/angular/components/activity-indicator/shared.js +0 -260
  150. package/build/angular/components/activity-indicator/shared.js.map +0 -1
  151. package/build/angular/components/button.d.ts +0 -68
  152. package/build/angular/components/button.js +0 -360
  153. package/build/angular/components/button.js.map +0 -1
  154. package/build/angular/components/image/index.android.d.ts +0 -15
  155. package/build/angular/components/image/index.android.js +0 -83
  156. package/build/angular/components/image/index.android.js.map +0 -1
  157. package/build/angular/components/image/index.d.ts +0 -1
  158. package/build/angular/components/image/index.ios.d.ts +0 -15
  159. package/build/angular/components/image/index.ios.js +0 -83
  160. package/build/angular/components/image/index.ios.js.map +0 -1
  161. package/build/angular/components/image/index.js +0 -3
  162. package/build/angular/components/image/index.js.map +0 -1
  163. package/build/angular/components/image/shared.js.map +0 -1
  164. package/build/angular/components/image-background.d.ts +0 -85
  165. package/build/angular/components/image-background.js +0 -443
  166. package/build/angular/components/image-background.js.map +0 -1
  167. package/build/angular/components/input-accessory-view/index.d.ts +0 -66
  168. package/build/angular/components/input-accessory-view/index.js +0 -287
  169. package/build/angular/components/input-accessory-view/index.js.map +0 -1
  170. package/build/angular/components/pressable/index.d.ts +0 -134
  171. package/build/angular/components/pressable/index.js +0 -514
  172. package/build/angular/components/pressable/index.js.map +0 -1
  173. package/build/angular/components/refresh-control/index.d.ts +0 -91
  174. package/build/angular/components/refresh-control/index.js +0 -382
  175. package/build/angular/components/refresh-control/index.js.map +0 -1
  176. package/build/angular/components/safe-area-view/index.d.ts +0 -64
  177. package/build/angular/components/safe-area-view/index.js +0 -287
  178. package/build/angular/components/safe-area-view/index.js.map +0 -1
  179. package/build/angular/components/scroll-view/index.android.d.ts +0 -20
  180. package/build/angular/components/scroll-view/index.android.js +0 -262
  181. package/build/angular/components/scroll-view/index.android.js.map +0 -1
  182. package/build/angular/components/scroll-view/index.d.ts +0 -3
  183. package/build/angular/components/scroll-view/index.ios.d.ts +0 -8
  184. package/build/angular/components/scroll-view/index.ios.js +0 -92
  185. package/build/angular/components/scroll-view/index.ios.js.map +0 -1
  186. package/build/angular/components/scroll-view/index.js +0 -6
  187. package/build/angular/components/scroll-view/index.js.map +0 -1
  188. package/build/angular/components/scroll-view/projection.d.ts +0 -67
  189. package/build/angular/components/scroll-view/projection.js +0 -560
  190. package/build/angular/components/scroll-view/projection.js.map +0 -1
  191. package/build/angular/components/scroll-view/shared.d.ts +0 -241
  192. package/build/angular/components/scroll-view/shared.js +0 -848
  193. package/build/angular/components/scroll-view/shared.js.map +0 -1
  194. package/build/angular/components/scroll-view/sticky-header.d.ts +0 -40
  195. package/build/angular/components/scroll-view/sticky-header.js +0 -224
  196. package/build/angular/components/scroll-view/sticky-header.js.map +0 -1
  197. package/build/angular/components/switch/index.android.d.ts +0 -24
  198. package/build/angular/components/switch/index.android.js +0 -112
  199. package/build/angular/components/switch/index.android.js.map +0 -1
  200. package/build/angular/components/switch/index.d.ts +0 -1
  201. package/build/angular/components/switch/index.ios.d.ts +0 -23
  202. package/build/angular/components/switch/index.ios.js +0 -111
  203. package/build/angular/components/switch/index.ios.js.map +0 -1
  204. package/build/angular/components/switch/index.js +0 -4
  205. package/build/angular/components/switch/index.js.map +0 -1
  206. package/build/angular/components/switch/shared.d.ts +0 -49
  207. package/build/angular/components/switch/shared.js +0 -191
  208. package/build/angular/components/switch/shared.js.map +0 -1
  209. package/build/angular/components/text-input.d.ts +0 -167
  210. package/build/angular/components/text-input.js +0 -684
  211. package/build/angular/components/text-input.js.map +0 -1
  212. package/build/angular/components/touchable/index.d.ts +0 -283
  213. package/build/angular/components/touchable/index.js +0 -1453
  214. package/build/angular/components/touchable/index.js.map +0 -1
  215. package/build/angular/components/touchable-native-feedback/index.d.ts +0 -87
  216. package/build/angular/components/touchable-native-feedback/index.js +0 -409
  217. package/build/angular/components/touchable-native-feedback/index.js.map +0 -1
  218. package/build/angular/state-style.d.ts +0 -9
  219. package/build/angular/state-style.js +0 -39
  220. package/build/angular/state-style.js.map +0 -1
  221. package/metro-transformer.cjs +0 -69
  222. package/src/components/activity-indicator/index.android.ts +0 -23
  223. package/src/components/activity-indicator/index.ios.ts +0 -21
  224. package/src/components/activity-indicator/index.ts +0 -4
  225. package/src/components/activity-indicator/shared.ts +0 -225
  226. package/src/components/button.ts +0 -237
  227. package/src/components/image/index.android.ts +0 -95
  228. package/src/components/image/index.ios.ts +0 -95
  229. package/src/components/image/index.ts +0 -3
  230. package/src/components/image-background.ts +0 -308
  231. package/src/components/input-accessory-view/index.ts +0 -246
  232. package/src/components/pressable/index.ts +0 -522
  233. package/src/components/refresh-control/index.ts +0 -349
  234. package/src/components/safe-area-view/index.ts +0 -247
  235. package/src/components/scroll-view/index.android.ts +0 -205
  236. package/src/components/scroll-view/index.ios.ts +0 -81
  237. package/src/components/scroll-view/index.ts +0 -7
  238. package/src/components/scroll-view/projection.ts +0 -713
  239. package/src/components/scroll-view/shared.ts +0 -1093
  240. package/src/components/scroll-view/sticky-header.ts +0 -267
  241. package/src/components/switch/index.android.ts +0 -113
  242. package/src/components/switch/index.ios.ts +0 -112
  243. package/src/components/switch/index.ts +0 -4
  244. package/src/components/switch/shared.ts +0 -284
  245. package/src/components/text-input.ts +0 -669
  246. package/src/components/touchable/index.ts +0 -956
  247. package/src/components/touchable-native-feedback/index.ts +0 -327
  248. package/src/state-style.ts +0 -39
@@ -2,7 +2,7 @@
2
2
  // adapters/vue/src/host-instance/index.ts. RN's "ref/instance -> native reactTag" lookup,
3
3
  // the seam imperative-interop libraries reach through.
4
4
  //
5
- // Angular template refs on a primitive host (`<View #myView>`) receive the component instance,
5
+ // Angular template refs on a primitive host (`<view #myView>`) receive the component instance,
6
6
  // not the raw engine node. SymbiotePrimitiveHost exposes `nativeElement`, which holds the
7
7
  // engine host node that `getNativeTag` is keyed on. This adapter accepts:
8
8
  // - a bare number (idempotent)
package/src/index.ts CHANGED
@@ -8,34 +8,20 @@
8
8
  import './register';
9
9
 
10
10
  export {
11
- ActivityIndicator,
12
11
  anchorHostStyle,
13
- Button,
14
12
  FlatList,
15
13
  HorizontalScrollContentView,
16
14
  HorizontalScrollView,
17
15
  Image,
18
- ImageBackground,
19
- InputAccessoryView,
20
16
  KeyboardAvoidingView,
21
17
  Modal,
22
- Pressable,
23
- RefreshControl,
24
- SafeAreaView,
25
18
  ScrollContentView,
26
- ScrollView,
27
- ScrollViewStickyHeader,
28
19
  SectionList,
29
20
  stableAnchorStyle,
30
- Switch,
31
21
  SymbioteHostPropsDirective,
32
22
  SymbioteStyleInputDirective,
33
23
  Text,
34
- TextInput,
35
- TouchableHighlight,
36
24
  TouchableNativeFeedback,
37
- TouchableOpacity,
38
- TouchableWithoutFeedback,
39
25
  View,
40
26
  VirtualizedList,
41
27
  VirtualizedSectionList,
@@ -75,6 +61,7 @@ export {
75
61
  export { createAnimatedComponent } from './modules/animated';
76
62
  export type {
77
63
  IActivityIndicatorProps,
64
+ IAngularPressableInputs,
78
65
  IAngularImageBackgroundProps,
79
66
  IAngularInputAccessoryViewProps,
80
67
  IAngularKeyboardAvoidingViewProps,
@@ -112,7 +99,6 @@ export type {
112
99
  ISectionListProps,
113
100
  ISeparatorProps,
114
101
  ISeparators,
115
- IStickyHeaderComponentType,
116
102
  ISubmitBehavior,
117
103
  IThemeAttrBackground,
118
104
  IViewabilityConfig,
@@ -130,10 +116,61 @@ export type {
130
116
  ISection,
131
117
  ISwitchProps,
132
118
  ISwitchTrackColor,
119
+ ISwitchChangeEvent,
133
120
  ITextInputHandle,
134
121
  ITextInputSelection,
122
+ ITextInputChangeEvent,
135
123
  } from './components';
136
124
  export { setImageSourceResolver } from './components';
125
+ // The element directives that make a HAND-WRITTEN intrinsic tag (`<view>`, `<text-input>`, ...)
126
+ // compile under ngtsc with no schema, and with a real type on every declared prop. `imports:
127
+ // [SYMBIOTE_ELEMENTS]` is the whole app-facing surface; the individual classes are exported for an
128
+ // app that wants a narrower import. See `elements.ts`'s header for why a directive rather than
129
+ // `CUSTOM_ELEMENTS_SCHEMA`/`NO_ERRORS_SCHEMA`.
130
+ export {
131
+ SYMBIOTE_ELEMENTS,
132
+ SymbioteElement,
133
+ ActivityIndicatorElement,
134
+ ActivityIndicatorSpinnerElement,
135
+ ButtonElement,
136
+ HorizontalScrollContentElement,
137
+ HorizontalScrollViewElement,
138
+ ImageBackgroundElement,
139
+ ImageElement,
140
+ InputAccessoryViewElement,
141
+ ManagedMultilineTextInputElement,
142
+ ManagedSwitchElement,
143
+ ManagedTextInputElement,
144
+ ModalElement,
145
+ MultilineTextInputElement,
146
+ PressableElement,
147
+ RefreshControlElement,
148
+ SafeAreaViewElement,
149
+ ScrollContentElement,
150
+ ScrollViewElement,
151
+ StickyHeaderElement,
152
+ SwitchElement,
153
+ TextElement,
154
+ TextInputElement,
155
+ // The `[(ngModel)]` / `formControl*` accessors for the two controlled tags — where the deleted
156
+ // wrappers' `NG_VALUE_ACCESSOR` went. Both ride `SYMBIOTE_ELEMENTS`; named separately for an app
157
+ // that imports narrowly.
158
+ SwitchValueAccessor,
159
+ TextInputValueAccessor,
160
+ // The three the list omitted while their components still existed. These are now the ONLY
161
+ // replacements for their deleted components in an app's `imports`, so a narrower import has to be
162
+ // able to name them.
163
+ TouchableHighlightElement,
164
+ TouchableNativeFeedbackElement,
165
+ TouchableOpacityElement,
166
+ TouchableWithoutFeedbackElement,
167
+ ViewElement,
168
+ } from './elements';
169
+ export type {
170
+ IElementProps,
171
+ IStickyHeaderElementProps,
172
+ ITextElementProps,
173
+ } from './element-props';
137
174
  export { mount, unmount } from './render';
138
175
  // The generic Descriptor→Angular bridge, the twin of descriptorToReact/descriptorToVue.
139
176
  // Exported so a component defined OUTSIDE this package (e.g.
@@ -37,12 +37,10 @@ import {
37
37
  resolveHostNode,
38
38
  type ISymbioteNode,
39
39
  } from '@symbiote-native/engine';
40
- import { selectScrollIntrinsics } from '@symbiote-native/components';
41
- import { Image, ScrollView, Text, View } from '../../components';
40
+ import { Text, View } from '../../components';
42
41
  import {
43
42
  anchorHostStyle,
44
43
  ImageHost,
45
- ScrollViewHost,
46
44
  SymbioteHostPropsDirective,
47
45
  SymbioteStyleInputDirective,
48
46
  TextHost,
@@ -55,11 +53,17 @@ import {
55
53
  ImageBase,
56
54
  isImageEventCallback,
57
55
  resolveImageProps,
58
- } from '../../components/image/shared';
56
+ } from '../../components/image-shared';
59
57
  import type { IGatedAccessibilityEvent } from '../../gate-demand';
60
58
  import { SectionList } from '../../components/section-list';
61
59
  import { AnimatedLeafBinder } from './animated-leaf-binder';
62
60
 
61
+ // The intrinsic `createAnimatedComponent` accepts in place of the deleted `Image` component.
62
+ const IMAGE_TAG = 'image';
63
+ // Same move for the deleted `ScrollView` component — the vertical axis's own tag stands in for
64
+ // the class identity the generic dispatcher used to compare against.
65
+ const SCROLL_VIEW_TAG = 'scroll-view';
66
+
63
67
  // RN's prop carrying explicit (already-rasterized) values that override the animated prop in
64
68
  // the COMMITTED props (sticky-header passthrough). Named once so the directive input and the
65
69
  // reconcile agree.
@@ -70,7 +74,17 @@ const PASSTHROUGH_PROP = 'passthroughAnimatedPropExplicitValues';
70
74
  // escape hatch for any OTHER prop that may hold an AnimatedNode or an Animated.event handler,
71
75
  // plus static host props to forward (the React/Vue `rest`). Each concrete @Component re-lists
72
76
  // these (Angular's inputs-on-base convention, mirroring Switch/Image).
73
- export const ANIMATED_INPUTS = ['style', 'animatedProps', PASSTHROUGH_PROP];
77
+ // `testID` is listed alongside them rather than left to `animatedProps`, which is the documented
78
+ // escape hatch for everything else. Every other component in this adapter takes it as an ordinary
79
+ // input, and React's `createAnimatedComponent` spreads `...props`, so `<AnimatedView testID="x">`
80
+ // is what an app writes — it compiled (a static attribute on a component tag is never checked),
81
+ // landed on the non-painting anchor host and committed nowhere.
82
+ export const ANIMATED_INPUTS = [
83
+ 'style',
84
+ 'testID',
85
+ 'animatedProps',
86
+ PASSTHROUGH_PROP,
87
+ ];
74
88
  export const ANIMATED_IMAGE_INPUTS = [
75
89
  ...IMAGE_INPUTS,
76
90
  'animatedProps',
@@ -87,6 +101,7 @@ export abstract class AnimatedComponentBase
87
101
  implements AfterViewInit, DoCheck, OnChanges, OnDestroy
88
102
  {
89
103
  style: unknown;
104
+ testID: string | undefined;
90
105
  animatedProps: Record<string, unknown> | undefined;
91
106
  passthroughAnimatedPropExplicitValues: unknown;
92
107
 
@@ -183,9 +198,11 @@ export abstract class AnimatedComponentBase
183
198
  // never disagree.
184
199
  private mergedProps(): Record<string, unknown> {
185
200
  const base = this.animatedProps ?? {};
186
- return this.style === undefined
187
- ? { ...base }
188
- : { ...base, style: this.style };
201
+ const merged: Record<string, unknown> =
202
+ this.style === undefined ? { ...base } : { ...base, style: this.style };
203
+ // Only when set, so an unset input cannot clear a `testID` the app put in `animatedProps`.
204
+ if (this.testID !== undefined) merged.testID = this.testID;
205
+ return merged;
189
206
  }
190
207
 
191
208
  // The committed host node held by IDENTITY. resolveHostNode unwraps an imperative scroll
@@ -207,9 +224,9 @@ export abstract class AnimatedComponentBase
207
224
  imports: [SymbioteHostPropsDirective, ViewHost],
208
225
  inputs: ANIMATED_INPUTS,
209
226
  template: `
210
- <symbiote-view [symbioteHostProps]="reducedProps">
227
+ <view [symbioteHostProps]="reducedProps">
211
228
  <ng-content></ng-content>
212
- </symbiote-view>
229
+ </view>
213
230
  `,
214
231
  changeDetection: ChangeDetectionStrategy.OnPush,
215
232
  })
@@ -225,9 +242,9 @@ export class AnimatedView extends AnimatedComponentBase {}
225
242
  imports: [SymbioteHostPropsDirective, TextHost],
226
243
  inputs: ANIMATED_INPUTS,
227
244
  template: `
228
- <symbiote-text [symbioteHostProps]="reducedProps">
245
+ <text [symbioteHostProps]="reducedProps">
229
246
  <ng-content></ng-content>
230
- </symbiote-text>
247
+ </text>
231
248
  `,
232
249
  changeDetection: ChangeDetectionStrategy.OnPush,
233
250
  })
@@ -244,7 +261,7 @@ export class AnimatedText extends AnimatedComponentBase {}
244
261
  inputs: ANIMATED_IMAGE_INPUTS,
245
262
  outputs: IMAGE_OUTPUTS,
246
263
  template: `
247
- <symbiote-image
264
+ <image
248
265
  [symbioteHostProps]="animatedImageProps"
249
266
  (loadStart)="handleLoadStart($event)"
250
267
  (load)="handleLoad($event)"
@@ -254,7 +271,7 @@ export class AnimatedText extends AnimatedComponentBase {}
254
271
  (partialLoad)="handlePartialLoad($event)"
255
272
  >
256
273
  <ng-content></ng-content>
257
- </symbiote-image>
274
+ </image>
258
275
  `,
259
276
  changeDetection: ChangeDetectionStrategy.OnPush,
260
277
  })
@@ -382,49 +399,23 @@ export class AnimatedImage
382
399
  { directive: SymbioteStyleInputDirective, inputs: ['style'] },
383
400
  ],
384
401
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
385
- imports: [SymbioteHostPropsDirective, ScrollViewHost],
402
+ imports: [SymbioteHostPropsDirective],
386
403
  inputs: ANIMATED_INPUTS,
387
404
  template: `
388
- <symbiote-scroll-view [symbioteHostProps]="reducedProps">
389
- <symbiote-scroll-content [symbioteHostProps]="contentProps">
390
- <ng-content></ng-content>
391
- </symbiote-scroll-content>
392
- </symbiote-scroll-view>
405
+ <scroll-view [symbioteHostProps]="reducedProps">
406
+ <ng-content></ng-content>
407
+ </scroll-view>
393
408
  `,
394
409
  changeDetection: ChangeDetectionStrategy.OnPush,
395
410
  })
396
411
  export class AnimatedScrollView extends AnimatedComponentBase {
397
- // RN defaults nested scrolling ON (ScrollView.js `nestedScrollEnabled ?? true`) — Android
398
- // needs it explicit or a nested ScrollView never receives touch (swallowed by the outer one;
399
- // iOS doesn't gate on this). The real ScrollView component already defaults this
400
- // (scroll-view/shared.ts), but AnimatedScrollView talks to the raw primitive directly and
401
- // must apply it too.
402
- //
403
- // scrollViewBaseStyle (overflow: 'scroll' + the per-axis flexDirection) is the other thing the
404
- // real ScrollView applies that this bespoke template used to skip — without it, iOS Fabric
405
- // never clips content to the scroll view's frame (invisible on Android, whose native ViewGroup
406
- // clips regardless of style). This wrapper has no `horizontal` input and its template is
407
- // hardcoded to the vertical intrinsics, so selectScrollIntrinsics is always called with
408
- // isHorizontal=false here, matching what the template can actually render.
409
- override get reducedProps(): Record<string, unknown> {
410
- const reduced = super.reducedProps;
411
- const { scrollViewBaseStyle } = selectScrollIntrinsics(false, undefined);
412
- const withScrollBase: Record<string, unknown> = {
413
- ...reduced,
414
- style: [scrollViewBaseStyle, reduced.style],
415
- };
416
- return withScrollBase.nestedScrollEnabled === undefined
417
- ? { ...withScrollBase, nestedScrollEnabled: true }
418
- : withScrollBase;
419
- }
420
-
421
- // The content (inner) view's props: contentStyle from the same intrinsics selection, plus
422
- // `collapsable: false` (the Android multi-child fix this file's tests already cover) — mirrors
423
- // the real ScrollView's own `contentProps` getter (scroll-view/shared.ts).
424
- get contentProps(): Record<string, unknown> {
425
- const { contentStyle } = selectScrollIntrinsics(false, undefined);
426
- return { style: contentStyle, collapsable: false };
427
- }
412
+ // Nothing left to override: `registerScrollViewBehavior()` (`../../register.ts`) now owns the
413
+ // content node, `scrollViewBaseStyle`/`nestedScrollEnabled` defaults, and the sticky seam for
414
+ // this SAME `scroll-view` tag. This class used to build its own `<scroll-content>` child by
415
+ // hand — deleted 2026-09-11, since that made TWO owners of the content node the moment the
416
+ // behavior registered on this tag: the engine's own `buildStructure` and this template both
417
+ // creating one. `<ng-content>` now lands directly under the owner and the behavior redirects it
418
+ // into the content node it builds, same as every other `<scroll-view>` in the app.
428
419
  }
429
420
 
430
421
  // List components are already explicit AOT-compiled Angular components with their own full input
@@ -441,12 +432,16 @@ export const AnimatedSectionList = SectionList;
441
432
  export function createAnimatedComponent(base: unknown): Type<unknown> {
442
433
  if (base === View) return AnimatedView;
443
434
  if (base === Text) return AnimatedText;
444
- if (base === Image) return AnimatedImage;
445
- if (base === ScrollView) return AnimatedScrollView;
435
+ // The TAG, not a component identity: `ScrollView`/`Image` are gone as classes now
436
+ // (`components/scroll-view-props.ts`, `modules/image`), so there is no class to compare
437
+ // against — the same move React made (`createAnimatedComponent('image')`). A tag string is also
438
+ // what portable app code can pass on every adapter.
439
+ if (base === SCROLL_VIEW_TAG) return AnimatedScrollView;
440
+ if (base === IMAGE_TAG) return AnimatedImage;
446
441
  throw new Error(
447
442
  'createAnimatedComponent: Angular cannot synthesize a component at runtime (no JIT compiler ' +
448
443
  'under AOT/Metro). Author an explicit standalone @Component extending AnimatedComponentBase ' +
449
- 'instead. The built-in primitives View / Text / Image / ScrollView map to ' +
450
- 'AnimatedView / AnimatedText / AnimatedImage / AnimatedScrollView.',
444
+ "instead. The built-in primitives View / Text and the tags 'scroll-view' / 'image' map to " +
445
+ 'AnimatedView / AnimatedText / AnimatedScrollView / AnimatedImage.',
451
446
  );
452
447
  }
@@ -55,7 +55,7 @@ export {
55
55
  // getter only exists to defer a RUNTIME createAnimatedComponent(ScrollView) past module init so
56
56
  // it cannot read ScrollView inside its own TDZ during an import cycle. AnimatedScrollView here is
57
57
  // a statically-declared component that never reads the ScrollView class at init (it targets the
58
- // `symbiote-scroll-view` host by string), so there is no cycle to defer.
58
+ // `scroll-view` host by string), so there is no cycle to defer.
59
59
  //
60
60
  // FlatList / SectionList are exposed as explicit AOT-safe list entries now that the Angular list
61
61
  // components exist. Angular still cannot synthesize arbitrary animated wrappers at runtime; custom
@@ -0,0 +1,18 @@
1
+ // RN's Image STATICS — `getSize`, `getSizeWithHeaders`, `prefetch`, `abortPrefetch`, `queryCache`,
2
+ // `resolveAssetSource`.
3
+ //
4
+ // IN `modules/`, NOT `components/`. The primitive is the `<image>` tag matched by `ImageElement`,
5
+ // and its fold runs in the engine (`behaviors/image.ts`); what is left under the name `Image` is an
6
+ // imperative API with no view, which is what this bucket is for
7
+ // (<adapter_src_follows_framework_idioms>). Sitting in `components/` was what made a statics object
8
+ // read as a component.
9
+ //
10
+ // A NAMESPACE OBJECT rather than six bare functions, deliberately: `Alert`, `Share`, `Linking` and
11
+ // `AppState` all take that shape, and an app porting from RN writes `Image.getSize(...)` verbatim.
12
+ //
13
+ // Shared verbatim via `imageStatics`, the same source every other adapter's Image uses.
14
+ import { imageStatics, type IImageStatics } from '@symbiote-native/components';
15
+
16
+ export type { IImageStatics };
17
+
18
+ export const Image: IImageStatics = imageStatics;
@@ -24,7 +24,7 @@ export {
24
24
  } from './shared';
25
25
 
26
26
  @Component({
27
- selector: 'symbiote-view, View',
27
+ selector: 'view',
28
28
  standalone: true,
29
29
  template: '<ng-content></ng-content>',
30
30
  })
@@ -41,7 +41,7 @@ export class ViewHost extends SymbiotePrimitiveHost {}
41
41
  * 'tail' would silently overwrite an explicit `ellipsizeMode="clip"`.
42
42
  */
43
43
  @Component({
44
- selector: 'symbiote-text, Text',
44
+ selector: 'text',
45
45
  standalone: true,
46
46
  template: '<ng-content></ng-content>',
47
47
  })
@@ -73,35 +73,35 @@ export class TextHost extends SymbiotePrimitiveHost implements OnInit {
73
73
  }
74
74
 
75
75
  @Component({
76
- selector: 'symbiote-image',
76
+ selector: 'image',
77
77
  standalone: true,
78
78
  template: '<ng-content></ng-content>',
79
79
  })
80
80
  export class ImageHost extends SymbiotePrimitiveHost {}
81
81
 
82
82
  @Component({
83
- selector: 'symbiote-scroll-view',
83
+ selector: 'scroll-view',
84
84
  standalone: true,
85
85
  template: '<ng-content></ng-content>',
86
86
  })
87
87
  export class ScrollViewHost extends SymbiotePrimitiveHost {}
88
88
 
89
89
  @Component({
90
- selector: 'symbiote-scroll-content',
90
+ selector: 'scroll-content',
91
91
  standalone: true,
92
92
  template: '<ng-content></ng-content>',
93
93
  })
94
94
  export class ScrollContentView extends SymbiotePrimitiveHost {}
95
95
 
96
96
  @Component({
97
- selector: 'symbiote-horizontal-scroll-view',
97
+ selector: 'horizontal-scroll-view',
98
98
  standalone: true,
99
99
  template: '<ng-content></ng-content>',
100
100
  })
101
101
  export class HorizontalScrollView extends SymbiotePrimitiveHost {}
102
102
 
103
103
  @Component({
104
- selector: 'symbiote-horizontal-scroll-content',
104
+ selector: 'horizontal-scroll-content',
105
105
  standalone: true,
106
106
  template: '<ng-content></ng-content>',
107
107
  })
@@ -111,35 +111,35 @@ export class HorizontalScrollContentView extends SymbiotePrimitiveHost {}
111
111
  // Angular template renders them: this adapter has no lowering transform. The `-managed` pair below
112
112
  // is what its own TextInput renders.
113
113
  @Component({
114
- selector: 'symbiote-text-input',
114
+ selector: 'text-input',
115
115
  standalone: true,
116
116
  template: '<ng-content></ng-content>',
117
117
  })
118
118
  export class TextInputHost extends SymbiotePrimitiveHost {}
119
119
 
120
120
  @Component({
121
- selector: 'symbiote-text-input-multiline',
121
+ selector: 'text-input-multiline',
122
122
  standalone: true,
123
123
  template: '<ng-content></ng-content>',
124
124
  })
125
125
  export class MultilineTextInputHost extends SymbiotePrimitiveHost {}
126
126
 
127
127
  @Component({
128
- selector: 'symbiote-text-input-managed',
128
+ selector: 'text-input-managed',
129
129
  standalone: true,
130
130
  template: '<ng-content></ng-content>',
131
131
  })
132
132
  export class ManagedTextInputHost extends SymbiotePrimitiveHost {}
133
133
 
134
134
  @Component({
135
- selector: 'symbiote-text-input-multiline-managed',
135
+ selector: 'text-input-multiline-managed',
136
136
  standalone: true,
137
137
  template: '<ng-content></ng-content>',
138
138
  })
139
139
  export class ManagedMultilineTextInputHost extends SymbiotePrimitiveHost {}
140
140
 
141
141
  @Component({
142
- selector: 'symbiote-switch',
142
+ selector: 'switch',
143
143
  standalone: true,
144
144
  template: '<ng-content></ng-content>',
145
145
  })
@@ -149,42 +149,46 @@ export class SwitchHost extends SymbiotePrimitiveHost {}
149
149
  // behavior does not carry. See `component-names/shared.ts` for why the wrapper may not share the
150
150
  // lowered tag; mirrors `ManagedTextInputHost` above.
151
151
  @Component({
152
- selector: 'symbiote-switch-managed',
152
+ selector: 'switch-managed',
153
153
  standalone: true,
154
154
  template: '<ng-content></ng-content>',
155
155
  })
156
156
  export class ManagedSwitchHost extends SymbiotePrimitiveHost {}
157
157
 
158
+ // The centering RCTView RN wraps the spinner in (ActivityIndicator.js:112), not the spinner —
159
+ // which is what this tag resolved to until 2026-09-09. The native view moved to
160
+ // `activity-indicator-spinner`, built by the engine's ActivityIndicator behavior, so nothing writes
161
+ // it in a template and it needs no host here.
158
162
  @Component({
159
- selector: 'symbiote-activity-indicator',
163
+ selector: 'activity-indicator',
160
164
  standalone: true,
161
165
  template: '<ng-content></ng-content>',
162
166
  })
163
167
  export class ActivityIndicatorHost extends SymbiotePrimitiveHost {}
164
168
 
165
169
  @Component({
166
- selector: 'symbiote-safe-area-view',
170
+ selector: 'safe-area-view',
167
171
  standalone: true,
168
172
  template: '<ng-content></ng-content>',
169
173
  })
170
174
  export class SafeAreaViewHost extends SymbiotePrimitiveHost {}
171
175
 
172
176
  @Component({
173
- selector: 'symbiote-modal',
177
+ selector: 'modal',
174
178
  standalone: true,
175
179
  template: '<ng-content></ng-content>',
176
180
  })
177
181
  export class ModalHost extends SymbiotePrimitiveHost {}
178
182
 
179
183
  @Component({
180
- selector: 'symbiote-refresh-control',
184
+ selector: 'refresh-control',
181
185
  standalone: true,
182
186
  template: '<ng-content></ng-content>',
183
187
  })
184
188
  export class RefreshControlHost extends SymbiotePrimitiveHost {}
185
189
 
186
190
  @Component({
187
- selector: 'symbiote-input-accessory-view',
191
+ selector: 'input-accessory-view',
188
192
  standalone: true,
189
193
  template: '<ng-content></ng-content>',
190
194
  })
@@ -10,6 +10,7 @@ import {
10
10
  type SimpleChanges,
11
11
  } from '@angular/core';
12
12
  import { countAngular } from '../diagnostics';
13
+ import { createCallbackWrapper } from '../change-detection-flush';
13
14
  import {
14
15
  flattenStyle,
15
16
  isSymbioteNode,
@@ -108,18 +109,6 @@ export function stableAnchorStyle(
108
109
  : next;
109
110
  }
110
111
 
111
- const ON_PREFIX = /^on[A-Z]/;
112
-
113
- // The one `onX` that fires per FRAME rather than per gesture, and with sticky headers RN pins
114
- // `scrollEventThrottle` to 1 (ScrollView.js), so 60 times a second. Since markForCheck walks
115
- // RefreshView|Dirty to the ROOT, wrapping it cost one full ancestor-screen template execution per
116
- // frame - the canary's ~37fps scroll (components/virtualized-list/scroll-cost.test.ts).
117
- //
118
- // Nothing lost its refresh: VirtualizedList marks itself when the window moves, sticky rides the
119
- // native driver, and a caller's own handler is typically an Animated.event touching no Angular
120
- // state. Drag/momentum begin/end stay wrapped - once per gesture is not a hot path.
121
- const PER_FRAME_CALLBACK = 'onScroll';
122
-
123
112
  /**
124
113
  * Restores the OnPush dirty-marking Angular skips for a `[style]` binding that a directive
125
114
  * declares as an input. Attach it to any component declaring a `style` input:
@@ -194,7 +183,7 @@ function isAnchorStyleUnchanged(previous: unknown, next: unknown): boolean {
194
183
  }
195
184
 
196
185
  /**
197
- * Base for all Angular primitive host components (`symbiote-view`, `symbiote-text`, ...).
186
+ * Base for all Angular primitive host components (`view`, `text`, ...).
198
187
  * RN's `StyleProp` can be an object, an array, nested arrays, falsy entries, and Animated
199
188
  * values. Angular's raw `[style]` binding on a custom element routes through the CSS style
200
189
  * engine (`setStyle` per key), which crashes on numeric array keys. By declaring `style` as
@@ -265,20 +254,15 @@ export class SymbioteHostPropsDirective {
265
254
  // The directive lives in its HOST component's template, so its injected ChangeDetectorRef IS
266
255
  // that component's own view detector — the one thing that can refresh it (see wrapCallback).
267
256
  private readonly cdr = inject(ChangeDetectorRef);
268
- // One wrapper per ORIGINAL handler, for the lifetime of this directive. Without it every push
269
- // of the props bag allocated a brand-new closure for every `onX` key, and the engine stored it
270
- // as a new listener (`setEventListener` allocates its own closure on top) - per key, per push,
271
- // on a path a scroll frame reaches 60 times a second. The wrapper body closes over nothing but
272
- // `value` and this instance's `cdr`, so the handler alone is a complete cache key; `key` is
273
- // read only by the ON_PREFIX guard above and never enters the wrapper.
274
- //
275
- // Reference stability is the point, not just the allocation: a fresh function on every push
276
- // defeats every downstream identity check on the bag, so nothing upstream could ever conclude
277
- // "this bag is unchanged" while a callback prop was in it.
278
- private readonly wrappers = new WeakMap<
279
- object,
280
- (...args: unknown[]) => unknown
281
- >();
257
+ // A flat-bag `onX` callback (responder negotiation, onLongPress, …) is invoked by the engine's
258
+ // event dispatch a plain JS call, entirely outside Angular so nothing dirties the view that
259
+ // binds its result: the "pan does nothing" bug. Shared with the TAG path's `SymbioteElement`,
260
+ // which has the identical deficit; the mechanism, the `onScroll` exemption and why the wrapper
261
+ // is cached per original handler live in `createCallbackWrapper`.
262
+ private readonly wrapCallback = createCallbackWrapper(
263
+ this.cdr,
264
+ this.elementRef.nativeElement,
265
+ );
282
266
 
283
267
  // Values pushed by the previous bag, so a push writes only what actually moved. Copied rather
284
268
  // than aliased: a component is free to hand back the same object with mutated fields.
@@ -330,21 +314,4 @@ export class SymbioteHostPropsDirective {
330
314
  // (`_cdRefInjectingView`, i.e. the host component), unlike `detectChanges()` which acts on a
331
315
  // resolved `_lView` that is NOT the host component here. The Angular twin of what React/Vue get
332
316
  // for free (setState / proxy reactivity).
333
- private wrapCallback(key: string, value: unknown): unknown {
334
- if (!ON_PREFIX.test(key) || typeof value !== 'function') return value;
335
- if (key === PER_FRAME_CALLBACK) return value;
336
- const cached = this.wrappers.get(value);
337
- if (cached !== undefined) return cached;
338
- // Reflect.apply rather than calling a cast-to-signature local: `typeof value === 'function'`
339
- // narrows to `Function`, which has no call signature TypeScript will accept, and this repo
340
- // does not use `as` to paper over that. `undefined` as the receiver preserves the previous
341
- // unbound call.
342
- const wrapper = (...args: unknown[]): unknown => {
343
- const result: unknown = Reflect.apply(value, undefined, args);
344
- this.cdr.markForCheck();
345
- return result;
346
- };
347
- this.wrappers.set(value, wrapper);
348
- return wrapper;
349
- }
350
317
  }
package/src/register.ts CHANGED
@@ -1,43 +1,15 @@
1
1
  // Side-effect ONLY. Exports nothing, and must never be re-exported from the barrel.
2
2
  //
3
- // A lowered `<symbiote-pressable>`/`<symbiote-text-input>` is a bare custom element with no
4
- // component behind it, so the press machine and the controlled-input machine have to live on the
5
- // engine node itself reached by `registerPressableBehavior()`/`registerTextInputBehavior()`
6
- // having run before the first node of that tag is created.
3
+ // The fourteen calls live once, in `@symbiote-native/components/register` they were identical in
4
+ // all five adapters, and each copy carried a migration ledger justifying a call against a wrapper
5
+ // that no longer exists. What is per-adapter is only the SPELLING of the import below: every
6
+ // `*-tag.test.*` here reaches the registry through `import './register';`, and this adapter's own
7
+ // barrel guard pins that line.
7
8
  //
8
- // Metro turns on `inlineRequires` for PRODUCTION only, moving a `require()` down to the first place
9
- // its binding is used as a VALUE; a barrel's `export { X } from './x'` compiles to a lazy getter,
10
- // so a module reached only that way never evaluates unless something names it as a value — silent
11
- // in Release builds alone, invisible to tsc, to vitest, and to grepping the bundle (see CLAUDE.md,
12
- // "Never make correctness depend on a module's load-time side effect"). A bare
13
- // `import './register';` placed NEXT TO a re-export of the same specifier does not help either —
14
- // Babel merges the two into one dependency and the merged one stays lazy. This is the shape that
15
- // survives both traps, matching `adapters/{vue,svelte,solid}/src/register.ts` and
16
- // `packages/slider/src/{react,vue,svelte,angular}/index.ts`.
17
- import {
18
- registerImageBehavior,
19
- registerInputAccessoryViewBehavior,
20
- registerPressableBehavior,
21
- registerSwitchBehavior,
22
- registerTextInputBehavior,
23
- } from '@symbiote-native/components';
24
-
25
- registerPressableBehavior();
26
- // Only the LOWERED tags carry this — the wrapper renders `symbiote-text-input-managed` /
27
- // `symbiote-text-input-multiline-managed` and keeps running its own lifecycle. One owner per node;
28
- // see `component-names/shared.ts`.
29
- registerTextInputBehavior();
30
- // Same reason as TextInput: the wrapper renders `symbiote-switch-managed` and runs its own
31
- // lastNativeReport/snap-back lifecycle, so the engine's copy attaches only to the bare tag.
32
- registerSwitchBehavior();
33
-
34
- // Image owns no runtime — its behavior is a prop FOLD and nothing else, and it is registered on the
35
- // same `symbiote-image` the wrapper already emits rather than on a `-managed` twin. That is safe
36
- // only because the mapping is idempotent, which `core/components/src/behaviors/image.test.ts`
37
- // asserts rather than assumes; a wrapper-built node simply folds a second time and nothing moves.
38
- registerImageBehavior();
39
-
40
- // Fold-only, and it shares the wrapper's tag for the same reason Image does: the mapping has no
41
- // aliasing at all, so a wrapper-built node folding a second time moves nothing.
42
- // `core/components/src/behaviors/input-accessory-view.test.ts` asserts that rather than assuming it.
43
- registerInputAccessoryViewBehavior();
9
+ // WHY A SIDE-EFFECT SUBPATH AND NOT A BARREL EXPORT. Metro turns on `inlineRequires` for PRODUCTION
10
+ // only: it moves a `require` down to the first place its binding is used as a VALUE, and
11
+ // `export { X } from './x'` compiles to a lazy getter. A registration reached that way never
12
+ // evaluates in a Release build invisible to tsc, to vitest, and to grepping the bundle. A bare
13
+ // `import` placed NEXT TO a re-export of the same specifier does not help either: Babel merges the
14
+ // two into one dependency and the merged dependency stays lazy.
15
+ import '@symbiote-native/components/register';