@emeryld/rrroutes-client 2.8.9 → 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.cjs CHANGED
@@ -1954,7 +1954,9 @@ var SocketClient = class {
1954
1954
  this.hbTimer = null;
1955
1955
  // stats
1956
1956
  this.roomCounts = /* @__PURE__ */ new Map();
1957
+ this.roomJoinMeta = /* @__PURE__ */ new Map();
1957
1958
  this.handlerMap = /* @__PURE__ */ new Map();
1959
+ this.hasConnected = false;
1958
1960
  this.events = events;
1959
1961
  this.socket = opts.socket ?? null;
1960
1962
  this.environment = opts.environment ?? "development";
@@ -1963,11 +1965,14 @@ var SocketClient = class {
1963
1965
  this.sysEvents = opts.sys;
1964
1966
  this.roomJoinSchema = buildRoomPayloadSchema(this.config.joinMetaMessage);
1965
1967
  this.roomLeaveSchema = buildRoomPayloadSchema(this.config.leaveMetaMessage);
1968
+ this.reconnection = this.resolveReconnectionOptions(opts.reconnection);
1969
+ this.hasConnected = Boolean(this.socket?.connected);
1966
1970
  const hb = opts.heartbeat ?? {};
1967
1971
  this.hb = {
1968
1972
  intervalMs: hb.intervalMs ?? 15e3,
1969
1973
  timeoutMs: hb.timeoutMs ?? 7500
1970
1974
  };
1975
+ this.configureReconnection();
1971
1976
  if (!this.socket) {
1972
1977
  this.dbg({
1973
1978
  type: "lifecycle",
@@ -1994,6 +1999,8 @@ var SocketClient = class {
1994
1999
  return;
1995
2000
  }
1996
2001
  const socket = this.socket;
2002
+ const isReconnect = this.hasConnected;
2003
+ this.hasConnected = true;
1997
2004
  this.dbg({
1998
2005
  type: "connection",
1999
2006
  phase: "connect_event",
@@ -2003,12 +2010,15 @@ var SocketClient = class {
2003
2010
  }
2004
2011
  });
2005
2012
  this.logSocketConfigSnapshot("connect_event");
2006
- void Promise.resolve(
2007
- this.getSysEvent("sys:connect")({
2013
+ void (async () => {
2014
+ if (isReconnect && this.reconnection.restoreRooms) {
2015
+ await this.restoreRooms();
2016
+ }
2017
+ await this.getSysEvent("sys:connect")({
2008
2018
  socket,
2009
2019
  client: this
2010
- })
2011
- ).catch((error) => {
2020
+ });
2021
+ })().catch((error) => {
2012
2022
  this.dbg({
2013
2023
  type: "connection",
2014
2024
  phase: "connect_event",
@@ -2180,7 +2190,7 @@ var SocketClient = class {
2180
2190
  };
2181
2191
  if (this.socket) {
2182
2192
  this.socket.on("connect", this.onConnect);
2183
- this.socket.on("reconnect", this.onReconnect);
2193
+ this.socket.io.on("reconnect", this.onReconnect);
2184
2194
  this.socket.on("disconnect", this.onDisconnect);
2185
2195
  this.socket.on("connect_error", this.onConnectError);
2186
2196
  this.socket.on("sys:pong", this.onPong);
@@ -2253,6 +2263,57 @@ var SocketClient = class {
2253
2263
  getSysEvent(name) {
2254
2264
  return this.sysEvents[name];
2255
2265
  }
2266
+ resolveReconnectionOptions(value) {
2267
+ if (value === true) {
2268
+ return { mode: "managed", restoreRooms: true };
2269
+ }
2270
+ if (value === false) {
2271
+ return { mode: "manual", restoreRooms: true };
2272
+ }
2273
+ return {
2274
+ ...value,
2275
+ mode: value?.mode ?? "inherit",
2276
+ restoreRooms: value?.restoreRooms ?? true
2277
+ };
2278
+ }
2279
+ configureReconnection() {
2280
+ if (!this.socket) return;
2281
+ const manager = this.socket.io;
2282
+ const options = this.reconnection;
2283
+ if (options.mode === "managed") {
2284
+ manager.reconnection(true);
2285
+ if (options.attempts !== void 0) {
2286
+ manager.reconnectionAttempts(options.attempts);
2287
+ }
2288
+ if (options.delayMs !== void 0) {
2289
+ manager.reconnectionDelay(options.delayMs);
2290
+ }
2291
+ if (options.delayMaxMs !== void 0) {
2292
+ manager.reconnectionDelayMax(options.delayMaxMs);
2293
+ }
2294
+ if (options.randomizationFactor !== void 0) {
2295
+ manager.randomizationFactor(options.randomizationFactor);
2296
+ }
2297
+ if (options.timeoutMs !== void 0) {
2298
+ manager.timeout(options.timeoutMs);
2299
+ }
2300
+ } else if (options.mode === "manual") {
2301
+ manager.reconnection(false);
2302
+ }
2303
+ this.dbg({
2304
+ type: "connection",
2305
+ phase: "reconnect_config",
2306
+ details: {
2307
+ mode: options.mode,
2308
+ restoreRooms: options.restoreRooms,
2309
+ attempts: options.attempts,
2310
+ delayMs: options.delayMs,
2311
+ delayMaxMs: options.delayMaxMs,
2312
+ randomizationFactor: options.randomizationFactor,
2313
+ timeoutMs: options.timeoutMs
2314
+ }
2315
+ });
2316
+ }
2256
2317
  dbg(e) {
2257
2318
  const d = this.debug;
2258
2319
  if (!d.logger) return;
@@ -2284,6 +2345,63 @@ var SocketClient = class {
2284
2345
  handlers
2285
2346
  };
2286
2347
  }
2348
+ /**
2349
+ * Re-emit joins for all rooms with active references without incrementing
2350
+ * their reference counts. This is safe to call after any externally or
2351
+ * internally managed reconnect.
2352
+ */
2353
+ async restoreRooms() {
2354
+ if (!this.socket?.connected) {
2355
+ this.dbg({
2356
+ type: "room",
2357
+ phase: "restore",
2358
+ rooms: [],
2359
+ err: "Socket is not connected"
2360
+ });
2361
+ return [];
2362
+ }
2363
+ const restored = [];
2364
+ const desiredRooms = Array.from(this.roomCounts.entries()).filter(([, count]) => count > 0).map(([room]) => room);
2365
+ for (const room of desiredRooms) {
2366
+ const meta = this.roomJoinMeta.get(room);
2367
+ const allowed = await this.getSysEvent("sys:room_join")({
2368
+ rooms: [room],
2369
+ meta,
2370
+ socket: this.socket,
2371
+ client: this
2372
+ });
2373
+ if (!allowed) {
2374
+ this.dbg({
2375
+ type: "room",
2376
+ phase: "restore",
2377
+ rooms: [room],
2378
+ err: "sys:room_join handler aborted restore"
2379
+ });
2380
+ continue;
2381
+ }
2382
+ const payloadResult = this.roomJoinSchema.safeParse({
2383
+ rooms: [room],
2384
+ meta
2385
+ });
2386
+ if (!payloadResult.success) {
2387
+ this.dbg({
2388
+ type: "room",
2389
+ phase: "restore",
2390
+ rooms: [room],
2391
+ err: "payload validation failed",
2392
+ details: this.getValidationDetails(payloadResult.error)
2393
+ });
2394
+ continue;
2395
+ }
2396
+ this.socket.emit(
2397
+ "sys:room_join",
2398
+ payloadResult.data
2399
+ );
2400
+ restored.push(room);
2401
+ }
2402
+ this.dbg({ type: "room", phase: "restore", rooms: restored });
2403
+ return restored;
2404
+ }
2287
2405
  toArray(rooms) {
2288
2406
  return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];
2289
2407
  }
@@ -2471,6 +2589,9 @@ var SocketClient = class {
2471
2589
  }
2472
2590
  const payload = payloadResult.data;
2473
2591
  const normalizedRooms = this.toArray(payload.rooms);
2592
+ for (const room of normalizedRooms) {
2593
+ this.roomJoinMeta.set(room, meta);
2594
+ }
2474
2595
  this.socket.emit("sys:room_join", payload);
2475
2596
  this.dbg({ type: "room", phase: "join", rooms: normalizedRooms });
2476
2597
  }
@@ -2529,6 +2650,9 @@ var SocketClient = class {
2529
2650
  }
2530
2651
  const payload = payloadResult.data;
2531
2652
  const normalizedRooms = this.toArray(payload.rooms);
2653
+ for (const room of normalizedRooms) {
2654
+ this.roomJoinMeta.delete(room);
2655
+ }
2532
2656
  this.socket.emit("sys:room_leave", payload);
2533
2657
  this.dbg({ type: "room", phase: "leave", rooms: normalizedRooms });
2534
2658
  }
@@ -2671,7 +2795,7 @@ var SocketClient = class {
2671
2795
  this.stopHeartbeat("destroy");
2672
2796
  if (socket) {
2673
2797
  socket.off("connect", this.onConnect);
2674
- socket.off("reconnect", this.onReconnect);
2798
+ socket.io.off("reconnect", this.onReconnect);
2675
2799
  socket.off("disconnect", this.onDisconnect);
2676
2800
  socket.off("connect_error", this.onConnectError);
2677
2801
  socket.off("sys:pong", this.onPong);
@@ -2688,6 +2812,7 @@ var SocketClient = class {
2688
2812
  await this.leaveRooms(toLeave, leaveMeta);
2689
2813
  }
2690
2814
  this.roomCounts.clear();
2815
+ this.roomJoinMeta.clear();
2691
2816
  this.dbg({
2692
2817
  type: "lifecycle",
2693
2818
  phase: "destroy_complete",