@symbiote-native/engine 1.3.0 → 1.4.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 (79) hide show
  1. package/build/accessibility-props.d.ts +0 -11
  2. package/build/accessibility-props.js +30 -68
  3. package/build/animated/graph.js +1 -1
  4. package/build/animated/leaf-lifecycle.js +2 -2
  5. package/build/asset-source-resolver.d.ts +2 -0
  6. package/build/asset-source-resolver.js +13 -0
  7. package/build/back-handler/index.d.ts +1 -5
  8. package/build/back-handler/index.js +0 -6
  9. package/build/debug.js +8 -22
  10. package/build/dispatch.js +3 -9
  11. package/build/events/delivery.d.ts +9 -0
  12. package/build/events/delivery.js +143 -0
  13. package/build/events/index.js +199 -660
  14. package/build/events/names.d.ts +24 -0
  15. package/build/events/names.js +80 -0
  16. package/build/events/press.d.ts +20 -0
  17. package/build/events/press.js +89 -0
  18. package/build/events/responder.d.ts +6 -0
  19. package/build/events/responder.js +124 -0
  20. package/build/fabric-props.js +74 -179
  21. package/build/fabric.d.ts +0 -13
  22. package/build/fabric.js +18 -38
  23. package/build/host-access.d.ts +1 -128
  24. package/build/host-access.js +96 -205
  25. package/build/host-behavior.d.ts +0 -100
  26. package/build/host-behavior.js +125 -311
  27. package/build/image-loader.js +10 -23
  28. package/build/image-source-resolver.js +3 -7
  29. package/build/image-source-write.d.ts +0 -11
  30. package/build/image-source-write.js +14 -34
  31. package/build/imperative.d.ts +2 -28
  32. package/build/imperative.js +60 -93
  33. package/build/index.d.ts +6 -2
  34. package/build/index.js +29 -39
  35. package/build/mutation-buffer.d.ts +3 -177
  36. package/build/mutation-buffer.js +162 -316
  37. package/build/native-engine.d.ts +6 -102
  38. package/build/native-engine.js +60 -141
  39. package/build/native-events.js +9 -18
  40. package/build/native-tree-host.d.ts +0 -21
  41. package/build/native-tree-host.js +15 -31
  42. package/build/node-events.d.ts +11 -0
  43. package/build/node-events.js +145 -0
  44. package/build/node-instance.d.ts +8 -0
  45. package/build/node-instance.js +168 -0
  46. package/build/node-props.d.ts +13 -0
  47. package/build/node-props.js +131 -0
  48. package/build/node-route.d.ts +2 -0
  49. package/build/node-route.js +151 -0
  50. package/build/node-style.d.ts +15 -0
  51. package/build/node-style.js +214 -0
  52. package/build/node-tree.d.ts +6 -0
  53. package/build/node-tree.js +159 -0
  54. package/build/node-types.d.ts +70 -0
  55. package/build/node-types.js +36 -0
  56. package/build/node.d.ts +7 -309
  57. package/build/node.js +9 -1564
  58. package/build/post-commit.js +3 -8
  59. package/build/process-aspect-ratio.js +3 -7
  60. package/build/process-background-longhands.js +10 -19
  61. package/build/process-filter.js +11 -19
  62. package/build/process-font-variant.js +3 -7
  63. package/build/registry.d.ts +0 -33
  64. package/build/registry.js +22 -57
  65. package/build/report-error.js +4 -18
  66. package/build/structured-style.d.ts +0 -9
  67. package/build/structured-style.js +16 -31
  68. package/build/styles.js +3 -6
  69. package/build/surface.d.ts +0 -26
  70. package/build/surface.js +29 -76
  71. package/build/text-input-state.js +4 -8
  72. package/build/touch-history.js +5 -11
  73. package/build/tree-host.d.ts +7 -270
  74. package/build/tree-host.js +63 -153
  75. package/build/view-config.js +17 -37
  76. package/cpp/SymbioteEngineBindings.cpp +19 -18
  77. package/cpp/SymbioteTree.cpp +81 -156
  78. package/cpp/SymbioteTree.h +6 -0
  79. package/package.json +2 -2
