@emeryld/rrroutes-client 2.2.7 → 2.2.9

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
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  buildRoomPayloadSchema: () => buildRoomPayloadSchema,
36
36
  buildRouter: () => buildRouter,
37
37
  buildSocketProvider: () => buildSocketProvider,
38
+ buildSocketedRoute: () => buildSocketedRoute,
38
39
  createRouteClient: () => createRouteClient,
39
40
  defaultFetcher: () => defaultFetcher,
40
41
  useSocketClient: () => useSocketClient,
@@ -68,19 +69,9 @@ var defaultFetcher = async (req) => {
68
69
  };
69
70
 
70
71
  // src/routesV3.client.index.ts
72
+ var import_react = require("react");
71
73
  var import_react_query = require("@tanstack/react-query");
72
74
  var import_rrroutes_contract = require("@emeryld/rrroutes-contract");
73
- function splitUseEndpointArgs(useArgs, expectsArgs) {
74
- if (useArgs.length === 0) return { args: void 0, options: void 0 };
75
- if (!expectsArgs) {
76
- return { args: void 0, options: useArgs[0] };
77
- }
78
- const [maybeArgs, maybeOptions] = useArgs;
79
- return {
80
- args: maybeArgs,
81
- options: useArgs.length > 1 ? maybeOptions : void 0
82
- };
83
- }
84
75
  var toUpper = (m) => m.toUpperCase();
