@metamask-previews/storage-service 1.0.0-preview-e493d3e8 → 1.0.0-preview-f5c5aecd

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.
@@ -0,0 +1,129 @@
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 _InMemoryStorageAdapter_storage;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.InMemoryStorageAdapter = void 0;
16
+ const types_1 = require("./types.cjs");
17
+ /**
18
+ * In-memory storage adapter (default fallback).
19
+ * Implements the {@link StorageAdapter} interface using a Map.
20
+ *
21
+ * ⚠️ **Warning**: Data is NOT persisted - lost on restart.
22
+ *
23
+ * **Suitable for:**
24
+ * - Testing (isolated, no mocking needed)
25
+ * - Development (quick start, zero config)
26
+ * - Temporary/ephemeral data
27
+ *
28
+ * **Not suitable for:**
29
+ * - Production (unless data is truly ephemeral)
30
+ * - Data that needs to persist across restarts
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const adapter = new InMemoryStorageAdapter();
35
+ * await adapter.setItem('key', 'value');
36
+ * const value = await adapter.getItem('key'); // Returns 'value'
37
+ * // After restart: data is lost
38
+ * ```
39
+ */
40
+ class InMemoryStorageAdapter {
41
+ /**
42
+ * Constructs a new InMemoryStorageAdapter.
43
+ */
44
+ constructor() {
45
+ // Explicitly implement StorageAdapter interface
46
+ /**
47
+ * Internal storage map.
48
+ */
49
+ _InMemoryStorageAdapter_storage.set(this, void 0);
50
+ __classPrivateFieldSet(this, _InMemoryStorageAdapter_storage, new Map(), "f");
51
+ }
52
+ /**
53
+ * Retrieve an item from in-memory storage.
54
+ * Deserializes and unwraps the stored data.
55
+ *
56
+ * @param namespace - The controller namespace.
57
+ * @param key - The data key.
58
+ * @returns The unwrapped data, or null if not found.
59
+ */
60
+ async getItem(namespace, key) {
61
+ const fullKey = `${types_1.STORAGE_KEY_PREFIX}${namespace}:${key}`;
62
+ const serialized = __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").get(fullKey);
63
+ if (!serialized) {
64
+ return null;
65
+ }
66
+ try {
67
+ const wrapper = JSON.parse(serialized);
68
+ return wrapper.data;
69
+ }
70
+ catch (error) {
71
+ // istanbul ignore next - defensive error handling for corrupted data
72
+ console.error(`Failed to parse stored data for ${fullKey}:`, error);
73
+ // istanbul ignore next
74
+ return null;
75
+ }
76
+ }
77
+ /**
78
+ * Store an item in in-memory storage.
79
+ * Wraps with metadata and serializes to string.
80
+ *
81
+ * @param namespace - The controller namespace.
82
+ * @param key - The data key.
83
+ * @param value - The value to store (will be wrapped and serialized).
84
+ */
85
+ async setItem(namespace, key, value) {
86
+ const fullKey = `${types_1.STORAGE_KEY_PREFIX}${namespace}:${key}`;
87
+ const wrapper = {
88
+ timestamp: Date.now(),
89
+ data: value,
90
+ };
91
+ __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").set(fullKey, JSON.stringify(wrapper));
92
+ }
93
+ /**
94
+ * Remove an item from in-memory storage.
95
+ *
96
+ * @param namespace - The controller namespace.
97
+ * @param key - The data key.
98
+ */
99
+ async removeItem(namespace, key) {
100
+ const fullKey = `${types_1.STORAGE_KEY_PREFIX}${namespace}:${key}`;
101
+ __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").delete(fullKey);
102
+ }
103
+ /**
104
+ * Get all keys for a namespace.
105
+ * Returns keys without the 'storage:namespace:' prefix.
106
+ *
107
+ * @param namespace - The namespace to get keys for.
108
+ * @returns Array of keys (without prefix) for this namespace.
109
+ */
110
+ async getAllKeys(namespace) {
111
+ const prefix = `${types_1.STORAGE_KEY_PREFIX}${namespace}:`;
112
+ return Array.from(__classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").keys())
113
+ .filter((key) => key.startsWith(prefix))
114
+ .map((key) => key.slice(prefix.length));
115
+ }
116
+ /**
117
+ * Clear all items for a namespace.
118
+ *
119
+ * @param namespace - The namespace to clear.
120
+ */
121
+ async clear(namespace) {
122
+ const prefix = `${types_1.STORAGE_KEY_PREFIX}${namespace}:`;
123
+ const keysToDelete = Array.from(__classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").keys()).filter((key) => key.startsWith(prefix));
124
+ keysToDelete.forEach((key) => __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").delete(key));
125
+ }
126
+ }
127
+ exports.InMemoryStorageAdapter = InMemoryStorageAdapter;
128
+ _InMemoryStorageAdapter_storage = new WeakMap();
129
+ //# sourceMappingURL=InMemoryStorageAdapter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryStorageAdapter.cjs","sourceRoot":"","sources":["../src/InMemoryStorageAdapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,uCAA6C;AAa7C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,sBAAsB;IAOjC;;OAEG;IACH;QATA,gDAAgD;QAChD;;WAEG;QACM,kDAA8B;QAMrC,uBAAA,IAAI,mCAAY,IAAI,GAAG,EAAE,MAAA,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,GAAW;QAC1C,MAAM,OAAO,GAAG,GAAG,0BAAkB,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC;QAC3D,MAAM,UAAU,GAAG,uBAAA,IAAI,uCAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE9C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAsB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC1D,OAAO,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,qEAAqE;YACrE,OAAO,CAAC,KAAK,CAAC,mCAAmC,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC;YACpE,uBAAuB;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,GAAW,EAAE,KAAc;QAC1D,MAAM,OAAO,GAAG,GAAG,0BAAkB,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAsB;YACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,IAAI,EAAE,KAAK;SACZ,CAAC;QACF,uBAAA,IAAI,uCAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,GAAW;QAC7C,MAAM,OAAO,GAAG,GAAG,0BAAkB,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC;QAC3D,uBAAA,IAAI,uCAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,MAAM,MAAM,GAAG,GAAG,0BAAkB,GAAG,SAAS,GAAG,CAAC;QACpD,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,uCAAS,CAAC,IAAI,EAAE,CAAC;aACpC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;aACvC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,MAAM,GAAG,GAAG,0BAAkB,GAAG,SAAS,GAAG,CAAC;QACpD,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,uCAAS,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACnE,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CACvB,CAAC;QACF,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,uBAAA,IAAI,uCAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;CACF;AA/FD,wDA+FC","sourcesContent":["import type { StorageAdapter } from './types';\nimport { STORAGE_KEY_PREFIX } from './types';\n\n/**\n * Wrapper for stored data with metadata.\n * Each adapter can define its own wrapper structure.\n */\ntype StoredDataWrapper<T = unknown> = {\n /** Timestamp when data was stored (milliseconds since epoch). */\n timestamp: number;\n /** The actual data being stored. */\n data: T;\n};\n\n/**\n * In-memory storage adapter (default fallback).\n * Implements the {@link StorageAdapter} interface using a Map.\n *\n * ⚠️ **Warning**: Data is NOT persisted - lost on restart.\n *\n * **Suitable for:**\n * - Testing (isolated, no mocking needed)\n * - Development (quick start, zero config)\n * - Temporary/ephemeral data\n *\n * **Not suitable for:**\n * - Production (unless data is truly ephemeral)\n * - Data that needs to persist across restarts\n *\n * @example\n * ```typescript\n * const adapter = new InMemoryStorageAdapter();\n * await adapter.setItem('key', 'value');\n * const value = await adapter.getItem('key'); // Returns 'value'\n * // After restart: data is lost\n * ```\n */\nexport class InMemoryStorageAdapter implements StorageAdapter {\n // Explicitly implement StorageAdapter interface\n /**\n * Internal storage map.\n */\n readonly #storage: Map<string, string>;\n\n /**\n * Constructs a new InMemoryStorageAdapter.\n */\n constructor() {\n this.#storage = new Map();\n }\n\n /**\n * Retrieve an item from in-memory storage.\n * Deserializes and unwraps the stored data.\n *\n * @param namespace - The controller namespace.\n * @param key - The data key.\n * @returns The unwrapped data, or null if not found.\n */\n async getItem(namespace: string, key: string): Promise<unknown> {\n const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;\n const serialized = this.#storage.get(fullKey);\n\n if (!serialized) {\n return null;\n }\n\n try {\n const wrapper: StoredDataWrapper = JSON.parse(serialized);\n return wrapper.data;\n } catch (error) {\n // istanbul ignore next - defensive error handling for corrupted data\n console.error(`Failed to parse stored data for ${fullKey}:`, error);\n // istanbul ignore next\n return null;\n }\n }\n\n /**\n * Store an item in in-memory storage.\n * Wraps with metadata and serializes to string.\n *\n * @param namespace - The controller namespace.\n * @param key - The data key.\n * @param value - The value to store (will be wrapped and serialized).\n */\n async setItem(namespace: string, key: string, value: unknown): Promise<void> {\n const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;\n const wrapper: StoredDataWrapper = {\n timestamp: Date.now(),\n data: value,\n };\n this.#storage.set(fullKey, JSON.stringify(wrapper));\n }\n\n /**\n * Remove an item from in-memory storage.\n *\n * @param namespace - The controller namespace.\n * @param key - The data key.\n */\n async removeItem(namespace: string, key: string): Promise<void> {\n const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;\n this.#storage.delete(fullKey);\n }\n\n /**\n * Get all keys for a namespace.\n * Returns keys without the 'storage:namespace:' prefix.\n *\n * @param namespace - The namespace to get keys for.\n * @returns Array of keys (without prefix) for this namespace.\n */\n async getAllKeys(namespace: string): Promise<string[]> {\n const prefix = `${STORAGE_KEY_PREFIX}${namespace}:`;\n return Array.from(this.#storage.keys())\n .filter((key) => key.startsWith(prefix))\n .map((key) => key.slice(prefix.length));\n }\n\n /**\n * Clear all items for a namespace.\n *\n * @param namespace - The namespace to clear.\n */\n async clear(namespace: string): Promise<void> {\n const prefix = `${STORAGE_KEY_PREFIX}${namespace}:`;\n const keysToDelete = Array.from(this.#storage.keys()).filter((key) =>\n key.startsWith(prefix),\n );\n keysToDelete.forEach((key) => this.#storage.delete(key));\n }\n}\n"]}
@@ -0,0 +1,71 @@
1
+ import type { StorageAdapter } from "./types.cjs";
2
+ /**
3
+ * In-memory storage adapter (default fallback).
4
+ * Implements the {@link StorageAdapter} interface using a Map.
5
+ *
6
+ * ⚠️ **Warning**: Data is NOT persisted - lost on restart.
7
+ *
8
+ * **Suitable for:**
9
+ * - Testing (isolated, no mocking needed)
10
+ * - Development (quick start, zero config)
11
+ * - Temporary/ephemeral data
12
+ *
13
+ * **Not suitable for:**
14
+ * - Production (unless data is truly ephemeral)
15
+ * - Data that needs to persist across restarts
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const adapter = new InMemoryStorageAdapter();
20
+ * await adapter.setItem('key', 'value');
21
+ * const value = await adapter.getItem('key'); // Returns 'value'
22
+ * // After restart: data is lost
23
+ * ```
24
+ */
25
+ export declare class InMemoryStorageAdapter implements StorageAdapter {
26
+ #private;
27
+ /**
28
+ * Constructs a new InMemoryStorageAdapter.
29
+ */
30
+ constructor();
31
+ /**
32
+ * Retrieve an item from in-memory storage.
33
+ * Deserializes and unwraps the stored data.
34
+ *
35
+ * @param namespace - The controller namespace.
36
+ * @param key - The data key.
37
+ * @returns The unwrapped data, or null if not found.
38
+ */
39
+ getItem(namespace: string, key: string): Promise<unknown>;
40
+ /**
41
+ * Store an item in in-memory storage.
42
+ * Wraps with metadata and serializes to string.
43
+ *
44
+ * @param namespace - The controller namespace.
45
+ * @param key - The data key.
46
+ * @param value - The value to store (will be wrapped and serialized).
47
+ */
48
+ setItem(namespace: string, key: string, value: unknown): Promise<void>;
49
+ /**
50
+ * Remove an item from in-memory storage.
51
+ *
52
+ * @param namespace - The controller namespace.
53
+ * @param key - The data key.
54
+ */
55
+ removeItem(namespace: string, key: string): Promise<void>;
56
+ /**
57
+ * Get all keys for a namespace.
58
+ * Returns keys without the 'storage:namespace:' prefix.
59
+ *
60
+ * @param namespace - The namespace to get keys for.
61
+ * @returns Array of keys (without prefix) for this namespace.
62
+ */
63
+ getAllKeys(namespace: string): Promise<string[]>;
64
+ /**
65
+ * Clear all items for a namespace.
66
+ *
67
+ * @param namespace - The namespace to clear.
68
+ */
69
+ clear(namespace: string): Promise<void>;
70
+ }
71
+ //# sourceMappingURL=InMemoryStorageAdapter.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryStorageAdapter.d.cts","sourceRoot":"","sources":["../src/InMemoryStorageAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAgB;AAc9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,sBAAuB,YAAW,cAAc;;IAO3D;;OAEG;;IAKH;;;;;;;OAOG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB/D;;;;;;;OAOG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5E;;;;;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;IAOtD;;;;OAIG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO9C"}
@@ -0,0 +1,71 @@
1
+ import type { StorageAdapter } from "./types.mjs";
2
+ /**
3
+ * In-memory storage adapter (default fallback).
4
+ * Implements the {@link StorageAdapter} interface using a Map.
5
+ *
6
+ * ⚠️ **Warning**: Data is NOT persisted - lost on restart.
7
+ *
8
+ * **Suitable for:**
9
+ * - Testing (isolated, no mocking needed)
10
+ * - Development (quick start, zero config)
11
+ * - Temporary/ephemeral data
12
+ *
13
+ * **Not suitable for:**
14
+ * - Production (unless data is truly ephemeral)
15
+ * - Data that needs to persist across restarts
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const adapter = new InMemoryStorageAdapter();
20
+ * await adapter.setItem('key', 'value');
21
+ * const value = await adapter.getItem('key'); // Returns 'value'
22
+ * // After restart: data is lost
23
+ * ```
24
+ */
25
+ export declare class InMemoryStorageAdapter implements StorageAdapter {
26
+ #private;
27
+ /**
28
+ * Constructs a new InMemoryStorageAdapter.
29
+ */
30
+ constructor();
31
+ /**
32
+ * Retrieve an item from in-memory storage.
33
+ * Deserializes and unwraps the stored data.
34
+ *
35
+ * @param namespace - The controller namespace.
36
+ * @param key - The data key.
37
+ * @returns The unwrapped data, or null if not found.
38
+ */
39
+ getItem(namespace: string, key: string): Promise<unknown>;
40
+ /**
41
+ * Store an item in in-memory storage.
42
+ * Wraps with metadata and serializes to string.
43
+ *
44
+ * @param namespace - The controller namespace.
45
+ * @param key - The data key.
46
+ * @param value - The value to store (will be wrapped and serialized).
47
+ */
48
+ setItem(namespace: string, key: string, value: unknown): Promise<void>;
49
+ /**
50
+ * Remove an item from in-memory storage.
51
+ *
52
+ * @param namespace - The controller namespace.
53
+ * @param key - The data key.
54
+ */
55
+ removeItem(namespace: string, key: string): Promise<void>;
56
+ /**
57
+ * Get all keys for a namespace.
58
+ * Returns keys without the 'storage:namespace:' prefix.
59
+ *
60
+ * @param namespace - The namespace to get keys for.
61
+ * @returns Array of keys (without prefix) for this namespace.
62
+ */
63
+ getAllKeys(namespace: string): Promise<string[]>;
64
+ /**
65
+ * Clear all items for a namespace.
66
+ *
67
+ * @param namespace - The namespace to clear.
68
+ */
69
+ clear(namespace: string): Promise<void>;
70
+ }
71
+ //# sourceMappingURL=InMemoryStorageAdapter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryStorageAdapter.d.mts","sourceRoot":"","sources":["../src/InMemoryStorageAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAgB;AAc9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,sBAAuB,YAAW,cAAc;;IAO3D;;OAEG;;IAKH;;;;;;;OAOG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB/D;;;;;;;OAOG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5E;;;;;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;IAOtD;;;;OAIG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO9C"}
@@ -0,0 +1,125 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ 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");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ 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");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _InMemoryStorageAdapter_storage;
13
+ import { STORAGE_KEY_PREFIX } from "./types.mjs";
14
+ /**
15
+ * In-memory storage adapter (default fallback).
16
+ * Implements the {@link StorageAdapter} interface using a Map.
17
+ *
18
+ * ⚠️ **Warning**: Data is NOT persisted - lost on restart.
19
+ *
20
+ * **Suitable for:**
21
+ * - Testing (isolated, no mocking needed)
22
+ * - Development (quick start, zero config)
23
+ * - Temporary/ephemeral data
24
+ *
25
+ * **Not suitable for:**
26
+ * - Production (unless data is truly ephemeral)
27
+ * - Data that needs to persist across restarts
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const adapter = new InMemoryStorageAdapter();
32
+ * await adapter.setItem('key', 'value');
33
+ * const value = await adapter.getItem('key'); // Returns 'value'
34
+ * // After restart: data is lost
35
+ * ```
36
+ */
37
+ export class InMemoryStorageAdapter {
38
+ /**
39
+ * Constructs a new InMemoryStorageAdapter.
40
+ */
41
+ constructor() {
42
+ // Explicitly implement StorageAdapter interface
43
+ /**
44
+ * Internal storage map.
45
+ */
46
+ _InMemoryStorageAdapter_storage.set(this, void 0);
47
+ __classPrivateFieldSet(this, _InMemoryStorageAdapter_storage, new Map(), "f");
48
+ }
49
+ /**
50
+ * Retrieve an item from in-memory storage.
51
+ * Deserializes and unwraps the stored data.
52
+ *
53
+ * @param namespace - The controller namespace.
54
+ * @param key - The data key.
55
+ * @returns The unwrapped data, or null if not found.
56
+ */
57
+ async getItem(namespace, key) {
58
+ const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;
59
+ const serialized = __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").get(fullKey);
60
+ if (!serialized) {
61
+ return null;
62
+ }
63
+ try {
64
+ const wrapper = JSON.parse(serialized);
65
+ return wrapper.data;
66
+ }
67
+ catch (error) {
68
+ // istanbul ignore next - defensive error handling for corrupted data
69
+ console.error(`Failed to parse stored data for ${fullKey}:`, error);
70
+ // istanbul ignore next
71
+ return null;
72
+ }
73
+ }
74
+ /**
75
+ * Store an item in in-memory storage.
76
+ * Wraps with metadata and serializes to string.
77
+ *
78
+ * @param namespace - The controller namespace.
79
+ * @param key - The data key.
80
+ * @param value - The value to store (will be wrapped and serialized).
81
+ */
82
+ async setItem(namespace, key, value) {
83
+ const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;
84
+ const wrapper = {
85
+ timestamp: Date.now(),
86
+ data: value,
87
+ };
88
+ __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").set(fullKey, JSON.stringify(wrapper));
89
+ }
90
+ /**
91
+ * Remove an item from in-memory storage.
92
+ *
93
+ * @param namespace - The controller namespace.
94
+ * @param key - The data key.
95
+ */
96
+ async removeItem(namespace, key) {
97
+ const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;
98
+ __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").delete(fullKey);
99
+ }
100
+ /**
101
+ * Get all keys for a namespace.
102
+ * Returns keys without the 'storage:namespace:' prefix.
103
+ *
104
+ * @param namespace - The namespace to get keys for.
105
+ * @returns Array of keys (without prefix) for this namespace.
106
+ */
107
+ async getAllKeys(namespace) {
108
+ const prefix = `${STORAGE_KEY_PREFIX}${namespace}:`;
109
+ return Array.from(__classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").keys())
110
+ .filter((key) => key.startsWith(prefix))
111
+ .map((key) => key.slice(prefix.length));
112
+ }
113
+ /**
114
+ * Clear all items for a namespace.
115
+ *
116
+ * @param namespace - The namespace to clear.
117
+ */
118
+ async clear(namespace) {
119
+ const prefix = `${STORAGE_KEY_PREFIX}${namespace}:`;
120
+ const keysToDelete = Array.from(__classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").keys()).filter((key) => key.startsWith(prefix));
121
+ keysToDelete.forEach((key) => __classPrivateFieldGet(this, _InMemoryStorageAdapter_storage, "f").delete(key));
122
+ }
123
+ }
124
+ _InMemoryStorageAdapter_storage = new WeakMap();
125
+ //# sourceMappingURL=InMemoryStorageAdapter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryStorageAdapter.mjs","sourceRoot":"","sources":["../src/InMemoryStorageAdapter.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,OAAO,EAAE,kBAAkB,EAAE,oBAAgB;AAa7C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,sBAAsB;IAOjC;;OAEG;IACH;QATA,gDAAgD;QAChD;;WAEG;QACM,kDAA8B;QAMrC,uBAAA,IAAI,mCAAY,IAAI,GAAG,EAAE,MAAA,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,GAAW;QAC1C,MAAM,OAAO,GAAG,GAAG,kBAAkB,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC;QAC3D,MAAM,UAAU,GAAG,uBAAA,IAAI,uCAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE9C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAsB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC1D,OAAO,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,qEAAqE;YACrE,OAAO,CAAC,KAAK,CAAC,mCAAmC,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC;YACpE,uBAAuB;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,GAAW,EAAE,KAAc;QAC1D,MAAM,OAAO,GAAG,GAAG,kBAAkB,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAsB;YACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,IAAI,EAAE,KAAK;SACZ,CAAC;QACF,uBAAA,IAAI,uCAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,GAAW;QAC7C,MAAM,OAAO,GAAG,GAAG,kBAAkB,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC;QAC3D,uBAAA,IAAI,uCAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,MAAM,MAAM,GAAG,GAAG,kBAAkB,GAAG,SAAS,GAAG,CAAC;QACpD,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,uCAAS,CAAC,IAAI,EAAE,CAAC;aACpC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;aACvC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,MAAM,GAAG,GAAG,kBAAkB,GAAG,SAAS,GAAG,CAAC;QACpD,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,uCAAS,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACnE,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CACvB,CAAC;QACF,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,uBAAA,IAAI,uCAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;CACF","sourcesContent":["import type { StorageAdapter } from './types';\nimport { STORAGE_KEY_PREFIX } from './types';\n\n/**\n * Wrapper for stored data with metadata.\n * Each adapter can define its own wrapper structure.\n */\ntype StoredDataWrapper<T = unknown> = {\n /** Timestamp when data was stored (milliseconds since epoch). */\n timestamp: number;\n /** The actual data being stored. */\n data: T;\n};\n\n/**\n * In-memory storage adapter (default fallback).\n * Implements the {@link StorageAdapter} interface using a Map.\n *\n * ⚠️ **Warning**: Data is NOT persisted - lost on restart.\n *\n * **Suitable for:**\n * - Testing (isolated, no mocking needed)\n * - Development (quick start, zero config)\n * - Temporary/ephemeral data\n *\n * **Not suitable for:**\n * - Production (unless data is truly ephemeral)\n * - Data that needs to persist across restarts\n *\n * @example\n * ```typescript\n * const adapter = new InMemoryStorageAdapter();\n * await adapter.setItem('key', 'value');\n * const value = await adapter.getItem('key'); // Returns 'value'\n * // After restart: data is lost\n * ```\n */\nexport class InMemoryStorageAdapter implements StorageAdapter {\n // Explicitly implement StorageAdapter interface\n /**\n * Internal storage map.\n */\n readonly #storage: Map<string, string>;\n\n /**\n * Constructs a new InMemoryStorageAdapter.\n */\n constructor() {\n this.#storage = new Map();\n }\n\n /**\n * Retrieve an item from in-memory storage.\n * Deserializes and unwraps the stored data.\n *\n * @param namespace - The controller namespace.\n * @param key - The data key.\n * @returns The unwrapped data, or null if not found.\n */\n async getItem(namespace: string, key: string): Promise<unknown> {\n const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;\n const serialized = this.#storage.get(fullKey);\n\n if (!serialized) {\n return null;\n }\n\n try {\n const wrapper: StoredDataWrapper = JSON.parse(serialized);\n return wrapper.data;\n } catch (error) {\n // istanbul ignore next - defensive error handling for corrupted data\n console.error(`Failed to parse stored data for ${fullKey}:`, error);\n // istanbul ignore next\n return null;\n }\n }\n\n /**\n * Store an item in in-memory storage.\n * Wraps with metadata and serializes to string.\n *\n * @param namespace - The controller namespace.\n * @param key - The data key.\n * @param value - The value to store (will be wrapped and serialized).\n */\n async setItem(namespace: string, key: string, value: unknown): Promise<void> {\n const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;\n const wrapper: StoredDataWrapper = {\n timestamp: Date.now(),\n data: value,\n };\n this.#storage.set(fullKey, JSON.stringify(wrapper));\n }\n\n /**\n * Remove an item from in-memory storage.\n *\n * @param namespace - The controller namespace.\n * @param key - The data key.\n */\n async removeItem(namespace: string, key: string): Promise<void> {\n const fullKey = `${STORAGE_KEY_PREFIX}${namespace}:${key}`;\n this.#storage.delete(fullKey);\n }\n\n /**\n * Get all keys for a namespace.\n * Returns keys without the 'storage:namespace:' prefix.\n *\n * @param namespace - The namespace to get keys for.\n * @returns Array of keys (without prefix) for this namespace.\n */\n async getAllKeys(namespace: string): Promise<string[]> {\n const prefix = `${STORAGE_KEY_PREFIX}${namespace}:`;\n return Array.from(this.#storage.keys())\n .filter((key) => key.startsWith(prefix))\n .map((key) => key.slice(prefix.length));\n }\n\n /**\n * Clear all items for a namespace.\n *\n * @param namespace - The namespace to clear.\n */\n async clear(namespace: string): Promise<void> {\n const prefix = `${STORAGE_KEY_PREFIX}${namespace}:`;\n const keysToDelete = Array.from(this.#storage.keys()).filter((key) =>\n key.startsWith(prefix),\n );\n keysToDelete.forEach((key) => this.#storage.delete(key));\n }\n}\n"]}
@@ -0,0 +1,187 @@
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
+ /**
19
+ * StorageService provides a platform-agnostic way for controllers to store
20
+ * large, infrequently accessed data outside of memory/Redux state.
21
+ *
22
+ * **Use cases:**
23
+ * - Snap source code (6+ MB that's rarely accessed)
24
+ * - Token metadata caches (4+ MB of cached data)
25
+ * - Large cached responses from APIs
26
+ * - Any data > 100 KB that's not frequently accessed
27
+ *
28
+ * **Benefits:**
29
+ * - Reduces memory usage (data stays on disk)
30
+ * - Faster Redux persist (less data to serialize)
31
+ * - Faster app startup (less data to parse)
32
+ * - Lazy loading (data loaded only when needed)
33
+ *
34
+ * **Platform Support:**
35
+ * - Mobile: FilesystemStorage adapter
36
+ * - Extension: IndexedDB adapter
37
+ * - Tests/Dev: InMemoryStorageAdapter (default)
38
+ *
39
+ * @example Using the service via messenger
40
+ *
41
+ * ```typescript
42
+ * // In a controller
43
+ * type AllowedActions =
44
+ * | StorageServiceSetItemAction
45
+ * | StorageServiceGetItemAction;
46
+ *
47
+ * class SnapController extends BaseController {
48
+ * async storeSnapSourceCode(snapId: string, sourceCode: string) {
49
+ * await this.messenger.call(
50
+ * 'StorageService:setItem',
51
+ * 'SnapController',
52
+ * `${snapId}:sourceCode`,
53
+ * sourceCode,
54
+ * );
55
+ * }
56
+ *
57
+ * async getSnapSourceCode(snapId: string): Promise<string | null> {
58
+ * return await this.messenger.call(
59
+ * 'StorageService:getItem',
60
+ * 'SnapController',
61
+ * `${snapId}:sourceCode`,
62
+ * );
63
+ * }
64
+ * }
65
+ * ```
66
+ *
67
+ * @example Initializing in a client
68
+ *
69
+ * ```typescript
70
+ * // Mobile
71
+ * const service = new StorageService({
72
+ * messenger: storageServiceMessenger,
73
+ * storage: filesystemStorageAdapter, // Platform-specific
74
+ * });
75
+ *
76
+ * // Extension
77
+ * const service = new StorageService({
78
+ * messenger: storageServiceMessenger,
79
+ * storage: indexedDBAdapter, // Platform-specific
80
+ * });
81
+ *
82
+ * // Tests (uses in-memory by default)
83
+ * const service = new StorageService({
84
+ * messenger: storageServiceMessenger,
85
+ * // No storage - uses InMemoryStorageAdapter
86
+ * });
87
+ * ```
88
+ */
89
+ class StorageService {
90
+ /**
91
+ * Constructs a new StorageService.
92
+ *
93
+ * @param options - The options.
94
+ * @param options.messenger - The messenger suited for this service.
95
+ * @param options.storage - Storage adapter for persisting data.
96
+ * If not provided, uses InMemoryStorageAdapter (data lost on restart).
97
+ */
98
+ constructor({ messenger, storage }) {
99
+ /**
100
+ * The messenger suited for this service.
101
+ */
102
+ _StorageService_messenger.set(this, void 0);
103
+ /**
104
+ * The storage adapter for persisting data.
105
+ */
106
+ _StorageService_storage.set(this, void 0);
107
+ this.name = types_1.SERVICE_NAME;
108
+ __classPrivateFieldSet(this, _StorageService_messenger, messenger, "f");
109
+ __classPrivateFieldSet(this, _StorageService_storage, storage ?? new InMemoryStorageAdapter_1.InMemoryStorageAdapter(), "f");
110
+ // Warn if using in-memory storage (data won't persist)
111
+ if (!storage) {
112
+ console.warn(`${types_1.SERVICE_NAME}: No storage adapter provided. Using in-memory storage. ` +
113
+ 'Data will be lost on restart. Provide a storage adapter for persistence.');
114
+ }
115
+ // Register messenger actions
116
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").registerActionHandler(`${types_1.SERVICE_NAME}:setItem`, this.setItem.bind(this));
117
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").registerActionHandler(`${types_1.SERVICE_NAME}:getItem`, this.getItem.bind(this));
118
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").registerActionHandler(`${types_1.SERVICE_NAME}:removeItem`, this.removeItem.bind(this));
119
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").registerActionHandler(`${types_1.SERVICE_NAME}:getAllKeys`, this.getAllKeys.bind(this));
120
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").registerActionHandler(`${types_1.SERVICE_NAME}:clear`, this.clear.bind(this));
121
+ }
122
+ /**
123
+ * Store data in storage.
124
+ *
125
+ * @param namespace - Controller namespace (e.g., 'SnapController').
126
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
127
+ * @param value - Data to store (will be JSON stringified).
128
+ * @template T - The type of the value being stored.
129
+ */
130
+ async setItem(namespace, key, value) {
131
+ // Adapter handles serialization and wrapping with metadata
132
+ await __classPrivateFieldGet(this, _StorageService_storage, "f").setItem(namespace, key, value);
133
+ // Publish event so other controllers can react to changes
134
+ // Event type: StorageService:itemSet:namespace
135
+ // Payload: [value, key]
136
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").publish(`${types_1.SERVICE_NAME}:itemSet:${namespace}`, value, key);
137
+ }
138
+ /**
139
+ * Retrieve data from storage.
140
+ *
141
+ * @param namespace - Controller namespace (e.g., 'SnapController').
142
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
143
+ * @returns Parsed data or null if not found.
144
+ * @template T - The type of the value being retrieved.
145
+ */
146
+ async getItem(namespace, key) {
147
+ // Adapter handles deserialization and unwrapping
148
+ const result = await __classPrivateFieldGet(this, _StorageService_storage, "f").getItem(namespace, key);
149
+ return result;
150
+ }
151
+ /**
152
+ * Remove data from storage.
153
+ *
154
+ * @param namespace - Controller namespace (e.g., 'SnapController').
155
+ * @param key - Storage key (e.g., 'npm:@metamask/example-snap:sourceCode').
156
+ */
157
+ async removeItem(namespace, key) {
158
+ // Adapter builds full storage key (e.g., mobile: 'storageService:namespace:key')
159
+ await __classPrivateFieldGet(this, _StorageService_storage, "f").removeItem(namespace, key);
160
+ // Publish event so other controllers can react to removal
161
+ // Event type: StorageService:itemRemoved:namespace
162
+ // Payload: [key]
163
+ __classPrivateFieldGet(this, _StorageService_messenger, "f").publish(`${types_1.SERVICE_NAME}:itemRemoved:${namespace}`, key);
164
+ }
165
+ /**
166
+ * Get all keys for a namespace.
167
+ * Delegates to storage adapter which handles filtering.
168
+ *
169
+ * @param namespace - Controller namespace (e.g., 'SnapController').
170
+ * @returns Array of keys (without prefix) for this namespace.
171
+ */
172
+ async getAllKeys(namespace) {
173
+ return await __classPrivateFieldGet(this, _StorageService_storage, "f").getAllKeys(namespace);
174
+ }
175
+ /**
176
+ * Clear all data for a namespace.
177
+ * Delegates to storage adapter which handles clearing.
178
+ *
179
+ * @param namespace - Controller namespace (e.g., 'SnapController').
180
+ */
181
+ async clear(namespace) {
182
+ await __classPrivateFieldGet(this, _StorageService_storage, "f").clear(namespace);
183
+ }
184
+ }
185
+ exports.StorageService = StorageService;
186
+ _StorageService_messenger = new WeakMap(), _StorageService_storage = new WeakMap();
187
+ //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;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,qBAAqB,CACnC,GAAG,oBAAY,UAAU,EACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CACxB,CAAC;QACF,uBAAA,IAAI,iCAAW,CAAC,qBAAqB,CACnC,GAAG,oBAAY,UAAU,EACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CACxB,CAAC;QACF,uBAAA,IAAI,iCAAW,CAAC,qBAAqB,CACnC,GAAG,oBAAY,aAAa,EAC5B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3B,CAAC;QACF,uBAAA,IAAI,iCAAW,CAAC,qBAAqB,CACnC,GAAG,oBAAY,aAAa,EAC5B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3B,CAAC;QACF,uBAAA,IAAI,iCAAW,CAAC,qBAAqB,CACnC,GAAG,oBAAY,QAAQ,EACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CACtB,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAI,SAAiB,EAAE,GAAW,EAAE,KAAQ;QACvD,2DAA2D;QAC3D,MAAM,uBAAA,IAAI,+BAAS,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,KAAc,CAAC,CAAC;QAE5D,0DAA0D;QAC1D,+CAA+C;QAC/C,wBAAwB;QACxB,uBAAA,IAAI,iCAAW,CAAC,OAAO,CACrB,GAAG,oBAAY,YAAY,SAAS,EAAgD,EACpF,KAAK,EACL,GAAG,CACJ,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAI,SAAiB,EAAE,GAAW;QAC7C,iDAAiD;QACjD,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,+BAAS,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAC3D,OAAO,MAAkB,CAAC;IAC5B,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;QAE/C,0DAA0D;QAC1D,mDAAmD;QACnD,iBAAiB;QACjB,uBAAA,IAAI,iCAAW,CAAC,OAAO,CACrB,GAAG,oBAAY,gBAAgB,SAAS,EAAoD,EAC5F,GAAG,CACJ,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,OAAO,MAAM,uBAAA,IAAI,+BAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,uBAAA,IAAI,+BAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;CACF;AAvID,wCAuIC","sourcesContent":["import { InMemoryStorageAdapter } from './InMemoryStorageAdapter';\nimport type {\n StorageAdapter,\n StorageServiceMessenger,\n StorageServiceOptions,\n} from './types';\nimport { SERVICE_NAME } from './types';\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 * return await this.messenger.call(\n * 'StorageService:getItem',\n * 'SnapController',\n * `${snapId}:sourceCode`,\n * );\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.registerActionHandler(\n `${SERVICE_NAME}:setItem`,\n this.setItem.bind(this),\n );\n this.#messenger.registerActionHandler(\n `${SERVICE_NAME}:getItem`,\n this.getItem.bind(this),\n );\n this.#messenger.registerActionHandler(\n `${SERVICE_NAME}:removeItem`,\n this.removeItem.bind(this),\n );\n this.#messenger.registerActionHandler(\n `${SERVICE_NAME}:getAllKeys`,\n this.getAllKeys.bind(this),\n );\n this.#messenger.registerActionHandler(\n `${SERVICE_NAME}:clear`,\n this.clear.bind(this),\n );\n }\n\n /**\n * Store data in storage.\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 (will be JSON stringified).\n * @template T - The type of the value being stored.\n */\n async setItem<T>(namespace: string, key: string, value: T): Promise<void> {\n // Adapter handles serialization and wrapping with metadata\n await this.#storage.setItem(namespace, key, value as never);\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 `${typeof SERVICE_NAME}:itemSet:${string}`,\n value,\n key,\n );\n }\n\n /**\n * Retrieve 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 * @returns Parsed data or null if not found.\n * @template T - The type of the value being retrieved.\n */\n async getItem<T>(namespace: string, key: string): Promise<T | null> {\n // Adapter handles deserialization and unwrapping\n const result = await this.#storage.getItem(namespace, key);\n return result as T | null;\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 // Publish event so other controllers can react to removal\n // Event type: StorageService:itemRemoved:namespace\n // Payload: [key]\n this.#messenger.publish(\n `${SERVICE_NAME}:itemRemoved:${namespace}` as `${typeof SERVICE_NAME}:itemRemoved:${string}`,\n key,\n );\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 * Delegates to storage adapter which handles clearing.\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"]}