@miot-rn/common-component 1.0.1
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/README.md +9 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +0 -0
- package/dist/resources/i18n.d.ts +55 -0
- package/dist/resources/i18n.js +212 -0
- package/dist/service/specs/error-code.d.ts +19 -0
- package/dist/service/specs/error-code.js +41 -0
- package/dist/service/specs/index.d.ts +95 -0
- package/dist/service/specs/index.js +412 -0
- package/dist/service/specs/spec.d.ts +10 -0
- package/dist/service/specs/spec.js +109 -0
- package/dist/service/specs/specCache.d.ts +85 -0
- package/dist/service/specs/specCache.js +421 -0
- package/dist/service/specs/std-spec.d.ts +5 -0
- package/dist/service/specs/std-spec.js +228 -0
- package/dist/service/specs/subject.d.ts +20 -0
- package/dist/service/specs/subject.js +145 -0
- package/dist/service/specs/utils.d.ts +28 -0
- package/dist/service/specs/utils.js +46 -0
- package/dist/specs/i18n-parser.d.ts +10 -0
- package/dist/specs/i18n-parser.js +300 -0
- package/dist/specs/instance-parser.d.ts +26 -0
- package/dist/specs/instance-parser.js +706 -0
- package/dist/store/useGlobalSpecManager.d.ts +320 -0
- package/dist/store/useGlobalSpecManager.js +867 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/fns.d.ts +12 -0
- package/dist/utils/fns.js +308 -0
- package/dist/utils/get-value-or-default.d.ts +1 -0
- package/dist/utils/get-value-or-default.js +4 -0
- package/dist/utils/i18n.d.ts +1 -0
- package/dist/utils/i18n.js +141 -0
- package/dist/utils/objects.d.ts +2 -0
- package/dist/utils/objects.js +222 -0
- package/dist/utils/spec.d.ts +8 -0
- package/dist/utils/spec.js +171 -0
- package/dist/utils/types.d.ts +3 -0
- package/dist/utils/types.js +25 -0
- package/package.json +27 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { getType } from "./types";
|
|
2
|
+
const ReactSpecialKeys = new Set(['_owner', '__v', '__o']); // 预定义需跳过的特殊属性
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 判断基础类型
|
|
6
|
+
* @param v
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
function isPrimary(v) {
|
|
11
|
+
if (v === null || v === undefined) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return ['number', 'string', 'boolean', 'symbol'].includes(typeof v);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
|
|
18
|
+
const TypeHandlers = {
|
|
19
|
+
'[object Array]': {
|
|
20
|
+
related: false,
|
|
21
|
+
create: () => {
|
|
22
|
+
return [];
|
|
23
|
+
},
|
|
24
|
+
forEach: (src, fn) => {
|
|
25
|
+
src.forEach((v, i) => fn(v, i));
|
|
26
|
+
},
|
|
27
|
+
map: (src, fn) => {
|
|
28
|
+
return src.map((v, i) => fn(v, i));
|
|
29
|
+
},
|
|
30
|
+
set: (src, key, value) => {
|
|
31
|
+
src[key] = value;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
'[object Object]': {
|
|
35
|
+
related: true,
|
|
36
|
+
create: () => {
|
|
37
|
+
return {};
|
|
38
|
+
},
|
|
39
|
+
forEach: (src, fn) => {
|
|
40
|
+
Object.entries(src).forEach(([k, v]) => fn(v, k));
|
|
41
|
+
},
|
|
42
|
+
map: (src, fn) => {
|
|
43
|
+
return Object.entries(src).map(([k, v]) => fn(v, k));
|
|
44
|
+
},
|
|
45
|
+
set: (src, key, value) => {
|
|
46
|
+
src[key] = value;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
'[object Set]': {
|
|
50
|
+
related: false,
|
|
51
|
+
create: () => {
|
|
52
|
+
return new Set();
|
|
53
|
+
},
|
|
54
|
+
forEach: (src, fn) => {
|
|
55
|
+
src.forEach(v => fn(v));
|
|
56
|
+
},
|
|
57
|
+
map: (src, fn) => {
|
|
58
|
+
return Array.from(src.entries()).map(vs => fn(vs[0]));
|
|
59
|
+
},
|
|
60
|
+
set: (src, key, value) => {
|
|
61
|
+
src.add(value);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
'[object Map]': {
|
|
65
|
+
related: true,
|
|
66
|
+
create: () => {
|
|
67
|
+
return new Map();
|
|
68
|
+
},
|
|
69
|
+
forEach: (src, fn) => {
|
|
70
|
+
src.forEach((v, k) => fn(v, k));
|
|
71
|
+
},
|
|
72
|
+
map: (src, fn) => {
|
|
73
|
+
return Array.from(src.entries()).map(([k, v]) => fn(v, k));
|
|
74
|
+
},
|
|
75
|
+
set: (src, key, value) => {
|
|
76
|
+
src.set(key, value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
|
|
81
|
+
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
|
+
function _copy(value, memo) {
|
|
84
|
+
if (isPrimary(value) || value instanceof Function) {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
let newValue = memo.get(value);
|
|
88
|
+
if (newValue) {
|
|
89
|
+
return newValue;
|
|
90
|
+
}
|
|
91
|
+
const type = TypeHandlers[Object.prototype.toString.call(value)];
|
|
92
|
+
if (type !== undefined) {
|
|
93
|
+
newValue = type.create();
|
|
94
|
+
memo.set(value, newValue);
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
type.forEach(value, (v, k) => {
|
|
97
|
+
type.set(newValue, k, _copy(v, memo));
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
// 不认识的类型默认处理
|
|
101
|
+
newValue = JSON.parse(JSON.stringify(value));
|
|
102
|
+
}
|
|
103
|
+
return newValue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 深度拷贝函数
|
|
108
|
+
* 1 基础类型:直接返回原值
|
|
109
|
+
* 2 数组 or 对象:深copy,支持 Object, Array, Map, Set, Function
|
|
110
|
+
* @returns {*}
|
|
111
|
+
* @param value
|
|
112
|
+
*/
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
|
+
export function copy(value) {
|
|
115
|
+
return _copy(value, new Map());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @description 深度比较两个对象是否相等(优化版)基础版本来源:https://github.com/FormidableLabs/react-fast-compare
|
|
120
|
+
* @author guhao
|
|
121
|
+
* @param {any} a
|
|
122
|
+
* @param {any} b
|
|
123
|
+
* @returns {boolean}
|
|
124
|
+
*/
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
export function deepEqual(a, b) {
|
|
127
|
+
// 快速相等(包括 +0/-0 和 NaN 处理)
|
|
128
|
+
if (Object.is(a, b)) return true;
|
|
129
|
+
|
|
130
|
+
// 类型检查
|
|
131
|
+
const typeA = getType(a);
|
|
132
|
+
const typeB = getType(b);
|
|
133
|
+
if (typeA !== typeB) return false;
|
|
134
|
+
|
|
135
|
+
// 处理非对象类型
|
|
136
|
+
if (!a || !b || typeof a !== 'object') {
|
|
137
|
+
return typeA === 'Date' ? a.getTime() === b.getTime() : false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 构造函数一致性检查
|
|
141
|
+
if (a.constructor !== b.constructor) return false;
|
|
142
|
+
|
|
143
|
+
// 分类型处理
|
|
144
|
+
switch (typeA) {
|
|
145
|
+
// 数组处理(反向遍历优化)
|
|
146
|
+
case 'Array':
|
|
147
|
+
{
|
|
148
|
+
if (a.length !== b.length) return false;
|
|
149
|
+
for (let i = a.length - 1; i >= 0; i--) {
|
|
150
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Date 处理(时间戳比对)
|
|
156
|
+
case 'Date':
|
|
157
|
+
return a.getTime() === b.getTime();
|
|
158
|
+
|
|
159
|
+
// RegExp 处理(源和标志比对)
|
|
160
|
+
case 'RegExp':
|
|
161
|
+
return a.source === b.source && a.flags === b.flags;
|
|
162
|
+
|
|
163
|
+
// Map 处理(合并两次遍历为一次)
|
|
164
|
+
case 'Map':
|
|
165
|
+
{
|
|
166
|
+
if (a.size !== b.size) return false;
|
|
167
|
+
for (const [key, val] of a) {
|
|
168
|
+
if (!b.has(key) || !deepEqual(val, b.get(key))) return false;
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Set 处理(值存在性检查)
|
|
174
|
+
case 'Set':
|
|
175
|
+
{
|
|
176
|
+
if (a.size !== b.size) return false;
|
|
177
|
+
for (const val of a) {
|
|
178
|
+
if (!b.has(val)) return false;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ArrayBuffer 视图处理(直接内存比对)
|
|
184
|
+
case 'ArrayBufferView':
|
|
185
|
+
{
|
|
186
|
+
const viewA = new DataView(a.buffer, a.byteOffset, a.byteLength);
|
|
187
|
+
const viewB = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
188
|
+
if (viewA.byteLength !== viewB.byteLength) return false;
|
|
189
|
+
for (let i = 0; i < viewA.byteLength; i++) {
|
|
190
|
+
if (viewA.getUint8(i) !== viewB.getUint8(i)) return false;
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 通用对象处理
|
|
196
|
+
case 'Object':
|
|
197
|
+
{
|
|
198
|
+
const keysA = Object.keys(a);
|
|
199
|
+
const keysB = Object.keys(b);
|
|
200
|
+
if (keysA.length !== keysB.length) return false;
|
|
201
|
+
|
|
202
|
+
// 使用 Set 优化属性存在性检查
|
|
203
|
+
const keysBSet = new Set(keysB);
|
|
204
|
+
for (const key of keysA) {
|
|
205
|
+
if (!keysBSet.has(key)) return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 深度比较属性值(跳过 React 特殊属性)
|
|
209
|
+
for (const key of keysA) {
|
|
210
|
+
// @ts-ignore
|
|
211
|
+
// eslint-disable-next-line no-continue
|
|
212
|
+
if (ReactSpecialKeys.has(key) && a.$$typeof) continue;
|
|
213
|
+
if (!deepEqual(a[key], b[key])) return false;
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 其他未处理类型默认返回 false
|
|
219
|
+
default:
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Spec } from '../types';
|
|
2
|
+
export declare function isSpecProp(prop: any): boolean;
|
|
3
|
+
export declare function encodeProp(prop: Spec): string;
|
|
4
|
+
export declare function decodeProp(key: string): any;
|
|
5
|
+
export declare function encodeSceneKey(o: Spec): string;
|
|
6
|
+
export declare function encodeIidsKey(o: Spec): string;
|
|
7
|
+
export declare function specAccess(prop: Spec, type: string): boolean;
|
|
8
|
+
export declare function specsToSubscribeKeys(specs?: Spec[]): string[];
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import { Device } from 'miot';
|
|
4
|
+
export function isSpecProp(prop) {
|
|
5
|
+
return !!(prop && prop.siid && typeof prop.siid === 'number');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 需要和服务端保持一致
|
|
10
|
+
* @param prop
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function encodeProp(prop) {
|
|
14
|
+
const {
|
|
15
|
+
miid,
|
|
16
|
+
siid,
|
|
17
|
+
piid,
|
|
18
|
+
aiid,
|
|
19
|
+
eiid
|
|
20
|
+
} = prop || {};
|
|
21
|
+
if (eiid !== null && eiid !== undefined) {
|
|
22
|
+
return miid ? `event.${miid}.${siid}.${eiid}` : `event.${siid}.${eiid}`;
|
|
23
|
+
}
|
|
24
|
+
if (aiid !== null && aiid !== undefined) {
|
|
25
|
+
return miid ? `action.${miid}.${siid}.${aiid}` : `action.${siid}.${aiid}`;
|
|
26
|
+
} // if (piid !== null && piid !== undefined) {
|
|
27
|
+
return miid ? `prop.${miid}.${siid}.${piid}` : `prop.${siid}.${piid}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 需要和服务端保持一致
|
|
32
|
+
* @returns { { siid, piid, aiid, eiid } |null}
|
|
33
|
+
* @param key
|
|
34
|
+
*/
|
|
35
|
+
export function decodeProp(key) {
|
|
36
|
+
const res = {};
|
|
37
|
+
const parts = key.split('.');
|
|
38
|
+
if (parts.length === 3) {
|
|
39
|
+
const type = parts[0];
|
|
40
|
+
const sNumber = parseInt(parts[1], 10);
|
|
41
|
+
// 2 for '.'
|
|
42
|
+
const suffix = key.slice(parts[0].length + parts[1].length + 2);
|
|
43
|
+
const isNotSpec = isNaN(sNumber);
|
|
44
|
+
const siid = isNotSpec ? parts[1] : sNumber;
|
|
45
|
+
const xiid = isNotSpec ? suffix : parseInt(suffix, 10);
|
|
46
|
+
if (type === 'event') {
|
|
47
|
+
return {
|
|
48
|
+
siid,
|
|
49
|
+
eiid: xiid
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (type === 'action') {
|
|
53
|
+
return {
|
|
54
|
+
siid,
|
|
55
|
+
aiid: xiid
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (type === 'prop') {
|
|
59
|
+
return {
|
|
60
|
+
siid,
|
|
61
|
+
piid: xiid
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
} else if (parts.length >= 4) {
|
|
65
|
+
const type = parts[0];
|
|
66
|
+
const mNumber = parseInt(parts[1], 10);
|
|
67
|
+
const sNumber = parseInt(parts[2], 10);
|
|
68
|
+
const suffix = key.slice(parts[0].length + parts[1].length + parts[2].length + 3); // 3 for '.'
|
|
69
|
+
const isNotSpec = isNaN(sNumber);
|
|
70
|
+
const miid = isNotSpec ? parts[1] : mNumber;
|
|
71
|
+
const siid = isNotSpec ? parts[2] : sNumber;
|
|
72
|
+
const xiid = isNotSpec ? suffix : parseInt(suffix, 10);
|
|
73
|
+
if (type === 'event') {
|
|
74
|
+
return {
|
|
75
|
+
miid,
|
|
76
|
+
siid,
|
|
77
|
+
eiid: xiid
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (type === 'action') {
|
|
81
|
+
return {
|
|
82
|
+
miid,
|
|
83
|
+
siid,
|
|
84
|
+
aiid: xiid
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (type === 'prop') {
|
|
88
|
+
return {
|
|
89
|
+
miid,
|
|
90
|
+
siid,
|
|
91
|
+
piid: xiid
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return res;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 根据spec 获取智能场景的key
|
|
100
|
+
* @param {object} o
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
export function encodeSceneKey(o) {
|
|
104
|
+
if (!o) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
const {
|
|
108
|
+
miid,
|
|
109
|
+
siid,
|
|
110
|
+
piid,
|
|
111
|
+
aiid,
|
|
112
|
+
eiid
|
|
113
|
+
} = o;
|
|
114
|
+
const type = eiid ? 'event' : aiid ? 'action' : piid ? 'prop' : '';
|
|
115
|
+
if (!type || !siid) {
|
|
116
|
+
return '';
|
|
117
|
+
}
|
|
118
|
+
return miid ? `${type}.${Device.model}.${miid}.${siid}.${eiid || aiid || piid}` : `${type}.${Device.model}.${siid}.${eiid || aiid || piid}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 根据spec 获取iids 组装的key
|
|
123
|
+
* @param {object} o
|
|
124
|
+
* @returns {string}
|
|
125
|
+
*/
|
|
126
|
+
export function encodeIidsKey(o) {
|
|
127
|
+
if (!o) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
const {
|
|
131
|
+
miid,
|
|
132
|
+
siid,
|
|
133
|
+
piid,
|
|
134
|
+
aiid,
|
|
135
|
+
eiid
|
|
136
|
+
} = o;
|
|
137
|
+
const xiid = eiid || aiid || piid;
|
|
138
|
+
if (!xiid || !siid) {
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
return miid ? `${miid}.${siid}.${xiid}` : `${siid}.${xiid}`;
|
|
142
|
+
}
|
|
143
|
+
export function specAccess(prop, type) {
|
|
144
|
+
if (!prop) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
const access = prop.access || prop.prop?.access || undefined;
|
|
148
|
+
const gattAccess = prop['gatt-access'] || prop.prop?.['gatt-access'] || undefined;
|
|
149
|
+
if (
|
|
150
|
+
// gatt-access 匹配(蓝牙网关上报、插件代报)
|
|
151
|
+
gattAccess && gattAccess.includes(type) ||
|
|
152
|
+
// access 匹配时
|
|
153
|
+
access && access.includes(type)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 根据spec的订阅key
|
|
161
|
+
* @param {Array<object>} o
|
|
162
|
+
* @returns {Array<string>}
|
|
163
|
+
*/
|
|
164
|
+
export function specsToSubscribeKeys(specs = []) {
|
|
165
|
+
const keys = specs.filter(prop => {
|
|
166
|
+
return prop && (prop.eiid || specAccess(prop, 'notify')) && (!prop.dataSource || prop.dataSource === 1);
|
|
167
|
+
}).map(s => {
|
|
168
|
+
return encodeProp(s);
|
|
169
|
+
});
|
|
170
|
+
return keys;
|
|
171
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
export const getType = value => Object.prototype.toString.call(value).match(/\[object (.*)\]/)[1];
|
|
3
|
+
/**
|
|
4
|
+
* 判断 value 的类型
|
|
5
|
+
* @param {string|Array<string>} type
|
|
6
|
+
* @param {any} value
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export const isTypeOf = (type, value) => {
|
|
10
|
+
const valueType = getType(value);
|
|
11
|
+
if (Array.isArray(type)) {
|
|
12
|
+
return type.includes(valueType);
|
|
13
|
+
}
|
|
14
|
+
return valueType === type;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @description value是否为array
|
|
19
|
+
* @param {any} value
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
export const isArrayValue = value => {
|
|
23
|
+
const valueType = getType(value);
|
|
24
|
+
return valueType === 'Array';
|
|
25
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miot-rn/common-component",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"build": "rm -rf dist && rm -f tsconfig.tsbuildinfo && tsc && babel src -d dist --config-file ../../babel.config.js --extensions '.ts,.tsx' --verbose",
|
|
6
|
+
"lint": "eslint src/**/*.ts",
|
|
7
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
8
|
+
"tsc": "rm -f tsconfig.tsbuildinfo && tsc",
|
|
9
|
+
"format": "prettier --write src/**/*.ts"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"min_sdk_api_level": 10115,
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@types/react": "^16.14.0",
|
|
21
|
+
"@types/react-native": "^0.61.23",
|
|
22
|
+
"react": "^16.14.0",
|
|
23
|
+
"react-native": "0.61.0",
|
|
24
|
+
"zustand": "^4.4.1"
|
|
25
|
+
},
|
|
26
|
+
"gitHead": "3e6285821f0cebb8cdce54ea0e2a6fa624453ef6"
|
|
27
|
+
}
|