@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
@@ -137,6 +137,16 @@ declare type StorageDescriptor = {
137
137
  supportsSubscriptions: boolean;
138
138
  };
139
139
 
140
+ /**
141
+ * Announced by the device once it is listening for panel requests. The panel is
142
+ * recreated on every app reload and asks for storages immediately, which can
143
+ * land before the app's React tree has mounted the plugin; this lets the panel
144
+ * ask again instead of waiting forever on a dropped request.
145
+ */
146
+ declare type StorageDeviceReadyEvent = {
147
+ type: 'device-ready';
148
+ };
149
+
140
150
  declare type StorageDiscoverStoragesRequestEvent = {
141
151
  type: 'discover-storages';
142
152
  requestId: string;
@@ -178,7 +188,7 @@ export declare type StorageEntryType = StorageEntry['type'];
178
188
 
179
189
  export declare type StorageEntryValue = StorageEntry['value'];
180
190
 
181
- declare type StorageEvent_2 = StorageSetEntryEvent | StorageDeleteEntryEvent | StoragePurgeEvent | StorageInvalidatedEvent | StorageDiscoverStoragesRequestEvent | StorageDiscoverStoragesResponseEvent | StorageListEntryPreviewsRequestEvent | StorageListEntryPreviewsResponseEvent | StorageGetEntryRequestEvent | StorageGetEntryResponseEvent | StorageExportSnapshotRequestEvent | StorageExportSnapshotResponseEvent | StorageImportPreviewRequestEvent | StorageImportPreviewResponseEvent | StorageRequestErrorEvent | StorageImportEntriesEvent | StorageImportProgressEvent | StorageImportResultEvent;
191
+ declare type StorageEvent_2 = StorageSetEntryEvent | StorageDeleteEntryEvent | StoragePurgeEvent | StorageInvalidatedEvent | StorageDeviceReadyEvent | StorageDiscoverStoragesRequestEvent | StorageDiscoverStoragesResponseEvent | StorageListEntryPreviewsRequestEvent | StorageListEntryPreviewsResponseEvent | StorageGetEntryRequestEvent | StorageGetEntryResponseEvent | StorageExportSnapshotRequestEvent | StorageExportSnapshotResponseEvent | StorageImportPreviewRequestEvent | StorageImportPreviewResponseEvent | StorageRequestErrorEvent | StorageImportEntriesEvent | StorageImportProgressEvent | StorageImportResultEvent;
182
192
 
183
193
  declare type StorageEventMap = {
184
194
  [K in StorageEvent_2['type']]: Extract<StorageEvent_2, {
@@ -259,6 +269,8 @@ declare type StorageInvalidatedEvent = {
259
269
  /** Native subscriptions only expose a key, not the operation. */
260
270
  key?: string;
261
271
  operation?: StorageInvalidationOperation;
272
+ /** Updated entry count for the storage after the invalidation. */
273
+ entryCount: number;
262
274
  };
263
275
 
264
276
  declare type StorageInvalidationOperation = 'set' | 'delete' | 'import' | 'purge';
@@ -348,6 +360,6 @@ export declare type SyncStorage = {
348
360
 
349
361
  export declare let useRozeniteStoragePlugin: typeof useRozeniteStoragePlugin_2;
350
362
 
351
- declare const useRozeniteStoragePlugin_2: ({ storages, }: RozeniteStoragePluginOptions) => RozeniteDevToolsRequestClient<StorageEventMap> | null;
363
+ declare const useRozeniteStoragePlugin_2: ({ storages }: RozeniteStoragePluginOptions) => RozeniteDevToolsRequestClient<StorageEventMap> | null;
352
364
 
353
365
  export { }
@@ -13,18 +13,10 @@ if (!i || u)
13
13
  e,
14
14
  "expo-secure-store",
15
15
  "Expo SecureStore"
16
- )), s = ((e) => t(
17
- e,
18
- "mmkv",
19
- "MMKV"
20
- )), p = () => null;
16
+ )), s = ((e) => t(e, "mmkv", "MMKV")), p = () => null;
21
17
  else if (d) {
22
18
  const e = require("./chunks/async-storage.require.js"), a = require("./chunks/secure-storage.require.js");
23
- r = e.createAsyncStorageAdapter, c = e.createExpoAsyncStorageAdapter, n = a.createExpoSecureStorageAdapter, s = ((o) => t(
24
- o,
25
- "mmkv",
26
- "MMKV"
27
- )), p = require("./chunks/useRozeniteStoragePlugin.require.js").useRozeniteStoragePlugin;
19
+ r = e.createAsyncStorageAdapter, c = e.createExpoAsyncStorageAdapter, n = a.createExpoSecureStorageAdapter, s = ((o) => t(o, "mmkv", "MMKV")), p = require("./chunks/useRozeniteStoragePlugin.require.js").useRozeniteStoragePlugin;
28
20
  } else
