@moltendb-web/core 1.0.0-rc.4 → 1.0.0-rc.5
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/dist/index.d.ts +1 -1
- package/dist/index.js +25 -11
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare class MoltenDb {
|
|
|
13
13
|
readonly dbName: string;
|
|
14
14
|
readonly workerUrl?: string | URL;
|
|
15
15
|
worker: Worker | null;
|
|
16
|
+
private initPromise;
|
|
16
17
|
private pendingRequests;
|
|
17
18
|
isLeader: boolean;
|
|
18
19
|
private bc;
|
|
@@ -28,7 +29,6 @@ export declare class MoltenDb {
|
|
|
28
29
|
/** Manually remove a specific listener */
|
|
29
30
|
unsubscribe(listener: (event: DbEvent) => void): void;
|
|
30
31
|
private dispatchEvent;
|
|
31
|
-
private initialized;
|
|
32
32
|
init(): Promise<void>;
|
|
33
33
|
private startAsLeader;
|
|
34
34
|
private startAsFollower;
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export class MoltenDb {
|
|
|
3
3
|
dbName;
|
|
4
4
|
workerUrl;
|
|
5
5
|
worker = null;
|
|
6
|
+
initPromise = null;
|
|
6
7
|
pendingRequests = new Map();
|
|
7
8
|
// Multi-tab Sync State
|
|
8
9
|
isLeader = false;
|
|
@@ -39,13 +40,13 @@ export class MoltenDb {
|
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
// ───────────────────────────────────────────────────────────────────────────
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (this.
|
|
45
|
-
return;
|
|
46
|
-
|
|
47
|
-
this.
|
|
48
|
-
|
|
43
|
+
init() {
|
|
44
|
+
// 1. If initialization has already started or finished, return the existing promise
|
|
45
|
+
if (this.initPromise)
|
|
46
|
+
return this.initPromise;
|
|
47
|
+
// 2. Otherwise, start the initialization and store the promise
|
|
48
|
+
this.initPromise = new Promise((resolveInit, rejectInit) => {
|
|
49
|
+
this.bc = new BroadcastChannel(`moltendb_channel_${this.dbName}`);
|
|
49
50
|
navigator.locks.request(`moltendb_lock_${this.dbName}`, { ifAvailable: true }, async (lock) => {
|
|
50
51
|
if (lock) {
|
|
51
52
|
try {
|
|
@@ -60,6 +61,7 @@ export class MoltenDb {
|
|
|
60
61
|
else {
|
|
61
62
|
this.startAsFollower();
|
|
62
63
|
resolveInit();
|
|
64
|
+
// Wait in the background to become leader if the current leader dies
|
|
63
65
|
navigator.locks.request(`moltendb_lock_${this.dbName}`, async () => {
|
|
64
66
|
console.log(`[MoltenDb] Promoting this tab to Leader.`);
|
|
65
67
|
await this.startAsLeader();
|
|
@@ -68,6 +70,7 @@ export class MoltenDb {
|
|
|
68
70
|
}
|
|
69
71
|
});
|
|
70
72
|
});
|
|
73
|
+
return this.initPromise;
|
|
71
74
|
}
|
|
72
75
|
async startAsLeader() {
|
|
73
76
|
// Guard: OPFS is required
|
|
@@ -139,21 +142,33 @@ export class MoltenDb {
|
|
|
139
142
|
};
|
|
140
143
|
}
|
|
141
144
|
async sendMessage(action, payload) {
|
|
142
|
-
//
|
|
145
|
+
// Wait for the engine to boot before routing the message.
|
|
146
|
+
// If the DB is already initialized, this resolves instantly.
|
|
147
|
+
if (action !== 'init') {
|
|
148
|
+
if (this.initPromise) {
|
|
149
|
+
await this.initPromise;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
throw new Error('[MoltenDb] You must call db.init() before querying the database.');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// 2. Generate a unique ID
|
|
143
156
|
const id = (typeof crypto !== 'undefined' && crypto.randomUUID)
|
|
144
157
|
? crypto.randomUUID()
|
|
145
158
|
: Math.random().toString(36).substring(2, 9);
|
|
146
159
|
return new Promise((resolve, reject) => {
|
|
147
160
|
const successHandler = (res) => resolve(mapToObj(res));
|
|
161
|
+
// 3. We are now GUARANTEED that isLeader, worker, and bc are accurately set
|
|
148
162
|
if (this.isLeader && this.worker) {
|
|
149
163
|
this.pendingRequests.set(id, { resolve: successHandler, reject });
|
|
150
164
|
this.worker.postMessage({ id, action, ...payload });
|
|
151
165
|
}
|
|
152
166
|
else {
|
|
167
|
+
// Follower routing via BroadcastChannel
|
|
153
168
|
const timer = setTimeout(() => {
|
|
154
169
|
if (this.pendingRequests.has(id)) {
|
|
155
170
|
this.pendingRequests.delete(id);
|
|
156
|
-
reject(new Error(`[MoltenDb] Request "${action}" timed out.`));
|
|
171
|
+
reject(new Error(`[MoltenDb] Request "${action}" timed out after 10s.`));
|
|
157
172
|
}
|
|
158
173
|
}, 10000);
|
|
159
174
|
this.pendingRequests.set(id, {
|
|
@@ -163,8 +178,7 @@ export class MoltenDb {
|
|
|
163
178
|
this.bc.postMessage({ type: 'query', id, action, payload });
|
|
164
179
|
}
|
|
165
180
|
});
|
|
166
|
-
}
|
|
167
|
-
// ── Convenience CRUD helpers ───────────────────────────────────────────────
|
|
181
|
+
} // ── Convenience CRUD helpers ───────────────────────────────────────────────
|
|
168
182
|
async set(collection, key, value) {
|
|
169
183
|
await this.sendMessage('set', { collection, data: { [key]: value } });
|
|
170
184
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moltendb-web/core",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.5",
|
|
4
4
|
"description": "MoltenDb WASM runtime — the database engine, Web Worker, and main-thread client in one package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Maximilian Both <maximilian.both27@outlook.com>",
|