@pluv/react 0.17.3 → 0.19.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.
package/dist/index.mjs CHANGED
@@ -8,26 +8,33 @@ import { createContext as createContext2, memo as memo2, useContext as useContex
8
8
  import { MockedRoom } from "@pluv/client";
9
9
  import { noop } from "@pluv/crdt";
10
10
  import fastDeepEqual from "fast-deep-equal";
11
- import {
12
- createContext,
13
- memo,
14
- useCallback as useCallback2,
15
- useContext,
16
- useEffect,
17
- useState as useState2
18
- } from "react";
11
+ import { createContext, memo, useCallback as useCallback3, useContext, useEffect as useEffect2, useMemo, useState as useState3 } from "react";
12
+
13
+ // src/internal/hooks/useNoSsr.ts
14
+ import { useCallback, useEffect, useState } from "react";
15
+ var useNoSsr = () => {
16
+ const [didMount, setDidMount] = useState(false);
17
+ useEffect(() => setDidMount(typeof window !== "undefined"), []);
18
+ const noSsr = useCallback(
19
+ (value, fallback = null) => {
20
+ return didMount ? typeof value !== "function" ? value : value() : fallback;
21
+ },
22
+ [didMount]
23
+ );
24
+ return noSsr;
25
+ };
19
26
 
20
27
  // src/internal/hooks/useRerender.ts
21
- import { useCallback, useState } from "react";
28
+ import { useCallback as useCallback2, useState as useState2 } from "react";
22
29
  var useRerender = () => {
23
- const [, setCount] = useState(0);
24
- return useCallback(() => {
30
+ const [, setCount] = useState2(0);
31
+ return useCallback2(() => {
25
32
  setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
26
33
  }, []);
27
34
  };
28
35
 
29
36
  // src/internal/hooks/useSyncExternalStoreWithSelector.ts
30
- import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector.js";
37
+ import { useRef, useSyncExternalStore } from "react";
31
38
 
32
39
  // src/internal/utils/identity.ts
33
40
  var identity = (x) => x;
@@ -37,6 +44,30 @@ var shallowArrayEqual = (a, b) => {
37
44
  return a.length === b.length && a.every((item, i) => item === b[i]);
38
45
  };
39
46
 
47
+ // src/internal/utils/shallowEqual.ts
48
+ var shallowEqual = (a, b) => a === b;
49
+
50
+ // src/internal/hooks/useSyncExternalStoreWithSelector.ts
51
+ var useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerSnapshot, selector, isEqual) => {
52
+ const noSsr = useNoSsr();
53
+ const cacheRef = useRef(null);
54
+ const _getServerSnapshot = getServerSnapshot ? () => {
55
+ const data = getServerSnapshot();
56
+ return selector(data);
57
+ } : void 0;
58
+ const _getClientSnapshot = () => {
59
+ const data = getClientSnapshot();
60
+ const snapshot = selector(data);
61
+ const compare = isEqual != null ? isEqual : shallowEqual;
62
+ const cached = cacheRef.current;
63
+ const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
64
+ cacheRef.current = isUnchanged ? cached != null ? cached : snapshot : snapshot;
65
+ return cacheRef.current;
66
+ };
67
+ const result = useSyncExternalStore(subscribe, _getClientSnapshot, _getServerSnapshot);
68
+ return typeof window !== "undefined" ? noSsr(result, _getServerSnapshot == null ? void 0 : _getServerSnapshot()) : result;
69
+ };
70
+
40
71
  // src/createRoomBundle.tsx
41
72
  import { jsx } from "react/jsx-runtime";
42
73
  var createRoomBundle = (client, options) => {
@@ -45,14 +76,8 @@ var createRoomBundle = (client, options) => {
45
76
  const PluvRoomContext = createContext(null);
46
77
  const MockedRoomContext = createContext({ room: null });
47
78
  const MockedRoomProvider = memo((props) => {
48
- const {
49
- children,
50
- events,
51
- initialPresence,
52
- initialStorage,
53
- room: _room
54
- } = props;
55
- const [room] = useState2(() => {
79
+ const { children, events, initialPresence, initialStorage, room: _room } = props;
80
+ const [room] = useState3(() => {
56
81
  return new MockedRoom(_room, {
57
82
  events,
58
83
  initialPresence,
@@ -64,17 +89,10 @@ var createRoomBundle = (client, options) => {
64
89
  });
65
90
  MockedRoomProvider.displayName = "MockedRoomProvider";
66
91
  const PluvRoomProvider = memo((props) => {
67
- const {
68
- children,
69
- debug,
70
- initialPresence,
71
- initialStorage,
72
- onAuthorizationFail,
73
- room: _room
74
- } = props;
92
+ const { children, debug, initialPresence, initialStorage, onAuthorizationFail, room: _room } = props;
75
93
  const rerender = useRerender();
76
94
  const { room: mockedRoom } = useContext(MockedRoomContext);
77
- const [room] = useState2(() => {
95
+ const [room] = useState3(() => {
78
96
  return client.createRoom(_room, {
79
97
  addons: options.addons,
80
98
  debug,
@@ -84,7 +102,7 @@ var createRoomBundle = (client, options) => {
84
102
  onAuthorizationFail
85
103
  });
86
104
  });
87
- useEffect(() => {
105
+ useEffect2(() => {
88
106
  const unsubscribe = room.subscribe("connection", () => {
89
107
  rerender();
90
108
  });
@@ -92,7 +110,7 @@ var createRoomBundle = (client, options) => {
92
110
  unsubscribe();
93
111
  };
94
112
  }, [rerender, room]);
95
- useEffect(() => {
113
+ useEffect2(() => {
96
114
  client.enter(room);
97
115
  return () => {
98
116
  client.leave(room);
@@ -104,57 +122,47 @@ var createRoomBundle = (client, options) => {
104
122
  const useRoom = () => useContext(PluvRoomContext);
105
123
  const useBroadcast = () => {
106
124
  const room = useRoom();
107
- return useCallback2(
108
- (event, data) => {
109
- room.broadcast(event, data);
125
+ const broadcast = useCallback3(
126
+ (event2, data) => {
127
+ room.broadcast(event2, data);
110
128
  },
111
129
  [room]
112
130
  );
131
+ return useMemo(() => {
132
+ return new Proxy(broadcast, {
133
+ get(fn, prop) {
134
+ return (data) => {
135
+ return fn(prop, data);
136
+ };
137
+ }
138
+ });
139
+ }, [broadcast]);
113
140
  };
114
141
  const useCanRedo = () => {
115
142
  const room = useRoom();
116
- const subscribe = useCallback2(
117
- (onStoreChange) => room.storageRoot(onStoreChange),
118
- [room]
119
- );
120
- const getSnapshot = useCallback2(() => room.canRedo(), [room]);
121
- const canRedo = useSyncExternalStoreWithSelector(
122
- subscribe,
123
- getSnapshot,
124
- getSnapshot,
125
- identity
126
- );
143
+ const subscribe = useCallback3((onStoreChange) => room.storageRoot(onStoreChange), [room]);
144
+ const getSnapshot = useCallback3(() => room.canRedo(), [room]);
145
+ const canRedo = useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, identity);
127
146
  return canRedo;
128
147
  };
129
148
  const useCanUndo = () => {
130
149
  const room = useRoom();
131
- const subscribe = useCallback2(
132
- (onStoreChange) => room.storageRoot(onStoreChange),
133
- [room]
134
- );
135
- const getSnapshot = useCallback2(() => room.canUndo(), [room]);
136
- const canRedo = useSyncExternalStoreWithSelector(
137
- subscribe,
138
- getSnapshot,
139
- getSnapshot,
140
- identity
141
- );
142
- return canRedo;
150
+ const subscribe = useCallback3((onStoreChange) => room.storageRoot(onStoreChange), [room]);
151
+ const getSnapshot = useCallback3(() => room.canUndo(), [room]);
152
+ const canUndo = useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, identity);
153
+ return canUndo;
143
154
  };
144
155
  const useConnection = (selector = identity, options2) => {
145
156
  var _a2;
146
157
  const room = useRoom();
147
- const subscribe = useCallback2(
158
+ const subscribe = useCallback3(
148
159
  (onStoreChange) => {
149
160
  return room.subscribe("connection", onStoreChange);
150
161
  },
151
162
  [room]
152
163
  );
153
164
  const getSnapshot = room.getConnection;
154
- const _selector = useCallback2(
155
- (snapshot) => selector(snapshot),
156
- [selector]
157
- );
165
+ const _selector = useCallback3((snapshot) => selector(snapshot), [selector]);
158
166
  return useSyncExternalStoreWithSelector(
159
167
  subscribe,
160
168
  getSnapshot,
@@ -169,27 +177,35 @@ var createRoomBundle = (client, options) => {
169
177
  };
170
178
  const useEvent = (type, callback) => {
171
179
  const room = useRoom();
172
- useEffect(() => {
180
+ useEffect2(() => {
173
181
  const unsubscribe = room.event(type, callback);
174
182
  return () => {
175
183
  unsubscribe();
176
184
  };
177
185
  }, [callback, room, type]);
178
186
  };
187
+ const event = new Proxy(
188
+ {},
189
+ {
190
+ get(_, prop) {
191
+ const useProxyEvent = (callback) => {
192
+ return useEvent(prop, callback);
193
+ };
194
+ return { useEvent: useProxyEvent };
195
+ }
196
+ }
197
+ );
179
198
  const useMyPresence = (selector = identity, options2) => {
180
199
  var _a2;
181
200
  const room = useRoom();
182
- const subscribe = useCallback2(
201
+ const subscribe = useCallback3(
183
202
  (onStoreChange) => {
184
203
  return room.subscribe("my-presence", onStoreChange);
185
204
  },
186
205
  [room]
187
206
  );
188
207
  const getSnapshot = room.getMyPresence;
189
- const _selector = useCallback2(
190
- (snapshot) => selector(snapshot),
191
- [selector]
192
- );
208
+ const _selector = useCallback3((snapshot) => selector(snapshot), [selector]);
193
209
  const myPresence = useSyncExternalStoreWithSelector(
194
210
  subscribe,
195
211
  getSnapshot,
@@ -197,26 +213,19 @@ var createRoomBundle = (client, options) => {
197
213
  _selector,
198
214
  (_a2 = options2 == null ? void 0 : options2.isEqual) != null ? _a2 : fastDeepEqual
199
215
  );
200
- const updateMyPresence = useCallback2(
201
- (value) => {
202
- const presence = typeof value === "function" ? value(room.getMyPresence()) : value;
203
- room.updateMyPresence(presence);
204
- },
205
- [room]
206
- );
207
- return [myPresence, updateMyPresence];
216
+ return [myPresence, room.updateMyPresence];
208
217
  };
209
218
  const useMyself = (selector = identity, options2) => {
210
219
  var _a2;
211
220
  const room = useRoom();
212
- const subscribe = useCallback2(
221
+ const subscribe = useCallback3(
213
222
  (onStoreChange) => {
214
223
  return room.subscribe("myself", onStoreChange);
215
224
  },
216
225
  [room]
217
226
  );
218
227
  const getSnapshot = room.getMyself;
219
- const _selector = useCallback2(
228
+ const _selector = useCallback3(
220
229
  (snapshot) => {
221
230
  return !snapshot ? null : selector(snapshot);
222
231
  },
@@ -233,17 +242,14 @@ var createRoomBundle = (client, options) => {
233
242
  const useOther = (connectionId, selector = identity, options2) => {
234
243
  var _a2;
235
244
  const room = useRoom();
236
- const subscribe = useCallback2(
245
+ const subscribe = useCallback3(
237
246
  (onStoreChange) => {
238
247
  return room.other(connectionId, onStoreChange);
239
248
  },
240
249
  [room, connectionId]
241
250
  );
242
- const getSnapshot = useCallback2(
243
- () => room.getOther(connectionId),
244
- [room, connectionId]
245
- );
246
- const _selector = useCallback2(
251
+ const getSnapshot = useCallback3(() => room.getOther(connectionId), [room, connectionId]);
252
+ const _selector = useCallback3(
247
253
  (snapshot) => {
248
254
  return !snapshot ? null : selector(snapshot);
249
255
  },
@@ -260,7 +266,7 @@ var createRoomBundle = (client, options) => {
260
266
  const useOthers = (selector = identity, options2) => {
261
267
  var _a2;
262
268
  const room = useRoom();
263
- const subscribe = useCallback2(
269
+ const subscribe = useCallback3(
264
270
  (onStoreChange) => {
265
271
  return room.subscribe("others", onStoreChange);
266
272
  },
@@ -272,7 +278,12 @@ var createRoomBundle = (client, options) => {
272
278
  getSnapshot,
273
279
  getSnapshot,
274
280
  selector,
275
- (_a2 = options2 == null ? void 0 : options2.isEqual) != null ? _a2 : shallowArrayEqual
281
+ (_a2 = options2 == null ? void 0 : options2.isEqual) != null ? _a2 : (a, b) => {
282
+ if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
283
+ return shallowArrayEqual(a, b);
284
+ }
285
+ return fastDeepEqual(a, b);
286
+ }
276
287
  );
277
288
  };
278
289
  const useRedo = () => {
@@ -286,15 +297,12 @@ var createRoomBundle = (client, options) => {
286
297
  room.subscribe("storage-loaded", () => {
287
298
  rerender();
288
299
  });
289
- const subscribe = useCallback2(
290
- (onStoreChange) => room.storage(key, onStoreChange),
291
- [key, room]
292
- );
293
- const getSnapshot = useCallback2(() => {
300
+ const subscribe = useCallback3((onStoreChange) => room.storage(key, onStoreChange), [key, room]);
301
+ const getSnapshot = useCallback3(() => {
294
302
  var _a3;
295
303
  return (_a3 = room.getStorage(key)) == null ? void 0 : _a3.toJson();
296
304
  }, [key, room]);
297
- const _selector = useCallback2(
305
+ const _selector = useCallback3(
298
306
  (snapshot) => {
299
307
  return snapshot === null ? null : selector(snapshot);
300
308
  },
@@ -322,6 +330,8 @@ var createRoomBundle = (client, options) => {
322
330
  // components
323
331
  MockedRoomProvider,
324
332
  PluvRoomProvider,
333
+ // proxies
334
+ event,
325
335
  // hooks
326
336
  useBroadcast,
327
337
  useCanRedo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "0.17.3",
3
+ "version": "0.19.0",
4
4
  "description": "@pluv/client React.js bindings",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -17,24 +17,23 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@types/react": "^18.2.78",
21
- "@types/use-sync-external-store": "^0.0.6",
22
20
  "fast-deep-equal": "^3.1.3",
23
- "use-sync-external-store": "^1.2.0",
24
- "@pluv/client": "^0.17.3",
25
- "@pluv/crdt": "^0.17.3",
26
- "@pluv/types": "^0.17.3"
21
+ "@pluv/client": "^0.19.0",
22
+ "@pluv/crdt": "^0.19.0",
23
+ "@pluv/types": "^0.19.0"
27
24
  },
28
25
  "peerDependencies": {
29
- "react": "^17.0.0 || ^18.0.0"
26
+ "@types/react": "^18.0.0",
27
+ "react": "^18.0.0"
30
28
  },
31
29
  "devDependencies": {
30
+ "@types/react": "^18.3.3",
32
31
  "eslint": "^8.57.0",
33
- "react": "^18.2.0",
34
- "tsup": "^8.0.2",
32
+ "react": "^18.3.1",
33
+ "tsup": "^8.1.0",
35
34
  "typescript": "^5.4.5",
36
- "@pluv/tsconfig": "^0.17.3",
37
- "eslint-config-pluv": "^0.17.3"
35
+ "@pluv/tsconfig": "^0.19.0",
36
+ "eslint-config-pluv": "^0.19.0"
38
37
  },
39
38
  "scripts": {
40
39
  "build": "tsup src/index.ts --format esm,cjs --dts --external react",
@@ -3,10 +3,7 @@ import type { AbstractCrdtType } from "@pluv/crdt";
3
3
  import type { IOLike, JsonObject } from "@pluv/types";
4
4
  import type { FC, ReactNode } from "react";
5
5
  import { createContext, memo, useContext } from "react";
6
- import type {
7
- CreateRoomBundle,
8
- CreateRoomBundleOptions,
9
- } from "./createRoomBundle";
6
+ import type { CreateRoomBundle, CreateRoomBundleOptions } from "./createRoomBundle";
10
7
  import { createRoomBundle as _createRoomBundle } from "./createRoomBundle";
11
8
 
12
9
  export interface PluvProviderProps {
@@ -29,9 +26,7 @@ export interface CreateBundle<TIO extends IOLike> {
29
26
  useClient: () => PluvClient<TIO>;
30
27
  }
31
28
 
32
- export const createBundle = <TIO extends IOLike>(
33
- client: PluvClient<TIO>,
34
- ): CreateBundle<TIO> => {
29
+ export const createBundle = <TIO extends IOLike>(client: PluvClient<TIO>): CreateBundle<TIO> => {
35
30
  /**
36
31
  * !HACK
37
32
  * @description We'll let the context error out if client is not provided,
@@ -44,11 +39,7 @@ export const createBundle = <TIO extends IOLike>(
44
39
  const PluvProvider = memo<PluvProviderProps>((props) => {
45
40
  const { children } = props;
46
41
 
47
- return (
48
- <PluvContext.Provider value={client}>
49
- {children}
50
- </PluvContext.Provider>
51
- );
42
+ return <PluvContext.Provider value={client}>{children}</PluvContext.Provider>;
52
43
  });
53
44
 
54
45
  PluvProvider.displayName = "PluvProvider";
@@ -60,8 +51,7 @@ export const createBundle = <TIO extends IOLike>(
60
51
  TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
61
52
  >(
62
53
  options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
63
- ): CreateRoomBundle<TIO, TPresence, TStorage> =>
64
- _createRoomBundle(client, options);
54
+ ): CreateRoomBundle<TIO, TPresence, TStorage> => _createRoomBundle(client, options);
65
55
 
66
56
  return {
67
57
  createRoomBundle,