@moyodb/sdk 0.0.0-alpha.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.
Files changed (47) hide show
  1. package/README.md +51 -0
  2. package/dist/assets/worker-CaOzbBz1.js +2351 -0
  3. package/dist/assets/worker-CaOzbBz1.js.map +1 -0
  4. package/dist/change-events.d.ts +29 -0
  5. package/dist/change-events.d.ts.map +1 -0
  6. package/dist/codec.d.ts +17 -0
  7. package/dist/codec.d.ts.map +1 -0
  8. package/dist/compression.d.ts +11 -0
  9. package/dist/compression.d.ts.map +1 -0
  10. package/dist/demo.d.ts +21 -0
  11. package/dist/demo.d.ts.map +1 -0
  12. package/dist/engine/README.md +56 -0
  13. package/dist/engine/moyodb_engine.d.ts +127 -0
  14. package/dist/engine/moyodb_engine.js +1193 -0
  15. package/dist/engine/moyodb_engine_bg.wasm +0 -0
  16. package/dist/engine/moyodb_engine_bg.wasm.d.ts +52 -0
  17. package/dist/engine/package.json +21 -0
  18. package/dist/engine/snippets/moyodb-engine-cff0a0e2441a7a8f/js/opfs_shim.js +402 -0
  19. package/dist/errors.d.ts +77 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/index.d.ts +9 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/indexing.d.ts +33 -0
  24. package/dist/indexing.d.ts.map +1 -0
  25. package/dist/internal.d.ts +4 -0
  26. package/dist/internal.d.ts.map +1 -0
  27. package/dist/moyodb-sdk.js +1988 -0
  28. package/dist/moyodb-sdk.js.map +1 -0
  29. package/dist/moyodb-sdk.umd.cjs +2 -0
  30. package/dist/moyodb-sdk.umd.cjs.map +1 -0
  31. package/dist/registry.d.ts +33 -0
  32. package/dist/registry.d.ts.map +1 -0
  33. package/dist/subscriptions.d.ts +10 -0
  34. package/dist/subscriptions.d.ts.map +1 -0
  35. package/dist/types.d.ts +158 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/worker-api.d.ts +51 -0
  38. package/dist/worker-api.d.ts.map +1 -0
  39. package/dist/worker-client.d.ts +67 -0
  40. package/dist/worker-client.d.ts.map +1 -0
  41. package/dist/worker-protocol.d.ts +86 -0
  42. package/dist/worker-protocol.d.ts.map +1 -0
  43. package/dist/worker-server.d.ts +6 -0
  44. package/dist/worker-server.d.ts.map +1 -0
  45. package/dist/worker.d.ts +2 -0
  46. package/dist/worker.d.ts.map +1 -0
  47. package/package.json +71 -0
