@metamask-previews/storage-service 1.0.0-preview-e493d3e8 → 1.0.0-preview-81c9cf83

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 (46) hide show
  1. package/CHANGELOG.md +0 -8
  2. package/LICENSE +4 -18
  3. package/LICENSE.APACHE2 +201 -0
  4. package/LICENSE.MIT +21 -0
  5. package/README.md +41 -303
  6. package/dist/InMemoryStorageAdapter.cjs +129 -0
  7. package/dist/InMemoryStorageAdapter.cjs.map +1 -0
  8. package/dist/InMemoryStorageAdapter.d.cts +71 -0
  9. package/dist/InMemoryStorageAdapter.d.cts.map +1 -0
  10. package/dist/InMemoryStorageAdapter.d.mts +71 -0
  11. package/dist/InMemoryStorageAdapter.d.mts.map +1 -0
  12. package/dist/InMemoryStorageAdapter.mjs +125 -0
  13. package/dist/InMemoryStorageAdapter.mjs.map +1 -0
  14. package/dist/StorageService-method-action-types.cjs +7 -0
  15. package/dist/StorageService-method-action-types.cjs.map +1 -0
  16. package/dist/StorageService-method-action-types.d.cts +81 -0
  17. package/dist/StorageService-method-action-types.d.cts.map +1 -0
  18. package/dist/StorageService-method-action-types.d.mts +81 -0
  19. package/dist/StorageService-method-action-types.d.mts.map +1 -0
  20. package/dist/StorageService-method-action-types.mjs +6 -0
  21. package/dist/StorageService-method-action-types.mjs.map +1 -0
  22. package/dist/StorageService.cjs +203 -0
  23. package/dist/StorageService.cjs.map +1 -0
  24. package/dist/StorageService.d.cts +147 -0
  25. package/dist/StorageService.d.cts.map +1 -0
  26. package/dist/StorageService.d.mts +147 -0
  27. package/dist/StorageService.d.mts.map +1 -0
  28. package/dist/StorageService.mjs +199 -0
  29. package/dist/StorageService.mjs.map +1 -0
  30. package/dist/index.cjs +14 -0
  31. package/dist/index.cjs.map +1 -0
  32. package/dist/index.d.cts +6 -0
  33. package/dist/index.d.cts.map +1 -0
  34. package/dist/index.d.mts +6 -0
  35. package/dist/index.d.mts.map +1 -0
  36. package/dist/index.mjs +7 -0
  37. package/dist/index.mjs.map +1 -0
  38. package/dist/types.cjs +12 -0
  39. package/dist/types.cjs.map +1 -0
  40. package/dist/types.d.cts +148 -0
  41. package/dist/types.d.cts.map +1 -0
  42. package/dist/types.d.mts +148 -0
  43. package/dist/types.d.mts.map +1 -0
  44. package/dist/types.mjs +9 -0
  45. package/dist/types.mjs.map +1 -0
  46. package/package.json +2 -2
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _StorageService_messenger, _StorageService_storage;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.StorageService = void 0;
16
+ const InMemoryStorageAdapter_1 = require("./InMemoryStorageAdapter.cjs");
17
+ const types_1 = require("./types.cjs");
18
+ // === MESSENGER ===
19
+ const MESSENGER_EXPOSED_METHODS = [
20
+ 'setItem',
21
+ 'getItem',
22
+ 'removeItem',
23
+ 'getAllKeys',
24
+ 'clear',
25
+ ];
26
+ /**
27
+ * StorageService provides a platform-agnostic way for controllers to store
28
+ * large, infrequently accessed data outside of memory/Redux state.
29
+ *
30
+ * **Use cases:**
31
+ * - Snap source code (6+ MB that's rarely accessed)
32
+ * - Token metadata caches (4+ MB of cached data)
33
+ * - Large cached responses from APIs
34
+ * - Any data > 100 KB that's not frequently accessed
35
+ *
36
+ * **Benefits:**
37
+ * - Reduces memory usage (data stays on disk)
38
+ * - Faster Redux persist (less data to serialize)
39
+ * - Faster app startup (less data to parse)
40
+ * - Lazy loading (data loaded only when needed)
41
+ *
42
+ * **Platform Support:**
43
+ * - Mobile: FilesystemStorage adapter
44
+ * - Extension: IndexedDB adapter
45
+ * - Tests/Dev: InMemoryStorageAdapter (default)
46
+ *
47
+ * @example Using the service via messenger
48
+ *
49
+ * ```typescript
50
+ * // In a controller
51
+ * type AllowedActions =
52
+ * | StorageServiceSetItemAction
53
+ * | StorageServiceGetItemAction;
54
+ *
55
+ * class SnapController extends BaseController {
56
+ * async storeSnapSourceCode(snapId: string, sourceCode: string) {
57
+ * await this.messenger.call(
58
+ * 'StorageService:setItem',
59
+ * 'SnapController',
60
+ * `${snapId}:sourceCode`,
61
+ * sourceCode,
62
+ * );
63
+ * }
64
+ *
65
+ * async getSnapSourceCode(snapId: string): Promise<string | null> {
66
+ * const result = await this.messenger.call(
67
+ * 'StorageService:getItem',
68
+ * 'SnapController',
69
+ * `${snapId}:sourceCode`,
70
+ * );
71
+ * return result as string | null; // Caller must validate/cast
72
+ * }
73
+ * }
74
+ * ```
75
+ *
76
+ * @example Initializing in a client
77
+ *
78
+ * ```typescript
79
+ * // Mobile
80
+ * const service = new StorageService({
81
+ * messenger: storageServiceMessenger,
82
+ * storage: filesystemStorageAdapter, // Platform-specific
83
+ * });
84
+ *
85
+ * // Extension
86
+ * const service = new StorageService({
87
+ * messenger: storageServiceMessenger,
88
+ * storage: indexedDBAdapter, // Platform-specific
89
+ * });
90
+ *
91
+ * // Tests (uses in-memory by default)
92
+ * const service = new StorageService({
93
+ * messenger: storageServiceMessenger,
94
+ * // No storage - uses InMemoryStorageAdapter
95
+ * });
96
+ * ```
97
+ */
98
+ class StorageService {
99
+ /**
100
+ * Constructs a new StorageService.
101
+ *
102
+ * @param options - The options.
103
+ * @param options.messenger - The messenger suited for this service.
104
+ * @param options.storage - Storage adapter for persisting data.
105
+ * If not provided, uses InMemoryStorageAdapter (data lost on restart).
106
+ */
107
+ constructor({ messenger, storage }) {
108
+ /**
109
+ * The messenger suited for this service.
110
+ */
111
+ _StorageService_messenger.set(this, void 0);
112
+ /**
113
+ * The storage adapter for persisting data.
114
+ */
115
+ _StorageService_storage.set(this, void 0);
116
+ this.name = types_1.SERVICE_NAME;
117
+ __classPrivateFieldSet(this, _StorageService_messenger, messenger, "f");
118
+ __classPrivateFieldSet(this, _StorageService_storage, storage ?? new InMemoryStorageAdapter_1.InMemoryStorageAdapter(), "f");
119
+ // Warn if using in-memory storage (data won't persist)
120
+ if (!storage) {
121
+ console.warn(`${types_1.SERVICE_NAME}: No storage adapter provided. Using in-memory storage. ` +
122
+ 'Data will be lost on restart. Provide a storage adapter for persistence.');
123
+ }
124
+ // Register messenger actions
125
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
126
+ }
127
+ /**
128
+ * Store large data in storage.
129
+ *
130
+ * ⚠️ **Designed for large values (100KB+), not many small ones.**
131
+ * Each storage operation has I/O overhead. For best performance,
132
+ * store one large object rather than many small key-value pairs.
133
+ *
134
+ * @example Good: Store entire cache as one value
135
+ * ```typescript
136
+ * await service.setItem('TokenList', 'cache', { '0x1': [...], '0x38': [...] });
137
+ * ```
138
+ *
139
+ * @example Avoid: Many small values
140
+ * ```typescript
141
+ * // ❌ Don't do this - too many small writes
142
+ * await service.setItem('TokenList', 'cache:0x1', [...]);
143
+ * await service.setItem('TokenList', 'cache:0x38', [...]);
144
+ * ```
145
+ *
146
+ * @param namespace - Controller namespace (e.g., 'SnapController').
147
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
148
+ * @param value - Data to store (should be 100KB+ for optimal use).
149
+ */
150
+ async setItem(namespace, key, value) {
151
+ // Adapter handles serialization and wrapping with metadata
152
+ await __classPrivateFieldGet(this, _StorageService_storage, "f").setItem(namespace, key, value);
153
+ // Publish event so other controllers can react to changes
154
+ // Event type: StorageService:itemSet:namespace
155
+ // Payload: [value, key]
156
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").publish(`${types_1.SERVICE_NAME}:itemSet:${namespace}`, value, key);
157
+ }
158
+ /**
159
+ * Retrieve data from storage.
160
+ *
161
+ * Returns `unknown` since there's no schema validation.
162
+ * Callers should validate or cast the result to the expected type.
163
+ *
164
+ * @param namespace - Controller namespace (e.g., 'SnapController').
165
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
166
+ * @returns Parsed data or null if not found. Type is `unknown` - caller must validate.
167
+ */
168
+ async getItem(namespace, key) {
169
+ // Adapter handles deserialization and unwrapping
170
+ return await __classPrivateFieldGet(this, _StorageService_storage, "f").getItem(namespace, key);
171
+ }
172
+ /**
173
+ * Remove data from storage.
174
+ *
175
+ * @param namespace - Controller namespace (e.g., 'SnapController').
176
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
177
+ */
178
+ async removeItem(namespace, key) {
179
+ // Adapter builds full storage key (e.g., mobile: 'storageService:namespace:key')
180
+ await __classPrivateFieldGet(this, _StorageService_storage, "f").removeItem(namespace, key);
181
+ }
182
+ /**
183
+ * Get all keys for a namespace.
184
+ * Delegates to storage adapter which handles filtering.
185
+ *
186
+ * @param namespace - Controller namespace (e.g., 'SnapController').
187
+ * @returns Array of keys (without prefix) for this namespace.
188
+ */
189
+ async getAllKeys(namespace) {
190
+ return await __classPrivateFieldGet(this, _StorageService_storage, "f").getAllKeys(namespace);
191
+ }
192
+ /**
193
+ * Clear all data for a namespace.
194
+ *
195
+ * @param namespace - Controller namespace (e.g., 'SnapController').
196
+ */
197
+ async clear(namespace) {
198
+ await __classPrivateFieldGet(this, _StorageService_storage, "f").clear(namespace);
199
+ }
200
+ }
201
+ exports.StorageService = StorageService;
202
+ _StorageService_messenger = new WeakMap(), _StorageService_storage = new WeakMap();
203
+ //# sourceMappingURL=StorageService.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageService.cjs","sourceRoot":"","sources":["../src/StorageService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,yEAAkE;AAMlE,uCAAuC;AAEvC,oBAAoB;AAEpB,MAAM,yBAAyB,GAAG;IAChC,SAAS;IACT,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,OAAO;CACC,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,MAAa,cAAc;IAgBzB;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS,EAAE,OAAO,EAAyB;QAlBzD;;WAEG;QACM,4CAAoC;QAE7C;;WAEG;QACM,0CAAyB;QAWhC,IAAI,CAAC,IAAI,GAAG,oBAAY,CAAC;QACzB,uBAAA,IAAI,6BAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,2BAAY,OAAO,IAAI,IAAI,+CAAsB,EAAE,MAAA,CAAC;QAExD,uDAAuD;QACvD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CACV,GAAG,oBAAY,0DAA0D;gBACvE,0EAA0E,CAC7E,CAAC;QACJ,CAAC;QAED,6BAA6B;QAC7B,uBAAA,IAAI,iCAAW,CAAC,4BAA4B,CAC1C,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,GAAW,EAAE,KAAc;QAC1D,2DAA2D;QAC3D,MAAM,uBAAA,IAAI,+BAAS,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAEnD,0DAA0D;QAC1D,+CAA+C;QAC/C,wBAAwB;QACxB,uBAAA,IAAI,iCAAW,CAAC,OAAO,CACrB,GAAG,oBAAY,YAAY,SAAS,EAAW,EAC/C,KAAK,EACL,GAAG,CACJ,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,GAAW;QAC1C,iDAAiD;QACjD,OAAO,MAAM,uBAAA,IAAI,+BAAS,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,GAAW;QAC7C,iFAAiF;QACjF,MAAM,uBAAA,IAAI,+BAAS,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,OAAO,MAAM,uBAAA,IAAI,+BAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,uBAAA,IAAI,+BAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;CACF;AA9HD,wCA8HC","sourcesContent":["import { InMemoryStorageAdapter } from './InMemoryStorageAdapter';\nimport type {\n StorageAdapter,\n StorageServiceMessenger,\n StorageServiceOptions,\n} from './types';\nimport { SERVICE_NAME } from './types';\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'setItem',\n 'getItem',\n 'removeItem',\n 'getAllKeys',\n 'clear',\n] as const;\n\n/**\n * StorageService provides a platform-agnostic way for controllers to store\n * large, infrequently accessed data outside of memory/Redux state.\n *\n * **Use cases:**\n * - Snap source code (6+ MB that's rarely accessed)\n * - Token metadata caches (4+ MB of cached data)\n * - Large cached responses from APIs\n * - Any data > 100 KB that's not frequently accessed\n *\n * **Benefits:**\n * - Reduces memory usage (data stays on disk)\n * - Faster Redux persist (less data to serialize)\n * - Faster app startup (less data to parse)\n * - Lazy loading (data loaded only when needed)\n *\n * **Platform Support:**\n * - Mobile: FilesystemStorage adapter\n * - Extension: IndexedDB adapter\n * - Tests/Dev: InMemoryStorageAdapter (default)\n *\n * @example Using the service via messenger\n *\n * ```typescript\n * // In a controller\n * type AllowedActions =\n * | StorageServiceSetItemAction\n * | StorageServiceGetItemAction;\n *\n * class SnapController extends BaseController {\n * async storeSnapSourceCode(snapId: string, sourceCode: string) {\n * await this.messenger.call(\n * 'StorageService:setItem',\n * 'SnapController',\n * `${snapId}:sourceCode`,\n * sourceCode,\n * );\n * }\n *\n * async getSnapSourceCode(snapId: string): Promise<string | null> {\n * const result = await this.messenger.call(\n * 'StorageService:getItem',\n * 'SnapController',\n * `${snapId}:sourceCode`,\n * );\n * return result as string | null; // Caller must validate/cast\n * }\n * }\n * ```\n *\n * @example Initializing in a client\n *\n * ```typescript\n * // Mobile\n * const service = new StorageService({\n * messenger: storageServiceMessenger,\n * storage: filesystemStorageAdapter, // Platform-specific\n * });\n *\n * // Extension\n * const service = new StorageService({\n * messenger: storageServiceMessenger,\n * storage: indexedDBAdapter, // Platform-specific\n * });\n *\n * // Tests (uses in-memory by default)\n * const service = new StorageService({\n * messenger: storageServiceMessenger,\n * // No storage - uses InMemoryStorageAdapter\n * });\n * ```\n */\nexport class StorageService {\n /**\n * The name of the service.\n */\n readonly name: typeof SERVICE_NAME;\n\n /**\n * The messenger suited for this service.\n */\n readonly #messenger: StorageServiceMessenger;\n\n /**\n * The storage adapter for persisting data.\n */\n readonly #storage: StorageAdapter;\n\n /**\n * Constructs a new StorageService.\n *\n * @param options - The options.\n * @param options.messenger - The messenger suited for this service.\n * @param options.storage - Storage adapter for persisting data.\n * If not provided, uses InMemoryStorageAdapter (data lost on restart).\n */\n constructor({ messenger, storage }: StorageServiceOptions) {\n this.name = SERVICE_NAME;\n this.#messenger = messenger;\n this.#storage = storage ?? new InMemoryStorageAdapter();\n\n // Warn if using in-memory storage (data won't persist)\n if (!storage) {\n console.warn(\n `${SERVICE_NAME}: No storage adapter provided. Using in-memory storage. ` +\n 'Data will be lost on restart. Provide a storage adapter for persistence.',\n );\n }\n\n // Register messenger actions\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Store large data in storage.\n *\n * ⚠️ **Designed for large values (100KB+), not many small ones.**\n * Each storage operation has I/O overhead. For best performance,\n * store one large object rather than many small key-value pairs.\n *\n * @example Good: Store entire cache as one value\n * ```typescript\n * await service.setItem('TokenList', 'cache', { '0x1': [...], '0x38': [...] });\n * ```\n *\n * @example Avoid: Many small values\n * ```typescript\n * // ❌ Don't do this - too many small writes\n * await service.setItem('TokenList', 'cache:0x1', [...]);\n * await service.setItem('TokenList', 'cache:0x38', [...]);\n * ```\n *\n * @param namespace - Controller namespace (e.g., 'SnapController').\n * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').\n * @param value - Data to store (should be 100KB+ for optimal use).\n */\n async setItem(namespace: string, key: string, value: unknown): Promise<void> {\n // Adapter handles serialization and wrapping with metadata\n await this.#storage.setItem(namespace, key, value);\n\n // Publish event so other controllers can react to changes\n // Event type: StorageService:itemSet:namespace\n // Payload: [value, key]\n this.#messenger.publish(\n `${SERVICE_NAME}:itemSet:${namespace}` as const,\n value,\n key,\n );\n }\n\n /**\n * Retrieve data from storage.\n *\n * Returns `unknown` since there's no schema validation.\n * Callers should validate or cast the result to the expected type.\n *\n * @param namespace - Controller namespace (e.g., 'SnapController').\n * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').\n * @returns Parsed data or null if not found. Type is `unknown` - caller must validate.\n */\n async getItem(namespace: string, key: string): Promise<unknown> {\n // Adapter handles deserialization and unwrapping\n return await this.#storage.getItem(namespace, key);\n }\n\n /**\n * Remove data from storage.\n *\n * @param namespace - Controller namespace (e.g., 'SnapController').\n * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').\n */\n async removeItem(namespace: string, key: string): Promise<void> {\n // Adapter builds full storage key (e.g., mobile: 'storageService:namespace:key')\n await this.#storage.removeItem(namespace, key);\n }\n\n /**\n * Get all keys for a namespace.\n * Delegates to storage adapter which handles filtering.\n *\n * @param namespace - Controller namespace (e.g., 'SnapController').\n * @returns Array of keys (without prefix) for this namespace.\n */\n async getAllKeys(namespace: string): Promise<string[]> {\n return await this.#storage.getAllKeys(namespace);\n }\n\n /**\n * Clear all data for a namespace.\n *\n * @param namespace - Controller namespace (e.g., 'SnapController').\n */\n async clear(namespace: string): Promise<void> {\n await this.#storage.clear(namespace);\n }\n}\n"]}
@@ -0,0 +1,147 @@
1
+ import type { StorageServiceOptions } from "./types.cjs";
2
+ import { SERVICE_NAME } from "./types.cjs";
3
+ /**
4
+ * StorageService provides a platform-agnostic way for controllers to store
5
+ * large, infrequently accessed data outside of memory/Redux state.
6
+ *
7
+ * **Use cases:**
8
+ * - Snap source code (6+ MB that's rarely accessed)
9
+ * - Token metadata caches (4+ MB of cached data)
10
+ * - Large cached responses from APIs
11
+ * - Any data > 100 KB that's not frequently accessed
12
+ *
13
+ * **Benefits:**
14
+ * - Reduces memory usage (data stays on disk)
15
+ * - Faster Redux persist (less data to serialize)
16
+ * - Faster app startup (less data to parse)
17
+ * - Lazy loading (data loaded only when needed)
18
+ *
19
+ * **Platform Support:**
20
+ * - Mobile: FilesystemStorage adapter
21
+ * - Extension: IndexedDB adapter
22
+ * - Tests/Dev: InMemoryStorageAdapter (default)
23
+ *
24
+ * @example Using the service via messenger
25
+ *
26
+ * ```typescript
27
+ * // In a controller
28
+ * type AllowedActions =
29
+ * | StorageServiceSetItemAction
30
+ * | StorageServiceGetItemAction;
31
+ *
32
+ * class SnapController extends BaseController {
33
+ * async storeSnapSourceCode(snapId: string, sourceCode: string) {
34
+ * await this.messenger.call(
35
+ * 'StorageService:setItem',
36
+ * 'SnapController',
37
+ * `${snapId}:sourceCode`,
38
+ * sourceCode,
39
+ * );
40
+ * }
41
+ *
42
+ * async getSnapSourceCode(snapId: string): Promise<string | null> {
43
+ * const result = await this.messenger.call(
44
+ * 'StorageService:getItem',
45
+ * 'SnapController',
46
+ * `${snapId}:sourceCode`,
47
+ * );
48
+ * return result as string | null; // Caller must validate/cast
49
+ * }
50
+ * }
51
+ * ```
52
+ *
53
+ * @example Initializing in a client
54
+ *
55
+ * ```typescript
56
+ * // Mobile
57
+ * const service = new StorageService({
58
+ * messenger: storageServiceMessenger,
59
+ * storage: filesystemStorageAdapter, // Platform-specific
60
+ * });
61
+ *
62
+ * // Extension
63
+ * const service = new StorageService({
64
+ * messenger: storageServiceMessenger,
65
+ * storage: indexedDBAdapter, // Platform-specific
66
+ * });
67
+ *
68
+ * // Tests (uses in-memory by default)
69
+ * const service = new StorageService({
70
+ * messenger: storageServiceMessenger,
71
+ * // No storage - uses InMemoryStorageAdapter
72
+ * });
73
+ * ```
74
+ */
75
+ export declare class StorageService {
76
+ #private;
77
+ /**
78
+ * The name of the service.
79
+ */
80
+ readonly name: typeof SERVICE_NAME;
81
+ /**
82
+ * Constructs a new StorageService.
83
+ *
84
+ * @param options - The options.
85
+ * @param options.messenger - The messenger suited for this service.
86
+ * @param options.storage - Storage adapter for persisting data.
87
+ * If not provided, uses InMemoryStorageAdapter (data lost on restart).
88
+ */
89
+ constructor({ messenger, storage }: StorageServiceOptions);
90
+ /**
91
+ * Store large data in storage.
92
+ *
93
+ * ⚠️ **Designed for large values (100KB+), not many small ones.**
94
+ * Each storage operation has I/O overhead. For best performance,
95
+ * store one large object rather than many small key-value pairs.
96
+ *
97
+ * @example Good: Store entire cache as one value
98
+ * ```typescript
99
+ * await service.setItem('TokenList', 'cache', { '0x1': [...], '0x38': [...] });
100
+ * ```
101
+ *
102
+ * @example Avoid: Many small values
103
+ * ```typescript
104
+ * // ❌ Don't do this - too many small writes
105
+ * await service.setItem('TokenList', 'cache:0x1', [...]);
106
+ * await service.setItem('TokenList', 'cache:0x38', [...]);
107
+ * ```
108
+ *
109
+ * @param namespace - Controller namespace (e.g., 'SnapController').
110
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
111
+ * @param value - Data to store (should be 100KB+ for optimal use).
112
+ */
113
+ setItem(namespace: string, key: string, value: unknown): Promise<void>;
114
+ /**
115
+ * Retrieve data from storage.
116
+ *
117
+ * Returns `unknown` since there's no schema validation.
118
+ * Callers should validate or cast the result to the expected type.
119
+ *
120
+ * @param namespace - Controller namespace (e.g., 'SnapController').
121
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
122
+ * @returns Parsed data or null if not found. Type is `unknown` - caller must validate.
123
+ */
124
+ getItem(namespace: string, key: string): Promise<unknown>;
125
+ /**
126
+ * Remove data from storage.
127
+ *
128
+ * @param namespace - Controller namespace (e.g., 'SnapController').
129
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
130
+ */
131
+ removeItem(namespace: string, key: string): Promise<void>;
132
+ /**
133
+ * Get all keys for a namespace.
134
+ * Delegates to storage adapter which handles filtering.
135
+ *
136
+ * @param namespace - Controller namespace (e.g., 'SnapController').
137
+ * @returns Array of keys (without prefix) for this namespace.
138
+ */
139
+ getAllKeys(namespace: string): Promise<string[]>;
140
+ /**
141
+ * Clear all data for a namespace.
142
+ *
143
+ * @param namespace - Controller namespace (e.g., 'SnapController').
144
+ */
145
+ clear(namespace: string): Promise<void>;
146
+ }
147
+ //# sourceMappingURL=StorageService.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageService.d.cts","sourceRoot":"","sources":["../src/StorageService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,qBAAqB,EACtB,oBAAgB;AACjB,OAAO,EAAE,YAAY,EAAE,oBAAgB;AAYvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,qBAAa,cAAc;;IACzB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,YAAY,CAAC;IAYnC;;;;;;;OAOG;gBACS,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,qBAAqB;IAoBzD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5E;;;;;;;;;OASG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK/D;;;;;OAKG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D;;;;;;OAMG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAItD;;;;OAIG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}
@@ -0,0 +1,147 @@
1
+ import type { StorageServiceOptions } from "./types.mjs";
2
+ import { SERVICE_NAME } from "./types.mjs";
3
+ /**
4
+ * StorageService provides a platform-agnostic way for controllers to store
5
+ * large, infrequently accessed data outside of memory/Redux state.
6
+ *
7
+ * **Use cases:**
8
+ * - Snap source code (6+ MB that's rarely accessed)
9
+ * - Token metadata caches (4+ MB of cached data)
10
+ * - Large cached responses from APIs
11
+ * - Any data > 100 KB that's not frequently accessed
12
+ *
13
+ * **Benefits:**
14
+ * - Reduces memory usage (data stays on disk)
15
+ * - Faster Redux persist (less data to serialize)
16
+ * - Faster app startup (less data to parse)
17
+ * - Lazy loading (data loaded only when needed)
18
+ *
19
+ * **Platform Support:**
20
+ * - Mobile: FilesystemStorage adapter
21
+ * - Extension: IndexedDB adapter
22
+ * - Tests/Dev: InMemoryStorageAdapter (default)
23
+ *
24
+ * @example Using the service via messenger
25
+ *
26
+ * ```typescript
27
+ * // In a controller
28
+ * type AllowedActions =
29
+ * | StorageServiceSetItemAction
30
+ * | StorageServiceGetItemAction;
31
+ *
32
+ * class SnapController extends BaseController {
33
+ * async storeSnapSourceCode(snapId: string, sourceCode: string) {
34
+ * await this.messenger.call(
35
+ * 'StorageService:setItem',
36
+ * 'SnapController',
37
+ * `${snapId}:sourceCode`,
38
+ * sourceCode,
39
+ * );
40
+ * }
41
+ *
42
+ * async getSnapSourceCode(snapId: string): Promise<string | null> {
43
+ * const result = await this.messenger.call(
44
+ * 'StorageService:getItem',
45
+ * 'SnapController',
46
+ * `${snapId}:sourceCode`,
47
+ * );
48
+ * return result as string | null; // Caller must validate/cast
49
+ * }
50
+ * }
51
+ * ```
52
+ *
53
+ * @example Initializing in a client
54
+ *
55
+ * ```typescript
56
+ * // Mobile
57
+ * const service = new StorageService({
58
+ * messenger: storageServiceMessenger,
59
+ * storage: filesystemStorageAdapter, // Platform-specific
60
+ * });
61
+ *
62
+ * // Extension
63
+ * const service = new StorageService({
64
+ * messenger: storageServiceMessenger,
65
+ * storage: indexedDBAdapter, // Platform-specific
66
+ * });
67
+ *
68
+ * // Tests (uses in-memory by default)
69
+ * const service = new StorageService({
70
+ * messenger: storageServiceMessenger,
71
+ * // No storage - uses InMemoryStorageAdapter
72
+ * });
73
+ * ```
74
+ */
75
+ export declare class StorageService {
76
+ #private;
77
+ /**
78
+ * The name of the service.
79
+ */
80
+ readonly name: typeof SERVICE_NAME;
81
+ /**
82
+ * Constructs a new StorageService.
83
+ *
84
+ * @param options - The options.
85
+ * @param options.messenger - The messenger suited for this service.
86
+ * @param options.storage - Storage adapter for persisting data.
87
+ * If not provided, uses InMemoryStorageAdapter (data lost on restart).
88
+ */
89
+ constructor({ messenger, storage }: StorageServiceOptions);
90
+ /**
91
+ * Store large data in storage.
92
+ *
93
+ * ⚠️ **Designed for large values (100KB+), not many small ones.**
94
+ * Each storage operation has I/O overhead. For best performance,
95
+ * store one large object rather than many small key-value pairs.
96
+ *
97
+ * @example Good: Store entire cache as one value
98
+ * ```typescript
99
+ * await service.setItem('TokenList', 'cache', { '0x1': [...], '0x38': [...] });
100
+ * ```
101
+ *
102
+ * @example Avoid: Many small values
103
+ * ```typescript
104
+ * // ❌ Don't do this - too many small writes
105
+ * await service.setItem('TokenList', 'cache:0x1', [...]);
106
+ * await service.setItem('TokenList', 'cache:0x38', [...]);
107
+ * ```
108
+ *
109
+ * @param namespace - Controller namespace (e.g., 'SnapController').
110
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
111
+ * @param value - Data to store (should be 100KB+ for optimal use).
112
+ */
113
+ setItem(namespace: string, key: string, value: unknown): Promise<void>;
114
+ /**
115
+ * Retrieve data from storage.
116
+ *
117
+ * Returns `unknown` since there's no schema validation.
118
+ * Callers should validate or cast the result to the expected type.
119
+ *
120
+ * @param namespace - Controller namespace (e.g., 'SnapController').
121
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
122
+ * @returns Parsed data or null if not found. Type is `unknown` - caller must validate.
123
+ */
124
+ getItem(namespace: string, key: string): Promise<unknown>;
125
+ /**
126
+ * Remove data from storage.
127
+ *
128
+ * @param namespace - Controller namespace (e.g., 'SnapController').
129
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
130
+ */
131
+ removeItem(namespace: string, key: string): Promise<void>;
132
+ /**
133
+ * Get all keys for a namespace.
134
+ * Delegates to storage adapter which handles filtering.
135
+ *
136
+ * @param namespace - Controller namespace (e.g., 'SnapController').
137
+ * @returns Array of keys (without prefix) for this namespace.
138
+ */
139
+ getAllKeys(namespace: string): Promise<string[]>;
140
+ /**
141
+ * Clear all data for a namespace.
142
+ *
143
+ * @param namespace - Controller namespace (e.g., 'SnapController').
144
+ */
145
+ clear(namespace: string): Promise<void>;
146
+ }
147
+ //# sourceMappingURL=StorageService.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageService.d.mts","sourceRoot":"","sources":["../src/StorageService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,qBAAqB,EACtB,oBAAgB;AACjB,OAAO,EAAE,YAAY,EAAE,oBAAgB;AAYvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,qBAAa,cAAc;;IACzB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,YAAY,CAAC;IAYnC;;;;;;;OAOG;gBACS,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,qBAAqB;IAoBzD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5E;;;;;;;;;OASG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK/D;;;;;OAKG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D;;;;;;OAMG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAItD;;;;OAIG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}