@symbiote-native/svelte 0.3.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 (107) hide show
  1. package/README.md +8 -10
  2. package/build/components/{activity-indicator/activity-indicator-props.d.ts → activity-indicator-props.d.ts} +1 -1
  3. package/build/components/flat-list/index.svelte +4 -4
  4. package/build/components/{image-background/image-background-props.d.ts → image-background-props.d.ts} +1 -1
  5. package/build/components/index.d.ts +6 -24
  6. package/build/components/index.js +0 -18
  7. package/build/components/keyboard-avoiding-view/index.svelte +6 -6
  8. package/build/components/modal/index.svelte +7 -7
  9. package/build/components/pressable/pressable-props.d.ts +1 -0
  10. package/build/components/refresh-control-props.d.ts +1 -0
  11. package/build/components/safe-area-view-props.d.ts +1 -0
  12. package/build/components/text-props.d.ts +1 -0
  13. package/build/components/virtualized-list/index.svelte +71 -250
  14. package/build/dom-shim/canonical-prop-names.d.ts +2 -0
  15. package/build/dom-shim/canonical-prop-names.js +320 -0
  16. package/build/dom-shim/document.js +1 -1
  17. package/build/dom-shim/element.d.ts +17 -5
  18. package/build/dom-shim/element.js +258 -40
  19. package/build/dom-shim/fold-host-bag.d.ts +1 -0
  20. package/build/dom-shim/fold-host-bag.js +7 -0
  21. package/build/dom-shim/patch-globals.js +35 -7
  22. package/build/dom-shim/shim-node.js +11 -0
  23. package/build/dom-shim/style-cache.d.ts +1 -0
  24. package/build/dom-shim/style-cache.js +27 -0
  25. package/build/dom-shim/text.js +2 -2
  26. package/build/host-instance.d.ts +2 -3
  27. package/build/host-instance.js +9 -4
  28. package/build/index.d.ts +8 -4
  29. package/build/index.js +21 -5
  30. package/build/intrinsic-elements.d.ts +63 -0
  31. package/build/modules/animated/index.d.ts +7 -27
  32. package/build/modules/animated/index.js +24 -34
  33. package/build/modules/image/index.d.ts +3 -0
  34. package/build/modules/image/index.js +15 -0
  35. package/build/preprocessor/scoped-styles.js +1 -1
  36. package/build/register.d.ts +1 -0
  37. package/build/register.js +15 -0
  38. package/build/root-element.js +1 -1
  39. package/metro-css-parser.cjs +2 -1
  40. package/metro-svelte-transformer.cjs +30 -12
  41. package/package.json +19 -9
  42. package/build/components/RefreshControl.svelte +0 -40
  43. package/build/components/RefreshControl.svelte.d.ts +0 -5
  44. package/build/components/SafeAreaView.svelte +0 -34
  45. package/build/components/SafeAreaView.svelte.d.ts +0 -5
  46. package/build/components/Text.svelte +0 -34
  47. package/build/components/Text.svelte.d.ts +0 -5
  48. package/build/components/View.svelte +0 -43
  49. package/build/components/View.svelte.d.ts +0 -5
  50. package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +0 -3
  51. package/build/components/activity-indicator/activity-indicator-platform.android.js +0 -5
  52. package/build/components/activity-indicator/activity-indicator-platform.d.ts +0 -1
  53. package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +0 -3
  54. package/build/components/activity-indicator/activity-indicator-platform.ios.js +0 -6
  55. package/build/components/activity-indicator/activity-indicator-platform.js +0 -5
  56. package/build/components/activity-indicator/index.svelte +0 -63
  57. package/build/components/activity-indicator/index.svelte.d.ts +0 -5
  58. package/build/components/button.svelte +0 -76
  59. package/build/components/button.svelte.d.ts +0 -5
  60. package/build/components/image/image-logic.d.ts +0 -2
  61. package/build/components/image/image-logic.js +0 -135
  62. package/build/components/image/index.d.ts +0 -3
  63. package/build/components/image/index.js +0 -10
  64. package/build/components/image/index.svelte +0 -66
  65. package/build/components/image/index.svelte.d.ts +0 -5
  66. package/build/components/image-background/index.svelte +0 -87
  67. package/build/components/image-background/index.svelte.d.ts +0 -5
  68. package/build/components/input-accessory-view/index.svelte +0 -51
  69. package/build/components/input-accessory-view/index.svelte.d.ts +0 -5
  70. package/build/components/pressable/index.svelte +0 -167
  71. package/build/components/pressable/index.svelte.d.ts +0 -5
  72. package/build/components/scroll-view/index.svelte +0 -331
  73. package/build/components/scroll-view/index.svelte.d.ts +0 -18
  74. package/build/components/scroll-view/scroll-view-platform-types.d.ts +0 -3
  75. package/build/components/scroll-view/scroll-view-platform.android.d.ts +0 -3
  76. package/build/components/scroll-view/scroll-view-platform.android.js +0 -3
  77. package/build/components/scroll-view/scroll-view-platform.d.ts +0 -1
  78. package/build/components/scroll-view/scroll-view-platform.ios.d.ts +0 -3
  79. package/build/components/scroll-view/scroll-view-platform.ios.js +0 -3
  80. package/build/components/scroll-view/scroll-view-platform.js +0 -4
  81. package/build/components/scroll-view/scroll-view-sticky-context.d.ts +0 -7
  82. package/build/components/scroll-view/scroll-view-sticky-context.js +0 -1
  83. package/build/components/scroll-view/sticky-header-props.d.ts +0 -10
  84. package/build/components/scroll-view/sticky-header-props.js +0 -1
  85. package/build/components/scroll-view/sticky-header.svelte +0 -287
  86. package/build/components/scroll-view/sticky-header.svelte.d.ts +0 -5
  87. package/build/components/switch/index.svelte +0 -134
  88. package/build/components/switch/index.svelte.d.ts +0 -5
  89. package/build/components/text-input/index.svelte +0 -316
  90. package/build/components/text-input/index.svelte.d.ts +0 -12
  91. package/build/components/touchable-highlight/index.svelte +0 -138
  92. package/build/components/touchable-highlight/index.svelte.d.ts +0 -5
  93. package/build/components/touchable-native-feedback/index.d.ts +0 -9
  94. package/build/components/touchable-native-feedback/index.js +0 -14
  95. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +0 -49
  96. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +0 -5
  97. package/build/components/touchable-opacity/index.svelte +0 -180
  98. package/build/components/touchable-opacity/index.svelte.d.ts +0 -5
  99. package/build/components/touchable-without-feedback/index.svelte +0 -74
  100. package/build/components/touchable-without-feedback/index.svelte.d.ts +0 -5
  101. package/build/modules/animated/animated-component-props.d.ts +0 -7
  102. package/build/modules/animated/animated-component-props.js +0 -1
  103. package/build/modules/animated/create-animated-component.d.ts +0 -5
  104. package/build/modules/animated/create-animated-component.js +0 -131
  105. /package/build/components/{activity-indicator/activity-indicator-props.js → activity-indicator-props.js} +0 -0
  106. /package/build/components/{image-background/image-background-props.js → image-background-props.js} +0 -0
  107. /package/build/{components/scroll-view/scroll-view-platform-types.js → intrinsic-elements.js} +0 -0