@@ -0,0 +1,52 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ export const memory: WebAssembly.Memory;
4
+ export const __wbg_wasmengine_free: (a: number, b: number) => void;
5
+ export const cleanupInactiveEntries: (a: number, b: number) => any;
6
+ export const dbDirectorySize: (a: number, b: number) => any;
7
+ export const deleteDB: (a: number, b: number) => any;
8
+ export const prepareRebuildTarget: (a: number, b: number) => any;
9
+ export const swapActiveGeneration: (a: number, b: number, c: number, d: number) => any;
10
+ export const wasmengine_apply_batch: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
11
+ export const wasmengine_apply_batch_packed: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
12
+ export const wasmengine_begin_tx: (a: number, b: number, c: number) => [bigint, number, number];
13
+ export const wasmengine_changes_since: (a: number, b: bigint, c: any) => [number, number, number];
14
+ export const wasmengine_clear_store: (a: number, b: bigint, c: number, d: number) => [number, number];
15
+ export const wasmengine_close: (a: number) => [number, number];
16
+ export const wasmengine_commit_tx: (a: number, b: bigint) => [bigint, number, number];
17
+ export const wasmengine_create_store: (a: number, b: bigint, c: number, d: number, e: any) => [number, number];
18
+ export const wasmengine_delete: (a: number, b: bigint, c: number, d: number, e: number, f: number) => [number, number, number];
19
+ export const wasmengine_delete_many: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
20
+ export const wasmengine_delete_many_packed: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
21
+ export const wasmengine_drop_store: (a: number, b: bigint, c: number, d: number) => [number, number];
22
+ export const wasmengine_export_snapshot: (a: number) => [number, number, number];
23
+ export const wasmengine_get: (a: number, b: bigint, c: number, d: number, e: number, f: number) => [number, number, number];
24
+ export const wasmengine_get_many: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
25
+ export const wasmengine_get_many_packed: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
26
+ export const wasmengine_get_schema_version: (a: number) => [bigint, number, number];
27
+ export const wasmengine_has: (a: number, b: bigint, c: number, d: number, e: number, f: number) => [number, number, number];
28
+ export const wasmengine_import_snapshot: (a: number, b: number, c: number) => [bigint, number, number];
29
+ export const wasmengine_list_store_configs: (a: number) => [number, number, number];
30
+ export const wasmengine_list_stores: (a: number) => [number, number, number];
31
+ export const wasmengine_new: () => number;
32
+ export const wasmengine_open: (a: number, b: number, c: number, d: any) => any;
33
+ export const wasmengine_openGeneration: (a: number, b: number, c: number, d: number, e: number, f: any) => any;
34
+ export const wasmengine_put: (a: number, b: bigint, c: number, d: number, e: number, f: number, g: number, h: number, i: any) => [number, number];
35
+ export const wasmengine_put_many: (a: number, b: bigint, c: number, d: number, e: any, f: any) => [number, number, number];
36
+ export const wasmengine_put_many_packed: (a: number, b: bigint, c: number, d: number, e: any, f: any) => [number, number, number];
37
+ export const wasmengine_reset: (a: number) => [bigint, number, number];
38
+ export const wasmengine_rollback_tx: (a: number, b: bigint) => [number, number];
39
+ export const wasmengine_scan: (a: number, b: bigint, c: number, d: number, e: any) => [number, number, number];
40
+ export const wasmengine_set_failpoint: (a: number, b: any) => [number, number];
41
+ export const wasmengine_set_schema_version: (a: number, b: bigint, c: bigint) => [number, number];
42
+ export const wasmengine_stats: (a: number) => [number, number, number];
43
+ export const wasm_bindgen__convert__closures_____invoke__h0b808fc55658bd50: (a: number, b: number, c: any) => [number, number];
44
+ export const wasm_bindgen__convert__closures_____invoke__h694ddb4a279886ac: (a: number, b: number, c: any, d: any) => void;
45
+ export const __wbindgen_malloc: (a: number, b: number) => number;
46
+ export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
47
+ export const __wbindgen_exn_store: (a: number) => void;
48
+ export const __externref_table_alloc: () => number;
49
+ export const __wbindgen_externrefs: WebAssembly.Table;
50
+ export const __wbindgen_destroy_closure: (a: number, b: number) => void;
51
+ export const __externref_table_dealloc: (a: number) => void;
52
+ export const __wbindgen_start: () => void;
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "moyodb-engine",
3
+ "type": "module",
4
+ "description": "Experimental Rust/WASM transactional key-value storage engine for browser apps.",
5
+ "version": "0.1.0",
6
+ "license": "MIT OR Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/moyodb/moyodb"
10
+ },
11
+ "files": [
12
+ "moyodb_engine_bg.wasm",
13
+ "moyodb_engine.js",
14
+ "moyodb_engine.d.ts"
15
+ ],
16
+ "main": "moyodb_engine.js",
17
+ "types": "moyodb_engine.d.ts",
18
+ "sideEffects": [
19
+ "./snippets/*"
20
+ ]
21
+ }
@@ -0,0 +1,402 @@
1
+ const DB_ROOT_DIR = 'stackdb';
2
+ const FILE_NAMES = ['manifest.bin', 'main.bin', 'wal.bin'];
3
+ const CONTROL_FILE_NAME = 'root-manifest.bin';
4
+ const CONTROL_SLOT_SIZE = 4096;
5
+ const CONTROL_VERSION = 1;
6
+ const CONTROL_CHECKSUM_OFFSET = 24;
7
+ const CONTROL_NAME_OFFSET = 32;
8
+ const CONTROL_MAGIC = new Uint8Array([66, 68, 66, 82, 79, 79, 84, 49]);
9
+ const OPFS_WRITE_CHUNK_SIZE = 256 * 1024;
10
+ const TEXT_ENCODER = new TextEncoder();
11
+ const TEXT_DECODER = new TextDecoder();
12
+ let rootDir = null;
13
+ let nextSessionId = 1;
14
+ const sessions = new Map();
15
+ async function getRootDir() {
16
+ if (!navigator?.storage?.getDirectory) {
17
+ throw new Error('navigator.storage.getDirectory is unavailable');
18
+ }
19
+ if (!rootDir) {
20
+ rootDir = await navigator.storage.getDirectory();
21
+ }
22
+ return rootDir;
23
+ }
24
+ async function getOrCreateStackdbRoot() {
25
+ const root = await getRootDir();
26
+ return await root.getDirectoryHandle(DB_ROOT_DIR, { create: true });
27
+ }
28
+ async function lookupDirectoryHandle(parent, name) {
29
+ try {
30
+ return await parent.getDirectoryHandle(name, { create: false });
31
+ } catch (_err) {
32
+ return null;
33
+ }
34
+ }
35
+ async function lookupFileHandle(parent, name) {
36
+ try {
37
+ return await parent.getFileHandle(name, { create: false });
38
+ } catch (_err) {
39
+ return null;
40
+ }
41
+ }
42
+ function sessionPath(encodedDbName, generationName = null) {
43
+ return generationName ? `${encodedDbName}/${generationName}` : encodedDbName;
44
+ }
45
+ function slotBytesView(bytes) {
46
+ return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
47
+ }
48
+ function writeU64(view, offset, value) {
49
+ view.setBigUint64(offset, BigInt(value), true);
50
+ }
51
+ function readU64(view, offset) {
52
+ return Number(view.getBigUint64(offset, true));
53
+ }
54
+ function fnv1a32(bytes, zeroOffset = -1, zeroLength = 0) {
55
+ let hash = 0x811c9dc5;
56
+ const zeroEnd = zeroOffset >= 0 ? zeroOffset + zeroLength : -1;
57
+ for (let index = 0; index < bytes.length; index += 1) {
58
+ const value = zeroOffset >= 0 && index >= zeroOffset && index < zeroEnd ? 0 : (bytes[index] ?? 0);
59
+ hash ^= value;
60
+ hash = Math.imul(hash, 0x01000193) >>> 0;
61
+ }
62
+ return hash >>> 0;
63
+ }
64
+ function isGenerationNameValid(name) {
65
+ return typeof name === 'string' && /^gen-[a-z0-9]+-[a-z0-9]+$/i.test(name);
66
+ }
67
+ function encodeControlSlot(generationCounter, activeGeneration) {
68
+ if (!isGenerationNameValid(activeGeneration)) {
69
+ throw new Error(`invalid generation name ${activeGeneration}`);
70
+ }
71
+ const nameBytes = TEXT_ENCODER.encode(activeGeneration);
72
+ if (CONTROL_NAME_OFFSET + nameBytes.length > CONTROL_SLOT_SIZE) {
73
+ throw new Error(`generation name too long: ${nameBytes.length}`);
74
+ }
75
+ const slot = new Uint8Array(CONTROL_SLOT_SIZE);
76
+ const view = slotBytesView(slot);
77
+ slot.set(CONTROL_MAGIC, 0);
78
+ view.setUint32(8, CONTROL_VERSION, true);
79
+ writeU64(view, 12, generationCounter);
80
+ view.setUint16(20, nameBytes.length, true);
81
+ view.setUint16(22, 0, true);
82
+ view.setUint32(CONTROL_CHECKSUM_OFFSET, 0, true);
83
+ view.setUint32(28, 0, true);
84
+ slot.set(nameBytes, CONTROL_NAME_OFFSET);
85
+ const checksum = fnv1a32(slot, CONTROL_CHECKSUM_OFFSET, 4);
86
+ view.setUint32(CONTROL_CHECKSUM_OFFSET, checksum, true);
87
+ return slot;
88
+ }
89
+ function decodeControlSlot(slotIndex, bytes) {
90
+ if (bytes.length < CONTROL_SLOT_SIZE) {
91
+ return null;
92
+ }
93
+ for (let index = 0; index < CONTROL_MAGIC.length; index += 1) {
94
+ if (bytes[index] !== CONTROL_MAGIC[index]) {
95
+ return null;
96
+ }
97
+ }
98
+ const view = slotBytesView(bytes);
99
+ const version = view.getUint32(8, true);
100
+ if (version !== CONTROL_VERSION) {
101
+ throw new Error(`unsupported control file version ${version}`);
102
+ }
103
+ const expectedChecksum = fnv1a32(bytes, CONTROL_CHECKSUM_OFFSET, 4);
104
+ const storedChecksum = view.getUint32(CONTROL_CHECKSUM_OFFSET, true);
105
+ if (expectedChecksum !== storedChecksum) {
106
+ return null;
107
+ }
108
+ const nameLength = view.getUint16(20, true);
109
+ const nameEnd = CONTROL_NAME_OFFSET + nameLength;
110
+ if (nameEnd > CONTROL_SLOT_SIZE) {
111
+ throw new Error(`control slot name length out of bounds: ${nameLength}`);
112
+ }
113
+ const activeGeneration = TEXT_DECODER.decode(bytes.subarray(CONTROL_NAME_OFFSET, nameEnd));
114
+ if (!isGenerationNameValid(activeGeneration)) {
115
+ throw new Error(`invalid generation in control slot: ${activeGeneration}`);
116
+ }
117
+ return {
118
+ slotIndex,
119
+ generationCounter: readU64(view, 12),
120
+ activeGeneration
121
+ };
122
+ }
123
+ function readControlStateFromAccessHandle(accessHandle) {
124
+ const buffer = new Uint8Array(CONTROL_SLOT_SIZE * 2);
125
+ const size = Number(accessHandle.getSize());
126
+ if (size <= 0) {
127
+ return null;
128
+ }
129
+ const readLength = Math.min(buffer.length, size);
130
+ accessHandle.read(buffer.subarray(0, readLength), { at: 0 });
131
+ const slot0 = decodeControlSlot(0, buffer.subarray(0, CONTROL_SLOT_SIZE));
132
+ const slot1 = decodeControlSlot(1, buffer.subarray(CONTROL_SLOT_SIZE, CONTROL_SLOT_SIZE * 2));
133
+ if (slot0 && slot1) {
134
+ return slot0.generationCounter >= slot1.generationCounter ? slot0 : slot1;
135
+ }
136
+ return slot0 ?? slot1 ?? null;
137
+ }
138
+ async function readControlState(dbRoot) {
139
+ const fileHandle = await lookupFileHandle(dbRoot, CONTROL_FILE_NAME);
140
+ if (!fileHandle) {
141
+ return null;
142
+ }
143
+ const accessHandle = await fileHandle.createSyncAccessHandle();
144
+ try {
145
+ return readControlStateFromAccessHandle(accessHandle);
146
+ } finally {
147
+ accessHandle.close();
148
+ }
149
+ }
150
+ async function writeControlState(dbRoot, activeGeneration) {
151
+ const fileHandle = await dbRoot.getFileHandle(CONTROL_FILE_NAME, { create: true });
152
+ const accessHandle = await fileHandle.createSyncAccessHandle();
153
+ try {
154
+ const current = readControlStateFromAccessHandle(accessHandle);
155
+ const nextSlot = current ? (current.slotIndex === 0 ? 1 : 0) : 0;
156
+ const nextGenerationCounter = (current?.generationCounter ?? 0) + 1;
157
+ const encoded = encodeControlSlot(nextGenerationCounter, activeGeneration);
158
+ accessHandle.write(encoded, { at: nextSlot * CONTROL_SLOT_SIZE });
159
+ accessHandle.flush();
160
+ } finally {
161
+ accessHandle.close();
162
+ }
163
+ }
164
+ function createGenerationName() {
165
+ const timePart = Date.now().toString(36);
166
+ const randomBytes = new Uint8Array(4);
167
+ crypto.getRandomValues(randomBytes);
168
+ const randomPart = Array.from(randomBytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
169
+ return `gen-${timePart}-${randomPart}`;
170
+ }
171
+ async function getDbRoot(encodedDbName, createIfMissing) {
172
+ const stackdb = await getOrCreateStackdbRoot();
173
+ let dbRoot = await lookupDirectoryHandle(stackdb, encodedDbName);
174
+ if (!dbRoot) {
175
+ if (!createIfMissing) {
176
+ throw new Error(`database ${encodedDbName} does not exist`);
177
+ }
178
+ dbRoot = await stackdb.getDirectoryHandle(encodedDbName, { create: true });
179
+ }
180
+ return dbRoot;
181
+ }
182
+ async function resolveActiveDataDir(dbRoot) {
183
+ const control = await readControlState(dbRoot);
184
+ if (!control) {
185
+ return {
186
+ dirHandle: dbRoot,
187
+ generationName: null,
188
+ path: dbRoot.name
189
+ };
190
+ }
191
+ const activeDir = await lookupDirectoryHandle(dbRoot, control.activeGeneration);
192
+ if (!activeDir) {
193
+ throw new Error(`active generation ${control.activeGeneration} is missing`);
194
+ }
195
+ return {
196
+ dirHandle: activeDir,
197
+ generationName: control.activeGeneration,
198
+ path: `${dbRoot.name}/${control.activeGeneration}`
199
+ };
200
+ }
201
+ function closeSession(sessionId) {
202
+ const session = sessions.get(sessionId);
203
+ if (!session) {
204
+ return;
205
+ }
206
+ for (const handle of session.handles.values()) {
207
+ try {
208
+ handle.close();
209
+ } catch (_err) {}
210
+ }
211
+ sessions.delete(sessionId);
212
+ }
213
+ function closeSessionsForDb(encodedDbName) {
214
+ const prefix = `${encodedDbName}/`;
215
+ for (const [sessionId, session] of Array.from(sessions.entries())) {
216
+ if (session.path === encodedDbName || session.path.startsWith(prefix)) {
217
+ closeSession(sessionId);
218
+ }
219
+ }
220
+ }
221
+ async function openSessionForDir(dbPath, dirHandle) {
222
+ const handles = new Map();
223
+ try {
224
+ for (let fileKind = 0; fileKind < FILE_NAMES.length; fileKind += 1) {
225
+ const fileName = FILE_NAMES[fileKind];
226
+ const fileHandle = await dirHandle.getFileHandle(fileName, { create: true });
227
+ if (typeof fileHandle.createSyncAccessHandle !== 'function') {
228
+ throw new Error('createSyncAccessHandle is unavailable');
229
+ }
230
+ const accessHandle = await fileHandle.createSyncAccessHandle();
231
+ handles.set(fileKind, accessHandle);
232
+ }
233
+ } catch (error) {
234
+ for (const handle of handles.values()) {
235
+ try {
236
+ handle.close();
237
+ } catch (_err) {}
238
+ }
239
+ throw error;
240
+ }
241
+ const sessionId = nextSessionId;
242
+ nextSessionId += 1;
243
+ sessions.set(sessionId, {
244
+ path: dbPath,
245
+ handles
246
+ });
247
+ return { sessionId };
248
+ }
249
+ function getSession(sessionId) {
250
+ const session = sessions.get(sessionId);
251
+ if (!session) {
252
+ throw new Error(`no OPFS session ${sessionId}`);
253
+ }
254
+ return session;
255
+ }
256
+ function getAccessHandle(sessionId, fileKind) {
257
+ const session = getSession(sessionId);
258
+ const handle = session.handles.get(fileKind);
259
+ if (!handle) {
260
+ throw new Error(`no OPFS access handle for session ${sessionId} file kind ${fileKind}`);
261
+ }
262
+ return handle;
263
+ }
264
+ function lookupOpenFileSize(path) {
265
+ for (const session of sessions.values()) {
266
+ for (let fileKind = 0; fileKind < FILE_NAMES.length; fileKind += 1) {
267
+ if (`${session.path}/${FILE_NAMES[fileKind]}` === path) {
268
+ return Number(session.handles.get(fileKind)?.getSize() ?? 0);
269
+ }
270
+ }
271
+ }
272
+ return null;
273
+ }
274
+ async function sumDirectorySize(dirHandle, pathPrefix) {
275
+ let total = 0;
276
+ for await (const [name, handle] of dirHandle.entries()) {
277
+ const entryPath = pathPrefix ? `${pathPrefix}/${name}` : name;
278
+ if (handle.kind === 'directory') {
279
+ total += await sumDirectorySize(handle, entryPath);
280
+ continue;
281
+ }
282
+ const openSize = lookupOpenFileSize(entryPath);
283
+ if (openSize !== null) {
284
+ total += openSize;
285
+ continue;
286
+ }
287
+ const file = await handle.getFile();
288
+ total += file.size;
289
+ }
290
+ return total;
291
+ }
292
+ export async function opfsOpenActiveDb(encodedDbName, createIfMissing = true) {
293
+ const dbRoot = await getDbRoot(encodedDbName, createIfMissing);
294
+ const active = await resolveActiveDataDir(dbRoot);
295
+ return await openSessionForDir(active.path, active.dirHandle);
296
+ }
297
+ export async function opfsOpenGenerationDb(encodedDbName, generationName, createIfMissing = true) {
298
+ if (!isGenerationNameValid(generationName)) {
299
+ throw new Error(`invalid generation name ${generationName}`);
300
+ }
301
+ const dbRoot = await getDbRoot(encodedDbName, createIfMissing);
302
+ const generationDir = await dbRoot.getDirectoryHandle(generationName, { create: createIfMissing });
303
+ return await openSessionForDir(sessionPath(encodedDbName, generationName), generationDir);
304
+ }
305
+ export function opfsReadAt(sessionId, fileKind, offset, len) {
306
+ const handle = getAccessHandle(sessionId, fileKind);
307
+ const buffer = new Uint8Array(len);
308
+ handle.read(buffer, { at: Number(offset) });
309
+ return buffer;
310
+ }
311
+ export function opfsWriteAt(sessionId, fileKind, offset, bytes) {
312
+ const handle = getAccessHandle(sessionId, fileKind);
313
+ const startOffset = Number(offset);
314
+ let writtenTotal = 0;
315
+ while (writtenTotal < bytes.length) {
316
+ const chunk = bytes.subarray(writtenTotal, writtenTotal + OPFS_WRITE_CHUNK_SIZE);
317
+ const rawWritten = handle.write(chunk, { at: startOffset + writtenTotal });
318
+ const written = Number(rawWritten);
319
+ if (!Number.isSafeInteger(written) || written < 0 || written > chunk.length) {
320
+ throw new Error(`opfs write failed: invalid byte count ${String(rawWritten)}`);
321
+ }
322
+ if (written === 0 && chunk.length > 0) {
323
+ throw new Error('opfs write failed: wrote 0 bytes');
324
+ }
325
+ writtenTotal += written;
326
+ }
327
+ return writtenTotal;
328
+ }
329
+ export function opfsFlush(sessionId, fileKind) {
330
+ const handle = getAccessHandle(sessionId, fileKind);
331
+ handle.flush();
332
+ }
333
+ export function opfsLen(sessionId, fileKind) {
334
+ const handle = getAccessHandle(sessionId, fileKind);
335
+ return BigInt(handle.getSize());
336
+ }
337
+ export function opfsTruncate(sessionId, fileKind, size) {
338
+ const handle = getAccessHandle(sessionId, fileKind);
339
+ handle.truncate(Number(size));
340
+ }
341
+ export function opfsCloseSession(sessionId) {
342
+ closeSession(sessionId);
343
+ }
344
+ export async function opfsPrepareRebuildTarget(encodedDbName) {
345
+ const dbRoot = await getDbRoot(encodedDbName, true);
346
+ let generationName = createGenerationName();
347
+ while (await lookupDirectoryHandle(dbRoot, generationName)) {
348
+ generationName = createGenerationName();
349
+ }
350
+ await dbRoot.getDirectoryHandle(generationName, { create: true });
351
+ return { generationName };
352
+ }
353
+ export async function opfsSwapActiveGeneration(encodedDbName, generationName) {
354
+ if (!isGenerationNameValid(generationName)) {
355
+ throw new Error(`invalid generation name ${generationName}`);
356
+ }
357
+ const dbRoot = await getDbRoot(encodedDbName, false);
358
+ const generationDir = await lookupDirectoryHandle(dbRoot, generationName);
359
+ if (!generationDir) {
360
+ throw new Error(`generation ${generationName} does not exist`);
361
+ }
362
+ await writeControlState(dbRoot, generationName);
363
+ }
364
+ export async function opfsCleanupInactiveEntries(encodedDbName) {
365
+ const dbRoot = await lookupDirectoryHandle(await getOrCreateStackdbRoot(), encodedDbName);
366
+ if (!dbRoot) {
367
+ return;
368
+ }
369
+ const control = await readControlState(dbRoot);
370
+ const activeGeneration = control?.activeGeneration ?? null;
371
+ for await (const [name, handle] of dbRoot.entries()) {
372
+ if (name === CONTROL_FILE_NAME) {
373
+ continue;
374
+ }
375
+ if (handle.kind === 'directory') {
376
+ if (activeGeneration && name === activeGeneration) {
377
+ continue;
378
+ }
379
+ if (name.startsWith('gen-')) {
380
+ await dbRoot.removeEntry(name, { recursive: true });
381
+ }
382
+ continue;
383
+ }
384
+ if (activeGeneration && FILE_NAMES.includes(name)) {
385
+ await dbRoot.removeEntry(name);
386
+ }
387
+ }
388
+ }
389
+ export async function opfsDbDirectorySize(encodedDbName) {
390
+ const dbRoot = await lookupDirectoryHandle(await getOrCreateStackdbRoot(), encodedDbName);
391
+ if (!dbRoot) {
392
+ return 0;
393
+ }
394
+ return await sumDirectorySize(dbRoot, encodedDbName);
395
+ }
396
+ export async function opfsRemoveDb(encodedDbName) {
397
+ closeSessionsForDb(encodedDbName);
398
+ const stackdb = await getOrCreateStackdbRoot();
399
+ try {
400
+ await stackdb.removeEntry(encodedDbName, { recursive: true });
401
+ } catch (_err) {}
402
+ }
@@ -0,0 +1,77 @@
1
+ export declare class MoyoDbError extends Error {
2
+ constructor(name: string, message: string);
3
+ }
4
+ export declare class UnsupportedPlatformError extends MoyoDbError {
5
+ constructor(message: string);
6
+ }
7
+ export declare class DatabaseBusyError extends MoyoDbError {
8
+ constructor(message: string);
9
+ }
10
+ export declare class CorruptionError extends MoyoDbError {
11
+ constructor(message: string);
12
+ }
13
+ export declare class StoreExistsError extends MoyoDbError {
14
+ constructor(message: string);
15
+ }
16
+ export declare class StoreNotFoundError extends MoyoDbError {
17
+ constructor(message: string);
18
+ }
19
+ export declare class IndexNotFoundError extends MoyoDbError {
20
+ constructor(message: string);
21
+ }
22
+ export declare class ConstraintError extends MoyoDbError {
23
+ constructor(message: string);
24
+ }
25
+ export declare class UniqueIndexConstraintError extends ConstraintError {
26
+ constructor(message: string);
27
+ }
28
+ export declare class WriteTransactionAlreadyOpenError extends MoyoDbError {
29
+ constructor(message?: string);
30
+ }
31
+ export declare class ReadonlyTransactionError extends MoyoDbError {
32
+ constructor(message?: string);
33
+ }
34
+ export declare class TransactionClosedError extends MoyoDbError {
35
+ constructor(message?: string);
36
+ }
37
+ export declare class DatabaseClosedError extends MoyoDbError {
38
+ constructor(message?: string);
39
+ }
40
+ export declare class ValueTooLargeError extends MoyoDbError {
41
+ constructor(message: string);
42
+ }
43
+ export declare class KeyTooLargeError extends MoyoDbError {
44
+ constructor(message: string);
45
+ }
46
+ export declare class StoreNameTooLongError extends MoyoDbError {
47
+ constructor(message: string);
48
+ }
49
+ export declare class ReservedStoreNameError extends MoyoDbError {
50
+ constructor(message: string);
51
+ }
52
+ export declare class InvalidRangeError extends MoyoDbError {
53
+ constructor(message: string);
54
+ }
55
+ export declare class StorageError extends MoyoDbError {
56
+ constructor(message: string);
57
+ }
58
+ export declare class SerializationError extends MoyoDbError {
59
+ constructor(message: string);
60
+ }
61
+ export declare class InjectedFailureError extends MoyoDbError {
62
+ constructor(message: string);
63
+ }
64
+ export declare class ChangeFeedCompactedError extends MoyoDbError {
65
+ constructor(message: string);
66
+ }
67
+ export declare class InternalError extends MoyoDbError {
68
+ constructor(message: string);
69
+ }
70
+ export declare class InvalidOpenOptionsError extends MoyoDbError {
71
+ constructor(message: string);
72
+ }
73
+ export declare class VersionError extends MoyoDbError {
74
+ constructor(message: string);
75
+ }
76
+ export declare function normalizeError(error: unknown): Error;
77
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;gBACtB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK5C;AACD,qBAAa,wBAAyB,SAAQ,WAAW;gBACzC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,iBAAkB,SAAQ,WAAW;gBAClC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,eAAgB,SAAQ,WAAW;gBAChC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,gBAAiB,SAAQ,WAAW;gBACjC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,kBAAmB,SAAQ,WAAW;gBACnC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,kBAAmB,SAAQ,WAAW;gBACnC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,eAAgB,SAAQ,WAAW;gBAChC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,0BAA2B,SAAQ,eAAe;gBAC/C,OAAO,EAAE,MAAM;CAI9B;AACD,qBAAa,gCAAiC,SAAQ,WAAW;gBACjD,OAAO,SAAmC;CAGzD;AACD,qBAAa,wBAAyB,SAAQ,WAAW;gBACzC,OAAO,SAAuD;CAG7E;AACD,qBAAa,sBAAuB,SAAQ,WAAW;gBACvC,OAAO,SAAkC;CAGxD;AACD,qBAAa,mBAAoB,SAAQ,WAAW;gBACpC,OAAO,SAA8B;CAGpD;AACD,qBAAa,kBAAmB,SAAQ,WAAW;gBACnC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,gBAAiB,SAAQ,WAAW;gBACjC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,qBAAsB,SAAQ,WAAW;gBACtC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,sBAAuB,SAAQ,WAAW;gBACvC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,iBAAkB,SAAQ,WAAW;gBAClC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,YAAa,SAAQ,WAAW;gBAC7B,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,kBAAmB,SAAQ,WAAW;gBACnC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,oBAAqB,SAAQ,WAAW;gBACrC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,wBAAyB,SAAQ,WAAW;gBACzC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,aAAc,SAAQ,WAAW;gBAC9B,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,uBAAwB,SAAQ,WAAW;gBACxC,OAAO,EAAE,MAAM;CAG9B;AACD,qBAAa,YAAa,SAAQ,WAAW;gBAC7B,OAAO,EAAE,MAAM;CAG9B;AA4CD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAcpD"}
@@ -0,0 +1,9 @@
1
+ import type { DB, OpenOptions } from './types';
2
+ export declare function openDB(name: string, options?: OpenOptions): Promise<DB>;
3
+ export declare function deleteDB(name: string): Promise<void>;
4
+ export declare function unsafeDebugCrashWorker(name: string): boolean;
5
+ export * from './codec';
6
+ export * from './errors';
7
+ export * from './indexing';
8
+ export type * from './types';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAMR,EAAE,EAMF,WAAW,EASd,MAAM,SAAS,CAAC;AA6xBjB,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,EAAE,CAAC,CA2DjF;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1D;AACD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG5D;AACD,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,mBAAmB,SAAS,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { IndexDef, Range } from './types';
2
+ export declare const INDEX_METADATA_STORE = "__browserdb:indexes";
3
+ export interface NormalizedIndexDef {
4
+ store: string;
5
+ name: string;
6
+ keyPath: string[];
7
+ compound: boolean;
8
+ unique: boolean;
9
+ internalStore: string;
10
+ }
11
+ export interface DecodedIndexEntryKey {
12
+ logicalKey: Uint8Array;
13
+ primaryKey: Uint8Array;
14
+ }
15
+ export declare function isInternalStoreName(name: string): boolean;
16
+ export declare function assertPublicStoreName(name: string): void;
17
+ export declare function normalizeIndexDefinitions(value: unknown): NormalizedIndexDef[];
18
+ export declare function compareNormalizedIndexDefinitions(left: NormalizedIndexDef, right: NormalizedIndexDef): number;
19
+ export declare function indexDefinitionIdentity(store: string, name: string): string;
20
+ export declare function findIndexDefinition(defs: ReadonlyArray<NormalizedIndexDef>, store: string, name: string): NormalizedIndexDef | null;
21
+ export declare function indexesForStore(defs: ReadonlyArray<NormalizedIndexDef>, store: string): NormalizedIndexDef[];
22
+ export declare function encodeIndexMetadataKey(store: string, name: string): Uint8Array;
23
+ export declare function encodeIndexMetadataValue(def: NormalizedIndexDef): Uint8Array;
24
+ export declare function decodeIndexMetadataValue(bytes: Uint8Array): NormalizedIndexDef;
25
+ export declare function encodeIndexEntryKey(logicalIndexKey: Uint8Array, primaryKey: Uint8Array): Uint8Array;
26
+ export declare function decodeIndexEntryKey(bytes: Uint8Array): DecodedIndexEntryKey;
27
+ export declare function indexKeyExactRange(logicalIndexKey: Uint8Array): Range;
28
+ export declare function indexRangeToPhysicalRange(range?: Range): Range;
29
+ export declare function extractLogicalIndexKey(def: NormalizedIndexDef, valueBytes: Uint8Array): Uint8Array | null;
30
+ export declare function serializeNormalizedIndexDefinitions(defs: ReadonlyArray<NormalizedIndexDef>): string;
31
+ export declare function cloneNormalizedIndexDefinitions(defs: ReadonlyArray<NormalizedIndexDef>): NormalizedIndexDef[];
32
+ export declare function toPublicIndexDefinitions(defs: ReadonlyArray<NormalizedIndexDef>): IndexDef[];
33
+ //# sourceMappingURL=indexing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAU/C,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAC1D,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,UAAU,CAAC;CAC1B;AACD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AACD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIxD;AACD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,EAAE,CAuD9E;AACD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAc7G;AACD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AACD,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,EACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACb,kBAAkB,GAAG,IAAI,CAE3B;AACD,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAE5G;AACD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAE9E;AACD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,kBAAkB,GAAG,UAAU,CAE5E;AACD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,kBAAkB,CAS9E;AACD,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAEnG;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,oBAAoB,CAS3E;AACD,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,UAAU,GAAG,KAAK,CAErE;AACD,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,KAAU,GAAG,KAAK,CA4BlE;AACD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAkBzG;AACD,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAEnG;AACD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,EAAE,CAS7G;AACD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,QAAQ,EAAE,CAE5F"}
@@ -0,0 +1,4 @@
1
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
2
+ export declare function compareStringsByCodeUnit(left: string, right: string): number;
3
+ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, fallback: T): Promise<T>;
4
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK5E;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAc9F"}