@buoy-gg/storage 7.0.16 → 7.0.18
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/lib/commonjs/index.js +13 -0
- package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +5 -602
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +17 -1
- package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +13 -10
- package/lib/commonjs/storage/hooks/useMMKVKeys.js +28 -23
- package/lib/commonjs/storage/stores/storageEventStore.js +85 -1
- package/lib/commonjs/storage/sync/storageSyncAdapter.js +214 -6
- package/lib/commonjs/storage/utils/requiredStorageKeyScope.js +17 -0
- package/lib/commonjs/storage/utils/snapshotProvider.js +447 -0
- package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +17 -3
- package/lib/module/index.js +9 -1
- package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +7 -603
- package/lib/module/storage/components/StorageModalWithTabs.js +17 -1
- package/lib/module/storage/hooks/useAsyncStorageKeys.js +13 -10
- package/lib/module/storage/hooks/useMMKVKeys.js +28 -23
- package/lib/module/storage/stores/storageEventStore.js +81 -0
- package/lib/module/storage/sync/storageSyncAdapter.js +215 -7
- package/lib/module/storage/utils/requiredStorageKeyScope.js +13 -0
- package/lib/module/storage/utils/snapshotProvider.js +445 -0
- package/lib/module/storage/utils/storageTimeTravelUtils.js +17 -3
- package/lib/typescript/index.d.ts +3 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +7 -18
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +8 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +1 -1
- package/lib/typescript/storage/stores/storageEventStore.d.ts +26 -0
- package/lib/typescript/storage/stores/storageEventStore.d.ts.map +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +71 -5
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/storage/types.d.ts +9 -9
- package/lib/typescript/storage/types.d.ts.map +1 -1
- package/lib/typescript/storage/utils/requiredStorageKeyScope.d.ts +10 -0
- package/lib/typescript/storage/utils/requiredStorageKeyScope.d.ts.map +1 -0
- package/lib/typescript/storage/utils/snapshotProvider.d.ts +2 -0
- package/lib/typescript/storage/utils/snapshotProvider.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +1 -1
- package/package.json +9 -6
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Time Machine snapshot provider for device storage (AsyncStorage + MMKV +
|
|
5
|
+
* SecureStore).
|
|
6
|
+
*
|
|
7
|
+
* Registered into @buoy-gg/shared-ui's stateSnapshotRegistry at module init;
|
|
8
|
+
* consumed by @buoy-gg/time-machine.
|
|
9
|
+
*
|
|
10
|
+
* Buoy's own keys (isDevToolsStorageKey — @react_buoy*, @buoy*, buoy-*,
|
|
11
|
+
* legacy patterns) are excluded on BOTH capture and restore, so a snapshot
|
|
12
|
+
* can never clobber dev-tool settings. Restore is a diff: app keys missing
|
|
13
|
+
* from the snapshot are deleted, snapshot entries are written back.
|
|
14
|
+
*
|
|
15
|
+
* Known-lossy by design (each surfaces a warning/skip):
|
|
16
|
+
* - MMKV ArrayBuffer values are never transported.
|
|
17
|
+
* - SecureStore biometric keys are never read or written (auth prompt).
|
|
18
|
+
* - SecureStore keys outside the registered descriptor list are invisible.
|
|
19
|
+
*/
|
|
20
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
21
|
+
import { isDevToolsStorageKey, registerStateSnapshotProvider } from "@buoy-gg/shared-ui";
|
|
22
|
+
import { readMany, writeMany, removeMany } from "./asyncStorageCompat";
|
|
23
|
+
import { clearAllAppStorage } from "./clearAllStorage";
|
|
24
|
+
import { mmkvInstanceRegistry } from "./MMKVInstanceRegistry";
|
|
25
|
+
import { detectMMKVType } from "./mmkvTypeDetection";
|
|
26
|
+
import { secureStoreRegistry } from "./SecureStoreRegistry";
|
|
27
|
+
function deleteMMKVKey(instance, key) {
|
|
28
|
+
// v4 renamed delete() to remove() — support both
|
|
29
|
+
if (instance.remove) instance.remove(key);else instance.delete?.(key);
|
|
30
|
+
}
|
|
31
|
+
function status() {
|
|
32
|
+
return {
|
|
33
|
+
canCapture: true,
|
|
34
|
+
canRestore: true
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Shared by capture() and the live side of previewItems(). */
|
|
39
|
+
async function captureStorageData() {
|
|
40
|
+
const warnings = [];
|
|
41
|
+
const appKeys = (await AsyncStorage.getAllKeys()).filter(k => !isDevToolsStorageKey(k));
|
|
42
|
+
const asyncPairs = (await readMany(appKeys)).filter(pair => pair[1] !== null);
|
|
43
|
+
const mmkv = {};
|
|
44
|
+
for (const {
|
|
45
|
+
id,
|
|
46
|
+
instance
|
|
47
|
+
} of mmkvInstanceRegistry.getAll()) {
|
|
48
|
+
try {
|
|
49
|
+
mmkv[id] = instance.getAllKeys().filter(key => !isDevToolsStorageKey(key)).map(key => {
|
|
50
|
+
const {
|
|
51
|
+
value,
|
|
52
|
+
type: valueType
|
|
53
|
+
} = detectMMKVType(instance, key);
|
|
54
|
+
if (valueType === "buffer") {
|
|
55
|
+
warnings.push(`mmkv "${id}"/"${key}": ArrayBuffer value not capturable`);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
key,
|
|
59
|
+
value,
|
|
60
|
+
valueType
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
} catch {
|
|
64
|
+
warnings.push(`mmkv "${id}": unreadable, captured without entries`);
|
|
65
|
+
mmkv[id] = [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const secure = {};
|
|
69
|
+
const secureModule = secureStoreRegistry.getModule();
|
|
70
|
+
for (const {
|
|
71
|
+
key,
|
|
72
|
+
options
|
|
73
|
+
} of secureStoreRegistry.getAll()) {
|
|
74
|
+
if (options.requireAuthentication) {
|
|
75
|
+
warnings.push(`secure "${key}": biometric-protected, not capturable`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
let value = null;
|
|
79
|
+
if (secureModule) {
|
|
80
|
+
try {
|
|
81
|
+
value = await secureModule.getItemAsync(key, options);
|
|
82
|
+
} catch {
|
|
83
|
+
warnings.push(`secure "${key}": unreadable, captured as unset`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
secure[key] = value;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
data: {
|
|
90
|
+
async: asyncPairs,
|
|
91
|
+
mmkv,
|
|
92
|
+
secure
|
|
93
|
+
},
|
|
94
|
+
warnings
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function capture() {
|
|
98
|
+
const {
|
|
99
|
+
data,
|
|
100
|
+
warnings
|
|
101
|
+
} = await captureStorageData();
|
|
102
|
+
return {
|
|
103
|
+
data,
|
|
104
|
+
warnings
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function restore(data, options) {
|
|
108
|
+
const snapshot = data;
|
|
109
|
+
const excludedSet = new Set(options?.excludeKeys ?? []);
|
|
110
|
+
const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
|
|
111
|
+
/** True when this item may be touched (scope + exclusions agree). */
|
|
112
|
+
const touch = key => !excludedSet.has(key) && (!includedSet || includedSet.has(key));
|
|
113
|
+
let excludedHits = 0;
|
|
114
|
+
const result = {
|
|
115
|
+
ok: true,
|
|
116
|
+
applied: 0,
|
|
117
|
+
skipped: [],
|
|
118
|
+
warnings: []
|
|
119
|
+
};
|
|
120
|
+
if (!snapshot) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
applied: 0,
|
|
124
|
+
skipped: [],
|
|
125
|
+
warnings: ["empty storage snapshot"]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ── AsyncStorage: delete app keys missing from the snapshot, write the rest ──
|
|
130
|
+
try {
|
|
131
|
+
const snapPairs = (snapshot.async ?? []).filter(([k]) => !isDevToolsStorageKey(k) && touch(`async:${k}`));
|
|
132
|
+
const snapKeys = new Set(snapPairs.map(([k]) => k));
|
|
133
|
+
const liveKeys = (await AsyncStorage.getAllKeys()).filter(k => !isDevToolsStorageKey(k));
|
|
134
|
+
const toRemove = liveKeys.filter(k => !snapKeys.has(k) && touch(`async:${k}`));
|
|
135
|
+
excludedHits += (snapshot.async ?? []).length - snapPairs.length;
|
|
136
|
+
if (toRemove.length > 0) await removeMany(toRemove);
|
|
137
|
+
if (snapPairs.length > 0) await writeMany(snapPairs);
|
|
138
|
+
result.applied += snapPairs.length + toRemove.length;
|
|
139
|
+
} catch (e) {
|
|
140
|
+
result.ok = false;
|
|
141
|
+
result.warnings.push(`asyncstorage restore failed: ${String(e)}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── MMKV: per registered writable instance, diff against the snapshot ──
|
|
145
|
+
for (const [id, entries] of Object.entries(snapshot.mmkv ?? {})) {
|
|
146
|
+
const registered = mmkvInstanceRegistry.get(id);
|
|
147
|
+
if (!registered) {
|
|
148
|
+
result.skipped.push({
|
|
149
|
+
id: `mmkv:${id}`,
|
|
150
|
+
reason: "instance not registered"
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (registered.instance.isReadOnly) {
|
|
155
|
+
result.skipped.push({
|
|
156
|
+
id: `mmkv:${id}`,
|
|
157
|
+
reason: "instance registered read-only"
|
|
158
|
+
});
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const {
|
|
162
|
+
instance
|
|
163
|
+
} = registered;
|
|
164
|
+
try {
|
|
165
|
+
const snapKeys = new Set(entries.map(e => e.key));
|
|
166
|
+
const liveKeys = instance.getAllKeys().filter(key => !isDevToolsStorageKey(key));
|
|
167
|
+
for (const key of liveKeys) {
|
|
168
|
+
if (!touch(`mmkv:${id}/${key}`)) {
|
|
169
|
+
excludedHits += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (!snapKeys.has(key)) {
|
|
173
|
+
deleteMMKVKey(instance, key);
|
|
174
|
+
result.applied += 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
for (const entry of entries) {
|
|
178
|
+
if (isDevToolsStorageKey(entry.key)) continue;
|
|
179
|
+
if (!touch(`mmkv:${id}/${entry.key}`)) continue;
|
|
180
|
+
if (entry.valueType === "buffer") {
|
|
181
|
+
result.skipped.push({
|
|
182
|
+
id: `mmkv:${id}/${entry.key}`,
|
|
183
|
+
reason: "ArrayBuffer values cannot be restored"
|
|
184
|
+
});
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
instance.set(entry.key, entry.value);
|
|
188
|
+
result.applied += 1;
|
|
189
|
+
}
|
|
190
|
+
} catch (e) {
|
|
191
|
+
result.ok = false;
|
|
192
|
+
result.warnings.push(`mmkv "${id}" restore failed: ${String(e)}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── SecureStore: diff over the registered, non-biometric descriptor list ──
|
|
197
|
+
const secureSnap = snapshot.secure ?? {};
|
|
198
|
+
for (const {
|
|
199
|
+
key,
|
|
200
|
+
options
|
|
201
|
+
} of secureStoreRegistry.getAll()) {
|
|
202
|
+
if (!touch(`secure:${key}`)) {
|
|
203
|
+
excludedHits += 1;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (options.requireAuthentication) {
|
|
207
|
+
if (key in secureSnap) {
|
|
208
|
+
result.skipped.push({
|
|
209
|
+
id: `secure:${key}`,
|
|
210
|
+
reason: "biometric-protected keys are never written"
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
if (key in secureSnap) {
|
|
217
|
+
const value = secureSnap[key];
|
|
218
|
+
if (value === null) await secureStoreRegistry.deleteValue(key);else await secureStoreRegistry.setValue(key, value);
|
|
219
|
+
} else {
|
|
220
|
+
// Registered key with no snapshot entry — snapshot predates it; unset.
|
|
221
|
+
await secureStoreRegistry.deleteValue(key);
|
|
222
|
+
}
|
|
223
|
+
result.applied += 1;
|
|
224
|
+
} catch (e) {
|
|
225
|
+
result.ok = false;
|
|
226
|
+
result.warnings.push(`secure "${key}" restore failed: ${String(e)}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
for (const key of Object.keys(secureSnap)) {
|
|
230
|
+
if (!touch(`secure:${key}`)) continue;
|
|
231
|
+
if (!secureStoreRegistry.getAll().some(e => e.key === key)) {
|
|
232
|
+
result.skipped.push({
|
|
233
|
+
id: `secure:${key}`,
|
|
234
|
+
reason: "key no longer registered with registerSecureStoreKeys"
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (excludedHits > 0) {
|
|
239
|
+
result.warnings.push(`${excludedHits} item(s) excluded by you`);
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Wipe to fresh-install state (Buoy keys preserved). Used by baseline restore. */
|
|
245
|
+
async function clear() {
|
|
246
|
+
const result = {
|
|
247
|
+
ok: true,
|
|
248
|
+
applied: 0,
|
|
249
|
+
skipped: [],
|
|
250
|
+
warnings: []
|
|
251
|
+
};
|
|
252
|
+
try {
|
|
253
|
+
await clearAllAppStorage();
|
|
254
|
+
result.applied += 1;
|
|
255
|
+
} catch (e) {
|
|
256
|
+
result.ok = false;
|
|
257
|
+
result.warnings.push(`asyncstorage clear failed: ${String(e)}`);
|
|
258
|
+
}
|
|
259
|
+
for (const {
|
|
260
|
+
id,
|
|
261
|
+
instance
|
|
262
|
+
} of mmkvInstanceRegistry.getAll()) {
|
|
263
|
+
if (instance.isReadOnly) {
|
|
264
|
+
result.skipped.push({
|
|
265
|
+
id: `mmkv:${id}`,
|
|
266
|
+
reason: "instance registered read-only"
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
for (const key of instance.getAllKeys().filter(k => !isDevToolsStorageKey(k))) {
|
|
272
|
+
deleteMMKVKey(instance, key);
|
|
273
|
+
result.applied += 1;
|
|
274
|
+
}
|
|
275
|
+
} catch (e) {
|
|
276
|
+
result.ok = false;
|
|
277
|
+
result.warnings.push(`mmkv "${id}" clear failed: ${String(e)}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (const {
|
|
281
|
+
key,
|
|
282
|
+
options
|
|
283
|
+
} of secureStoreRegistry.getAll()) {
|
|
284
|
+
if (options.requireAuthentication) {
|
|
285
|
+
result.skipped.push({
|
|
286
|
+
id: `secure:${key}`,
|
|
287
|
+
reason: "biometric-protected keys are never written"
|
|
288
|
+
});
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
await secureStoreRegistry.deleteValue(key);
|
|
293
|
+
result.applied += 1;
|
|
294
|
+
} catch (e) {
|
|
295
|
+
result.ok = false;
|
|
296
|
+
result.warnings.push(`secure "${key}" clear failed: ${String(e)}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
const ITEM_RENDER_CHAR_LIMIT = 256 * 1024;
|
|
302
|
+
function classifyValue(key, label, oldValue, newValue, inOld, inNew) {
|
|
303
|
+
if (!inNew) return {
|
|
304
|
+
key,
|
|
305
|
+
label,
|
|
306
|
+
verdict: "removed",
|
|
307
|
+
oldValue
|
|
308
|
+
};
|
|
309
|
+
if (!inOld) return {
|
|
310
|
+
key,
|
|
311
|
+
label,
|
|
312
|
+
verdict: "added",
|
|
313
|
+
newValue
|
|
314
|
+
};
|
|
315
|
+
if (Object.is(oldValue, newValue)) return {
|
|
316
|
+
key,
|
|
317
|
+
label,
|
|
318
|
+
verdict: "unchanged"
|
|
319
|
+
};
|
|
320
|
+
const tooLarge = typeof oldValue === "string" && oldValue.length > ITEM_RENDER_CHAR_LIMIT || typeof newValue === "string" && newValue.length > ITEM_RENDER_CHAR_LIMIT;
|
|
321
|
+
if (tooLarge) {
|
|
322
|
+
return {
|
|
323
|
+
key,
|
|
324
|
+
label,
|
|
325
|
+
verdict: "too-large",
|
|
326
|
+
reason: "value too large to render a diff — restore still applies it"
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
key,
|
|
331
|
+
label,
|
|
332
|
+
verdict: "changed",
|
|
333
|
+
oldValue,
|
|
334
|
+
newValue
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Per-key restore preview across all three backends. */
|
|
339
|
+
async function previewItems(target, base) {
|
|
340
|
+
const targetData = target ?? {
|
|
341
|
+
async: [],
|
|
342
|
+
mmkv: {},
|
|
343
|
+
secure: {}
|
|
344
|
+
};
|
|
345
|
+
const liveMode = base === undefined;
|
|
346
|
+
const baseData = liveMode ? (await captureStorageData()).data : base ?? {
|
|
347
|
+
async: [],
|
|
348
|
+
mmkv: {},
|
|
349
|
+
secure: {}
|
|
350
|
+
};
|
|
351
|
+
const items = [];
|
|
352
|
+
const notes = [];
|
|
353
|
+
|
|
354
|
+
// ── AsyncStorage ──
|
|
355
|
+
const oldAsync = new Map(baseData.async ?? []);
|
|
356
|
+
const newAsync = new Map(targetData.async ?? []);
|
|
357
|
+
for (const key of new Set([...oldAsync.keys(), ...newAsync.keys()])) {
|
|
358
|
+
if (isDevToolsStorageKey(key)) continue;
|
|
359
|
+
items.push(classifyValue(`async:${key}`, key, oldAsync.get(key), newAsync.get(key), oldAsync.has(key), newAsync.has(key)));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ── MMKV ──
|
|
363
|
+
const instanceIds = new Set([...Object.keys(baseData.mmkv ?? {}), ...Object.keys(targetData.mmkv ?? {})]);
|
|
364
|
+
for (const id of instanceIds) {
|
|
365
|
+
const registered = liveMode ? mmkvInstanceRegistry.get(id) : null;
|
|
366
|
+
const instanceUnavailable = liveMode && !registered;
|
|
367
|
+
const readOnly = liveMode && registered?.instance.isReadOnly === true;
|
|
368
|
+
const oldEntries = new Map((baseData.mmkv?.[id] ?? []).map(e => [e.key, e]));
|
|
369
|
+
const newEntries = new Map((targetData.mmkv?.[id] ?? []).map(e => [e.key, e]));
|
|
370
|
+
for (const key of new Set([...oldEntries.keys(), ...newEntries.keys()])) {
|
|
371
|
+
if (isDevToolsStorageKey(key)) continue;
|
|
372
|
+
const itemKey = `mmkv:${id}/${key}`;
|
|
373
|
+
const label = `${id} · ${key}`;
|
|
374
|
+
const oldE = oldEntries.get(key);
|
|
375
|
+
const newE = newEntries.get(key);
|
|
376
|
+
let item = classifyValue(itemKey, label, oldE?.value, newE?.value, oldEntries.has(key), newEntries.has(key));
|
|
377
|
+
if (item.verdict !== "unchanged") {
|
|
378
|
+
if (instanceUnavailable) {
|
|
379
|
+
item = {
|
|
380
|
+
...item,
|
|
381
|
+
verdict: "wont-apply",
|
|
382
|
+
reason: "MMKV instance not registered"
|
|
383
|
+
};
|
|
384
|
+
} else if (readOnly) {
|
|
385
|
+
item = {
|
|
386
|
+
...item,
|
|
387
|
+
verdict: "wont-apply",
|
|
388
|
+
reason: "MMKV instance registered read-only"
|
|
389
|
+
};
|
|
390
|
+
} else if (newE?.valueType === "buffer" || oldE?.valueType === "buffer") {
|
|
391
|
+
item = {
|
|
392
|
+
...item,
|
|
393
|
+
verdict: "wont-apply",
|
|
394
|
+
reason: "ArrayBuffer values cannot be restored"
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
items.push(item);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ── SecureStore ──
|
|
403
|
+
const oldSecure = baseData.secure ?? {};
|
|
404
|
+
const newSecure = targetData.secure ?? {};
|
|
405
|
+
const registeredSecure = liveMode ? new Map(secureStoreRegistry.getAll().map(e => [e.key, e])) : null;
|
|
406
|
+
for (const key of new Set([...Object.keys(oldSecure), ...Object.keys(newSecure)])) {
|
|
407
|
+
let item = classifyValue(`secure:${key}`, key, oldSecure[key], newSecure[key], key in oldSecure, key in newSecure);
|
|
408
|
+
if (liveMode && item.verdict !== "unchanged") {
|
|
409
|
+
const entry = registeredSecure?.get(key);
|
|
410
|
+
if (!entry) {
|
|
411
|
+
item = {
|
|
412
|
+
...item,
|
|
413
|
+
verdict: "wont-apply",
|
|
414
|
+
reason: "key no longer registered with registerSecureStoreKeys"
|
|
415
|
+
};
|
|
416
|
+
} else if (entry.options.requireAuthentication) {
|
|
417
|
+
item = {
|
|
418
|
+
...item,
|
|
419
|
+
verdict: "wont-apply",
|
|
420
|
+
reason: "biometric-protected keys are never written"
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
items.push(item);
|
|
425
|
+
}
|
|
426
|
+
if (liveMode) {
|
|
427
|
+
const biometric = secureStoreRegistry.getAll().filter(e => e.options.requireAuthentication).length;
|
|
428
|
+
if (biometric > 0) {
|
|
429
|
+
notes.push(`${biometric} biometric-protected secure key(s) are never read or written`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
items,
|
|
434
|
+
notes
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
registerStateSnapshotProvider({
|
|
438
|
+
id: "storage",
|
|
439
|
+
label: "Storage",
|
|
440
|
+
status,
|
|
441
|
+
capture,
|
|
442
|
+
restore,
|
|
443
|
+
clear,
|
|
444
|
+
previewItems
|
|
445
|
+
});
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
import { isDevToolsStorageKey } from "@buoy-gg/shared-ui";
|
|
10
10
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
11
11
|
import { writeMany, removeMany } from "./asyncStorageCompat";
|
|
12
|
+
function isOmittedStorageValue(value) {
|
|
13
|
+
return !!value && typeof value === "object" && value.__buoyValueOnDevice === true;
|
|
14
|
+
}
|
|
15
|
+
|
|
12
16
|
/**
|
|
13
17
|
* Undo a single storage operation
|
|
14
18
|
*
|
|
@@ -28,6 +32,9 @@ export async function undoOperation(event) {
|
|
|
28
32
|
action,
|
|
29
33
|
data
|
|
30
34
|
} = event;
|
|
35
|
+
if (isOmittedStorageValue(data?.prevValue) || isOmittedStorageValue(data?.prevPairs)) {
|
|
36
|
+
throw new Error("Cannot undo: previous value is still on the device — fetch getEventDetail first");
|
|
37
|
+
}
|
|
31
38
|
{
|
|
32
39
|
switch (action) {
|
|
33
40
|
case "setItem":
|
|
@@ -106,6 +113,12 @@ export async function jumpToState(events, targetEventIndex) {
|
|
|
106
113
|
if (targetEventIndex < 0 || targetEventIndex >= events.length) {
|
|
107
114
|
throw new Error(`Invalid event index: ${targetEventIndex}`);
|
|
108
115
|
}
|
|
116
|
+
for (const event of events) {
|
|
117
|
+
const data = event.data;
|
|
118
|
+
if (isOmittedStorageValue(data?.value) || isOmittedStorageValue(data?.prevValue) || isOmittedStorageValue(data?.pairs) || isOmittedStorageValue(data?.prevPairs)) {
|
|
119
|
+
throw new Error("Cannot jump: some event values are still on the device — fetch getEventDetail first");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
109
122
|
|
|
110
123
|
// Build up the state by applying each event up to and including the target.
|
|
111
124
|
// We use the final values from each event, not the operations themselves.
|
|
@@ -253,14 +266,15 @@ export function canUndo(event) {
|
|
|
253
266
|
case "setItem":
|
|
254
267
|
case "mergeItem":
|
|
255
268
|
case "removeItem":
|
|
256
|
-
// Can undo if we have the key
|
|
257
|
-
|
|
269
|
+
// Can undo if we have the key — but not while prevValue is still a
|
|
270
|
+
// wire-form on-device marker (restoring that would write garbage).
|
|
271
|
+
return !!data?.key && !isOmittedStorageValue(data.prevValue);
|
|
258
272
|
case "multiSet":
|
|
259
273
|
case "multiRemove":
|
|
260
274
|
case "multiMerge":
|
|
261
275
|
case "clear":
|
|
262
276
|
// Can undo if we have previous pairs
|
|
263
|
-
return !!data?.prevPairs && data.prevPairs.length > 0;
|
|
277
|
+
return !!data?.prevPairs && data.prevPairs.length > 0 && !isOmittedStorageValue(data.prevPairs);
|
|
264
278
|
default:
|
|
265
279
|
return false;
|
|
266
280
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Internal listeners and capture controls are not exported to prevent
|
|
9
9
|
* bypassing the tool's intended usage patterns.
|
|
10
10
|
*/
|
|
11
|
+
import "./storage/utils/snapshotProvider";
|
|
11
12
|
export { storageToolPreset, createStorageTool } from "./preset";
|
|
12
13
|
export { StorageSection } from "./storage/components/StorageSection";
|
|
13
14
|
export { StorageModalWithTabs, StorageToolView, } from "./storage/components/StorageModalWithTabs";
|
|
@@ -28,6 +29,7 @@ export { useMMKVKeys, useMultiMMKVKeys } from "./storage/hooks/useMMKVKeys";
|
|
|
28
29
|
export { useMMKVInstances, useMMKVInstance, useMMKVInstanceExists, } from "./storage/hooks/useMMKVInstances";
|
|
29
30
|
export { useStorageEvents, type UseStorageEventsOptions, type UseStorageEventsResult, } from "./storage/hooks/useStorageEvents";
|
|
30
31
|
export type { StorageEvent, StorageEventListener, StorageEventCallback, } from "./storage/stores/storageEventStore";
|
|
32
|
+
export type { RequiredStorageKey, StorageKeyInfo } from "./storage/types";
|
|
31
33
|
export { clearAllAppStorage } from "./storage/utils/clearAllStorage";
|
|
32
34
|
export { setStorageValue, storageEditBlockedReason, validateStorageValue, storageValueToEditableText, type EditableMMKVInstance, } from "./storage/utils/setStorageValue";
|
|
33
35
|
export { isMMKVAvailable, getMMKVClass, getMMKVUnavailableMessage, } from "./storage/utils/mmkvAvailability";
|
|
@@ -41,7 +43,7 @@ export { registerSecureStoreKeys, unregisterSecureStoreKey, } from "./storage/ut
|
|
|
41
43
|
export type { SecureStoreLike, SecureStoreKeyDescriptor, SecureStoreOptionsLike, } from "./storage/utils/SecureStoreRegistry";
|
|
42
44
|
export { storageSyncAdapter } from "./storage/sync/storageSyncAdapter";
|
|
43
45
|
/** @internal */
|
|
44
|
-
export { storageEventStore } from "./storage/stores/storageEventStore";
|
|
46
|
+
export { storageEventStore, isValueOnDevice, batchPairsOf, } from "./storage/stores/storageEventStore";
|
|
45
47
|
/**
|
|
46
48
|
* Tell a mounted storage browser to re-read every backend. Used by the desktop
|
|
47
49
|
* dashboard, where local storage listeners are suppressed and device data
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
options?: DiffOptions;
|
|
9
|
-
showThemeName?: boolean;
|
|
10
|
-
/**
|
|
11
|
-
* Scroll the rows inside their own fixed-height pane (default). Set false to
|
|
12
|
-
* render every row inline at natural height and let an ancestor ScrollView
|
|
13
|
-
* move them — one page-level scroll instead of a nested scroll region.
|
|
14
|
-
*/
|
|
15
|
-
scrollEnabled?: boolean;
|
|
16
|
-
}
|
|
17
|
-
export declare function ThemedSplitView({ oldValue, newValue, theme, options, showThemeName, scrollEnabled, }: ThemedSplitViewProps): import("react").JSX.Element;
|
|
18
|
-
export {};
|
|
1
|
+
/**
|
|
2
|
+
* Re-export shim — ThemedSplitView now lives in @buoy-gg/shared-ui/dataViewer
|
|
3
|
+
* (promoted so redux/zustand/jotai/time-machine share one copy; this used to
|
|
4
|
+
* be the only fork with folding + virtualization, and fixes had to be made
|
|
5
|
+
* twice). Keep importing from here inside the storage package.
|
|
6
|
+
*/
|
|
7
|
+
export { ThemedSplitView, type DiffOptions, type DiffCompareMethod, } from "@buoy-gg/shared-ui/dataViewer";
|
|
19
8
|
//# sourceMappingURL=ThemedSplitView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemedSplitView.d.ts","sourceRoot":"","sources":["../../../../../../src/storage/components/DiffViewer/modes/ThemedSplitView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ThemedSplitView.d.ts","sourceRoot":"","sources":["../../../../../../src/storage/components/DiffViewer/modes/ThemedSplitView.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,+BAA+B,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RequiredStorageKey } from "../types";
|
|
2
|
+
import { type StorageEvent } from "../hooks/useStorageEvents";
|
|
2
3
|
export declare const FREE_TIER_EVENT_LIMIT = 25;
|
|
3
4
|
interface StorageModalWithTabsProps {
|
|
4
5
|
visible: boolean;
|
|
@@ -27,8 +28,14 @@ interface StorageModalWithTabsProps {
|
|
|
27
28
|
* pull is the only way to refresh a backend that has not pushed.
|
|
28
29
|
*/
|
|
29
30
|
onRefresh?: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Fired when the user opens an event's detail view (and its neighbor, for
|
|
33
|
+
* the diff). Remote mirrors (Buoy Desktop) use this to fetch oversized
|
|
34
|
+
* values on demand — wire-form snapshots omit payloads over 16KB.
|
|
35
|
+
*/
|
|
36
|
+
onEventSelect?: (event: StorageEvent) => void;
|
|
30
37
|
}
|
|
31
|
-
export declare function StorageModalWithTabs({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, requiredStorageKeys, inline, onTabChange, onRefresh, }: StorageModalWithTabsProps): import("react").JSX.Element | null;
|
|
38
|
+
export declare function StorageModalWithTabs({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, requiredStorageKeys, inline, onTabChange, onRefresh, onEventSelect, }: StorageModalWithTabsProps): import("react").JSX.Element | null;
|
|
32
39
|
/**
|
|
33
40
|
* The same storage tool as `StorageModalWithTabs`, rendered INLINE for hosts
|
|
34
41
|
* that place tools in a panel rather than a modal. Exists so there is exactly
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageModalWithTabs.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageModalWithTabs.tsx"],"names":[],"mappings":"AA6BA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"StorageModalWithTabs.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageModalWithTabs.tsx"],"names":[],"mappings":"AA6BA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAY9C,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAehF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,GAAG,QAAQ,KAAK,IAAI,CAAC;IAClD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAC/C;AAqBD,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,mBAAwB,EACxB,MAAc,EACd,WAAW,EACX,SAAS,EACT,aAAa,GACd,EAAE,yBAAyB,sCAo/B3B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,GAAE,IAAI,CAAC,yBAAyB,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAM,+BAG9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAsyncStorageKeys.d.ts","sourceRoot":"","sources":["../../../../src/storage/hooks/useAsyncStorageKeys.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"useAsyncStorageKeys.d.ts","sourceRoot":"","sources":["../../../../src/storage/hooks/useAsyncStorageKeys.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG9D,UAAU,yBAAyB;IACjC,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,wBAAgB,mBAAmB,CACjC,mBAAmB,GAAE,kBAAkB,EAAO,GAC7C,yBAAyB,CA6J3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMMKVKeys.d.ts","sourceRoot":"","sources":["../../../../src/storage/hooks/useMMKVKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"useMMKVKeys.d.ts","sourceRoot":"","sources":["../../../../src/storage/hooks/useMMKVKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAQ9D,KAAK,IAAI,GAAG,GAAG,CAAC;AAEhB,UAAU,iBAAiB;IACzB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EACjC,UAAU,EAAE,MAAM,EAClB,mBAAmB,GAAE,kBAAkB,EAAO,GAC7C,iBAAiB,CA4LnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EAChD,mBAAmB,GAAE,kBAAkB,EAAO,GAC7C,iBAAiB,CAoJnB"}
|
|
@@ -35,6 +35,20 @@ export type StorageEvent = (AsyncStorageEvent & {
|
|
|
35
35
|
storageType: "mmkv";
|
|
36
36
|
id: string;
|
|
37
37
|
});
|
|
38
|
+
/** True when a field is the wire-form VALUE_ON_DEVICE marker. */
|
|
39
|
+
export declare function isValueOnDevice(value: unknown): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* `pairs` / `prevPairs` are AsyncStorage-only — they carry the batch
|
|
42
|
+
* operations (multiSet / multiRemove / multiMerge / clear), and MMKV has no
|
|
43
|
+
* batch API, so its `data` has no such fields. Reading them off the
|
|
44
|
+
* `StorageEvent` union needs narrowing, and this is the one place that does
|
|
45
|
+
* it; writing them unconditionally would also graft meaningless keys onto
|
|
46
|
+
* every MMKV event on the wire.
|
|
47
|
+
*/
|
|
48
|
+
export declare function batchPairsOf(event: StorageEvent): {
|
|
49
|
+
pairs?: unknown;
|
|
50
|
+
prevPairs?: unknown;
|
|
51
|
+
};
|
|
38
52
|
/**
|
|
39
53
|
* Listener callback type for storage events array
|
|
40
54
|
*/
|
|
@@ -71,6 +85,18 @@ declare class StorageEventStore extends BaseEventStore<StorageEvent> {
|
|
|
71
85
|
* Get events filtered by storage type
|
|
72
86
|
*/
|
|
73
87
|
getEventsByType(storageType: "async" | "mmkv"): StorageEvent[];
|
|
88
|
+
/**
|
|
89
|
+
* Replace the event list, keeping values a remote mirror already hydrated.
|
|
90
|
+
* Wire-form snapshots always carry VALUE_ON_DEVICE markers for oversized
|
|
91
|
+
* payloads; a later snapshot must not blank the detail pane (or wipe
|
|
92
|
+
* `prevValue` that time-travel undo needs).
|
|
93
|
+
*/
|
|
94
|
+
replaceEvents(events: StorageEvent[]): void;
|
|
95
|
+
/**
|
|
96
|
+
* Hydrate one mirrored event with real values fetched on demand from the
|
|
97
|
+
* device via the adapter's `getEventDetail`. Remote-mirror (desktop) only.
|
|
98
|
+
*/
|
|
99
|
+
hydrateEvent(event: StorageEvent): void;
|
|
74
100
|
}
|
|
75
101
|
export declare const storageEventStore: StorageEventStore;
|
|
76
102
|
export declare const subscribeToStorageEvents: (listener: StorageEventListener) => () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storageEventStore.d.ts","sourceRoot":"","sources":["../../../../src/storage/stores/storageEventStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAC;AAI/B;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,CAAC,iBAAiB,GAAG;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1D,CAAC,SAAS,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"storageEventStore.d.ts","sourceRoot":"","sources":["../../../../src/storage/stores/storageEventStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAC;AAI/B;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,CAAC,iBAAiB,GAAG;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1D,CAAC,SAAS,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEtD,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMvD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAGA;AAmBD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAEjE,cAAM,iBAAkB,SAAQ,cAAc,CAAC,YAAY,CAAC;IAE1D,OAAO,CAAC,uBAAuB,CAA6B;IAC5D,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,sBAAsB,CAAS;;IASvC;;;;;OAKG;YACW,iBAAiB;IA0D/B;;OAEG;cACa,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC/C;;OAEG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAc/B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,EAAE;IAI9D;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI;IAyB3C;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;CAaxC;AAGD,eAAO,MAAM,iBAAiB,mBAA0B,CAAC;AAGzD,eAAO,MAAM,wBAAwB,GAAI,UAAU,oBAAoB,eACxB,CAAC;AAChD,eAAO,MAAM,cAAc,GAAI,UAAU,oBAAoB,eACxB,CAAC;AACtC,eAAO,MAAM,gBAAgB,sBAAsC,CAAC;AACpE,eAAO,MAAM,kBAAkB,YAAwC,CAAC;AACxE,eAAO,MAAM,kBAAkB,eAAwC,CAAC"}
|