@pluv/react 0.0.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/.eslintrc.js ADDED
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ["pluv"],
4
+ };
@@ -0,0 +1,17 @@
1
+
2
+ > @pluv/react@0.0.0 build /home/runner/work/pluv/pluv/packages/react
3
+ > tsup src/index.ts --format esm,cjs --dts --external react
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v6.5.0
8
+ CLI Target: es6
9
+ ESM Build start
10
+ CJS Build start
11
+ CJS dist/index.js 9.71 KB
12
+ CJS ⚡️ Build success in 107ms
13
+ ESM dist/index.mjs 7.71 KB
14
+ ESM ⚡️ Build success in 112ms
15
+ DTS Build start
16
+ DTS ⚡️ Build success in 4395ms
17
+ DTS dist/index.d.ts 3.92 KB
@@ -0,0 +1,57 @@
1
+ import { PluvRoomDebug, WebSocketConnection, UserInfo, PluvRoom, InferYjsSharedTypeJson, PluvClient } from '@pluv/client';
2
+ export { AbstractType, ConnectionState, createClient, y } from '@pluv/client';
3
+ import * as _pluv_types from '@pluv/types';
4
+ import { JsonObject, InputZodLike, IOLike, InferEventMessage, InferIOInput, Id, InferIOOutput, IOEventMessage } from '@pluv/types';
5
+ export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
6
+ import { ReactNode, FC, Dispatch } from 'react';
7
+ import { AbstractType } from 'yjs';
8
+
9
+ interface CreateRoomBundleOptions<TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> {
10
+ initialStorage?: () => TStorage;
11
+ presence?: InputZodLike<TPresence>;
12
+ }
13
+ type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> = {
14
+ children?: ReactNode;
15
+ debug?: boolean | PluvRoomDebug<TIO>;
16
+ onAuthorizationFail?: (error: Error) => void;
17
+ room: string;
18
+ } & (keyof TPresence extends never ? {
19
+ initialPresence?: never;
20
+ } : {
21
+ initialPresence: TPresence;
22
+ }) & (keyof TStorage extends never ? {
23
+ initialStorage?: never;
24
+ } : {
25
+ initialStorage?: () => TStorage;
26
+ });
27
+ interface SubscriptionHookOptions<T extends unknown> {
28
+ isEqual?: (a: T, b: T) => boolean;
29
+ }
30
+ type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
31
+ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> {
32
+ PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
33
+ usePluvBroadcast: <TMessage extends InferEventMessage<InferIOInput<TIO>>>() => (message: TMessage) => void;
34
+ usePluvConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
35
+ usePluvEvent: <TType extends keyof InferIOOutput<TIO>>(type: TType, callback: (data: Id<IOEventMessage<TIO, TType>>) => void) => void;
36
+ usePluvMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [
37
+ myPresence: Id<T> | null,
38
+ updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>
39
+ ];
40
+ usePluvMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
41
+ usePluvOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
42
+ usePluvOthers: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T[], options?: SubscriptionHookOptions<readonly Id<T>[]>) => readonly Id<T>[];
43
+ usePluvRoom: () => PluvRoom<TIO, TPresence>;
44
+ usePluvStorage: <TKey extends keyof TStorage, TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>>(key: TKey, selector?: (data: InferYjsSharedTypeJson<TStorage[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: TStorage[TKey] | null];
45
+ }
46
+
47
+ interface PluvProviderProps {
48
+ children?: ReactNode;
49
+ }
50
+ interface CreateBundle<TIO extends IOLike> {
51
+ createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}>(options?: CreateRoomBundleOptions<TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
52
+ PluvProvider: FC<PluvProviderProps>;
53
+ usePluvClient: () => PluvClient<TIO>;
54
+ }
55
+ declare const createBundle: <TIO extends IOLike<_pluv_types.IOAuthorize<any, any>, {}, {}>>(client: PluvClient<TIO>) => CreateBundle<TIO>;
56
+
57
+ export { CreateBundle, CreateRoomBundle, CreateRoomBundleOptions, PluvProviderProps, PluvRoomProviderProps, SubscriptionHookOptions, createBundle };
package/dist/index.js ADDED
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
25
+
26
+ // src/index.ts
27
+ var src_exports = {};
28
+ __export(src_exports, {
29
+ ConnectionState: () => import_client.ConnectionState,
30
+ createBundle: () => createBundle,
31
+ createClient: () => import_client.createClient,
32
+ y: () => import_client.y
33
+ });
34
+ module.exports = __toCommonJS(src_exports);
35
+ var import_client = require("@pluv/client");
36
+
37
+ // src/createBundle.tsx
38
+ var import_react4 = require("react");
39
+ var import_react5 = require("react");
40
+
41
+ // src/createRoomBundle.tsx
42
+ var import_fast_deep_equal = __toESM(require("fast-deep-equal"));
43
+ var import_react2 = require("react");
44
+ var import_react3 = require("react");
45
+
46
+ // src/internal/hooks/useRerender.ts
47
+ var import_react = require("react");
48
+ var useRerender = () => {
49
+ const [, setCount] = (0, import_react.useState)(0);
50
+ return (0, import_react.useCallback)(() => {
51
+ setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
52
+ }, []);
53
+ };
54
+
55
+ // src/internal/hooks/useSyncExternalStoreWithSelector.ts
56
+ var import_with_selector = require("use-sync-external-store/with-selector");
57
+
58
+ // src/internal/utils/identity.ts
59
+ var identity = (x) => x;
60
+
61
+ // src/internal/utils/shallowArrayEqual.ts
62
+ var shallowArrayEqual = (a, b) => {
63
+ return a.length === b.length && a.every((item, i) => item === b[i]);
64
+ };
65
+
66
+ // src/createRoomBundle.tsx
67
+ var import_jsx_runtime = require("react/jsx-runtime");
68
+ var createRoomBundle = (client, options = {}) => {
69
+ const PluvRoomContext = (0, import_react2.createContext)(
70
+ null
71
+ );
72
+ const PluvRoomProvider = (0, import_react3.memo)((props) => {
73
+ const {
74
+ children,
75
+ debug,
76
+ initialPresence,
77
+ initialStorage,
78
+ onAuthorizationFail,
79
+ room: _room
80
+ } = props;
81
+ const rerender = useRerender();
82
+ const [room] = (0, import_react2.useState)(() => {
83
+ return client.createRoom(_room, {
84
+ debug,
85
+ initialPresence,
86
+ initialStorage: typeof initialStorage === "function" ? initialStorage : options.initialStorage,
87
+ presence: options.presence,
88
+ onAuthorizationFail
89
+ });
90
+ });
91
+ (0, import_react3.useEffect)(() => {
92
+ const unsubscribe = room.subscribe("connection", () => {
93
+ rerender();
94
+ });
95
+ return () => {
96
+ unsubscribe();
97
+ };
98
+ }, [rerender, room]);
99
+ (0, import_react3.useEffect)(() => {
100
+ client.enter(_room);
101
+ return () => {
102
+ client.leave(_room);
103
+ };
104
+ }, [_room]);
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PluvRoomContext.Provider, {
106
+ value: room,
107
+ children
108
+ });
109
+ });
110
+ PluvRoomProvider.displayName = "PluvRoomProvider";
111
+ const usePluvRoom = () => (0, import_react3.useContext)(PluvRoomContext);
112
+ const usePluvBroadcast = () => {
113
+ const room = usePluvRoom();
114
+ return (0, import_react3.useCallback)(
115
+ (message) => {
116
+ room.broadcast(message);
117
+ },
118
+ [room]
119
+ );
120
+ };
121
+ const usePluvConnection = (selector = identity, options2) => {
122
+ const room = usePluvRoom();
123
+ const subscribe = (0, import_react3.useCallback)(
124
+ (onStoreChange) => {
125
+ return room.subscribe("connection", onStoreChange);
126
+ },
127
+ [room]
128
+ );
129
+ const getSnapshot = room.getConnection;
130
+ const _selector = (0, import_react3.useCallback)(
131
+ (snapshot) => selector(snapshot),
132
+ [selector]
133
+ );
134
+ return (0, import_with_selector.useSyncExternalStoreWithSelector)(
135
+ subscribe,
136
+ getSnapshot,
137
+ getSnapshot,
138
+ _selector,
139
+ options2 == null ? void 0 : options2.isEqual
140
+ );
141
+ };
142
+ const usePluvEvent = (type, callback) => {
143
+ const room = usePluvRoom();
144
+ (0, import_react3.useEffect)(() => {
145
+ const unsubscribe = room.event(type, callback);
146
+ return () => {
147
+ unsubscribe();
148
+ };
149
+ }, [callback, room, type]);
150
+ };
151
+ const usePluvMyPresence = (selector = identity, options2) => {
152
+ const room = usePluvRoom();
153
+ const subscribe = (0, import_react3.useCallback)(
154
+ (onStoreChange) => {
155
+ return room.subscribe("my-presence", onStoreChange);
156
+ },
157
+ [room]
158
+ );
159
+ const getSnapshot = room.getMyPresence;
160
+ const _selector = (0, import_react3.useCallback)(
161
+ (snapshot) => {
162
+ return !snapshot ? null : selector(snapshot);
163
+ },
164
+ [selector]
165
+ );
166
+ const myPresence = (0, import_with_selector.useSyncExternalStoreWithSelector)(
167
+ subscribe,
168
+ getSnapshot,
169
+ getSnapshot,
170
+ _selector,
171
+ options2 == null ? void 0 : options2.isEqual
172
+ );
173
+ const updateMyPresence = (0, import_react3.useCallback)(
174
+ (value) => {
175
+ const oldMyPresence = room.getMyPresence();
176
+ const presence = typeof value === "function" ? value(oldMyPresence) : value;
177
+ room.broadcast({
178
+ type: "$UPDATE_PRESENCE",
179
+ data: { presence }
180
+ });
181
+ },
182
+ [room]
183
+ );
184
+ return [myPresence, updateMyPresence];
185
+ };
186
+ const usePluvMyself = (selector = identity, options2) => {
187
+ const room = usePluvRoom();
188
+ const subscribe = (0, import_react3.useCallback)(
189
+ (onStoreChange) => {
190
+ return room.subscribe("myself", onStoreChange);
191
+ },
192
+ [room]
193
+ );
194
+ const getSnapshot = room.getMyself;
195
+ const _selector = (0, import_react3.useCallback)(
196
+ (snapshot) => {
197
+ return !snapshot ? null : selector(snapshot);
198
+ },
199
+ [selector]
200
+ );
201
+ return (0, import_with_selector.useSyncExternalStoreWithSelector)(
202
+ subscribe,
203
+ getSnapshot,
204
+ getSnapshot,
205
+ _selector,
206
+ options2 == null ? void 0 : options2.isEqual
207
+ );
208
+ };
209
+ const usePluvOther = (connectionId, selector = identity, options2) => {
210
+ const room = usePluvRoom();
211
+ const subscribe = (0, import_react3.useCallback)(
212
+ (onStoreChange) => {
213
+ return room.other(connectionId, onStoreChange);
214
+ },
215
+ [room, connectionId]
216
+ );
217
+ const getSnapshot = (0, import_react3.useCallback)(
218
+ () => room.getOther(connectionId),
219
+ [room, connectionId]
220
+ );
221
+ const _selector = (0, import_react3.useCallback)(
222
+ (snapshot) => {
223
+ return !snapshot ? null : selector(snapshot);
224
+ },
225
+ [selector]
226
+ );
227
+ return (0, import_with_selector.useSyncExternalStoreWithSelector)(
228
+ subscribe,
229
+ getSnapshot,
230
+ getSnapshot,
231
+ _selector,
232
+ options2 == null ? void 0 : options2.isEqual
233
+ );
234
+ };
235
+ const usePluvOthers = (selector = identity, options2) => {
236
+ var _a;
237
+ const room = usePluvRoom();
238
+ const subscribe = (0, import_react3.useCallback)(
239
+ (onStoreChange) => {
240
+ return room.subscribe("others", onStoreChange);
241
+ },
242
+ [room]
243
+ );
244
+ const getSnapshot = room.getOthers;
245
+ return (0, import_with_selector.useSyncExternalStoreWithSelector)(
246
+ subscribe,
247
+ getSnapshot,
248
+ getSnapshot,
249
+ selector,
250
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : shallowArrayEqual
251
+ );
252
+ };
253
+ const usePluvStorage = (key, selector = identity, options2) => {
254
+ var _a;
255
+ const room = usePluvRoom();
256
+ const subscribe = (0, import_react3.useCallback)(
257
+ (onStoreChange) => room.storage(key, onStoreChange),
258
+ [key, room]
259
+ );
260
+ const getSnapshot = (0, import_react3.useCallback)(() => {
261
+ var _a2, _b;
262
+ return (_b = (_a2 = room.getStorage(key)) == null ? void 0 : _a2.toJSON()) != null ? _b : null;
263
+ }, [key, room]);
264
+ const _selector = (0, import_react3.useCallback)(
265
+ (snapshot) => {
266
+ return !snapshot ? null : selector(snapshot);
267
+ },
268
+ [selector]
269
+ );
270
+ const data = (0, import_with_selector.useSyncExternalStoreWithSelector)(
271
+ subscribe,
272
+ getSnapshot,
273
+ getSnapshot,
274
+ _selector,
275
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : import_fast_deep_equal.default
276
+ );
277
+ const sharedType = room.getStorage(key);
278
+ return [data, sharedType];
279
+ };
280
+ return {
281
+ PluvRoomProvider,
282
+ usePluvBroadcast,
283
+ usePluvConnection,
284
+ usePluvEvent,
285
+ usePluvMyPresence,
286
+ usePluvMyself,
287
+ usePluvOther,
288
+ usePluvOthers,
289
+ usePluvRoom,
290
+ usePluvStorage
291
+ };
292
+ };
293
+
294
+ // src/createBundle.tsx
295
+ var import_jsx_runtime2 = require("react/jsx-runtime");
296
+ var createBundle = (client) => {
297
+ const PluvContext = (0, import_react4.createContext)(null);
298
+ const PluvProvider = (0, import_react5.memo)((props) => {
299
+ const { children } = props;
300
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PluvContext.Provider, {
301
+ value: client,
302
+ children
303
+ });
304
+ });
305
+ PluvProvider.displayName = "PluvProvider";
306
+ const usePluvClient = () => (0, import_react5.useContext)(PluvContext);
307
+ const createRoomBundle2 = (options = {}) => createRoomBundle(client, options);
308
+ return {
309
+ createRoomBundle: createRoomBundle2,
310
+ PluvProvider,
311
+ usePluvClient
312
+ };
313
+ };
314
+ // Annotate the CommonJS export names for ESM import in node:
315
+ 0 && (module.exports = {
316
+ ConnectionState,
317
+ createBundle,
318
+ createClient,
319
+ y
320
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,286 @@
1
+ // src/index.ts
2
+ import { ConnectionState, createClient, y } from "@pluv/client";
3
+
4
+ // src/createBundle.tsx
5
+ import { createContext as createContext2 } from "react";
6
+ import { memo as memo2, useContext as useContext2 } from "react";
7
+
8
+ // src/createRoomBundle.tsx
9
+ import fastDeepEqual from "fast-deep-equal";
10
+ import { createContext, useState as useState2 } from "react";
11
+ import { memo, useCallback as useCallback2, useContext, useEffect } from "react";
12
+
13
+ // src/internal/hooks/useRerender.ts
14
+ import { useCallback, useState } from "react";
15
+ var useRerender = () => {
16
+ const [, setCount] = useState(0);
17
+ return useCallback(() => {
18
+ setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
19
+ }, []);
20
+ };
21
+
22
+ // src/internal/hooks/useSyncExternalStoreWithSelector.ts
23
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
24
+
25
+ // src/internal/utils/identity.ts
26
+ var identity = (x) => x;
27
+
28
+ // src/internal/utils/shallowArrayEqual.ts
29
+ var shallowArrayEqual = (a, b) => {
30
+ return a.length === b.length && a.every((item, i) => item === b[i]);
31
+ };
32
+
33
+ // src/createRoomBundle.tsx
34
+ import { jsx } from "react/jsx-runtime";
35
+ var createRoomBundle = (client, options = {}) => {
36
+ const PluvRoomContext = createContext(
37
+ null
38
+ );
39
+ const PluvRoomProvider = memo((props) => {
40
+ const {
41
+ children,
42
+ debug,
43
+ initialPresence,
44
+ initialStorage,
45
+ onAuthorizationFail,
46
+ room: _room
47
+ } = props;
48
+ const rerender = useRerender();
49
+ const [room] = useState2(() => {
50
+ return client.createRoom(_room, {
51
+ debug,
52
+ initialPresence,
53
+ initialStorage: typeof initialStorage === "function" ? initialStorage : options.initialStorage,
54
+ presence: options.presence,
55
+ onAuthorizationFail
56
+ });
57
+ });
58
+ useEffect(() => {
59
+ const unsubscribe = room.subscribe("connection", () => {
60
+ rerender();
61
+ });
62
+ return () => {
63
+ unsubscribe();
64
+ };
65
+ }, [rerender, room]);
66
+ useEffect(() => {
67
+ client.enter(_room);
68
+ return () => {
69
+ client.leave(_room);
70
+ };
71
+ }, [_room]);
72
+ return /* @__PURE__ */ jsx(PluvRoomContext.Provider, {
73
+ value: room,
74
+ children
75
+ });
76
+ });
77
+ PluvRoomProvider.displayName = "PluvRoomProvider";
78
+ const usePluvRoom = () => useContext(PluvRoomContext);
79
+ const usePluvBroadcast = () => {
80
+ const room = usePluvRoom();
81
+ return useCallback2(
82
+ (message) => {
83
+ room.broadcast(message);
84
+ },
85
+ [room]
86
+ );
87
+ };
88
+ const usePluvConnection = (selector = identity, options2) => {
89
+ const room = usePluvRoom();
90
+ const subscribe = useCallback2(
91
+ (onStoreChange) => {
92
+ return room.subscribe("connection", onStoreChange);
93
+ },
94
+ [room]
95
+ );
96
+ const getSnapshot = room.getConnection;
97
+ const _selector = useCallback2(
98
+ (snapshot) => selector(snapshot),
99
+ [selector]
100
+ );
101
+ return useSyncExternalStoreWithSelector(
102
+ subscribe,
103
+ getSnapshot,
104
+ getSnapshot,
105
+ _selector,
106
+ options2 == null ? void 0 : options2.isEqual
107
+ );
108
+ };
109
+ const usePluvEvent = (type, callback) => {
110
+ const room = usePluvRoom();
111
+ useEffect(() => {
112
+ const unsubscribe = room.event(type, callback);
113
+ return () => {
114
+ unsubscribe();
115
+ };
116
+ }, [callback, room, type]);
117
+ };
118
+ const usePluvMyPresence = (selector = identity, options2) => {
119
+ const room = usePluvRoom();
120
+ const subscribe = useCallback2(
121
+ (onStoreChange) => {
122
+ return room.subscribe("my-presence", onStoreChange);
123
+ },
124
+ [room]
125
+ );
126
+ const getSnapshot = room.getMyPresence;
127
+ const _selector = useCallback2(
128
+ (snapshot) => {
129
+ return !snapshot ? null : selector(snapshot);
130
+ },
131
+ [selector]
132
+ );
133
+ const myPresence = useSyncExternalStoreWithSelector(
134
+ subscribe,
135
+ getSnapshot,
136
+ getSnapshot,
137
+ _selector,
138
+ options2 == null ? void 0 : options2.isEqual
139
+ );
140
+ const updateMyPresence = useCallback2(
141
+ (value) => {
142
+ const oldMyPresence = room.getMyPresence();
143
+ const presence = typeof value === "function" ? value(oldMyPresence) : value;
144
+ room.broadcast({
145
+ type: "$UPDATE_PRESENCE",
146
+ data: { presence }
147
+ });
148
+ },
149
+ [room]
150
+ );
151
+ return [myPresence, updateMyPresence];
152
+ };
153
+ const usePluvMyself = (selector = identity, options2) => {
154
+ const room = usePluvRoom();
155
+ const subscribe = useCallback2(
156
+ (onStoreChange) => {
157
+ return room.subscribe("myself", onStoreChange);
158
+ },
159
+ [room]
160
+ );
161
+ const getSnapshot = room.getMyself;
162
+ const _selector = useCallback2(
163
+ (snapshot) => {
164
+ return !snapshot ? null : selector(snapshot);
165
+ },
166
+ [selector]
167
+ );
168
+ return useSyncExternalStoreWithSelector(
169
+ subscribe,
170
+ getSnapshot,
171
+ getSnapshot,
172
+ _selector,
173
+ options2 == null ? void 0 : options2.isEqual
174
+ );
175
+ };
176
+ const usePluvOther = (connectionId, selector = identity, options2) => {
177
+ const room = usePluvRoom();
178
+ const subscribe = useCallback2(
179
+ (onStoreChange) => {
180
+ return room.other(connectionId, onStoreChange);
181
+ },
182
+ [room, connectionId]
183
+ );
184
+ const getSnapshot = useCallback2(
185
+ () => room.getOther(connectionId),
186
+ [room, connectionId]
187
+ );
188
+ const _selector = useCallback2(
189
+ (snapshot) => {
190
+ return !snapshot ? null : selector(snapshot);
191
+ },
192
+ [selector]
193
+ );
194
+ return useSyncExternalStoreWithSelector(
195
+ subscribe,
196
+ getSnapshot,
197
+ getSnapshot,
198
+ _selector,
199
+ options2 == null ? void 0 : options2.isEqual
200
+ );
201
+ };
202
+ const usePluvOthers = (selector = identity, options2) => {
203
+ var _a;
204
+ const room = usePluvRoom();
205
+ const subscribe = useCallback2(
206
+ (onStoreChange) => {
207
+ return room.subscribe("others", onStoreChange);
208
+ },
209
+ [room]
210
+ );
211
+ const getSnapshot = room.getOthers;
212
+ return useSyncExternalStoreWithSelector(
213
+ subscribe,
214
+ getSnapshot,
215
+ getSnapshot,
216
+ selector,
217
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : shallowArrayEqual
218
+ );
219
+ };
220
+ const usePluvStorage = (key, selector = identity, options2) => {
221
+ var _a;
222
+ const room = usePluvRoom();
223
+ const subscribe = useCallback2(
224
+ (onStoreChange) => room.storage(key, onStoreChange),
225
+ [key, room]
226
+ );
227
+ const getSnapshot = useCallback2(() => {
228
+ var _a2, _b;
229
+ return (_b = (_a2 = room.getStorage(key)) == null ? void 0 : _a2.toJSON()) != null ? _b : null;
230
+ }, [key, room]);
231
+ const _selector = useCallback2(
232
+ (snapshot) => {
233
+ return !snapshot ? null : selector(snapshot);
234
+ },
235
+ [selector]
236
+ );
237
+ const data = useSyncExternalStoreWithSelector(
238
+ subscribe,
239
+ getSnapshot,
240
+ getSnapshot,
241
+ _selector,
242
+ (_a = options2 == null ? void 0 : options2.isEqual) != null ? _a : fastDeepEqual
243
+ );
244
+ const sharedType = room.getStorage(key);
245
+ return [data, sharedType];
246
+ };
247
+ return {
248
+ PluvRoomProvider,
249
+ usePluvBroadcast,
250
+ usePluvConnection,
251
+ usePluvEvent,
252
+ usePluvMyPresence,
253
+ usePluvMyself,
254
+ usePluvOther,
255
+ usePluvOthers,
256
+ usePluvRoom,
257
+ usePluvStorage
258
+ };
259
+ };
260
+
261
+ // src/createBundle.tsx
262
+ import { jsx as jsx2 } from "react/jsx-runtime";
263
+ var createBundle = (client) => {
264
+ const PluvContext = createContext2(null);
265
+ const PluvProvider = memo2((props) => {
266
+ const { children } = props;
267
+ return /* @__PURE__ */ jsx2(PluvContext.Provider, {
268
+ value: client,
269
+ children
270
+ });
271
+ });
272
+ PluvProvider.displayName = "PluvProvider";
273
+ const usePluvClient = () => useContext2(PluvContext);
274
+ const createRoomBundle2 = (options = {}) => createRoomBundle(client, options);
275
+ return {
276
+ createRoomBundle: createRoomBundle2,
277
+ PluvProvider,
278
+ usePluvClient
279
+ };
280
+ };
281
+ export {
282
+ ConnectionState,
283
+ createBundle,
284
+ createClient,
285
+ y
286
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@pluv/react",
3
+ "version": "0.0.0",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "dependencies": {
11
+ "@pluv/client": "^0.0.0",
12
+ "@pluv/types": "^0.0.0",
13
+ "@types/use-sync-external-store": "^0.0.3",
14
+ "fast-deep-equal": "^3.1.3",
15
+ "use-sync-external-store": "^1.2.0"
16
+ },
17
+ "peerDependencies": {
18
+ "react": "^17.0.0 || ^18.0.0",
19
+ "yjs": "^13.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "@pluv/tsconfig": "^0.0.0",
23
+ "@types/react": "18.0.26",
24
+ "eslint-config-pluv": "^0.0.0",
25
+ "react": "18.2.0",
26
+ "tsup": "^6.5.0",
27
+ "typescript": "^4.9.4",
28
+ "yjs": "^13.5.44"
29
+ },
30
+ "scripts": {
31
+ "build": "tsup src/index.ts --format esm,cjs --dts --external react",
32
+ "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
33
+ "lint": "eslint src/**/*.ts* --fix",
34
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
35
+ }
36
+ }
@@ -0,0 +1,71 @@
1
+ import type { PluvClient } from "@pluv/client";
2
+ import type { IOLike, JsonObject } from "@pluv/types";
3
+ import { createContext, FC, ReactNode } from "react";
4
+ import { memo, useContext } from "react";
5
+ import type { AbstractType } from "yjs";
6
+ import {
7
+ CreateRoomBundle,
8
+ createRoomBundle as _createRoomBundle,
9
+ CreateRoomBundleOptions,
10
+ } from "./createRoomBundle";
11
+
12
+ export interface PluvProviderProps {
13
+ children?: ReactNode;
14
+ }
15
+
16
+ export interface CreateBundle<TIO extends IOLike> {
17
+ // factories
18
+ createRoomBundle: <
19
+ TPresence extends JsonObject = {},
20
+ TStorage extends Record<string, AbstractType<any>> = {}
21
+ >(
22
+ options?: CreateRoomBundleOptions<TPresence, TStorage>
23
+ ) => CreateRoomBundle<TIO, TPresence, TStorage>;
24
+
25
+ // components
26
+ PluvProvider: FC<PluvProviderProps>;
27
+
28
+ // hooks
29
+ usePluvClient: () => PluvClient<TIO>;
30
+ }
31
+
32
+ export const createBundle = <TIO extends IOLike>(
33
+ client: PluvClient<TIO>
34
+ ): CreateBundle<TIO> => {
35
+ /**
36
+ * !HACK
37
+ * @description We'll let the context error out if client is not provided,
38
+ * and let the users deal with it.
39
+ * @author leedavidcs
40
+ * @date October 27, 2022
41
+ */
42
+ const PluvContext = createContext<PluvClient<any>>(null as any);
43
+
44
+ const PluvProvider = memo<PluvProviderProps>((props) => {
45
+ const { children } = props;
46
+
47
+ return (
48
+ <PluvContext.Provider value={client}>
49
+ {children}
50
+ </PluvContext.Provider>
51
+ );
52
+ });
53
+
54
+ PluvProvider.displayName = "PluvProvider";
55
+
56
+ const usePluvClient = (): PluvClient<TIO> => useContext(PluvContext);
57
+
58
+ const createRoomBundle = <
59
+ TPresence extends JsonObject = {},
60
+ TStorage extends Record<string, AbstractType<any>> = {}
61
+ >(
62
+ options: CreateRoomBundleOptions<TPresence, TStorage> = {}
63
+ ): CreateRoomBundle<TIO, TPresence, TStorage> =>
64
+ _createRoomBundle(client, options);
65
+
66
+ return {
67
+ createRoomBundle,
68
+ PluvProvider,
69
+ usePluvClient,
70
+ };
71
+ };
@@ -0,0 +1,447 @@
1
+ import type {
2
+ InferYjsSharedTypeJson,
3
+ PluvClient,
4
+ PluvRoomDebug,
5
+ UserInfo,
6
+ WebSocketConnection,
7
+ } from "@pluv/client";
8
+ import { ConnectionState, PluvRoom } from "@pluv/client";
9
+ import type {
10
+ Id,
11
+ InferEventMessage,
12
+ InferIOInput,
13
+ InferIOOutput,
14
+ InputZodLike,
15
+ IOEventMessage,
16
+ IOLike,
17
+ JsonObject,
18
+ } from "@pluv/types";
19
+ import fastDeepEqual from "fast-deep-equal";
20
+ import { createContext, Dispatch, FC, ReactNode, useState } from "react";
21
+ import { memo, useCallback, useContext, useEffect } from "react";
22
+ import type { AbstractType } from "yjs";
23
+ import {
24
+ identity,
25
+ shallowArrayEqual,
26
+ useRerender,
27
+ useSyncExternalStoreWithSelector,
28
+ } from "./internal";
29
+
30
+ export interface CreateRoomBundleOptions<
31
+ TPresence extends JsonObject = {},
32
+ TStorage extends Record<string, AbstractType<any>> = {}
33
+ > {
34
+ initialStorage?: () => TStorage;
35
+ presence?: InputZodLike<TPresence>;
36
+ }
37
+
38
+ export type PluvRoomProviderProps<
39
+ TIO extends IOLike,
40
+ TPresence extends JsonObject,
41
+ TStorage extends Record<string, AbstractType<any>>
42
+ > = {
43
+ children?: ReactNode;
44
+ debug?: boolean | PluvRoomDebug<TIO>;
45
+ onAuthorizationFail?: (error: Error) => void;
46
+ room: string;
47
+ } & (keyof TPresence extends never
48
+ ? { initialPresence?: never }
49
+ : { initialPresence: TPresence }) &
50
+ (keyof TStorage extends never
51
+ ? { initialStorage?: never }
52
+ : { initialStorage?: () => TStorage });
53
+
54
+ export interface SubscriptionHookOptions<T extends unknown> {
55
+ isEqual?: (a: T, b: T) => boolean;
56
+ }
57
+
58
+ export type UpdateMyPresenceAction<TPresence extends JsonObject> =
59
+ | Partial<TPresence>
60
+ | ((oldPresence: TPresence | null) => Partial<TPresence>);
61
+
62
+ export interface CreateRoomBundle<
63
+ TIO extends IOLike,
64
+ TPresence extends JsonObject,
65
+ TStorage extends Record<string, AbstractType<any>>
66
+ > {
67
+ // components
68
+ PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
69
+
70
+ // hooks
71
+ usePluvBroadcast: <
72
+ TMessage extends InferEventMessage<InferIOInput<TIO>>
73
+ >() => (message: TMessage) => void;
74
+ usePluvConnection: <T extends unknown = WebSocketConnection>(
75
+ selector: (connection: WebSocketConnection) => T,
76
+ options?: SubscriptionHookOptions<Id<T>>
77
+ ) => Id<T>;
78
+ usePluvEvent: <TType extends keyof InferIOOutput<TIO>>(
79
+ type: TType,
80
+ callback: (data: Id<IOEventMessage<TIO, TType>>) => void
81
+ ) => void;
82
+ usePluvMyPresence: <T extends unknown = TPresence>(
83
+ selector?: (myPresence: TPresence) => T,
84
+ options?: SubscriptionHookOptions<Id<T> | null>
85
+ ) => [
86
+ myPresence: Id<T> | null,
87
+ updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>
88
+ ];
89
+ usePluvMyself: <T extends unknown = UserInfo<TIO, TPresence>>(
90
+ selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T,
91
+ options?: SubscriptionHookOptions<Id<T> | null>
92
+ ) => Id<T> | null;
93
+ usePluvOther: <T extends unknown = UserInfo<TIO, TPresence>>(
94
+ connectionId: string,
95
+ selector?: (other: UserInfo<TIO, TPresence>) => T,
96
+ options?: SubscriptionHookOptions<Id<T> | null>
97
+ ) => Id<T> | null;
98
+ usePluvOthers: <T extends unknown = UserInfo<TIO, TPresence>>(
99
+ selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T[],
100
+ options?: SubscriptionHookOptions<readonly Id<T>[]>
101
+ ) => readonly Id<T>[];
102
+ usePluvRoom: () => PluvRoom<TIO, TPresence>;
103
+ usePluvStorage: <
104
+ TKey extends keyof TStorage,
105
+ TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>
106
+ >(
107
+ key: TKey,
108
+ selector?: (data: InferYjsSharedTypeJson<TStorage[TKey]>) => TData,
109
+ options?: SubscriptionHookOptions<TData | null>
110
+ ) => [data: TData | null, sharedType: TStorage[TKey] | null];
111
+ }
112
+
113
+ export const createRoomBundle = <
114
+ TIO extends IOLike,
115
+ TPresence extends JsonObject = {},
116
+ TStorage extends Record<string, AbstractType<any>> = {}
117
+ >(
118
+ client: PluvClient<TIO>,
119
+ options: CreateRoomBundleOptions<TPresence, TStorage> = {}
120
+ ): CreateRoomBundle<TIO, TPresence, TStorage> => {
121
+ /**
122
+ * !HACK
123
+ * @description We'll let the context error out if the room is not provided,
124
+ * and let the users deal with it.
125
+ * @author leedavidcs
126
+ * @date November 11, 2022
127
+ */
128
+ const PluvRoomContext = createContext<PluvRoom<TIO, TPresence, TStorage>>(
129
+ null as any
130
+ );
131
+
132
+ const PluvRoomProvider = memo<
133
+ PluvRoomProviderProps<TIO, TPresence, TStorage>
134
+ >((props) => {
135
+ const {
136
+ children,
137
+ debug,
138
+ initialPresence,
139
+ initialStorage,
140
+ onAuthorizationFail,
141
+ room: _room,
142
+ } = props;
143
+
144
+ const rerender = useRerender();
145
+
146
+ const [room] = useState<PluvRoom<TIO, TPresence, TStorage>>(() => {
147
+ return client.createRoom<TPresence, TStorage>(_room, {
148
+ debug,
149
+ initialPresence,
150
+ initialStorage:
151
+ typeof initialStorage === "function"
152
+ ? initialStorage
153
+ : options.initialStorage,
154
+ presence: options.presence,
155
+ onAuthorizationFail,
156
+ });
157
+ });
158
+
159
+ useEffect(() => {
160
+ const unsubscribe = room.subscribe("connection", () => {
161
+ rerender();
162
+ });
163
+
164
+ return () => {
165
+ unsubscribe();
166
+ };
167
+ }, [rerender, room]);
168
+
169
+ useEffect(() => {
170
+ client.enter(_room);
171
+
172
+ return () => {
173
+ client.leave(_room);
174
+ };
175
+ }, [_room]);
176
+
177
+ return (
178
+ <PluvRoomContext.Provider value={room}>
179
+ {children}
180
+ </PluvRoomContext.Provider>
181
+ );
182
+ });
183
+
184
+ PluvRoomProvider.displayName = "PluvRoomProvider";
185
+
186
+ const usePluvRoom = () => useContext(PluvRoomContext);
187
+
188
+ const usePluvBroadcast = <
189
+ TMessage extends InferEventMessage<InferIOInput<TIO>>
190
+ >(): ((message: TMessage) => void) => {
191
+ const room = usePluvRoom();
192
+
193
+ return useCallback(
194
+ (message: TMessage) => {
195
+ room.broadcast(message);
196
+ },
197
+ [room]
198
+ );
199
+ };
200
+
201
+ const usePluvConnection = <T extends unknown = WebSocketConnection>(
202
+ selector = identity as (connection: WebSocketConnection) => T,
203
+ options?: SubscriptionHookOptions<Id<T>>
204
+ ): Id<T> => {
205
+ const room = usePluvRoom();
206
+
207
+ const subscribe = useCallback(
208
+ (onStoreChange: () => void) => {
209
+ return room.subscribe("connection", onStoreChange);
210
+ },
211
+ [room]
212
+ );
213
+
214
+ const getSnapshot = room.getConnection;
215
+
216
+ const _selector = useCallback(
217
+ (snapshot: WebSocketConnection) => selector(snapshot) as Id<T>,
218
+ [selector]
219
+ );
220
+
221
+ return useSyncExternalStoreWithSelector(
222
+ subscribe,
223
+ getSnapshot,
224
+ getSnapshot,
225
+ _selector,
226
+ options?.isEqual
227
+ );
228
+ };
229
+
230
+ const usePluvEvent = <TType extends keyof InferIOOutput<TIO>>(
231
+ type: TType,
232
+ callback: (data: Id<IOEventMessage<TIO, TType>>) => void
233
+ ): void => {
234
+ const room = usePluvRoom();
235
+
236
+ useEffect(() => {
237
+ const unsubscribe = room.event(type, callback);
238
+
239
+ return () => {
240
+ unsubscribe();
241
+ };
242
+ }, [callback, room, type]);
243
+ };
244
+
245
+ const usePluvMyPresence = <T extends unknown = TPresence>(
246
+ selector = identity as (myPresence: TPresence) => T,
247
+ options?: SubscriptionHookOptions<Id<T> | null>
248
+ ): [Id<T> | null, Dispatch<UpdateMyPresenceAction<TPresence>>] => {
249
+ const room = usePluvRoom();
250
+
251
+ const subscribe = useCallback(
252
+ (onStoreChange: () => void) => {
253
+ return room.subscribe("my-presence", onStoreChange);
254
+ },
255
+ [room]
256
+ );
257
+
258
+ const getSnapshot = room.getMyPresence;
259
+
260
+ const _selector = useCallback(
261
+ (snapshot: TPresence | null) => {
262
+ return !snapshot ? null : (selector(snapshot) as Id<T>);
263
+ },
264
+ [selector]
265
+ );
266
+
267
+ const myPresence = useSyncExternalStoreWithSelector(
268
+ subscribe,
269
+ getSnapshot,
270
+ getSnapshot,
271
+ _selector,
272
+ options?.isEqual
273
+ );
274
+
275
+ const updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>> =
276
+ useCallback(
277
+ (value: UpdateMyPresenceAction<TPresence>): void => {
278
+ const oldMyPresence = room.getMyPresence();
279
+ const presence =
280
+ typeof value === "function"
281
+ ? value(oldMyPresence)
282
+ : value;
283
+
284
+ room.broadcast({
285
+ type: "$UPDATE_PRESENCE",
286
+ data: { presence },
287
+ } as InferEventMessage<InferIOInput<TIO>>);
288
+ },
289
+ [room]
290
+ );
291
+
292
+ return [myPresence, updateMyPresence];
293
+ };
294
+
295
+ const usePluvMyself = <T extends unknown = UserInfo<TIO, TPresence>>(
296
+ selector = identity as (myself: Id<UserInfo<TIO, TPresence>>) => T,
297
+ options?: SubscriptionHookOptions<Id<T> | null>
298
+ ): Id<T> | null => {
299
+ const room = usePluvRoom();
300
+
301
+ const subscribe = useCallback(
302
+ (onStoreChange: () => void) => {
303
+ return room.subscribe("myself", onStoreChange);
304
+ },
305
+ [room]
306
+ );
307
+
308
+ const getSnapshot = room.getMyself;
309
+
310
+ const _selector = useCallback(
311
+ (snapshot: Id<UserInfo<TIO, TPresence>> | null) => {
312
+ return !snapshot ? null : (selector(snapshot) as Id<T>);
313
+ },
314
+ [selector]
315
+ );
316
+
317
+ return useSyncExternalStoreWithSelector(
318
+ subscribe,
319
+ getSnapshot,
320
+ getSnapshot,
321
+ _selector,
322
+ options?.isEqual
323
+ );
324
+ };
325
+
326
+ const usePluvOther = <T extends unknown = UserInfo<TIO, TPresence>>(
327
+ connectionId: string,
328
+ selector = identity as (other: UserInfo<TIO, TPresence>) => T,
329
+ options?: SubscriptionHookOptions<Id<T> | null>
330
+ ): Id<T> | null => {
331
+ const room = usePluvRoom();
332
+
333
+ const subscribe = useCallback(
334
+ (onStoreChange: () => void) => {
335
+ return room.other(connectionId, onStoreChange);
336
+ },
337
+ [room, connectionId]
338
+ );
339
+
340
+ const getSnapshot = useCallback(
341
+ () => room.getOther(connectionId),
342
+ [room, connectionId]
343
+ );
344
+
345
+ const _selector = useCallback(
346
+ (snapshot: Id<UserInfo<TIO, TPresence>> | null) => {
347
+ return !snapshot ? null : (selector(snapshot) as Id<T>);
348
+ },
349
+ [selector]
350
+ );
351
+
352
+ return useSyncExternalStoreWithSelector(
353
+ subscribe,
354
+ getSnapshot,
355
+ getSnapshot,
356
+ _selector,
357
+ options?.isEqual
358
+ );
359
+ };
360
+
361
+ const usePluvOthers = <T extends unknown = UserInfo<TIO, TPresence>>(
362
+ selector = identity as (
363
+ other: readonly Id<UserInfo<TIO, TPresence>>[]
364
+ ) => T[],
365
+ options?: SubscriptionHookOptions<readonly Id<T>[]>
366
+ ): readonly Id<T>[] => {
367
+ const room = usePluvRoom();
368
+
369
+ const subscribe = useCallback(
370
+ (onStoreChange: () => void) => {
371
+ return room.subscribe("others", onStoreChange);
372
+ },
373
+ [room]
374
+ );
375
+
376
+ const getSnapshot = room.getOthers;
377
+
378
+ return useSyncExternalStoreWithSelector(
379
+ subscribe,
380
+ getSnapshot,
381
+ getSnapshot,
382
+ selector as (
383
+ other: readonly Id<UserInfo<TIO, TPresence>>[]
384
+ ) => Id<T>[],
385
+ options?.isEqual ?? shallowArrayEqual
386
+ );
387
+ };
388
+
389
+ const usePluvStorage = <
390
+ TKey extends keyof TStorage,
391
+ TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>
392
+ >(
393
+ key: TKey,
394
+ selector = identity as (
395
+ data: InferYjsSharedTypeJson<TStorage[TKey]>
396
+ ) => TData,
397
+ options?: SubscriptionHookOptions<TData | null>
398
+ ): [data: TData | null, sharedType: TStorage[TKey] | null] => {
399
+ const room = usePluvRoom();
400
+
401
+ const subscribe = useCallback(
402
+ (onStoreChange: () => void) => room.storage(key, onStoreChange),
403
+ [key, room]
404
+ );
405
+
406
+ const getSnapshot = useCallback((): InferYjsSharedTypeJson<
407
+ TStorage[TKey]
408
+ > | null => {
409
+ return room.getStorage(key)?.toJSON() ?? null;
410
+ }, [key, room]);
411
+
412
+ const _selector = useCallback(
413
+ (snapshot: InferYjsSharedTypeJson<TStorage[TKey]> | null) => {
414
+ return !snapshot ? null : selector(snapshot);
415
+ },
416
+ [selector]
417
+ );
418
+
419
+ const data = useSyncExternalStoreWithSelector(
420
+ subscribe,
421
+ getSnapshot,
422
+ getSnapshot,
423
+ _selector,
424
+ options?.isEqual ?? fastDeepEqual
425
+ );
426
+
427
+ const sharedType = room.getStorage(key);
428
+
429
+ return [data, sharedType];
430
+ };
431
+
432
+ return {
433
+ // components
434
+ PluvRoomProvider,
435
+
436
+ // hooks
437
+ usePluvBroadcast,
438
+ usePluvConnection,
439
+ usePluvEvent,
440
+ usePluvMyPresence,
441
+ usePluvMyself,
442
+ usePluvOther,
443
+ usePluvOthers,
444
+ usePluvRoom,
445
+ usePluvStorage,
446
+ };
447
+ };
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export type { AbstractType } from "@pluv/client";
2
+ export { ConnectionState, createClient, y } from "@pluv/client";
3
+ export type {
4
+ BaseUser,
5
+ EventMessage,
6
+ EventRecord,
7
+ IOEventMessage,
8
+ } from "@pluv/types";
9
+ export type { CreateBundle, PluvProviderProps } from "./createBundle";
10
+ export { createBundle } from "./createBundle";
11
+ export type {
12
+ CreateRoomBundle,
13
+ CreateRoomBundleOptions,
14
+ PluvRoomProviderProps,
15
+ SubscriptionHookOptions,
16
+ } from "./createRoomBundle";
@@ -0,0 +1,3 @@
1
+ export { useRerender } from "./useRerender";
2
+ export { useSyncExternalStore } from "./useSyncExternalStore";
3
+ export { useSyncExternalStoreWithSelector } from "./useSyncExternalStoreWithSelector";
@@ -0,0 +1,9 @@
1
+ import { useCallback, useState } from "react";
2
+
3
+ export const useRerender = (): (() => void) => {
4
+ const [, setCount] = useState(0);
5
+
6
+ return useCallback(() => {
7
+ setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
8
+ }, []);
9
+ };
@@ -0,0 +1,14 @@
1
+ import { useSyncExternalStoreWithSelector } from "./useSyncExternalStoreWithSelector";
2
+ import { identity } from "../utils";
3
+
4
+ export const useSyncExternalStore = <TSnapshot extends unknown>(
5
+ subscribe: (onStoreChange: () => void) => () => void,
6
+ getSnapshot: () => TSnapshot,
7
+ getServerSnapshot: undefined | null | (() => TSnapshot)
8
+ ) =>
9
+ useSyncExternalStoreWithSelector(
10
+ subscribe,
11
+ getSnapshot,
12
+ getServerSnapshot,
13
+ identity
14
+ );
@@ -0,0 +1 @@
1
+ export { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
@@ -0,0 +1,6 @@
1
+ export {
2
+ useRerender,
3
+ useSyncExternalStore,
4
+ useSyncExternalStoreWithSelector,
5
+ } from "./hooks";
6
+ export { identity, shallowArrayEqual } from "./utils";
@@ -0,0 +1 @@
1
+ export const identity = <T extends unknown>(x: T): T => x;
@@ -0,0 +1,2 @@
1
+ export { identity } from "./identity";
2
+ export { shallowArrayEqual } from "./shallowArrayEqual";
@@ -0,0 +1,6 @@
1
+ export const shallowArrayEqual = <T extends unknown>(
2
+ a: readonly T[] | T[],
3
+ b: readonly T[] | T[]
4
+ ): boolean => {
5
+ return a.length === b.length && a.every((item, i) => item === b[i]);
6
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@pluv/tsconfig/react-library.json",
3
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules"]
5
+ }