@@ -0,0 +1,214 @@
1
+ // The declarative style slots and their publication: two halves an adapter may write in either
2
+ // order, merged into the one array the payload carries
3
+ import { recordSetUnderlayShown } from './mutation-buffer.js';
4
+ import { EMPTY_STYLE, resolveActiveClassName, } from './style-registry/index.js';
5
+ import { setProp } from './node-props.js';
6
+ // Narrowed rather than cast: `routeProp` takes `unknown`, and a bare `typeof v === 'function'`
7
+ // leaves TS with `Function`, callable with anything
8
+ export function isStyleCallback(value) {
9
+ return typeof value === 'function';
10
+ }
11
+ // All slots present from the start rather than added as they are written, so a styled node keeps
12
+ // one hidden class instead of paying a shape transition per slot
13
+ export function stylePartsOf(node) {
14
+ return (node.styleParts ??= {
15
+ classStyle: undefined,
16
+ explicitStyle: undefined,
17
+ hiddenStyle: undefined,
18
+ className: undefined,
19
+ isPressed: false,
20
+ activeStyle: undefined,
21
+ activeStyleFromCallback: false,
22
+ published: undefined,
23
+ });
24
+ }
25
+ // Slot 1's twin of `baseStyleOf`: the variant replaces slot 1, not slot 0, so it beats the class
26
+ // cascade the way the authored style does while a `:active` rule can still win slot 0 underneath
27
+ function explicitStyleOf(parts) {
28
+ return parts.isPressed && parts.activeStyle !== undefined
29
+ ? parts.activeStyle
30
+ : parts.explicitStyle;
31
+ }
32
+ // `resolveActiveClassName` resolves the element's tokens PLUS `:active` through the same matcher,
33
+ // so the result already contains everything the base class gave. Resolved LAZILY at press time,
34
+ // т.к. eager would double the resolutions on every class WRITE to serve a rare state
35
+ function baseStyleOf(parts) {
36
+ return parts.isPressed && typeof parts.className === 'string'
37
+ ? resolveActiveClassName(parts.className)
38
+ : parts.classStyle;
39
+ }
40
+ // What a node publishes when NOTHING resolves. Length 0 is the marker and needs no second field,
41
+ // т.к. `pushClassStyle` never otherwise publishes an empty array
42
+ const PUBLISHED_NOTHING = Object.freeze([]);
43
+ // `mutation-buffer.ts` interns values BY IDENTITY, so distinct-but-equal arrays across a list
44
+ // styled the same way cost distinct entries and separate conversions to `folly::dynamic`
45
+ // `WeakMap` at both levels so nothing grows unbounded. The three-slot (hidden) form is deliberately
46
+ // NOT cached: `display: 'none'` is rare, so a third map would be paid for on every write
47
+ const sharedPairByExplicit = new WeakMap();
48
+ const sharedPairByBase = new WeakMap();
49
+ // The published array for this pair, the same object every time the same two parts are handed in.
50
+ // `undefined` when the pair cannot be keyed, and the caller then builds its own array
51
+ function sharedStylePair(base, explicit) {
52
+ const baseIsKeyable = typeof base === 'object' && base !== null;
53
+ const explicitIsKeyable = typeof explicit === 'object' && explicit !== null;
54
+ if (base === undefined && explicitIsKeyable) {
55
+ const cached = sharedPairByExplicit.get(explicit);
56
+ if (cached !== undefined)
57
+ return cached;
58
+ const made = [base, explicit];
59
+ sharedPairByExplicit.set(explicit, made);
60
+ return made;
61
+ }
62
+ if (!baseIsKeyable)
63
+ return undefined;
64
+ if (explicit === undefined) {
65
+ const cached = sharedPairByBase.get(base);
66
+ if (Array.isArray(cached))
67
+ return cached;
68
+ if (cached === undefined) {
69
+ const made = [base, explicit];
70
+ sharedPairByBase.set(base, made);
71
+ return made;
72
+ }
73
+ // A base already seen WITH an explicit half holds the second-level map here, and the base-only
74
+ // array has nowhere to live beside it. Rare enough not to earn a third map
75
+ return undefined;
76
+ }
77
+ if (!explicitIsKeyable)
78
+ return undefined;
79
+ const existing = sharedPairByBase.get(base);
80
+ const byExplicit = existing instanceof WeakMap ? existing : new WeakMap();
81
+ if (existing === undefined)
82
+ sharedPairByBase.set(base, byExplicit);
83
+ // The same clash the other way round: this base is holding its base-only array, so leave it
84
+ if (Array.isArray(existing))
85
+ return undefined;
86
+ const cached = byExplicit.get(explicit);
87
+ if (cached !== undefined)
88
+ return cached;
89
+ const made = [base, explicit];
90
+ byExplicit.set(explicit, made);
91
+ return made;
92
+ }
93
+ // A plain style bag: not an array of styles, not a callback, not null
94
+ function isStyleRecord(value) {
95
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
96
+ }
97
+ // Is this rebuilt style the same style, key for key? A component body writing its style inline
98
+ // hands over a fresh object every render, equal to the one standing, which `Object.is` cannot see
99
+ // Shallow and conservative: a nested value reports "not the same" rather than being deep-compared,
100
+ // so being wrong here is slow and never incorrect
101
+ export function isSameShallowStyle(next, standing) {
102
+ // THE SAME OBJECT IS THE SAME STYLE, checked first: without it a re-push of a hoisted constant
103
+ // allocates two key arrays and walks them to reach the answer identity gives for free
104
+ if (next === standing)
105
+ return isStyleRecord(next);
106
+ if (!isStyleRecord(next) || !isStyleRecord(standing))
107
+ return false;
108
+ const keys = Object.keys(next);
109
+ if (keys.length !== Object.keys(standing).length)
110
+ return false;
111
+ for (const key of keys) {
112
+ const value = next[key];
113
+ if (value === undefined || isStyleRecord(value) || Array.isArray(value)) {
114
+ return false;
115
+ }
116
+ if (!Object.is(value, standing[key]))
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+ function contributesNothing(slot) {
122
+ return slot === undefined || slot === EMPTY_STYLE;
123
+ }
124
+ // Read through the same two resolvers as the publication: guard and publication disagreeing is a
125
+ // silent wrong screen
126
+ function hasNothingToPublish(parts) {
127
+ return (parts.hiddenStyle === undefined &&
128
+ contributesNothing(baseStyleOf(parts)) &&
129
+ contributesNothing(explicitStyleOf(parts)));
130
+ }
131
+ function isAlreadyPublished(parts) {
132
+ const published = parts.published;
133
+ if (published === undefined)
134
+ return false;
135
+ // The delete is already standing. Asked before the slot comparisons, т.к. an empty array would
136
+ // otherwise pass both on `undefined` and then fail the length check
137
+ if (published.length === 0)
138
+ return hasNothingToPublish(parts);
139
+ // Through the resolvers, not the raw slots: guard and publication must read slot 0 and slot 1 the
140
+ // same way, or a press is turned away as already-published and does nothing with nothing red
141
+ if (!Object.is(published[0], baseStyleOf(parts)))
142
+ return false;
143
+ if (!Object.is(published[1], explicitStyleOf(parts)))
144
+ return false;
145
+ return parts.hiddenStyle === undefined
146
+ ? published.length === 2
147
+ : published.length === 3 && Object.is(published[2], parts.hiddenStyle);
148
+ }
149
+ // The fresh ARRAY allocation stays DELIBERATELY: `setNativeProps` bypasses these parts, so a
150
+ // hoisted style constant would be skipped by the identity guard and never restore the declarative
151
+ // style an animation overwrote. The re-push IS the restore path
152
+ export function pushClassStyle(node, parts) {
153
+ // An unchanged class still reaches this write, т.к. Solid has no diff. The guard keys off
154
+ // `published`, which `setNativeProps` clears, so an imperative restore still re-publishes
155
+ if (isAlreadyPublished(parts))
156
+ return;
157
+ // Emits NO_VALUE rather than `[undefined, undefined]`, so the host skips a real array with one
158
+ // pointer check instead of building and diffing a `folly::dynamic`
159
+ if (hasNothingToPublish(parts)) {
160
+ parts.published = PUBLISHED_NOTHING;
161
+ setProp(node, 'style', undefined);
162
+ return;
163
+ }
164
+ // Third slot only appended while hidden: a permanent 3-element array would add an allocation to
165
+ // every style write for a state most nodes never enter
166
+ const base = baseStyleOf(parts);
167
+ const explicit = explicitStyleOf(parts);
168
+ const published = parts.hiddenStyle === undefined
169
+ ? (sharedStylePair(base, explicit) ?? [base, explicit])
170
+ : [base, explicit, parts.hiddenStyle];
171
+ parts.published = published;
172
+ setProp(node, 'style', published);
173
+ }
174
+ // `display: 'none'` is a real RN style value (Yoga's DisplayNone), so a hidden node keeps its
175
+ // place in the tree, its state and its children, and just stops laying out and painting
176
+ const HIDDEN_STYLE = { display: 'none' };
177
+ // The seam React's Activity/Suspense reach for through `hideInstance` / `unhideInstance`. In the
178
+ // engine rather than an adapter, т.к. restoring the author's style byte belongs to the style merge
179
+ export function setNodeHidden(node, hidden) {
180
+ const parts = stylePartsOf(node);
181
+ parts.hiddenStyle = hidden ? HIDDEN_STYLE : undefined;
182
+ pushClassStyle(node, parts);
183
+ }
184
+ // Put a node into (or out of) its pressed state so `:active` rules apply. Costs nothing when no
185
+ // `:active` rule is registered, т.к. `isAlreadyPublished` turns the re-push away
186
+ export function setNodePressed(node, pressed) {
187
+ const parts = stylePartsOf(node);
188
+ parts.isPressed = pressed;
189
+ pushClassStyle(node, parts);
190
+ }
191
+ // Tell the host a behavior's FEEDBACK is showing, which is TouchableHighlight's underlay and only
192
+ // that. Not the same bit as `setNodePressed`, т.к. `shown` lags `pressed` by a timer
193
+ export function setNodeUnderlayShown(node, shown) {
194
+ recordSetUnderlayShown(node, shown);
195
+ }
196
+ // Forget what was last published so the next `pushClassStyle` cannot be turned away. A no-op for a
197
+ // node nobody has styled, which is why this is not `stylePartsOf(node).published = undefined`
198
+ export function clearPublishedStyle(node) {
199
+ if (node.styleParts !== undefined)
200
+ node.styleParts.published = undefined;
201
+ }
202
+ // The explicit (non-class) style half, which an adapter building style key by key merges onto
203
+ // rather than `node.props.style`, which may hold the pair `pushClassStyle` publishes
204
+ export function getExplicitStyle(node) {
205
+ return node.styleParts?.explicitStyle;
206
+ }
207
+ // The `[classStyle, explicitStyle]` pair the node publishes, in the order `pushClassStyle` writes.
208
+ // For a caller that wants the merged answer without a host, `core/css-parser` reads it here
209
+ export function getPublishedStyle(node) {
210
+ const parts = node.styleParts;
211
+ if (parts === undefined)
212
+ return [];
213
+ return [parts.classStyle, parts.explicitStyle];
214
+ }
@@ -0,0 +1,6 @@
1
+ import { type ISymbioteNode } from './node-types';
2
+ import type { ITreeCensus } from './tree-host';
3
+ export declare function appendChild(requestedParent: ISymbioteNode, child: ISymbioteNode): void;
4
+ export declare function insertBefore(requestedParent: ISymbioteNode, child: ISymbioteNode, beforeChild: ISymbioteNode | null | undefined): void;
5
+ export declare function removeChild(requestedParent: ISymbioteNode, child: ISymbioteNode): void;
6
+ export declare function censusRetainedTree(roots: readonly ISymbioteNode[]): ITreeCensus;
@@ -0,0 +1,159 @@
1
+ // The structural ops. Each is one op and nothing else: the host detaches a child from whatever
2
+ // parent it currently has before linking it, so JS never tracks the old parent
3
+ import { recordAppendChild, recordInsertBefore, recordRemoveChild, } from './mutation-buffer.js';
4
+ import { claimModeFor, hasAttachedBehaviors, hasHostBehaviors, markDetachCandidate, notifyChildInserted, noteCommitHookNodeChanged, reattachHostBehaviors, slotTakesChildren, } from './host-behavior.js';
5
+ import { hasAnimatedBindings, reattachAnimatedProps, } from './animated/host-binding.js';
6
+ import { EMPTY_CENSUS, flushOps, treeHost } from './tree-host.js';
7
+ import { isSymbioteNode } from './node-types.js';
8
+ // The host's raw answer, surface INCLUDED, unlike `parentOf` in `host-access.ts`, which reports a
9
+ // top-level node as parentless by design. The two swaps below have to NAME the holder in an op
10
+ function holderOf(node) {
11
+ flushOps();
12
+ const parent = treeHost()?.parentOf(node);
13
+ return isSymbioteNode(parent) ? parent : undefined;
14
+ }
15
+ // Which node a child actually lands on: the adapter always names the OWNER, and a node whose
16
+ // behavior built an internal subtree redirects the app's children into it unless the behavior
17
+ // CLAIMS this particular child
18
+ function hostFor(parent, child) {
19
+ const slot = parent.childHost;
20
+ if (slot === undefined)
21
+ return parent;
22
+ // A slot that is a built SIBLING rather than a container (ImageBackground's filled image) keeps
23
+ // the app's children on the owner
24
+ if (!slotTakesChildren(parent))
25
+ return parent;
26
+ return claimModeFor(parent, child.component) === undefined ? slot : parent;
27
+ }
28
+ // The node a child must be inserted before, or `undefined` for an ordinary append. A host that
29
+ // still has a slot is an owner taking a claimed child, which goes before the slot whatever the
30
+ // framework asked, т.к. RN renders `{refreshControl}{content}` in that order
31
+ function slotAnchorOf(host) {
32
+ const slot = host.childHost;
33
+ if (slot === undefined || !slotTakesChildren(host))
34
+ return undefined;
35
+ return slot;
36
+ }
37
+ // Arm a parent's recurring post-commit hook for a STRUCTURAL change, not just a prop write: the
38
+ // sticky-header machine drops a wrapper writing no prop at all, and a props-only beat left it stuck
39
+ function armCommitHookForChildChange(parent) {
40
+ if (parent.hasCommitHook)
41
+ noteCommitHookNodeChanged(parent);
42
+ }
43
+ // `mayHaveChildren` is only sound if every op that gives a node a child raises it, so it is raised
44
+ // in these two rather than at each of the five call sites
45
+ function recordAppendInto(parent, child) {
46
+ parent.mayHaveChildren = true;
47
+ armCommitHookForChildChange(parent);
48
+ recordAppendChild(parent, child);
49
+ }
50
+ function recordInsertInto(parent, child, beforeChild) {
51
+ parent.mayHaveChildren = true;
52
+ armCommitHookForChildChange(parent);
53
+ recordInsertBefore(parent, child, beforeChild);
54
+ }
55
+ // What actually occupies this node's place in its parent's child list: a wrapped owner is what the
56
+ // adapter names and the wrapper is what the tree holds
57
+ function placedNode(node) {
58
+ return node.wrapper ?? node;
59
+ }
60
+ // Make `child` the owner's parent, in place, returning false when this is not a wrap claim. The
61
+ // owner being unattached is the normal case, т.к. every adapter fills a node's children before
62
+ // appending it to its own parent
63
+ function wrapsOwner(owner, child) {
64
+ if (owner.childHost === undefined)
65
+ return false;
66
+ if (claimModeFor(owner, child.component) !== 'wrap')
67
+ return false;
68
+ if (hasHostBehaviors())
69
+ reattachHostBehaviors(child);
70
+ if (hasAnimatedBindings())
71
+ reattachAnimatedProps(child);
72
+ const holder = holderOf(owner);
73
+ // Wrapper takes the owner's place first, then the owner moves under it: the host's own detach on
74
+ // link is what unlinks the owner from `holder`, so no removal op is needed
75
+ if (holder !== undefined)
76
+ recordInsertInto(holder, child, owner);
77
+ owner.wrapper = child;
78
+ recordAppendInto(child, owner);
79
+ return true;
80
+ }
81
+ // Put the owner back where its wrapper stood, the mirror of `wrapsOwner`. It must leave the owner
82
+ // ATTACHED: the framework is removing the RefreshControl, not the ScrollView
83
+ function unwrapsOwner(owner, child) {
84
+ if (owner.wrapper !== child)
85
+ return false;
86
+ owner.wrapper = undefined;
87
+ const holder = holderOf(child);
88
+ if (holder === undefined) {
89
+ // The wrapper never reached a parent, so there is no place to take back
90
+ recordRemoveChild(child, owner);
91
+ }
92
+ else {
93
+ recordInsertInto(holder, owner, child);
94
+ recordRemoveChild(holder, child);
95
+ }
96
+ return true;
97
+ }
98
+ // The body both inserts share: only the anchor differs, and NO anchor means append. A real case
99
+ // rather than a defensive guard, т.к. `solid-js/universal` spells "insert at end" as
100
+ // `insertNode(parent, node, null)` and Vue passes `anchor` through
101
+ function place(requestedParent, child, beforeChild) {
102
+ if (wrapsOwner(requestedParent, child))
103
+ return;
104
+ const parent = hostFor(requestedParent, child);
105
+ // A node the sweep tore down can be put back, т.к. Svelte parks live subtrees offscreen across
106
+ // commits. A `WeakSet` miss for anything freshly built, so the create path pays nothing
107
+ if (hasHostBehaviors())
108
+ reattachHostBehaviors(child);
109
+ if (hasAnimatedBindings())
110
+ reattachAnimatedProps(child);
111
+ const placed = placedNode(child);
112
+ const anchor = slotAnchorOf(parent) ??
113
+ (beforeChild === null || beforeChild === undefined
114
+ ? undefined
115
+ : placedNode(beforeChild));
116
+ if (anchor === undefined)
117
+ recordAppendInto(parent, placed);
118
+ else
119
+ recordInsertInto(parent, placed, anchor);
120
+ if (hasHostBehaviors())
121
+ notifyChildInserted(parent, placed);
122
+ }
123
+ export function appendChild(requestedParent, child) {
124
+ place(requestedParent, child, undefined);
125
+ }
126
+ export function insertBefore(requestedParent, child, beforeChild) {
127
+ place(requestedParent, child, beforeChild);
128
+ }
129
+ // Removal only NOMINATES a behavior for teardown and the commit sweep decides, т.к. a framework may
130
+ // spell a move as remove-then-reinsert and the machine must survive it
131
+ export function removeChild(requestedParent, child) {
132
+ // A wrap claim leaving: the owner takes its own place back and stays in the tree, nominated for
133
+ // teardown like any other removed node т.к. the wrapper IS leaving
134
+ if (unwrapsOwner(requestedParent, child)) {
135
+ if (hasAttachedBehaviors() || hasAnimatedBindings())
136
+ markDetachCandidate(child);
137
+ return;
138
+ }
139
+ // A slot that IS the child being removed stops being one. Without the clear, `hostFor` below
140
+ // redirects the removal into the node being removed and the next child nests inside the orphan
141
+ if (requestedParent.childHost === child)
142
+ requestedParent.childHost = undefined;
143
+ const parent = hostFor(requestedParent, child);
144
+ // `hasAttachedBehaviors`, not `hasHostBehaviors`: the latter is on from module load in every app
145
+ // just from registering Pressable as a type
146
+ if (hasAttachedBehaviors() || hasAnimatedBindings())
147
+ markDetachCandidate(child);
148
+ // BOTH, and the owner is the one that matters: a composed primitive's behavior lives on the node
149
+ // the adapter named, while `hostFor` redirects the mutation into its internal slot
150
+ armCommitHookForChildChange(requestedParent);
151
+ armCommitHookForChildChange(parent);
152
+ recordRemoveChild(parent, placedNode(child));
153
+ }
154
+ // A structural census of the tree the HOST holds. Walks nothing here, т.к. the walk needs
155
+ // `props.text` and a child list, and JS has neither
156
+ export function censusRetainedTree(roots) {
157
+ flushOps();
158
+ return treeHost()?.census(roots) ?? EMPTY_CENSUS;
159
+ }
@@ -0,0 +1,70 @@
1
+ import type { IMeasureOnSuccess, IMeasureInWindowOnSuccess, IMeasureLayoutOnSuccess } from './fabric';
2
+ import type { IHostBehavior, IPayloadFold } from './host-behavior';
3
+ import type { IClassNameValue } from './style-registry';
4
+ export declare const BRAND: unique symbol;
5
+ export declare const RAW_TEXT_COMPONENT = "RCTRawText";
6
+ export declare const TEXT_COMPONENT = "RCTText";
7
+ export declare const VIRTUAL_TEXT_COMPONENT = "RCTVirtualText";
8
+ export declare const ANCHOR_COMPONENT = "#anchor";
9
+ export declare const VOID_COMPONENT = "#void";
10
+ export declare const SURFACE_COMPONENT = "#surface";
11
+ export interface ISymbioteEvent {
12
+ type: string;
13
+ target: ISymbioteNode;
14
+ currentTarget: ISymbioteNode;
15
+ nativeEvent: Record<string, unknown>;
16
+ stopPropagation: () => void;
17
+ }
18
+ export type IListener = (event: ISymbioteEvent) => unknown;
19
+ export type IEventDispatch = {
20
+ readonly names: ReadonlySet<string>;
21
+ readonly deliver: (node: ISymbioteNode, name: string, event: ISymbioteEvent) => unknown;
22
+ };
23
+ export declare function isSymbioteEvent(value: unknown): value is ISymbioteEvent;
24
+ export interface IClassStyleParts {
25
+ classStyle: unknown;
26
+ explicitStyle: unknown;
27
+ hiddenStyle: unknown;
28
+ className: IClassNameValue | undefined;
29
+ isPressed: boolean;
30
+ activeStyle: unknown;
31
+ activeStyleFromCallback: boolean;
32
+ published: readonly unknown[] | undefined;
33
+ }
34
+ export interface ISymbioteNode {
35
+ readonly [BRAND]: true;
36
+ component: string;
37
+ readonly isText: boolean;
38
+ listeners: Map<string, IListener> | undefined;
39
+ dispatch: IEventDispatch | undefined;
40
+ hasCommitHook: boolean;
41
+ resolvesImageSources: boolean;
42
+ nativeIdWinsOverId: boolean;
43
+ payloadFold: IPayloadFold | undefined;
44
+ hostBehavior: IHostBehavior | undefined;
45
+ styleParts: IClassStyleParts | undefined;
46
+ childHost: ISymbioteNode | undefined;
47
+ wrapper: ISymbioteNode | undefined;
48
+ mayHaveChildren: boolean;
49
+ isTornDown: boolean;
50
+ slot: number;
51
+ slotBatch: number;
52
+ createdBatch: number;
53
+ measure(callback: IMeasureOnSuccess): void;
54
+ measureInWindow(callback: IMeasureInWindowOnSuccess): void;
55
+ measureLayout(relativeToNativeNode: ISymbioteNode | number, onSuccess: IMeasureLayoutOnSuccess, onFail?: () => void): void;
56
+ setNativeProps(nativeProps: Record<string, unknown>): void;
57
+ focus(): void;
58
+ blur(): void;
59
+ scrollTo(options?: {
60
+ x?: number;
61
+ y?: number;
62
+ animated?: boolean;
63
+ }): void;
64
+ scrollToEnd(options?: {
65
+ animated?: boolean;
66
+ }): void;
67
+ flashScrollIndicators(): void;
68
+ }
69
+ export declare function isSymbioteNode(value: unknown): value is ISymbioteNode;
70
+ export declare function isAnchor(node: ISymbioteNode): boolean;
@@ -0,0 +1,36 @@
1
+ // The node's SHAPE and nothing that acts on it: the interface every sibling of `node.ts` writes
2
+ // through, the Fabric view names, and the two guards
3
+ export const BRAND = Symbol('symbiote.node');
4
+ // A node carries the Fabric view name directly, so adding a primitive is just a new string from
5
+ // the adapter. The one name resolved at commit time is text: `isText` marks a container, and a
6
+ // `<Text>` nested inside another commits as a virtual span
7
+ export const RAW_TEXT_COMPONENT = 'RCTRawText';
8
+ export const TEXT_COMPONENT = 'RCTText';
9
+ export const VIRTUAL_TEXT_COMPONENT = 'RCTVirtualText';
10
+ // Vue's runtime-core needs anchor nodes (fragments, `v-if`, `v-for`) to track sibling order and
11
+ // Fabric has no such concept. A real retained node keeps the ordering correct, and the commit walk
12
+ // SKIPS it so no native view is created
13
+ export const ANCHOR_COMPONENT = '#anchor';
14
+ // The sentinel a primitive resolves to when its ENTIRE subtree must vanish from Fabric on this
15
+ // platform (`input-accessory-view` on Android). An anchor hoists its children up, a void node does
16
+ // not: the walk stops there, recursively
17
+ export const VOID_COMPONENT = '#void';
18
+ // The sentinel a SURFACE's own root node carries, so `parentOf` can stop there and a top-level node
19
+ // answers `undefined` for its parent. JS-side name only, т.к. what goes over the wire is `RCTView`
20
+ export const SURFACE_COMPONENT = '#surface';
21
+ // Guard narrowing `unknown` to `ISymbioteEvent`, beside the interface it tests so every adapter
22
+ // checking for a Symbiote event shares one instead of writing its own
23
+ export function isSymbioteEvent(value) {
24
+ if (typeof value !== 'object' || value === null)
25
+ return false;
26
+ const nativeEvent = Reflect.get(value, 'nativeEvent');
27
+ return typeof nativeEvent === 'object' && nativeEvent !== null;
28
+ }
29
+ // `instanceHandle` round-trips through Fabric unchanged and comes back as the event target, so our
30
+ // nodes are branded and the handler can confirm one is ours
31
+ export function isSymbioteNode(value) {
32
+ return typeof value === 'object' && value !== null && BRAND in value;
33
+ }
34
+ export function isAnchor(node) {
35
+ return node.component === ANCHOR_COMPONENT;
36
+ }