@symbiote-native/svelte 0.1.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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/build/bootstrap.d.ts +7 -0
  4. package/build/bootstrap.js +17 -0
  5. package/build/class-value.d.ts +6 -0
  6. package/build/class-value.js +81 -0
  7. package/build/components/RefreshControl.svelte +38 -0
  8. package/build/components/RefreshControl.svelte.d.ts +5 -0
  9. package/build/components/SafeAreaView.svelte +34 -0
  10. package/build/components/SafeAreaView.svelte.d.ts +5 -0
  11. package/build/components/Text.svelte +25 -0
  12. package/build/components/Text.svelte.d.ts +5 -0
  13. package/build/components/View.svelte +41 -0
  14. package/build/components/View.svelte.d.ts +5 -0
  15. package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +3 -0
  16. package/build/components/activity-indicator/activity-indicator-platform.android.js +5 -0
  17. package/build/components/activity-indicator/activity-indicator-platform.d.ts +1 -0
  18. package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +3 -0
  19. package/build/components/activity-indicator/activity-indicator-platform.ios.js +6 -0
  20. package/build/components/activity-indicator/activity-indicator-platform.js +5 -0
  21. package/build/components/activity-indicator/activity-indicator-props.d.ts +5 -0
  22. package/build/components/activity-indicator/activity-indicator-props.js +1 -0
  23. package/build/components/activity-indicator/index.svelte +57 -0
  24. package/build/components/activity-indicator/index.svelte.d.ts +5 -0
  25. package/build/components/button-props.d.ts +5 -0
  26. package/build/components/button-props.js +1 -0
  27. package/build/components/button.svelte +71 -0
  28. package/build/components/button.svelte.d.ts +5 -0
  29. package/build/components/flat-list/flat-list-props.d.ts +10 -0
  30. package/build/components/flat-list/flat-list-props.js +1 -0
  31. package/build/components/flat-list/index.svelte +254 -0
  32. package/build/components/flat-list/index.svelte.d.ts +79 -0
  33. package/build/components/image/image-logic.d.ts +2 -0
  34. package/build/components/image/image-logic.js +125 -0
  35. package/build/components/image/image-props.d.ts +5 -0
  36. package/build/components/image/image-props.js +1 -0
  37. package/build/components/image/index.d.ts +3 -0
  38. package/build/components/image/index.js +10 -0
  39. package/build/components/image/index.svelte +66 -0
  40. package/build/components/image/index.svelte.d.ts +5 -0
  41. package/build/components/image-background/image-background-props.d.ts +10 -0
  42. package/build/components/image-background/image-background-props.js +1 -0
  43. package/build/components/image-background/index.svelte +81 -0
  44. package/build/components/image-background/index.svelte.d.ts +5 -0
  45. package/build/components/index.d.ts +50 -0
  46. package/build/components/index.js +24 -0
  47. package/build/components/input-accessory-view/index.svelte +48 -0
  48. package/build/components/input-accessory-view/index.svelte.d.ts +5 -0
  49. package/build/components/input-accessory-view/input-accessory-view-props.d.ts +11 -0
  50. package/build/components/input-accessory-view/input-accessory-view-props.js +1 -0
  51. package/build/components/keyboard-avoiding-view/index.svelte +135 -0
  52. package/build/components/keyboard-avoiding-view/index.svelte.d.ts +5 -0
  53. package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.d.ts +15 -0
  54. package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.js +1 -0
  55. package/build/components/modal/index.svelte +126 -0
  56. package/build/components/modal/index.svelte.d.ts +5 -0
  57. package/build/components/modal/modal-props.d.ts +24 -0
  58. package/build/components/modal/modal-props.js +1 -0
  59. package/build/components/pressable/index.svelte +160 -0
  60. package/build/components/pressable/index.svelte.d.ts +5 -0
  61. package/build/components/pressable/pressable-props.d.ts +27 -0
  62. package/build/components/pressable/pressable-props.js +1 -0
  63. package/build/components/refresh-control-props.d.ts +19 -0
  64. package/build/components/refresh-control-props.js +1 -0
  65. package/build/components/safe-area-view-props.d.ts +10 -0
  66. package/build/components/safe-area-view-props.js +1 -0
  67. package/build/components/scroll-view/index.svelte +298 -0
  68. package/build/components/scroll-view/index.svelte.d.ts +18 -0
  69. package/build/components/scroll-view/scroll-view-platform-types.d.ts +3 -0
  70. package/build/components/scroll-view/scroll-view-platform-types.js +1 -0
  71. package/build/components/scroll-view/scroll-view-platform.android.d.ts +3 -0
  72. package/build/components/scroll-view/scroll-view-platform.android.js +3 -0
  73. package/build/components/scroll-view/scroll-view-platform.d.ts +1 -0
  74. package/build/components/scroll-view/scroll-view-platform.ios.d.ts +3 -0
  75. package/build/components/scroll-view/scroll-view-platform.ios.js +3 -0
  76. package/build/components/scroll-view/scroll-view-platform.js +4 -0
  77. package/build/components/scroll-view/scroll-view-props.d.ts +78 -0
  78. package/build/components/scroll-view/scroll-view-props.js +1 -0
  79. package/build/components/scroll-view/scroll-view-sticky-context.d.ts +7 -0
  80. package/build/components/scroll-view/scroll-view-sticky-context.js +1 -0
  81. package/build/components/scroll-view/sticky-header-props.d.ts +10 -0
  82. package/build/components/scroll-view/sticky-header-props.js +1 -0
  83. package/build/components/scroll-view/sticky-header.svelte +254 -0
  84. package/build/components/scroll-view/sticky-header.svelte.d.ts +5 -0
  85. package/build/components/section-list/index.svelte +107 -0
  86. package/build/components/section-list/index.svelte.d.ts +57 -0
  87. package/build/components/section-list/section-list-props.d.ts +1 -0
  88. package/build/components/section-list/section-list-props.js +1 -0
  89. package/build/components/switch/index.svelte +123 -0
  90. package/build/components/switch/index.svelte.d.ts +5 -0
  91. package/build/components/switch/switch-platform-types.d.ts +5 -0
  92. package/build/components/switch/switch-platform-types.js +1 -0
  93. package/build/components/switch/switch-platform.android.d.ts +3 -0
  94. package/build/components/switch/switch-platform.android.js +8 -0
  95. package/build/components/switch/switch-platform.d.ts +1 -0
  96. package/build/components/switch/switch-platform.ios.d.ts +3 -0
  97. package/build/components/switch/switch-platform.ios.js +7 -0
  98. package/build/components/switch/switch-platform.js +5 -0
  99. package/build/components/switch/switch-props.d.ts +5 -0
  100. package/build/components/switch/switch-props.js +1 -0
  101. package/build/components/text-input/index.svelte +300 -0
  102. package/build/components/text-input/index.svelte.d.ts +12 -0
  103. package/build/components/text-input/text-input-props.d.ts +5 -0
  104. package/build/components/text-input/text-input-props.js +1 -0
  105. package/build/components/text-props.d.ts +23 -0
  106. package/build/components/text-props.js +1 -0
  107. package/build/components/touchable-base-props.d.ts +10 -0
  108. package/build/components/touchable-base-props.js +1 -0
  109. package/build/components/touchable-highlight/index.svelte +39 -0
  110. package/build/components/touchable-highlight/index.svelte.d.ts +5 -0
  111. package/build/components/touchable-highlight/touchable-highlight-props.d.ts +5 -0
  112. package/build/components/touchable-highlight/touchable-highlight-props.js +1 -0
  113. package/build/components/touchable-native-feedback/index.d.ts +9 -0
  114. package/build/components/touchable-native-feedback/index.js +14 -0
  115. package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +8 -0
  116. package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
  117. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +44 -0
  118. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +5 -0
  119. package/build/components/touchable-opacity/index.svelte +124 -0
  120. package/build/components/touchable-opacity/index.svelte.d.ts +5 -0
  121. package/build/components/touchable-opacity/touchable-opacity-props.d.ts +5 -0
  122. package/build/components/touchable-opacity/touchable-opacity-props.js +1 -0
  123. package/build/components/touchable-without-feedback/index.svelte +19 -0
  124. package/build/components/touchable-without-feedback/index.svelte.d.ts +5 -0
  125. package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +2 -0
  126. package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
  127. package/build/components/view-props.d.ts +29 -0
  128. package/build/components/view-props.js +1 -0
  129. package/build/components/virtualized-list/index.svelte +702 -0
  130. package/build/components/virtualized-list/index.svelte.d.ts +79 -0
  131. package/build/components/virtualized-list/virtualized-list-props.d.ts +70 -0
  132. package/build/components/virtualized-list/virtualized-list-props.js +73 -0
  133. package/build/components/virtualized-section-list/index.svelte +206 -0
  134. package/build/components/virtualized-section-list/index.svelte.d.ts +57 -0
  135. package/build/components/virtualized-section-list/virtualized-section-list-props.d.ts +62 -0
  136. package/build/components/virtualized-section-list/virtualized-section-list-props.js +1 -0
  137. package/build/create-tunnel/index.d.ts +3 -0
  138. package/build/create-tunnel/index.js +3 -0
  139. package/build/create-tunnel/tunnel-in.svelte +24 -0
  140. package/build/create-tunnel/tunnel-in.svelte.d.ts +9 -0
  141. package/build/create-tunnel/tunnel-out.svelte +14 -0
  142. package/build/create-tunnel/tunnel-out.svelte.d.ts +7 -0
  143. package/build/create-tunnel/tunnel.d.ts +9 -0
  144. package/build/create-tunnel/tunnel.js +9 -0
  145. package/build/descriptor-to-svelte.d.ts +7 -0
  146. package/build/descriptor-to-svelte.js +104 -0
  147. package/build/dom-shim/comment.d.ts +9 -0
  148. package/build/dom-shim/comment.js +24 -0
  149. package/build/dom-shim/document-fragment.d.ts +8 -0
  150. package/build/dom-shim/document-fragment.js +23 -0
  151. package/build/dom-shim/document.d.ts +23 -0
  152. package/build/dom-shim/document.js +61 -0
  153. package/build/dom-shim/element.d.ts +22 -0
  154. package/build/dom-shim/element.js +130 -0
  155. package/build/dom-shim/index.d.ts +5 -0
  156. package/build/dom-shim/index.js +7 -0
  157. package/build/dom-shim/patch-globals.d.ts +2 -0
  158. package/build/dom-shim/patch-globals.js +94 -0
  159. package/build/dom-shim/shim-node.d.ts +30 -0
  160. package/build/dom-shim/shim-node.js +207 -0
  161. package/build/dom-shim/text.d.ts +14 -0
  162. package/build/dom-shim/text.js +97 -0
  163. package/build/host-instance.d.ts +5 -0
  164. package/build/host-instance.js +48 -0
  165. package/build/index.d.ts +28 -0
  166. package/build/index.js +77 -0
  167. package/build/modules/accessibility-info/index.android.d.ts +2 -0
  168. package/build/modules/accessibility-info/index.android.js +4 -0
  169. package/build/modules/accessibility-info/index.d.ts +1 -0
  170. package/build/modules/accessibility-info/index.ios.d.ts +2 -0
  171. package/build/modules/accessibility-info/index.ios.js +3 -0
  172. package/build/modules/accessibility-info/index.js +5 -0
  173. package/build/modules/animated/AnimatedImage.svelte +115 -0
  174. package/build/modules/animated/AnimatedImage.svelte.d.ts +5 -0
  175. package/build/modules/animated/AnimatedScrollView.svelte +92 -0
  176. package/build/modules/animated/AnimatedScrollView.svelte.d.ts +17 -0
  177. package/build/modules/animated/AnimatedText.svelte +58 -0
  178. package/build/modules/animated/AnimatedText.svelte.d.ts +5 -0
  179. package/build/modules/animated/AnimatedView.svelte +95 -0
  180. package/build/modules/animated/AnimatedView.svelte.d.ts +5 -0
  181. package/build/modules/animated/animated-component-props.d.ts +7 -0
  182. package/build/modules/animated/animated-component-props.js +1 -0
  183. package/build/modules/animated/index.d.ts +58 -0
  184. package/build/modules/animated/index.js +69 -0
  185. package/build/modules/app-registry/index.d.ts +7 -0
  186. package/build/modules/app-registry/index.js +28 -0
  187. package/build/modules/status-bar/index.d.ts +5 -0
  188. package/build/modules/status-bar/index.js +21 -0
  189. package/build/modules/status-bar/index.svelte +20 -0
  190. package/build/modules/status-bar/index.svelte.d.ts +4 -0
  191. package/build/native-view-bridge.d.ts +4 -0
  192. package/build/native-view-bridge.js +27 -0
  193. package/build/preprocessor/forbid-web-only-constructs.d.ts +8 -0
  194. package/build/preprocessor/forbid-web-only-constructs.js +204 -0
  195. package/build/preprocessor/scoped-styles.d.ts +10 -0
  196. package/build/preprocessor/scoped-styles.js +399 -0
  197. package/build/render.d.ts +7 -0
  198. package/build/render.js +46 -0
  199. package/build/root-element.d.ts +3 -0
  200. package/build/root-element.js +27 -0
  201. package/build/runes/attachments.d.ts +5 -0
  202. package/build/runes/attachments.js +73 -0
  203. package/build/runes/dimensions-value.d.ts +4 -0
  204. package/build/runes/dimensions-value.js +30 -0
  205. package/build/runes/media-query.d.ts +8 -0
  206. package/build/runes/media-query.js +38 -0
  207. package/build/runes/use-color-scheme.svelte.d.ts +4 -0
  208. package/build/runes/use-color-scheme.svelte.js +25 -0
  209. package/build/runes/use-window-dimensions.svelte.d.ts +4 -0
  210. package/build/runes/use-window-dimensions.svelte.js +43 -0
  211. package/build/runes/window.d.ts +6 -0
  212. package/build/runes/window.js +28 -0
  213. package/build/scope-token.d.ts +2 -0
  214. package/build/scope-token.js +33 -0
  215. package/build/style-scope.d.ts +1 -0
  216. package/build/style-scope.js +29 -0
  217. package/metro-css-parser.cjs +9 -0
  218. package/metro-svelte-transformer.cjs +156 -0
  219. package/package.json +83 -0