@@ -1,19 +1,38 @@
1
- // The workhorse of the shim. Every Symbiote intrinsic (`symbiote-view`, `symbiote-text`, …)
2
- // is a hyphenated tag, so Svelte compiles it down the CUSTOM-ELEMENT codegen path (§3g):
3
- // attributes go through `set_custom_element_data`, which stringifies scalars and hard-excludes
4
- // `style`. The resolution (§3g(c)) is a single object-bag PROPERTY, not a spread of
5
- // attributes — an object always satisfies `value && typeof value === 'object'`, so it lands as
6
- // a real property SET (`node.p = bag`), untouched by stringification. `p`'s setter (below)
7
- // unpacks the bag into `routeProp`, the same entry point React's flat bag and Vue's patchProp
8
- // already use.
1
+ // The workhorse of the shim, and it has FIVE doors, not one — svelte decides which by tag name,
2
+ // by compiled namespace and by whether the attribute starts with `on`, so an element cannot
3
+ // choose its own:
4
+ //
5
+ // set_custom_element_data hyphenated tags (`text-input`, …). Stringifies a scalar; an OBJECT
6
+ // lands as a real property set, which is what makes `p={{…}}` work.
7
+ // set_attribute every other tag, and the hyphenated ones for a non-string value.
8
+ // set_class -> `setAttribute('class')` in the svg namespace, `dom.className`
9
+ // in html. Both spellings land in the same bag key.
10
+ // set_style -> `dom.style.cssText`, STRINGIFIED, plus the authored value under
11
+ // a private Symbol. See `style-cache.ts`.
12
+ // addEventListener any `on<Name>` attribute, via `$.event()`, with the `on` stripped.
13
+ //
14
+ // All five converge on one folded bag and out through `routeProp`, the entry point React's flat
15
+ // bag and Vue's patchProp already use — because only `routeProp` knows which `on*` names the
16
+ // node's ViewConfig declares as events and which are ordinary callback props a behavior reads.
17
+ // `p` is ours to name (§3g(c)); a component forwarding a whole bag emits it, app code need not.
18
+ //
19
+ // They converge, they do not merge: `p` owns its key SET, since a key dropped from the bag has to
20
+ // reset the prop, while the other four own a key each. Hence `doorBag` / `pBag` below.
9
21
  //
10
22
  // A per-key diff in the setter is MANDATORY (§3g(c)), not an optimization: Svelte's
11
23
  // `set_custom_element_data` has no early-out guard and runs on every effect re-fire, so
12
24
  // without the diff every prop gets rewritten whenever any one of them changes.
13
- import { createElement, dlog, isDebug, routeProp, setEventListener, toPublicInstance, } from '@symbiote-native/engine';
25
+ import { createElement, dlog, routeProp, toPublicInstance, } from '@symbiote-native/engine';
14
26
  import { descriptorFor } from '@symbiote-native/components';
15
27
  import { normalizeSvelteClass } from '../class-value.js';
28
+ import { CANONICAL_BY_LOWER, CANONICAL_PROP_NAMES, } from './canonical-prop-names.js';
29
+ import { foldHostBag } from './fold-host-bag.js';
16
30
  import { ShimNode } from './shim-node.js';
31
+ import { discoverStyleCacheKey } from './style-cache.js';
32
+ // The bag a not-yet-live element is diffed against in onMadeLive. A module constant rather than a
33
+ // fresh `{}` per node: it is only ever read, and the identity lets applyBagDiff skip its second
34
+ // pass outright on the create path.
35
+ const EMPTY_BAG = Object.freeze({});
17
36
  // Diagnostic-only: a process-wide sequence so every `set p` call across every shim element in a
18
37
  // log dump is individually orderable against AnimatedProps reconcile#N / AnimatedView reduced#N
19
38
  // / hostShim identity-change#N, to see which one is driving which.
@@ -21,12 +40,32 @@ let globalSetPSeq = 0;
21
40
  // `on<Name>` handlers ride inside the prop bag (idiomatic Svelte 5 callback props — see the
22
41
  // skill's §3g(c) "most of §5 collapses" note) — they are not addEventListener-style DOM
23
42
  // listeners, so they are diffed and routed exactly like any other bag key, through routeProp.
