@pluv/react 3.2.2 → 4.0.1

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
@@ -1,571 +1,425 @@
1
- var __defProp = Object.defineProperty;
2
- var __defProps = Object.defineProperties;
3
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __spreadValues = (a, b) => {
9
- for (var prop in b || (b = {}))
10
- if (__hasOwnProp.call(b, prop))
11
- __defNormalProp(a, prop, b[prop]);
12
- if (__getOwnPropSymbols)
13
- for (var prop of __getOwnPropSymbols(b)) {
14
- if (__propIsEnum.call(b, prop))
15
- __defNormalProp(a, prop, b[prop]);
16
- }
17
- return a;
18
- };
19
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
- var __async = (__this, __arguments, generator) => {
21
- return new Promise((resolve, reject) => {
22
- var fulfilled = (value) => {
23
- try {
24
- step(generator.next(value));
25
- } catch (e) {
26
- reject(e);
27
- }
28
- };
29
- var rejected = (value) => {
30
- try {
31
- step(generator.throw(value));
32
- } catch (e) {
33
- reject(e);
34
- }
35
- };
36
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
- step((generator = generator.apply(__this, __arguments)).next());
38
- });
39
- };
40
-
41
- // src/createBundle.tsx
42
1
  import { MockedRoom } from "@pluv/client";
