@symbiote-native/engine 0.1.3 → 0.1.5
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.
- package/build/accessibility-info/index.android.js +17 -29
- package/build/accessibility-info/index.ios.js +23 -37
- package/build/accessibility-info/shared.d.ts +3 -2
- package/build/accessibility-info/shared.js +25 -2
- package/build/action-sheet-ios/index.d.ts +6 -0
- package/build/action-sheet-ios/index.js +1 -1
- package/build/alert/index.android.js +1 -3
- package/build/alert/index.js +1 -1
- package/build/animated/animations/base.js +5 -14
- package/build/animated/event.js +7 -9
- package/build/animated/graph.d.ts +6 -0
- package/build/animated/graph.js +14 -0
- package/build/animated/interpolation-node.d.ts +0 -1
- package/build/animated/interpolation-node.js +5 -4
- package/build/animated/native/native-animated.js +1 -3
- package/build/animated/operators.d.ts +14 -34
- package/build/animated/operators.js +19 -95
- package/build/animated/value.d.ts +2 -4
- package/build/animated/value.js +17 -5
- package/build/app-state/index.js +17 -22
- package/build/appearance/index.js +17 -25
- package/build/back-handler/index.js +15 -18
- package/build/commit.d.ts +1 -3
- package/build/commit.js +10 -207
- package/build/dimensions/index.js +25 -20
- package/build/events/index.js +6 -151
- package/build/fabric-props.d.ts +3 -0
- package/build/fabric-props.js +186 -0
- package/build/i18n-manager/index.js +3 -5
- package/build/image-loader.d.ts +24 -0
- package/build/image-loader.js +175 -0
- package/build/image-source-resolver.d.ts +9 -0
- package/build/image-source-resolver.js +16 -0
- package/build/index.d.ts +10 -3
- package/build/index.js +18 -9
- package/build/keyboard/index.js +19 -34
- package/build/layout-animation/index.d.ts +1 -0
- package/build/layout-animation/index.js +53 -35
- package/build/native-modules.d.ts +12 -0
- package/build/native-modules.js +64 -1
- package/build/node.d.ts +1 -0
- package/build/node.js +9 -0
- package/build/pan-responder/index.js +1 -3
- package/build/permissions-android/index.js +1 -3
- package/build/platform-color.d.ts +3 -0
- package/build/platform-color.js +24 -0
- package/build/process-background-image/index.js +7 -14
- package/build/process-box-shadow/index.js +1 -5
- package/build/process-filter.js +2 -8
- package/build/registry.js +3 -5
- package/build/settings/index.js +1 -3
- package/build/share/index.ios.js +1 -1
- package/build/status-bar/index.android.js +1 -1
- package/build/style/index.js +2 -4
- package/build/style-registry/index.d.ts +1 -3
- package/build/style-registry/index.js +15 -48
- package/build/style-registry/scope.d.ts +3 -0
- package/build/style-registry/scope.js +41 -0
- package/build/touch-history.d.ts +23 -0
- package/build/touch-history.js +150 -0
- package/build/type-guards.d.ts +4 -0
- package/build/type-guards.js +15 -0
- package/package.json +2 -2
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
// Runtime style registry. Side-effect CSS imports (compiled by the sibling CSS-to-style
|
|
2
2
|
// build package, not this module) call registerStyles() with camelCase keys; components
|
|
3
|
-
// look them up
|
|
4
|
-
// Map<string, ...> lookup, nothing more.
|
|
3
|
+
// look them up via resolveClassName(). No CSS parsing here - just a Map<string, ...> lookup.
|
|
5
4
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// No Tailwind-utility detection layer: this repo's style surface has no Tailwind layer,
|
|
6
|
+
// so the compound lookup below always runs for 2-4-part class strings instead of being
|
|
7
|
+
// gated behind "no part looks like a utility class".
|
|
9
8
|
//
|
|
10
9
|
// kebab-case authoring: a CSS selector `.section-label` always registers under the
|
|
11
10
|
// camelCase key `sectionLabel` (@symbiote-native/css-parser's extractClassName), so a template
|
|
12
|
-
// can write EITHER `class="sectionLabel"` OR `class="section-label"`
|
|
13
|
-
// falls back to the kebab->camel form on a miss
|
|
14
|
-
//
|
|
11
|
+
// can write EITHER `class="sectionLabel"` OR `class="section-label"` - resolveOne below
|
|
12
|
+
// falls back to the kebab->camel form on a miss, since authors don't reliably write the
|
|
13
|
+
// exact camelCase key.
|
|
15
14
|
// Compound lookup tries every ordering of 2-4 space-separated class parts joined by
|
|
16
15
|
// '.' (e.g. "btn primary" -> "btn.primary" / "primary.btn") before falling back to a
|
|
17
16
|
// per-class merge, mirroring CSS compound-selector registration (`.btn.primary { }`).
|
|
@@ -25,14 +24,14 @@ export function registerStyles(styles) {
|
|
|
25
24
|
globalStyles.set(name, style);
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
|
-
//
|
|
27
|
+
// Used between tests to reset registry state.
|
|
29
28
|
export function clearGlobalStyles() {
|
|
30
29
|
globalStyles.clear();
|
|
31
30
|
}
|
|
32
31
|
// A `class`/`className` prop arrives as `unknown` at the routeProp boundary (any adapter can
|
|
33
32
|
// hand over anything); this narrows before resolveClassName without an `as` cast. Shared with
|
|
34
33
|
// adapters/vue/src/components/scroll-view/shared.ts's identical need, rather than each keeping
|
|
35
|
-
// its own copy
|
|
34
|
+
// its own copy - that file imports this one instead of redeclaring it.
|
|
36
35
|
export function isClassNameValue(value) {
|
|
37
36
|
return typeof value === 'string' || (typeof value === 'object' && value !== null);
|
|
38
37
|
}
|
|
@@ -101,10 +100,14 @@ function capitalize(value) {
|
|
|
101
100
|
}
|
|
102
101
|
// Duplicated from @symbiote-native/css-parser's identical helper rather than imported: css-parser
|
|
103
102
|
// pulls in postcss and is build-time only (never shipped in the app bundle), and this registry
|
|
104
|
-
// is the opposite
|
|
103
|
+
// is the opposite - pure runtime, in every app bundle - so importing it here would leak a
|
|
105
104
|
// build-time dependency into the shipped app. The conversion itself is two lines; keeping both
|
|
106
105
|
// copies in sync is a smaller cost than the alternative.
|
|
107
|
-
|
|
106
|
+
//
|
|
107
|
+
// Exported (not just local to this file) because ./scope.ts's Vue `<style scoped>` name
|
|
108
|
+
// rewriter needs the same kebab->camel normalization and is a different responsibility living
|
|
109
|
+
// in a sibling module - see that file's own doc comment for why it's split out of this one.
|
|
110
|
+
export function kebabToCamel(value) {
|
|
108
111
|
return value.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
109
112
|
}
|
|
110
113
|
function tryCompoundLookup(parts) {
|
|
@@ -123,39 +126,3 @@ function resolveOne(name) {
|
|
|
123
126
|
return {};
|
|
124
127
|
return globalStyles.get(trimmed) ?? globalStyles.get(kebabToCamel(trimmed)) ?? {};
|
|
125
128
|
}
|
|
126
|
-
// Pure NAME rewriter for Vue `<style scoped>`: suffixes every class token that this
|
|
127
|
-
// file's scoped block locally defines with `__${scopeId}`, leaving unrecognized tokens
|
|
128
|
-
// (globals, external classes) untouched. Runs at the compiled call site BEFORE Vue's own
|
|
129
|
-
// normalizeClass() collapses string/object/array `class` values to a final string, so it
|
|
130
|
-
// must pre-process all three shapes normalizeClass understands. No registry lookup here —
|
|
131
|
-
// resolveClassName still does the actual style lookup, unchanged, against the rewritten name.
|
|
132
|
-
export function scopeClassName(value, localNames, scopeId) {
|
|
133
|
-
if (value === undefined || value === null)
|
|
134
|
-
return value;
|
|
135
|
-
if (Array.isArray(value)) {
|
|
136
|
-
return value.map(item => scopeClassEntry(item, localNames, scopeId));
|
|
137
|
-
}
|
|
138
|
-
return scopeClassEntry(value, localNames, scopeId);
|
|
139
|
-
}
|
|
140
|
-
// A token arrives as either the camelCase registry key (`sectionLabel`) or its kebab-case
|
|
141
|
-
// authoring form (`section-label`) — normalize to camelCase FIRST, then decide scoping, so
|
|
142
|
-
// `localNames` (always camelCase, built from the css-parser's registered keys) recognizes a
|
|
143
|
-
// kebab-written token. The emitted (possibly suffixed) name is always the camelCase form.
|
|
144
|
-
function scopeToken(token, localNames, scopeId) {
|
|
145
|
-
const camelToken = kebabToCamel(token);
|
|
146
|
-
return localNames.has(camelToken) ? `${camelToken}__${scopeId}` : camelToken;
|
|
147
|
-
}
|
|
148
|
-
function scopeClassEntry(value, localNames, scopeId) {
|
|
149
|
-
if (typeof value === 'object') {
|
|
150
|
-
const scoped = {};
|
|
151
|
-
for (const [name, enabled] of Object.entries(value)) {
|
|
152
|
-
scoped[scopeToken(name, localNames, scopeId)] = enabled;
|
|
153
|
-
}
|
|
154
|
-
return scoped;
|
|
155
|
-
}
|
|
156
|
-
return value
|
|
157
|
-
.split(/\s+/)
|
|
158
|
-
.filter(Boolean)
|
|
159
|
-
.map(token => scopeToken(token, localNames, scopeId))
|
|
160
|
-
.join(' ');
|
|
161
|
-
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type IClassToggleMap = Record<string, boolean | undefined>;
|
|
2
|
+
export type IScopableClassValue = string | IClassToggleMap | Array<string | IClassToggleMap> | undefined | null;
|
|
3
|
+
export declare function scopeClassName(value: IScopableClassValue, localNames: ReadonlySet<string>, scopeId: string): IScopableClassValue;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Vue `<style scoped>` class-name rewriter. Distinct responsibility from the sibling
|
|
2
|
+
// ./index.ts (the CSS class -> style registry): this module does pure NAME rewriting,
|
|
3
|
+
// no registry lookup, no CSS parsing. It runs at the compiled call site of a Vue SFC's
|
|
4
|
+
// scoped-style template - `adapters/vue/metro-vue-transformer.cjs` emits calls to
|
|
5
|
+
// scopeClassName (imported there as `__scopeClass`) - BEFORE Vue's own normalizeClass()
|
|
6
|
+
// collapses string/object/array `class` values to a final string, so it must pre-process
|
|
7
|
+
// all three shapes normalizeClass understands. resolveClassName in ./index.ts still does
|
|
8
|
+
// the actual style lookup, unchanged, against the rewritten (possibly suffixed) name.
|
|
9
|
+
import { kebabToCamel } from './index.js';
|
|
10
|
+
// Suffixes every class token that this file's scoped block locally defines with
|
|
11
|
+
// `__${scopeId}`, leaving unrecognized tokens (globals, external classes) untouched.
|
|
12
|
+
export function scopeClassName(value, localNames, scopeId) {
|
|
13
|
+
if (value === undefined || value === null)
|
|
14
|
+
return value;
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map(item => scopeClassEntry(item, localNames, scopeId));
|
|
17
|
+
}
|
|
18
|
+
return scopeClassEntry(value, localNames, scopeId);
|
|
19
|
+
}
|
|
20
|
+
// A token arrives as either the camelCase registry key (`sectionLabel`) or its kebab-case
|
|
21
|
+
// authoring form (`section-label`) - normalize to camelCase FIRST, then decide scoping, so
|
|
22
|
+
// `localNames` (always camelCase, built from the css-parser's registered keys) recognizes a
|
|
23
|
+
// kebab-written token. The emitted (possibly suffixed) name is always the camelCase form.
|
|
24
|
+
function scopeToken(token, localNames, scopeId) {
|
|
25
|
+
const camelToken = kebabToCamel(token);
|
|
26
|
+
return localNames.has(camelToken) ? `${camelToken}__${scopeId}` : camelToken;
|
|
27
|
+
}
|
|
28
|
+
function scopeClassEntry(value, localNames, scopeId) {
|
|
29
|
+
if (typeof value === 'object') {
|
|
30
|
+
const scoped = {};
|
|
31
|
+
for (const [name, enabled] of Object.entries(value)) {
|
|
32
|
+
scoped[scopeToken(name, localNames, scopeId)] = enabled;
|
|
33
|
+
}
|
|
34
|
+
return scoped;
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
.split(/\s+/)
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.map(token => scopeToken(token, localNames, scopeId))
|
|
40
|
+
.join(' ');
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface ITouchRecord {
|
|
2
|
+
touchActive: boolean;
|
|
3
|
+
startPageX: number;
|
|
4
|
+
startPageY: number;
|
|
5
|
+
startTimeStamp: number;
|
|
6
|
+
currentPageX: number;
|
|
7
|
+
currentPageY: number;
|
|
8
|
+
currentTimeStamp: number;
|
|
9
|
+
previousPageX: number;
|
|
10
|
+
previousPageY: number;
|
|
11
|
+
previousTimeStamp: number;
|
|
12
|
+
}
|
|
13
|
+
interface ITouchHistory {
|
|
14
|
+
touchBank: ITouchRecord[];
|
|
15
|
+
numberActiveTouches: number;
|
|
16
|
+
indexOfSingleActiveTouch: number;
|
|
17
|
+
mostRecentTimeStamp: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const touchHistory: ITouchHistory;
|
|
20
|
+
export declare function recordTouchTrack(kind: 'start' | 'move' | 'end', nativeEvent: Record<string, unknown>): void;
|
|
21
|
+
export declare function resetTouchHistory(): void;
|
|
22
|
+
export declare function attachTouchHistory(nativeEvent: Record<string, unknown>): void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Per-touch position/time tracking, ported from RN's
|
|
2
|
+
// react-native-renderer/.../legacy-events/ResponderTouchHistoryStore.js. PanResponder's
|
|
3
|
+
// multitouch dx/vx math needs each touch's own previous->current delta (RN counts only
|
|
4
|
+
// touches that moved since `_accountsForMovesUpTo`), which a grant-relative centroid of
|
|
5
|
+
// ALL live touches cannot reconstruct. We maintain the bank as touches flow and ATTACH
|
|
6
|
+
// `touchHistory` onto the nativeEvent reaching responder handlers, exactly how
|
|
7
|
+
// ResponderEventPlugin.js sets `*.touchHistory`.
|
|
8
|
+
//
|
|
9
|
+
// events/index.ts consumes only this file's public surface (recordTouchTrack,
|
|
10
|
+
// attachTouchHistory, resetTouchHistory, touchHistory); everything else here is a
|
|
11
|
+
// private implementation detail of the bank.
|
|
12
|
+
import { isRecord } from './type-guards.js';
|
|
13
|
+
// RN's bank is indexed by touch identifier and warns above 20; we never warn (headless
|
|
14
|
+
// events may carry larger or absent ids), we just skip anything out of a sane range.
|
|
15
|
+
const MAX_TOUCH_BANK = 20;
|
|
16
|
+
const touchBank = [];
|
|
17
|
+
export const touchHistory = {
|
|
18
|
+
touchBank,
|
|
19
|
+
numberActiveTouches: 0,
|
|
20
|
+
indexOfSingleActiveTouch: -1,
|
|
21
|
+
mostRecentTimeStamp: 0,
|
|
22
|
+
};
|
|
23
|
+
function toFiniteNumber(value) {
|
|
24
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
25
|
+
}
|
|
26
|
+
// Pull a recordable touch out of an untyped entry. RN's getTouchIdentifier throws on
|
|
27
|
+
// a null id; we skip instead, so events without touch geometry leave the bank untouched.
|
|
28
|
+
function normalizeTouch(raw) {
|
|
29
|
+
if (!isRecord(raw))
|
|
30
|
+
return undefined;
|
|
31
|
+
const identifier = toFiniteNumber(raw.identifier);
|
|
32
|
+
const pageX = toFiniteNumber(raw.pageX);
|
|
33
|
+
const pageY = toFiniteNumber(raw.pageY);
|
|
34
|
+
if (identifier === undefined || pageX === undefined || pageY === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (identifier < 0 || identifier > MAX_TOUCH_BANK)
|
|
37
|
+
return undefined;
|
|
38
|
+
return { identifier, pageX, pageY, timestamp: toFiniteNumber(raw.timestamp) ?? 0 };
|
|
39
|
+
}
|
|
40
|
+
// The changed touches for this frame (start/move/end), defensively read.
|
|
41
|
+
function changedTouchesOf(nativeEvent) {
|
|
42
|
+
const raw = nativeEvent.changedTouches;
|
|
43
|
+
if (!Array.isArray(raw))
|
|
44
|
+
return [];
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const entry of raw) {
|
|
47
|
+
const touch = normalizeTouch(entry);
|
|
48
|
+
if (touch !== undefined)
|
|
49
|
+
out.push(touch);
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
// Count of all touches still down (RN reads nativeEvent.touches.length directly).
|
|
54
|
+
function activeTouchCount(nativeEvent) {
|
|
55
|
+
const raw = nativeEvent.touches;
|
|
56
|
+
return Array.isArray(raw) ? raw.length : 0;
|
|
57
|
+
}
|
|
58
|
+
function recordTouchStart(touch) {
|
|
59
|
+
const record = touchBank[touch.identifier];
|
|
60
|
+
if (record) {
|
|
61
|
+
record.touchActive = true;
|
|
62
|
+
record.startPageX = touch.pageX;
|
|
63
|
+
record.startPageY = touch.pageY;
|
|
64
|
+
record.startTimeStamp = touch.timestamp;
|
|
65
|
+
record.currentPageX = touch.pageX;
|
|
66
|
+
record.currentPageY = touch.pageY;
|
|
67
|
+
record.currentTimeStamp = touch.timestamp;
|
|
68
|
+
record.previousPageX = touch.pageX;
|
|
69
|
+
record.previousPageY = touch.pageY;
|
|
70
|
+
record.previousTimeStamp = touch.timestamp;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
touchBank[touch.identifier] = {
|
|
74
|
+
touchActive: true,
|
|
75
|
+
startPageX: touch.pageX,
|
|
76
|
+
startPageY: touch.pageY,
|
|
77
|
+
startTimeStamp: touch.timestamp,
|
|
78
|
+
currentPageX: touch.pageX,
|
|
79
|
+
currentPageY: touch.pageY,
|
|
80
|
+
currentTimeStamp: touch.timestamp,
|
|
81
|
+
previousPageX: touch.pageX,
|
|
82
|
+
previousPageY: touch.pageY,
|
|
83
|
+
previousTimeStamp: touch.timestamp,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
touchHistory.mostRecentTimeStamp = touch.timestamp;
|
|
87
|
+
}
|
|
88
|
+
// Move and end share the previous<-current shift; only `touchActive` differs.
|
|
89
|
+
function shiftTouchRecord(touch, active) {
|
|
90
|
+
const record = touchBank[touch.identifier];
|
|
91
|
+
if (!record)
|
|
92
|
+
return;
|
|
93
|
+
record.touchActive = active;
|
|
94
|
+
record.previousPageX = record.currentPageX;
|
|
95
|
+
record.previousPageY = record.currentPageY;
|
|
96
|
+
record.previousTimeStamp = record.currentTimeStamp;
|
|
97
|
+
record.currentPageX = touch.pageX;
|
|
98
|
+
record.currentPageY = touch.pageY;
|
|
99
|
+
record.currentTimeStamp = touch.timestamp;
|
|
100
|
+
touchHistory.mostRecentTimeStamp = touch.timestamp;
|
|
101
|
+
}
|
|
102
|
+
function arrayFirst(value) {
|
|
103
|
+
return Array.isArray(value) ? value[0] : undefined;
|
|
104
|
+
}
|
|
105
|
+
// Maintain the bank as a touch frame flows. Mirrors RN's recordTouchTrack: moveish
|
|
106
|
+
// shifts records, startish records + recomputes numberActiveTouches, endish marks the
|
|
107
|
+
// record inactive + rescans for the single remaining touch. `kind` is the touch phase.
|
|
108
|
+
export function recordTouchTrack(kind, nativeEvent) {
|
|
109
|
+
if (kind === 'move') {
|
|
110
|
+
for (const touch of changedTouchesOf(nativeEvent))
|
|
111
|
+
shiftTouchRecord(touch, true);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (kind === 'start') {
|
|
115
|
+
for (const touch of changedTouchesOf(nativeEvent))
|
|
116
|
+
recordTouchStart(touch);
|
|
117
|
+
touchHistory.numberActiveTouches = activeTouchCount(nativeEvent);
|
|
118
|
+
if (touchHistory.numberActiveTouches === 1) {
|
|
119
|
+
const first = normalizeTouch(arrayFirst(nativeEvent.touches));
|
|
120
|
+
touchHistory.indexOfSingleActiveTouch = first?.identifier ?? -1;
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
for (const touch of changedTouchesOf(nativeEvent))
|
|
125
|
+
shiftTouchRecord(touch, false);
|
|
126
|
+
touchHistory.numberActiveTouches = activeTouchCount(nativeEvent);
|
|
127
|
+
if (touchHistory.numberActiveTouches === 1) {
|
|
128
|
+
for (let i = 0; i < touchBank.length; i++) {
|
|
129
|
+
const record = touchBank[i];
|
|
130
|
+
if (record !== undefined && record.touchActive) {
|
|
131
|
+
touchHistory.indexOfSingleActiveTouch = i;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Drop all touch state. Called on a fully-released / cancelled gesture so a stale bank
|
|
138
|
+
// never leaks geometry into the next gesture's first frame.
|
|
139
|
+
export function resetTouchHistory() {
|
|
140
|
+
touchBank.length = 0;
|
|
141
|
+
touchHistory.numberActiveTouches = 0;
|
|
142
|
+
touchHistory.indexOfSingleActiveTouch = -1;
|
|
143
|
+
touchHistory.mostRecentTimeStamp = 0;
|
|
144
|
+
}
|
|
145
|
+
// Attach the live touch history onto the event the responder handlers receive, matching
|
|
146
|
+
// ResponderEventPlugin.js (`grantEvent.touchHistory = ...`, etc.). PanResponder reads
|
|
147
|
+
// it for the per-touch dx/vx math; handlers that ignore it are unaffected.
|
|
148
|
+
export function attachTouchHistory(nativeEvent) {
|
|
149
|
+
nativeEvent.touchHistory = touchHistory;
|
|
150
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
export declare function isBoolean(value: unknown): value is boolean;
|
|
3
|
+
export declare function isNumber(value: unknown): value is number;
|
|
4
|
+
export declare function isString(value: unknown): value is string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Runtime guards to narrow `unknown` at trust boundaries (native payloads, ViewConfig
|
|
2
|
+
// attributes, style values) without an `as` cast. `isRecord` excludes arrays - most
|
|
3
|
+
// call sites mean "a native payload keyed by string," never a list.
|
|
4
|
+
export function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
export function isBoolean(value) {
|
|
8
|
+
return typeof value === 'boolean';
|
|
9
|
+
}
|
|
10
|
+
export function isNumber(value) {
|
|
11
|
+
return typeof value === 'number';
|
|
12
|
+
}
|
|
13
|
+
export function isString(value) {
|
|
14
|
+
return typeof value === 'string';
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "SymbioteNative's retained shadow-tree engine — clone-on-write commit path + event normalization over React Native Fabric, shared by every framework adapter.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"react-native": ">=0.86"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@symbiote-native/test-utils": "0.1.
|
|
35
|
+
"@symbiote-native/test-utils": "0.1.5"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"typecheck": "tsc --build",
|