@topgunbuild/client 0.2.0-alpha → 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 +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +116 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +116 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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
|