@synclib-io/sync 0.2.1 → 0.2.2

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
@@ -552,6 +552,24 @@ declare class SyncClient {
552
552
  private serverHashColumns?;
553
553
  private isInitialized;
554
554
  private hasConnectedOnce;
555
+ /**
556
+ * True while a call to [connect] is in progress. Used by
557
+ * [handleStateChange] to skip its auto-rejoin of channels when an
558
+ * explicit caller is already going to call [joinChannels] itself.
559
+ *
560
+ * Without this guard, every reconnect after the first one races two
561
+ * `joinChannels()` invocations against each other: one from `connect()`'s
562
+ * `await this.joinChannels()` and one from `handleStateChange`'s fire-and-
563
+ * forget call. `WebSocketManager.joinChannel` (line 192-196) EVICTS any
564
+ * existing channel for the same topic before creating a new one, so the
565
+ * second call disposes the first call's channel — its push completer
566
+ * never sees the reply, the await hangs until the phoenix-js push
567
+ * timeout fires. Symptom server-side: two `JOINED <topic>` events ~50ms
568
+ * apart with the same device_id; client-side: 30s wait then timeout
569
+ * even though the channel is functionally joined via the surviving
570
+ * second call. Mirror of the Dart fix in synclib_sync/sync_client.dart.
571
+ */
572
+ private explicitConnectInProgress;
555
573
  /**
556
574
  * Per-install identifier minted on first DB open and persisted in
557
575
  * `_synclib_metadata`. Stable for the life of the SQLite/IndexedDB file.
package/dist/index.d.ts CHANGED
@@ -552,6 +552,24 @@ declare class SyncClient {
552
552
  private serverHashColumns?;
553
553
  private isInitialized;
554
554
  private hasConnectedOnce;
555
+ /**
556
+ * True while a call to [connect] is in progress. Used by
557
+ * [handleStateChange] to skip its auto-rejoin of channels when an
558
+ * explicit caller is already going to call [joinChannels] itself.
559
+ *
560
+ * Without this guard, every reconnect after the first one races two
561
+ * `joinChannels()` invocations against each other: one from `connect()`'s
562
+ * `await this.joinChannels()` and one from `handleStateChange`'s fire-and-
563
+ * forget call. `WebSocketManager.joinChannel` (line 192-196) EVICTS any
564
+ * existing channel for the same topic before creating a new one, so the
565
+ * second call disposes the first call's channel — its push completer
566
+ * never sees the reply, the await hangs until the phoenix-js push
567
+ * timeout fires. Symptom server-side: two `JOINED <topic>` events ~50ms
568
+ * apart with the same device_id; client-side: 30s wait then timeout
569
+ * even though the channel is functionally joined via the surviving
570
+ * second call. Mirror of the Dart fix in synclib_sync/sync_client.dart.
571
+ */
572
+ private explicitConnectInProgress;
555
573
  /**
556
574
  * Per-install identifier minted on first DB open and persisted in
557
575
  * `_synclib_metadata`. Stable for the life of the SQLite/IndexedDB file.
package/dist/index.js CHANGED
@@ -922,6 +922,24 @@ var SyncClient = class {
922
922
  constructor(config) {
923
923
  this.isInitialized = false;
924
924
  this.hasConnectedOnce = false;
925
+ /**
926
+ * True while a call to [connect] is in progress. Used by
927
+ * [handleStateChange] to skip its auto-rejoin of channels when an
928
+ * explicit caller is already going to call [joinChannels] itself.
929
+ *
930
+ * Without this guard, every reconnect after the first one races two
931
+ * `joinChannels()` invocations against each other: one from `connect()`'s
932
+ * `await this.joinChannels()` and one from `handleStateChange`'s fire-and-
933
+ * forget call. `WebSocketManager.joinChannel` (line 192-196) EVICTS any
934
+ * existing channel for the same topic before creating a new one, so the
935
+ * second call disposes the first call's channel — its push completer
936
+ * never sees the reply, the await hangs until the phoenix-js push
937
+ * timeout fires. Symptom server-side: two `JOINED <topic>` events ~50ms
938
+ * apart with the same device_id; client-side: 30s wait then timeout
939
+ * even though the channel is functionally joined via the surviving
940
+ * second call. Mirror of the Dart fix in synclib_sync/sync_client.dart.
941
+ */
942
+ this.explicitConnectInProgress = false;
925
943
  /**
926
944
  * Per-install identifier minted on first DB open and persisted in
927
945
  * `_synclib_metadata`. Stable for the life of the SQLite/IndexedDB file.
@@ -1114,12 +1132,17 @@ var SyncClient = class {
1114
1132
  if (!this.isInitialized) {
1115
1133
  throw new Error("Not initialized. Call initialize() first.");
1116
1134
  }
1117
- await this.ws.connect({
1118
- token,
1119
- client_id: this.config.clientId,
1120
- ...extra || {}
1121
- });
1122
- await this.joinChannels();
1135
+ this.explicitConnectInProgress = true;
1136
+ try {
1137
+ await this.ws.connect({
1138
+ token,
1139
+ client_id: this.config.clientId,
1140
+ ...extra || {}
1141
+ });
1142
+ await this.joinChannels();
1143
+ } finally {
1144
+ this.explicitConnectInProgress = false;
1145
+ }
1123
1146
  }
1124
1147
  /**
1125
1148
  * Update the auth token used on subsequent (re)connects without tearing
@@ -2114,7 +2137,7 @@ var SyncClient = class {
2114
2137
  this.updateSyncState("error" /* ERROR */);
