@pluv/react 0.25.4 → 0.27.0

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.
@@ -1,18 +1,18 @@
1
1
 
2
- > @pluv/react@0.25.4 build /home/runner/work/pluv/pluv/packages/react
2
+ > @pluv/react@0.27.0 build /home/runner/work/pluv/pluv/packages/react
3
3
  > tsup src/index.ts --format esm,cjs --dts --external react
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.2.4
7
+ CLI tsup v8.3.0
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- ESM dist/index.mjs 11.87 KB
12
- ESM ⚡️ Build success in 124ms
13
- CJS dist/index.js 14.27 KB
14
- CJS ⚡️ Build success in 125ms
11
+ CJS dist/index.js 14.26 KB
12
+ CJS ⚡️ Build success in 101ms
13
+ ESM dist/index.mjs 11.85 KB
14
+ ESM ⚡️ Build success in 102ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 4984ms
17
- DTS dist/index.d.mts 5.17 KB
18
- DTS dist/index.d.ts 5.17 KB
16
+ DTS ⚡️ Build success in 5825ms
17
+ DTS dist/index.d.mts 6.12 KB
18
+ DTS dist/index.d.ts 6.12 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,181 @@
1
1
  # @pluv/react
2
2
 
3
+ ## 0.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 19ed36c: Updated `createClient` and `createRoomBundle` object params to better support automatic type inference.
8
+
9
+ ```ts
10
+ // Before
11
+ import { createClient } from "@pluv/react";
12
+ import { yjs } from "@pluv/crdt-yjs";
13
+ import { z } from "zod";
14
+ import type { ioServer } from "./server/ioServer";
15
+
16
+ const client = createClient<typeof ioServer>({
17
+ authEndpoint: ({ room }) => "MY_AUTH_URL",
18
+ wsEndpoint: ({ room }) => "MY_WEBSOCKET_URL",
19
+ });
20
+
21
+ client.createRoom("my-example-room", {
22
+ presence: z.object({
23
+ selectionId: z.string().nullish(),
24
+ }),
25
+ initialPresence: {
26
+ selectionId: null,
27
+ },
28
+ initialStorage: yjs.doc(() => ({
29
+ messages: yjs.array<string>([]),
30
+ })),
31
+ });
32
+
33
+ // After
34
+
35
+ import { createBundle, createClient } from "@pluv/react";
36
+
37
+ const client = createClient({
38
+ authEndpoint: ({ room }) => "MY_AUTH_URL",
39
+ wsEndpoint: ({ room }) => "MY_WEBSOCKET_URL",
40
+ /**
41
+ * @description The ioServer type now needs to be inferred on this property
42
+ * instead of within the generic on `createClient`. This is because of
43
+ * limitations with TypeScript not yet supporting partial type-inference
44
+ * for generics. Ensure to no-loonger include any generics on `createClient`
45
+ */
46
+ infer: (i) => ({ io: i<typeof ioServer> }),
47
+ // Now moved to `createClient` from `PluvClient.createRoom`
48
+ presence: z.object({
49
+ selectionId: z.string().nullish(),
50
+ }),
51
+ /**
52
+ * @description Now added to `createClient` to specify defaults for all
53
+ * rooms, and to infer types for storage. This value will be overwritten by
54
+ * `PluvClient.createRoom`.
55
+ */
56
+ initialStorage: yjs.doc(() => ({
57
+ messages: yjs.array<string>([]),
58
+ })),
59
+ });
60
+
61
+ const { createRoomBundle } = createBundle(client);
62
+ const pluv = createRoomBundle({});
63
+ ```
64
+
65
+ ### Patch Changes
66
+
67
+ - 19ed36c: Added client-side event router. This more-or-less mirrors the server-side event router. You can use both the server-side and client-side router simultaneously, but you should generally prefer only using one or the other.
68
+
69
+ ```ts
70
+ // Backend Server
71
+ import { createIO } from "@pluv/io";
72
+
73
+ const io = createIO({ /* ... */ });
74
+ const ioServer = io.server({
75
+ router: io.router({
76
+ multiply2: io.procedure
77
+ .input(z.object({ value: z.number() }))
78
+ .broadcast(({ value }) => ({
79
+ multiplied2: { value: value * 2 },
80
+ })),
81
+ }),
82
+ });
83
+
84
+ // Frontend Client
85
+ import { createBundle createClient } from "@pluv/react";
86
+
87
+ const io = createClient({ /* ... */ });
88
+
89
+ const { createRoomBundle } = createBundle(io);
90
+ const pluv = createRoomBundle({
91
+ // ...
92
+ router: io.router({
93
+ add5: io.procedure
94
+ .input(z.object({ value: z.number() }))
95
+ .broadcast(({ value }) => ({
96
+ /**
97
+ * @description Because `ioServer` does not specify `added5`,
98
+ * this will be forwarded to connected peers.
99
+ */
100
+ added5: { value: value + 5 },
101
+ })),
102
+ /**
103
+ * @description This is possible, but not recommended to do. Because
104
+ * `multiply2` is a procedure on `ioServer`, this will trigger the
105
+ * `multiply2` procedure on `ioServer`. Connected peers will not
106
+ * receive `multiply2`, but rather `multipled2`.
107
+ */
108
+ subtract5Multiply2: io.procedure
109
+ .input(z.object({ value: z.number() }))
110
+ .broadcast(({ value }) => ({
111
+ multiply2: { value: value - 5 },
112
+ })),
113
+ }),
114
+ });
115
+ ```
116
+
117
+ - Updated dependencies [19ed36c]
118
+ - Updated dependencies [19ed36c]
119
+ - @pluv/client@0.27.0
120
+ - @pluv/crdt@0.27.0
121
+ - @pluv/types@0.27.0
122
+
123
+ ## 0.26.0
124
+
125
+ ### Minor Changes
126
+
127
+ - 19e5dda: **BREAKING**
128
+ Update `authEndpoint` and `wsEndpoint` to now use a parameterized object that passes through additional data via a `metadata` property.
129
+
130
+ ```tsx
131
+ // Before
132
+
133
+ import { env } from "./env";
134
+ import type { ioServer } from "./server";
135
+
136
+ const client = createClient<typeof ioServer>({
137
+ authEndpoint: (room: string) => `${env.API_URL}}/room/${room}`,
138
+ wsEndpoint: (room: string) => `${env.WS_URL}/room/${room}`,
139
+ });
140
+
141
+ // After
142
+
143
+ const client = createClient<
144
+ typeof ioServer,
145
+ // Optional: You can specify types for metadata here. This is useful for Cloudflare Pages
146
+ { apiUrl: string; wsUrl: string }
147
+ >({
148
+ authEndpoint: ({ metadata, room }) => `${metadata.apiUrl}}/room/${room}`,
149
+ wsEndpoint: ({ metadata, room }) => `${{ metadata.wsUrl }}/room/${room}`,
150
+ });
151
+
152
+ client.createRoom("my-room", {
153
+ // This property exists when the `metadata` generic type is defined above
154
+ metadata: {
155
+ apiUrl: "https://example.com/api",
156
+ wsUrl: "wss://example.com/api",
157
+ },
158
+ // ...
159
+ });
160
+
161
+ <PluvRoomProvider
162
+ // This property exists when the `metadata` generic type is defined above
163
+ metadata={{
164
+ apiUrl: "https://example.com/api",
165
+ wsUrl: "wss://example.com/api",
166
+ }}
167
+ >
168
+ {/* ... */}
169
+ </PluvRoomProvider>
170
+ ```
171
+
172
+ ### Patch Changes
173
+
174
+ - Updated dependencies [19e5dda]
175
+ - @pluv/client@0.26.0
176
+ - @pluv/crdt@0.26.0
177
+ - @pluv/types@0.26.0
178
+
3
179
  ## 0.25.4
