@lark-sh/client 0.1.3 → 0.1.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.mts CHANGED
@@ -28,13 +28,20 @@ declare class OnDisconnect {
28
28
  setWithPriority(value: unknown, priority: number | string): Promise<void>;
29
29
  }
30
30
 
31
- type EventType = 'value' | 'child_added' | 'child_changed' | 'child_removed';
31
+ type EventType = 'value' | 'child_added' | 'child_changed' | 'child_removed' | 'child_moved';
32
32
 
33
33
  /**
34
34
  * DatabaseReference - a reference to a specific path in the database.
35
35
  * Immutable - navigation returns new references.
36
36
  */
37
37
 
38
+ /** Result of a transaction operation */
39
+ interface TransactionResult {
40
+ /** Whether the transaction was committed (always true on success) */
41
+ committed: boolean;
42
+ /** Snapshot of the data after the transaction */
43
+ snapshot: DataSnapshot;
44
+ }
38
45
  type SnapshotCallback$1 = (snapshot: DataSnapshot, previousChildKey?: string | null) => void;
39
46
  interface QueryState {
40
47
  orderBy?: 'key' | 'priority' | 'child' | 'value';
@@ -85,6 +92,22 @@ declare class DatabaseReference {
85
92
  set(value: unknown): Promise<void>;
86
93
  /**
87
94
  * Update specific children at this location without overwriting other children.
95
+ *
96
+ * Also supports Firebase-style multi-path updates when keys look like paths
97
+ * (start with '/'). In this mode, each path is written atomically as a transaction.
98
+ *
99
+ * @example
100
+ * ```javascript
101
+ * // Normal update (merge at single path)
102
+ * await ref.update({ score: 10, name: 'Riley' });
103
+ *
104
+ * // Multi-path update (atomic writes to multiple paths)
105
+ * await db.ref().update({
106
+ * '/users/alice/score': 100,
107
+ * '/users/bob/score': 200,
108
+ * '/leaderboard/alice': null // null = delete
109
+ * });
110
+ * ```
88
111
  */
89
112
  update(values: Record<string, unknown>): Promise<void>;
90
113
  /**
@@ -109,6 +132,29 @@ declare class DatabaseReference {
109
132
  * Set the priority of the data at this location.
110
133
  */
111
134
  setPriority(priority: number | string): Promise<void>;
135
+ /**
136
+ * Atomically modify the data at this location using optimistic concurrency.
137
+ *
138
+ * The update function receives the current value and should return the new
139
+ * value. If the data changed on the server before the write could be
140
+ * committed, the function is called again with the new value, and the
141
+ * process repeats until successful or the maximum retries are exceeded.
142
+ *
143
+ * @example
144
+ * ```javascript
145
+ * // Increment a counter atomically
146
+ * const result = await ref.transaction(currentValue => {
147
+ * return (currentValue || 0) + 1;
148
+ * });
149
+ * console.log('New value:', result.snapshot.val());
150
+ * ```
151
+ *
152
+ * @param updateFunction - Function that receives current value and returns new value.
153
+ * Return undefined to abort the transaction.
154
+ * @param maxRetries - Maximum number of retries (default: 25)
155
+ * @returns TransactionResult with committed status and final snapshot
156
+ */
157
+ transaction(updateFunction: (currentValue: unknown) => unknown, maxRetries?: number): Promise<TransactionResult>;
112
158
  /**
113
159
  * Read the data at this location once.
114
160
  */
@@ -138,12 +184,10 @@ declare class DatabaseReference {
138
184
  orderByPriority(): DatabaseReference;
139
185
  /**
140
186
  * Order results by a child key.
141
- * NOTE: Phase 2 - not yet implemented on server.
142
187
  */
143
188
  orderByChild(path: string): DatabaseReference;
144
189
  /**
145
190
  * Order results by value.
146
- * NOTE: Phase 2 - not yet implemented on server.
147
191
  */
148
192
  orderByValue(): DatabaseReference;
149
193
  /**
@@ -156,17 +200,14 @@ declare class DatabaseReference {
156
200
  limitToLast(limit: number): DatabaseReference;
157
201
  /**
158
202
  * Start at a specific value/key.
159
- * NOTE: Phase 2 - not yet implemented on server.
160
203
  */
161
204
  startAt(value: unknown, key?: string): DatabaseReference;
162
205
  /**
163
206
  * End at a specific value/key.
164
- * NOTE: Phase 2 - not yet implemented on server.
165
207
  */
166
208
  endAt(value: unknown, key?: string): DatabaseReference;
167
209
  /**
168
210
  * Filter to items equal to a specific value.
169
- * NOTE: Phase 2 - not yet implemented on server.
170
211
  */
171
212
  equalTo(value: unknown, key?: string): DatabaseReference;
172
213
  /**
@@ -191,9 +232,11 @@ declare class DataSnapshot {
191
232
  private readonly _db;
192
233
  private readonly _volatile;
193
234
  private readonly _priority;
235
+ private readonly _serverTimestamp;
194
236
  constructor(data: unknown, path: string, db: LarkDatabase, options?: {
195
237
  volatile?: boolean;
196
238
  priority?: number | string | null;
239
+ serverTimestamp?: number | null;
197
240
  });
198
241
  /**
199
242
  * Get a DatabaseReference for the location of this snapshot.
@@ -240,6 +283,13 @@ declare class DataSnapshot {
240
283
  * This is a Lark extension not present in Firebase.
241
284
  */
242
285
  isVolatile(): boolean;
286
+ /**
287
+ * Get the server timestamp for this snapshot (milliseconds since Unix epoch).
288
+ * Only present on volatile value events. Use deltas between timestamps for
289
+ * interpolation rather than absolute times to avoid clock sync issues.
290
+ * This is a Lark extension not present in Firebase.
291
+ */
292
+ getServerTimestamp(): number | null;
243
293
  /**
244
294
  * Export the snapshot data as JSON (alias for val()).
245
295
  */
@@ -247,16 +297,50 @@ declare class DataSnapshot {
247
297
  }
248
298
 
249
299
  interface QueryParams {
250
- ob?: 'k' | 'p';
251
- lf?: number;
252
- ll?: number;
300
+ orderBy?: 'key' | 'priority' | 'child' | 'value';
301
+ orderByChild?: string;
302
+ limitToFirst?: number;
303
+ limitToLast?: number;
304
+ startAt?: unknown;
305
+ startAtKey?: string;
306
+ endAt?: unknown;
307
+ endAtKey?: string;
308
+ equalTo?: unknown;
309
+ equalToKey?: string;
310
+ }
311
+ interface TxSetOp {
312
+ o: 's';
313
+ p: string;
314
+ v: unknown;
315
+ }
316
+ interface TxUpdateOp {
317
+ o: 'u';
318
+ p: string;
319
+ v: Record<string, unknown>;
320
+ }
321
+ interface TxDeleteOp {
322
+ o: 'd';
323
+ p: string;
324
+ }
325
+ interface TxConditionOp {
326
+ o: 'c';
327
+ p: string;
328
+ v: unknown;
329
+ }
330
+ type TxOperation = TxSetOp | TxUpdateOp | TxDeleteOp | TxConditionOp;
331
+ interface MoveEntry {
332
+ k: string;
333
+ ak: string;
253
334
  }
254
335
 
255
336
  /**
256
337
  * SubscriptionManager - tracks active subscriptions and routes events to callbacks.
257
338
  *
258
- * Also manages a local data cache that is populated by subscription events.
259
- * The cache only contains "live" data from active subscriptions.
339
+ * Handles the new delta-based protocol where server sends 'put' and 'patch' events.
340
+ * Client generates child_added, child_changed, child_removed, child_moved events locally.
341
+ *
342
+ * Supports ordered queries: tracks child order from server's `k` (orderedKeys) and `ak` (afterKey)
343
+ * fields, and fires child_moved when positions change via `mv` (moves) array.
260
344
  */
261
345
 
262
346
  type SnapshotCallback = (snapshot: DataSnapshot, previousChildKey?: string | null) => void;
@@ -278,14 +362,43 @@ interface AuthInfo {
278
362
  provider: string;
279
363
  token: Record<string, unknown>;
280
364
  }
365
+ /** A set operation in a transaction */
366
+ interface TransactionSetOp {
367
+ op: 'set';
368
+ path: string;
369
+ value: unknown;
370
+ }
371
+ /** An update (merge) operation in a transaction */
372
+ interface TransactionUpdateOp {
373
+ op: 'update';
374
+ path: string;
375
+ value: Record<string, unknown>;
376
+ }
377
+ /** A delete operation in a transaction */
378
+ interface TransactionDeleteOp {
379
+ op: 'delete';
380
+ path: string;
381
+ }
382
+ /** A condition check in a transaction (for compare-and-swap) */
383
+ interface TransactionConditionOp {
384
+ op: 'condition';
385
+ path: string;
386
+ value: unknown;
387
+ }
388
+ /** A single operation in a transaction (array syntax) */
389
+ type TransactionOp = TransactionSetOp | TransactionUpdateOp | TransactionDeleteOp | TransactionConditionOp;
390
+ /** Object syntax for transactions: path -> value (null = delete) */
391
+ type TransactionObject = Record<string, unknown>;
281
392
  declare class LarkDatabase {
282
393
  private _state;
283
394
  private _auth;
284
395
  private _databaseId;
285
396
  private _coordinatorUrl;
397
+ private _volatilePaths;
286
398
  private ws;
287
399
  private messageQueue;
288
400
  private subscriptionManager;
401
+ private pendingWrites;
289
402
  private connectCallbacks;
290
403
  private disconnectCallbacks;
291
404
  private errorCallbacks;
@@ -302,6 +415,26 @@ declare class LarkDatabase {
302
415
  * @internal Get the base URL for reference toString().
303
416
  */
304
417
  _getBaseUrl(): string;
418
+ /**
419
+ * Get the volatile path patterns from the server.
420
+ * These patterns indicate which paths should use unreliable transport.
421
+ * WebSocket always uses reliable transport, but this is stored for future UDP support.
422
+ */
423
+ get volatilePaths(): string[];
424
+ /**
425
+ * Check if there are any pending writes waiting for acknowledgment.
426
+ * Useful for showing "saving..." indicators in UI.
427
+ */
428
+ hasPendingWrites(): boolean;
429
+ /**
430
+ * Get the number of pending writes waiting for acknowledgment.
431
+ */
432
+ getPendingWriteCount(): number;
433
+ /**
434
+ * Clear all pending writes.
435
+ * Call this if you don't want to retry writes on reconnect.
436
+ */
437
+ clearPendingWrites(): void;
305
438
  /**
306
439
  * Connect to a database.
307
440
  *
@@ -322,6 +455,48 @@ declare class LarkDatabase {
322
455
  * Get a reference to a path in the database.
323
456
  */
324
457
  ref(path?: string): DatabaseReference;
458
+ /**
459
+ * Execute an atomic transaction with multiple operations.
460
+ *
461
+ * Supports two syntaxes:
462
+ *
463
+ * **Object syntax** (like Firebase multi-path update):
464
+ * ```javascript
465
+ * await db.transaction({
466
+ * '/users/alice/name': 'Alice',
467
+ * '/users/alice/score': 100,
468
+ * '/temp/data': null // null = delete
469
+ * });
470
+ * ```
471
+ *
472
+ * **Array syntax** (explicit operations):
473
+ * ```javascript
474
+ * await db.transaction([
475
+ * { op: 'set', path: '/users/alice/name', value: 'Alice' },
476
+ * { op: 'update', path: '/metadata', value: { lastUpdated: '...' } },
477
+ * { op: 'delete', path: '/temp/data' },
478
+ * { op: 'condition', path: '/counter', value: 5 } // CAS check
479
+ * ]);
480
+ * ```
481
+ *
482
+ * All operations are atomic: either all succeed or all fail.
483
+ * Conditions are checked first; if any fail, the transaction is rejected
484
+ * with error code 'condition_failed'.
485
+ */
486
+ transaction(operations: TransactionOp[] | TransactionObject): Promise<void>;
487
+ /**
488
+ * Convert a public TransactionOp to wire format TxOperation.
489
+ */
490
+ private convertToTxOp;
491
+ /**
492
+ * Convert object syntax to wire format TxOperations.
493
+ * Each path becomes a set operation, null values become deletes.
494
+ */
495
+ private convertObjectToTxOps;
496
+ /**
497
+ * @internal Send a transaction to the server.
498
+ */
499
+ _sendTransaction(ops: TxOperation[]): Promise<void>;
325
500
  /**
326
501
  * Register a callback for when connection is established.
327
502
  * Returns an unsubscribe function.
@@ -389,7 +564,7 @@ declare class LarkDatabase {
389
564
  /**
390
565
  * @internal Subscribe to events at a path.
391
566
  */
392
- _subscribe(path: string, eventType: EventType, callback: SnapshotCallback): () => void;
567
+ _subscribe(path: string, eventType: EventType, callback: SnapshotCallback, queryParams?: QueryParams): () => void;
393
568
  /**
394
569
  * @internal Unsubscribe from a specific event type at a path.
395
570
  */
@@ -408,6 +583,67 @@ declare class LarkError extends Error {
408
583
  constructor(code: string, message?: string);
409
584
  }
410
585
 
586
+ /**
587
+ * PendingWriteManager - tracks pending write operations for optimistic updates.
588
+ *
589
+ * Each write operation is assigned a unique operation ID (oid). The manager tracks
590
+ * pending writes until they are acknowledged by the server. On reconnect, pending
591
+ * writes can be retried to ensure data consistency.
592
+ */
593
+ type WriteOperation = 'set' | 'update' | 'delete' | 'push' | 'transaction';
594
+ interface PendingWrite {
595
+ oid: string;
596
+ operation: WriteOperation;
597
+ path: string;
598
+ value?: unknown;
599
+ timestamp: number;
600
+ }
601
+ declare class PendingWriteManager {
602
+ private pending;
603
+ private counter;
604
+ /**
605
+ * Generate a unique operation ID.
606
+ * Format: op_{timestamp}_{counter}_{random}
607
+ */
608
+ generateOid(): string;
609
+ /**
610
+ * Track a new pending write operation.
611
+ */
612
+ trackWrite(oid: string, operation: WriteOperation, path: string, value?: unknown): void;
613
+ /**
614
+ * Called when a write is acknowledged by the server.
615
+ * Removes the write from pending tracking.
616
+ */
617
+ onAck(oid: string): boolean;
618
+ /**
619
+ * Called when a write is rejected by the server.
620
+ * Removes the write from pending tracking.
621
+ */
622
+ onNack(oid: string): boolean;
623
+ /**
624
+ * Get all pending writes for retry on reconnect.
625
+ * Returns writes in order of their timestamps (oldest first).
626
+ */
627
+ getPendingWrites(): PendingWrite[];
628
+ /**
629
+ * Check if a specific operation is still pending.
630
+ */
631
+ isPending(oid: string): boolean;
632
+ /**
633
+ * Get the number of pending writes.
634
+ */
635
+ get pendingCount(): number;
636
+ /**
637
+ * Clear all pending writes (e.g., on disconnect when not retrying).
638
+ */
639
+ clear(): void;
640
+ /**
641
+ * Remove writes older than a specified age (in milliseconds).
642
+ * Useful for cleaning up stale pending writes that may never be acked.
643
+ */
644
+ removeStale(maxAgeMs: number): number;
645
+ }
646
+
411
647
  /**
412
648
  * Push ID generation - Firebase-compatible chronologically-sortable IDs.
413
649
  *
@@ -424,4 +660,24 @@ declare class LarkError extends Error {
424
660
  */
425
661
  declare function generatePushId(): string;
426
662
 
427
- export { type AuthInfo, type ConnectOptions, DataSnapshot, DatabaseReference, type EventType, LarkDatabase, LarkError, OnDisconnect, type QueryState, type SnapshotCallback$1 as SnapshotCallback, generatePushId };
663
+ /**
664
+ * Volatile path matching utilities.
665
+ *
666
+ * Volatile paths are patterns where a wildcard matches exactly one path segment.
667
+ * These paths use unreliable transport for better performance (UDP when available).
668
+ */
669
+ /**
670
+ * Check if a path matches any of the volatile path patterns.
671
+ *
672
+ * Pattern matching rules:
673
+ * - Wildcard matches exactly one path segment (not zero, not multiple)
674
+ * - Patterns and paths are compared segment by segment
675
+ * - Path must have the same number of segments as the pattern
676
+ *
677
+ * @param path - The path to check (e.g., "/players/abc/position")
678
+ * @param patterns - Array of volatile patterns with wildcards
679
+ * @returns true if the path matches any pattern
680
+ */
681
+ declare function isVolatilePath(path: string, patterns: string[] | null | undefined): boolean;
682
+
683
+ export { type AuthInfo, type ConnectOptions, DataSnapshot, DatabaseReference, type EventType, LarkDatabase, LarkError, type MoveEntry, OnDisconnect, type PendingWrite, PendingWriteManager, type QueryParams, type QueryState, type SnapshotCallback$1 as SnapshotCallback, type TransactionConditionOp, type TransactionDeleteOp, type TransactionObject, type TransactionOp, type TransactionResult, type TransactionSetOp, type TransactionUpdateOp, type WriteOperation, generatePushId, isVolatilePath };