2115
2138
  break;
2116
2139
  }
2117
- if (state === "connected" /* CONNECTED */ && this.hasConnectedOnce) {
2140
+ if (state === "connected" /* CONNECTED */ && this.hasConnectedOnce && !this.explicitConnectInProgress) {
2118
2141
  console.log("Reconnected - rejoining channels");
2119
2142
  this.joinChannels().catch((e) => {
2120
2143
  console.error("Failed to rejoin channels after reconnect:", e);
package/dist/index.mjs CHANGED
@@ -889,6 +889,24 @@ var SyncClient = class {
889
889
  constructor(config) {
890
890
  this.isInitialized = false;
891
891
  this.hasConnectedOnce = false;
892
+ /**
893
+ * True while a call to [connect] is in progress. Used by
894
+ * [handleStateChange] to skip its auto-rejoin of channels when an
895
+ * explicit caller is already going to call [joinChannels] itself.
896
+ *
897
+ * Without this guard, every reconnect after the first one races two
898
+ * `joinChannels()` invocations against each other: one from `connect()`'s
899
+ * `await this.joinChannels()` and one from `handleStateChange`'s fire-and-
900
+ * forget call. `WebSocketManager.joinChannel` (line 192-196) EVICTS any
901
+ * existing channel for the same topic before creating a new one, so the
902
+ * second call disposes the first call's channel — its push completer
903
+ * never sees the reply, the await hangs until the phoenix-js push
904
+ * timeout fires. Symptom server-side: two `JOINED <topic>` events ~50ms
905
+ * apart with the same device_id; client-side: 30s wait then timeout
906
+ * even though the channel is functionally joined via the surviving
907
+ * second call. Mirror of the Dart fix in synclib_sync/sync_client.dart.
908
+ */
909
+ this.explicitConnectInProgress = false;
892
910
  /**
893
911
  * Per-install identifier minted on first DB open and persisted in
894
912
  * `_synclib_metadata`. Stable for the life of the SQLite/IndexedDB file.
@@ -1081,12 +1099,17 @@ var SyncClient = class {
1081
1099
  if (!this.isInitialized) {
1082
1100
  throw new Error("Not initialized. Call initialize() first.");
1083
1101
  }
1084
- await this.ws.connect({
1085
- token,
1086
- client_id: this.config.clientId,
1087
- ...extra || {}
1088
- });
1089
- await this.joinChannels();
1102
+ this.explicitConnectInProgress = true;
1103
+ try {
1104
+ await this.ws.connect({
1105
+ token,
1106
+ client_id: this.config.clientId,
1107
+ ...extra || {}
1108
+ });
1109
+ await this.joinChannels();
1110
+ } finally {
1111
+ this.explicitConnectInProgress = false;
1112
+ }
1090
1113
  }
1091
1114
  /**
1092
1115
  * Update the auth token used on subsequent (re)connects without tearing
@@ -2081,7 +2104,7 @@ var SyncClient = class {
2081
2104
  this.updateSyncState("error" /* ERROR */);
2082
2105
  break;
2083
2106
  }
2084
- if (state === "connected" /* CONNECTED */ && this.hasConnectedOnce) {
2107
+ if (state === "connected" /* CONNECTED */ && this.hasConnectedOnce && !this.explicitConnectInProgress) {
2085
2108
  console.log("Reconnected - rejoining channels");
2086
2109
  this.joinChannels().catch((e) => {
2087
2110
  console.error("Failed to rejoin channels after reconnect:", e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synclib-io/sync",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "TypeScript/JavaScript sync client for coordinating database changes with Elixir Phoenix server",
5
5
  "publishConfig": {
6
6
  "access": "public"