4
180
 
5
181
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,15 +1,15 @@
1
- import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
1
+ import { PluvRouterEventConfig, PluvRoomAddon, PluvRouter, PluvRoomDebug, MergeEvents, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { ConnectionState, createClient } from '@pluv/client';
3
3
  import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
4
4
  export { CrdtType, InferCrdtJson } from '@pluv/crdt';
5
- import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
5
+ import { IOLike, JsonObject, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
6
6
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
7
7
  import { FC, Dispatch, ReactNode } from 'react';
8
8
 
9
- type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
10
- addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
9
+ type CreateRoomBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = {
10
+ addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
11
11
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
12
- presence?: InputZodLike<TPresence>;
12
+ router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
13
13
  };
14
14
  type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
15
15
  children?: ReactNode;
@@ -20,13 +20,17 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
20
20
  } : {
21
21
  initialPresence: TPresence;
22
22
  });
23
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
24
- events?: MockedRoomEvents<TIO>;
23
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = BaseRoomProviderProps<TPresence, TStorage> & {
24
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
25
25
  };
26
- type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
26
+ type PluvRoomProviderProps<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
27
27
  debug?: boolean | PluvRoomDebug<TIO>;
28
28
  onAuthorizationFail?: (error: Error) => void;
29
- };
29
+ } & (keyof TMetadata extends never ? {
30
+ metadata?: undefined;
31
+ } : {
32
+ metadata: TMetadata;
33
+ });
30
34
  interface SubscriptionHookOptions<T extends unknown> {
31
35
  isEqual?: (a: T, b: T) => boolean;
32
36
  }
