@rozenite/storage-plugin 2.0.0 → 2.2.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 (69) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/devtools/assets/panel-D5z1BY8U.js +32 -0
  3. package/dist/devtools/assets/panel-DVlWRg6d.css +1 -0
  4. package/dist/devtools/panel.html +2 -2
  5. package/dist/react-native/chunks/index.require.js +1 -3
  6. package/dist/react-native/chunks/secure-storage.require.js +2 -6
  7. package/dist/react-native/chunks/useRozeniteStoragePlugin.require.cjs +1 -1
  8. package/dist/react-native/chunks/useRozeniteStoragePlugin.require.js +266 -295
  9. package/dist/react-native/index.d.ts +14 -2
  10. package/dist/react-native/index.js +2 -10
  11. package/dist/rozenite.json +1 -1
  12. package/package.json +29 -29
  13. package/react-native.ts +11 -36
  14. package/rozenite.config.ts +2 -6
  15. package/sdk.ts +1 -5
  16. package/src/react-native/__tests__/entry-preview-pagination.test.ts +12 -40
  17. package/src/react-native/__tests__/export-snapshot.test.ts +2 -6
  18. package/src/react-native/__tests__/full-entry-request.test.ts +5 -17
  19. package/src/react-native/__tests__/import.test.ts +9 -8
  20. package/src/react-native/__tests__/storage-discovery.test.ts +7 -18
  21. package/src/react-native/__tests__/stress-verification.test.ts +5 -18
  22. package/src/react-native/__tests__/use-storage-agent-tools.test.ts +25 -35
  23. package/src/react-native/adapters/__tests__/mmkv.test.ts +20 -30
  24. package/src/react-native/adapters/async-storage.ts +2 -5
  25. package/src/react-native/adapters/index.ts +2 -8
  26. package/src/react-native/adapters/mmkv.ts +7 -24
  27. package/src/react-native/adapters/secure-storage.ts +2 -6
  28. package/src/react-native/entry-preview-pagination.ts +11 -27
  29. package/src/react-native/export-snapshot.ts +3 -9
  30. package/src/react-native/full-entry-request.ts +3 -8
  31. package/src/react-native/import.ts +10 -21
  32. package/src/react-native/storage-discovery.ts +2 -6
  33. package/src/react-native/storage-view.ts +2 -6
  34. package/src/react-native/useRozeniteStoragePlugin.ts +107 -119
  35. package/src/react-native/useStorageAgentTools.ts +8 -24
  36. package/src/shared/__tests__/entry-preview.test.ts +9 -27
  37. package/src/shared/__tests__/snapshot.test.ts +10 -32
  38. package/src/shared/agent-tools.ts +9 -30
  39. package/src/shared/entry-preview.ts +3 -8
  40. package/src/shared/messaging.ts +15 -11
  41. package/src/shared/snapshot.ts +4 -17
  42. package/src/shared/types.ts +2 -4
  43. package/src/ui/__tests__/binary-value-editor-state.test.ts +6 -16
  44. package/src/ui/__tests__/binary.test.ts +13 -33
  45. package/src/ui/__tests__/large-value-viewer.test.tsx +4 -13
  46. package/src/ui/__tests__/panel.test.tsx +50 -63
  47. package/src/ui/__tests__/storage-groups.test.ts +1 -4
  48. package/src/ui/__tests__/type-conversion.test.ts +8 -24
  49. package/src/ui/__tests__/use-storage-entries.test.tsx +18 -34
  50. package/src/ui/add-entry-dialog.tsx +66 -87
  51. package/src/ui/binary-value-editor-state.ts +6 -24
  52. package/src/ui/binary-value-editor.tsx +6 -18
  53. package/src/ui/binary.ts +6 -19
  54. package/src/ui/edit-entry-dialog.tsx +59 -78
  55. package/src/ui/editor-switcher.tsx +2 -11
  56. package/src/ui/entry-detail-dialog.tsx +9 -16
  57. package/src/ui/format-value.tsx +2 -9
  58. package/src/ui/import-dialog.tsx +12 -24
  59. package/src/ui/large-value-viewer-state.ts +2 -9
  60. package/src/ui/large-value-viewer.tsx +3 -9
  61. package/src/ui/panel.tsx +129 -235
  62. package/src/ui/query-client.tsx +4 -15
  63. package/src/ui/type-conversion.ts +2 -5
  64. package/src/ui/typed-value-editor.tsx +1 -5
  65. package/src/ui/use-storage-entries.ts +6 -21
  66. package/src/ui/utils.ts +1 -4
  67. package/tsconfig.json +4 -7
  68. package/dist/devtools/assets/panel-B6Wp0eie.css +0 -1
  69. package/dist/devtools/assets/panel-D7MFc4HG.js +0 -32