24
- export class ShimElement extends ShimNode {
43
+ // An empty layer that exists to be what `globalThis.Element` points at, and it is load-bearing.
44
+ // Svelte's `get_setters` (internal/client/dom/elements/attributes.js) walks from the ELEMENT
45
+ // INSTANCE up and STOPS when it reaches `Element.prototype`. While `Element` was `ShimElement`
46
+ // itself, the first prototype step was already the stop, so the walk collected nothing and `p` —
47
+ // which lives on `ShimElement.prototype`, one step past it — was invisible. `set_attributes` then
48
+ // fell through to `setAttribute`, which writes an inert Map, and every prop vanished with nothing
49
+ // red. The rule is therefore that `Element` must be a proper ANCESTOR of the class owning the
50
+ // setters, never that class: `.claude/rules/svelte-shim-element-global-must-be-an-ancestor.md`.
51
+ export class ShimElementBase extends ShimNode {
52
+ }
53
+ export class ShimElement extends ShimElementBase {
25
54
  tagName;
26
55
  namespaceURI;
27
- attributes = new Map();
28
- domListeners = new Map();
29
- lastBag = {};
56
+ // LAZY, and that is a measured decision, not a style one. A primitive carrying its whole prop
57
+ // surface in the `p` bag never writes this on the create path — but an eager field allocated a
58
+ // Map per element regardless, 9 002 of them on a 1 000-row create, in a window where GC is the
59
+ // largest single bucket (29%).
60
+ attributes = undefined;
61
+ // The four attribute doors write `doorBag`, `set p` writes `pBag`, `lastBag` is the folded merge.
62
+ // Held in ONE object, `set p` deleted the `class` `from_tree` had written at clone time, so
63
+ // `<view p={handlers} class="x">` committed with no style at all and nothing was red. Unreachable
64
+ // while an element used one door, which every lowered tag and adapter component does.
65
+ // Both LAZY: an element touching one door still allocates one object.
66
+ doorBag = undefined;
67
+ pBag = undefined;
68
+ lastBag = EMPTY_BAG;
30
69
  constructor(tagName, namespaceURI) {
31
70
  super();
32
71
  this.tagName = tagName;
@@ -35,70 +74,235 @@ export class ShimElement extends ShimNode {
35
74
  get nodeName() {
36
75
  return this.tagName;
37
76
  }
77
+ // `set_style` writes `dom.style.cssText`, so a shim with no `.style` THROWS rather than
78
+ // no-opping — a `style` attribute on a bare tag crashed the mount. LAZY, for the reason
79
+ // `.claude/rules/svelte-shim-is-the-per-node-create-path.md` records about the two Maps below it:
80
+ // an eager field is one object per element, ~9 000 per create, in the window where GC is the
81
+ // largest bucket. Only `set_style` touches this, and no lowered element takes that path.
82
+ styleSlot = undefined;
83
+ get style() {
84
+ return (this.styleSlot ??= { cssText: '' });
85
+ }
86
+ // The real `style` seam, reached from the Symbol accessor below rather than from `.style`: that
87
+ // slot only ever sees `String(value)`. Not part of the DOM surface.
88
+ writeStyleValue(value) {
89
+ this.writeBagKey('style', value);
90
+ }
91
+ // `set_class`'s html-namespace exit. Without the setter it lands as a plain JS property, and the
92
+ // failure is half-silent: a static `class="card"` rides the template through `setAttribute` and
93
+ // keeps working, so only the dynamic spelling stops styling.
94
+ get className() {
95
+ return this.lastBag.class;
96
+ }
97
+ set className(value) {
98
+ this.writeBagKey('class', value);
99
+ }
38
100
  // The single object-bag prop. The literal name is ours to choose (§3g(c)) — the adapter's
39
- // own View.svelte/Text.svelte/… emit `<symbiote-view p={bag}>`; app code never sees it.
101
+ // own View.svelte/Text.svelte/… emit `<view p={bag}>`; app code never sees it.
40
102
  get p() {
41
103
  return this.lastBag;
42
104
  }
43
105
  set p(bag) {
44
- const next = normalizeBagClasses(bag ?? {});
106
+ // The fold runs HERE rather than at element creation because an alias can arrive on an update
107
+ // (`id` bound to a signal), and it runs before the diff so `lastBag` is always the folded shape
108
+ // — otherwise a seeded default would look like a change on every single set.
109
+ this.pBag = bag;
110
+ const next = this.foldedBag();
45
111
  const prev = this.lastBag;
46
112
  this.lastBag = next;
47
- const changedKeys = diffKeys(prev, next);
48
- dlog(`ShimElement set-p#${++globalSetPSeq} tag=${this.tagName} live=${this.engineNode !== undefined} ` +
49
- `changedKeys=${changedKeys.join(',')}`);
113
+ // THUNK, not a string (see debug.ts's header): this setter runs once per element per create
114
+ // and the changed-key list is diagnostic only. Built eagerly it cost a Set, two key arrays, a
115
+ // join and a template literal on every one of 9 002 elements, with logging off.
116
+ dlog(() => `ShimElement set-p#${++globalSetPSeq} tag=${this.tagName} live=${this.engineNode !== undefined} ` +
117
+ `changedKeys=${diffKeys(prev, next).join(',')}`);
50
118
  if (this.engineNode === undefined)
51
119
  return; // not live yet — onMadeLive() replays `next` in full
52
120
  applyBagDiff(this.engineNode, prev, next);
53
121
  this.surface?.requestCommit();
54
122
  }
123
+ // A BARE tag's props arrive here, one key at a time, and they used to stop here: an inert Map,
124
+ // nothing routed, nothing committed, nothing red. That was survivable only while every host
125
+ // element in an app was produced by the lowering transform, which builds the `p` bag above —
126
+ // and it is exactly what made that transform load-bearing for CORRECTNESS on this adapter
127
+ // alone. Routing the key makes `<view testID="x">` and `<view p={{ testID: 'x' }}>` the same
128
+ // commit, so the transform goes back to being the optimisation it is everywhere else.
129
+ //
130
+ // `value` is `unknown`, not `string`: Svelte's `set_attribute` hands the raw value straight
131
+ // through for a name with no prototype setter, so an object `style` or a number arrives
132
+ // unstringified. The DOM would coerce it; we must not — `routeProp` wants the real value.
133
+ //
134
+ // The Map stays, and is not redundant: `getAttribute` must answer with the name the caller
135
+ // wrote, while the bag has been folded (`id` -> `nativeID`) by the time it is stored.
136
+ // `name` arrives LOWERCASED for every static attribute on every tag, and for a dynamic one on any
137
+ // tag whose name is not also an SVG element — the compiler settles that, so the repair has to
138
+ // happen here. One Map lookup per attribute write, no allocation; a name already canonical (or
139
+ // one we do not know) is handed on untouched. See `canonical-prop-names.ts`.
55
140
  setAttribute(name, value) {
56
- this.attributes.set(name, value);
141
+ (this.attributes ??= new Map()).set(name, value);
142
+ this.writeBagKey(CANONICAL_BY_LOWER.get(name) ?? name, value);
57
143
  }
58
144
  getAttribute(name) {
59
- return this.attributes.get(name) ?? null;
145
+ return this.attributes?.get(name) ?? null;
60
146
  }
61
147
  removeAttribute(name) {
62
- this.attributes.delete(name);
148
+ this.attributes?.delete(name);
149
+ // Same repair as the write, or a removal clears a key nothing ever set.
150
+ this.writeBagKey(CANONICAL_BY_LOWER.get(name) ?? name, undefined);
63
151
  }
152
+ // One key into the same bag `set p` writes, so the three spellings cannot drift into three
153
+ // representations. Folded on every write because the fold is idempotent by construction
154
+ // (`fold-host-bag.ts` states and tests that) — a bare tag has no other pass that could do it.
155
+ //
156
+ // Not `private`: the prototype accessors installed at the bottom of this file are the third
157
+ // writer, and they are defined from module scope because there are ~290 of them.
158
+ writeBagKey(name, value) {
159
+ const door = { ...this.doorBag };
160
+ if (value === undefined)
161
+ delete door[name];
162
+ else
163
+ door[name] = value;
164
+ this.doorBag = door;
165
+ const prev = this.lastBag;
166
+ const next = this.foldedBag();
167
+ this.lastBag = next;
168
+ if (this.engineNode === undefined)
169
+ return; // not live yet — onMadeLive() replays in full
170
+ applyBagDiff(this.engineNode, prev, next);
171
+ this.surface?.requestCommit();
172
+ }
173
+ // A door key WINS over the same name inside `p`: fixed precedence, so the committed value does
174
+ // not depend on which setter svelte called last. Branched to keep a one-door element at one fold.
175
+ foldedBag() {
176
+ const door = this.doorBag;
177
+ const bag = this.pBag;
178
+ if (door === undefined)
179
+ return foldHostBag(this.tagName, normalizeBagClasses(bag ?? {}));
180
+ if (bag === undefined)
181
+ return foldHostBag(this.tagName, normalizeBagClasses(door));
182
+ return foldHostBag(this.tagName, normalizeBagClasses({ ...bag, ...door }));
183
+ }
184
+ // THE FIFTH DOOR, and it converges on the same bag as the other four. Svelte turns EVERY
185
+ // `on<Name>` attribute into `$.event()`, whatever the name means to us — so this receives both
186
+ // `Press`, which the node's ViewConfig declares as an event, and `ValueChange`, which no config
187
+ // declares and which `behaviors/{switch,text-input}.ts` read as `node.props.onValueChange`.
188
+ // Calling `setEventListener` directly answered "event" for both and stashed the second under a
189
+ // name nothing dispatches to: the toggle moved natively and the app's callback never ran.
190
+ // Only `routeProp` knows which is which, so the handler goes back through the bag under the name
191
+ // the author wrote.
64
192
  addEventListener(name, handler) {
65
- this.domListeners.set(name, handler);
66
- if (this.engineNode !== undefined)
67
- setEventListener(this.engineNode, name, handler);
193
+ this.writeBagKey(propNameForEvent(name), handler);
68
194
  }
69
195
  removeEventListener(name) {
70
- this.domListeners.delete(name);
71
- if (this.engineNode !== undefined)
72
- setEventListener(this.engineNode, name, undefined);
196
+ this.writeBagKey(propNameForEvent(name), undefined);
73
197
  }
74
198
  cloneNode(deep) {
75
199
  const clone = new ShimElement(this.tagName, this.namespaceURI);
76
- for (const [key, value] of this.attributes)
77
- clone.attributes.set(key, value);
200
+ if (this.attributes !== undefined)
201
+ clone.attributes = new Map(this.attributes);
202
+ // The bag, not just the Map — `from_tree` builds each template ONCE, writes its static
203
+ // attributes onto that master, and then clones per instance, so without this every attribute
204
+ // an app spells statically (`<view testID="x" class="card">`) is dropped from every instance.
205
+ // A reference copy is safe because `writeBagKey` and `set p` both publish a fresh object.
206
+ // `doorBag` too, or a `set p` on the CLONE re-folds without the master's static attributes.
207
+ clone.doorBag = this.doorBag;
208
+ clone.pBag = this.pBag;
209
+ clone.lastBag = this.lastBag;
78
210
  if (deep === true) {
79
211
  for (const child of this.children)
80
212
  clone.appendChild(child.cloneNode(true));
81
213
  }
82
214
  return clone;
83
215
  }
84
- // toPublicInstance grafts measure/measureInWindow/measureLayout/setNativeProps/focus/blur
85
- // onto the node — the imperative API a `bind:this` host ref hands back, the same augmentation
86
- // Vue's renderer applies at its own commit point (renderer/index.ts) and React gets from
87
- // getPublicInstance. Mutates in place and returns the SAME node, so this is a safe drop-in for
88
- // the previously-bare createElement() call.
216
+ // measure/measureInWindow/measureLayout/setNativeProps/focus/blur — the imperative API a
217
+ // `bind:this` host ref hands back — ride on the engine node's prototype, so toPublicInstance is
218
+ // the identity and this reads the same as Vue's renderer (renderer/index.ts) and React's
219
+ // getPublicInstance.
89
220
  createEngineNode() {
90
221
  const descriptor = descriptorFor(this.tagName);
91
- return toPublicInstance(createElement(descriptor.component, descriptor.isText));
222
+ // The INTRINSIC TAG as the third argument, and this is the only place the tag alphabet still
223
+ // exists: `descriptor.component` is the Fabric view name (`view` -> `RCTView`), so a
224
+ // host-behavior registry keyed by tag can only be reached from here. The argument defaults to
225
+ // `component`, so passing it changes nothing until a behavior is registered.
226
+ return toPublicInstance(createElement(descriptor.component, descriptor.isText, this.tagName));
92
227
  }
228
+ // One replay, because a handler is an ordinary bag key now — it arrives before the node is live
229
+ // exactly like `class` or `style` does, and leaves through the same diff.
93
230
  onMadeLive() {
94
231
  const engineNode = this.engineNode;
95
232
  if (engineNode === undefined)
96
233
  return;
97
- applyBagDiff(engineNode, {}, this.lastBag);
98
- for (const [name, handler] of this.domListeners)
99
- setEventListener(engineNode, name, handler);
234
+ applyBagDiff(engineNode, EMPTY_BAG, this.lastBag);
100
235
  }
101
236
  }
237
+ // Svelte hands `addEventListener` the authored name minus its `on`, so `onPress` arrives as
238
+ // `Press`. Putting it back is what lets the handler re-enter through the ordinary prop bag, where
239
+ // `routeProp` decides event-vs-prop from the node's ViewConfig — the one decision this layer
240
+ // cannot make. A name that was already lowercase (`onclick` -> `click`) rebuilds as `onclick`,
241
+ // which no config declares and which therefore lands inertly in `node.props`, exactly as a DOM
242
+ // event name should on a native host.
243
+ //
244
+ // A charCode test rather than `name[0] !== name[0].toUpperCase()`: this runs per handler per
245
+ // element on the create path, and the comparison spelling allocates two strings to answer it.
246
+ function propNameForEvent(name) {
247
+ const first = name.charCodeAt(0);
248
+ if (first >= 65 /* A */ && first <= 90 /* Z */)
249
+ return `on${name}`;
250
+ return `on${name[0].toUpperCase()}${name.slice(1)}`;
251
+ }
252
+ // The seam `set_style` writes the authored value to. Discovered once, at module load, because
253
+ // svelte's key is a module-private Symbol — see `style-cache.ts` for the whole mechanism.
254
+ Object.defineProperty(ShimElement.prototype, discoverStyleCacheKey(), {
255
+ get() {
256
+ return this.p.style;
257
+ },
258
+ set(value) {
259
+ this.writeStyleValue(value);
260
+ },
261
+ });
262
+ // The setters `get_setters` has to find, so `set_custom_element_data` stops guessing. Its
263
+ // heuristic branch — reached whenever no real setter exists for the name — hands a scalar on as
264
+ // `String(value)` and assigns an object to a plain JS property the shim never reads, so a
265
+ // hyphenated tag committed `"false"` for `multiline={false}` and nothing at all for an object.
266
+ // With a setter present the raw value lands in the same folded bag `set p` and `setAttribute`
267
+ // write, and the three doors agree.
268
+ //
269
+ // ONE-TIME, at module load: ~290 defineProperty calls, nothing per node
270
+ // (`svelte-shim-is-the-per-node-create-path.md`). The instance shape does not move — these live on
271
+ // the prototype — so no element pays for them.
272
+ //
273
+ // A name the shim ALREADY owns is skipped rather than overwritten, and it is owned in TWO places
274
+ // that need two different tests. On the PROTOTYPE: `style` is a `{cssText}` slot `set_style`
275
+ // writes through, `className` is `set_class`'s html exit, `p` is the bag itself — an `in` test
276
+ // covers those, and covers a future member of the chain the day it is added.
277
+ //
278
+ // On the INSTANCE is the one that cost a device crash. `children` is a canonical prop name AND
279
+ // ShimNode's own field — the tree itself — and `in` cannot see it, because a class field lives on
280
+ // the instance and the prototype is empty at module load. Installing an accessor over it is
281
+ // harmless under vitest, where `useDefineForClassFields` makes `this.children = []` a
282
+ // `defineProperty` that shadows the accessor, and FATAL under Metro, whose loose class fields
283
+ // compile it to an assignment that the prototype setter swallows — so `this.children` read back
284
+ // `this.p.children`, i.e. undefined, and `makeLive` died iterating it on the app's ROOT node.
285
+ // Every one of the 328 headless tests passed.
286
+ //
287
+ // The probe is a real instance built before the loop, so it answers with whatever fields the
288
+ // ACTIVE transform materialises rather than a list someone has to keep. Its fields carry explicit
289
+ // `= undefined` initializers for the same reason: a bare declaration emits nothing under Metro and
290
+ // the probe would not see it.
291
+ const SHAPE_PROBE = new ShimElement('view');
292
+ for (const name of CANONICAL_PROP_NAMES) {
293
+ if (name in ShimElement.prototype)
294
+ continue;
295
+ if (Object.hasOwn(SHAPE_PROBE, name))
296
+ continue;
297
+ Object.defineProperty(ShimElement.prototype, name, {
298
+ get() {
299
+ return this.p[name];
300
+ },
301
+ set(value) {
302
+ this.writeBagKey(name, value);
303
+ },
304
+ });
305
+ }
102
306
  // Both spellings route through the engine's shared class+style merge (routeProp's
103
307
  // CLASS_PROP_KEYS), so both get the same clsx normalization here.
104
308
  const CLASS_BAG_KEYS = ['class', 'className'];
@@ -120,11 +324,25 @@ function normalizeBagClasses(bag) {
120
324
  }
121
325
  return next;
122
326
  }
327
+ // Diagnostic only — the one caller is the `dlog` thunk above. The allocation-free twin below is
328
+ // what the hot path uses; keep them in step.
123
329
  function diffKeys(prev, next) {
124
330
  const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
125
331
  return [...keys].filter(key => prev[key] !== next[key]);
126
332
  }
333
+ // Deliberately NOT `for (const key of diffKeys(...))`: that materialized a Set, two key arrays,
334
+ // a spread and a filtered array per element. Two direct passes route exactly the same keys —
335
+ // changed-or-added from `next`, then dropped keys from `prev` as `undefined`, which is what
336
+ // `next[key]` evaluated to in the old shape.
127
337
  function applyBagDiff(engineNode, prev, next) {
128
- for (const key of diffKeys(prev, next))
129
- routeProp(engineNode, key, next[key]);
338
+ for (const key of Object.keys(next)) {
339
+ if (prev[key] !== next[key])
340
+ routeProp(engineNode, key, next[key]);
341
+ }
342
+ if (prev === EMPTY_BAG)
343
+ return;
344
+ for (const key of Object.keys(prev)) {
345
+ if (!(key in next))
346
+ routeProp(engineNode, key, undefined);
347
+ }
130
348
  }
@@ -0,0 +1 @@
1
+ export { foldHostBag, FOLD_PLAN_BY_TAG as BY_TAG, type IHostBag, } from '@symbiote-native/components/fold-host-bag';
@@ -0,0 +1,7 @@
1
+ // Moved to `@symbiote-native/components/fold-host-bag` (2026-09-01) and kept here as a re-export.
2
+ //
3
+ // It left this adapter the moment a SECOND adapter needed it: React's wrappers are being replaced
4
+ // by bare intrinsic tags, and a bare tag has no wrapper to fold in — the same "third path" this
5
+ // file was written for, arriving in an adapter that has no lowering transform either. A per-adapter
6
+ // copy of a fold driven by a shared spec is the duplication `<adapters_stay_thin>` exists to stop.
7
+ export { foldHostBag, FOLD_PLAN_BY_TAG as BY_TAG, } from '@symbiote-native/components/fold-host-bag';
@@ -24,7 +24,7 @@
24
24
  //
25
25
  // `customElements` IS patched, unlike the three above — see its own comment below.
26
26
  import { dlog } from '@symbiote-native/engine';
27
- import { ShimElement } from './element.js';
27
+ import { ShimElement, ShimElementBase } from './element.js';
28
28
  import { ShimText } from './text.js';
29
29
  import { ShimComment } from './comment.js';
30
30
  import { ShimDocumentFragment } from './document-fragment.js';
@@ -40,6 +40,7 @@ const PATCHED_KEYS = [
40
40
  'DocumentFragment',
41
41
  'document',
42
42
  'customElements',
43
+ 'HTMLMediaElement',
43
44
  ];
44
45
  // Real DOM's `CustomElementRegistry`. `set_custom_element_data` (svelte's
45
46
  // dom/elements/attributes.js) reads the BARE global `customElements` unconditionally, with no
@@ -48,11 +49,34 @@ const PATCHED_KEYS = [
48
49
  // pipeline (mount-pipeline.smoke.test.ts); `tsc --build` has no way to catch a missing global
49
50
  // a compiled Svelte bundle reads. RN/Hermes has no Custom Elements API of its own to collide
50
51
  // with (unlike `navigator`/`requestAnimationFrame`, verified against .vendors/react-native —
51
- // see the header comment above), so patching this is safe. `get()` always returns `undefined`
52
- // since we never call `customElements.define()`, which is exactly what steers
53
- // `set_custom_element_data` down the object-bag "set as property" branch every `p={bag}` prop
54
- // needs (skill §3g(c)).
55
- const FAKE_CUSTOM_ELEMENT_REGISTRY = { get: () => undefined };
52
+ // see the header comment above), so patching this is safe.
53
+ //
54
+ // `get()` returns a TRUTHY stand-in, and that is the whole reason `set_custom_element_data` can be
55
+ // made lossless. Its condition (attributes.js:245-265) is a ternary, not a fallback:
56
+ //
57
+ // setters_cache.has(nodeName) || !customElements || customElements.get(nodeName)
58
+ // ? get_setters(node).includes(prop) <- a real setter decides; the value passes through
59
+ // : value && typeof value === 'object' <- a HEURISTIC: an object is set as a property and
60
+ // every scalar is handed on as String(value)
61
+ //
62
+ // Returning `undefined` — which it did until 2026-09-07 — selects the heuristic, so
63
+ // `<text-input multiline={false}>` committed the STRING "false" and an object-valued prop with no
64
+ // prototype setter was assigned to a plain JS property and silently lost. Anything truthy steers
65
+ // it to `get_setters`, which reaches `ShimElement.prototype`'s accessors (see
66
+ // `svelte-shim-element-global-must-be-an-ancestor.md` for why that walk terminates where it does).
67
+ // One reader in the whole client runtime, so nothing else is affected.
68
+ const FAKE_CUSTOM_ELEMENT_REGISTRY = {
69
+ get: () => ShimElement,
70
+ };
71
+ // Same class of trap as `customElements` above, found the same way — by running the pipeline
72
+ // rather than by reading. Svelte's `event()` (dom/elements/events.js) ends its teardown check with
73
+ // a BARE `dom instanceof HTMLMediaElement`, no `typeof` guard, so an undeclared global throws
74
+ // ReferenceError instead of yielding `undefined`. It is reached by every `on<Name>={fn}` an app
75
+ // writes on a bare tag, which is why nothing hit it while the lowering transform funnelled every
76
+ // handler through the `p` bag. Nothing of ours ever derives from it, so an empty class is enough:
77
+ // `instanceof` only has to answer false.
78
+ class FakeHTMLMediaElement {
79
+ }
56
80
  let previous;
57
81
  export function patchGlobals() {
58
82
  if (previous !== undefined)
@@ -68,9 +92,12 @@ export function patchGlobals() {
68
92
  DocumentFragment: g.DocumentFragment,
69
93
  document: g.document,
70
94
  customElements: g.customElements,
95
+ HTMLMediaElement: g.HTMLMediaElement,
71
96
  };
72
97
  g.Node = ShimNode;
73
- g.Element = ShimElement;
98
+ // ShimElementBase, NOT ShimElement: `get_setters` stops AT `Element.prototype`, so pointing this
99
+ // at the class that owns `p` hides `p` from every `set_attributes` caller. See element.ts.
100
+ g.Element = ShimElementBase;
74
101
  // Svelte's mandatory paths never distinguish HTMLElement/SVGElement from Element (we have
75
102
  // no `<svg>` primitives), so both alias the same class — real, extensible prototypes either
76
103
  // way, which is all `init_operations()` requires (§3a).
@@ -81,6 +108,7 @@ export function patchGlobals() {
81
108
  g.DocumentFragment = ShimDocumentFragment;
82
109
  g.document = getShimDocument();
83
110
  g.customElements = FAKE_CUSTOM_ELEMENT_REGISTRY;
111
+ g.HTMLMediaElement = FakeHTMLMediaElement;
84
112
  dlog('svelte dom-shim: patchGlobals installed');
85
113
  }
86
114
  export function restoreGlobals() {
@@ -69,12 +69,23 @@ export class ShimNode {
69
69
  for (const node of nodes)
70
70
  this.appendChild(node);
71
71
  }
72
+ // The fragment branch is the rare one — every ordinary insert is a single node — but
73
+ // `normalizeInsertable` allocated a one-element array for it regardless, 23 006 of them on a
74
+ // 1 000-row create. Test the flag first and hand the node straight to insertOne.
72
75
  appendChild(node) {
76
+ if (!node.isDocumentFragment) {
77
+ this.insertOne(node, null);
78
+ return node;
79
+ }
73
80
  for (const single of normalizeInsertable(node))
74
81
  this.insertOne(single, null);
75
82
  return node;
76
83
  }
77
84
  insertBefore(node, ref) {
85
+ if (!node.isDocumentFragment) {
86
+ this.insertOne(node, ref);
87
+ return node;
88
+ }
78
89
  for (const single of normalizeInsertable(node))
79
90
  this.insertOne(single, ref);
80
91
  return node;
@@ -0,0 +1 @@
1
+ export declare function discoverStyleCacheKey(): symbol;
@@ -0,0 +1,27 @@
1
+ // Where a `style={…}` attribute really goes, and why the shim cannot read it off `cssText`.
2
+ //
3
+ // Every tag name takes the same path: the compiler emits `$.set_style(node, value)` for `style`,
4
+ // on a short tag and a hyphenated one alike. `set_style` then STRINGIFIES —
5
+ // `to_style(value, undefined)` is `String(value)` — so a React Native style object arrives at
6
+ // `dom.style.cssText` as the literal `"[object Object]"`. Nothing downstream can recover it.
7
+ //
8
+ // One line later `set_style` writes the ORIGINAL value onto the element under a module-private
9
+ // `Symbol('style')` (svelte's `internal/client/constants.js`), which it also reads back as the
10
+ // change guard. That symbol is the only surviving copy, so intercepting it is the only way a
11
+ // bare `<view style={obj}>` reaches `routeProp`.
12
+ //
13
+ // The symbol is not exported, so it is DISCOVERED by running svelte's own `set_style` against a
14
+ // probe and reading the one own symbol it leaves behind — once per process, never per node. A
15
+ // version that changes the shape fails loudly here rather than silently dropping every style.
16
+ import { set_style } from 'svelte/internal/client';
17
+ export function discoverStyleCacheKey() {
18
+ const probe = { style: { cssText: '' } };
19
+ // A non-null string: `to_style` returns it unchanged, so the probe stays on the assignment
20
+ // branch and never reaches `removeAttribute`, which it does not implement.
21
+ set_style(probe, 'probe');
22
+ const symbols = Object.getOwnPropertySymbols(probe);
23
+ if (symbols.length !== 1)
24
+ throw new Error(`svelte's set_style left ${symbols.length} own symbols on a probe; ` +
25
+ 'the style-cache key cannot be identified and `style` would be dropped');
26
+ return symbols[0];
27
+ }
@@ -36,8 +36,8 @@ const WHITESPACE_ONLY = /^[\s\u200b-\u200d\ufeff]+$/;
36
36
  // The PARENT is what makes this exact rather than a heuristic. Measured on svelte 5.56.8, a
37
37
  // stray gap and an {#each} text placeholder are the same ' ' string in the from_tree template:
38
38
  //
39
- // stray gap ['symbiote-view', null, [...], ' ', [...]] parent takes no raw text -> drop
40
- // placeholder ['symbiote-text', null, ' '] parent IS a <Text> -> keep
39
+ // stray gap ['view', null, [...], ' ', [...]] parent takes no raw text -> drop
40
+ // placeholder ['text', null, ' '] parent IS a <Text> -> keep
41
41
  //
42
42
  // So `<Text><Text>a</Text> <Text>b</Text></Text>` keeps its separator, correctly — there the
43
43
  // space really is a word boundary. This also covers the one shape the source preprocessor
@@ -1,5 +1,4 @@
1
1
  import { type IHostInstance } from '@symbiote-native/engine';
2
- import type { ShimElement } from './dom-shim';
3
2
  export type { IHostInstance } from '@symbiote-native/engine';
4
- export declare function hostInstance(shim: ShimElement | null | undefined): IHostInstance | undefined;
5
- export declare function findNodeHandle(componentOrHandle: ShimElement | number | null | undefined): number | null;
3
+ export declare function hostInstance(shim: unknown): IHostInstance | undefined;
4
+ export declare function findNodeHandle(componentOrHandle: unknown): number | null;
@@ -22,13 +22,18 @@ function isShimElement(value) {
22
22
  }
23
23
  // The typed imperative handle (measure/measureInWindow/measureLayout/setNativeProps/focus/blur)
24
24
  // a `bind:this` host ref carries — the Svelte twin of a React `ref.current`/Vue template ref
25
- // already being an `IHostInstance`. `dom-shim/element.ts`'s `createEngineNode()` grafts
26
- // toPublicInstance onto every host node AT CREATION, so this call is idempotent (its own
27
- // isHostInstance guard short-circuits); this helper exists only to give app code a correctly
25
+ // already being an `IHostInstance`. Every engine node carries those methods on its prototype, so
26
+ // the toPublicInstance call below is the identity; this helper exists only to give app code a correctly
28
27
  // TYPED accessor off the SHIM value (`ShimElement`) instead of the bare `.engineNode` field,
29
28
  // with no `as` cast at the call site.
29
+ // `unknown` rather than `ShimElement`, and the guard is what makes that sound. svelte2tsx bakes a
30
+ // `bind:this` return type out of `lib.dom`'s tag maps — an SVG element type for `view`/`text`/
31
+ // `image`/`switch`, `any` for the other seventeen — and neither map accepts augmentation
32
+ // (`src/intrinsic-elements.ts` records the measurement). So an app CANNOT declare its ref as
33
+ // `ShimElement` without a cast, and this is the accessor that exists to keep casts out of app
34
+ // code. Declaring the parameter `unknown` moves the narrowing where it can actually be checked.
30
35
  export function hostInstance(shim) {
31
- if (shim === null || shim === undefined)
36
+ if (!isShimElement(shim))
32
37
  return undefined;
33
38
  const node = shim.engineNode;
34
39
  return node !== undefined && isSymbioteNode(node)
package/build/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
+ import './register';
1
2
  export { mount, unmount } from './render';
2
3
  export { AppRegistry, setHostRegistrar } from './modules/app-registry';
3
4
  export type { IComponentProvider, IWrapperComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
4
- export { View, Text, ActivityIndicator, Image, ImageBackground, InputAccessoryView, KeyboardAvoidingView, Switch, TextInput, Modal, SafeAreaView, RefreshControl, Pressable, TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, TouchableNativeFeedback, Button, ScrollView, ScrollViewStickyHeader, VirtualizedList, FlatList, VirtualizedSectionList, SectionList, } from './components';
5
- export type { IViewProps, ITextProps, IActivityIndicatorProps, IImageProps, IImageStatics, IImageBackgroundProps, IInputAccessoryViewProps, IKeyboardAvoidingViewProps, IKeyboardAvoidingBehavior, ISwitchProps, ITextInputProps, ITextInputHandle, IModalProps, ISafeAreaViewProps, IRefreshControlProps, IPressableProps, ITouchableOpacityProps, ITouchableHighlightProps, ITouchableWithoutFeedbackProps, ITouchableNativeFeedbackProps, IButtonProps, IScrollViewProps, IScrollViewHandle, IStickyHeaderComponentProps, IVirtualizedListProps, IVirtualizedListHandle, IFlatListProps, IFlatListHandle, IVirtualizedSectionListProps, ISectionListProps, ISection, } from './components';
5
+ export { KeyboardAvoidingView, Modal, VirtualizedList, FlatList, VirtualizedSectionList, SectionList, } from './components';
6
+ export type { IViewProps, ITextProps, IActivityIndicatorProps, IImageProps, IImageBackgroundProps, IInputAccessoryViewProps, IKeyboardAvoidingViewProps, IKeyboardAvoidingBehavior, ISwitchProps, ISwitchChangeEvent, ITextInputProps, ITextInputHandle, ITextInputChangeEvent, IModalProps, ISafeAreaViewProps, IRefreshControlProps, IPressableProps, ITouchableOpacityProps, ITouchableHighlightProps, ITouchableWithoutFeedbackProps, ITouchableNativeFeedbackProps, IButtonProps, IScrollViewProps, IScrollViewHandle, IVirtualizedListProps, IVirtualizedListHandle, IFlatListProps, IFlatListHandle, IVirtualizedSectionListProps, ISectionListProps, ISection, } from './components';
6
7
  export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, IResponderProps, } from '@symbiote-native/components';
7
8
  export { setImageSourceResolver } from '@symbiote-native/components';
8
9
  export type { IImageSource, IImageSourceProp, IResizeMode, IImageSize, IImageCacheStatus, IModalAnimationType, IModalPresentationStyle, IModalOrientation, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, IPressState, ISwitchTrackColor, INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, IVirtualizedSectionListHandle, IVirtualizedSectionListHandle as ISectionListHandle, ICellLayout, ISeparatorProps, ISeparators, IModalOrientationChangeEvent, IPressableAndroidRippleConfig, IEnterKeyHint, IInputMode, ISubmitBehavior, ITextInputSelection, } from '@symbiote-native/components';
9
10
  export { Platform, StyleSheet, PlatformColor, DynamicColorIOS, processColor, setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, PixelRatio, PanResponder, dlog, isDebug, } from '@symbiote-native/engine';
10
11
  export type { INativeViewConfig, INativeViewConfigSource, IPixelRatioStatic, ISymbioteEvent, ISymbioteNode, IRootTag, IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, IViewStyle, ITextStyle, IFlexAlign, IFlexJustify, IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, } from '@symbiote-native/engine';
12
+ export { Image } from './modules/image';
13
+ export type { IImageStatics } from './modules/image';
14
+ export { TouchableNativeFeedback } from '@symbiote-native/components';
11
15
  export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
12
16
  export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, IShareContent, IShareOptions, IShareAction, IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, IUrlEvent, II18nManagerConstants, IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, IColorSchemeName, IColorSchemePreference, IAppStateStatus, IAppStateEvent, IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, IBackPressEventName, IBackPressHandler, IPermission, IPermissionStatus, IRationale, ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, } from '@symbiote-native/engine';
13
17
  export { StatusBar } from './modules/status-bar';
@@ -17,6 +21,7 @@ export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessi
17
21
  export { findNodeHandle, hostInstance } from './host-instance';
18
22
  export type { IHostInstance } from './host-instance';
19
23
  export type { ShimElement } from './dom-shim';
24
+ export type { ISymbioteIntrinsicTag, ISymbioteHostAttributes, } from './intrinsic-elements';
20
25
  export { createTunnel, TunnelIn, TunnelOut, type ITunnel, } from './create-tunnel';
21
26
  export { Portal, type IPortalProps, type IPortalTarget } from './create-portal';
22
27
  export { useWindowDimensions } from './runes/use-window-dimensions.svelte';
@@ -26,5 +31,4 @@ export { orientation, createWidthQuery } from './runes/media-query';
26
31
  export type { IOrientation, IWidthQueryBounds } from './runes/media-query';
27
32
  export type { IReactiveValue } from './runes/dimensions-value';
28
33
  export { normalizeSvelteClass, resolveSvelteClass, type ISvelteClassValue, type IClassMap, type IClassEntry, } from './class-value';
29
- export { Animated, createAnimatedComponent } from './modules/animated';
30
- export type { IAnimatedComponentProps } from './modules/animated';
34
+ export { Animated } from './modules/animated';