@@ -0,0 +1,104 @@
1
+ // A generic Descriptor -> shim-tree bridge, the Svelte twin of React's descriptorToReact /
2
+ // Vue's descriptorToVue — but SHAPE-STABLE rather than a walker that rebuilds on every call.
3
+ // React/Vue can afford a plain recursive `createElement`/`h()` walk because their OWN
4
+ // reconciler (outside this function) diffs the result against the previous render and reuses
5
+ // DOM nodes by identity; Svelte has no such external reconciler for an externally-built tree,
6
+ // so building fresh nodes on every call would mean recreating (not updating) the underlying
7
+ // Fabric/native view on every reactive change — expensive, and it would break any imperative
8
+ // command / native-owned state (focus, `dispatchViewCommand`) tied to the old node's identity.
9
+ //
10
+ // This works ONLY because every `core/components/src/view/render-*.ts` produces a Descriptor
11
+ // tree of CONSTANT shape across renders (svelte-adapter-dom-shim skill §15) — only prop VALUES
12
+ // vary. So: build each node ONCE (`mountDescriptorChildren`), cache it by tree position, and on
13
+ // every `update()` just re-set `.p`/`.data` on the SAME already-live shim nodes — never
14
+ // removeChild+recreate. See skill §19 for the full design rationale, the wolf-tui precedent
15
+ // this diverges from, and why the naive "rebuild the whole subtree" approach was rejected.
16
+ //
17
+ // A shape change (different type, different child count, text where an element was) throws
18
+ // rather than silently falling back to some best-effort behavior: it means a render-*.ts
19
+ // function's actual output changed shape, which this bridge's whole cost-free model assumes
20
+ // never happens. If this ever fires for real, the render fn genuinely stopped being
21
+ // shape-stable and needs its own fix, not a workaround here.
22
+ import { getShimDocument } from './dom-shim/index.js';
23
+ function shapeChangedMessage(detail) {
24
+ return (`descriptorToSvelte: Descriptor shape changed between renders (${detail}) — a ` +
25
+ `render-*.ts fn is expected to produce a CONSTANT tree shape (svelte-adapter-dom-shim ` +
26
+ `skill §15/§19); only prop values may vary between calls.`);
27
+ }
28
+ function buildChild(child) {
29
+ const document = getShimDocument();
30
+ if (typeof child === 'string') {
31
+ return { kind: 'text', shim: document.createTextNode(child) };
32
+ }
33
+ const shim = document.createElement(child.type);
34
+ shim.p = child.props;
35
+ const children = child.children.map(grandchild => {
36
+ const cached = buildChild(grandchild);
37
+ shim.appendChild(cached.shim);
38
+ return cached;
39
+ });
40
+ return { kind: 'element', shim, children };
41
+ }
42
+ function syncChild(cached, child) {
43
+ if (typeof child === 'string') {
44
+ if (cached.kind !== 'text')
45
+ throw new Error(shapeChangedMessage('text/element'));
46
+ if (cached.shim.data !== child)
47
+ cached.shim.data = child;
48
+ return;
49
+ }
50
+ if (cached.kind !== 'element' || cached.shim.tagName !== child.type) {
51
+ const was = cached.kind === 'element' ? cached.shim.tagName : 'text';
52
+ throw new Error(shapeChangedMessage(`${was} -> ${child.type}`));
53
+ }
54
+ cached.shim.p = child.props;
55
+ if (cached.children.length !== child.children.length) {
56
+ throw new Error(shapeChangedMessage('child count'));
57
+ }
58
+ cached.children.forEach((c, index) => syncChild(c, child.children[index]));
59
+ }
60
+ // Materializes `descriptor.children` onto an already-live `parent` ONCE, then reuses the same
61
+ // shim nodes by position on every `update()`. `parent` is expected to already be live (has an
62
+ // `engineNode`) — the normal case, since it is reached via a component's own `bind:this`
63
+ // inside an `$effect` that runs after mount, matching every other imperative-command use in
64
+ // this adapter (Switch's snap-back, TextInput's controlled write).
65
+ export function mountDescriptorChildren(parent, children) {
66
+ const cached = children.map(child => {
67
+ const built = buildChild(child);
68
+ parent.appendChild(built.shim);
69
+ return built;
70
+ });
71
+ return {
72
+ update(next) {
73
+ if (cached.length !== next.length)
74
+ throw new Error(shapeChangedMessage('root child count'));
75
+ cached.forEach((c, index) => syncChild(c, next[index]));
76
+ },
77
+ };
78
+ }
79
+ // The uniform wiring every category-1 component repeats: mount `mountDescriptorChildren` once
80
+ // (as soon as the root shim is live) and update() it thereafter — the "call the bridge" half of
81
+ // React's `descriptorToReact(useXLogic(...))` / Vue's `descriptorToVue(...)`. Root tags with an
82
+ // empty `children` array (Switch, TextInput — every prop rides the root's own `p`) still call
83
+ // this, for the SAME reason React still routes them through `descriptorToReact`: one uniform
84
+ // shape for every category-1 component, not two different idioms picked by child count. Usage:
85
+ //
86
+ // const syncChildren = createDescriptorChildrenSync();
87
+ // $effect(() => { syncChildren(hostShim, descriptor.children); });
88
+ //
89
+ // `hostShim` and `descriptor.children` are read unconditionally at the top of the effect body —
90
+ // same dependency-tracking discipline as every other $effect in this adapter (skill's TextInput
91
+ // note): a guard placed before the read would drop it from the tracked set on a guarded run.
92
+ export function createDescriptorChildrenSync() {
93
+ let mounted;
94
+ return (hostShim, children) => {
95
+ if (hostShim === null)
96
+ return;
97
+ if (mounted === undefined) {
98
+ mounted = mountDescriptorChildren(hostShim, children);
99
+ }
100
+ else {
101
+ mounted.update(children);
102
+ }
103
+ };
104
+ }
@@ -0,0 +1,9 @@
1
+ import { type ISymbioteNode } from '@symbiote-native/engine';
2
+ import { ShimNode } from './shim-node';
3
+ export declare class ShimComment extends ShimNode {
4
+ data: string;
5
+ constructor(data: string);
6
+ get nodeName(): string;
7
+ cloneNode(deep?: boolean): ShimComment;
8
+ createEngineNode(): ISymbioteNode;
9
+ }
@@ -0,0 +1,24 @@
1
+ // A comment — the anchor path (svelte-adapter-dom-shim skill §3e, §9). Maps to the engine's
2
+ // createAnchor: a real retained node so insert/nextSibling ordering stays correct, but the
3
+ // commit walk skips it (core/engine/src/node.ts's ANCHOR_COMPONENT) — no native view is ever
4
+ // created for it. Unlike text/element, `data` carries no engine-visible meaning; Svelte never
5
+ // reads a comment's content on any mandatory path, only its position in the tree.
6
+ import { createAnchor } from '@symbiote-native/engine';
7
+ import { ShimNode } from './shim-node.js';
8
+ export class ShimComment extends ShimNode {
9
+ data;
10
+ constructor(data) {
11
+ super();
12
+ this.data = data;
13
+ }
14
+ get nodeName() {
15
+ return '#comment';
16
+ }
17
+ cloneNode(deep) {
18
+ void deep; // DOM signature parity only — a comment node has no children to deep-clone
19
+ return new ShimComment(this.data);
20
+ }
21
+ createEngineNode() {
22
+ return createAnchor();
23
+ }
24
+ }
@@ -0,0 +1,8 @@
1
+ import type { ISymbioteNode } from '@symbiote-native/engine';
2
+ import { ShimNode } from './shim-node';
3
+ export declare class ShimDocumentFragment extends ShimNode {
4
+ readonly isDocumentFragment = true;
5
+ get nodeName(): string;
6
+ cloneNode(deep?: boolean): ShimDocumentFragment;
7
+ createEngineNode(): ISymbioteNode;
8
+ }
@@ -0,0 +1,23 @@
1
+ // A document fragment: never itself carries an engine node — inserting a fragment inserts
2
+ // its CHILDREN and leaves it empty (the DOM rule `ShimNode`'s `normalizeInsertable` depends
3
+ // on). Allocated per template build and on every `{#each}`/`{#if}`/`<svelte:boundary>` update
4
+ // (svelte-adapter-dom-shim skill §3e) — must stay cheap.
5
+ import { ShimNode } from './shim-node.js';
6
+ export class ShimDocumentFragment extends ShimNode {
7
+ isDocumentFragment = true;
8
+ get nodeName() {
9
+ return '#document-fragment';
10
+ }
11
+ cloneNode(deep) {
12
+ const clone = new ShimDocumentFragment();
13
+ if (deep === true) {
14
+ for (const child of this.children)
15
+ clone.appendChild(child.cloneNode(true));
16
+ }
17
+ return clone;
18
+ }
19
+ createEngineNode() {
20
+ throw new Error('ShimDocumentFragment must never be inserted directly — normalizeInsertable() ' +
21
+ 'should have unwrapped it into its children first');
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ import { ShimElement } from './element';
2
+ import { ShimText } from './text';
3
+ import { ShimComment } from './comment';
4
+ import { ShimDocumentFragment } from './document-fragment';
5
+ export declare class ShimDocument {
6
+ readonly body: ShimElement;
7
+ createElement(tag: string, options?: {
8
+ is?: string;
9
+ }): ShimElement;
10
+ createElementNS(namespace: string, tag: string, options?: {
11
+ is?: string;
12
+ }): ShimElement;
13
+ createTextNode(value: string): ShimText;
14
+ createDocumentFragment(): ShimDocumentFragment;
15
+ createComment(data: string): ShimComment;
16
+ importNode(node: ShimElement, deep: boolean): ShimElement;
17
+ importNode(node: ShimText, deep: boolean): ShimText;
18
+ importNode(node: ShimComment, deep: boolean): ShimComment;
19
+ importNode(node: ShimDocumentFragment, deep: boolean): ShimDocumentFragment;
20
+ getComputedStyle(): Record<string, never>;
21
+ }
22
+ export declare function getShimDocument(): ShimDocument;
23
+ export declare function resetShimDocumentForTests(): void;
@@ -0,0 +1,61 @@
1
+ // The shim's `document` singleton — the six factories `init_operations()` and `from_tree`
2
+ // require (svelte-adapter-dom-shim skill §3b), plus the two cheap stubs §4 calls for
3
+ // explicitly (`document.body`, `getComputedStyle`) so a forbidden/dead path degrades to a
4
+ // harmless no-op instead of a hard crash. No `<svelte:head|window|body|document>` support
5
+ // beyond that stub — those tags are rejected at build time by the preprocessor (§7), not
6
+ // handled here.
7
+ import { ShimElement } from './element.js';
8
+ import { ShimText } from './text.js';
9
+ import { ShimComment } from './comment.js';
10
+ import { ShimDocumentFragment } from './document-fragment.js';
11
+ import { registerShimDocumentFactory } from './shim-node.js';
12
+ export class ShimDocument {
13
+ // The delegation root real Svelte events bubble toward (`dom/elements/events.js:122`).
14
+ // Our own event names never delegate (§5c: no camelCase name matches Svelte's 23-name
15
+ // DELEGATED_EVENTS list), but a minimal, unattached node converts any stray read into a
16
+ // no-op instead of a crash — one line, per §4's "provide a stub anyway" call.
17
+ body = new ShimElement('symbiote-view');
18
+ createElement(tag, options) {
19
+ const element = new ShimElement(tag);
20
+ if (options?.is !== undefined)
21
+ element.setAttribute('is', options.is);
22
+ return element;
23
+ }
24
+ createElementNS(namespace, tag, options) {
25
+ const element = new ShimElement(tag, namespace);
26
+ if (options?.is !== undefined)
27
+ element.setAttribute('is', options.is);
28
+ return element;
29
+ }
30
+ createTextNode(value) {
31
+ return new ShimText(value);
32
+ }
33
+ createDocumentFragment() {
34
+ return new ShimDocumentFragment();
35
+ }
36
+ createComment(data) {
37
+ return new ShimComment(data);
38
+ }
39
+ importNode(node, deep) {
40
+ return node.cloneNode(deep);
41
+ }
42
+ // §4: not read by any mandatory path; stubbed so a forbidden transition (which we reject
43
+ // at build time anyway, §7) degrades to an inert value rather than a hard crash if it is
44
+ // ever reached some other way.
45
+ getComputedStyle() {
46
+ return {};
47
+ }
48
+ }
49
+ let singleton;
50
+ export function getShimDocument() {
51
+ singleton ??= new ShimDocument();
52
+ return singleton;
53
+ }
54
+ // Exercised only by tests / restoreGlobals bookkeeping — production code always goes through
55
+ // getShimDocument() so the singleton survives a mount/unmount cycle within one process.
56
+ export function resetShimDocumentForTests() {
57
+ singleton = undefined;
58
+ }
59
+ // Plugs this module into ShimNode's `ownerDocument`/`textContent` (see shim-node.ts's header
60
+ // comment for why the dependency runs this direction and not the reverse).
61
+ registerShimDocumentFactory(getShimDocument);
@@ -0,0 +1,22 @@
1
+ import { type ISymbioteNode } from '@symbiote-native/engine';
2
+ import { ShimNode } from './shim-node';
3
+ export type IShimPropBag = Record<string, unknown>;
4
+ export declare class ShimElement extends ShimNode {
5
+ readonly tagName: string;
6
+ readonly namespaceURI: string | undefined;
7
+ private readonly attributes;
8
+ private readonly domListeners;
9
+ private lastBag;
10
+ constructor(tagName: string, namespaceURI?: string);
11
+ get nodeName(): string;
12
+ get p(): IShimPropBag;
13
+ set p(bag: IShimPropBag | undefined);
14
+ setAttribute(name: string, value: string): void;
15
+ getAttribute(name: string): string | null;
16
+ removeAttribute(name: string): void;
17
+ addEventListener(name: string, handler: (event: unknown) => void): void;
18
+ removeEventListener(name: string): void;
19
+ cloneNode(deep?: boolean): ShimElement;
20
+ createEngineNode(): ISymbioteNode;
21
+ onMadeLive(): void;
22
+ }
@@ -0,0 +1,130 @@
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.
9
+ //
10
+ // A per-key diff in the setter is MANDATORY (§3g(c)), not an optimization: Svelte's
11
+ // `set_custom_element_data` has no early-out guard and runs on every effect re-fire, so
12
+ // 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';
14
+ import { descriptorFor } from '@symbiote-native/components';
15
+ import { normalizeSvelteClass } from '../class-value.js';
16
+ import { ShimNode } from './shim-node.js';
17
+ // Diagnostic-only: a process-wide sequence so every `set p` call across every shim element in a
18
+ // log dump is individually orderable against AnimatedProps reconcile#N / AnimatedView reduced#N
19
+ // / hostShim identity-change#N, to see which one is driving which.
20
+ let globalSetPSeq = 0;
21
+ // `on<Name>` handlers ride inside the prop bag (idiomatic Svelte 5 callback props — see the
22
+ // skill's §3g(c) "most of §5 collapses" note) — they are not addEventListener-style DOM
23
+ // listeners, so they are diffed and routed exactly like any other bag key, through routeProp.
24
+ export class ShimElement extends ShimNode {
25
+ tagName;
26
+ namespaceURI;
27
+ attributes = new Map();
28
+ domListeners = new Map();
29
+ lastBag = {};
30
+ constructor(tagName, namespaceURI) {
31
+ super();
32
+ this.tagName = tagName;
33
+ this.namespaceURI = namespaceURI;
34
+ }
35
+ get nodeName() {
36
+ return this.tagName;
37
+ }
38
+ // 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.
40
+ get p() {
41
+ return this.lastBag;
42
+ }
43
+ set p(bag) {
44
+ const next = normalizeBagClasses(bag ?? {});
45
+ const prev = this.lastBag;
46
+ 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(',')}`);
50
+ if (this.engineNode === undefined)
51
+ return; // not live yet — onMadeLive() replays `next` in full
52
+ applyBagDiff(this.engineNode, prev, next);
53
+ this.surface?.requestCommit();
54
+ }
55
+ setAttribute(name, value) {
56
+ this.attributes.set(name, value);
57
+ }
58
+ getAttribute(name) {
59
+ return this.attributes.get(name) ?? null;
60
+ }
61
+ removeAttribute(name) {
62
+ this.attributes.delete(name);
63
+ }
64
+ addEventListener(name, handler) {
65
+ this.domListeners.set(name, handler);
66
+ if (this.engineNode !== undefined)
67
+ setEventListener(this.engineNode, name, handler);
68
+ }
69
+ removeEventListener(name) {
70
+ this.domListeners.delete(name);
71
+ if (this.engineNode !== undefined)
72
+ setEventListener(this.engineNode, name, undefined);
73
+ }
74
+ cloneNode(deep) {
75
+ const clone = new ShimElement(this.tagName, this.namespaceURI);
76
+ for (const [key, value] of this.attributes)
77
+ clone.attributes.set(key, value);
78
+ if (deep === true) {
79
+ for (const child of this.children)
80
+ clone.appendChild(child.cloneNode(true));
81
+ }
82
+ return clone;
83
+ }
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.
89
+ createEngineNode() {
90
+ const descriptor = descriptorFor(this.tagName);
91
+ return toPublicInstance(createElement(descriptor.component, descriptor.isText));
92
+ }
93
+ onMadeLive() {
94
+ const engineNode = this.engineNode;
95
+ if (engineNode === undefined)
96
+ return;
97
+ applyBagDiff(engineNode, {}, this.lastBag);
98
+ for (const [name, handler] of this.domListeners)
99
+ setEventListener(engineNode, name, handler);
100
+ }
101
+ }
102
+ // Both spellings route through the engine's shared class+style merge (routeProp's
103
+ // CLASS_PROP_KEYS), so both get the same clsx normalization here.
104
+ const CLASS_BAG_KEYS = ['class', 'className'];
105
+ // Normalizing BEFORE the bag is stored — rather than at the routeProp call — is deliberate: the
106
+ // diff below then compares two class STRINGS instead of two freshly-allocated object literals, so
107
+ // a component re-rendering with an unchanged `class={{ active: isOn }}` stops marking the node
108
+ // dirty and stops driving a Fabric subtree clone that changes nothing.
109
+ function normalizeBagClasses(bag) {
110
+ let next = bag;
111
+ for (const key of CLASS_BAG_KEYS) {
112
+ if (!(key in next))
113
+ continue;
114
+ const normalized = normalizeSvelteClass(next[key]);
115
+ if (normalized === next[key])
116
+ continue;
117
+ if (next === bag)
118
+ next = { ...bag };
119
+ next[key] = normalized;
120
+ }
121
+ return next;
122
+ }
123
+ function diffKeys(prev, next) {
124
+ const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
125
+ return [...keys].filter(key => prev[key] !== next[key]);
126
+ }
127
+ function applyBagDiff(engineNode, prev, next) {
128
+ for (const key of diffKeys(prev, next))
129
+ routeProp(engineNode, key, next[key]);
130
+ }
@@ -0,0 +1,5 @@
1
+ export { patchGlobals, restoreGlobals } from './patch-globals';
2
+ export { ShimElement } from './element';
3
+ export { ShimNode } from './shim-node';
4
+ export { ShimText } from './text';
5
+ export { getShimDocument } from './document';
@@ -0,0 +1,7 @@
1
+ // Internal barrel — consumed by render.ts and root-element.ts, never by app code (app code
2
+ // never sees a shim class or a `symbiote-*` tag; see the adapter's own index.ts).
3
+ export { patchGlobals, restoreGlobals } from './patch-globals.js';
4
+ export { ShimElement } from './element.js';
5
+ export { ShimNode } from './shim-node.js';
6
+ export { ShimText } from './text.js';
7
+ export { getShimDocument } from './document.js';
@@ -0,0 +1,2 @@
1
+ export declare function patchGlobals(): void;
2
+ export declare function restoreGlobals(): void;
@@ -0,0 +1,94 @@
1
+ // Installs the shim as the DOM globals Svelte's `init_operations()` reads at mount time
2
+ // (svelte-adapter-dom-shim skill §2, §3a, §6). Must run before `mount()` (§3f) — not
3
+ // necessarily before `svelte` is imported.
4
+ //
5
+ // Single-root only, by design (§10, decided during Svelte adapter planning 2026-08-11): one
6
+ // Symbiote app = one process = one Svelte root, so `restoreGlobals()` needs no ref-counting —
7
+ // `unmount()` calls it unconditionally.
8
+ //
9
+ // Deliberately NOT patched, and must stay that way (§6b, §6c, §6d) — both verified against the
10
+ // real vendored RN source (.vendors/react-native/packages/react-native/Libraries/Core/), not
11
+ // just asserted:
12
+ // - `navigator` — RN's own `setUpNavigator.js` sets `global.navigator = {product:
13
+ // 'ReactNative'}` before any app code runs; clobbering it breaks third-party detection
14
+ // silently. `init_operations()` reads `navigator.userAgent` (undefined on RN, since
15
+ // setUpNavigator.js sets no such field) but only via `/Firefox/.test(...)`, which coerces
16
+ // `undefined` to the string `"undefined"` rather than throwing.
17
+ // - `requestAnimationFrame` / `cancelAnimationFrame` — `@symbiote-native/engine`'s own Animated
18
+ // driver reads these globals at call time; replacing them would silently degrade every
19
+ // adapter's animations to a 16ms timer, not just Svelte's.
20
+ // - `window` — RN's own `setUpGlobals.js` sets `global.window = global` before any app code
21
+ // runs; nothing to do. (A bare Node/vitest sandbox does NOT have this — a test exercising
22
+ // the real mount pipeline needs to replicate it itself; see
23
+ // mount-pipeline.smoke.test.ts's module-level setup.)
24
+ //
25
+ // `customElements` IS patched, unlike the three above — see its own comment below.
26
+ import { dlog } from '@symbiote-native/engine';
27
+ import { ShimElement } from './element.js';
28
+ import { ShimText } from './text.js';
29
+ import { ShimComment } from './comment.js';
30
+ import { ShimDocumentFragment } from './document-fragment.js';
31
+ import { ShimNode } from './shim-node.js';
32
+ import { getShimDocument } from './document.js';
33
+ const PATCHED_KEYS = [
34
+ 'Node',
35
+ 'Element',
36
+ 'HTMLElement',
37
+ 'SVGElement',
38
+ 'Text',
39
+ 'Comment',
40
+ 'DocumentFragment',
41
+ 'document',
42
+ 'customElements',
43
+ ];
44
+ // Real DOM's `CustomElementRegistry`. `set_custom_element_data` (svelte's
45
+ // dom/elements/attributes.js) reads the BARE global `customElements` unconditionally, with no
46
+ // `typeof` guard — an undeclared global throws ReferenceError, not just `undefined`, so this
47
+ // must be a real assigned property, not merely absent. Found only by actually running the
48
+ // pipeline (mount-pipeline.smoke.test.ts); `tsc --build` has no way to catch a missing global
49
+ // a compiled Svelte bundle reads. RN/Hermes has no Custom Elements API of its own to collide
50
+ // 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 };
56
+ let previous;
57
+ export function patchGlobals() {
58
+ if (previous !== undefined)
59
+ return; // already installed — single root, idempotent
60
+ const g = globalThis;
61
+ previous = {
62
+ Node: g.Node,
63
+ Element: g.Element,
64
+ HTMLElement: g.HTMLElement,
65
+ SVGElement: g.SVGElement,
66
+ Text: g.Text,
67
+ Comment: g.Comment,
68
+ DocumentFragment: g.DocumentFragment,
69
+ document: g.document,
70
+ customElements: g.customElements,
71
+ };
72
+ g.Node = ShimNode;
73
+ g.Element = ShimElement;
74
+ // Svelte's mandatory paths never distinguish HTMLElement/SVGElement from Element (we have
75
+ // no `<svg>` primitives), so both alias the same class — real, extensible prototypes either
76
+ // way, which is all `init_operations()` requires (§3a).
77
+ g.HTMLElement = ShimElement;
78
+ g.SVGElement = ShimElement;
79
+ g.Text = ShimText;
80
+ g.Comment = ShimComment;
81
+ g.DocumentFragment = ShimDocumentFragment;
82
+ g.document = getShimDocument();
83
+ g.customElements = FAKE_CUSTOM_ELEMENT_REGISTRY;
84
+ dlog('svelte dom-shim: patchGlobals installed');
85
+ }
86
+ export function restoreGlobals() {
87
+ if (previous === undefined)
88
+ return;
89
+ const g = globalThis;
90
+ for (const key of PATCHED_KEYS)
91
+ g[key] = previous[key];
92
+ previous = undefined;
93
+ dlog('svelte dom-shim: restoreGlobals');
94
+ }
@@ -0,0 +1,30 @@
1
+ import { type ISymbioteNode, type SymbioteSurface } from '@symbiote-native/engine';
2
+ type IDocumentLike = {
3
+ createTextNode(value: string): ShimNode;
4
+ };
5
+ export declare function registerShimDocumentFactory(factory: () => IDocumentLike): void;
6
+ export declare abstract class ShimNode {
7
+ parent: ShimNode | null;
8
+ children: ShimNode[];
9
+ engineNode: ISymbioteNode | undefined;
10
+ surface: SymbioteSurface | undefined;
11
+ readonly isDocumentFragment: boolean;
12
+ abstract createEngineNode(): ISymbioteNode;
13
+ onMadeLive(): void;
14
+ abstract cloneNode(deep?: boolean): ShimNode;
15
+ get parentNode(): ShimNode | null;
16
+ get lastChild(): ShimNode | null;
17
+ get nodeName(): string;
18
+ get content(): undefined;
19
+ get ownerDocument(): IDocumentLike;
20
+ makeLive(surface: SymbioteSurface): ISymbioteNode;
21
+ append(...nodes: ShimNode[]): void;
22
+ appendChild<T extends ShimNode>(node: T): T;
23
+ insertBefore<T extends ShimNode>(node: T, ref: ShimNode | null): T;
24
+ before(node: ShimNode): void;
25
+ remove(): void;
26
+ removeChild<T extends ShimNode>(child: T): T;
27
+ set textContent(value: string);
28
+ private insertOne;
29
+ }
30
+ export {};