@lark-sh/client 0.1.21 → 0.1.23

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
@@ -210,6 +210,46 @@ db.onAuthStateChanged((auth) => {
210
210
  });
211
211
  ```
212
212
 
213
+ ### Lazy Connect
214
+
215
+ You don't need to wait for `connect()` to finish before using the database. Operations called during connection are automatically queued and execute once authenticated:
216
+
217
+ ```typescript
218
+ const db = new LarkDatabase();
219
+
220
+ // Start connecting (don't await)
221
+ const connectPromise = db.connect('project/db', { anonymous: true });
222
+
223
+ // These queue and run after auth completes
224
+ db.ref('users').on('value', callback);
225
+ const writePromise = db.ref('data').set({ hello: 'world' });
226
+
227
+ await connectPromise;
228
+ await writePromise;
229
+ ```
230
+
231
+ ### Volatile Paths
232
+
233
+ Lark supports volatile paths for high-frequency data like player positions or cursors. Volatile paths are defined in your server rules with `.volatile: true` and use fire-and-forget writes for lower latency:
234
+
235
+ ```typescript
236
+ // Check which paths are volatile (set by server rules)
237
+ db.volatilePaths; // e.g. ['players/*/position', 'cursors']
238
+
239
+ // Writes to volatile paths resolve immediately (no server ack)
240
+ await db.ref('players/abc/position').set({ x: 10, y: 20 });
241
+
242
+ // Volatile events include a server timestamp for interpolation
243
+ db.ref('players').on('value', (snapshot) => {
244
+ if (snapshot.isVolatile()) {
245
+ const ts = snapshot.getServerTimestamp(); // Unix epoch ms
246
+ // Use deltas between timestamps for smooth interpolation
247
+ }
248
+ });
249
+ ```
250
+
251
+ When using WebTransport, volatile writes are sent via UDP datagrams for even lower latency. The server batches volatile events at 20Hz over WebTransport vs 7Hz over WebSocket.
252
+
213
253
  ## Firebase v8 Compatibility
214
254
 
215
255
  For users migrating from Firebase who need exact v8 API behavior, use the `fb-v8` sub-package:
@@ -368,9 +368,6 @@ function isEventMessage(msg) {
368
368
  function isOnceResponseMessage(msg) {
369
369
  return "oc" in msg;
370
370
  }
371
- function isPingMessage(msg) {
372
- return "o" in msg && msg.o === "pi";
373
- }
374
371
 
375
372
  // src/connection/MessageQueue.ts
376
373
  var MessageQueue = class {
@@ -1665,15 +1662,14 @@ var SubscriptionManager = class {
1665
1662
  }
1666
1663
  /**
1667
1664
  * Handle an incoming event message from the server.
1668
- * Server sends 'put', 'patch', or 'vb' (volatile batch) events; we generate child_* events client-side.
1665
+ * Server sends 'put' or 'patch' events; we generate child_* events client-side.
1666
+ * Volatile events use `x: true` flag on patch events.
1669
1667
  */
1670
1668
  handleEvent(message) {
1671
1669
  if (message.ev === "put") {
1672
1670
  this.handlePutEvent(message);
1673
1671
  } else if (message.ev === "patch") {
1674
1672
  this.handlePatchEvent(message);
1675
- } else if (message.ev === "vb") {
1676
- this.handleVolatileBatchEvent(message);
1677
1673
  } else {
1678
1674
  console.warn("Unknown event type:", message.ev);
1679
1675
  }
@@ -1746,28 +1742,6 @@ var SubscriptionManager = class {
1746
1742
  this.applyServerUpdateToView(view, updates, isVolatile, serverTimestamp);
1747
1743
  }
1748
1744
  }
1749
- /**
1750
- * Handle a 'vb' (volatile batch) event - batched volatile updates across subscriptions.
1751
- * Server batches volatile events in 50ms intervals to reduce message overhead.
1752
- * Format: { ev: 'vb', b: { subscriptionPath: { relativePath: value } }, ts: timestamp }
1753
- * Dispatches to ALL Views at each subscription path.
1754
- */
1755
- handleVolatileBatchEvent(message) {
1756
- const batch = message.b;
1757
- const serverTimestamp = message.ts;
1758
- if (!batch) return;
1759
- for (const [subscriptionPath, updates] of Object.entries(batch)) {
1760
- const views = this.getViewsAtPath(subscriptionPath);
1761
- if (views.length === 0) continue;
1762
- const updatesList = [];
1763
- for (const [relativePath, value] of Object.entries(updates)) {
1764
- updatesList.push({ relativePath, value });
1765
- }
1766
- for (const view of views) {
1767
- this.applyServerUpdateToView(view, updatesList, true, serverTimestamp);
1768
- }
1769
- }
1770
- }
1771
1745
  /**
1772
1746
  * Detect and fire child_moved events for children that changed position OR sort value.
1773
1747
  *
@@ -4233,7 +4207,7 @@ var ServerValue = {
4233
4207
  */
4234
4208
  increment: (delta) => ({ ".sv": { increment: delta } })
4235
4209
  };
4236
- var LarkDatabase = class {
4210
+ var _LarkDatabase = class _LarkDatabase {
4237
4211
  /**
4238
4212
  * Create a new LarkDatabase instance.
4239
4213
  *
@@ -4276,6 +4250,7 @@ var LarkDatabase = class {
4276
4250
  // Connection promise - shared by connect() and lazy operations
4277
4251
  this._connectionPromise = null;
4278
4252
  this._serverTimeOffset = 0;
4253
+ this._pingInterval = null;
4279
4254
  this.messageQueue = new MessageQueue();
4280
4255
  this.subscriptionManager = new SubscriptionManager();
4281
4256
  this.pendingWrites = new PendingWriteManager();
@@ -4473,6 +4448,7 @@ var LarkDatabase = class {
4473
4448
  };
4474
4449
  this._state = "authenticated";
4475
4450
  this._reconnectAttempt = 0;
4451
+ this.startPingInterval();
4476
4452
  this.fireConnectionStateChange();
4477
4453
  if (isReconnect) {
4478
4454
  await this.restoreAfterReconnect();
@@ -4562,6 +4538,7 @@ var LarkDatabase = class {
4562
4538
  */
4563
4539
  cleanupFull() {
4564
4540
  const wasAuthenticated = this._state === "authenticated";
4541
+ this.stopPingInterval();
4565
4542
  this.transport?.close();
4566
4543
  this.transport = null;
4567
4544
  this._state = "disconnected";
@@ -4584,6 +4561,7 @@ var LarkDatabase = class {
4584
4561
  * Used for unexpected disconnect.
4585
4562
  */
4586
4563
  cleanupForReconnect() {
4564
+ this.stopPingInterval();
4587
4565
  this.transport?.close();
4588
4566
  this.transport = null;
4589
4567
  this._auth = null;
@@ -4591,6 +4569,21 @@ var LarkDatabase = class {
4591
4569
  this.messageQueue.rejectAll(new Error("Connection closed"));
4592
4570
  this.fireConnectionStateChange();
4593
4571
  }
4572
+ startPingInterval() {
4573
+ this.stopPingInterval();
4574
+ this._pingInterval = _LarkDatabase._realSetInterval(() => {
4575
+ this.transport?.send(JSON.stringify({ o: "pi" }));
4576
+ }, 1e4);
4577
+ if (typeof this._pingInterval === "object" && "unref" in this._pingInterval) {
4578
+ this._pingInterval.unref();
4579
+ }
4580
+ }
4581
+ stopPingInterval() {
4582
+ if (this._pingInterval) {
4583
+ _LarkDatabase._realClearInterval(this._pingInterval);
4584
+ this._pingInterval = null;
4585
+ }
4586
+ }
4594
4587
  // ============================================
4595
4588
  // .info Path Handling
4596
4589
  // ============================================
@@ -4973,10 +4966,6 @@ var LarkDatabase = class {
4973
4966
  if (process.env.LARK_DEBUG) {
4974
4967
  console.log("[LARK] <<< SERVER:", JSON.stringify(message, null, 2));
4975
4968
  }
4976
- if (isPingMessage(message)) {
4977
- this.transport?.send(JSON.stringify({ o: "po" }));
4978
- return;
4979
- }
4980
4969
  if (isAckMessage(message)) {
4981
4970
  this.pendingWrites.onAck(message.a);
4982
4971
  this.subscriptionManager.clearPendingWrite(message.a);
@@ -5330,7 +5319,11 @@ var LarkDatabase = class {
5330
5319
  * Server values that are resolved by the server when a write is committed.
5331
5320
  * Alias for the exported ServerValue object.
5332
5321
  */
5333
- LarkDatabase.ServerValue = ServerValue;
5322
+ _LarkDatabase.ServerValue = ServerValue;
5323
+ // Use real timers for ping so fake timers in tests don't cause infinite loops
5324
+ _LarkDatabase._realSetInterval = globalThis.setInterval;
5325
+ _LarkDatabase._realClearInterval = globalThis.clearInterval;
5326
+ var LarkDatabase = _LarkDatabase;
5334
5327
 
5335
5328
  export {
5336
5329
  LarkError,
@@ -5344,4 +5337,4 @@ export {
5344
5337
  ServerValue,
5345
5338
  LarkDatabase
5346
5339
  };
5347
- //# sourceMappingURL=chunk-HHBHX2EM.mjs.map
5340
+ //# sourceMappingURL=chunk-3MBKCYWV.mjs.map