@pylonsync/react 0.3.368 → 0.3.371

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.ts CHANGED
@@ -14,8 +14,9 @@ export type { PylonRouter } from "./useRouter";
14
14
  import { type Storage as PylonStorage } from "@pylonsync/sync";
15
15
  export { useQuery, useQueryOne, useReactiveQuery, useMutation, useInfiniteQuery, usePaginatedQuery, useEntityMutation, useAction, useQueryRaw, useQueryOneRaw, useLiveList, useLiveRow, useInsert, useUpdate, useDelete, useFn, useAggregate, useSearch, } from "./hooks";
16
16
  export type { QueryOptions, QueryFilter, IncludeSpec, UseQueryReturn, UseQueryOneReturn, UseReactiveQueryReturn, UseMutationReturn, UseInfiniteQueryReturn, UsePaginatedQueryReturn, PaginatedQueryStatus, UseFnReturn, AggregateSpec, UseAggregateReturn, SearchSpec, UseSearchReturn, } from "./hooks";
17
- export { useRoom } from "./useRoom";
17
+ export { useRoom, useRoomMessages } from "./useRoom";
18
18
  export type { RoomPeer, RoomSnapshot, UseRoomOptions, UseRoomReturn, } from "./useRoom";
19
+ export type { RoomMessage } from "@pylonsync/sync";
19
20
  export { useShard, connectShard } from "./useShard";
20
21
  export type { UseShardOptions, UseShardReturn, ShardClient, } from "./useShard";
21
22
  export { useSession } from "./useSession";
package/dist/useRoom.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type SyncEngine } from '@pylonsync/sync';
1
+ import { type RoomMessage, type SyncEngine } from '@pylonsync/sync';
2
2
  export interface RoomPeer {
3
3
  user_id: string;
4
4
  data: any;
@@ -90,4 +90,30 @@ export declare const __roomRegistryInternals: {
90
90
  isPolling(room: SharedRoom): boolean;
91
91
  };
92
92
  export declare function useRoom(roomId: string, userId: string, options?: UseRoomOptions): UseRoomReturn;
93
+ /**
94
+ * Subscribe to broadcasts on a room — both from other members
95
+ * (`useRoom(...).broadcast`) and from the server
96
+ * (`ctx.rooms.broadcast` in a mutation or action).
97
+ *
98
+ * `useRoom` is send-only; this is the receive half. Streaming agent
99
+ * output is the motivating case: the handler broadcasts each delta,
100
+ * and every device watching the room renders it, including tabs that
101
+ * weren't the one that started the run.
102
+ *
103
+ * ```tsx
104
+ * const [text, setText] = useState("");
105
+ * useRoomMessages(`chat-${chatId}`, (msg) => {
106
+ * if (msg.topic === "agent.delta") {
107
+ * setText((t) => t + (msg.payload as { text: string }).text);
108
+ * }
109
+ * });
110
+ * ```
111
+ *
112
+ * `from` is the sending user's id, or `""` for a server broadcast.
113
+ *
114
+ * The callback is held in a ref, so an inline arrow function doesn't
115
+ * resubscribe on every render — only a changed `roomId` does. Requires
116
+ * `init()`; without an engine (SSR, tests) this is a no-op.
117
+ */
118
+ export declare function useRoomMessages(roomId: string, onMessage: (message: RoomMessage) => void): void;
93
119
  export {};
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.368",
6
+ "version": "0.3.371",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "types": "./dist/index.d.ts",
@@ -14,8 +14,8 @@
14
14
  "prepack": "bun run build"
15
15
  },
16
16
  "dependencies": {
17
- "@pylonsync/sdk": "0.3.368",
18
- "@pylonsync/sync": "0.3.368"
17
+ "@pylonsync/sdk": "0.3.371",
18
+ "@pylonsync/sync": "0.3.371"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "react": ">=19.0.0"
package/src/index.ts CHANGED
@@ -105,13 +105,14 @@ export type {
105
105
  } from "./hooks";
106
106
 
107
107
  // Room hook
108
- export { useRoom } from "./useRoom";
108
+ export { useRoom, useRoomMessages } from "./useRoom";
109
109
  export type {
110
110
  RoomPeer,
111
111
  RoomSnapshot,
112
112
  UseRoomOptions,
113
113
  UseRoomReturn,
114
114
  } from "./useRoom";
115
+ export type { RoomMessage } from "@pylonsync/sync";
115
116
 
116
117
  // Shard hook for real-time sims (games, MMO, live docs, etc.)
117
118
  export { useShard, connectShard } from "./useShard";
package/src/useRoom.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useEffect, useCallback, useRef } from 'react';
4
- import { pylonFetch, type RoomMember, type SyncEngine } from '@pylonsync/sync';
4
+ import {
5
+ pylonFetch,
6
+ type RoomMember,
7
+ type RoomMessage,
8
+ type SyncEngine,
9
+ } from '@pylonsync/sync';
5
10
  import { getBaseUrl, getReactStorage, storageKey } from './index';
6
11
  import { getSync } from './db';
7
12
 
@@ -679,3 +684,45 @@ export function useRoom(
679
684
 
680
685
  return { peers, isConnected, setPresence, broadcast, leave, error };
681
686
  }
687
+
688
+ /**
689
+ * Subscribe to broadcasts on a room — both from other members
690
+ * (`useRoom(...).broadcast`) and from the server
691
+ * (`ctx.rooms.broadcast` in a mutation or action).
692
+ *
693
+ * `useRoom` is send-only; this is the receive half. Streaming agent
694
+ * output is the motivating case: the handler broadcasts each delta,
695
+ * and every device watching the room renders it, including tabs that
696
+ * weren't the one that started the run.
697
+ *
698
+ * ```tsx
699
+ * const [text, setText] = useState("");
700
+ * useRoomMessages(`chat-${chatId}`, (msg) => {
701
+ * if (msg.topic === "agent.delta") {
702
+ * setText((t) => t + (msg.payload as { text: string }).text);
703
+ * }
704
+ * });
705
+ * ```
706
+ *
707
+ * `from` is the sending user's id, or `""` for a server broadcast.
708
+ *
709
+ * The callback is held in a ref, so an inline arrow function doesn't
710
+ * resubscribe on every render — only a changed `roomId` does. Requires
711
+ * `init()`; without an engine (SSR, tests) this is a no-op.
712
+ */
713
+ export function useRoomMessages(
714
+ roomId: string,
715
+ onMessage: (message: RoomMessage) => void,
716
+ ): void {
717
+ const handlerRef = useRef(onMessage);
718
+ handlerRef.current = onMessage;
719
+
720
+ useEffect(() => {
721
+ if (!roomId) return;
722
+ const engine = tryGetSync();
723
+ if (!engine) return;
724
+ return engine.subscribeRoomMessages(roomId, (msg) => {
725
+ handlerRef.current(msg);
726
+ });
727
+ }, [roomId]);
728
+ }