@@ -39,16 +43,16 @@ type EventProxy<TIO extends IOLike> = {
39
43
  useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
40
44
  };
41
45
  };
42
- interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
46
+ interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> {
43
47
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
44
- PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
45
- event: EventProxy<TIO>;
46
- useBroadcast: () => BroadcastProxy<TIO>;
48
+ PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>;
49
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
50
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
47
51
  useCanRedo: () => boolean;
48
52
  useCanUndo: () => boolean;
49
53
  useConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
50
54
  useDoc: () => AbstractCrdtDoc<TStorage>;
51
- useEvent: <TType extends keyof InferIOOutput<TIO>>(type: TType, callback: (data: Id<IOEventMessage<TIO, TType>>) => void) => void;
55
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(type: TType, callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void) => void;
52
56
  useMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
53
57
  useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
54
58
  useOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<T | null>) => T | null;
@@ -59,16 +63,16 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
59
63
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
60
64
  useUndo: () => () => void;
61
65
  }
62
- type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, infer IStorage> ? IStorage : never;
66
+ type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
63
67
 
64
68
  interface PluvProviderProps {
65
69
  children?: ReactNode;
66
70
  }
67
- interface CreateBundle<TIO extends IOLike> {
68
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
71
+ interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
72
+ createRoomBundle: <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents>;
69
73
  PluvProvider: FC<PluvProviderProps>;
70
- useClient: () => PluvClient<TIO>;
74
+ useClient: () => PluvClient<TIO, TPresence, TStorage, TMetadata>;
71
75
  }
72
- declare const createBundle: <TIO extends IOLike>(client: PluvClient<TIO>) => CreateBundle<TIO>;
76
+ declare const createBundle: <TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(client: PluvClient<TIO, TPresence, TStorage, TMetadata>) => CreateBundle<TIO, TMetadata, TPresence, TStorage>;
73
77
 
74
78
  export { type CreateBundle, type CreateRoomBundle, type CreateRoomBundleOptions, type InferRoomStorage, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
1
+ import { PluvRouterEventConfig, PluvRoomAddon, PluvRouter, PluvRoomDebug, MergeEvents, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { ConnectionState, createClient } from '@pluv/client';
3
3
  import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
4
4
  export { CrdtType, InferCrdtJson } from '@pluv/crdt';
5
- import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
5
+ import { IOLike, JsonObject, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
6
6
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
7
7
  import { FC, Dispatch, ReactNode } from 'react';
8
8
 
9
- type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
10
- addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
9
+ type CreateRoomBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = {
10
+ addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
11
11
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
12
- presence?: InputZodLike<TPresence>;
12
+ router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
13
13
  };
14
14
  type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
15
15
  children?: ReactNode;
@@ -20,13 +20,17 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
20
20
  } : {
21
21
  initialPresence: TPresence;
22
22
  });
23
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
24
- events?: MockedRoomEvents<TIO>;
23
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = BaseRoomProviderProps<TPresence, TStorage> & {
24
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
25
25
  };
26
- type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
26
+ type PluvRoomProviderProps<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
27
27
  debug?: boolean | PluvRoomDebug<TIO>;
28
28
  onAuthorizationFail?: (error: Error) => void;
29
- };
29
+ } & (keyof TMetadata extends never ? {
30
+ metadata?: undefined;
31
+ } : {
32
+ metadata: TMetadata;
33
+ });
30
34
  interface SubscriptionHookOptions<T extends unknown> {
31
35
  isEqual?: (a: T, b: T) => boolean;
32
36
  }
@@ -39,16 +43,16 @@ type EventProxy<TIO extends IOLike> = {
39
43
  useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
40
44
  };
41
45
  };