@@ -17,35 +17,31 @@ const createViews = (
17
17
  entries: Record<string, StorageEntry>;
18
18
  }>,
19
19
  ) => {
20
- const adapters = storages.map(
21
- ({ adapterId, storageId, entries }): StorageAdapter => ({
22
- id: adapterId,
23
- name: `Adapter ${adapterId}`,
24
- storages: [
25
- {
26
- id: storageId,
27
- name: `Storage ${storageId}`,
28
- capabilities: { supportedTypes: ['string', 'buffer'] },
29
- storage: {
30
- kind: 'sync',
31
- getAllKeys: vi.fn(() => Object.keys(entries)),
32
- get: vi.fn((key: string) => entries[key]),
33
- set: vi.fn(),
34
- delete: vi.fn(),
35
- clear: vi.fn(),
36
- },
20
+ const adapters = storages.map(({ adapterId, storageId, entries }): StorageAdapter => ({
21
+ id: adapterId,
22
+ name: `Adapter ${adapterId}`,
23
+ storages: [
24
+ {
25
+ id: storageId,
26
+ name: `Storage ${storageId}`,
27
+ capabilities: { supportedTypes: ['string', 'buffer'] },
28
+ storage: {
29
+ kind: 'sync',
30
+ getAllKeys: vi.fn(() => Object.keys(entries)),
31
+ get: vi.fn((key: string) => entries[key]),
32
+ set: vi.fn(),
33
+ delete: vi.fn(),
34
+ clear: vi.fn(),
37
35
  },
38
- ],
39
- }),
40
- );
36
+ },
37
+ ],
38
+ }));
41
39
 
42
40
  return { adapters, views: createStorageViews(adapters) };
43
41
  };
44
42
 
45
43
  const entries = (...keys: string[]): Record<string, StorageEntry> =>
46
- Object.fromEntries(
47
- keys.map((key) => [key, { key, type: 'string', value: `value:${key}` }]),
48
- );
44
+ Object.fromEntries(keys.map((key) => [key, { key, type: 'string', value: `value:${key}` }]));
49
45
 
50
46
  describe('storage Agent tool handlers', () => {
51
47
  it('lists storage descriptors without reading keys or values', async () => {
@@ -54,9 +50,7 @@ describe('storage Agent tool handlers', () => {
54
50
  { adapterId: 'two', storageId: 'second', entries: entries('b') },
55
51
  ]);
56
52
 
57
- await expect(
58
- createStorageAgentHandlers(views).listStorages(),
59
- ).resolves.toEqual({
53
+ await expect(createStorageAgentHandlers(views).listStorages()).resolves.toEqual({
60
54
  storages: [
61
55
  {
62
56
  adapterId: 'one',
@@ -111,9 +105,7 @@ describe('storage Agent tool handlers', () => {
111
105
  });
112
106
  expect(first.items.every((item) => !('value' in item))).toBe(true);
113
107
 
114
- await expect(
115
- handlers.listEntries({ cursor: first.page.nextCursor }),
116
- ).resolves.toEqual({
108
+ await expect(handlers.listEntries({ cursor: first.page.nextCursor })).resolves.toEqual({
117
109
  adapterId: 'adapter',
118
110
  storageId: 'storage',
119
111
  total: 3,
@@ -129,9 +121,7 @@ describe('storage Agent tool handlers', () => {
129
121
  adapterId: 'adapter',
130
122
  storageId: 'storage',
131
123
  entries: entries(
132
- ...Array.from({ length: MAX_PAGE_LIMIT + 1 }, (_, index) =>
133
- String(index),
134
- ),
124
+ ...Array.from({ length: MAX_PAGE_LIMIT + 1 }, (_, index) => String(index)),
135
125
  ),
136
126
  },
137
127
  ]);
@@ -198,9 +188,9 @@ describe('storage Agent tool handlers', () => {
198
188
  offset: 0,
199
189
  }),
200
190
  ).rejects.toThrow('either cursor or offset');
201
- await expect(
202
- handlers.listEntries({ adapterId: 'adapter' }),
203
- ).rejects.toThrow('Multiple storages matched');
191
+ await expect(handlers.listEntries({ adapterId: 'adapter' })).rejects.toThrow(
192
+ 'Multiple storages matched',
193
+ );
204
194
  await expect(handlers.listEntries({ storageId: 'one' })).rejects.toThrow(
205
195
  'Multiple storages matched',
206
196
  );
@@ -109,9 +109,9 @@ describe('createMMKVStorageAdapter', () => {
109
109
  it('rejects MMKV v4 storages passed as an array', () => {
110
110
  const storage = createFakeMMKVV4();
111
111
 
112
- expect(() =>
113
- createMMKVStorageAdapter({ storages: [storage as any] }),
114
- ).toThrow(/MMKV arrays are not supported for v4 storages/);
112
+ expect(() => createMMKVStorageAdapter({ storages: [storage as any] })).toThrow(
113
+ /MMKV arrays are not supported for v4 storages/,
114
+ );
115
115
  });
116
116
 
117
117
  it('applies global blacklist patterns against storageId:key', async () => {
@@ -129,12 +129,8 @@ describe('createMMKVStorageAdapter', () => {
129
129
  });
130
130
 
131
131
  const views = createStorageViews([adapter]);
132
- const userView = views.find(
133
- (view) => view.target.storageId === 'user-storage',
134
- );
135
- const cacheView = views.find(
136
- (view) => view.target.storageId === 'cache-storage',
137
- );
132
+ const userView = views.find((view) => view.target.storageId === 'user-storage');
133
+ const cacheView = views.find((view) => view.target.storageId === 'cache-storage');
138
134
 
139
135
  expect(userView).toBeDefined();
140
136
  expect(cacheView).toBeDefined();
@@ -173,22 +169,20 @@ const createAmbiguousFakeMMKVV4 = ({
173
169
  const values = new Map<string, Stored>();
174
170
 
175
171
  return {
176
- set: vi.fn(
177
- (key: string, value: string | number | boolean | ArrayBuffer) => {
178
- if (typeof value === 'string') {
179
- values.set(key, { kind: 'string', value });
180
- } else if (typeof value === 'number') {
181
- values.set(key, { kind: 'number', value });
182
- } else if (typeof value === 'boolean') {
183
- values.set(key, { kind: 'boolean', value });
184
- } else {
185
- values.set(key, {
186
- kind: 'buffer',
187
- bytes: new Uint8Array(value),
188
- });
189
- }
190
- },
191
- ),
172
+ set: vi.fn((key: string, value: string | number | boolean | ArrayBuffer) => {
173
+ if (typeof value === 'string') {
174
+ values.set(key, { kind: 'string', value });
175
+ } else if (typeof value === 'number') {
176
+ values.set(key, { kind: 'number', value });
177
+ } else if (typeof value === 'boolean') {
178
+ values.set(key, { kind: 'boolean', value });
179
+ } else {
180
+ values.set(key, {
181
+ kind: 'buffer',
182
+ bytes: new Uint8Array(value),
183
+ });
184
+ }
185
+ }),
192
186
  getString: vi.fn((key: string) => {
193
187
  const stored = values.get(key);
194
188
  if (!stored) return undefined;
@@ -205,11 +199,7 @@ const createAmbiguousFakeMMKVV4 = ({
205
199
  getNumber: vi.fn((key: string) => {
206
200
  const stored = values.get(key);
207
201
  if (stored?.kind === 'number') return stored.value;
208
- if (
209
- lenientGetNumber &&
210
- stored?.kind === 'buffer' &&
211
- stored.bytes.byteLength === 8
212
- ) {
202
+ if (lenientGetNumber && stored?.kind === 'buffer' && stored.bytes.byteLength === 8) {
213
203
  return new DataView(stored.bytes.buffer).getFloat64(0, true);
214
204
  }
215
205
  return undefined;
@@ -29,9 +29,7 @@ type MultiStorageOptions = {
29
29
  adapterName?: string;
30
30
  };
31
31
 
32
- export type CreateAsyncStorageAdapterOptions =
33
- | SingleStorageOptions
34
- | MultiStorageOptions;
32
+ export type CreateAsyncStorageAdapterOptions = SingleStorageOptions | MultiStorageOptions;
35
33
 
36
34
  const toStorageNode = (
37
35
  storageId: string,
@@ -113,5 +111,4 @@ export const createAsyncStorageAdapter = (
113
111
  * @deprecated Use createAsyncStorageAdapter instead.
114
112
  */
115
113
  export const createExpoAsyncStorageAdapter = createAsyncStorageAdapter;
116
- export type CreateExpoAsyncStorageAdapterOptions =
117
- CreateAsyncStorageAdapterOptions;
114
+ export type CreateExpoAsyncStorageAdapterOptions = CreateAsyncStorageAdapterOptions;
@@ -1,10 +1,7 @@
1
1
  export { createMMKVStorageAdapter } from './mmkv';
2
2
  export type { CreateMMKVStorageAdapterOptions } from './mmkv';
3
3
 
4
- export {
5
- createAsyncStorageAdapter,
6
- createExpoAsyncStorageAdapter,
7
- } from './async-storage';
4
+ export { createAsyncStorageAdapter, createExpoAsyncStorageAdapter } from './async-storage';
8
5
  export type {
9
6
  AsyncStorageInstanceConfig,
10
7
  AsyncStorageLike,
@@ -13,7 +10,4 @@ export type {
13
10
  } from './async-storage';
14
11
 
15
12
  export { createExpoSecureStorageAdapter } from './secure-storage';
16
- export type {
17
- CreateExpoSecureStorageAdapterOptions,
18
- SecureStorageLike,
19
- } from './secure-storage';
13
+ export type { CreateExpoSecureStorageAdapterOptions, SecureStorageLike } from './secure-storage';
@@ -1,10 +1,6 @@
1
1
  import type { MMKV as MMKVV3 } from 'react-native-mmkv-v3';
2
2
  import type { MMKV as MMKVV4 } from 'react-native-mmkv-v4';
3
- import type {
4
- StorageAdapter,
5
- StorageEntry,
6
- StorageNode,
7
- } from '../../shared/types';
3
+ import type { StorageAdapter, StorageEntry, StorageNode } from '../../shared/types';
8
4
  import { DEFAULT_SUPPORTED_TYPES } from '../../shared/types';
9
5
 
10
6
  type MMKV = MMKVV3 | MMKVV4;
@@ -44,9 +40,7 @@ const normalizeStorages = (storages: MMKV[] | Record<string, MMKV>) => {
44
40
  );
45
41
  }
46
42
 
47
- return Object.fromEntries(
48
- (storages as MMKVV3[]).map((storage) => [storage['id'], storage]),
49
- );
43
+ return Object.fromEntries((storages as MMKVV3[]).map((storage) => [storage['id'], storage]));
50
44
  }
51
45
 
52
46
  return storages;
@@ -64,8 +58,7 @@ const getMMKVAdapter = (mmkv: MMKV): MMKVAdapter => {
64
58
  delete: (key) => mmkv.remove(key),
65
59
  getAllKeys: () => mmkv.getAllKeys(),
66
60
  clear,
67
- addOnValueChangedListener: (callback) =>
68
- mmkv.addOnValueChangedListener(callback),
61
+ addOnValueChangedListener: (callback) => mmkv.addOnValueChangedListener(callback),
69
62
  };
70
63
  }
71
64
 
@@ -78,8 +71,7 @@ const getMMKVAdapter = (mmkv: MMKV): MMKVAdapter => {
78
71
  delete: (key) => mmkv.delete(key),
79
72
  getAllKeys: () => mmkv.getAllKeys(),
80
73
  clear,
81
- addOnValueChangedListener: (callback) =>
82
- mmkv.addOnValueChangedListener(callback),
74
+ addOnValueChangedListener: (callback) => mmkv.addOnValueChangedListener(callback),
83
75
  };
84
76
  };
85
77
 
@@ -102,10 +94,7 @@ const getMMKVAdapter = (mmkv: MMKV): MMKVAdapter => {
102
94
  // those keys, so the chain falls through.
103
95
  // 4. Empty string — intentional `setString(key, "")` lands here once
104
96
  // we've ruled out a non-empty buffer payload at the same key.
105
- const getEntry = (
106
- adapter: MMKVAdapter,
107
- key: string,
108
- ): StorageEntry | undefined => {
97
+ const getEntry = (adapter: MMKVAdapter, key: string): StorageEntry | undefined => {
109
98
  const stringValue = adapter.getString(key);
110
99
  if (stringValue !== undefined && stringValue.length > 0) {
111
100
  return { key, type: 'string', value: stringValue };
@@ -145,10 +134,7 @@ const setEntry = (adapter: MMKVAdapter, entry: StorageEntry) => {
145
134
  }
146
135
  };
147
136
 
148
- const getStorageBlacklist = (
149
- config: MMKVBlacklistConfig | undefined,
150
- storageId: string,
151
- ) => {
137
+ const getStorageBlacklist = (config: MMKVBlacklistConfig | undefined, storageId: string) => {
152
138
  if (!config) {
153
139
  return undefined;
154
140
  }
@@ -180,10 +166,7 @@ export const createMMKVStorageAdapter = ({
180
166
  storages,
181
167
  blacklist,
182
168
  }: CreateMMKVStorageAdapterOptions): StorageAdapter => {
183
- const normalizedStorages = normalizeStorages(storages) as Record<
184
- string,
185
- MMKV
186
- >;
169
+ const normalizedStorages = normalizeStorages(storages) as Record<string, MMKV>;
187
170
 
188
171
  const storageNodes: StorageNode[] = Object.entries(normalizedStorages).map(
189
172
  ([storageId, storage]) => {
@@ -59,9 +59,7 @@ export const createExpoSecureStorageAdapter = ({
59
59
  },
60
60
  set: async (entry) => {
61
61
  if (entry.type !== 'string') {
62
- throw new Error(
63
- 'Expo SecureStore adapter supports only string values.',
64
- );
62
+ throw new Error('Expo SecureStore adapter supports only string values.');
65
63
  }
66
64
 
67
65
  await storage.setItemAsync(entry.key, entry.value);
@@ -69,9 +67,7 @@ export const createExpoSecureStorageAdapter = ({
69
67
  delete: (key) => storage.deleteItemAsync(key),
70
68
  clear: async () => {
71
69
  const storageKeys = await resolveKeys(keys);
72
- await Promise.all(
73
- storageKeys.map((key) => storage.deleteItemAsync(key)),
74
- );
70
+ await Promise.all(storageKeys.map((key) => storage.deleteItemAsync(key)));
75
71
  },
76
72
  },
77
73
  };
@@ -10,9 +10,7 @@ import type { StorageView } from './storage-view';
10
10
  export const MAX_ENTRY_PREVIEW_PAGE_SIZE = 100;
11
11
  export const ENTRY_PREVIEW_READ_CONCURRENCY = 4;
12
12
 
13
- type PaginationResult =
14
- | StorageListEntryPreviewsResponseEvent
15
- | StorageRequestErrorEvent;
13
+ type PaginationResult = StorageListEntryPreviewsResponseEvent | StorageRequestErrorEvent;
16
14
 
17
15
  type Cursor = {
18
16
  version: 1;
@@ -49,8 +47,7 @@ const isTarget = (value: unknown): value is StorageTarget =>
49
47
  typeof (value as { storageId?: unknown }).storageId === 'string' &&
50
48
  (value as { storageId: string }).storageId.trim().length > 0;
51
49
 
52
- const normalizeSearch = (search: string | undefined) =>
53
- (search ?? '').trim().toLowerCase();
50
+ const normalizeSearch = (search: string | undefined) => (search ?? '').trim().toLowerCase();
54
51
 
55
52
  const validateRequest = (payload: unknown): ValidRequest | undefined => {
56
53
  if (
@@ -72,8 +69,7 @@ const validateRequest = (payload: unknown): ValidRequest | undefined => {
72
69
  return undefined;
73
70
  }
74
71
 
75
- const keySortDirection = (payload as { keySortDirection?: unknown })
76
- .keySortDirection;
72
+ const keySortDirection = (payload as { keySortDirection?: unknown }).keySortDirection;
77
73
  if (
78
74
  keySortDirection !== undefined &&
79
75
  keySortDirection !== 'ascending' &&
@@ -93,8 +89,7 @@ const validateRequest = (payload: unknown): ValidRequest | undefined => {
93
89
 
94
90
  // Cursor contents are intentionally private to the device protocol. Callers only
95
91
  // receive and return this opaque token; its representation may change freely.
96
- const encodeCursor = (cursor: Cursor) =>
97
- btoa(encodeURIComponent(JSON.stringify(cursor)));
92
+ const encodeCursor = (cursor: Cursor) => btoa(encodeURIComponent(JSON.stringify(cursor)));
98
93
 
99
94
  const decodeCursor = (value: string): Cursor | undefined => {
100
95
  try {
@@ -106,10 +101,8 @@ const decodeCursor = (value: string): Cursor | undefined => {
106
101
  (parsed as { version?: unknown }).version !== 1 ||
107
102
  !isTarget((parsed as { target?: unknown }).target) ||
108
103
  typeof (parsed as { search?: unknown }).search !== 'string' ||
109
- ((parsed as { keySortDirection?: unknown }).keySortDirection !==
110
- 'ascending' &&
111
- (parsed as { keySortDirection?: unknown }).keySortDirection !==
112
- 'descending') ||
104
+ ((parsed as { keySortDirection?: unknown }).keySortDirection !== 'ascending' &&
105
+ (parsed as { keySortDirection?: unknown }).keySortDirection !== 'descending') ||
113
106
  typeof (parsed as { boundaryKey?: unknown }).boundaryKey !== 'string' ||
114
107
  ((parsed as { direction?: unknown }).direction !== 'next' &&
115
108
  (parsed as { direction?: unknown }).direction !== 'previous')
@@ -126,8 +119,7 @@ const decodeCursor = (value: string): Cursor | undefined => {
126
119
  const isSameTarget = (left: StorageTarget, right: StorageTarget) =>
127
120
  left.adapterId === right.adapterId && left.storageId === right.storageId;
128
121
 
129
- const compareKeys = (left: string, right: string) =>
130
- left < right ? -1 : left > right ? 1 : 0;
122
+ const compareKeys = (left: string, right: string) => (left < right ? -1 : left > right ? 1 : 0);
131
123
 
132
124
  const readPreviews = async (
133
125
  view: StorageView,
@@ -148,14 +140,9 @@ const readPreviews = async (
148
140
  };
149
141
 
150
142
  await Promise.all(
151
- Array.from(
152
- { length: Math.min(ENTRY_PREVIEW_READ_CONCURRENCY, keys.length) },
153
- worker,
154
- ),
155
- );
156
- return previews.filter(
157
- (preview): preview is StorageEntryPreview => preview != null,
143
+ Array.from({ length: Math.min(ENTRY_PREVIEW_READ_CONCURRENCY, keys.length) }, worker),
158
144
  );
145
+ return previews.filter((preview): preview is StorageEntryPreview => preview != null);
159
146
  };
160
147
 
161
148
  export const handleListEntryPreviewsRequest = async (
@@ -174,9 +161,7 @@ export const handleListEntryPreviewsRequest = async (
174
161
  };
175
162
  }
176
163
 
177
- const view = views.find((candidate) =>
178
- isSameTarget(candidate.target, request.target),
179
- );
164
+ const view = views.find((candidate) => isSameTarget(candidate.target, request.target));
180
165
  if (!view) {
181
166
  return {
182
167
  type: 'storage-request-error',
@@ -199,8 +184,7 @@ export const handleListEntryPreviewsRequest = async (
199
184
  type: 'storage-request-error',
200
185
  requestId,
201
186
  code: 'INVALID_CURSOR',
202
- message:
203
- 'The entry preview cursor is invalid or belongs to a different query.',
187
+ message: 'The entry preview cursor is invalid or belongs to a different query.',
204
188
  resetPagination: true,
205
189
  };
206
190
  }
@@ -7,9 +7,7 @@ import type {
7
7
  import type { StorageTarget } from '../shared/types';
8
8
  import type { StorageView } from './storage-view';
9
9
 
10
- type ExportSnapshotResult =
11
- | StorageExportSnapshotResponseEvent
12
- | StorageRequestErrorEvent;
10
+ type ExportSnapshotResult = StorageExportSnapshotResponseEvent | StorageRequestErrorEvent;
13
11
 
14
12
  const getRequestId = (payload: unknown) =>
15
13
  typeof payload === 'object' &&
@@ -31,9 +29,7 @@ const isTarget = (value: unknown): value is StorageTarget =>
31
29
  const isSameTarget = (left: StorageTarget, right: StorageTarget) =>
32
30
  left.adapterId === right.adapterId && left.storageId === right.storageId;
33
31
 
34
- const validateRequest = (
35
- payload: unknown,
36
- ): payload is StorageExportSnapshotRequestEvent =>
32
+ const validateRequest = (payload: unknown): payload is StorageExportSnapshotRequestEvent =>
37
33
  typeof payload === 'object' &&
38
34
  payload != null &&
39
35
  !Array.isArray(payload) &&
@@ -56,9 +52,7 @@ export const handleExportSnapshotRequest = async (
56
52
  };
57
53
  }
58
54
 
59
- const view = views.find((candidate) =>
60
- isSameTarget(candidate.target, payload.target),
61
- );
55
+ const view = views.find((candidate) => isSameTarget(candidate.target, payload.target));
62
56
  if (!view) {
63
57
  return {
64
58
  type: 'storage-request-error',
@@ -28,9 +28,7 @@ const isTarget = (value: unknown): value is StorageTarget =>
28
28
  const isSameTarget = (left: StorageTarget, right: StorageTarget) =>
29
29
  left.adapterId === right.adapterId && left.storageId === right.storageId;
30
30
 
31
- const validateRequest = (
32
- payload: unknown,
33
- ): payload is StorageGetEntryRequestEvent =>
31
+ const validateRequest = (payload: unknown): payload is StorageGetEntryRequestEvent =>
34
32
  typeof payload === 'object' &&
35
33
  payload != null &&
36
34
  !Array.isArray(payload) &&
@@ -50,14 +48,11 @@ export const handleFullEntryRequest = async (
50
48
  type: 'storage-request-error',
51
49
  requestId,
52
50
  code: 'INVALID_REQUEST',
53
- message:
54
- 'Full entry requests require a valid target, request ID, and string key.',
51
+ message: 'Full entry requests require a valid target, request ID, and string key.',
55
52
  };
56
53
  }
57
54
 
58
- const view = views.find((candidate) =>
59
- isSameTarget(candidate.target, payload.target),
60
- );
55
+ const view = views.find((candidate) => isSameTarget(candidate.target, payload.target));
61
56
  if (!view) {
62
57
  return {
63
58
  type: 'storage-request-error',
@@ -8,11 +8,7 @@ import type {
8
8
  StorageRequestErrorEvent,
9
9
  } from '../shared/messaging';
10
10
  import { computePreview, parseSnapshot } from '../shared/snapshot';
11
- import {
12
- supportsType,
13
- type StorageEntry,
14
- type StorageTarget,
15
- } from '../shared/types';
11
+ import { supportsType, type StorageEntry, type StorageTarget } from '../shared/types';
16
12
  import type { StorageView } from './storage-view';
17
13
 
18
14
  export type ImportEmittedEvent =
@@ -20,9 +16,7 @@ export type ImportEmittedEvent =
20
16
  | StorageImportResultEvent
21
17
  | StorageInvalidatedEvent;
22
18
 
23
- type ImportPreviewResult =
24
- | StorageImportPreviewResponseEvent
25
- | StorageRequestErrorEvent;
19
+ type ImportPreviewResult = StorageImportPreviewResponseEvent | StorageRequestErrorEvent;
26
20
 
27
21
  const isSameTarget = (left: StorageTarget, right: StorageTarget) =>
28
22
  left.adapterId === right.adapterId && left.storageId === right.storageId;
@@ -58,11 +52,7 @@ const isEntry = (value: unknown): value is StorageEntry => {
58
52
  entry.type === 'buffer' &&
59
53
  Array.isArray(entry.value) &&
60
54
  entry.value.every(
61
- (byte) =>
62
- typeof byte === 'number' &&
63
- Number.isInteger(byte) &&
64
- byte >= 0 &&
65
- byte <= 255,
55
+ (byte) => typeof byte === 'number' && Number.isInteger(byte) && byte >= 0 && byte <= 255,
66
56
  )
67
57
  );
68
58
  };
@@ -96,8 +86,7 @@ export const handleImportPreviewRequest = async (
96
86
  type: 'storage-request-error',
97
87
  requestId,
98
88
  code: 'INVALID_REQUEST',
99
- message:
100
- 'Import preview requests require a valid target, request ID, and snapshot.',
89
+ message: 'Import preview requests require a valid target, request ID, and snapshot.',
101
90
  };
102
91
  }
103
92
 
@@ -154,9 +143,7 @@ export const handleImportEntries = async (
154
143
  emit({
155
144
  type: 'import-result',
156
145
  requestId,
157
- target: isTarget(event.target)
158
- ? event.target
159
- : { adapterId: '', storageId: '' },
146
+ target: isTarget(event.target) ? event.target : { adapterId: '', storageId: '' },
160
147
  ok: false,
161
148
  written: 0,
162
149
  total: 0,
@@ -182,9 +169,7 @@ export const handleImportEntries = async (
182
169
 
183
170
  // Recheck policy at apply time so a preview cannot bypass a changed target.
184
171
  const entries = event.entries.filter(
185
- (entry) =>
186
- supportsType(view.capabilities, entry.type) &&
187
- !isBlacklisted(view, entry.key),
172
+ (entry) => supportsType(view.capabilities, entry.type) && !isBlacklisted(view, entry.key),
188
173
  );
189
174
 
190
175
  for (let i = 0; i < entries.length; i++) {
@@ -194,10 +179,12 @@ export const handleImportEntries = async (
194
179
  await view.set(entry);
195
180
  } catch (error) {
196
181
  if (i > 0) {
182
+ const entryCount = (await view.getAllKeys()).length;
197
183
  emit({
198
184
  type: 'storage-invalidated',
199
185
  target: event.target,
200
186
  operation: 'import',
187
+ entryCount,
201
188
  });
202
189
  }
203
190
  emit({
@@ -223,10 +210,12 @@ export const handleImportEntries = async (
223
210
  }
224
211
 
225
212
  if (entries.length > 0) {
213
+ const entryCount = (await view.getAllKeys()).length;
226
214
  emit({
227
215
  type: 'storage-invalidated',
228
216
  target: event.target,
229
217
  operation: 'import',
218
+ entryCount,
230
219
  });
231
220
  }
232
221
 
@@ -6,9 +6,7 @@ import type {
6
6
  import type { StorageDescriptor } from '../shared/types';
7
7
  import type { StorageView } from './storage-view';
8
8
 
9
- type DiscoveryResult =
10
- | StorageDiscoverStoragesResponseEvent
11
- | StorageRequestErrorEvent;
9
+ type DiscoveryResult = StorageDiscoverStoragesResponseEvent | StorageRequestErrorEvent;
12
10
 
13
11
  const getRequestId = (payload: unknown) => {
14
12
  if (
@@ -23,9 +21,7 @@ const getRequestId = (payload: unknown) => {
23
21
  return '';
24
22
  };
25
23
 
26
- const validateRequest = (
27
- payload: unknown,
28
- ): payload is StorageDiscoverStoragesRequestEvent =>
24
+ const validateRequest = (payload: unknown): payload is StorageDiscoverStoragesRequestEvent =>
29
25
  typeof payload === 'object' &&
30
26
  payload != null &&
31
27
  !Array.isArray(payload) &&
@@ -123,9 +123,7 @@ export const createStorageView = (
123
123
  const getAllEntries = async () => {
124
124
  const keys = await getAllKeys(storage);
125
125
  const visibleEntries = await Promise.all(
126
- keys
127
- .filter((key) => !shouldFilterKey(storageNode, key))
128
- .map((key) => getEntry(storage, key)),
126
+ keys.filter((key) => !shouldFilterKey(storageNode, key)).map((key) => getEntry(storage, key)),
129
127
  );
130
128
 
131
129
  return visibleEntries.filter((entry): entry is StorageEntry => !!entry);
@@ -189,7 +187,5 @@ export const createStorageView = (
189
187
 
190
188
  export const createStorageViews = (storages: StorageAdapter[]) =>
191
189
  storages.flatMap((adapter) =>
192
- adapter.storages.map((storageNode) =>
193
- createStorageView(adapter, storageNode),
194
- ),
190
+ adapter.storages.map((storageNode) => createStorageView(adapter, storageNode)),
195
191
  );