43
- import fastDeepEqual2 from "fast-deep-equal";
44
- import {
45
- createContext,
46
- memo,
47
- useCallback as useCallback4,
48
- useContext,
49
- useEffect as useEffect3,
50
- useMemo as useMemo2,
51
- useState as useState4,
52
- useSyncExternalStore as useSyncExternalStore2
53
- } from "react";
2
+ import fastDeepEqual from "fast-deep-equal";
3
+ import { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
+ import { jsx } from "react/jsx-runtime";
54
5
 
55
- // src/internal/hooks/useAsyncQueue.ts
56
- import { useCallback, useMemo, useRef } from "react";
57
- var useAsyncQueue = () => {
58
- const queueRef = useRef(Promise.resolve(void 0));
59
- const push = useCallback((promise) => __async(null, null, function* () {
60
- queueRef.current = queueRef.current.then(() => __async(null, null, function* () {
61
- return yield promise;
62
- }));
63
- return yield queueRef.current;
64
- }), []);
65
- return useMemo(() => ({ push }), [push]);
6
+ //#region src/internal/hooks/useAsyncQueue.ts
7
+ /**
8
+ * @description This is to handle race conditions, so that our async operations that can
9
+ * happen between re-renders will happen sequentially.
10
+ * @date April 13, 2025
11
+ */
12
+ const useAsyncQueue = () => {
13
+ const queueRef = useRef(Promise.resolve(void 0));
14
+ const push = useCallback(async (promise) => {
15
+ queueRef.current = queueRef.current.then(async () => await promise);
16
+ return await queueRef.current;
17
+ }, []);
18
+ return useMemo(() => ({ push }), [push]);
66
19
  };
67
20
 
68
- // src/internal/hooks/useDeepAsyncMemo.ts
69
- import { useEffect, useRef as useRef2, useState } from "react";
70
- import fastDeepEqual from "fast-deep-equal";
71
- var useDeepAsyncMemo = (factory, isEqual = fastDeepEqual) => {
72
- const [state, setState] = useState({
73
- isInitialized: false,
74
- isLoading: true,
75
- value: void 0,
76
- error: null
77
- });
78
- const lastValueRef = useRef2(void 0);
79
- const hasValueRef = useRef2(false);
80
- const inflightRef = useRef2(null);
81
- const isMountedRef = useRef2(true);
82
- useEffect(() => {
83
- isMountedRef.current = true;
84
- const promise = factory();
85
- inflightRef.current = promise;
86
- setState((prev) => {
87
- return __spreadProps(__spreadValues({}, prev), {
88
- isLoading: true,
89
- error: null
90
- });
91
- });
92
- promise.then((result) => {
93
- if (!isMountedRef.current || inflightRef.current !== promise) return;
94
- const isSame = hasValueRef.current && isEqual(lastValueRef.current, result);
95
- if (isSame) {
96
- setState((prev) => {
97
- return __spreadProps(__spreadValues({}, prev), {
98
- isInitialized: true,
99
- isLoading: false
100
- });
101
- });
102
- return;
103
- }
104
- lastValueRef.current = result;
105
- hasValueRef.current = true;
106
- setState({
107
- isInitialized: true,
108
- isLoading: false,
109
- value: result,
110
- error: null
111
- });
112
- }).catch((err) => {
113
- if (!isMountedRef.current || inflightRef.current !== promise) return;
114
- setState({
115
- isInitialized: hasValueRef.current,
116
- isLoading: false,
117
- value: void 0,
118
- error: err instanceof Error ? err : new Error("Unknown error")
119
- });
120
- });
121
- return () => {
122
- isMountedRef.current = false;
123
- };
124
- }, [
125
- /**
126
- * !HACK
127
- * @description We're only going to use the `isEqual` function to determine if this hook
128
- * should re-run. This is basically our hook's version of a dependency array
129
- * @date April 13, 2025
130
- */
131
- // factory,
132
- isEqual
133
- ]);
134
- return state;
21
+ //#endregion
22
+ //#region src/internal/hooks/useDeepAsyncMemo.ts
23
+ const useDeepAsyncMemo = (factory, isEqual = fastDeepEqual) => {
24
+ const [state, setState] = useState({
25
+ isInitialized: false,
26
+ isLoading: true,
27
+ value: void 0,
28
+ error: null
29
+ });
30
+ const lastValueRef = useRef(void 0);
31
+ const hasValueRef = useRef(false);
32
+ const inflightRef = useRef(null);
33
+ const isMountedRef = useRef(true);
34
+ useEffect(() => {
35
+ isMountedRef.current = true;
36
+ const promise = factory();
37
+ inflightRef.current = promise;
38
+ setState((prev) => {
39
+ return {
40
+ ...prev,
41
+ isLoading: true,
42
+ error: null
43
+ };
44
+ });
45
+ promise.then((result) => {
46
+ if (!isMountedRef.current || inflightRef.current !== promise) return;
47
+ if (hasValueRef.current && isEqual(lastValueRef.current, result)) {
48
+ setState((prev) => {
49
+ return {
50
+ ...prev,
51
+ isInitialized: true,
52
+ isLoading: false
53
+ };
54
+ });
55
+ return;
56
+ }
57
+ lastValueRef.current = result;
58
+ hasValueRef.current = true;
59
+ setState({
60
+ isInitialized: true,
61
+ isLoading: false,
62
+ value: result,
63
+ error: null
64
+ });
65
+ }).catch((err) => {
66
+ if (!isMountedRef.current || inflightRef.current !== promise) return;
67
+ setState({
68
+ isInitialized: hasValueRef.current,
69
+ isLoading: false,
70
+ value: void 0,
71
+ error: err instanceof Error ? err : /* @__PURE__ */ new Error("Unknown error")
72
+ });
73
+ });
74
+ return () => {
75
+ isMountedRef.current = false;
76
+ };
77
+ }, [isEqual]);
78
+ return state;
135
79
  };
136
80
 
137
- // src/internal/hooks/useNoSsr.ts
138
- import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
139
- var useNoSsr = () => {
140
- const [didMount, setDidMount] = useState2(false);
141
- useEffect2(() => setDidMount(typeof window !== "undefined"), []);
142
- const noSsr = useCallback2(
143
- (value, fallback = null) => {
144
- return didMount ? typeof value !== "function" ? value : value() : fallback;
145
- },
146
- [didMount]
147
- );
148
- return noSsr;
81
+ //#endregion
82
+ //#region src/internal/hooks/useNoSsr.ts
83
+ const useNoSsr = () => {
84
+ const [didMount, setDidMount] = useState(false);
85
+ useEffect(() => setDidMount(typeof window !== "undefined"), []);
86
+ return useCallback((value, fallback = null) => {
87
+ return didMount ? typeof value !== "function" ? value : value() : fallback;
88
+ }, [didMount]);
149
89
  };
150
90
 
151
- // src/internal/hooks/useRerender.ts
152
- import { useCallback as useCallback3, useState as useState3 } from "react";
153
- var useRerender = () => {
154
- const [, setCount] = useState3(0);
155
- return useCallback3(() => {
156
- setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
157
- }, []);
91
+ //#endregion
92
+ //#region src/internal/hooks/useRerender.ts
93
+ const useRerender = () => {
94
+ const [, setCount] = useState(0);
95
+ return useCallback(() => {
96
+ setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
97
+ }, []);
158
98
  };
159
99
 
160
- // src/internal/hooks/useSyncExternalStoreWithSelector.ts
161
- import { useRef as useRef3, useSyncExternalStore } from "react";
162
-
163
- // src/internal/utils/identity.ts
164
- var identity = (x) => x;
100
+ //#endregion
101
+ //#region src/internal/utils/identity.ts
102
+ const identity = (x) => x;
165
103
 
166
- // src/internal/utils/shallowArrayEqual.ts
167
- var shallowArrayEqual = (a, b) => {
168
- return a.length === b.length && a.every((item, i) => item === b[i]);
104
+ //#endregion
105
+ //#region src/internal/utils/shallowArrayEqual.ts
106
+ const shallowArrayEqual = (a, b) => {
107
+ return a.length === b.length && a.every((item, i) => item === b[i]);
169
108
  };
170
109
 
171
- // src/internal/utils/shallowEqual.ts
172
- var shallowEqual = (a, b) => a === b;
110
+ //#endregion
111
+ //#region src/internal/utils/shallowEqual.ts
112
+ const shallowEqual = (a, b) => a === b;
173
113
 
174
- // src/internal/hooks/useSyncExternalStoreWithSelector.ts
175
- var useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerSnapshot, selector, isEqual) => {
176
- const noSsr = useNoSsr();
177
- const cacheRef = useRef3(null);
178
- const _getServerSnapshot = getServerSnapshot ? () => {
179
- const data = getServerSnapshot();
180
- const snapshot = selector(data);
181
- const compare = isEqual != null ? isEqual : shallowEqual;
182
- const cached = cacheRef.current;
183
- const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
184
- cacheRef.current = isUnchanged ? cached != null ? cached : snapshot : snapshot;
185
- return cacheRef.current;
186
- } : void 0;
187
- const _getClientSnapshot = () => {
188
- const data = getClientSnapshot();
189
- const snapshot = selector(data);
190
- const compare = isEqual != null ? isEqual : shallowEqual;
191
- const cached = cacheRef.current;
192
- const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
193
- cacheRef.current = isUnchanged ? cached != null ? cached : snapshot : snapshot;
194
- return cacheRef.current;
195
- };
196
- const result = useSyncExternalStore(subscribe, _getClientSnapshot, _getServerSnapshot);
197
- return typeof window !== "undefined" ? noSsr(result, _getServerSnapshot == null ? void 0 : _getServerSnapshot()) : result;
114
+ //#endregion
115
+ //#region src/internal/hooks/useSyncExternalStoreWithSelector.ts
116
+ const useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerSnapshot, selector, isEqual) => {
117
+ const noSsr = useNoSsr();
118
+ const cacheRef = useRef(null);
119
+ const _getServerSnapshot = getServerSnapshot ? () => {
120
+ const snapshot = selector(getServerSnapshot());
121
+ const compare = isEqual ?? shallowEqual;
122
+ const cached = cacheRef.current;
123
+ /**
124
+ * !HACK
125
+ * @description Memoize only the last snapshot so that we can only rerender on changes
126
+ * @date June 22, 2024
127
+ */
128
+ cacheRef.current = (cached === null ? snapshot === null : compare(cached, snapshot)) ? cached ?? snapshot : snapshot;
129
+ return cacheRef.current;
130
+ } : void 0;
131
+ const _getClientSnapshot = () => {
132
+ const snapshot = selector(getClientSnapshot());
133
+ const compare = isEqual ?? shallowEqual;
134
+ const cached = cacheRef.current;
135
+ /**
136
+ * !HACK
137
+ * @description Memoize only the last snapshot so that we can only rerender on changes
138
+ * @date June 22, 2024
139
+ */
140
+ cacheRef.current = (cached === null ? snapshot === null : compare(cached, snapshot)) ? cached ?? snapshot : snapshot;
141
+ return cacheRef.current;
142
+ };
143
+ const result = useSyncExternalStore(subscribe, _getClientSnapshot, _getServerSnapshot);
144
+ return typeof window !== "undefined" ? noSsr(result, _getServerSnapshot?.()) : result;
198
145
  };
199
146
 
200
- // src/createBundle.tsx
201
- import { jsx } from "react/jsx-runtime";
202
- var createBundle = (client, options = {}) => {
203
- const PluvContext = createContext(null);
204
- const PluvRoomContext = createContext(null);
205
- const MockedRoomContext = createContext(null);
206
- const MockedRoomProvider = memo((props) => {
207
- const { children, events, initialPresence, initialStorage, room: _room } = props;
208
- const [room] = useState4(() => {
209
- var _a;
210
- return new MockedRoom(_room, {
211
- events,
212
- initialPresence,
213
- initialStorage: typeof initialStorage === "function" ? (_a = client._defs.initialStorage) == null ? void 0 : _a.getFactory(initialStorage) : void 0
214
- });
215
- });
216
- return /* @__PURE__ */ jsx(MockedRoomContext.Provider, { value: room, children: /* @__PURE__ */ jsx(PluvRoomContext.Provider, { value: room, children }) });
217
- });
218
- MockedRoomProvider.displayName = "MockedRoomProvider";
219
- const PluvRoomProvider = memo(
220
- (props) => {
221
- const {
222
- children,
223
- connect = true,
224
- debug,
225
- initialPresence,
226
- initialStorage,
227
- metadata,
228
- onAuthorizationFail,
229
- room: _room
230
- } = props;
231
- const queue = useAsyncQueue();
232
- const rerender = useRerender();
233
- const mockedRoom = useContext(MockedRoomContext);
234
- const resolvedMeta = useDeepAsyncMemo(() => __async(null, null, function* () {
235
- const resolved = yield Promise.resolve(
236
- typeof metadata === "function" ? metadata() : metadata
237
- );
238
- return !!room.metadata ? room.metadata.parse(resolved) : resolved;
239
- }));
240
- const createRoom = useCallback4(() => {
241
- var _a;
242
- return client.createRoom(_room, {
243
- addons: options.addons,
244
- debug,
245
- initialPresence,
246
- initialStorage: typeof initialStorage === "function" ? (_a = client._defs.initialStorage) == null ? void 0 : _a.getFactory(initialStorage) : void 0,
247
- metadata,
248
- onAuthorizationFail,
249
- router: options.router
250
- });
251
- }, [_room, debug, initialPresence, initialStorage, metadata, onAuthorizationFail]);
252
- const [room, setRoom] = useState4(() => createRoom());
253
- useEffect3(() => {
254
- if (room.id === _room) return;
255
- setRoom(createRoom());
256
- }, [_room, createRoom, room]);
257
- useEffect3(() => {
258
- const unsubscribe = room.subscribe.connection(() => {
259
- rerender();
260
- });
261
- return () => {
262
- unsubscribe();
263
- };
264
- }, [rerender, room]);
265
- useEffect3(() => {
266
- const leaveRoom = () => __async(null, null, function* () {
267
- yield queue.push(
268
- client.leave(room).catch((error) => {
269
- console.error(error);
270
- })
271
- );
272
- });
273
- if (!connect) {
274
- leaveRoom();
275
- return;
276
- }
277
- if (!resolvedMeta.isInitialized) {
278
- leaveRoom();
279
- return;
280
- }
281
- const resolved = resolvedMeta.value;
282
- queue.push(
283
- client.enter(room, ...[{ metadata: resolved }]).catch((error) => __async(null, null, function* () {
284
- console.error(error);
285
- yield leaveRoom();
286
- }))
287
- );
288
- return () => {
289
- leaveRoom();
290
- };
291
- }, [connect, queue, resolvedMeta.isInitialized, resolvedMeta.value, room]);
292
- return /* @__PURE__ */ jsx(PluvRoomContext.Provider, { value: mockedRoom != null ? mockedRoom : room, children });
293
- }
294
- );
295
- PluvRoomProvider.displayName = "PluvRoomProvider";
296
- const PluvProvider = memo((props) => {
297
- const { children } = props;
298
- return /* @__PURE__ */ jsx(PluvContext.Provider, { value: client, children });
299
- });
300
- PluvProvider.displayName = "PluvProvider";
301
- const useClient = () => useContext(PluvContext);
302
- const useRoom = () => {
303
- const room = useContext(PluvRoomContext);
304
- if (!room)
305
- throw new Error(
306
- "Room could not be found. Component must be wrapped with PluvRoomProvider"
307
- );
308
- return room;
309
- };
310
- const useBroadcast = () => {
311
- const room = useRoom();
312
- const broadcast = useCallback4(
313
- (event2, data) => {
314
- room.broadcast(event2, data);
315
- },
316
- [room]
317
- );
318
- return useMemo2(() => {
319
- return new Proxy(broadcast, {
320
- get(fn, prop) {
321
- return (data) => {
322
- return fn(prop, data);
323
- };
324
- }
325
- });
326
- }, [broadcast]);
327
- };
328
- const useCanRedo = () => {
329
- const room = useRoom();
330
- const subscribe = useCallback4(
331
- (onStoreChange) => room.subscribe.storage(onStoreChange),
332
- [room]
333
- );
334
- const getSnapshot = useCallback4(() => room.canRedo(), [room]);
335
- const canRedo = useSyncExternalStoreWithSelector(
336
- subscribe,
337
- getSnapshot,
338
- getSnapshot,
339
- identity
340
- );
341
- return canRedo;
342
- };
343
- const useCanUndo = () => {
344
- const room = useRoom();
345
- const subscribe = useCallback4(
346
- (onStoreChange) => room.subscribe.storage(onStoreChange),
347
- [room]
348
- );
349
- const getSnapshot = useCallback4(() => room.canUndo(), [room]);
350
- const canUndo = useSyncExternalStoreWithSelector(
351
- subscribe,
352
- getSnapshot,
353
- getSnapshot,
354
- identity
355
- );
356
- return canUndo;
357
- };
358
- const useConnection = (selector = identity, options2) => {
359
- var _a;
360
- const room = useRoom();
361
- const subscribe = useCallback4(
362
- (onStoreChange) => room.subscribe.connection(onStoreChange),
363
- [room]
364
- );
365
- const getSnapshot = room.getConnection;
366
- const _selector = useCallback4(
367
- (snapshot) => selector(snapshot),
368
- [selector]
369
- );
370
- return useSyncExternalStoreWithSelector(
371
- subscribe,
372
- getSnapshot,
373
- getSnapshot,
374
- _selector,
375
- (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
376
- );
377
- };
378
- const useDoc = () => {
379
- const room = useRoom();
380
- const subscribe = useCallback4(
381
- (onStoreChange) => room.subscribe.storageLoaded(onStoreChange),
382
- [room]
383
- );
384
- const getSnapshot = room.getDoc;
385
- return useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
386
- };
387
- const useEvent = (type, callback) => {
388
- const room = useRoom();
389
- useEffect3(() => {
390
- const unsubscribe = room.subscribe.event(type, callback);
391
- return () => {
392
- unsubscribe();
393
- };
394
- }, [callback, room, type]);
395
- };
396
- const event = new Proxy(
397
- {},
398
- {
399
- get(_, prop) {
400
- const useProxyEvent = (callback) => {
401
- return useEvent(
402
- prop,
403
- callback
404
- );
405
- };
406
- return { useEvent: useProxyEvent };
407
- }
408
- }
409
- );
410
- const useMyPresence = (selector = identity, options2) => {
411
- var _a;
412
- const room = useRoom();
413
- const subscribe = useCallback4(
414
- (onStoreChange) => room.subscribe.myPresence(onStoreChange),
415
- [room]
416
- );
417
- const getSnapshot = room.getMyPresence;
418
- const _selector = useCallback4(
419
- (snapshot) => selector(snapshot),
420
- [selector]
421
- );
422
- const myPresence = useSyncExternalStoreWithSelector(
423
- subscribe,
424
- getSnapshot,
425
- getSnapshot,
426
- _selector,
427
- (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
428
- );
429
- return [myPresence, room.updateMyPresence];
430
- };
431
- const useMyself = (selector = identity, options2) => {
432
- var _a;
433
- const room = useRoom();
434
- const subscribe = useCallback4(
435
- (onStoreChange) => room.subscribe.myself(onStoreChange),
436
- [room]
437
- );
438
- const getSnapshot = room.getMyself;
439
- const _selector = useCallback4(
440
- (snapshot) => {
441
- return !snapshot ? null : selector(snapshot);
442
- },
443
- [selector]
444
- );
445
- return useSyncExternalStoreWithSelector(
446
- subscribe,
447
- getSnapshot,
448
- getSnapshot,
449
- _selector,
450
- (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
451
- );
452
- };
453
- const useOther = (connectionId, selector = identity, options2) => {
454
- var _a;
455
- const room = useRoom();
456
- const subscribe = useCallback4(
457
- (onStoreChange) => room.subscribe.other(connectionId, onStoreChange),
458
- [room, connectionId]
459
- );
460
- const getSnapshot = useCallback4(() => room.getOther(connectionId), [room, connectionId]);
461
- const _selector = useCallback4(
462
- (snapshot) => {
463
- return !snapshot ? null : selector(snapshot);
464
- },
465
- [selector]
466
- );
467
- return useSyncExternalStoreWithSelector(
468
- subscribe,
469
- getSnapshot,
470
- getSnapshot,
471
- _selector,
472
- (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
473
- );
474
- };
475
- const useOthers = (selector = identity, options2) => {
476
- var _a;
477
- const room = useRoom();
478
- const subscribe = useCallback4(
479
- (onStoreChange) => room.subscribe.others(onStoreChange),
480
- [room]
481
- );
482
- const getSnapshot = room.getOthers;
483
- return useSyncExternalStoreWithSelector(
484
- subscribe,
485
- getSnapshot,
486
- getSnapshot,
487
- selector,
488
- (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : (a, b) => {
489
- if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
490
- return shallowArrayEqual(a, b);
491
- }
492
- return fastDeepEqual2(a, b);
493
- }
494
- );
495
- };
496
- const useRedo = () => {
497
- const room = useRoom();
498
- return room.redo;
499
- };
500
- const useStorage = (key, selector = identity, options2) => {
501
- var _a, _b;
502
- const room = useRoom();
503
- const rerender = useRerender();
504
- useEffect3(() => {
505
- const unsubscribe = room.subscribe.storageLoaded(() => {
506
- rerender();
507
- });
508
- return () => {
509
- unsubscribe();
510
- };
511
- }, [rerender, room]);
512
- const subscribe = useCallback4(
513
- (onStoreChange) => room.subscribe.storage(key, onStoreChange),
514
- [key, room]
515
- );
516
- const getSnapshot = useCallback4(() => {
517
- return room.getStorageJson(key);
518
- }, [key, room]);
519
- const _selector = useCallback4(
520
- (snapshot) => {
521
- return snapshot === null ? null : selector(snapshot);
522
- },
523
- [selector]
524
- );
525
- const data = useSyncExternalStoreWithSelector(
526
- subscribe,
527
- getSnapshot,
528
- getSnapshot,
529
- _selector,
530
- (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
531
- );
532
- const sharedType = (_b = room.getStorage(key)) != null ? _b : null;
533
- return [data, sharedType];
534
- };
535
- const useTransact = () => {
536
- const room = useRoom();
537
- return room.transact;
538
- };
539
- const useUndo = () => {
540
- const room = useRoom();
541
- return room.undo;
542
- };
543
- return {
544
- // components
545
- MockedRoomProvider,
546
- PluvProvider,
547
- PluvRoomProvider,
548
- // proxies
549
- event,
550
- // hooks
551
- useBroadcast,
552
- useCanRedo,
553
- useCanUndo,
554
- useClient,
555
- useConnection,
556
- useDoc,
557
- useEvent,
558
- useMyPresence,
559
- useMyself,
560
- useOther,
561
- useOthers,
562
- useRedo,
563
- useRoom,
564
- useStorage,
565
- useTransact,
566
- useUndo
567
- };
568
- };
569
- export {
570
- createBundle
147
+ //#endregion
148
+ //#region src/createBundle.tsx
149
+ const createBundle = (client, options = {}) => {
150
+ /**
151
+ * !HACK
152
+ * @description We'll let the context error out if client is not provided,
153
+ * and let the users deal with it.
154
+ * @date October 27, 2022
155
+ */
156
+ const PluvContext = createContext(null);
157
+ /**
158
+ * !HACK
159
+ * @description We'll let the context error out if the room is not provided,
160
+ * and let the users deal with it.
161
+ * @date November 11, 2022
162
+ */
163
+ const PluvRoomContext = createContext(null);
164
+ const MockedRoomContext = createContext(null);
165
+ const MockedRoomProvider = memo((props) => {
166
+ const { children, events, initialPresence, initialStorage, room: _room } = props;
167
+ const [room] = useState(() => {
168
+ return new MockedRoom(_room, {
169
+ events,
170
+ initialPresence,
171
+ initialStorage: typeof initialStorage === "function" ? client._defs.initialStorage?.getFactory(initialStorage) : void 0
172
+ });
173
+ });
174
+ return /* @__PURE__ */ jsx(MockedRoomContext.Provider, {
175
+ value: room,
176
+ children: /* @__PURE__ */ jsx(PluvRoomContext.Provider, {
177
+ value: room,
178
+ children
179
+ })
180
+ });
181
+ });
182
+ MockedRoomProvider.displayName = "MockedRoomProvider";
183
+ const PluvRoomProvider = memo((props) => {
184
+ const { children, connect = true, debug, initialPresence, initialStorage, metadata, onAuthorizationFail, room: _room } = props;
185
+ const queue = useAsyncQueue();
186
+ const rerender = useRerender();
187
+ const mockedRoom = useContext(MockedRoomContext);
188
+ const createRoom = useCallback(() => {
189
+ return client.createRoom(_room, {
190
+ addons: options.addons,
191
+ debug,
192
+ initialPresence,
193
+ initialStorage: typeof initialStorage === "function" ? client._defs.initialStorage?.getFactory(initialStorage) : void 0,
194
+ metadata,
195
+ onAuthorizationFail,
196
+ router: options.router
197
+ });
198
+ }, [
199
+ _room,
200
+ debug,
201
+ initialPresence,
202
+ initialStorage,
203
+ metadata,
204
+ onAuthorizationFail
205
+ ]);
206
+ const [room, setRoom] = useState(() => createRoom());
207
+ useEffect(() => {
208
+ if (room.id === _room) return;
209
+ setRoom(createRoom());
210
+ }, [
211
+ _room,
212
+ createRoom,
213
+ room
214
+ ]);
215
+ const resolvedMeta = useDeepAsyncMemo(async () => {
216
+ const resolved = await Promise.resolve(typeof metadata === "function" ? metadata() : metadata);
217
+ return !!room.metadata ? room.metadata.parse(resolved) : resolved;
218
+ });
219
+ useEffect(() => {
220
+ const unsubscribe = room.subscribe.connection(() => {
221
+ rerender();
222
+ });
223
+ return () => {
224
+ unsubscribe();
225
+ };
226
+ }, [rerender, room]);
227
+ useEffect(() => {
228
+ const leaveRoom = async () => {
229
+ await queue.push(client.leave(room).catch((error) => {
230
+ console.error(error);
231
+ }));
232
+ };
233
+ if (!connect) {
234
+ leaveRoom();
235
+ return;
236
+ }
237
+ if (!resolvedMeta.isInitialized) {
238
+ leaveRoom();
239
+ return;
240
+ }
241
+ const resolved = resolvedMeta.value;
242
+ queue.push(client.enter(room, ...[{ metadata: resolved }]).catch(async (error) => {
243
+ console.error(error);
244
+ await leaveRoom();
245
+ }));
246
+ return () => {
247
+ leaveRoom();
248
+ };
249
+ }, [
250
+ connect,
251
+ queue,
252
+ resolvedMeta.isInitialized,
253
+ resolvedMeta.value,
254
+ room
255
+ ]);
256
+ return /* @__PURE__ */ jsx(PluvRoomContext.Provider, {
257
+ value: mockedRoom ?? room,
258
+ children
259
+ });
260
+ });
261
+ PluvRoomProvider.displayName = "PluvRoomProvider";
262
+ const PluvProvider = memo((props) => {
263
+ const { children } = props;
264
+ return /* @__PURE__ */ jsx(PluvContext.Provider, {
265
+ value: client,
266
+ children
267
+ });
268
+ });
269
+ PluvProvider.displayName = "PluvProvider";
270
+ const useClient = () => useContext(PluvContext);
271
+ const useRoom = () => {
272
+ const room = useContext(PluvRoomContext);
273
+ if (!room) throw new Error("Room could not be found. Component must be wrapped with PluvRoomProvider");
274
+ return room;
275
+ };
276
+ const useBroadcast = () => {
277
+ const room = useRoom();
278
+ const broadcast = useCallback((event$1, data) => {
279
+ room.broadcast(event$1, data);
280
+ }, [room]);
281
+ return useMemo(() => {
282
+ return new Proxy(broadcast, { get(fn, prop) {
283
+ return (data) => {
284
+ return fn(prop, data);
285
+ };
286
+ } });
287
+ }, [broadcast]);
288
+ };
289
+ const useCanRedo = () => {
290
+ const room = useRoom();
291
+ const subscribe = useCallback((onStoreChange) => room.subscribe.storage(onStoreChange), [room]);
292
+ const getSnapshot = useCallback(() => room.canRedo(), [room]);
293
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, identity);
294
+ };
295
+ const useCanUndo = () => {
296
+ const room = useRoom();
297
+ const subscribe = useCallback((onStoreChange) => room.subscribe.storage(onStoreChange), [room]);
298
+ const getSnapshot = useCallback(() => room.canUndo(), [room]);
299
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, identity);
300
+ };
301
+ const useConnection = (selector = identity, options$1) => {
302
+ const room = useRoom();
303
+ const subscribe = useCallback((onStoreChange) => room.subscribe.connection(onStoreChange), [room]);
304
+ const getSnapshot = room.getConnection;
305
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, useCallback((snapshot) => selector(snapshot), [selector]), options$1?.isEqual ?? fastDeepEqual);
306
+ };
307
+ const useDoc = () => {
308
+ const room = useRoom();
309
+ const subscribe = useCallback((onStoreChange) => room.subscribe.storageLoaded(onStoreChange), [room]);
310
+ const getSnapshot = room.getDoc;
311
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
312
+ };
313
+ const useEvent = (type, callback) => {
314
+ const room = useRoom();
315
+ useEffect(() => {
316
+ const unsubscribe = room.subscribe.event(type, callback);
317
+ return () => {
318
+ unsubscribe();
319
+ };
320
+ }, [
321
+ callback,
322
+ room,
323
+ type
324
+ ]);
325
+ };
326
+ const event = new Proxy({}, { get(_, prop) {
327
+ const useProxyEvent = (callback) => {
328
+ return useEvent(prop, callback);
329
+ };
330
+ return { useEvent: useProxyEvent };
331
+ } });
332
+ const useMyPresence = (selector = identity, options$1) => {
333
+ const room = useRoom();
334
+ const subscribe = useCallback((onStoreChange) => room.subscribe.myPresence(onStoreChange), [room]);
335
+ const getSnapshot = room.getMyPresence;
336
+ return [useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, useCallback((snapshot) => selector(snapshot), [selector]), options$1?.isEqual ?? fastDeepEqual), room.updateMyPresence];
337
+ };
338
+ const useMyself = (selector = identity, options$1) => {
339
+ const room = useRoom();
340
+ const subscribe = useCallback((onStoreChange) => room.subscribe.myself(onStoreChange), [room]);
341
+ const getSnapshot = room.getMyself;
342
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, useCallback((snapshot) => {
343
+ return !snapshot ? null : selector(snapshot);
344
+ }, [selector]), options$1?.isEqual ?? fastDeepEqual);
345
+ };
346
+ const useOther = (connectionId, selector = identity, options$1) => {
347
+ const room = useRoom();
348
+ const subscribe = useCallback((onStoreChange) => room.subscribe.other(connectionId, onStoreChange), [room, connectionId]);
349
+ const getSnapshot = useCallback(() => room.getOther(connectionId), [room, connectionId]);
350
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, useCallback((snapshot) => {
351
+ return !snapshot ? null : selector(snapshot);
352
+ }, [selector]), options$1?.isEqual ?? fastDeepEqual);
353
+ };
354
+ const useOthers = (selector = identity, options$1) => {
355
+ const room = useRoom();
356
+ const subscribe = useCallback((onStoreChange) => room.subscribe.others(onStoreChange), [room]);
357
+ const getSnapshot = room.getOthers;
358
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, options$1?.isEqual ?? ((a, b) => {
359
+ /**
360
+ * !HACK
361
+ * @description Assume the return type will always be an array when this occurs
362
+ * and do a shallow comparison instead like how standard React dependency
363
+ * arrays work. This is for performance's sake and may lead to bugs if the
364
+ * selector doesn't always return an array
365
+ * @date June 22, 2024
366
+ */
367
+ if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) return shallowArrayEqual(a, b);
368
+ return fastDeepEqual(a, b);
369
+ }));
370
+ };
371
+ const useRedo = () => {
372
+ return useRoom().redo;
373
+ };
374
+ const useStorage = (key, selector = identity, options$1) => {
375
+ const room = useRoom();
376
+ const rerender = useRerender();
377
+ useEffect(() => {
378
+ const unsubscribe = room.subscribe.storageLoaded(() => {
379
+ rerender();
380
+ });
381
+ return () => {
382
+ unsubscribe();
383
+ };
384
+ }, [rerender, room]);
385
+ const subscribe = useCallback((onStoreChange) => room.subscribe.storage(key, onStoreChange), [key, room]);
386
+ const getSnapshot = useCallback(() => {
387
+ return room.getStorageJson(key);
388
+ }, [key, room]);
389
+ return [useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, useCallback((snapshot) => {
390
+ return snapshot === null ? null : selector(snapshot);
391
+ }, [selector]), options$1?.isEqual ?? fastDeepEqual), room.getStorage(key) ?? null];
392
+ };
393
+ const useTransact = () => {
394
+ return useRoom().transact;
395
+ };
396
+ const useUndo = () => {
397
+ return useRoom().undo;
398
+ };
399
+ return {
400
+ MockedRoomProvider,
401
+ PluvProvider,
402
+ PluvRoomProvider,
403
+ event,
404
+ useBroadcast,
405
+ useCanRedo,
406
+ useCanUndo,
407
+ useClient,
408
+ useConnection,
409
+ useDoc,
410
+ useEvent,
411
+ useMyPresence,
412
+ useMyself,
413
+ useOther,
414
+ useOthers,
415
+ useRedo,
416
+ useRoom,
417
+ useStorage,
418
+ useTransact,
419
+ useUndo
420
+ };
571
421
  };
422
+
423
+ //#endregion
424
+ export { createBundle };
425
+ //# sourceMappingURL=index.mjs.map