42
- interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
46
+ interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> {
43
47
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
44
- PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
45
- event: EventProxy<TIO>;
46
- useBroadcast: () => BroadcastProxy<TIO>;
48
+ PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>;
49
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
50
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
47
51
  useCanRedo: () => boolean;
48
52
  useCanUndo: () => boolean;
49
53
  useConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
50
54
  useDoc: () => AbstractCrdtDoc<TStorage>;
51
- useEvent: <TType extends keyof InferIOOutput<TIO>>(type: TType, callback: (data: Id<IOEventMessage<TIO, TType>>) => void) => void;
55
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(type: TType, callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void) => void;
52
56
  useMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
53
57
  useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
54
58
  useOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<T | null>) => T | null;
@@ -59,16 +63,16 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
59
63
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
60
64
  useUndo: () => () => void;
61
65
  }
62
- type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, infer IStorage> ? IStorage : never;
66
+ type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
63
67
 
64
68
  interface PluvProviderProps {
65
69
  children?: ReactNode;
66
70
  }
67
- interface CreateBundle<TIO extends IOLike> {
68
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
71
+ interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
72
+ createRoomBundle: <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents>;
69
73
  PluvProvider: FC<PluvProviderProps>;
70
- useClient: () => PluvClient<TIO>;
74
+ useClient: () => PluvClient<TIO, TPresence, TStorage, TMetadata>;
71
75
  }
72
- declare const createBundle: <TIO extends IOLike>(client: PluvClient<TIO>) => CreateBundle<TIO>;
76
+ declare const createBundle: <TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(client: PluvClient<TIO, TPresence, TStorage, TMetadata>) => CreateBundle<TIO, TMetadata, TPresence, TStorage>;
73
77
 
74
78
  export { type CreateBundle, type CreateRoomBundle, type CreateRoomBundleOptions, type InferRoomStorage, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.js CHANGED
@@ -122,15 +122,14 @@ var createRoomBundle = (client, options) => {
122
122
  return new import_client.MockedRoom(_room, {
123
123
  events,
124
124
  initialPresence,
125
- initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
126
- presence: options.presence
125
+ initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage
127
126
  });
128
127
  });
129
128
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MockedRoomContext.Provider, { value: { room }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PluvRoomContext.Provider, { value: room, children }) });
130
129
  });
131
130
  MockedRoomProvider.displayName = "MockedRoomProvider";
132
131
  const PluvRoomProvider = (0, import_react4.memo)((props) => {
133
- const { children, debug, initialPresence, initialStorage, onAuthorizationFail, room: _room } = props;
132
+ const { children, debug, initialPresence, initialStorage, metadata, onAuthorizationFail, room: _room } = props;
134
133
  const rerender = useRerender();
135
134
  const { room: mockedRoom } = (0, import_react4.useContext)(MockedRoomContext);
136
135
  const [room] = (0, import_react4.useState)(() => {
@@ -139,8 +138,9 @@ var createRoomBundle = (client, options) => {
139
138
  debug,
140
139
  initialPresence,
141
140
  initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
142
- presence: options.presence,
143
- onAuthorizationFail
141
+ metadata,
142
+ onAuthorizationFail,
143
+ router: options.router
144
144
  });
145
145
  });
146
146
  (0, import_react4.useEffect)(() => {
package/dist/index.mjs CHANGED
@@ -86,15 +86,14 @@ var createRoomBundle = (client, options) => {
86
86
  return new MockedRoom(_room, {
87
87
  events,
88
88
  initialPresence,
89
- initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
90
- presence: options.presence
89
+ initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage
91
90
  });
92
91
  });
93
92
  return /* @__PURE__ */ jsx(MockedRoomContext.Provider, { value: { room }, children: /* @__PURE__ */ jsx(PluvRoomContext.Provider, { value: room, children }) });
94
93
  });
95
94
  MockedRoomProvider.displayName = "MockedRoomProvider";
