@moltendb-web/core 1.0.0-rc.4 → 1.0.0-rc.6

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/README.md CHANGED
@@ -365,7 +365,7 @@ This monorepo contains the following packages:
365
365
  - [x] **Automatic Compaction:** Log compacts automatically at 500 entries or 5 MB — **stabilised in RC1**.
366
366
  - [x] **Rich Test Suite:** 50 unit, integration, and stress tests via Vitest — **stabilised in RC1**.
367
367
  - [ ] **React Adapter:** Official `@moltendb-web/react` package with `useQuery` hooks and real-time context providers.
368
- - [ ] **Angular Adapter:** Official `@moltendb-web/angular` package featuring RxJS observables and Signal-based data fetching.
368
+ - [x] **Angular Adapter:** Official `@moltendb-web/angular` package featuring Signal-based data fetching.
369
369
  - [ ] **Delta Sync:** Automatic two-way sync with the MoltenDb Rust server.
370
370
  - [ ] **Data Encryption:** Transparent encryption-at-rest using hardware-backed keys (Web Crypto API).
371
371
  - [ ] **Analytics Functionality:** Run complex analytics queries straight in the browser without blocking the UI.
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
- initialized = false;
43
- async init() {
44
- if (this.initialized)
45
- return;
46
- this.initialized = true;
47
- this.bc = new BroadcastChannel(`moltendb_channel_${this.dbName}`);
48
- return new Promise((resolveInit, rejectInit) => {
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
- // Generate a unique ID (random fallback for test environments without crypto.randomUUID)
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.4",
3
+ "version": "1.0.0-rc.6",
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>",