@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 +40 -0
- package/dist/{chunk-HHBHX2EM.mjs → chunk-3MBKCYWV.mjs} +28 -35
- package/dist/chunk-3MBKCYWV.mjs.map +1 -0
- package/dist/fb-v8/index.js +27 -34
- package/dist/fb-v8/index.js.map +1 -1
- package/dist/fb-v8/index.mjs +1 -1
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +27 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-HHBHX2EM.mjs.map +0 -1
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'
|
|
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
|
|
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
|
-
|
|
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-
|
|
5340
|
+
//# sourceMappingURL=chunk-3MBKCYWV.mjs.map
|