@peerbit/any-store 1.0.1

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,244 @@
1
+ import { deserialize, serialize } from "@dao-xyz/borsh";
2
+ import { AnyStore } from "./interface.js";
3
+ import * as memory from "./opfs-worker-messages.js";
4
+
5
+ export class OPFSStoreWorker {
6
+ level: string[];
7
+ private _levels: Map<string, AnyStore>;
8
+ private _rootStore: AnyStore;
9
+
10
+ private _memoryIterator: Map<
11
+ string,
12
+ AsyncIterator<[string, ArrayBuffer], void, void>
13
+ >;
14
+
15
+ constructor() {
16
+ const postMessageFn = postMessage;
17
+ this._memoryIterator = new Map();
18
+ this._levels = new Map();
19
+ const createMemory = (
20
+ root?: FileSystemDirectoryHandle,
21
+ level: string[] = []
22
+ ): AnyStore => {
23
+ let isOpen = false;
24
+
25
+ let m: FileSystemDirectoryHandle = root!;
26
+
27
+ // 'open' | 'closed' is just a virtual thing since OPFS is always open as soone as we get the FileSystemDirectoryHandle
28
+ // TODO remove status? or assume not storage adapters can be closed?
29
+ const open = async () => {
30
+ isOpen = true;
31
+ m = m || (await navigator.storage.getDirectory());
32
+ };
33
+
34
+ return {
35
+ clear: async () => {
36
+ for await (const key of m.keys()) {
37
+ m.removeEntry(key, { recursive: true });
38
+ }
39
+ },
40
+
41
+ del: async (key) => {
42
+ try {
43
+ await m.removeEntry(key, { recursive: true });
44
+ } catch (error) {
45
+ if (
46
+ error instanceof DOMException &&
47
+ error.name === "NotFoundError"
48
+ ) {
49
+ return;
50
+ } else {
51
+ throw error;
52
+ }
53
+ }
54
+ },
55
+
56
+ get: async (key) => {
57
+ try {
58
+ const fileHandle = await m.getFileHandle(key);
59
+ const buffer = await (await fileHandle.getFile()).arrayBuffer();
60
+ return new Uint8Array(buffer);
61
+ } catch (error) {
62
+ if (
63
+ error instanceof DOMException &&
64
+ error.name === "NotFoundError"
65
+ ) {
66
+ return;
67
+ } else {
68
+ throw error;
69
+ }
70
+ }
71
+ },
72
+ put: async (key, value) => {
73
+ const fileHandle = await m.getFileHandle(key, { create: true });
74
+ const writeFileHandle = await fileHandle.createSyncAccessHandle();
75
+
76
+ writeFileHandle.write(value);
77
+ writeFileHandle.close();
78
+ },
79
+
80
+ size: async () => {
81
+ let size = 0;
82
+ for await (const value of m.values()) {
83
+ if (value.kind === "file") {
84
+ const handle = await value.createSyncAccessHandle();
85
+ size += handle.getSize();
86
+ handle.close();
87
+ }
88
+ }
89
+ return size;
90
+ },
91
+ status: () => (isOpen ? "open" : "closed"),
92
+
93
+ sublevel: async (name) => {
94
+ const fileHandle = await m.getDirectoryHandle(name, { create: true });
95
+ const sublevel = [...level, name];
96
+ const subMemory = createMemory(fileHandle, sublevel);
97
+ this._levels.set(memory.levelKey(sublevel), subMemory);
98
+ await subMemory.open();
99
+ return subMemory;
100
+ },
101
+
102
+ async *iterator(): AsyncGenerator<[string, Uint8Array], void, void> {
103
+ for await (const v of m.values()) {
104
+ if (v.kind == "file") {
105
+ yield [
106
+ v.name,
107
+ new Uint8Array(await (await v.getFile()).arrayBuffer())
108
+ ];
109
+ }
110
+ }
111
+ },
112
+ close: async () => {
113
+ isOpen = false;
114
+ this._memoryIterator.clear();
115
+ },
116
+ open
117
+ };
118
+ };
119
+
120
+ this._rootStore = createMemory();
121
+
122
+ self.addEventListener("message", async (ev) => {
123
+ const message = deserialize(ev["data"], memory.MemoryRequest);
124
+ if (message instanceof memory.MemoryMessage) {
125
+ const m =
126
+ message.level.length === 0
127
+ ? this._rootStore
128
+ : this._levels.get(memory.levelKey(message.level));
129
+ if (!m) {
130
+ throw new Error("Recieved memory message for an undefined level");
131
+ } else if (message instanceof memory.REQ_Clear) {
132
+ await m.clear();
133
+ await this.respond(
134
+ message,
135
+ new memory.RESP_Clear({ level: message.level }),
136
+ postMessageFn
137
+ );
138
+ } else if (message instanceof memory.REQ_Close) {
139
+ await m.close();
140
+ await this.respond(
141
+ message,
142
+ new memory.RESP_Close({ level: message.level }),
143
+ postMessageFn
144
+ );
145
+ } else if (message instanceof memory.REQ_Del) {
146
+ await m.del(message.key);
147
+ await this.respond(
148
+ message,
149
+ new memory.RESP_Del({ level: message.level }),
150
+ postMessageFn
151
+ );
152
+ } else if (message instanceof memory.REQ_Iterator_Next) {
153
+ let iterator = this._memoryIterator.get(message.id);
154
+ if (!iterator) {
155
+ iterator = m.iterator()[Symbol.asyncIterator]();
156
+ this._memoryIterator.set(message.id, iterator);
157
+ }
158
+ const next = await iterator.next();
159
+ await this.respond(
160
+ message,
161
+ new memory.RESP_Iterator_Next({
162
+ keys: next.done ? [] : [next.value[0]],
163
+ values: next.done ? [] : [new Uint8Array(next.value[1])],
164
+ level: message.level
165
+ }),
166
+ postMessageFn
167
+ );
168
+ if (next.done) {
169
+ this._memoryIterator.delete(message.id);
170
+ }
171
+ } else if (message instanceof memory.REQ_Iterator_Stop) {
172
+ this._memoryIterator.delete(message.id);
173
+ await this.respond(
174
+ message,
175
+ new memory.RESP_Iterator_Stop({ level: message.level }),
176
+ postMessageFn
177
+ );
178
+ } else if (message instanceof memory.REQ_Get) {
179
+ const value = await m.get(message.key);
180
+ await this.respond(
181
+ message,
182
+ new memory.RESP_Get({
183
+ bytes: value ? new Uint8Array(value) : undefined,
184
+ level: message.level
185
+ }),
186
+ postMessageFn
187
+ );
188
+ } else if (message instanceof memory.REQ_Open) {
189
+ await m.open();
190
+ await this.respond(
191
+ message,
192
+ new memory.RESP_Open({ level: message.level }),
193
+ postMessageFn
194
+ );
195
+ } else if (message instanceof memory.REQ_Put) {
196
+ await m.put(message.key, message.bytes);
197
+ await this.respond(
198
+ message,
199
+ new memory.RESP_Put({ level: message.level }),
200
+ postMessageFn
201
+ );
202
+ } else if (message instanceof memory.REQ_Size) {
203
+ await this.respond(
204
+ message,
205
+ new memory.RESP_Size({
206
+ size: await m.size(),
207
+ level: message.level
208
+ }),
209
+ postMessageFn
210
+ );
211
+ } else if (message instanceof memory.REQ_Status) {
212
+ await this.respond(
213
+ message,
214
+ new memory.RESP_Status({
215
+ status: await m.status(),
216
+ level: message.level
217
+ }),
218
+ postMessageFn
219
+ );
220
+ } else if (message instanceof memory.REQ_Sublevel) {
221
+ await m.sublevel(message.name);
222
+
223
+ await this.respond(
224
+ message,
225
+ new memory.RESP_Sublevel({ level: message.level }),
226
+ postMessageFn
227
+ );
228
+ }
229
+ }
230
+ });
231
+ }
232
+
233
+ async respond(
234
+ request: memory.MemoryRequest,
235
+ response: memory.MemoryRequest,
236
+ postMessageFn = postMessage
237
+ ) {
238
+ response.messageId = request.messageId;
239
+ const bytes = serialize(response);
240
+ postMessageFn(bytes, { transfer: [bytes.buffer] });
241
+ }
242
+ }
243
+
244
+ new OPFSStoreWorker();
package/src/opfs.ts ADDED
@@ -0,0 +1,201 @@
1
+ import { AnyStore, MaybePromise } from "./interface.js";
2
+ import * as memory from "./opfs-worker-messages.js";
3
+ import { v4 as uuid } from "uuid";
4
+ import { serialize, deserialize } from "@dao-xyz/borsh";
5
+
6
+ function memoryIterator(
7
+ client: {
8
+ request<T extends memory.MemoryRequest>(
9
+ request: memory.MemoryRequest
10
+ ): Promise<T>;
11
+ },
12
+ level: string[]
13
+ ): {
14
+ [Symbol.asyncIterator]: () => AsyncIterator<[string, Uint8Array], void, void>;
15
+ } {
16
+ return {
17
+ [Symbol.asyncIterator]() {
18
+ const iteratorId = uuid();
19
+ return {
20
+ next: async () => {
21
+ const resp = await client.request<memory.RESP_Iterator_Next>(
22
+ new memory.REQ_Iterator_Next({ id: iteratorId, level })
23
+ );
24
+ if (resp.keys.length > 1) {
25
+ throw new Error("Unsupported iteration response");
26
+ }
27
+ // Will only have 0 or 1 element for now
28
+ for (let i = 0; i < resp.keys.length; i++) {
29
+ return {
30
+ done: false,
31
+ value: [resp.keys[i], resp.values[i]] as [string, Uint8Array]
32
+ } as { done: false; value: [string, Uint8Array] };
33
+ }
34
+ return { done: true, value: undefined } as {
35
+ done: true;
36
+ value: undefined;
37
+ };
38
+ },
39
+ async return() {
40
+ await client.request<memory.RESP_Iterator_Next>(
41
+ new memory.REQ_Iterator_Stop({ id: iteratorId, level })
42
+ );
43
+ return { done: true, value: undefined } as {
44
+ done: true;
45
+ value: undefined;
46
+ };
47
+ }
48
+ };
49
+ }
50
+ };
51
+ }
52
+
53
+ const workerURL = new URL("./opfs-worker.js", import.meta.url);
54
+
55
+ /* new Worker(workerURL, { type: 'module' }) */
56
+ const createWorker = () => new Worker(workerURL, { type: "module" });
57
+ export class OPFSStore implements AnyStore {
58
+ worker: Worker;
59
+ level: string[];
60
+ levelMap: Map<string, AnyStore>;
61
+ root: AnyStore;
62
+
63
+ private _responseCallbacks: Map<
64
+ string,
65
+ { fn: (message: memory.MemoryRequest) => any; once: boolean }
66
+ > = new Map();
67
+
68
+ private _createMemory: (level: string[]) => AnyStore;
69
+ constructor(level: string[] = []) {
70
+ this.level = level;
71
+ this.levelMap = new Map();
72
+ this._createMemory = (level: string[] = []): AnyStore => {
73
+ return {
74
+ clear: async () => {
75
+ await this.request<memory.RESP_Clear>(
76
+ new memory.REQ_Clear({ level })
77
+ );
78
+ },
79
+ del: async (key) => {
80
+ await this.request<memory.RESP_Del>(
81
+ new memory.REQ_Del({ level, key })
82
+ );
83
+ },
84
+ get: async (key) => {
85
+ return (
86
+ await this.request<memory.RESP_Get>(
87
+ new memory.REQ_Get({ level, key })
88
+ )
89
+ ).bytes;
90
+ },
91
+ put: async (key, value) => {
92
+ await this.request<memory.RESP_Put>(
93
+ new memory.REQ_Put({ level, key, bytes: value })
94
+ );
95
+ },
96
+ status: async () =>
97
+ (
98
+ await this.request<memory.RESP_Status>(
99
+ new memory.REQ_Status({ level })
100
+ )
101
+ ).status,
102
+ sublevel: async (name) => {
103
+ await this.request<memory.RESP_Sublevel>(
104
+ new memory.REQ_Sublevel({ level, name })
105
+ );
106
+ const newLevels = [...level, name];
107
+ const sublevel = this._createMemory(newLevels);
108
+ this.levelMap.set(memory.levelKey(newLevels), sublevel);
109
+ return sublevel;
110
+ },
111
+
112
+ iterator: () => memoryIterator(this, level),
113
+ close: async () => {
114
+ await this.request<memory.RESP_Close>(
115
+ new memory.REQ_Close({ level })
116
+ );
117
+ /* this.levelMap.delete(memory.levelKey(level)); */
118
+ },
119
+ open: async () => {
120
+ await this.request<memory.RESP_Open>(new memory.REQ_Open({ level }));
121
+ },
122
+
123
+ size: async () => {
124
+ const size = await this.request<memory.RESP_Size>(
125
+ new memory.REQ_Size({ level })
126
+ );
127
+ return size.size;
128
+ }
129
+ };
130
+ };
131
+ }
132
+ status() {
133
+ return this.worker ? this.root.status() : "closed";
134
+ }
135
+ async close(): Promise<void> {
136
+ this.worker.terminate();
137
+ this.worker = undefined!;
138
+ this._responseCallbacks.clear();
139
+ this.levelMap.clear();
140
+ }
141
+ async open(): Promise<void> {
142
+ if (!this.worker) {
143
+ this.root = this._createMemory([]);
144
+ this.worker = createWorker();
145
+ this.worker.addEventListener("message", async (ev) => {
146
+ const message = deserialize(ev.data, memory.MemoryMessage);
147
+ this._responseCallbacks.get(message.messageId)!.fn(message);
148
+ });
149
+ await this.root.open();
150
+ }
151
+ }
152
+ async get(key: string): Promise<Uint8Array | undefined> {
153
+ return this.root.get(key);
154
+ }
155
+ async put(key: string, value: Uint8Array) {
156
+ return this.root.put(key, value);
157
+ }
158
+ del(key: any): MaybePromise<void> {
159
+ return this.root.del(key);
160
+ }
161
+ sublevel(name: string): AnyStore | Promise<AnyStore> {
162
+ return this.root.sublevel(name);
163
+ }
164
+ iterator(): {
165
+ [Symbol.asyncIterator]: () => AsyncIterator<
166
+ [string, Uint8Array],
167
+ void,
168
+ void
169
+ >;
170
+ } {
171
+ return this.root.iterator();
172
+ }
173
+ clear(): MaybePromise<void> {
174
+ return this.root.clear();
175
+ }
176
+
177
+ size(): MaybePromise<number> {
178
+ return this.root.size();
179
+ }
180
+
181
+ async request<T extends memory.MemoryRequest>(
182
+ request: memory.MemoryRequest
183
+ ): Promise<T> {
184
+ return new Promise<T>((resolve, reject) => {
185
+ const onResponse = (message: memory.MemoryRequest) => {
186
+ this._responseCallbacks.delete(request.messageId);
187
+ if (message instanceof memory.RESP_Error) {
188
+ reject(message.error);
189
+ } else {
190
+ resolve(message as T);
191
+ }
192
+ };
193
+ this._responseCallbacks.set(request.messageId, {
194
+ fn: onResponse,
195
+ once: true
196
+ });
197
+ const bytes = serialize(request);
198
+ this.worker.postMessage(bytes, [bytes.buffer]);
199
+ });
200
+ }
201
+ }
@@ -0,0 +1,10 @@
1
+ import { MemoryStore } from "./memory.js";
2
+ import { OPFSStore } from "./opfs.js";
3
+
4
+ export const createStore = (directory?: string) => {
5
+ return directory ? new OPFSStore([directory]) : new MemoryStore();
6
+ };
7
+
8
+ /* export const estimate = (directory: string): Promise<{ quota?: number, usage?: number }> => {
9
+ return navigator.storage.estimate().then(x => { return { quota: x.quota, usage: x.usage } })
10
+ } */
package/src/store.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { LevelStore } from "./level.js";
2
+ import { Level } from "level";
3
+ import { MemoryStore } from "./memory.js";
4
+ /* import os from 'os'
5
+ import { check } from 'diskusage' */
6
+
7
+ export const createStore = (directory?: string) => {
8
+ return directory
9
+ ? new LevelStore(new Level(directory, { valueEncoding: "view" }))
10
+ : new MemoryStore();
11
+ };
12
+
13
+ /* export const estimate = (directory: string): Promise<{ quota?: number, usage?: number }> => {
14
+ return check(directory).then(x => { return { quota: x.total, usage: x.total - x.free } })
15
+ } */