96
95
  const PluvRoomProvider = memo((props) => {
97
- const { children, debug, initialPresence, initialStorage, onAuthorizationFail, room: _room } = props;
96
+ const { children, debug, initialPresence, initialStorage, metadata, onAuthorizationFail, room: _room } = props;
98
97
  const rerender = useRerender();
99
98
  const { room: mockedRoom } = useContext(MockedRoomContext);
100
99
  const [room] = useState3(() => {
@@ -103,8 +102,9 @@ var createRoomBundle = (client, options) => {
103
102
  debug,
104
103
  initialPresence,
105
104
  initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
106
- presence: options.presence,
107
- onAuthorizationFail
105
+ metadata,
106
+ onAuthorizationFail,
107
+ router: options.router
108
108
  });
109
109
  });
110
110
  useEffect2(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "0.25.4",
3
+ "version": "0.27.0",
4
4
  "description": "@pluv/client React.js bindings",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,22 +18,22 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "fast-deep-equal": "^3.1.3",
21
- "@pluv/client": "^0.25.4",
22
- "@pluv/crdt": "^0.25.4",
23
- "@pluv/types": "^0.25.4"
21
+ "@pluv/crdt": "^0.27.0",
22
+ "@pluv/types": "^0.27.0",
23
+ "@pluv/client": "^0.27.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@types/react": "^18.0.0",
27
27
  "react": "^18.0.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/react": "^18.3.4",
30
+ "@types/react": "^18.3.6",
31
31
  "eslint": "^8.57.0",
32
32
  "react": "^18.3.1",
33
- "tsup": "^8.2.4",
34
- "typescript": "^5.5.4",
35
- "@pluv/tsconfig": "^0.25.4",
36
- "eslint-config-pluv": "^0.25.4"
33
+ "tsup": "^8.3.0",
34
+ "typescript": "^5.6.2",
35
+ "@pluv/tsconfig": "^0.27.0",
36
+ "eslint-config-pluv": "^0.27.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup src/index.ts --format esm,cjs --dts --external react",
@@ -1,4 +1,4 @@
1
- import type { PluvClient } from "@pluv/client";
1
+ import type { PluvClient, PluvRouterEventConfig } from "@pluv/client";
2
2
  import type { CrdtType } from "@pluv/crdt";
3
3
  import type { IOLike, JsonObject } from "@pluv/types";
4
4
  import type { FC, ReactNode } from "react";
@@ -10,20 +10,32 @@ export interface PluvProviderProps {
10
10
  children?: ReactNode;
11
11
  }
12
12
 
13
- export interface CreateBundle<TIO extends IOLike> {
13
+ export interface CreateBundle<
14
+ TIO extends IOLike,
15
+ TMetadata extends JsonObject,
16
+ TPresence extends JsonObject = {},
17
+ TStorage extends Record<string, CrdtType<any, any>> = {},
18
+ > {
14
19
  // factories
15
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
16
- options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
17
- ) => CreateRoomBundle<TIO, TPresence, TStorage>;
20
+ createRoomBundle: <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(
21
+ options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>,
22
+ ) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents>;
18
23
 
19
24
  // components
20
25
  PluvProvider: FC<PluvProviderProps>;
21
26
 
22
27
  // hooks
23
- useClient: () => PluvClient<TIO>;
28
+ useClient: () => PluvClient<TIO, TPresence, TStorage, TMetadata>;
24
29
  }
25
30
 
26
- export const createBundle = <TIO extends IOLike>(client: PluvClient<TIO>): CreateBundle<TIO> => {
31
+ export const createBundle = <
32
+ TIO extends IOLike,
33
+ TMetadata extends JsonObject = {},
34
+ TPresence extends JsonObject = {},
35
+ TStorage extends Record<string, CrdtType<any, any>> = {},
36
+ >(
37
+ client: PluvClient<TIO, TPresence, TStorage, TMetadata>,
38
+ ): CreateBundle<TIO, TMetadata, TPresence, TStorage> => {
27
39
  /**
28
40
  * !HACK
29
41
  * @description We'll let the context error out if client is not provided,
@@ -31,7 +43,7 @@ export const createBundle = <TIO extends IOLike>(client: PluvClient<TIO>): Creat
31
43
  * @author leedavidcs
32
44
  * @date October 27, 2022
33
45
  */
34
- const PluvContext = createContext<PluvClient<any>>(null as any);
46
+ const PluvContext = createContext<PluvClient<TIO, TPresence, TStorage, TMetadata>>(null as any);
35
47
 
36
48
  const PluvProvider = memo<PluvProviderProps>((props) => {
37
49
  const { children } = props;
@@ -41,14 +53,11 @@ export const createBundle = <TIO extends IOLike>(client: PluvClient<TIO>): Creat
41
53
 
42
54
  PluvProvider.displayName = "PluvProvider";
43
55
 
44
- const useClient = (): PluvClient<TIO> => useContext(PluvContext);
56
+ const useClient = (): PluvClient<TIO, TPresence, TStorage, TMetadata> => useContext(PluvContext);
45
57
 
46
- const createRoomBundle = <
47
- TPresence extends JsonObject = {},
48
- TStorage extends Record<string, CrdtType<any, any>> = {},
49
- >(
50
- options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
51
- ): CreateRoomBundle<TIO, TPresence, TStorage> => _createRoomBundle(client, options);
58
+ const createRoomBundle = <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(
59
+ options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>,
60
+ ): CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents> => _createRoomBundle(client, options);
52
61
 
53
62
  return {
54
63
  createRoomBundle,
@@ -1,17 +1,21 @@
1
1
  import type {
2
2
  AbstractRoom,
3
+ CreateRoomOptions,
4
+ MergeEvents,
3
5
  MockedRoomEvents,
4
6
  PluvClient,
5
7
  PluvRoom,
6
8
  PluvRoomAddon,
7
9
  PluvRoomDebug,
10
+ PluvRouter,
11
+ PluvRouterEventConfig,
8
12
  UserInfo,
9
13
  WebSocketConnection,
10
14
  } from "@pluv/client";
11
15
  import { MockedRoom } from "@pluv/client";
12
16
  import type { AbstractCrdtDoc, AbstractCrdtDocFactory, CrdtType, InferCrdtJson } from "@pluv/crdt";
13
17
  import { noop } from "@pluv/crdt";
14
- import type { IOEventMessage, IOLike, Id, InferIOInput, InferIOOutput, InputZodLike, JsonObject } from "@pluv/types";
18
+ import type { IOEventMessage, IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
15
19
  import fastDeepEqual from "fast-deep-equal";
16
20
  import type { Dispatch, FC, ReactNode } from "react";
17
21
  import { createContext, memo, useCallback, useContext, useEffect, useMemo, useState } from "react";
@@ -19,12 +23,14 @@ import { identity, shallowArrayEqual, useRerender, useSyncExternalStoreWithSelec
19
23
 
20
24
  export type CreateRoomBundleOptions<
21
25
  TIO extends IOLike,
26
+ TMetadata extends JsonObject = {},
22
27
  TPresence extends JsonObject = {},
23
28
  TStorage extends Record<string, CrdtType<any, any>> = {},
29
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
24
30
  > = {
25
- addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
31
+ addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
26
32
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
27
- presence?: InputZodLike<TPresence>;
33
+ router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
28
34
  };
29
35
 
30
36
  type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
@@ -37,18 +43,20 @@ export type MockedRoomProviderProps<
37
43
  TIO extends IOLike,
38
44
  TPresence extends JsonObject,
39
45
  TStorage extends Record<string, CrdtType<any, any>>,
46
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
40
47
  > = BaseRoomProviderProps<TPresence, TStorage> & {
41
- events?: MockedRoomEvents<TIO>;
48
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
42
49
  };
43
50
 
44
51
  export type PluvRoomProviderProps<
45
52
  TIO extends IOLike,
53
+ TMetadata extends JsonObject,
46
54
  TPresence extends JsonObject,
47
55
  TStorage extends Record<string, CrdtType<any, any>>,
48
56
  > = BaseRoomProviderProps<TPresence, TStorage> & {
49
57
  debug?: boolean | PluvRoomDebug<TIO>;
50
58
  onAuthorizationFail?: (error: Error) => void;
51
- };
59
+ } & (keyof TMetadata extends never ? { metadata?: undefined } : { metadata: TMetadata });
52
60
 
53
61
  export interface SubscriptionHookOptions<T extends unknown> {
54
62
  isEqual?: (a: T, b: T) => boolean;
@@ -73,18 +81,20 @@ export type EventProxy<TIO extends IOLike> = {
73
81
 
74
82
  export interface CreateRoomBundle<
75
83
  TIO extends IOLike,
84
+ TMetadata extends JsonObject,
76
85
  TPresence extends JsonObject,
77
86
  TStorage extends Record<string, CrdtType<any, any>>,
87
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
78
88
  > {
79
89
  // components
80
90
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
81
- PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
91
+ PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>;
82
92
 
83
93
  // proxies
84
- event: EventProxy<TIO>;
94
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
85
95
 
86
96
  // hooks
87
- useBroadcast: () => BroadcastProxy<TIO>;
97
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
88
98
  useCanRedo: () => boolean;
89
99
  useCanUndo: () => boolean;
90
100
  useConnection: <T extends unknown = WebSocketConnection>(
@@ -92,9 +102,9 @@ export interface CreateRoomBundle<
92
102
  options?: SubscriptionHookOptions<Id<T>>,
93
103
  ) => Id<T>;
94
104
  useDoc: () => AbstractCrdtDoc<TStorage>;
95
- useEvent: <TType extends keyof InferIOOutput<TIO>>(
105
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
96
106
  type: TType,
97
- callback: (data: Id<IOEventMessage<TIO, TType>>) => void,
107
+ callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,
98
108
  ) => void;
99
109
  useMyPresence: <T extends unknown = TPresence>(
100
110
  selector?: (myPresence: TPresence) => T,
@@ -124,17 +134,19 @@ export interface CreateRoomBundle<
124
134
  useUndo: () => () => void;
125
135
  }
126
136
 
127
- export type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any>> =
128
- TRoomBundle extends CreateRoomBundle<any, any, infer IStorage> ? IStorage : never;
137
+ export type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any, any>> =
138
+ TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
129
139
 
130
140
  export const createRoomBundle = <
131
141
  TIO extends IOLike,
142
+ TMetadata extends JsonObject = {},
132
143
  TPresence extends JsonObject = {},
133
144
  TStorage extends Record<string, CrdtType<any, any>> = {},
145
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
134
146
  >(
135
- client: PluvClient<TIO>,
136
- options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
137
- ): CreateRoomBundle<TIO, TPresence, TStorage> => {
147
+ client: PluvClient<TIO, TPresence, TStorage, TMetadata>,
148
+ options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>,
149
+ ): CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents> => {
138
150
  const _initialStorage = (options.initialStorage ?? noop.doc()) as AbstractCrdtDocFactory<TStorage>;
139
151
 
140
152
  /**
@@ -153,13 +165,12 @@ export const createRoomBundle = <
153
165
  const MockedRoomProvider = memo<MockedRoomProviderProps<TIO, TPresence, TStorage>>((props) => {
154
166
  const { children, events, initialPresence, initialStorage, room: _room } = props;
155
167
 
156
- const [room] = useState<MockedRoom<TIO, TPresence, TStorage>>(() => {
157
- return new MockedRoom<TIO, TPresence, TStorage>(_room, {
168
+ const [room] = useState<MockedRoom<TIO, TPresence, TStorage, TEvents>>(() => {
169
+ return new MockedRoom<TIO, TPresence, TStorage, TEvents>(_room, {
158
170
  events,
159
171
  initialPresence,
160
172
  initialStorage:
161
173
  typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
162
- presence: options.presence,
163
174
  });
164
175
  });
165
176
 
@@ -172,22 +183,23 @@ export const createRoomBundle = <
172
183
 
173
184
  MockedRoomProvider.displayName = "MockedRoomProvider";
174
185
 
175
- const PluvRoomProvider = memo<PluvRoomProviderProps<TIO, TPresence, TStorage>>((props) => {
176
- const { children, debug, initialPresence, initialStorage, onAuthorizationFail, room: _room } = props;
186
+ const PluvRoomProvider = memo<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>((props) => {
187
+ const { children, debug, initialPresence, initialStorage, metadata, onAuthorizationFail, room: _room } = props;
177
188
 
178
189
  const rerender = useRerender();
179
190
  const { room: mockedRoom } = useContext(MockedRoomContext);
180
191
 
181
- const [room] = useState<PluvRoom<TIO, TPresence, TStorage>>(() => {
182
- return client.createRoom<TPresence, TStorage>(_room, {
192
+ const [room] = useState<PluvRoom<TIO, TMetadata, TPresence, TStorage, TEvents>>(() => {
193
+ return client.createRoom(_room, {
183
194
  addons: options.addons,
184
195
  debug,
185
196
  initialPresence,
186
197
  initialStorage:
187
198
  typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
188
- presence: options.presence,
199
+ metadata,
189
200
  onAuthorizationFail,
190
- });
201
+ router: options.router,
202
+ } as CreateRoomOptions<TIO, TPresence, TStorage, TMetadata, TEvents>);
191
203
  });
192
204
 
193
205
  useEffect(() => {
@@ -215,24 +227,31 @@ export const createRoomBundle = <
215
227
 
216
228
  const useRoom = () => useContext(PluvRoomContext);
217
229
 
218
- const useBroadcast = (): BroadcastProxy<TIO> => {
230
+ const useBroadcast = (): BroadcastProxy<MergeEvents<TEvents, TIO>> => {
219
231
  const room = useRoom();
220
232
 
221
233
  const broadcast = useCallback(
222
- <TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => {
234
+ <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
235
+ event: TEvent,
236
+ data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
237
+ ) => {
223
238
  room.broadcast(event, data);
224
239
  },
225
240
  [room],
226
241
  );
227
242
 
228
- return useMemo((): BroadcastProxy<TIO> => {
243
+ return useMemo((): BroadcastProxy<MergeEvents<TEvents, TIO>> => {
229
244
  return new Proxy(broadcast, {
230
245
  get(fn, prop) {
231
- return (data: Id<InferIOInput<TIO>[keyof InferIOInput<TIO>]>): void => {
232
- return fn(prop as keyof InferIOInput<TIO>, data);
246
+ return (
247
+ data: Id<
248
+ InferIOInput<MergeEvents<TEvents, TIO>>[keyof InferIOInput<MergeEvents<TEvents, TIO>>]
249
+ >,
250
+ ): void => {
251
+ return fn(prop as keyof InferIOInput<MergeEvents<TEvents, TIO>>, data);
233
252
  };
234
253
  },
235
- }) as BroadcastProxy<TIO>;
254
+ }) as BroadcastProxy<MergeEvents<TEvents, TIO>>;
236
255
  }, [broadcast]);
237
256
  };
238
257
 
@@ -292,9 +311,9 @@ export const createRoomBundle = <
292
311
  return room.getDoc();
293
312
  };
294
313
 
295
- const useEvent = <TType extends keyof InferIOOutput<TIO>>(
314
+ const useEvent = <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
296
315
  type: TType,
297
- callback: (data: Id<IOEventMessage<TIO, TType>>) => void,
316
+ callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,
298
317
  ): void => {
299
318
  const room = useRoom();
300
319
 
@@ -312,15 +331,19 @@ export const createRoomBundle = <
312
331
  {
313
332
  get(_, prop) {
314
333
  const useProxyEvent = (
315
- callback: (data: Id<IOEventMessage<TIO, keyof InferIOOutput<TIO>>>) => void,
334
+ callback: (
335
+ data: Id<
336
+ IOEventMessage<MergeEvents<TEvents, TIO>, keyof InferIOOutput<MergeEvents<TEvents, TIO>>>
337
+ >,
338
+ ) => void,
316
339
  ): void => {
317
- return useEvent(prop as keyof InferIOOutput<TIO>, callback);
340
+ return useEvent(prop as keyof InferIOOutput<MergeEvents<TEvents, TIO>>, callback);
318
341
  };
319
342
 
320
343
  return { useEvent: useProxyEvent };
321
344
  },
322
345
  },
323
- ) as EventProxy<TIO>;
346
+ ) as EventProxy<MergeEvents<TEvents, TIO>>;
324
347
 
325
348
  const useMyPresence = <T extends unknown = TPresence>(
326
349
  selector = identity as (myPresence: TPresence) => T,
@@ -26,7 +26,7 @@ export const useSyncExternalStoreWithSelector = <TSnapshot, TSelection>(
26
26
  * @description Memoize only the last snapshot so that we can only rerender on changes
27
27
  * @date June 22, 2024
28
28
  */
29
- cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
29
+ cacheRef.current = isUnchanged ? (cached ?? snapshot) : snapshot;
30
30
 
31
31
  return cacheRef.current;
32
32
  }
@@ -45,7 +45,7 @@ export const useSyncExternalStoreWithSelector = <TSnapshot, TSelection>(
45
45
  * @description Memoize only the last snapshot so that we can only rerender on changes
46
46
  * @date June 22, 2024
47
47
  */
48
- cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
48
+ cacheRef.current = isUnchanged ? (cached ?? snapshot) : snapshot;
49
49
 
50
50
  return cacheRef.current;
51
51
  };
@@ -1,2 +1,2 @@
1
- export { useRerender, useSyncExternalStore, useSyncExternalStoreWithSelector } from "./hooks";
1
+ export { useRerender, useSyncExternalStoreWithSelector } from "./hooks";
2
2
  export { identity, shallowArrayEqual } from "./utils";