@topgunbuild/client 0.2.0 → 0.2.1

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
@@ -357,6 +357,7 @@ declare class SyncEngine {
357
357
  private waitingForCapacity;
358
358
  private highWaterMarkEmitted;
359
359
  private backpressureListeners;
360
+ private pendingWriteConcernPromises;
360
361
  constructor(config: SyncEngineConfig);
361
362
  /**
362
363
  * Get the current connection state
@@ -424,6 +425,11 @@ declare class SyncEngine {
424
425
  releaseLock(name: string, requestId: string, fencingToken: number): Promise<boolean>;
425
426
  private handleServerMessage;
426
427
  getHLC(): HLC;
428
+ /**
429
+ * Helper method to apply a single server event to the local map.
430
+ * Used by both SERVER_EVENT and SERVER_BATCH_EVENT handlers.
431
+ */
432
+ private applyServerEvent;
427
433
  /**
428
434
  * Closes the WebSocket connection and cleans up resources.
429
435
  */
@@ -514,6 +520,26 @@ declare class SyncEngine {
514
520
  * Drop the oldest pending operation (used by 'drop-oldest' strategy).
515
521
  */
516
522
  private dropOldestOp;
523
+ /**
524
+ * Register a pending Write Concern promise for an operation.
525
+ * The promise will be resolved when the server sends an ACK with the operation result.
526
+ *
527
+ * @param opId - Operation ID
528
+ * @param timeout - Timeout in ms (default: 5000)
529
+ * @returns Promise that resolves with the Write Concern result
530
+ */
531
+ registerWriteConcernPromise(opId: string, timeout?: number): Promise<any>;
532
+ /**
533
+ * Resolve a pending Write Concern promise with the server result.
534
+ *
535
+ * @param opId - Operation ID
536
+ * @param result - Result from server ACK
537
+ */
538
+ private resolveWriteConcernPromise;
539
+ /**
540
+ * Cancel all pending Write Concern promises (e.g., on disconnect).
541
+ */
542
+ private cancelAllWriteConcernPromises;
517
543
  }
518
544
 
519
545
  interface ILock {
package/dist/index.d.ts CHANGED
@@ -357,6 +357,7 @@ declare class SyncEngine {
357
357
  private waitingForCapacity;
358
358
  private highWaterMarkEmitted;
359
359
  private backpressureListeners;
360
+ private pendingWriteConcernPromises;
360
361
  constructor(config: SyncEngineConfig);
361
362
  /**
362
363
  * Get the current connection state
@@ -424,6 +425,11 @@ declare class SyncEngine {
424
425
  releaseLock(name: string, requestId: string, fencingToken: number): Promise<boolean>;
425
426
  private handleServerMessage;
426
427
  getHLC(): HLC;
428
+ /**
429
+ * Helper method to apply a single server event to the local map.
430
+ * Used by both SERVER_EVENT and SERVER_BATCH_EVENT handlers.
431
+ */
432
+ private applyServerEvent;
427
433
  /**
428
434
  * Closes the WebSocket connection and cleans up resources.
429
435
  */
@@ -514,6 +520,26 @@ declare class SyncEngine {
514
520
  * Drop the oldest pending operation (used by 'drop-oldest' strategy).
515
521
  */
516
522
  private dropOldestOp;
523
+ /**
524
+ * Register a pending Write Concern promise for an operation.
525
+ * The promise will be resolved when the server sends an ACK with the operation result.
526
+ *
527
+ * @param opId - Operation ID
528
+ * @param timeout - Timeout in ms (default: 5000)
529
+ * @returns Promise that resolves with the Write Concern result
530
+ */
531
+ registerWriteConcernPromise(opId: string, timeout?: number): Promise<any>;
532
+ /**
533
+ * Resolve a pending Write Concern promise with the server result.
534
+ *
535
+ * @param opId - Operation ID
536
+ * @param result - Result from server ACK
537
+ */
538
+ private resolveWriteConcernPromise;
539
+ /**
540
+ * Cancel all pending Write Concern promises (e.g., on disconnect).
541
+ */
542
+ private cancelAllWriteConcernPromises;
517
543
  }
518
544
 
519
545
  interface ILock {
package/dist/index.js CHANGED
@@ -283,6 +283,8 @@ var SyncEngine = class {
283
283
  this.waitingForCapacity = [];
284
284
  this.highWaterMarkEmitted = false;
285
285
  this.backpressureListeners = /* @__PURE__ */ new Map();
286
+ // Write Concern state (Phase 5.01)
287
+ this.pendingWriteConcernPromises = /* @__PURE__ */ new Map();
286
288
  this.nodeId = config.nodeId;
287
289
  this.serverUrl = config.serverUrl;
288
290
  this.storageAdapter = config.storageAdapter;
@@ -684,6 +686,22 @@ var SyncEngine = class {
684
686
  }
685
687
  async handleServerMessage(message) {
686
688
  switch (message.type) {
689
+ case "BATCH": {
690
+ const batchData = message.data;
691
+ const view = new DataView(batchData.buffer, batchData.byteOffset, batchData.byteLength);
692
+ let offset = 0;
693
+ const count = view.getUint32(offset, true);
694
+ offset += 4;
695
+ for (let i = 0; i < count; i++) {
696
+ const msgLen = view.getUint32(offset, true);
697
+ offset += 4;
698
+ const msgData = batchData.slice(offset, offset + msgLen);
699
+ offset += msgLen;
700
+ const innerMsg = (0, import_core.deserialize)(msgData);
701
+ await this.handleServerMessage(innerMsg);
702
+ }
703
+ break;
704
+ }
687
705
  case "AUTH_REQUIRED":
688
706
  this.sendAuth();
689
707
  break;
@@ -715,8 +733,18 @@ var SyncEngine = class {
715
733
  this.authToken = null;
716
734
  break;
717
735
  case "OP_ACK": {
718
- const { lastId } = message.payload;
719
- logger.info({ lastId }, "Received ACK for ops");
736
+ const { lastId, achievedLevel, results } = message.payload;
737
+ logger.info({ lastId, achievedLevel, hasResults: !!results }, "Received ACK for ops");
738
+ if (results && Array.isArray(results)) {
739
+ for (const result of results) {
740
+ const op = this.opLog.find((o) => o.id === result.opId);
741
+ if (op && !op.synced) {
742
+ op.synced = true;
743
+ logger.debug({ opId: result.opId, achievedLevel: result.achievedLevel, success: result.success }, "Op ACK with Write Concern");
744
+ }
745
+ this.resolveWriteConcernPromise(result.opId, result);
746
+ }
747
+ }
720
748
  let maxSyncedId = -1;
721
749
  let ackedCount = 0;
722
750
  this.opLog.forEach((op) => {
@@ -777,18 +805,20 @@ var SyncEngine = class {
777
805
  }
778
806
  case "SERVER_EVENT": {
779
807
  const { mapName, eventType, key, record, orRecord, orTag } = message.payload;
780
- const localMap = this.maps.get(mapName);
781
- if (localMap) {
782
- if (localMap instanceof import_core.LWWMap && record) {
783
- localMap.merge(key, record);
784
- await this.storageAdapter.put(`${mapName}:${key}`, record);
785
- } else if (localMap instanceof import_core.ORMap) {
786
- if (eventType === "OR_ADD" && orRecord) {
787
- localMap.apply(key, orRecord);
788
- } else if (eventType === "OR_REMOVE" && orTag) {
789
- localMap.applyTombstone(orTag);
790
- }
791
- }
808
+ await this.applyServerEvent(mapName, eventType, key, record, orRecord, orTag);
809
+ break;
810
+ }
811
+ case "SERVER_BATCH_EVENT": {
812
+ const { events } = message.payload;
813
+ for (const event of events) {
814
+ await this.applyServerEvent(
815
+ event.mapName,
816
+ event.eventType,
817
+ event.key,
818
+ event.record,
819
+ event.orRecord,
820
+ event.orTag
821
+ );
792
822
  }
793
823
  break;
794
824
  }
@@ -991,6 +1021,25 @@ var SyncEngine = class {
991
1021
  getHLC() {
992
1022
  return this.hlc;
993
1023
  }
1024
+ /**
1025
+ * Helper method to apply a single server event to the local map.
1026
+ * Used by both SERVER_EVENT and SERVER_BATCH_EVENT handlers.
1027
+ */
1028
+ async applyServerEvent(mapName, eventType, key, record, orRecord, orTag) {
1029
+ const localMap = this.maps.get(mapName);
1030
+ if (localMap) {
1031
+ if (localMap instanceof import_core.LWWMap && record) {
1032
+ localMap.merge(key, record);
1033
+ await this.storageAdapter.put(`${mapName}:${key}`, record);
1034
+ } else if (localMap instanceof import_core.ORMap) {
1035
+ if (eventType === "OR_ADD" && orRecord) {
1036
+ localMap.apply(key, orRecord);
1037
+ } else if (eventType === "OR_REMOVE" && orTag) {
1038
+ localMap.applyTombstone(orTag);
1039
+ }
1040
+ }
1041
+ }
1042
+ }
994
1043
  /**
995
1044
  * Closes the WebSocket connection and cleans up resources.
996
1045
  */
@@ -1005,6 +1054,7 @@ var SyncEngine = class {
1005
1054
  this.websocket.close();
1006
1055
  this.websocket = null;
1007
1056
  }
1057
+ this.cancelAllWriteConcernPromises(new Error("SyncEngine closed"));
1008
1058
  this.stateMachine.transition("DISCONNECTED" /* DISCONNECTED */);
1009
1059
  logger.info("SyncEngine closed");
1010
1060
  }
@@ -1325,6 +1375,58 @@ var SyncEngine = class {
1325
1375
  });
1326
1376
  }
1327
1377
  }
1378
+ // ============================================
1379
+ // Write Concern Methods (Phase 5.01)
1380
+ // ============================================
1381
+ /**
1382
+ * Register a pending Write Concern promise for an operation.
1383
+ * The promise will be resolved when the server sends an ACK with the operation result.
1384
+ *
1385
+ * @param opId - Operation ID
1386
+ * @param timeout - Timeout in ms (default: 5000)
1387
+ * @returns Promise that resolves with the Write Concern result
1388
+ */
1389
+ registerWriteConcernPromise(opId, timeout = 5e3) {
1390
+ return new Promise((resolve, reject) => {
1391
+ const timeoutHandle = setTimeout(() => {
1392
+ this.pendingWriteConcernPromises.delete(opId);
1393
+ reject(new Error(`Write Concern timeout for operation ${opId}`));
1394
+ }, timeout);
1395
+ this.pendingWriteConcernPromises.set(opId, {
1396
+ resolve,
1397
+ reject,
1398
+ timeoutHandle
1399
+ });
1400
+ });
1401
+ }
1402
+ /**
1403
+ * Resolve a pending Write Concern promise with the server result.
1404
+ *
1405
+ * @param opId - Operation ID
1406
+ * @param result - Result from server ACK
1407
+ */
1408
+ resolveWriteConcernPromise(opId, result) {
1409
+ const pending = this.pendingWriteConcernPromises.get(opId);
1410
+ if (pending) {
1411
+ if (pending.timeoutHandle) {
1412
+ clearTimeout(pending.timeoutHandle);
1413
+ }
1414
+ pending.resolve(result);
1415
+ this.pendingWriteConcernPromises.delete(opId);
1416
+ }
1417
+ }
1418
+ /**
1419
+ * Cancel all pending Write Concern promises (e.g., on disconnect).
1420
+ */
1421
+ cancelAllWriteConcernPromises(error) {
1422
+ for (const [opId, pending] of this.pendingWriteConcernPromises.entries()) {
1423
+ if (pending.timeoutHandle) {
1424
+ clearTimeout(pending.timeoutHandle);
1425
+ }
1426
+ pending.reject(error);
1427
+ }
1428
+ this.pendingWriteConcernPromises.clear();
1429
+ }
1328
1430
  };
1329
1431
 
1330
1432
  // src/TopGunClient.ts