29
21
  r = require("./chunks/index.require.js").createAsyncStorageAdapter, c = require("./chunks/index.require.js").createExpoAsyncStorageAdapter, n = require("./chunks/index.require.js").createExpoSecureStorageAdapter, s = require("./chunks/index.require.js").createMMKVStorageAdapter, p = require("./chunks/useRozeniteStoragePlugin.require.js").useRozeniteStoragePlugin;
30
22
  export {
@@ -1 +1 @@
1
- {"name":"@rozenite/storage-plugin","version":"2.0.0","description":"Generic Storage Inspector for Rozenite.","panels":[{"name":"Storage","source":"/devtools/panel.html"}]}
1
+ {"name":"@rozenite/storage-plugin","version":"2.2.0","description":"Generic Storage Inspector for Rozenite.","panels":[{"name":"Storage","source":"/devtools/panel.html"}]}
package/package.json CHANGED
@@ -1,24 +1,42 @@
1
1
  {
2
2
  "name": "@rozenite/storage-plugin",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Generic Storage Inspector for Rozenite.",
5
- "type": "module",
6
- "main": "./dist/react-native/index.cjs",
7
- "module": "./dist/react-native/index.js",
8
- "types": "./dist/react-native/index.d.ts",
9
5
  "homepage": "https://github.com/callstackincubator/rozenite#readme",
10
6
  "bugs": {
11
7
  "url": "https://github.com/callstackincubator/rozenite/issues"
12
8
  },
9
+ "license": "MIT",
13
10
  "repository": {
14
11
  "type": "git",
15
12
  "url": "https://github.com/callstackincubator/rozenite.git"
16
13
  },
14
+ "type": "module",
15
+ "main": "./dist/react-native/index.cjs",
16
+ "module": "./dist/react-native/index.js",
17
+ "types": "./dist/react-native/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/react-native/index.d.ts",
21
+ "import": "./dist/react-native/index.js",
22
+ "require": "./dist/react-native/index.cjs"
23
+ },
24
+ "./sdk": {
25
+ "development": "./sdk.ts",
26
+ "types": "./dist/sdk/index.d.ts",
27
+ "import": "./dist/sdk/index.js",
28
+ "require": "./dist/sdk/index.cjs"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
17
35
  "dependencies": {
18
- "@rozenite/agent-shared": "2.0.0",
19
- "@rozenite/agent-bridge": "2.0.0",
20
- "@rozenite/plugin-bridge": "2.0.0",
21
- "@rozenite/ui": "2.0.0"
36
+ "@rozenite/agent-bridge": "2.2.0",
37
+ "@rozenite/agent-shared": "2.2.0",
38
+ "@rozenite/plugin-bridge": "2.2.0",
39
+ "@rozenite/ui": "2.2.0"
22
40
  },
23
41
  "devDependencies": {
24
42
  "@codemirror/commands": "^6.10.3",
@@ -39,8 +57,8 @@
39
57
  "react-virtuoso": "^4.14.1",
40
58
  "typescript": "~5.9.3",
41
59
  "vite": "^7.3.1",
42
- "@rozenite/vite-plugin": "2.0.0",
43
- "rozenite": "2.0.0"
60
+ "@rozenite/vite-plugin": "2.2.0",
61
+ "rozenite": "2.2.0"
44
62
  },
45
63
  "peerDependencies": {
46
64
  "@react-native-async-storage/async-storage": "*",
@@ -60,24 +78,6 @@
60
78
  "optional": true
61
79
  }
62
80
  },
63
- "license": "MIT",
64
- "exports": {
65
- ".": {
66
- "types": "./dist/react-native/index.d.ts",
67
- "import": "./dist/react-native/index.js",
68
- "require": "./dist/react-native/index.cjs"
69
- },
70
- "./sdk": {
71
- "development": "./sdk.ts",
72
- "types": "./dist/sdk/index.d.ts",
73
- "import": "./dist/sdk/index.js",
74
- "require": "./dist/sdk/index.cjs"
75
- },
76
- "./package.json": "./package.json"
77
- },
78
- "publishConfig": {
79
- "access": "public"
80
- },
81
81
  "scripts": {
82
82
  "build": "rozenite build",
83
83
  "dev": "rozenite dev",
package/react-native.ts CHANGED
@@ -25,8 +25,7 @@ export let createExpoSecureStorageAdapter: typeof import('./src/react-native/ada
25
25
  export let createMMKVStorageAdapter: typeof import('./src/react-native/adapters').createMMKVStorageAdapter;
26
26
  export let useRozeniteStoragePlugin: typeof import('./src/react-native/useRozeniteStoragePlugin').useRozeniteStoragePlugin;
27
27
 
28
- const isWeb =
29
- typeof window !== 'undefined' && window.navigator.product !== 'ReactNative';
28
+ const isWeb = typeof window !== 'undefined' && window.navigator.product !== 'ReactNative';
30
29
  const isDev = process.env.NODE_ENV !== 'production';
31
30
  const isServer = typeof window === 'undefined';
32
31
 
@@ -41,64 +40,40 @@ const createNoopStorageAdapter = (
41
40
  });
42
41
 
43
42
  if (!isDev || isServer) {
44
- createAsyncStorageAdapter = ((options: {
45
- adapterId?: string;
46
- adapterName?: string;
47
- }) =>
43
+ createAsyncStorageAdapter = ((options: { adapterId?: string; adapterName?: string }) =>
48
44
  createNoopStorageAdapter(
49
45
  options,
50
46
  'async-storage',
51
47
  'AsyncStorage',
52
48
  )) as typeof createAsyncStorageAdapter;
53
49
  createExpoAsyncStorageAdapter = createAsyncStorageAdapter;
54
- createExpoSecureStorageAdapter = ((options: {
55
- adapterId?: string;
56
- adapterName?: string;
57
- }) =>
50
+ createExpoSecureStorageAdapter = ((options: { adapterId?: string; adapterName?: string }) =>
58
51
  createNoopStorageAdapter(
59
52
  options,
60
53
  'expo-secure-store',
61
54
  'Expo SecureStore',
62
55
  )) as typeof createExpoSecureStorageAdapter;
63
- createMMKVStorageAdapter = ((options: {
64
- adapterId?: string;
65
- adapterName?: string;
66
- }) =>
67
- createNoopStorageAdapter(
68
- options,
69
- 'mmkv',
70
- 'MMKV',
71
- )) as typeof createMMKVStorageAdapter;
56
+ createMMKVStorageAdapter = ((options: { adapterId?: string; adapterName?: string }) =>
57
+ createNoopStorageAdapter(options, 'mmkv', 'MMKV')) as typeof createMMKVStorageAdapter;
72
58
  useRozeniteStoragePlugin = () => null;
73
59
  } else if (isWeb) {
74
60
  const asyncStorageModule = require('./src/react-native/adapters/async-storage');
75
61
  const secureStorageModule = require('./src/react-native/adapters/secure-storage');
76
62
 
77
63
  createAsyncStorageAdapter = asyncStorageModule.createAsyncStorageAdapter;
78
- createExpoAsyncStorageAdapter =
79
- asyncStorageModule.createExpoAsyncStorageAdapter;
80
- createExpoSecureStorageAdapter =
81
- secureStorageModule.createExpoSecureStorageAdapter;
82
- createMMKVStorageAdapter = ((options: {
83
- adapterId?: string;
84
- adapterName?: string;
85
- }) =>
86
- createNoopStorageAdapter(
87
- options,
88
- 'mmkv',
89
- 'MMKV',
90
- )) as typeof createMMKVStorageAdapter;
64
+ createExpoAsyncStorageAdapter = asyncStorageModule.createExpoAsyncStorageAdapter;
65
+ createExpoSecureStorageAdapter = secureStorageModule.createExpoSecureStorageAdapter;
66
+ createMMKVStorageAdapter = ((options: { adapterId?: string; adapterName?: string }) =>
67
+ createNoopStorageAdapter(options, 'mmkv', 'MMKV')) as typeof createMMKVStorageAdapter;
91
68
  useRozeniteStoragePlugin =
92
69
  require('./src/react-native/useRozeniteStoragePlugin').useRozeniteStoragePlugin;
93
70
  } else {
94
- createAsyncStorageAdapter =
95
- require('./src/react-native/adapters').createAsyncStorageAdapter;
71
+ createAsyncStorageAdapter = require('./src/react-native/adapters').createAsyncStorageAdapter;
96
72
  createExpoAsyncStorageAdapter =
97
73
  require('./src/react-native/adapters').createExpoAsyncStorageAdapter;
98
74
  createExpoSecureStorageAdapter =
99
75
  require('./src/react-native/adapters').createExpoSecureStorageAdapter;
100
- createMMKVStorageAdapter =
101
- require('./src/react-native/adapters').createMMKVStorageAdapter;
76
+ createMMKVStorageAdapter = require('./src/react-native/adapters').createMMKVStorageAdapter;
102
77
  useRozeniteStoragePlugin =
103
78
  require('./src/react-native/useRozeniteStoragePlugin').useRozeniteStoragePlugin;
104
79
  }
@@ -63,16 +63,12 @@ export default {
63
63
  name: 'Initialize storage fixtures',
64
64
  autoRun: true,
65
65
  run: async ({ send, waitForMessage }: DevFlowContext) => {
66
- await waitForMessage(
67
- { type: 'get-snapshot', direction: 'out' },
68
- { timeoutMs: 10000 },
69
- );
66
+ await waitForMessage({ type: 'get-snapshot', direction: 'out' }, { timeoutMs: 10000 });
70
67
 
71
68
  storageFixtures.forEach((snapshot) => send('snapshot', snapshot));
72
69
 
73
70
  return {
74
- message:
75
- 'Initialized AsyncStorage, Expo SecureStore, and MMKV fixtures.',
71
+ message: 'Initialized AsyncStorage, Expo SecureStore, and MMKV fixtures.',
76
72
  storages: storageFixtures.length,
77
73
  entries: storageFixtures.reduce(
78
74
  (total, snapshot) => total + snapshot.entries.length,
package/sdk.ts CHANGED
@@ -5,11 +5,7 @@ import {
5
5
  storageToolDefinitions,
6
6
  } from './src/shared/agent-tools.js';
7
7
 
8
- export {
9
- STORAGE_AGENT_ENTRY_TYPES,
10
- STORAGE_AGENT_PLUGIN_ID,
11
- storageToolDefinitions,
12
- };
8
+ export { STORAGE_AGENT_ENTRY_TYPES, STORAGE_AGENT_PLUGIN_ID, storageToolDefinitions };
13
9
 
14
10
  export const storageTools = defineAgentToolDescriptors(
15
11
  STORAGE_AGENT_PLUGIN_ID,
@@ -13,9 +13,7 @@ const createView = (
13
13
  values: Record<string, StorageEntry>,
14
14
  options?: {
15
15
  blacklist?: RegExp;
16
- get?: (
17
- key: string,
18
- ) => StorageEntry | undefined | Promise<StorageEntry | undefined>;
16
+ get?: (key: string) => StorageEntry | undefined | Promise<StorageEntry | undefined>;
19
17
  },
20
18
  ) => {
21
19
  const get = vi.fn(options?.get ?? ((key: string) => values[key]));
@@ -45,9 +43,7 @@ const createView = (
45
43
  };
46
44
 
47
45
  const entries = (...keys: string[]): Record<string, StorageEntry> =>
48
- Object.fromEntries(
49
- keys.map((key) => [key, { key, type: 'string', value: `value:${key}` }]),
50
- );
46
+ Object.fromEntries(keys.map((key) => [key, { key, type: 'string', value: `value:${key}` }]));
51
47
 
52
48
  const request = (overrides: Record<string, unknown> = {}) => ({
53
49
  type: 'list-entry-previews' as const,
@@ -59,9 +55,7 @@ const request = (overrides: Record<string, unknown> = {}) => ({
59
55
 
60
56
  describe('handleListEntryPreviewsRequest', () => {
61
57
  it('returns sorted first, next, and previous pages without reading outside each page', async () => {
62
- const { view, get } = createView(
63
- entries('delta', 'bravo', 'alpha', 'charlie', 'echo'),
64
- );
58
+ const { view, get } = createView(entries('delta', 'bravo', 'alpha', 'charlie', 'echo'));
65
59
 
66
60
  const first = await handleListEntryPreviewsRequest([view], request());
67
61
  expect(first).toMatchObject({
@@ -85,8 +79,7 @@ describe('handleListEntryPreviewsRequest', () => {
85
79
  const previous = await handleListEntryPreviewsRequest(
86
80
  [view],
87
81
  request({
88
- cursor:
89
- next.type === 'entry-previews' ? next.previousCursor : undefined,
82
+ cursor: next.type === 'entry-previews' ? next.previousCursor : undefined,
90
83
  }),
91
84
  );
92
85
  expect(previous).toMatchObject({
@@ -97,9 +90,7 @@ describe('handleListEntryPreviewsRequest', () => {
97
90
  });
98
91
 
99
92
  it('filters normalized key search and sorts before pagination without inspecting non-page values', async () => {
100
- const { view, get } = createView(
101
- entries('Alpha', 'alphabet', 'beta', 'ALPINE'),
102
- );
93
+ const { view, get } = createView(entries('Alpha', 'alphabet', 'beta', 'ALPINE'));
103
94
 
104
95
  const result = await handleListEntryPreviewsRequest(
105
96
  [view],
@@ -125,9 +116,7 @@ describe('handleListEntryPreviewsRequest', () => {
125
116
  target,
126
117
  items: [],
127
118
  });
128
- await expect(
129
- handleListEntryPreviewsRequest([view], request()),
130
- ).resolves.toEqual({
119
+ await expect(handleListEntryPreviewsRequest([view], request())).resolves.toEqual({
131
120
  type: 'entry-previews',
132
121
  requestId: 'request-1',
133
122
  target,
@@ -148,25 +137,16 @@ describe('handleListEntryPreviewsRequest', () => {
148
137
  const { view } = createView(entries('alpha', 'bravo', 'charlie'));
149
138
 
150
139
  await expect(
151
- handleListEntryPreviewsRequest(
152
- [view],
153
- request({ cursor: 'not-a-cursor' }),
154
- ),
140
+ handleListEntryPreviewsRequest([view], request({ cursor: 'not-a-cursor' })),
155
141
  ).resolves.toMatchObject({ code: 'INVALID_CURSOR', resetPagination: true });
156
142
 
157
143
  const first = await handleListEntryPreviewsRequest([view], request());
158
144
  const cursor = first.type === 'entry-previews' ? first.nextCursor! : '';
159
145
  await expect(
160
- handleListEntryPreviewsRequest(
161
- [view],
162
- request({ search: 'alpha', cursor }),
163
- ),
146
+ handleListEntryPreviewsRequest([view], request({ search: 'alpha', cursor })),
164
147
  ).resolves.toMatchObject({ code: 'INVALID_CURSOR', resetPagination: true });
165
148
 
166
- const stale = await handleListEntryPreviewsRequest(
167
- [view],
168
- request({ cursor }),
169
- );
149
+ const stale = await handleListEntryPreviewsRequest([view], request({ cursor }));
170
150
  expect(stale).toMatchObject({ type: 'entry-previews' });
171
151
  const noBoundary = createView(entries('alpha', 'charlie')).view;
172
152
  await expect(
@@ -177,10 +157,7 @@ describe('handleListEntryPreviewsRequest', () => {
177
157
  it('applies blacklist rules before pagination and caps oversized page limits', async () => {
178
158
  const { view, get } = createView(
179
159
  entries(
180
- ...Array.from(
181
- { length: MAX_ENTRY_PREVIEW_PAGE_SIZE + 2 },
182
- (_, index) => `key-${index}`,
183
- ),
160
+ ...Array.from({ length: MAX_ENTRY_PREVIEW_PAGE_SIZE + 2 }, (_, index) => `key-${index}`),
184
161
  'secret',
185
162
  ),
186
163
  { blacklist: /^secret$/ },
@@ -209,10 +186,7 @@ describe('handleListEntryPreviewsRequest', () => {
209
186
  },
210
187
  });
211
188
 
212
- const result = await handleListEntryPreviewsRequest(
213
- [view],
214
- request({ limit: 5 }),
215
- );
189
+ const result = await handleListEntryPreviewsRequest([view], request({ limit: 5 }));
216
190
 
217
191
  expect(maxActiveReads).toBeLessThanOrEqual(ENTRY_PREVIEW_READ_CONCURRENCY);
218
192
  expect(result).toMatchObject({
@@ -239,9 +213,7 @@ describe('handleListEntryPreviewsRequest', () => {
239
213
  },
240
214
  });
241
215
 
242
- await expect(
243
- handleListEntryPreviewsRequest([view], request()),
244
- ).resolves.toEqual({
216
+ await expect(handleListEntryPreviewsRequest([view], request())).resolves.toEqual({
245
217
  type: 'storage-request-error',
246
218
  requestId: 'request-1',
247
219
  code: 'READ_FAILED',
@@ -89,9 +89,7 @@ describe('handleExportSnapshotRequest', () => {
89
89
  it('exports an empty storage as a valid empty snapshot', async () => {
90
90
  const { view } = createView({});
91
91
 
92
- await expect(
93
- handleExportSnapshotRequest([view], request()),
94
- ).resolves.toMatchObject({
92
+ await expect(handleExportSnapshotRequest([view], request())).resolves.toMatchObject({
95
93
  type: 'export-snapshot-result',
96
94
  snapshot: { entries: [] },
97
95
  });
@@ -130,9 +128,7 @@ describe('handleExportSnapshotRequest', () => {
130
128
  },
131
129
  );
132
130
 
133
- await expect(
134
- handleExportSnapshotRequest([view], request()),
135
- ).resolves.toEqual({
131
+ await expect(handleExportSnapshotRequest([view], request())).resolves.toEqual({
136
132
  type: 'storage-request-error',
137
133
  requestId: 'request-1',
138
134
  code: 'READ_FAILED',
@@ -17,9 +17,7 @@ const createView = (
17
17
  values: Record<string, StorageEntry>,
18
18
  options?: {
19
19
  blacklist?: RegExp;
20
- get?: (
21
- key: string,
22
- ) => StorageEntry | undefined | Promise<StorageEntry | undefined>;
20
+ get?: (key: string) => StorageEntry | undefined | Promise<StorageEntry | undefined>;
23
21
  },
24
22
  ) => {
25
23
  const get = vi.fn(options?.get ?? ((key: string) => values[key]));
@@ -69,9 +67,7 @@ describe('handleFullEntryRequest', () => {
69
67
 
70
68
  it('returns structured errors for missing, blacklisted, invalid, and unknown targets', async () => {
71
69
  const missing = createView({});
72
- await expect(
73
- handleFullEntryRequest([missing.view], request()),
74
- ).resolves.toMatchObject({
70
+ await expect(handleFullEntryRequest([missing.view], request())).resolves.toMatchObject({
75
71
  type: 'storage-request-error',
76
72
  requestId: 'request-1',
77
73
  code: 'ENTRY_NOT_FOUND',
@@ -81,9 +77,7 @@ describe('handleFullEntryRequest', () => {
81
77
  { key: { key: 'key', type: 'string', value: 'secret' } },
82
78
  { blacklist: /^key$/ },
83
79
  );
84
- await expect(
85
- handleFullEntryRequest([blacklisted.view], request()),
86
- ).resolves.toMatchObject({
80
+ await expect(handleFullEntryRequest([blacklisted.view], request())).resolves.toMatchObject({
87
81
  code: 'ENTRY_NOT_FOUND',
88
82
  });
89
83
  expect(blacklisted.get).not.toHaveBeenCalled();
@@ -128,14 +122,8 @@ describe('handleFullEntryRequest', () => {
128
122
  }),
129
123
  },
130
124
  );
131
- const first = handleFullEntryRequest(
132
- [view],
133
- request({ requestId: 'first', key: 'first' }),
134
- );
135
- const second = handleFullEntryRequest(
136
- [view],
137
- request({ requestId: 'second', key: 'second' }),
138
- );
125
+ const first = handleFullEntryRequest([view], request({ requestId: 'first', key: 'first' }));
126
+ const second = handleFullEntryRequest([view], request({ requestId: 'second', key: 'second' }));
139
127
 
140
128
  await vi.waitFor(() => expect(get).toHaveBeenCalledTimes(2));
141
129
  resolvers.get('second')?.({ key: 'second', type: 'string', value: 'two' });
@@ -29,7 +29,7 @@ const buildView = (overrides?: Partial<StorageView>): StorageView => {
29
29
  set: vi.fn(async () => {}),
30
30
  delete: unexpected('delete'),
31
31
  purge: unexpected('purge'),
32
- getAllKeys: unexpected('getAllKeys'),
32
+ getAllKeys: vi.fn(async () => []),
33
33
  getAllEntries: unexpected('getAllEntries'),
34
34
  watch: unexpected('watch'),
35
35
  ...overrides,
@@ -74,6 +74,7 @@ describe('handleImportEntries', () => {
74
74
  type: 'storage-invalidated',
75
75
  target,
76
76
  operation: 'import',
77
+ entryCount: 0,
77
78
  });
78
79
  expect(emitted[6]).toEqual({
79
80
  type: 'import-result',
@@ -122,6 +123,7 @@ describe('handleImportEntries', () => {
122
123
  type: 'storage-invalidated',
123
124
  target,
124
125
  operation: 'import',
126
+ entryCount: 0,
125
127
  });
126
128
  expect(emitted[3]).toEqual({
127
129
  type: 'import-result',
@@ -294,6 +296,7 @@ describe('handleImportEntries', () => {
294
296
  type: 'storage-invalidated',
295
297
  target,
296
298
  operation: 'import',
299
+ entryCount: 0,
297
300
  },
298
301
  {
299
302
  type: 'import-result',
@@ -309,13 +312,11 @@ describe('handleImportEntries', () => {
309
312
  it('returns predictable errors for malformed and unknown preview targets', async () => {
310
313
  const view = buildView();
311
314
 
312
- await expect(handleImportPreviewRequest([view], {})).resolves.toMatchObject(
313
- {
314
- type: 'storage-request-error',
315
- requestId: '',
316
- code: 'INVALID_REQUEST',
317
- },
318
- );
315
+ await expect(handleImportPreviewRequest([view], {})).resolves.toMatchObject({
316
+ type: 'storage-request-error',
317
+ requestId: '',
318
+ code: 'INVALID_REQUEST',
319
+ });
319
320
  await expect(
320
321
  handleImportPreviewRequest([view], {
321
322
  type: 'preview-import',
@@ -1,9 +1,5 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
- import type {
3
- StorageAdapter,
4
- StorageCapabilities,
5
- StorageTarget,
6
- } from '../../shared/types';
2
+ import type { StorageAdapter, StorageCapabilities, StorageTarget } from '../../shared/types';
7
3
  import { handleStorageDiscoveryRequest } from '../storage-discovery';
8
4
  import { createStorageViews, type StorageView } from '../storage-view';
9
5
 
@@ -92,13 +88,10 @@ describe('handleStorageDiscoveryRequest', () => {
92
88
 
93
89
  it('returns a correlated structured error for invalid requests', async () => {
94
90
  expect(
95
- await handleStorageDiscoveryRequest(
96
- createStorageViews([createAdapter()]),
97
- {
98
- type: 'discover-storages',
99
- requestId: ' ',
100
- },
101
- ),
91
+ await handleStorageDiscoveryRequest(createStorageViews([createAdapter()]), {
92
+ type: 'discover-storages',
93
+ requestId: ' ',
94
+ }),
102
95
  ).toEqual({
103
96
  type: 'storage-request-error',
104
97
  requestId: ' ',
@@ -125,9 +118,7 @@ describe('handleStorageDiscoveryRequest', () => {
125
118
  watch: vi.fn(),
126
119
  });
127
120
 
128
- expect(
129
- await handleStorageDiscoveryRequest([view('One'), view('Two')], request),
130
- ).toEqual({
121
+ expect(await handleStorageDiscoveryRequest([view('One'), view('Two')], request)).toEqual({
131
122
  type: 'storage-request-error',
132
123
  requestId: 'request-1',
133
124
  code: 'INVALID_REQUEST',
@@ -143,9 +134,7 @@ describe('handleStorageDiscoveryRequest', () => {
143
134
  },
144
135
  } as unknown as StorageView;
145
136
 
146
- await expect(
147
- handleStorageDiscoveryRequest([view], request),
148
- ).resolves.toEqual({
137
+ await expect(handleStorageDiscoveryRequest([view], request)).resolves.toEqual({
149
138
  type: 'storage-request-error',
150
139
  requestId: 'request-1',
151
140
  code: 'READ_FAILED',
@@ -1,9 +1,5 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
- import type {
3
- StorageAdapter,
4
- StorageEntry,
5
- StorageEntryType,
6
- } from '../../shared/types';
2
+ import type { StorageAdapter, StorageEntry, StorageEntryType } from '../../shared/types';
7
3
  import {
8
4
  ENTRY_PREVIEW_READ_CONCURRENCY,
9
5
  MAX_ENTRY_PREVIEW_PAGE_SIZE,
@@ -100,10 +96,7 @@ describe('storage scalability stress fixture', () => {
100
96
  it('reads only one bounded page from the selected storage when values are slow and large', async () => {
101
97
  const fixture = createStressAdapter();
102
98
 
103
- const result = await handleListEntryPreviewsRequest(
104
- fixture.views,
105
- previewRequest(),
106
- );
99
+ const result = await handleListEntryPreviewsRequest(fixture.views, previewRequest());
107
100
 
108
101
  expect(result).toMatchObject({
109
102
  type: 'entry-previews',
@@ -119,12 +112,8 @@ describe('storage scalability stress fixture', () => {
119
112
  expect(result.items.every((item) => !('value' in item))).toBe(true);
120
113
  }
121
114
  expect(fixture.storageReads[0]?.getAllKeys).toHaveBeenCalledTimes(1);
122
- expect(fixture.storageReads[0]?.get).toHaveBeenCalledTimes(
123
- MAX_ENTRY_PREVIEW_PAGE_SIZE,
124
- );
125
- expect(fixture.maxActiveReads()).toBeLessThanOrEqual(
126
- ENTRY_PREVIEW_READ_CONCURRENCY,
127
- );
115
+ expect(fixture.storageReads[0]?.get).toHaveBeenCalledTimes(MAX_ENTRY_PREVIEW_PAGE_SIZE);
116
+ expect(fixture.maxActiveReads()).toBeLessThanOrEqual(ENTRY_PREVIEW_READ_CONCURRENCY);
128
117
  for (const { get, getAllKeys } of fixture.storageReads.slice(1)) {
129
118
  expect(getAllKeys).not.toHaveBeenCalled();
130
119
  expect(get).not.toHaveBeenCalled();
@@ -143,9 +132,7 @@ describe('storage scalability stress fixture', () => {
143
132
  type: 'entry-previews',
144
133
  items: [expect.objectContaining({ key: 'entry-0999' })],
145
134
  });
146
- expect(fixture.storageReads[0]?.get).toHaveBeenCalledExactlyOnceWith(
147
- 'entry-0999',
148
- );
135
+ expect(fixture.storageReads[0]?.get).toHaveBeenCalledExactlyOnceWith('entry-0999');
149
136
  for (const { get } of fixture.storageReads.slice(1)) {
150
137
  expect(get).not.toHaveBeenCalled();
151
138
  }