@pluv/react 0.0.0-experimental-20250527040415

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 ADDED
@@ -0,0 +1,576 @@
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
+ 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";
54
+
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]);
66
+ };
67
+
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;
135
+ };
136
+
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;
149
+ };
150
+
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
+ }, []);
158
+ };
159
+
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;
165
+
166
+ // src/internal/utils/shallowArrayEqual.ts
167
+ var shallowArrayEqual = (a, b) => {
168
+ return a.length === b.length && a.every((item, i) => item === b[i]);
169
+ };
170
+
171
+ // src/internal/utils/shallowEqual.ts
172
+ var shallowEqual = (a, b) => a === b;
173
+
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;
198
+ };
199
+
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) : client._defs.initialStorage
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) : client._defs.initialStorage,
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) => {
363
+ return room.subscribe.connection(onStoreChange);
364
+ },
365
+ [room]
366
+ );
367
+ const getSnapshot = room.getConnection;
368
+ const _selector = useCallback4(
369
+ (snapshot) => selector(snapshot),
370
+ [selector]
371
+ );
372
+ return useSyncExternalStoreWithSelector(
373
+ subscribe,
374
+ getSnapshot,
375
+ getSnapshot,
376
+ _selector,
377
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
378
+ );
379
+ };
380
+ const useDoc = () => {
381
+ const room = useRoom();
382
+ const subscribe = useCallback4(
383
+ (onStoreChange) => {
384
+ return room.subscribe.storageLoaded(onStoreChange);
385
+ },
386
+ [room]
387
+ );
388
+ const getSnapshot = room.getDoc;
389
+ return useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
390
+ };
391
+ const useEvent = (type, callback) => {
392
+ const room = useRoom();
393
+ useEffect3(() => {
394
+ const unsubscribe = room.subscribe.event(type, callback);
395
+ return () => {
396
+ unsubscribe();
397
+ };
398
+ }, [callback, room, type]);
399
+ };
400
+ const event = new Proxy(
401
+ {},
402
+ {
403
+ get(_, prop) {
404
+ const useProxyEvent = (callback) => {
405
+ return useEvent(
406
+ prop,
407
+ callback
408
+ );
409
+ };
410
+ return { useEvent: useProxyEvent };
411
+ }
412
+ }
413
+ );
414
+ const useMyPresence = (selector = identity, options2) => {
415
+ var _a;
416
+ const room = useRoom();
417
+ const subscribe = useCallback4(
418
+ (onStoreChange) => {
419
+ return room.subscribe.myPresence(onStoreChange);
420
+ },
421
+ [room]
422
+ );
423
+ const getSnapshot = room.getMyPresence;
424
+ const _selector = useCallback4(
425
+ (snapshot) => selector(snapshot),
426
+ [selector]
427
+ );
428
+ const myPresence = useSyncExternalStoreWithSelector(
429
+ subscribe,
430
+ getSnapshot,
431
+ getSnapshot,
432
+ _selector,
433
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
434
+ );
435
+ return [myPresence, room.updateMyPresence];
436
+ };
437
+ const useMyself = (selector = identity, options2) => {
438
+ var _a;
439
+ const room = useRoom();
440
+ const subscribe = useCallback4(
441
+ (onStoreChange) => {
442
+ return room.subscribe.myself(onStoreChange);
443
+ },
444
+ [room]
445
+ );
446
+ const getSnapshot = room.getMyself;
447
+ const _selector = useCallback4(
448
+ (snapshot) => {
449
+ return !snapshot ? null : selector(snapshot);
450
+ },
451
+ [selector]
452
+ );
453
+ return useSyncExternalStoreWithSelector(
454
+ subscribe,
455
+ getSnapshot,
456
+ getSnapshot,
457
+ _selector,
458
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
459
+ );
460
+ };
461
+ const useOther = (connectionId, selector = identity, options2) => {
462
+ var _a;
463
+ const room = useRoom();
464
+ const subscribe = useCallback4(
465
+ (onStoreChange) => room.subscribe.other(connectionId, onStoreChange),
466
+ [room, connectionId]
467
+ );
468
+ const getSnapshot = useCallback4(() => room.getOther(connectionId), [room, connectionId]);
469
+ const _selector = useCallback4(
470
+ (snapshot) => {
471
+ return !snapshot ? null : selector(snapshot);
472
+ },
473
+ [selector]
474
+ );
475
+ return useSyncExternalStoreWithSelector(
476
+ subscribe,
477
+ getSnapshot,
478
+ getSnapshot,
479
+ _selector,
480
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
481
+ );
482
+ };
483
+ const useOthers = (selector = identity, options2) => {
484
+ var _a;
485
+ const room = useRoom();
486
+ const subscribe = useCallback4(
487
+ (onStoreChange) => {
488
+ return room.subscribe.others(onStoreChange);
489
+ },
490
+ [room]
491
+ );
492
+ const getSnapshot = room.getOthers;
493
+ return useSyncExternalStoreWithSelector(
494
+ subscribe,
495
+ getSnapshot,
496
+ getSnapshot,
497
+ selector,
498
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : (a, b) => {
499
+ if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
500
+ return shallowArrayEqual(a, b);
501
+ }
502
+ return fastDeepEqual2(a, b);
503
+ }
504
+ );
505
+ };
506
+ const useRedo = () => {
507
+ const room = useRoom();
508
+ return room.redo;
509
+ };
510
+ const useStorage = (key, selector = identity, options2) => {
511
+ var _a, _b;
512
+ const room = useRoom();
513
+ const rerender = useRerender();
514
+ room.subscribe.storageLoaded(() => {
515
+ rerender();
516
+ });
517
+ const subscribe = useCallback4(
518
+ (onStoreChange) => room.subscribe.storage(key, onStoreChange),
519
+ [key, room]
520
+ );
521
+ const getSnapshot = useCallback4(() => {
522
+ return room.getStorageJson(key);
523
+ }, [key, room]);
524
+ const _selector = useCallback4(
525
+ (snapshot) => {
526
+ return snapshot === null ? null : selector(snapshot);
527
+ },
528
+ [selector]
529
+ );
530
+ const data = useSyncExternalStoreWithSelector(
531
+ subscribe,
532
+ getSnapshot,
533
+ getSnapshot,
534
+ _selector,
535
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual2
536
+ );
537
+ const sharedType = (_b = room.getStorage(key)) != null ? _b : null;
538
+ return [data, sharedType];
539
+ };
540
+ const useTransact = () => {
541
+ const room = useRoom();
542
+ return room.transact;
543
+ };
544
+ const useUndo = () => {
545
+ const room = useRoom();
546
+ return room.undo;
547
+ };
548
+ return {
549
+ // components
550
+ MockedRoomProvider,
551
+ PluvProvider,
552
+ PluvRoomProvider,
553
+ // proxies
554
+ event,
555
+ // hooks
556
+ useBroadcast,
557
+ useCanRedo,
558
+ useCanUndo,
559
+ useClient,
560
+ useConnection,
561
+ useDoc,
562
+ useEvent,
563
+ useMyPresence,
564
+ useMyself,
565
+ useOther,
566
+ useOthers,
567
+ useRedo,
568
+ useRoom,
569
+ useStorage,
570
+ useTransact,
571
+ useUndo
572
+ };
573
+ };
574
+ export {
575
+ createBundle
576
+ };
@@ -0,0 +1,4 @@
1
+ import { reactConfig } from "eslint-config-pluv/react";
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default reactConfig;
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@pluv/react",
3
+ "version": "0.0.0-experimental-20250527040415",
4
+ "description": "@pluv/client React.js bindings",
5
+ "author": "leedavidcs",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/pluv-io/pluv",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/pluv-io/pluv.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.mjs",
15
+ "types": "./dist/index.d.ts",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "dependencies": {
20
+ "fast-deep-equal": "^3.1.3",
21
+ "@pluv/client": "^0.0.0-experimental-20250527040415",
22
+ "@pluv/crdt": "^0.0.0-experimental-20250527040415",
23
+ "@pluv/types": "^0.0.0-experimental-20250527040415"
24
+ },
25
+ "peerDependencies": {
26
+ "@types/react": "^18.0.0 || ^19.0.0",
27
+ "react": "^18.0.0 || ^19.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/react": "^19.1.5",
31
+ "eslint": "^9.27.0",
32
+ "react": "^19.1.0",
33
+ "tsup": "^8.5.0",
34
+ "typescript": "^5.8.3",
35
+ "@pluv/tsconfig": "^0.0.0-experimental-20250527040415",
36
+ "eslint-config-pluv": "^0.0.0-experimental-20250527040415"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup src/index.ts --format esm,cjs --dts --external react",
40
+ "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
41
+ "lint": "eslint \"src/**/*.ts*\" --fix --max-warnings 0",
42
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
43
+ }
44
+ }