@metamask-previews/storage-service 0.0.0-preview-d6fe4594
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/LICENSE +6 -0
- package/LICENSE.APACHE2 +201 -0
- package/LICENSE.MIT +21 -0
- package/README.md +118 -0
- package/dist/InMemoryStorageAdapter.cjs +124 -0
- package/dist/InMemoryStorageAdapter.cjs.map +1 -0
- package/dist/InMemoryStorageAdapter.d.cts +72 -0
- package/dist/InMemoryStorageAdapter.d.cts.map +1 -0
- package/dist/InMemoryStorageAdapter.d.mts +72 -0
- package/dist/InMemoryStorageAdapter.d.mts.map +1 -0
- package/dist/InMemoryStorageAdapter.mjs +120 -0
- package/dist/InMemoryStorageAdapter.mjs.map +1 -0
- package/dist/StorageService-method-action-types.cjs +7 -0
- package/dist/StorageService-method-action-types.cjs.map +1 -0
- package/dist/StorageService-method-action-types.d.cts +78 -0
- package/dist/StorageService-method-action-types.d.cts.map +1 -0
- package/dist/StorageService-method-action-types.d.mts +78 -0
- package/dist/StorageService-method-action-types.d.mts.map +1 -0
- package/dist/StorageService-method-action-types.mjs +6 -0
- package/dist/StorageService-method-action-types.mjs.map +1 -0
- package/dist/StorageService.cjs +200 -0
- package/dist/StorageService.cjs.map +1 -0
- package/dist/StorageService.d.cts +145 -0
- package/dist/StorageService.d.cts.map +1 -0
- package/dist/StorageService.d.mts +145 -0
- package/dist/StorageService.d.mts.map +1 -0
- package/dist/StorageService.mjs +196 -0
- package/dist/StorageService.mjs.map +1 -0
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types.cjs +12 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +148 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +148 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +9 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +72 -0
package/dist/types.d.cts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Messenger } from "@metamask/messenger";
|
|
2
|
+
import type { Json } from "@metamask/utils";
|
|
3
|
+
import type { StorageServiceMethodActions } from "./StorageService-method-action-types.cjs";
|
|
4
|
+
/**
|
|
5
|
+
* Platform-agnostic storage adapter interface.
|
|
6
|
+
* Each client (mobile, extension) implements this interface
|
|
7
|
+
* with their preferred storage mechanism.
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ **Designed for large, infrequently accessed data (100KB+)**
|
|
10
|
+
*
|
|
11
|
+
* ✅ **Use for:**
|
|
12
|
+
* - Snap source code (~6 MB per snap)
|
|
13
|
+
* - Token metadata caches (~4 MB)
|
|
14
|
+
* - Large API response caches
|
|
15
|
+
*
|
|
16
|
+
* ❌ **Avoid for:**
|
|
17
|
+
* - Small values (< 10 KB) - use controller state instead
|
|
18
|
+
* - Frequently accessed data - use controller state instead
|
|
19
|
+
* - Many small key-value pairs - use a single large object instead
|
|
20
|
+
*
|
|
21
|
+
* @example Mobile implementation using FilesystemStorage
|
|
22
|
+
* @example Extension implementation using IndexedDB
|
|
23
|
+
* @example Tests using InMemoryStorageAdapter
|
|
24
|
+
*/
|
|
25
|
+
export type StorageAdapter = {
|
|
26
|
+
/**
|
|
27
|
+
* Retrieve an item from storage.
|
|
28
|
+
* Adapter is responsible for building the full storage key.
|
|
29
|
+
*
|
|
30
|
+
* @param namespace - The controller namespace (e.g., 'SnapController').
|
|
31
|
+
* @param key - The data key (e.g., 'snap-id:sourceCode').
|
|
32
|
+
* @returns The JSON value, or null if not found.
|
|
33
|
+
*/
|
|
34
|
+
getItem(namespace: string, key: string): Promise<Json | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Store a large JSON value in storage.
|
|
37
|
+
*
|
|
38
|
+
* ⚠️ **Store large values, not many small ones.**
|
|
39
|
+
* Each storage operation has I/O overhead. For best performance:
|
|
40
|
+
* - Store one large object rather than many small key-value pairs
|
|
41
|
+
* - Minimum recommended size: 100 KB per value
|
|
42
|
+
*
|
|
43
|
+
* Adapter is responsible for:
|
|
44
|
+
* - Building the full storage key
|
|
45
|
+
* - Serializing to string (JSON.stringify)
|
|
46
|
+
*
|
|
47
|
+
* @param namespace - The controller namespace (e.g., 'SnapController').
|
|
48
|
+
* @param key - The data key (e.g., 'snap-id:sourceCode').
|
|
49
|
+
* @param value - The JSON value to store.
|
|
50
|
+
*/
|
|
51
|
+
setItem(namespace: string, key: string, value: Json): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Remove an item from storage.
|
|
54
|
+
* Adapter is responsible for building the full storage key.
|
|
55
|
+
*
|
|
56
|
+
* @param namespace - The controller namespace (e.g., 'SnapController').
|
|
57
|
+
* @param key - The data key (e.g., 'snap-id:sourceCode').
|
|
58
|
+
*/
|
|
59
|
+
removeItem(namespace: string, key: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Get all keys for a specific namespace.
|
|
62
|
+
* Should return keys without the 'storage:namespace:' prefix.
|
|
63
|
+
*
|
|
64
|
+
* Adapter is responsible for:
|
|
65
|
+
* - Filtering keys by prefix: 'storage:{namespace}:'
|
|
66
|
+
* - Stripping the prefix from returned keys
|
|
67
|
+
* - Returning only the key portion after the prefix
|
|
68
|
+
*
|
|
69
|
+
* @param namespace - The namespace to get keys for (e.g., 'SnapController').
|
|
70
|
+
* @returns Array of keys without prefix (e.g., ['snap1:sourceCode', 'snap2:sourceCode']).
|
|
71
|
+
*/
|
|
72
|
+
getAllKeys(namespace: string): Promise<string[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Clear all items for a specific namespace.
|
|
75
|
+
*
|
|
76
|
+
* Adapter is responsible for:
|
|
77
|
+
* - Finding all keys with prefix: 'storageService:{namespace}:'
|
|
78
|
+
* - Removing all matching keys
|
|
79
|
+
*
|
|
80
|
+
* @param namespace - The namespace to clear (e.g., 'SnapController').
|
|
81
|
+
*/
|
|
82
|
+
clear(namespace: string): Promise<void>;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Options for constructing a {@link StorageService}.
|
|
86
|
+
*/
|
|
87
|
+
export type StorageServiceOptions = {
|
|
88
|
+
/**
|
|
89
|
+
* The messenger suited for this service.
|
|
90
|
+
*/
|
|
91
|
+
messenger: StorageServiceMessenger;
|
|
92
|
+
/**
|
|
93
|
+
* Storage adapter for persisting data.
|
|
94
|
+
* If not provided, uses in-memory storage (data lost on restart).
|
|
95
|
+
* Production clients MUST provide a persistent storage adapter.
|
|
96
|
+
*/
|
|
97
|
+
storage?: StorageAdapter;
|
|
98
|
+
};
|
|
99
|
+
export declare const SERVICE_NAME = "StorageService";
|
|
100
|
+
/**
|
|
101
|
+
* Storage key prefix for all keys managed by StorageService.
|
|
102
|
+
* Keys are formatted as: {STORAGE_KEY_PREFIX}{namespace}:{key}
|
|
103
|
+
* Example: 'storageService:SnapController:snap-id:sourceCode'
|
|
104
|
+
*/
|
|
105
|
+
export declare const STORAGE_KEY_PREFIX = "storageService:";
|
|
106
|
+
/**
|
|
107
|
+
* All actions that {@link StorageService} exposes to other consumers.
|
|
108
|
+
* Action types are auto-generated from the service methods.
|
|
109
|
+
*/
|
|
110
|
+
export type StorageServiceActions = StorageServiceMethodActions;
|
|
111
|
+
/**
|
|
112
|
+
* Event published when a storage item is set.
|
|
113
|
+
* Event type includes namespace only, key passed in payload.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* Subscribe to all changes in TokenListController:
|
|
117
|
+
* messenger.subscribe('StorageService:itemSet:TokenListController', (key, value) => {
|
|
118
|
+
* // key = 'cache:0x1', 'cache:0x38', etc.
|
|
119
|
+
* // value = the data that was set
|
|
120
|
+
* if (key.startsWith('cache:')) {
|
|
121
|
+
* const chainId = key.replace('cache:', '');
|
|
122
|
+
* // React to cache change for specific chain
|
|
123
|
+
* }
|
|
124
|
+
* });
|
|
125
|
+
*/
|
|
126
|
+
export type StorageServiceItemSetEvent = {
|
|
127
|
+
type: `${typeof SERVICE_NAME}:itemSet:${string}`;
|
|
128
|
+
payload: [key: string, value: Json];
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* All events that {@link StorageService} publishes.
|
|
132
|
+
*/
|
|
133
|
+
export type StorageServiceEvents = StorageServiceItemSetEvent;
|
|
134
|
+
/**
|
|
135
|
+
* Actions from other messengers that {@link StorageService} calls.
|
|
136
|
+
*/
|
|
137
|
+
type AllowedActions = never;
|
|
138
|
+
/**
|
|
139
|
+
* Events from other messengers that {@link StorageService} subscribes to.
|
|
140
|
+
*/
|
|
141
|
+
type AllowedEvents = never;
|
|
142
|
+
/**
|
|
143
|
+
* The messenger restricted to actions and events that
|
|
144
|
+
* {@link StorageService} needs to access.
|
|
145
|
+
*/
|
|
146
|
+
export type StorageServiceMessenger = Messenger<typeof SERVICE_NAME, StorageServiceActions | AllowedActions, StorageServiceEvents | AllowedEvents>;
|
|
147
|
+
export {};
|
|
148
|
+
//# sourceMappingURL=types.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAE5C,OAAO,KAAK,EAAE,2BAA2B,EAAE,iDAA6C;AAExF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE9D;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;OAMG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjD;;;;;;;;OAQG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,SAAS,EAAE,uBAAuB,CAAC;IAEnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC;AAGF,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAEhE;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,GAAG,OAAO,YAAY,YAAY,MAAM,EAAE,CAAC;IACjD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE9D;;GAEG;AACH,KAAK,cAAc,GAAG,KAAK,CAAC;AAE5B;;GAEG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,CAC7C,OAAO,YAAY,EACnB,qBAAqB,GAAG,cAAc,EACtC,oBAAoB,GAAG,aAAa,CACrC,CAAC"}
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Messenger } from "@metamask/messenger";
|
|
2
|
+
import type { Json } from "@metamask/utils";
|
|
3
|
+
import type { StorageServiceMethodActions } from "./StorageService-method-action-types.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Platform-agnostic storage adapter interface.
|
|
6
|
+
* Each client (mobile, extension) implements this interface
|
|
7
|
+
* with their preferred storage mechanism.
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ **Designed for large, infrequently accessed data (100KB+)**
|
|
10
|
+
*
|
|
11
|
+
* ✅ **Use for:**
|
|
12
|
+
* - Snap source code (~6 MB per snap)
|
|
13
|
+
* - Token metadata caches (~4 MB)
|
|
14
|
+
* - Large API response caches
|
|
15
|
+
*
|
|
16
|
+
* ❌ **Avoid for:**
|
|
17
|
+
* - Small values (< 10 KB) - use controller state instead
|
|
18
|
+
* - Frequently accessed data - use controller state instead
|
|
19
|
+
* - Many small key-value pairs - use a single large object instead
|
|
20
|
+
*
|
|
21
|
+
* @example Mobile implementation using FilesystemStorage
|
|
22
|
+
* @example Extension implementation using IndexedDB
|
|
23
|
+
* @example Tests using InMemoryStorageAdapter
|
|
24
|
+
*/
|
|
25
|
+
export type StorageAdapter = {
|
|
26
|
+
/**
|
|
27
|
+
* Retrieve an item from storage.
|
|
28
|
+
* Adapter is responsible for building the full storage key.
|
|
29
|
+
*
|
|
30
|
+
* @param namespace - The controller namespace (e.g., 'SnapController').
|
|
31
|
+
* @param key - The data key (e.g., 'snap-id:sourceCode').
|
|
32
|
+
* @returns The JSON value, or null if not found.
|
|
33
|
+
*/
|
|
34
|
+
getItem(namespace: string, key: string): Promise<Json | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Store a large JSON value in storage.
|
|
37
|
+
*
|
|
38
|
+
* ⚠️ **Store large values, not many small ones.**
|
|
39
|
+
* Each storage operation has I/O overhead. For best performance:
|
|
40
|
+
* - Store one large object rather than many small key-value pairs
|
|
41
|
+
* - Minimum recommended size: 100 KB per value
|
|
42
|
+
*
|
|
43
|
+
* Adapter is responsible for:
|
|
44
|
+
* - Building the full storage key
|
|
45
|
+
* - Serializing to string (JSON.stringify)
|
|
46
|
+
*
|
|
47
|
+
* @param namespace - The controller namespace (e.g., 'SnapController').
|
|
48
|
+
* @param key - The data key (e.g., 'snap-id:sourceCode').
|
|
49
|
+
* @param value - The JSON value to store.
|
|
50
|
+
*/
|
|
51
|
+
setItem(namespace: string, key: string, value: Json): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Remove an item from storage.
|
|
54
|
+
* Adapter is responsible for building the full storage key.
|
|
55
|
+
*
|
|
56
|
+
* @param namespace - The controller namespace (e.g., 'SnapController').
|
|
57
|
+
* @param key - The data key (e.g., 'snap-id:sourceCode').
|
|
58
|
+
*/
|
|
59
|
+
removeItem(namespace: string, key: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Get all keys for a specific namespace.
|
|
62
|
+
* Should return keys without the 'storage:namespace:' prefix.
|
|
63
|
+
*
|
|
64
|
+
* Adapter is responsible for:
|
|
65
|
+
* - Filtering keys by prefix: 'storage:{namespace}:'
|
|
66
|
+
* - Stripping the prefix from returned keys
|
|
67
|
+
* - Returning only the key portion after the prefix
|
|
68
|
+
*
|
|
69
|
+
* @param namespace - The namespace to get keys for (e.g., 'SnapController').
|
|
70
|
+
* @returns Array of keys without prefix (e.g., ['snap1:sourceCode', 'snap2:sourceCode']).
|
|
71
|
+
*/
|
|
72
|
+
getAllKeys(namespace: string): Promise<string[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Clear all items for a specific namespace.
|
|
75
|
+
*
|
|
76
|
+
* Adapter is responsible for:
|
|
77
|
+
* - Finding all keys with prefix: 'storageService:{namespace}:'
|
|
78
|
+
* - Removing all matching keys
|
|
79
|
+
*
|
|
80
|
+
* @param namespace - The namespace to clear (e.g., 'SnapController').
|
|
81
|
+
*/
|
|
82
|
+
clear(namespace: string): Promise<void>;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Options for constructing a {@link StorageService}.
|
|
86
|
+
*/
|
|
87
|
+
export type StorageServiceOptions = {
|
|
88
|
+
/**
|
|
89
|
+
* The messenger suited for this service.
|
|
90
|
+
*/
|
|
91
|
+
messenger: StorageServiceMessenger;
|
|
92
|
+
/**
|
|
93
|
+
* Storage adapter for persisting data.
|
|
94
|
+
* If not provided, uses in-memory storage (data lost on restart).
|
|
95
|
+
* Production clients MUST provide a persistent storage adapter.
|
|
96
|
+
*/
|
|
97
|
+
storage?: StorageAdapter;
|
|
98
|
+
};
|
|
99
|
+
export declare const SERVICE_NAME = "StorageService";
|
|
100
|
+
/**
|
|
101
|
+
* Storage key prefix for all keys managed by StorageService.
|
|
102
|
+
* Keys are formatted as: {STORAGE_KEY_PREFIX}{namespace}:{key}
|
|
103
|
+
* Example: 'storageService:SnapController:snap-id:sourceCode'
|
|
104
|
+
*/
|
|
105
|
+
export declare const STORAGE_KEY_PREFIX = "storageService:";
|
|
106
|
+
/**
|
|
107
|
+
* All actions that {@link StorageService} exposes to other consumers.
|
|
108
|
+
* Action types are auto-generated from the service methods.
|
|
109
|
+
*/
|
|
110
|
+
export type StorageServiceActions = StorageServiceMethodActions;
|
|
111
|
+
/**
|
|
112
|
+
* Event published when a storage item is set.
|
|
113
|
+
* Event type includes namespace only, key passed in payload.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* Subscribe to all changes in TokenListController:
|
|
117
|
+
* messenger.subscribe('StorageService:itemSet:TokenListController', (key, value) => {
|
|
118
|
+
* // key = 'cache:0x1', 'cache:0x38', etc.
|
|
119
|
+
* // value = the data that was set
|
|
120
|
+
* if (key.startsWith('cache:')) {
|
|
121
|
+
* const chainId = key.replace('cache:', '');
|
|
122
|
+
* // React to cache change for specific chain
|
|
123
|
+
* }
|
|
124
|
+
* });
|
|
125
|
+
*/
|
|
126
|
+
export type StorageServiceItemSetEvent = {
|
|
127
|
+
type: `${typeof SERVICE_NAME}:itemSet:${string}`;
|
|
128
|
+
payload: [key: string, value: Json];
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* All events that {@link StorageService} publishes.
|
|
132
|
+
*/
|
|
133
|
+
export type StorageServiceEvents = StorageServiceItemSetEvent;
|
|
134
|
+
/**
|
|
135
|
+
* Actions from other messengers that {@link StorageService} calls.
|
|
136
|
+
*/
|
|
137
|
+
type AllowedActions = never;
|
|
138
|
+
/**
|
|
139
|
+
* Events from other messengers that {@link StorageService} subscribes to.
|
|
140
|
+
*/
|
|
141
|
+
type AllowedEvents = never;
|
|
142
|
+
/**
|
|
143
|
+
* The messenger restricted to actions and events that
|
|
144
|
+
* {@link StorageService} needs to access.
|
|
145
|
+
*/
|
|
146
|
+
export type StorageServiceMessenger = Messenger<typeof SERVICE_NAME, StorageServiceActions | AllowedActions, StorageServiceEvents | AllowedEvents>;
|
|
147
|
+
export {};
|
|
148
|
+
//# sourceMappingURL=types.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAE5C,OAAO,KAAK,EAAE,2BAA2B,EAAE,iDAA6C;AAExF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE9D;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;OAMG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjD;;;;;;;;OAQG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,SAAS,EAAE,uBAAuB,CAAC;IAEnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC;AAGF,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAEhE;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,GAAG,OAAO,YAAY,YAAY,MAAM,EAAE,CAAC;IACjD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE9D;;GAEG;AACH,KAAK,cAAc,GAAG,KAAK,CAAC;AAE5B;;GAEG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,CAC7C,OAAO,YAAY,EACnB,qBAAqB,GAAG,cAAc,EACtC,oBAAoB,GAAG,aAAa,CACrC,CAAC"}
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Service name constant
|
|
2
|
+
export const SERVICE_NAME = 'StorageService';
|
|
3
|
+
/**
|
|
4
|
+
* Storage key prefix for all keys managed by StorageService.
|
|
5
|
+
* Keys are formatted as: {STORAGE_KEY_PREFIX}{namespace}:{key}
|
|
6
|
+
* Example: 'storageService:SnapController:snap-id:sourceCode'
|
|
7
|
+
*/
|
|
8
|
+
export const STORAGE_KEY_PREFIX = 'storageService:';
|
|
9
|
+
//# sourceMappingURL=types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.mjs","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2GA,wBAAwB;AACxB,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC","sourcesContent":["import type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\n\nimport type { StorageServiceMethodActions } from './StorageService-method-action-types';\n\n/**\n * Platform-agnostic storage adapter interface.\n * Each client (mobile, extension) implements this interface\n * with their preferred storage mechanism.\n *\n * ⚠️ **Designed for large, infrequently accessed data (100KB+)**\n *\n * ✅ **Use for:**\n * - Snap source code (~6 MB per snap)\n * - Token metadata caches (~4 MB)\n * - Large API response caches\n *\n * ❌ **Avoid for:**\n * - Small values (< 10 KB) - use controller state instead\n * - Frequently accessed data - use controller state instead\n * - Many small key-value pairs - use a single large object instead\n *\n * @example Mobile implementation using FilesystemStorage\n * @example Extension implementation using IndexedDB\n * @example Tests using InMemoryStorageAdapter\n */\nexport type StorageAdapter = {\n /**\n * Retrieve an item from storage.\n * Adapter is responsible for building the full storage key.\n *\n * @param namespace - The controller namespace (e.g., 'SnapController').\n * @param key - The data key (e.g., 'snap-id:sourceCode').\n * @returns The JSON value, or null if not found.\n */\n getItem(namespace: string, key: string): Promise<Json | null>;\n\n /**\n * Store a large JSON value in storage.\n *\n * ⚠️ **Store large values, 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 * - Minimum recommended size: 100 KB per value\n *\n * Adapter is responsible for:\n * - Building the full storage key\n * - Serializing to string (JSON.stringify)\n *\n * @param namespace - The controller namespace (e.g., 'SnapController').\n * @param key - The data key (e.g., 'snap-id:sourceCode').\n * @param value - The JSON value to store.\n */\n setItem(namespace: string, key: string, value: Json): Promise<void>;\n\n /**\n * Remove an item from storage.\n * Adapter is responsible for building the full storage key.\n *\n * @param namespace - The controller namespace (e.g., 'SnapController').\n * @param key - The data key (e.g., 'snap-id:sourceCode').\n */\n removeItem(namespace: string, key: string): Promise<void>;\n\n /**\n * Get all keys for a specific namespace.\n * Should return keys without the 'storage:namespace:' prefix.\n *\n * Adapter is responsible for:\n * - Filtering keys by prefix: 'storage:{namespace}:'\n * - Stripping the prefix from returned keys\n * - Returning only the key portion after the prefix\n *\n * @param namespace - The namespace to get keys for (e.g., 'SnapController').\n * @returns Array of keys without prefix (e.g., ['snap1:sourceCode', 'snap2:sourceCode']).\n */\n getAllKeys(namespace: string): Promise<string[]>;\n\n /**\n * Clear all items for a specific namespace.\n *\n * Adapter is responsible for:\n * - Finding all keys with prefix: 'storageService:{namespace}:'\n * - Removing all matching keys\n *\n * @param namespace - The namespace to clear (e.g., 'SnapController').\n */\n clear(namespace: string): Promise<void>;\n};\n\n/**\n * Options for constructing a {@link StorageService}.\n */\nexport type StorageServiceOptions = {\n /**\n * The messenger suited for this service.\n */\n messenger: StorageServiceMessenger;\n\n /**\n * Storage adapter for persisting data.\n * If not provided, uses in-memory storage (data lost on restart).\n * Production clients MUST provide a persistent storage adapter.\n */\n storage?: StorageAdapter;\n};\n\n// Service name constant\nexport const SERVICE_NAME = 'StorageService';\n\n/**\n * Storage key prefix for all keys managed by StorageService.\n * Keys are formatted as: {STORAGE_KEY_PREFIX}{namespace}:{key}\n * Example: 'storageService:SnapController:snap-id:sourceCode'\n */\nexport const STORAGE_KEY_PREFIX = 'storageService:';\n\n/**\n * All actions that {@link StorageService} exposes to other consumers.\n * Action types are auto-generated from the service methods.\n */\nexport type StorageServiceActions = StorageServiceMethodActions;\n\n/**\n * Event published when a storage item is set.\n * Event type includes namespace only, key passed in payload.\n *\n * @example\n * Subscribe to all changes in TokenListController:\n * messenger.subscribe('StorageService:itemSet:TokenListController', (key, value) => {\n * // key = 'cache:0x1', 'cache:0x38', etc.\n * // value = the data that was set\n * if (key.startsWith('cache:')) {\n * const chainId = key.replace('cache:', '');\n * // React to cache change for specific chain\n * }\n * });\n */\nexport type StorageServiceItemSetEvent = {\n type: `${typeof SERVICE_NAME}:itemSet:${string}`;\n payload: [key: string, value: Json];\n};\n\n/**\n * All events that {@link StorageService} publishes.\n */\nexport type StorageServiceEvents = StorageServiceItemSetEvent;\n\n/**\n * Actions from other messengers that {@link StorageService} calls.\n */\ntype AllowedActions = never;\n\n/**\n * Events from other messengers that {@link StorageService} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events that\n * {@link StorageService} needs to access.\n */\nexport type StorageServiceMessenger = Messenger<\n typeof SERVICE_NAME,\n StorageServiceActions | AllowedActions,\n StorageServiceEvents | AllowedEvents\n>;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamask-previews/storage-service",
|
|
3
|
+
"version": "0.0.0-preview-d6fe4594",
|
|
4
|
+
"description": "Platform-agnostic service for storing large, infrequently accessed controller data",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"MetaMask",
|
|
7
|
+
"Ethereum"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/MetaMask/core/tree/main/packages/storage-service#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/MetaMask/core/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/MetaMask/core.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "(MIT OR Apache-2.0)",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/index.d.cts",
|
|
27
|
+
"default": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.cjs",
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist/"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references",
|
|
39
|
+
"build:all": "ts-bridge --project tsconfig.build.json --verbose --clean",
|
|
40
|
+
"build:docs": "typedoc",
|
|
41
|
+
"changelog:update": "../../scripts/update-changelog.sh @metamask/storage-service",
|
|
42
|
+
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/storage-service",
|
|
43
|
+
"publish:preview": "yarn npm publish --tag preview",
|
|
44
|
+
"since-latest-release": "../../scripts/since-latest-release.sh",
|
|
45
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
|
|
46
|
+
"test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
|
|
47
|
+
"test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
|
|
48
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@metamask/messenger": "^0.3.0",
|
|
52
|
+
"@metamask/utils": "^11.8.1"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@metamask/auto-changelog": "^3.4.4",
|
|
56
|
+
"@ts-bridge/cli": "^0.6.4",
|
|
57
|
+
"@types/jest": "^27.4.1",
|
|
58
|
+
"deepmerge": "^4.2.2",
|
|
59
|
+
"jest": "^27.5.1",
|
|
60
|
+
"ts-jest": "^27.1.4",
|
|
61
|
+
"typedoc": "^0.24.8",
|
|
62
|
+
"typedoc-plugin-missing-exports": "^2.0.0",
|
|
63
|
+
"typescript": "~5.3.3"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": "^18.18 || >=20"
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public",
|
|
70
|
+
"registry": "https://registry.npmjs.org/"
|
|
71
|
+
}
|
|
72
|
+
}
|