@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/.turbo/turbo-build.log +19 -0
- package/.turbo/turbo-lint.log +5 -0
- package/CHANGELOG.md +2336 -0
- package/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/index.d.mts +72 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +601 -0
- package/dist/index.mjs +576 -0
- package/eslint.config.mjs +4 -0
- package/package.json +44 -0
- package/src/createBundle.tsx +635 -0
- package/src/index.ts +9 -0
- package/src/internal/hooks/index.ts +5 -0
- package/src/internal/hooks/useAsyncQueue.ts +18 -0
- package/src/internal/hooks/useDeepAsyncMemo.ts +120 -0
- package/src/internal/hooks/useNoSsr.ts +22 -0
- package/src/internal/hooks/useRerender.ts +9 -0
- package/src/internal/hooks/useSyncExternalStoreWithSelector.ts +56 -0
- package/src/internal/index.ts +7 -0
- package/src/internal/utils/identity.ts +1 -0
- package/src/internal/utils/index.ts +3 -0
- package/src/internal/utils/shallowArrayEqual.ts +6 -0
- package/src/internal/utils/shallowEqual.ts +1 -0
- package/src/types.ts +151 -0
- package/tsconfig.json +8 -0
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
|
+
};
|
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
|
+
}
|