@emeryld/rrroutes-client 2.8.8 → 2.8.10

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.mjs CHANGED
@@ -1924,7 +1924,9 @@ var SocketClient = class {
1924
1924
  this.hbTimer = null;
1925
1925
  // stats
1926
1926
  this.roomCounts = /* @__PURE__ */ new Map();
1927
+ this.roomJoinMeta = /* @__PURE__ */ new Map();
1927
1928
  this.handlerMap = /* @__PURE__ */ new Map();
1929
+ this.hasConnected = false;
1928
1930
  this.events = events;
1929
1931
  this.socket = opts.socket ?? null;
1930
1932
  this.environment = opts.environment ?? "development";
@@ -1933,11 +1935,14 @@ var SocketClient = class {
1933
1935
  this.sysEvents = opts.sys;
1934
1936
  this.roomJoinSchema = buildRoomPayloadSchema(this.config.joinMetaMessage);
1935
1937
  this.roomLeaveSchema = buildRoomPayloadSchema(this.config.leaveMetaMessage);
1938
+ this.reconnection = this.resolveReconnectionOptions(opts.reconnection);
1939
+ this.hasConnected = Boolean(this.socket?.connected);
1936
1940
  const hb = opts.heartbeat ?? {};
1937
1941
  this.hb = {
1938
1942
  intervalMs: hb.intervalMs ?? 15e3,
1939
1943
  timeoutMs: hb.timeoutMs ?? 7500
1940
1944
  };
1945
+ this.configureReconnection();
1941
1946
  if (!this.socket) {
1942
1947
  this.dbg({
1943
1948
  type: "lifecycle",
@@ -1964,6 +1969,8 @@ var SocketClient = class {
1964
1969
  return;
1965
1970
  }
1966
1971
  const socket = this.socket;
1972
+ const isReconnect = this.hasConnected;
1973
+ this.hasConnected = true;
1967
1974
  this.dbg({
1968
1975
  type: "connection",
1969
1976
  phase: "connect_event",
@@ -1973,12 +1980,15 @@ var SocketClient = class {
1973
1980
  }
1974
1981
  });
1975
1982
  this.logSocketConfigSnapshot("connect_event");
1976
- void Promise.resolve(
1977
- this.getSysEvent("sys:connect")({
1983
+ void (async () => {
1984
+ if (isReconnect && this.reconnection.restoreRooms) {
1985
+ await this.restoreRooms();
1986
+ }
1987
+ await this.getSysEvent("sys:connect")({
1978
1988
  socket,
1979
1989
  client: this
1980
- })
1981
- ).catch((error) => {
1990
+ });
1991
+ })().catch((error) => {
1982
1992
  this.dbg({
1983
1993
  type: "connection",
1984
1994
  phase: "connect_event",
@@ -2150,7 +2160,7 @@ var SocketClient = class {
2150
2160
  };
2151
2161
  if (this.socket) {
2152
2162
  this.socket.on("connect", this.onConnect);
2153
- this.socket.on("reconnect", this.onReconnect);
2163
+ this.socket.io.on("reconnect", this.onReconnect);
2154
2164
  this.socket.on("disconnect", this.onDisconnect);
2155
2165
  this.socket.on("connect_error", this.onConnectError);
2156
2166
  this.socket.on("sys:pong", this.onPong);
@@ -2223,6 +2233,57 @@ var SocketClient = class {
2223
2233
  getSysEvent(name) {
2224
2234
  return this.sysEvents[name];
2225
2235
  }
2236
+ resolveReconnectionOptions(value) {
2237
+ if (value === true) {
2238
+ return { mode: "managed", restoreRooms: true };
2239
+ }
2240
+ if (value === false) {
2241
+ return { mode: "manual", restoreRooms: true };
2242
+ }
2243
+ return {
2244
+ ...value,
2245
+ mode: value?.mode ?? "inherit",
2246
+ restoreRooms: value?.restoreRooms ?? true
2247
+ };
2248
+ }
2249
+ configureReconnection() {
2250
+ if (!this.socket) return;
2251
+ const manager = this.socket.io;
2252
+ const options = this.reconnection;
2253
+ if (options.mode === "managed") {
2254
+ manager.reconnection(true);
2255
+ if (options.attempts !== void 0) {
2256
+ manager.reconnectionAttempts(options.attempts);
2257
+ }
2258
+ if (options.delayMs !== void 0) {
2259
+ manager.reconnectionDelay(options.delayMs);
2260
+ }
2261
+ if (options.delayMaxMs !== void 0) {
2262
+ manager.reconnectionDelayMax(options.delayMaxMs);
2263
+ }
2264
+ if (options.randomizationFactor !== void 0) {
2265
+ manager.randomizationFactor(options.randomizationFactor);
2266
+ }
2267
+ if (options.timeoutMs !== void 0) {
2268
+ manager.timeout(options.timeoutMs);
2269
+ }
2270
+ } else if (options.mode === "manual") {
2271
+ manager.reconnection(false);
2272
+ }
2273
+ this.dbg({
2274
+ type: "connection",
2275
+ phase: "reconnect_config",
2276
+ details: {
2277
+ mode: options.mode,
2278
+ restoreRooms: options.restoreRooms,
2279
+ attempts: options.attempts,
2280
+ delayMs: options.delayMs,
2281
+ delayMaxMs: options.delayMaxMs,
2282
+ randomizationFactor: options.randomizationFactor,
2283
+ timeoutMs: options.timeoutMs
2284
+ }
2285
+ });
2286
+ }
2226
2287
  dbg(e) {
2227
2288
  const d = this.debug;
2228
2289
  if (!d.logger) return;
@@ -2254,6 +2315,63 @@ var SocketClient = class {
2254
2315
  handlers
2255
2316
  };
2256
2317
  }
2318
+ /**
2319
+ * Re-emit joins for all rooms with active references without incrementing
2320
+ * their reference counts. This is safe to call after any externally or
2321
+ * internally managed reconnect.
2322
+ */
2323
+ async restoreRooms() {
2324
+ if (!this.socket?.connected) {
2325
+ this.dbg({
2326
+ type: "room",
2327
+ phase: "restore",
2328
+ rooms: [],
2329
+ err: "Socket is not connected"
2330
+ });
2331
+ return [];
2332
+ }
2333
+ const restored = [];
2334
+ const desiredRooms = Array.from(this.roomCounts.entries()).filter(([, count]) => count > 0).map(([room]) => room);
2335
+ for (const room of desiredRooms) {
2336
+ const meta = this.roomJoinMeta.get(room);
2337
+ const allowed = await this.getSysEvent("sys:room_join")({
2338
+ rooms: [room],
2339
+ meta,
2340
+ socket: this.socket,
2341
+ client: this
2342
+ });
2343
+ if (!allowed) {
2344
+ this.dbg({
2345
+ type: "room",
2346
+ phase: "restore",
2347
+ rooms: [room],
2348
+ err: "sys:room_join handler aborted restore"
2349
+ });
2350
+ continue;
2351
+ }
2352
+ const payloadResult = this.roomJoinSchema.safeParse({
2353
+ rooms: [room],
2354
+ meta
2355
+ });
2356
+ if (!payloadResult.success) {
2357
+ this.dbg({
2358
+ type: "room",
2359
+ phase: "restore",
2360
+ rooms: [room],
2361
+ err: "payload validation failed",
2362
+ details: this.getValidationDetails(payloadResult.error)
2363
+ });
2364
+ continue;
2365
+ }
2366
+ this.socket.emit(
2367
+ "sys:room_join",
2368
+ payloadResult.data
2369
+ );
2370
+ restored.push(room);
2371
+ }
2372
+ this.dbg({ type: "room", phase: "restore", rooms: restored });
2373
+ return restored;
2374
+ }
2257
2375
  toArray(rooms) {
2258
2376
  return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];
2259
2377
  }
@@ -2441,6 +2559,9 @@ var SocketClient = class {
2441
2559
  }
2442
2560
  const payload = payloadResult.data;
2443
2561
  const normalizedRooms = this.toArray(payload.rooms);
2562
+ for (const room of normalizedRooms) {
2563
+ this.roomJoinMeta.set(room, meta);
2564
+ }
2444
2565
  this.socket.emit("sys:room_join", payload);
2445
2566
  this.dbg({ type: "room", phase: "join", rooms: normalizedRooms });
2446
2567
  }
@@ -2499,6 +2620,9 @@ var SocketClient = class {
2499
2620
  }
2500
2621
  const payload = payloadResult.data;
2501
2622
  const normalizedRooms = this.toArray(payload.rooms);
2623
+ for (const room of normalizedRooms) {
2624
+ this.roomJoinMeta.delete(room);
2625
+ }
2502
2626
  this.socket.emit("sys:room_leave", payload);
2503
2627
  this.dbg({ type: "room", phase: "leave", rooms: normalizedRooms });
2504
2628
  }
@@ -2641,7 +2765,7 @@ var SocketClient = class {
2641
2765
  this.stopHeartbeat("destroy");
2642
2766
  if (socket) {
2643
2767
  socket.off("connect", this.onConnect);
2644
- socket.off("reconnect", this.onReconnect);
2768
+ socket.io.off("reconnect", this.onReconnect);
2645
2769
  socket.off("disconnect", this.onDisconnect);
2646
2770
  socket.off("connect_error", this.onConnectError);
2647
2771
  socket.off("sys:pong", this.onPong);
@@ -2658,6 +2782,7 @@ var SocketClient = class {
2658
2782
  await this.leaveRooms(toLeave, leaveMeta);
2659
2783
  }
2660
2784
  this.roomCounts.clear();
2785
+ this.roomJoinMeta.clear();
2661
2786
  this.dbg({
2662
2787
  type: "lifecycle",
2663
2788
  phase: "destroy_complete",
@@ -3178,15 +3303,6 @@ function buildSocketedRoute(options) {
3178
3303
  return;
3179
3304
  }
3180
3305
  const { joinMeta, leaveMeta } = roomState;
3181
- if (!joinMeta || !leaveMeta) {
3182
- dbg2(debug, {
3183
- type: "room",
3184
- phase: "join_skip",
3185
- rooms: roomState.rooms,
3186
- reason: "missing_meta"
3187
- });
3188
- return;
3189
- }
3190
3306
  let active = true;
3191
3307
  dbg2(debug, {
3192
3308
  type: "room",