85
76
  function zParse(value, schema) {
86
77
  return schema ? schema.parse(value) : value;
@@ -332,14 +323,31 @@ function createRouteClient(opts) {
332
323
  };
333
324
  if (isGet && isFeed) {
334
325
  const useEndpoint2 = (...useArgs) => {
335
- const { args, options } = splitUseEndpointArgs(useArgs, expectsArgs);
326
+ const args = useArgs[0];
336
327
  emit({ type: "useEndpoint", leaf: leafLabel, variant: "infiniteGet" });
337
328
  const tuple = toArgsTuple(args);
338
329
  const params = args?.params;
339
330
  const query = args?.query;
340
331
  const buildOptions = rqOpts ?? {};
332
+ const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
333
+ const notifyOnReceive = (0, import_react.useCallback)(
334
+ (data) => {
335
+ buildOptions?.onReceive?.(data);
336
+ listenersRef.current.forEach((listener) => listener(data));
337
+ },
338
+ []
339
+ );
340
+ const registerOnReceive = (0, import_react.useCallback)(
341
+ (listener) => {
342
+ listenersRef.current.add(listener);
343
+ return () => {
344
+ listenersRef.current.delete(listener);
345
+ };
346
+ },
347
+ []
348
+ );
341
349
  const { normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);
342
- return (0, import_react_query.useInfiniteQuery)({
350
+ const queryResult = (0, import_react_query.useInfiniteQuery)({
343
351
  ...buildOptions,
344
352
  queryKey: getQueryKeys(...tuple),
345
353
  initialPageParam: void 0,
@@ -352,14 +360,12 @@ function createRouteClient(opts) {
352
360
  };
353
361
  return fetchEndpoint(tuple, {
354
362
  queryOverride: pageQuery,
355
- onReceive: (data) => {
356
- buildOptions?.onReceive?.(data);
357
- options?.onReceive?.(data);
358
- }
363
+ onReceive: notifyOnReceive
359
364
  });
360
365
  }
361
366
  // NOTE: TData is InfiniteData<T>, so we don't need a select here.
362
367
  }, queryClient);
368
+ return { ...queryResult, onReceive: registerOnReceive };
363
369
  };
364
370
  return {
365
371
  getQueryKeys,
@@ -371,24 +377,39 @@ function createRouteClient(opts) {
371
377
  }
372
378
  if (isGet) {
373
379
  const useEndpoint2 = (...useArgs) => {
374
- const { args, options } = splitUseEndpointArgs(useArgs, expectsArgs);
380
+ const args = useArgs[0];
375
381
  emit({ type: "useEndpoint", leaf: leafLabel, variant: "get" });
376
382
  const tuple = toArgsTuple(args);
377
383
  const params = args?.params;
378
384
  const query = args?.query;
379
385
  const buildOptions = rqOpts ?? {};
386
+ const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
387
+ const notifyOnReceive = (0, import_react.useCallback)(
388
+ (data) => {
389
+ buildOptions?.onReceive?.(data);
390
+ listenersRef.current.forEach((listener) => listener(data));
391
+ },
392
+ []
393
+ );
394
+ const registerOnReceive = (0, import_react.useCallback)(
395
+ (listener) => {
396
+ listenersRef.current.add(listener);
397
+ return () => {
398
+ listenersRef.current.delete(listener);
399
+ };
400
+ },
401
+ []
402
+ );
380
403
  buildUrl(leaf, baseUrl, params, query);
381
- return (0, import_react_query.useQuery)({
404
+ const queryResult = (0, import_react_query.useQuery)({
382
405
  ...buildOptions,
383
406
  queryKey: getQueryKeys(...tuple),
384
407
  placeholderData: import_react_query.keepPreviousData,
385
408
  queryFn: () => fetchEndpoint(tuple, {
386
- onReceive: (data) => {
387
- buildOptions?.onReceive?.(data);
388
- options?.onReceive?.(data);
389
- }
409
+ onReceive: notifyOnReceive
390
410
  })
391
411
  }, queryClient);
412
+ return { ...queryResult, onReceive: registerOnReceive };
392
413
  };
393
414
  return {
394
415
  getQueryKeys,
@@ -414,18 +435,29 @@ function createRouteClient(opts) {
414
435
  return result;
415
436
  };
416
437
  const useEndpoint = (...useArgs) => {
417
- const { args, options } = splitUseEndpointArgs(useArgs, expectsArgs);
438
+ const args = useArgs[0];
418
439
  emit({ type: "useEndpoint", leaf: leafLabel, variant: "mutation" });
419
440
  const tuple = toArgsTuple(args);
420
- return (0, import_react_query.useMutation)({
441
+ const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
442
+ const notifyListeners = (0, import_react.useCallback)((data) => {
443
+ listenersRef.current.forEach((listener) => listener(data));
444
+ }, []);
445
+ const registerOnReceive = (0, import_react.useCallback)((listener) => {
446
+ listenersRef.current.add(listener);
447
+ return () => {
448
+ listenersRef.current.delete(listener);
449
+ };
450
+ }, []);
451
+ const mutationResult = (0, import_react_query.useMutation)({
421
452
  ...mutationBuildOptions,
422
453
  mutationKey: getQueryKeys(...tuple),
423
454
  mutationFn: async (body) => {
424
455
  const result = await fetchMutation(...[...tuple, body]);
425
- options?.onReceive?.(result);
456
+ notifyListeners(result);
426
457
  return result;
427
458
  }
428
459
  }, queryClient);
460
+ return { ...mutationResult, onReceive: registerOnReceive };
429
461
  };
430
462
  return {
431
463
  getQueryKeys,
@@ -684,6 +716,90 @@ function useSocketConnection(args) {
684
716
  }, [client, event, onMessage, autoJoin, autoLeave, ...normalizedRooms]);
685
717
  }
686
718
 
719
+ // src/sockets/socketedRoute/socket.client.helper.ts
720
+ var import_react2 = require("react");
721
+ function normalizeRooms(rooms) {
722
+ if (rooms == null) return [];
723
+ const list = Array.isArray(rooms) ? rooms : [rooms];
724
+ const seen = /* @__PURE__ */ new Set();
725
+ const normalized = [];
726
+ for (const r of list) {
727
+ if (typeof r !== "string") continue;
728
+ if (seen.has(r)) continue;
729
+ seen.add(r);
730
+ normalized.push(r);
731
+ }
732
+ return normalized;
733
+ }
734
+ function roomsFromData(data, toRooms) {
735
+ if (data == null) return [];
736
+ const merge = /* @__PURE__ */ new Set();
737
+ const add = (input) => {
738
+ for (const r of normalizeRooms(input)) merge.add(r);
739
+ };
740
+ const maybePages = data?.pages;
741
+ if (Array.isArray(maybePages)) {
742
+ for (const page of maybePages) add(toRooms(page));
743
+ return Array.from(merge);
744
+ }
745
+ add(toRooms(data));
746
+ return Array.from(merge);
747
+ }
748
+ function buildSocketedRoute(options) {
749
+ const { built, event, toRooms, applyMessage, joinMeta, leaveMeta, autoJoin = true, autoLeave = true } = options;
750
+ return (...useArgs) => {
751
+ const client = useSocketClient();
752
+ const endpointResult = built.useEndpoint(...useArgs);
753
+ const argsKey = (0, import_react2.useMemo)(() => JSON.stringify(useArgs[0] ?? null), [useArgs]);
754
+ const [rooms, setRooms] = (0, import_react2.useState)(
755
+ () => roomsFromData(endpointResult.data, toRooms)
756
+ );
757
+ const roomsKey = (0, import_react2.useMemo)(() => rooms.join("|"), [rooms]);
758
+ (0, import_react2.useEffect)(() => {
759
+ const unsubscribe = endpointResult.onReceive((data) => {
760
+ setRooms((prev) => {
761
+ const next = normalizeRooms(toRooms(data));
762
+ if (next.length === 0) return prev;
763
+ const merged = new Set(prev);
764
+ next.forEach((r) => merged.add(r));
765
+ return Array.from(merged);
766
+ });
767
+ });
768
+ return unsubscribe;
769
+ }, [endpointResult, toRooms]);
770
+ (0, import_react2.useEffect)(() => {
771
+ setRooms(roomsFromData(endpointResult.data, toRooms));
772
+ }, [endpointResult.data, toRooms]);
773
+ (0, import_react2.useEffect)(() => {
774
+ if (!autoJoin || rooms.length === 0) return;
775
+ let active = true;
776
+ (async () => {
777
+ try {
778
+ await client.joinRooms(rooms, joinMeta);
779
+ } catch {
780
+ }
781
+ })();
782
+ return () => {
783
+ if (!active || !autoLeave || rooms.length === 0) return;
784
+ active = false;
785
+ void client.leaveRooms(rooms, leaveMeta).catch(() => {
786
+ });
787
+ };
788
+ }, [client, joinMeta, leaveMeta, autoJoin, autoLeave, roomsKey]);
789
+ (0, import_react2.useEffect)(() => {
790
+ const unsubscribe = client.on(event, (payload, meta) => {
791
+ built.setData((prev) => {
792
+ const next = applyMessage(prev, payload, meta);
793
+ setRooms(roomsFromData(next, toRooms));
794
+ return next;
795
+ }, ...useArgs);
796
+ });
797
+ return unsubscribe;
798
+ }, [client, event, applyMessage, built, argsKey]);
799
+ return { ...endpointResult, rooms };
800
+ };
801
+ }
802
+
687
803
  // src/sockets/socket.client.index.ts
688
804
  var SocketClient = class {
689
805
  constructor(events, opts) {
@@ -1306,6 +1422,7 @@ var SocketClient = class {
1306
1422
  buildRoomPayloadSchema,
1307
1423
  buildRouter,
1308
1424
  buildSocketProvider,
1425
+ buildSocketedRoute,
1309
1426
  createRouteClient,
1310
1427
  defaultFetcher,
1311
1428
  useSocketClient,