@pluv/react 0.0.0-canary-20261027022227
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/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/index.d.mts +77 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +528 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Shoubhit Dash
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## `@pluv/react`
|
|
2
|
+
|
|
3
|
+
> [React.js](https://reactjs.org/) bindings for [@pluv/client](https://www.npmjs.com/package/@pluv/client).
|
|
4
|
+
|
|
5
|
+
**👉 See full documentation on [docs.pluv.io](https://docs.pluv.io/docs). 👈**
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# npm
|
|
11
|
+
npm install @pluv/client @pluv/react @pluv/crdt-yjs yjs zod
|
|
12
|
+
|
|
13
|
+
# yarn
|
|
14
|
+
yarn add @pluv/client @pluv/react @pluv/crdt-yjs yjs zod
|
|
15
|
+
|
|
16
|
+
# pnpm
|
|
17
|
+
pnpm add @pluv/client @pluv/react @pluv/crdt-yjs yjs zod
|
|
18
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ClientDefs, InferClientMetadata, InferClientOutput, InferClientPresence, InferSchemaInput, MockedRoomEvents, PluvClient, PluvRoomAddon, PluvRoomDebug, UserInfo, WebSocketConnection } from "@pluv/client";
|
|
2
|
+
import { Dispatch, FC, ReactNode } from "react";
|
|
3
|
+
import { BroadcastProxy, CrdtDocLike, IOEventMessage, Id, MaybePromise, ParticipantKindOptions, ParticipantKindsOptions, PresenceProcedureProxy, PublicEventKey, RoomError, RoomLike, RoomStats, StorageProcedureProxy, UpdateMyPresenceAction } from "@pluv/types";
|
|
4
|
+
import { InferDoc, InferJson, InferSeed, InferStorage } from "@pluv/crdt";
|
|
5
|
+
interface PluvProviderProps {
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Storage is all-or-nothing, so a union (not two nullable slots) lets one check narrow both.
|
|
10
|
+
*/
|
|
11
|
+
type UseStorageResult<TData extends unknown, TSharedType extends unknown> = [data: null, sharedType: null] | [data: TData, sharedType: TSharedType];
|
|
12
|
+
type BaseRoomProviderProps<TDefs extends ClientDefs> = {
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
initialStorage?: keyof InferSeed<TDefs["storage"]> extends never ? never : InferSeed<TDefs["storage"]>;
|
|
15
|
+
room: string;
|
|
16
|
+
} & (keyof InferClientPresence<TDefs> extends never ? {
|
|
17
|
+
initialPresence?: never;
|
|
18
|
+
} : {
|
|
19
|
+
initialPresence: InferSchemaInput<TDefs["presence"]>;
|
|
20
|
+
});
|
|
21
|
+
type MockedRoomProviderProps<TDefs extends ClientDefs = ClientDefs> = BaseRoomProviderProps<TDefs> & {
|
|
22
|
+
events?: MockedRoomEvents<TDefs>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* React event helper (`event.foo.useEvent`). Distinct from `@pluv/types`
|
|
26
|
+
* `EventProxy`, which is `room.subscribe.event`.
|
|
27
|
+
*/
|
|
28
|
+
type EventProxy<TDefs extends ClientDefs = ClientDefs> = { [event in PublicEventKey<InferClientOutput<TDefs>>]: {
|
|
29
|
+
useEvent: (callback: (data: Id<IOEventMessage<TDefs["io"], event>>) => void) => void;
|
|
30
|
+
}; };
|
|
31
|
+
type MetadataGetter<TMetadata extends Record<string, any>> = TMetadata | (() => MaybePromise<TMetadata>);
|
|
32
|
+
type PluvRoomProviderProps<TDefs extends ClientDefs = ClientDefs> = BaseRoomProviderProps<TDefs> & {
|
|
33
|
+
connect?: boolean;
|
|
34
|
+
debug?: boolean | PluvRoomDebug<TDefs["io"]>;
|
|
35
|
+
onAuthorizationFail?: (error: Error) => void;
|
|
36
|
+
} & (keyof InferClientMetadata<TDefs> extends never ? {
|
|
37
|
+
metadata?: undefined;
|
|
38
|
+
} : {
|
|
39
|
+
metadata: MetadataGetter<InferSchemaInput<TDefs["metadata"]>>;
|
|
40
|
+
});
|
|
41
|
+
interface SubscriptionHookOptions<T extends unknown> {
|
|
42
|
+
isEqual?: (a: T, b: T) => boolean;
|
|
43
|
+
}
|
|
44
|
+
interface CreateBundle<TDefs extends ClientDefs = ClientDefs, TSuspense extends boolean = false> {
|
|
45
|
+
MockedRoomProvider: FC<MockedRoomProviderProps<TDefs>>;
|
|
46
|
+
PluvProvider: FC<PluvProviderProps>;
|
|
47
|
+
PluvRoomProvider: FC<PluvRoomProviderProps<TDefs>>;
|
|
48
|
+
event: EventProxy<TDefs>;
|
|
49
|
+
useBroadcast: () => BroadcastProxy<TDefs["io"]>;
|
|
50
|
+
useCanRedo: () => boolean;
|
|
51
|
+
useCanUndo: () => boolean;
|
|
52
|
+
useClient: () => PluvClient<TDefs>;
|
|
53
|
+
useConnection: <TValue extends unknown = WebSocketConnection>(selector?: (connection: WebSocketConnection) => TValue, options?: SubscriptionHookOptions<Id<TValue>>) => Id<TValue>;
|
|
54
|
+
useDoc: () => CrdtDocLike<InferDoc<TDefs["storage"]>, InferStorage<TDefs["storage"]>, InferJson<TDefs["storage"]>>;
|
|
55
|
+
useEvent: <TType extends PublicEventKey<InferClientOutput<TDefs>>>(type: TType, callback: Parameters<EventProxy<TDefs>[TType]["useEvent"]>[0]) => void;
|
|
56
|
+
useMyPresence: <TValue extends unknown = InferClientPresence<TDefs>>(selector?: (myPresence: InferClientPresence<TDefs>) => TValue, options?: SubscriptionHookOptions<Id<TValue> | null>) => [myPresence: Id<TValue>, updateMyPresence: Dispatch<UpdateMyPresenceAction<InferClientPresence<TDefs>>>];
|
|
57
|
+
useMyself: <TValue extends unknown = UserInfo<TDefs["io"], InferClientPresence<TDefs>>>(selector?: (myself: Id<UserInfo<TDefs["io"], InferClientPresence<TDefs>>>) => TValue, options?: SubscriptionHookOptions<Id<TValue> | null>) => Id<TValue> | null;
|
|
58
|
+
useOther: <TValue extends unknown = UserInfo<TDefs["io"], InferClientPresence<TDefs>>>(userId: string, selector?: (other: UserInfo<TDefs["io"], InferClientPresence<TDefs>>) => TValue, options?: SubscriptionHookOptions<TValue | null> & ParticipantKindOptions) => TValue | null;
|
|
59
|
+
useOthers: <TValue extends unknown = readonly UserInfo<TDefs["io"], InferClientPresence<TDefs>>[]>(selector?: (other: readonly Id<UserInfo<TDefs["io"], InferClientPresence<TDefs>>>[]) => TValue, options?: SubscriptionHookOptions<TValue> & ParticipantKindsOptions) => TValue;
|
|
60
|
+
usePresence: () => PresenceProcedureProxy<TDefs["treaty"]["_defs"]["procedures"]["presence"]>;
|
|
61
|
+
useRedo: () => () => void;
|
|
62
|
+
useRoom: () => RoomLike<TDefs["io"], InferDoc<TDefs["storage"]>, InferClientPresence<TDefs>, InferStorage<TDefs["storage"]>, InferJson<TDefs["storage"]>, TDefs["treaty"]>;
|
|
63
|
+
useRoomError: (callback: (error: RoomError) => void) => void;
|
|
64
|
+
useRoomStats: <TValue extends unknown = RoomStats>(selector?: (stats: RoomStats) => TValue, options?: SubscriptionHookOptions<TValue> & ParticipantKindsOptions) => TValue;
|
|
65
|
+
useStorage: () => StorageProcedureProxy<TDefs["treaty"]["_defs"]["procedures"]["storage"]>;
|
|
66
|
+
useStorageField: <TKey extends keyof InferJson<TDefs["storage"]>, TData extends unknown = InferJson<TDefs["storage"]>[TKey]>(key: TKey, selector?: (data: InferJson<TDefs["storage"]>[TKey]) => TData, options?: SubscriptionHookOptions<TData | null>) => TSuspense extends true ? [data: TData, sharedType: InferStorage<TDefs["storage"]>[TKey]] : UseStorageResult<TData, InferStorage<TDefs["storage"]>[TKey]>;
|
|
67
|
+
useTransact: () => (fn: (storage: InferStorage<TDefs["storage"]>) => void, origin?: string) => void;
|
|
68
|
+
useUndo: () => () => void;
|
|
69
|
+
}
|
|
70
|
+
type InferBundleRoom<TBundle extends CreateBundle<any, any>> = TBundle extends CreateBundle<infer TDefs, any> ? RoomLike<TDefs["io"], InferDoc<TDefs["storage"]>, InferClientPresence<TDefs>, InferStorage<TDefs["storage"]>, InferJson<TDefs["storage"]>, TDefs["treaty"]> : never;
|
|
71
|
+
type CreateBundleOptions<TSuspense extends boolean = false> = {
|
|
72
|
+
addons?: readonly PluvRoomAddon<any>[];
|
|
73
|
+
suspense?: TSuspense;
|
|
74
|
+
};
|
|
75
|
+
export declare const createBundle: <TDefs extends ClientDefs, const TSuspense extends boolean = false>(client: PluvClient<TDefs>, options?: CreateBundleOptions<TSuspense>) => CreateBundle<TDefs, TSuspense>;
|
|
76
|
+
export type { CreateBundle, EventProxy, InferBundleRoom, PluvProviderProps, PluvRoomProviderProps, SubscriptionHookOptions, UseStorageResult };
|
|
77
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/createBundle.tsx"],"mappings":";;;;UA+BiB;EACb,WAAW;;;;;KAMH,iBAAiB,uBAAuB,gCAC7C,YAAY,qBACZ,MAAM,OAAO,YAAY;KAE3B,sBAAsB,cAAc;EACrC,WAAW;EACX,uBAAuB,UAAU,0CAE3B,UAAU;EAChB;WACO,oBAAoB;EACvB;;EACA,iBAAiB,iBAAiB;;KAE9B,wBAAwB,cAAc,aAAa,cAC3D,sBAAsB;EAClB,SAAS,iBAAiB;;;;;;KAOtB,WAAW,cAAc,aAAa,iBAC7C,SAAS,eAAe,kBAAkB;EACvC,WAAW,WAAW,MAAM,GAAG,eAAe,aAAa;;KAIvD,eAAe,kBAAkB,uBACvC,mBACO,aAAa;KAEd,sBAAsB,cAAc,aAAa,cACzD,sBAAsB;EAClB;EACA,kBAAkB,cAAc;EAChC,uBAAuB,OAAO;WACvB,oBAAoB;EACnB;;EACA,UAAU,eAAe,iBAAiB;;UAEzC,wBAAwB;EACrC,WAAW,GAAG,GAAG,GAAG;;UAGP,aACb,cAAc,aAAa,YAC3B;EAGA,oBAAoB,GAAG,wBAAwB;EAC/C,cAAc,GAAG;EACjB,kBAAkB,GAAG,sBAAsB;EAG3C,OAAO,WAAW;EAGlB,oBAAoB,eAAe;EACnC;EACA;EACA,iBAAiB,WAAW;EAC5B,gBAAgB,yBAAyB,qBACrC,YAAY,YAAY,wBAAwB,QAChD,UAAU,wBAAwB,GAAG,aACpC,GAAG;EACR,cAAc,YACV,SAAS,mBACT,aAAa,mBACb,UAAU;EAEd,WAAW,cAAc,eAAe,kBAAkB,SACtD,MAAM,OACN,UAAU,WAAW,WAAW,OAAO;EAE3C,gBAAgB,yBAAyB,oBAAoB,QACzD,YAAY,YAAY,oBAAoB,WAAW,QACvD,UAAU,wBAAwB,GAAG,qBAErC,YAAY,GAAG,SACf,kBAAkB,SAAS,uBAAuB,oBAAoB;EAE1E,YAAY,yBAAyB,SAAS,aAAa,oBAAoB,SAC3E,YAAY,QAAQ,GAAG,SAAS,aAAa,oBAAoB,aAAa,QAC9E,UAAU,wBAAwB,GAAG,oBACpC,GAAG;EACR,WAAW,yBAAyB,SAAS,aAAa,oBAAoB,SAC1E,gBACA,YAAY,OAAO,SAAS,aAAa,oBAAoB,YAAY,QACzE,UAAU,wBAAwB,iBAAiB,2BAClD;EACL,YACI,kCAAkC,SAAS,aAAa,oBAAoB,WAE5E,YACI,gBAAgB,GAAG,SAAS,aAAa,oBAAoB,eAC5D,QACL,UAAU,wBAAwB,UAAU,4BAC3C;EACL,mBAAmB,uBAAuB;EAC1C;EACA,eAAe,SACX,aACA,SAAS,mBACT,oBAAoB,QACpB,aAAa,mBACb,UAAU,mBACV;EAEJ,eAAe,WAAW,OAAO;EACjC,eAAe,yBAAyB,WACpC,YAAY,OAAO,cAAc,QACjC,UAAU,wBAAwB,UAAU,4BAC3C;EACL,kBAAkB,sBAAsB;EACxC,kBACI,mBAAmB,UAAU,mBAC7B,wBAAwB,UAAU,kBAAkB,OAEpD,KAAK,MACL,YAAY,MAAM,UAAU,kBAAkB,UAAU,OACxD,UAAU,wBAAwB,kBACjC,0BACE,MAAM,OAAO,YAAY,aAAa,kBAAkB,SACzD,iBAAiB,OAAO,aAAa,kBAAkB;EAC7D,oBACI,KAAK,SAAS,aAAa,4BAC3B;EAEJ;;KAGQ,gBAAgB,gBAAgB,0BACxC,gBAAgB,mBAAmB,cAC7B,SACI,aACA,SAAS,mBACT,oBAAoB,QACpB,aAAa,mBACb,UAAU,mBACV;KCtHF,oBAAoB;EAC5B,kBAAkB;EAClB,WAAW;;qBAGF,eAAgB,cAAc,kBAAkB,mCAAiC,QAClF,WAAW,QAAM,UAChB,oBAAoB,eAC9B,aAAa,OAAO"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import { MockedRoom, parsePluvSchema } from "@pluv/client";
|
|
2
|
+
import fastDeepEqual from "fast-deep-equal";
|
|
3
|
+
import { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { ConnectionState } from "@pluv/types";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
// 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]);
|
|
19
|
+
};
|
|
20
|
+
// src/internal/hooks/useDeepAsyncMemo.ts
|
|
21
|
+
const useDeepAsyncMemo = (factory, isEqual = fastDeepEqual) => {
|
|
22
|
+
const [state, setState] = useState({
|
|
23
|
+
isInitialized: false,
|
|
24
|
+
isLoading: true,
|
|
25
|
+
value: void 0,
|
|
26
|
+
error: null
|
|
27
|
+
});
|
|
28
|
+
const lastValueRef = useRef(void 0);
|
|
29
|
+
const hasValueRef = useRef(false);
|
|
30
|
+
const inflightRef = useRef(null);
|
|
31
|
+
const isMountedRef = useRef(true);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
isMountedRef.current = true;
|
|
34
|
+
const promise = factory();
|
|
35
|
+
inflightRef.current = promise;
|
|
36
|
+
setState((prev) => {
|
|
37
|
+
return {
|
|
38
|
+
...prev,
|
|
39
|
+
isLoading: true,
|
|
40
|
+
error: null
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
promise.then((result) => {
|
|
44
|
+
if (!isMountedRef.current || inflightRef.current !== promise) return;
|
|
45
|
+
if (hasValueRef.current && isEqual(lastValueRef.current, result)) {
|
|
46
|
+
setState((prev) => {
|
|
47
|
+
return {
|
|
48
|
+
...prev,
|
|
49
|
+
isInitialized: true,
|
|
50
|
+
isLoading: false
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
lastValueRef.current = result;
|
|
56
|
+
hasValueRef.current = true;
|
|
57
|
+
setState({
|
|
58
|
+
isInitialized: true,
|
|
59
|
+
isLoading: false,
|
|
60
|
+
value: result,
|
|
61
|
+
error: null
|
|
62
|
+
});
|
|
63
|
+
}).catch((err) => {
|
|
64
|
+
if (!isMountedRef.current || inflightRef.current !== promise) return;
|
|
65
|
+
setState({
|
|
66
|
+
isInitialized: hasValueRef.current,
|
|
67
|
+
isLoading: false,
|
|
68
|
+
value: void 0,
|
|
69
|
+
error: err instanceof Error ? err : /* @__PURE__ */ new Error("Unknown error")
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
return () => {
|
|
73
|
+
isMountedRef.current = false;
|
|
74
|
+
};
|
|
75
|
+
}, [isEqual]);
|
|
76
|
+
return state;
|
|
77
|
+
};
|
|
78
|
+
// src/internal/hooks/useNoSsr.ts
|
|
79
|
+
const useNoSsr = () => {
|
|
80
|
+
const [didMount, setDidMount] = useState(false);
|
|
81
|
+
useEffect(() => setDidMount(typeof window !== "undefined"), []);
|
|
82
|
+
return useCallback((value, fallback = null) => {
|
|
83
|
+
return didMount ? typeof value !== "function" ? value : value() : fallback;
|
|
84
|
+
}, [didMount]);
|
|
85
|
+
};
|
|
86
|
+
// src/internal/hooks/useRerender.ts
|
|
87
|
+
const useRerender = () => {
|
|
88
|
+
const [, setCount] = useState(0);
|
|
89
|
+
return useCallback(() => {
|
|
90
|
+
setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);
|
|
91
|
+
}, []);
|
|
92
|
+
};
|
|
93
|
+
// src/internal/utils/getStorageReady.ts
|
|
94
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
95
|
+
const isFailedConnection = (state) => {
|
|
96
|
+
return state === ConnectionState.Closed || state === ConnectionState.Unavailable;
|
|
97
|
+
};
|
|
98
|
+
const getStorageReady = (room) => {
|
|
99
|
+
if (room.getStorageLoaded()) return Promise.resolve();
|
|
100
|
+
const cached = cache.get(room);
|
|
101
|
+
if (cached) return cached;
|
|
102
|
+
let settled = false;
|
|
103
|
+
let unsubscribe = null;
|
|
104
|
+
const promise = new Promise((resolve, reject) => {
|
|
105
|
+
const settle = (outcome) => {
|
|
106
|
+
if (settled) return;
|
|
107
|
+
settled = true;
|
|
108
|
+
unsubscribe?.();
|
|
109
|
+
cache.delete(room);
|
|
110
|
+
outcome();
|
|
111
|
+
};
|
|
112
|
+
const check = (connectionState) => {
|
|
113
|
+
if (room.getStorageLoaded()) {
|
|
114
|
+
settle(resolve);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (isFailedConnection(connectionState)) settle(() => {
|
|
118
|
+
reject(/* @__PURE__ */ new Error("Room closed or storage became unavailable before storage loaded"));
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
unsubscribe = room.subscribe.connection((state) => {
|
|
122
|
+
check(state.connection.state);
|
|
123
|
+
});
|
|
124
|
+
check(room.getConnection().state);
|
|
125
|
+
});
|
|
126
|
+
if (!settled) cache.set(room, promise);
|
|
127
|
+
return promise;
|
|
128
|
+
};
|
|
129
|
+
// src/internal/utils/identity.ts
|
|
130
|
+
const identity = (x) => x;
|
|
131
|
+
// src/internal/utils/participantKindsKey.ts
|
|
132
|
+
const participantKindsKey = {
|
|
133
|
+
toKey(kinds) {
|
|
134
|
+
if (kinds === void 0) return "";
|
|
135
|
+
return `\0${kinds.join("\0")}`;
|
|
136
|
+
},
|
|
137
|
+
fromKey(kindsKey) {
|
|
138
|
+
if (kindsKey === "") return void 0;
|
|
139
|
+
const joined = kindsKey.slice(1);
|
|
140
|
+
return { kinds: joined === "" ? [] : joined.split("\0") };
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
// src/internal/utils/shallowArrayEqual.ts
|
|
144
|
+
const shallowArrayEqual = (a, b) => {
|
|
145
|
+
return a.length === b.length && a.every((item, i) => item === b[i]);
|
|
146
|
+
};
|
|
147
|
+
// src/internal/utils/shallowEqual.ts
|
|
148
|
+
const shallowEqual = (a, b) => a === b;
|
|
149
|
+
// src/internal/hooks/useSyncExternalStoreWithSelector.ts
|
|
150
|
+
const useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerSnapshot, selector, isEqual) => {
|
|
151
|
+
const noSsr = useNoSsr();
|
|
152
|
+
const cacheRef = useRef(null);
|
|
153
|
+
const _getServerSnapshot = getServerSnapshot ? () => {
|
|
154
|
+
const snapshot = selector(getServerSnapshot());
|
|
155
|
+
const compare = isEqual ?? shallowEqual;
|
|
156
|
+
const cached = cacheRef.current;
|
|
157
|
+
const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
|
|
158
|
+
/**
|
|
159
|
+
* !HACK
|
|
160
|
+
* @description Memoize only the last snapshot so that we can only rerender on changes
|
|
161
|
+
* @date June 22, 2024
|
|
162
|
+
*/
|
|
163
|
+
cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
|
|
164
|
+
return cacheRef.current;
|
|
165
|
+
} : void 0;
|
|
166
|
+
const _getClientSnapshot = () => {
|
|
167
|
+
const snapshot = selector(getClientSnapshot());
|
|
168
|
+
const compare = isEqual ?? shallowEqual;
|
|
169
|
+
const cached = cacheRef.current;
|
|
170
|
+
const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
|
|
171
|
+
/**
|
|
172
|
+
* !HACK
|
|
173
|
+
* @description Memoize only the last snapshot so that we can only rerender on changes
|
|
174
|
+
* @date June 22, 2024
|
|
175
|
+
*/
|
|
176
|
+
cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
|
|
177
|
+
return cacheRef.current;
|
|
178
|
+
};
|
|
179
|
+
const result = useSyncExternalStore(subscribe, _getClientSnapshot, _getServerSnapshot);
|
|
180
|
+
return typeof window !== "undefined" ? noSsr(result, _getServerSnapshot?.()) : result;
|
|
181
|
+
};
|
|
182
|
+
// src/createBundle.tsx
|
|
183
|
+
const createBundle = (client, options = {}) => {
|
|
184
|
+
/**
|
|
185
|
+
* !HACK
|
|
186
|
+
* @description We'll let the context error out if client is not provided,
|
|
187
|
+
* and let the users deal with it.
|
|
188
|
+
* @date October 27, 2022
|
|
189
|
+
*/
|
|
190
|
+
const PluvContext = createContext(null);
|
|
191
|
+
/**
|
|
192
|
+
* !HACK
|
|
193
|
+
* @description We'll let the context error out if the room is not provided,
|
|
194
|
+
* and let the users deal with it.
|
|
195
|
+
* @date November 11, 2022
|
|
196
|
+
*/
|
|
197
|
+
const PluvRoomContext = createContext(null);
|
|
198
|
+
const MockedRoomContext = createContext(null);
|
|
199
|
+
const MockedRoomProvider = memo((props) => {
|
|
200
|
+
const { children, events, initialPresence, initialStorage, room: _room } = props;
|
|
201
|
+
const [room] = useState(() => {
|
|
202
|
+
return new MockedRoom(_room, {
|
|
203
|
+
events,
|
|
204
|
+
initialPresence,
|
|
205
|
+
initialStorage,
|
|
206
|
+
presence: client._defs.treaty.presence,
|
|
207
|
+
storage: client._defs.storage,
|
|
208
|
+
treaty: client._defs.treaty
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
return /* @__PURE__ */ jsx(MockedRoomContext.Provider, {
|
|
212
|
+
value: room,
|
|
213
|
+
children: /* @__PURE__ */ jsx(PluvRoomContext.Provider, {
|
|
214
|
+
value: room,
|
|
215
|
+
children
|
|
216
|
+
})
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
MockedRoomProvider.displayName = "MockedRoomProvider";
|
|
220
|
+
const PluvRoomProvider = memo((props) => {
|
|
221
|
+
const { children, connect = true, debug, initialPresence, initialStorage, metadata, onAuthorizationFail, room: _room } = props;
|
|
222
|
+
const queue = useAsyncQueue();
|
|
223
|
+
const rerender = useRerender();
|
|
224
|
+
const mockedRoom = useContext(MockedRoomContext);
|
|
225
|
+
const createRoom = useCallback(() => {
|
|
226
|
+
return client.createRoom(_room, {
|
|
227
|
+
addons: options.addons,
|
|
228
|
+
debug,
|
|
229
|
+
initialPresence,
|
|
230
|
+
initialStorage,
|
|
231
|
+
metadata,
|
|
232
|
+
onAuthorizationFail
|
|
233
|
+
});
|
|
234
|
+
}, [
|
|
235
|
+
_room,
|
|
236
|
+
debug,
|
|
237
|
+
initialPresence,
|
|
238
|
+
initialStorage,
|
|
239
|
+
metadata,
|
|
240
|
+
onAuthorizationFail
|
|
241
|
+
]);
|
|
242
|
+
const [room, setRoom] = useState(() => createRoom());
|
|
243
|
+
if (room.id !== _room) setRoom(createRoom());
|
|
244
|
+
const resolvedMeta = useDeepAsyncMemo(async () => {
|
|
245
|
+
const resolved = await Promise.resolve(typeof metadata === "function" ? metadata() : metadata);
|
|
246
|
+
return room.metadata ? parsePluvSchema(room.metadata, resolved) : resolved;
|
|
247
|
+
});
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
const unsubscribe = room.subscribe.connection(() => {
|
|
250
|
+
rerender();
|
|
251
|
+
});
|
|
252
|
+
return () => {
|
|
253
|
+
unsubscribe();
|
|
254
|
+
};
|
|
255
|
+
}, [rerender, room]);
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
const leaveRoom = async () => {
|
|
258
|
+
await queue.push(client.leave(room).catch((error) => {
|
|
259
|
+
console.error(error);
|
|
260
|
+
}));
|
|
261
|
+
};
|
|
262
|
+
if (!connect) {
|
|
263
|
+
leaveRoom();
|
|
264
|
+
return () => {};
|
|
265
|
+
}
|
|
266
|
+
if (!resolvedMeta.isInitialized) {
|
|
267
|
+
leaveRoom();
|
|
268
|
+
return () => {};
|
|
269
|
+
}
|
|
270
|
+
const resolved = resolvedMeta.value;
|
|
271
|
+
queue.push(client.enter(room, ...[{ metadata: resolved }]).catch(async (error) => {
|
|
272
|
+
console.error(error);
|
|
273
|
+
await leaveRoom();
|
|
274
|
+
}));
|
|
275
|
+
return () => {
|
|
276
|
+
leaveRoom();
|
|
277
|
+
};
|
|
278
|
+
}, [
|
|
279
|
+
connect,
|
|
280
|
+
queue,
|
|
281
|
+
resolvedMeta.isInitialized,
|
|
282
|
+
resolvedMeta.value,
|
|
283
|
+
room
|
|
284
|
+
]);
|
|
285
|
+
return /* @__PURE__ */ jsx(PluvRoomContext.Provider, {
|
|
286
|
+
value: mockedRoom ?? room,
|
|
287
|
+
children
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
PluvRoomProvider.displayName = "PluvRoomProvider";
|
|
291
|
+
const PluvProvider = memo((props) => {
|
|
292
|
+
const { children } = props;
|
|
293
|
+
return /* @__PURE__ */ jsx(PluvContext.Provider, {
|
|
294
|
+
value: client,
|
|
295
|
+
children
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
PluvProvider.displayName = "PluvProvider";
|
|
299
|
+
const useClient = () => useContext(PluvContext);
|
|
300
|
+
const useRoom = () => {
|
|
301
|
+
const room = useContext(PluvRoomContext);
|
|
302
|
+
if (!room) throw new Error("Room could not be found. Component must be wrapped with PluvRoomProvider");
|
|
303
|
+
return room;
|
|
304
|
+
};
|
|
305
|
+
const throwIfStoragePending = (room) => {
|
|
306
|
+
if (!options.suspense) return;
|
|
307
|
+
if (room.getStorageLoaded()) return;
|
|
308
|
+
throw getStorageReady(room);
|
|
309
|
+
};
|
|
310
|
+
const useBroadcast = () => {
|
|
311
|
+
return useRoom().broadcast;
|
|
312
|
+
};
|
|
313
|
+
const usePresence = () => {
|
|
314
|
+
return useRoom().presence;
|
|
315
|
+
};
|
|
316
|
+
const useCanRedo = () => {
|
|
317
|
+
const room = useRoom();
|
|
318
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.storage(onStoreChange), [room]);
|
|
319
|
+
const getSnapshot = useCallback(() => room.canRedo(), [room]);
|
|
320
|
+
const canRedo = useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, identity);
|
|
321
|
+
throwIfStoragePending(room);
|
|
322
|
+
return canRedo;
|
|
323
|
+
};
|
|
324
|
+
const useCanUndo = () => {
|
|
325
|
+
const room = useRoom();
|
|
326
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.storage(onStoreChange), [room]);
|
|
327
|
+
const getSnapshot = useCallback(() => room.canUndo(), [room]);
|
|
328
|
+
const canUndo = useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, identity);
|
|
329
|
+
throwIfStoragePending(room);
|
|
330
|
+
return canUndo;
|
|
331
|
+
};
|
|
332
|
+
const useConnection = (selector = identity, hookOptions) => {
|
|
333
|
+
const room = useRoom();
|
|
334
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.connection(onStoreChange), [room]);
|
|
335
|
+
const getSnapshot = room.getConnection.bind(room);
|
|
336
|
+
const _selector = useCallback((snapshot) => selector(snapshot), [selector]);
|
|
337
|
+
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, _selector, hookOptions?.isEqual ?? fastDeepEqual);
|
|
338
|
+
};
|
|
339
|
+
const useDoc = () => {
|
|
340
|
+
const room = useRoom();
|
|
341
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
342
|
+
const unsubscribes = [room.subscribe.storageLoaded(onStoreChange), room.subscribe.connection(onStoreChange)];
|
|
343
|
+
return () => {
|
|
344
|
+
unsubscribes.forEach((unsubscribe) => {
|
|
345
|
+
unsubscribe();
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
}, [room]);
|
|
349
|
+
const getSnapshot = room.getDoc.bind(room);
|
|
350
|
+
const doc = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
351
|
+
throwIfStoragePending(room);
|
|
352
|
+
return doc;
|
|
353
|
+
};
|
|
354
|
+
const useEvent = (type, callback) => {
|
|
355
|
+
const room = useRoom();
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
const unsubscribe = room.subscribe.event(type, callback);
|
|
358
|
+
return () => {
|
|
359
|
+
unsubscribe();
|
|
360
|
+
};
|
|
361
|
+
}, [
|
|
362
|
+
callback,
|
|
363
|
+
room,
|
|
364
|
+
type
|
|
365
|
+
]);
|
|
366
|
+
};
|
|
367
|
+
const event = new Proxy({}, { get(_, prop) {
|
|
368
|
+
const useProxyEvent = (callback) => {
|
|
369
|
+
return useEvent(prop, callback);
|
|
370
|
+
};
|
|
371
|
+
return { useEvent: useProxyEvent };
|
|
372
|
+
} });
|
|
373
|
+
const useMyPresence = (selector = identity, hookOptions) => {
|
|
374
|
+
const room = useRoom();
|
|
375
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.myPresence(onStoreChange), [room]);
|
|
376
|
+
const getSnapshot = room.getMyPresence.bind(room);
|
|
377
|
+
const _selector = useCallback((snapshot) => selector(snapshot), [selector]);
|
|
378
|
+
return [useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, _selector, hookOptions?.isEqual ?? fastDeepEqual), room.updateMyPresence.bind(room)];
|
|
379
|
+
};
|
|
380
|
+
const useMyself = (selector = identity, hookOptions) => {
|
|
381
|
+
const room = useRoom();
|
|
382
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.myself(onStoreChange), [room]);
|
|
383
|
+
const getSnapshot = room.getMyself.bind(room);
|
|
384
|
+
const _selector = useCallback((snapshot) => {
|
|
385
|
+
return !snapshot ? null : selector(snapshot);
|
|
386
|
+
}, [selector]);
|
|
387
|
+
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, _selector, hookOptions?.isEqual ?? fastDeepEqual);
|
|
388
|
+
};
|
|
389
|
+
const useOther = (userId, selector = identity, hookOptions) => {
|
|
390
|
+
const room = useRoom();
|
|
391
|
+
const kind = hookOptions?.kind;
|
|
392
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
393
|
+
return room.subscribe.other(userId, onStoreChange, kind === void 0 ? void 0 : { kind });
|
|
394
|
+
}, [
|
|
395
|
+
kind,
|
|
396
|
+
room,
|
|
397
|
+
userId
|
|
398
|
+
]);
|
|
399
|
+
const getSnapshot = useCallback(() => room.getOther(userId, kind === void 0 ? void 0 : { kind }), [
|
|
400
|
+
kind,
|
|
401
|
+
room,
|
|
402
|
+
userId
|
|
403
|
+
]);
|
|
404
|
+
const _selector = useCallback((snapshot) => {
|
|
405
|
+
return !snapshot ? null : selector(snapshot);
|
|
406
|
+
}, [selector]);
|
|
407
|
+
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, _selector, hookOptions?.isEqual ?? fastDeepEqual);
|
|
408
|
+
};
|
|
409
|
+
const useOthers = (selector = identity, hookOptions) => {
|
|
410
|
+
const room = useRoom();
|
|
411
|
+
const kindsKey = participantKindsKey.toKey(hookOptions?.kinds);
|
|
412
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
413
|
+
return room.subscribe.others(onStoreChange, participantKindsKey.fromKey(kindsKey));
|
|
414
|
+
}, [kindsKey, room]);
|
|
415
|
+
const getSnapshot = useCallback(() => room.getOthers(participantKindsKey.fromKey(kindsKey)), [kindsKey, room]);
|
|
416
|
+
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, hookOptions?.isEqual ?? ((a, b) => {
|
|
417
|
+
/**
|
|
418
|
+
* !HACK
|
|
419
|
+
* @description Assume the return type will always be an array when this occurs
|
|
420
|
+
* and do a shallow comparison instead like how standard React dependency
|
|
421
|
+
* arrays work. This is for performance's sake and may lead to bugs if the
|
|
422
|
+
* selector doesn't always return an array
|
|
423
|
+
* @date June 22, 2024
|
|
424
|
+
*/
|
|
425
|
+
if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) return shallowArrayEqual(a, b);
|
|
426
|
+
return fastDeepEqual(a, b);
|
|
427
|
+
}));
|
|
428
|
+
};
|
|
429
|
+
const useRedo = () => {
|
|
430
|
+
const room = useRoom();
|
|
431
|
+
throwIfStoragePending(room);
|
|
432
|
+
return room.redo.bind(room);
|
|
433
|
+
};
|
|
434
|
+
const useRoomError = (callback) => {
|
|
435
|
+
const room = useRoom();
|
|
436
|
+
useEffect(() => {
|
|
437
|
+
const unsubscribe = room.subscribe.error(callback);
|
|
438
|
+
return () => {
|
|
439
|
+
unsubscribe();
|
|
440
|
+
};
|
|
441
|
+
}, [callback, room]);
|
|
442
|
+
};
|
|
443
|
+
const useRoomStats = (selector = identity, hookOptions) => {
|
|
444
|
+
const room = useRoom();
|
|
445
|
+
const kindsKey = participantKindsKey.toKey(hookOptions?.kinds);
|
|
446
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.roomStats(onStoreChange), [room]);
|
|
447
|
+
const getSnapshot = useCallback(() => room.getRoomStats(participantKindsKey.fromKey(kindsKey)), [kindsKey, room]);
|
|
448
|
+
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, hookOptions?.isEqual ?? fastDeepEqual);
|
|
449
|
+
};
|
|
450
|
+
const useStorage = () => {
|
|
451
|
+
const room = useRoom();
|
|
452
|
+
throwIfStoragePending(room);
|
|
453
|
+
return room.storage;
|
|
454
|
+
};
|
|
455
|
+
const useStorageField = (key, selector = identity, hookOptions) => {
|
|
456
|
+
const room = useRoom();
|
|
457
|
+
const rerender = useRerender();
|
|
458
|
+
useEffect(() => {
|
|
459
|
+
let storageState = null;
|
|
460
|
+
const unsubscribes = [room.subscribe.storageLoaded(() => {
|
|
461
|
+
rerender();
|
|
462
|
+
}), room.subscribe.connection(({ storage }) => {
|
|
463
|
+
if (storage.state === storageState) return;
|
|
464
|
+
storageState = storage.state;
|
|
465
|
+
rerender();
|
|
466
|
+
})];
|
|
467
|
+
return () => {
|
|
468
|
+
unsubscribes.forEach((unsubscribe) => {
|
|
469
|
+
unsubscribe();
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
}, [rerender, room]);
|
|
473
|
+
const subscribe = useCallback((onStoreChange) => room.subscribe.storage(key, onStoreChange), [key, room]);
|
|
474
|
+
const getSnapshot = useCallback(() => {
|
|
475
|
+
return room.getStorageJson(key);
|
|
476
|
+
}, [key, room]);
|
|
477
|
+
const _selector = useCallback((snapshot) => {
|
|
478
|
+
return snapshot === null ? null : selector(snapshot);
|
|
479
|
+
}, [selector]);
|
|
480
|
+
const data = useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, _selector, hookOptions?.isEqual ?? fastDeepEqual);
|
|
481
|
+
const sharedType = room.getStorage(key) ?? null;
|
|
482
|
+
throwIfStoragePending(room);
|
|
483
|
+
if (data === null || sharedType === null) {
|
|
484
|
+
if (options.suspense) throw new Error("Storage is loaded but the requested field is missing");
|
|
485
|
+
return [null, null];
|
|
486
|
+
}
|
|
487
|
+
return [data, sharedType];
|
|
488
|
+
};
|
|
489
|
+
const useTransact = () => {
|
|
490
|
+
const room = useRoom();
|
|
491
|
+
throwIfStoragePending(room);
|
|
492
|
+
return room.transact.bind(room);
|
|
493
|
+
};
|
|
494
|
+
const useUndo = () => {
|
|
495
|
+
const room = useRoom();
|
|
496
|
+
throwIfStoragePending(room);
|
|
497
|
+
return room.undo.bind(room);
|
|
498
|
+
};
|
|
499
|
+
return {
|
|
500
|
+
MockedRoomProvider,
|
|
501
|
+
PluvProvider,
|
|
502
|
+
PluvRoomProvider,
|
|
503
|
+
event,
|
|
504
|
+
useBroadcast,
|
|
505
|
+
useCanRedo,
|
|
506
|
+
useCanUndo,
|
|
507
|
+
useClient,
|
|
508
|
+
useConnection,
|
|
509
|
+
useDoc,
|
|
510
|
+
useEvent,
|
|
511
|
+
useMyPresence,
|
|
512
|
+
useMyself,
|
|
513
|
+
useOther,
|
|
514
|
+
useOthers,
|
|
515
|
+
usePresence,
|
|
516
|
+
useRedo,
|
|
517
|
+
useRoom,
|
|
518
|
+
useRoomError,
|
|
519
|
+
useRoomStats,
|
|
520
|
+
useStorage,
|
|
521
|
+
useStorageField,
|
|
522
|
+
useTransact,
|
|
523
|
+
useUndo
|
|
524
|
+
};
|
|
525
|
+
};
|
|
526
|
+
export { createBundle };
|
|
527
|
+
|
|
528
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/internal/hooks/useAsyncQueue.ts","../src/internal/hooks/useDeepAsyncMemo.ts","../src/internal/hooks/useNoSsr.ts","../src/internal/hooks/useRerender.ts","../src/internal/utils/getStorageReady.ts","../src/internal/utils/identity.ts","../src/internal/utils/participantKindsKey.ts","../src/internal/utils/shallowArrayEqual.ts","../src/internal/utils/shallowEqual.ts","../src/internal/hooks/useSyncExternalStoreWithSelector.ts","../src/createBundle.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef } from \"react\";\n\n/**\n * @description This is to handle race conditions, so that our async operations that can\n * happen between re-renders will happen sequentially.\n * @date April 13, 2025\n */\nexport const useAsyncQueue = () => {\n const queueRef = useRef<Promise<void>>(Promise.resolve(undefined));\n\n const push = useCallback(async (promise: Promise<any>) => {\n queueRef.current = queueRef.current.then(async () => await promise);\n\n return await queueRef.current;\n }, []);\n\n return useMemo(() => ({ push }), [push]);\n};\n","import { useEffect, useRef, useState } from \"react\";\nimport fastDeepEqual from \"fast-deep-equal\";\n\nexport type UseDeepAsyncMemoResult<T extends unknown> =\n | {\n error: null;\n isInitialized: true;\n isLoading: false;\n value: T;\n }\n | {\n error: null;\n isInitialized: boolean;\n value: undefined;\n isLoading: true;\n }\n | {\n error: Error;\n isInitialized: boolean;\n value: undefined;\n isLoading: false;\n };\n\nexport const useDeepAsyncMemo = <T>(\n factory: () => Promise<T>,\n isEqual: (a: T, b: T) => boolean = fastDeepEqual,\n): UseDeepAsyncMemoResult<T> => {\n // Store the current async state (loading, value, error, etc.)\n const [state, setState] = useState<UseDeepAsyncMemoResult<T>>({\n isInitialized: false,\n isLoading: true,\n value: undefined,\n error: null,\n });\n\n // Keep track of the last resolved value to compare with new ones\n const lastValueRef = useRef<T | undefined>(undefined);\n const hasValueRef = useRef(false); // Tracks whether we've ever resolved a value\n\n // Tracks the current in-flight promise to guard against race conditions\n const inflightRef = useRef<Promise<T> | null>(null);\n\n // Used to prevent state updates after the component unmounts\n const isMountedRef = useRef(true);\n\n useEffect(() => {\n isMountedRef.current = true;\n\n // Trigger the async factory\n const promise = factory();\n inflightRef.current = promise;\n\n // Set loading state\n setState((prev) => {\n return {\n ...prev,\n isLoading: true,\n error: null,\n } as UseDeepAsyncMemoResult<T>;\n });\n\n promise\n .then((result) => {\n // Ignore if component unmounted or if this promise is stale\n if (!isMountedRef.current || inflightRef.current !== promise) return;\n\n // Skip update if value hasn't changed\n const isSame = hasValueRef.current && isEqual(lastValueRef.current as T, result);\n if (isSame) {\n setState((prev) => {\n return {\n ...prev,\n isInitialized: true,\n isLoading: false,\n } as UseDeepAsyncMemoResult<T>;\n });\n return;\n }\n\n // Save the new value\n lastValueRef.current = result;\n hasValueRef.current = true;\n\n setState({\n isInitialized: true,\n isLoading: false,\n value: result,\n error: null,\n });\n })\n .catch((err) => {\n // Ignore if component unmounted or if this promise is stale\n if (!isMountedRef.current || inflightRef.current !== promise) return;\n\n setState({\n isInitialized: hasValueRef.current,\n isLoading: false,\n value: undefined,\n error: err instanceof Error ? err : new Error(\"Unknown error\"),\n });\n });\n\n // Cleanup: prevent state updates after unmount\n return () => {\n isMountedRef.current = false;\n };\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [\n /**\n * !HACK\n * @description We're only going to use the `isEqual` function to determine if this hook\n * should re-run. This is basically our hook's version of a dependency array\n * @date April 13, 2025\n */\n // factory,\n isEqual,\n ]);\n\n return state;\n};\n","import { useCallback, useEffect, useState } from \"react\";\n\nexport type NoSsrFunction = <T = null>(value: T | (() => T), fallback?: T) => T;\n\nexport const useNoSsr = (): NoSsrFunction => {\n const [didMount, setDidMount] = useState<boolean>(false);\n\n // oxlint-disable-next-line react-hooks/set-state-in-effect\n useEffect(() => setDidMount(typeof window !== \"undefined\"), []);\n\n const noSsr = useCallback(\n <T = null>(value: T | (() => T), fallback: T = null as T): T => {\n return didMount\n ? typeof value !== \"function\"\n ? value\n : (value as () => T)()\n : fallback;\n },\n [didMount],\n );\n\n return noSsr;\n};\n","import { useCallback, useState } from \"react\";\n\nexport const useRerender = (): (() => void) => {\n const [, setCount] = useState(0);\n\n return useCallback(() => {\n setCount((oldCount) => (oldCount + 1) % Number.MAX_SAFE_INTEGER);\n }, []);\n};\n","import type { SubscribeFn, WebSocketConnection } from \"@pluv/types\";\nimport { ConnectionState } from \"@pluv/types\";\n\ntype StorageReadyRoom = {\n getConnection: () => WebSocketConnection;\n getStorageLoaded: () => boolean;\n subscribe: {\n connection: SubscribeFn<{ connection: WebSocketConnection }>;\n };\n};\n\nconst cache = new WeakMap<object, Promise<void>>();\n\nconst isFailedConnection = (state: ConnectionState): boolean => {\n return state === ConnectionState.Closed || state === ConnectionState.Unavailable;\n};\n\nexport const getStorageReady = (room: StorageReadyRoom): Promise<void> => {\n if (room.getStorageLoaded()) return Promise.resolve();\n\n const cached = cache.get(room);\n\n if (cached) return cached;\n\n let settled = false;\n let unsubscribe: (() => void) | null = null;\n\n const promise = new Promise<void>((resolve, reject) => {\n const settle = (outcome: () => void): void => {\n if (settled) return;\n\n settled = true;\n unsubscribe?.();\n cache.delete(room);\n outcome();\n };\n\n const check = (connectionState: ConnectionState): void => {\n if (room.getStorageLoaded()) {\n settle(resolve);\n return;\n }\n\n if (isFailedConnection(connectionState)) {\n settle(() => {\n reject(\n new Error(\n \"Room closed or storage became unavailable before storage loaded\",\n ),\n );\n });\n }\n };\n\n unsubscribe = room.subscribe.connection((state) => {\n check(state.connection.state);\n });\n\n check(room.getConnection().state);\n });\n\n if (!settled) cache.set(room, promise);\n\n return promise;\n};\n","export const identity = <T extends unknown>(x: T): T => x;\n","import type { ParticipantKind, ParticipantKindsOptions } from \"@pluv/types\";\n\nexport const participantKindsKey = {\n toKey(kinds: readonly ParticipantKind[] | undefined): string {\n if (kinds === undefined) return \"\";\n\n return `\\0${kinds.join(\"\\0\")}`;\n },\n fromKey(kindsKey: string): ParticipantKindsOptions | undefined {\n if (kindsKey === \"\") return undefined;\n\n const joined = kindsKey.slice(1);\n\n return { kinds: joined === \"\" ? [] : (joined.split(\"\\0\") as ParticipantKind[]) };\n },\n};\n","export const shallowArrayEqual = <T extends unknown>(\n a: readonly T[] | T[],\n b: readonly T[] | T[],\n): boolean => {\n return a.length === b.length && a.every((item, i) => item === b[i]);\n};\n","export const shallowEqual = (a: any, b: any): boolean => a === b;\n","import { useRef, useSyncExternalStore } from \"react\";\nimport { shallowEqual } from \"../utils\";\nimport { useNoSsr } from \"./useNoSsr\";\n\nexport const useSyncExternalStoreWithSelector = <TSnapshot, TSelection>(\n subscribe: (onStoreChange: () => void) => () => void,\n getClientSnapshot: () => TSnapshot,\n getServerSnapshot: undefined | null | (() => TSnapshot),\n selector: (snapshot: TSnapshot) => TSelection,\n isEqual?: (a: TSelection, b: TSelection) => boolean,\n): TSelection => {\n const noSsr = useNoSsr();\n const cacheRef = useRef<TSelection | null>(null);\n\n const _getServerSnapshot = getServerSnapshot\n ? () => {\n const data = getServerSnapshot();\n const snapshot = selector(data);\n const compare = isEqual ?? shallowEqual;\n const cached = cacheRef.current;\n\n const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);\n\n /**\n * !HACK\n * @description Memoize only the last snapshot so that we can only rerender on changes\n * @date June 22, 2024\n */\n cacheRef.current = isUnchanged ? (cached ?? snapshot) : snapshot;\n\n return cacheRef.current;\n }\n : undefined;\n\n const _getClientSnapshot = () => {\n const data = getClientSnapshot();\n const snapshot = selector(data);\n const compare = isEqual ?? shallowEqual;\n const cached = cacheRef.current;\n\n const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);\n\n /**\n * !HACK\n * @description Memoize only the last snapshot so that we can only rerender on changes\n * @date June 22, 2024\n */\n cacheRef.current = isUnchanged ? (cached ?? snapshot) : snapshot;\n\n return cacheRef.current;\n };\n\n const result = useSyncExternalStore(subscribe, _getClientSnapshot, _getServerSnapshot);\n\n // oxlint-disable-next-line react-hooks/refs\n return typeof window !== \"undefined\" ? noSsr(result, _getServerSnapshot?.()) : result;\n};\n","import type {\n ClientDefs,\n CreateRoomOptions,\n EnterRoomParams,\n InferClientMetadata,\n InferClientOutput,\n InferClientPresence,\n InferSchemaInput,\n PluvClient,\n PluvRoom,\n PluvRoomAddon,\n UserInfo,\n WebSocketConnection,\n} from \"@pluv/client\";\nimport { MockedRoom, parsePluvSchema } from \"@pluv/client\";\nimport type { InferDoc, InferJson, InferStorage } from \"@pluv/crdt\";\nimport type {\n BroadcastProxy,\n Id,\n ParticipantKindOptions,\n ParticipantKindsOptions,\n PresenceProcedureProxy,\n PublicEventKey,\n RoomError,\n RoomLike,\n RoomStats,\n StorageProcedureProxy,\n StorageState,\n UpdateMyPresenceAction,\n} from \"@pluv/types\";\nimport fastDeepEqual from \"fast-deep-equal\";\nimport type { Dispatch } from \"react\";\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n getStorageReady,\n identity,\n participantKindsKey,\n shallowArrayEqual,\n useAsyncQueue,\n useDeepAsyncMemo,\n useRerender,\n useSyncExternalStoreWithSelector,\n} from \"./internal\";\nimport type {\n CreateBundle,\n EventProxy,\n MockedRoomProviderProps,\n PluvProviderProps,\n PluvRoomProviderProps,\n SubscriptionHookOptions,\n UseStorageResult,\n} from \"./types\";\n\nexport type CreateBundleOptions<TSuspense extends boolean = false> = {\n addons?: readonly PluvRoomAddon<any>[];\n suspense?: TSuspense;\n};\n\nexport const createBundle = <TDefs extends ClientDefs, const TSuspense extends boolean = false>(\n client: PluvClient<TDefs>,\n options: CreateBundleOptions<TSuspense> = {} as CreateBundleOptions<TSuspense>,\n): CreateBundle<TDefs, TSuspense> => {\n type TRoom = TDefs;\n type TPresence = InferClientPresence<TRoom>;\n type TMetadata = InferClientMetadata<TRoom>;\n /**\n * !HACK\n * @description We'll let the context error out if client is not provided,\n * and let the users deal with it.\n * @date October 27, 2022\n */\n const PluvContext = createContext<PluvClient<TDefs>>(null as any);\n\n /**\n * !HACK\n * @description We'll let the context error out if the room is not provided,\n * and let the users deal with it.\n * @date November 11, 2022\n */\n const PluvRoomContext = createContext<\n RoomLike<\n TDefs[\"io\"],\n InferDoc<TDefs[\"storage\"]>,\n TPresence,\n InferStorage<TDefs[\"storage\"]>,\n InferJson<TDefs[\"storage\"]>,\n TDefs[\"treaty\"]\n >\n >(null as any);\n\n const MockedRoomContext = createContext<RoomLike<\n TDefs[\"io\"],\n InferDoc<TDefs[\"storage\"]>,\n TPresence,\n InferStorage<TDefs[\"storage\"]>,\n InferJson<TDefs[\"storage\"]>,\n TDefs[\"treaty\"]\n > | null>(null);\n\n const MockedRoomProvider = memo<MockedRoomProviderProps<TRoom>>((props) => {\n const { children, events, initialPresence, initialStorage, room: _room } = props;\n\n const [room] = useState<MockedRoom<TRoom>>(() => {\n return new MockedRoom<TRoom>(_room, {\n events,\n initialPresence,\n initialStorage,\n presence: client._defs.treaty.presence,\n storage: client._defs.storage as TDefs[\"storage\"] | undefined,\n treaty: client._defs.treaty,\n });\n });\n\n return (\n <MockedRoomContext.Provider value={room}>\n <PluvRoomContext.Provider value={room}>{children}</PluvRoomContext.Provider>\n </MockedRoomContext.Provider>\n );\n });\n\n MockedRoomProvider.displayName = \"MockedRoomProvider\";\n\n const PluvRoomProvider = memo<PluvRoomProviderProps<TRoom>>((props) => {\n const {\n children,\n connect = true,\n debug,\n initialPresence,\n initialStorage,\n metadata,\n onAuthorizationFail,\n room: _room,\n } = props;\n\n const queue = useAsyncQueue();\n const rerender = useRerender();\n const mockedRoom = useContext(MockedRoomContext);\n\n const createRoom = useCallback((): PluvRoom<TRoom> => {\n return client.createRoom(_room, {\n addons: options.addons,\n debug,\n initialPresence,\n initialStorage,\n metadata,\n onAuthorizationFail,\n } as CreateRoomOptions<TRoom>);\n }, [_room, debug, initialPresence, initialStorage, metadata, onAuthorizationFail]);\n\n const [room, setRoom] = useState(() => createRoom());\n\n if (room.id !== _room) setRoom(createRoom());\n\n const resolvedMeta = useDeepAsyncMemo(async () => {\n const resolved = await Promise.resolve(\n typeof metadata === \"function\"\n ? (\n metadata as () =>\n | InferSchemaInput<TDefs[\"metadata\"]>\n | Promise<InferSchemaInput<TDefs[\"metadata\"]>>\n )()\n : metadata,\n );\n\n return room.metadata ? parsePluvSchema(room.metadata, resolved) : resolved;\n });\n\n useEffect(() => {\n const unsubscribe = room.subscribe.connection(() => {\n rerender();\n });\n\n return () => {\n unsubscribe();\n };\n }, [rerender, room]);\n\n useEffect(() => {\n const leaveRoom = async (): Promise<void> => {\n await queue.push(\n client.leave(room).catch((error) => {\n console.error(error);\n }),\n );\n };\n\n if (!connect) {\n void leaveRoom();\n return () => {};\n }\n\n if (!resolvedMeta.isInitialized) {\n void leaveRoom();\n return () => {};\n }\n\n const resolved = resolvedMeta.value as TMetadata;\n\n void queue.push(\n client\n .enter(\n room,\n ...([{ metadata: resolved }] as unknown as EnterRoomParams<\n InferSchemaInput<TDefs[\"metadata\"]>\n >),\n )\n .catch(async (error) => {\n console.error(error);\n await leaveRoom();\n }),\n );\n\n return () => {\n void leaveRoom();\n };\n }, [connect, queue, resolvedMeta.isInitialized, resolvedMeta.value, room]);\n\n return (\n <PluvRoomContext.Provider value={mockedRoom ?? room}>\n {children}\n </PluvRoomContext.Provider>\n );\n });\n\n PluvRoomProvider.displayName = \"PluvRoomProvider\";\n\n const PluvProvider = memo<PluvProviderProps>((props) => {\n const { children } = props;\n\n return <PluvContext.Provider value={client}>{children}</PluvContext.Provider>;\n });\n\n PluvProvider.displayName = \"PluvProvider\";\n\n const useClient = (): PluvClient<TDefs> => useContext(PluvContext);\n\n const useRoom = () => {\n const room = useContext(PluvRoomContext);\n\n if (!room)\n throw new Error(\n \"Room could not be found. Component must be wrapped with PluvRoomProvider\",\n );\n\n return room;\n };\n\n const throwIfStoragePending = (\n room: RoomLike<\n TDefs[\"io\"],\n InferDoc<TDefs[\"storage\"]>,\n TPresence,\n InferStorage<TDefs[\"storage\"]>,\n InferJson<TDefs[\"storage\"]>,\n TDefs[\"treaty\"]\n >,\n ): void => {\n if (!options.suspense) return;\n if (room.getStorageLoaded()) return;\n\n throw getStorageReady(room);\n };\n\n const useBroadcast = (): BroadcastProxy<TDefs[\"io\"]> => {\n const room = useRoom();\n\n return room.broadcast;\n };\n\n const usePresence = (): PresenceProcedureProxy<\n TDefs[\"treaty\"][\"_defs\"][\"procedures\"][\"presence\"]\n > => {\n const room = useRoom();\n\n return room.presence;\n };\n\n const useCanRedo = (): boolean => {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.storage(onStoreChange),\n [room],\n );\n\n const getSnapshot = useCallback((): boolean => room.canRedo(), [room]);\n\n const canRedo = useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n identity,\n );\n\n throwIfStoragePending(room);\n\n return canRedo;\n };\n\n const useCanUndo = (): boolean => {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.storage(onStoreChange),\n [room],\n );\n\n const getSnapshot = useCallback((): boolean => room.canUndo(), [room]);\n\n const canUndo = useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n identity,\n );\n\n throwIfStoragePending(room);\n\n return canUndo;\n };\n\n const useConnection = <TValue extends unknown = WebSocketConnection>(\n selector = identity as (connection: WebSocketConnection) => TValue,\n hookOptions?: SubscriptionHookOptions<Id<TValue>>,\n ): Id<TValue> => {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.connection(onStoreChange),\n [room],\n );\n\n const getSnapshot = room.getConnection.bind(room);\n\n const _selector = useCallback(\n (snapshot: WebSocketConnection) => selector(snapshot) as Id<TValue>,\n [selector],\n );\n\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n _selector,\n hookOptions?.isEqual ?? fastDeepEqual,\n );\n };\n\n const useDoc = () => {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n // Storage teardown only surfaces on the connection state, so watch both.\n const unsubscribes = [\n room.subscribe.storageLoaded(onStoreChange),\n room.subscribe.connection(onStoreChange),\n ];\n\n return () => {\n unsubscribes.forEach((unsubscribe) => {\n unsubscribe();\n });\n };\n },\n [room],\n );\n\n const getSnapshot = room.getDoc.bind(room);\n\n const doc = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n throwIfStoragePending(room);\n\n return doc;\n };\n\n const useEvent = <TType extends PublicEventKey<InferClientOutput<TRoom>>>(\n type: TType,\n callback: Parameters<EventProxy<TRoom>[TType][\"useEvent\"]>[0],\n ): void => {\n const room = useRoom();\n\n useEffect(() => {\n const unsubscribe = room.subscribe.event(type, callback);\n\n return () => {\n unsubscribe();\n };\n }, [callback, room, type]);\n };\n\n const event = new Proxy(\n {},\n {\n get(_, prop) {\n const useProxyEvent = (\n callback: Parameters<\n EventProxy<TRoom>[PublicEventKey<InferClientOutput<TRoom>>][\"useEvent\"]\n >[0],\n ): void => {\n return useEvent(prop as PublicEventKey<InferClientOutput<TRoom>>, callback);\n };\n\n return { useEvent: useProxyEvent };\n },\n },\n ) as EventProxy<TRoom>;\n\n const useMyPresence = <TValue extends unknown = TPresence>(\n selector = identity as (myPresence: TPresence) => TValue,\n hookOptions?: SubscriptionHookOptions<Id<TValue> | null>,\n ): [Id<TValue>, Dispatch<UpdateMyPresenceAction<TPresence>>] => {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.myPresence(onStoreChange),\n [room],\n );\n\n const getSnapshot = room.getMyPresence.bind(room);\n\n const _selector = useCallback(\n (snapshot: TPresence) => selector(snapshot) as Id<TValue>,\n [selector],\n );\n\n const myPresence = useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n _selector,\n hookOptions?.isEqual ?? fastDeepEqual,\n );\n\n return [myPresence, room.updateMyPresence.bind(room)];\n };\n\n const useMyself = <TValue extends unknown = UserInfo<TDefs[\"io\"], TPresence>>(\n selector = identity as (myself: Id<UserInfo<TDefs[\"io\"], TPresence>>) => TValue,\n hookOptions?: SubscriptionHookOptions<Id<TValue> | null>,\n ): Id<TValue> | null => {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.myself(onStoreChange),\n [room],\n );\n\n const getSnapshot = room.getMyself.bind(room);\n\n const _selector = useCallback(\n (snapshot: Id<UserInfo<TDefs[\"io\"], TPresence>> | null) => {\n return !snapshot ? null : (selector(snapshot) as Id<TValue>);\n },\n [selector],\n );\n\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n _selector,\n hookOptions?.isEqual ?? fastDeepEqual,\n );\n };\n\n const useOther = <TValue extends unknown = UserInfo<TDefs[\"io\"], TPresence>>(\n userId: string,\n selector = identity as (other: UserInfo<TDefs[\"io\"], TPresence>) => TValue,\n hookOptions?: SubscriptionHookOptions<TValue | null> & ParticipantKindOptions,\n ): TValue | null => {\n const room = useRoom();\n const kind = hookOptions?.kind;\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return room.subscribe.other(\n userId,\n onStoreChange,\n kind === undefined ? undefined : { kind },\n );\n },\n [kind, room, userId],\n );\n\n const getSnapshot = useCallback(\n () => room.getOther(userId, kind === undefined ? undefined : { kind }),\n [kind, room, userId],\n );\n\n const _selector = useCallback(\n (snapshot: Id<UserInfo<TDefs[\"io\"], TPresence>> | null) => {\n return !snapshot ? null : (selector(snapshot) as TValue);\n },\n [selector],\n );\n\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n _selector,\n hookOptions?.isEqual ?? fastDeepEqual,\n );\n };\n\n const useOthers = <TValue extends unknown = readonly UserInfo<TDefs[\"io\"], TPresence>[]>(\n selector = identity as (other: readonly Id<UserInfo<TDefs[\"io\"], TPresence>>[]) => TValue,\n hookOptions?: SubscriptionHookOptions<TValue> & ParticipantKindsOptions,\n ): TValue => {\n const room = useRoom();\n const kindsKey = participantKindsKey.toKey(hookOptions?.kinds);\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return room.subscribe.others(onStoreChange, participantKindsKey.fromKey(kindsKey));\n },\n [kindsKey, room],\n );\n\n const getSnapshot = useCallback(\n () => room.getOthers(participantKindsKey.fromKey(kindsKey)),\n [kindsKey, room],\n );\n\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector as (other: readonly Id<UserInfo<TDefs[\"io\"], TPresence>>[]) => TValue,\n hookOptions?.isEqual ??\n ((a, b) => {\n /**\n * !HACK\n * @description Assume the return type will always be an array when this occurs\n * and do a shallow comparison instead like how standard React dependency\n * arrays work. This is for performance's sake and may lead to bugs if the\n * selector doesn't always return an array\n * @date June 22, 2024\n */\n if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {\n return shallowArrayEqual(a, b);\n }\n\n return fastDeepEqual(a, b);\n }),\n );\n };\n\n const useRedo = () => {\n const room = useRoom();\n\n throwIfStoragePending(room);\n\n return room.redo.bind(room);\n };\n\n const useRoomError = (callback: (error: RoomError) => void): void => {\n const room = useRoom();\n\n useEffect(() => {\n const unsubscribe = room.subscribe.error(callback);\n\n return () => {\n unsubscribe();\n };\n }, [callback, room]);\n };\n\n const useRoomStats = <TValue extends unknown = RoomStats>(\n selector = identity as (stats: RoomStats) => TValue,\n hookOptions?: SubscriptionHookOptions<TValue> & ParticipantKindsOptions,\n ): TValue => {\n const room = useRoom();\n const kindsKey = participantKindsKey.toKey(hookOptions?.kinds);\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.roomStats(onStoreChange),\n [room],\n );\n\n const getSnapshot = useCallback(\n () => room.getRoomStats(participantKindsKey.fromKey(kindsKey)),\n [kindsKey, room],\n );\n\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector as (stats: RoomStats) => TValue,\n hookOptions?.isEqual ?? fastDeepEqual,\n );\n };\n\n const useStorage = (): StorageProcedureProxy<\n TDefs[\"treaty\"][\"_defs\"][\"procedures\"][\"storage\"]\n > => {\n const room = useRoom();\n\n throwIfStoragePending(room);\n\n return room.storage;\n };\n\n const useStorageField = <\n TKey extends keyof InferJson<TDefs[\"storage\"]>,\n TData extends unknown = InferJson<TDefs[\"storage\"]>[TKey],\n >(\n key: TKey,\n selector = identity as (data: InferJson<TDefs[\"storage\"]>[TKey]) => TData,\n hookOptions?: SubscriptionHookOptions<TData | null>,\n ): UseStorageResult<TData, InferStorage<TDefs[\"storage\"]>[TKey]> => {\n const room = useRoom();\n const rerender = useRerender();\n\n useEffect(() => {\n let storageState: StorageState | null = null;\n\n const unsubscribes = [\n room.subscribe.storageLoaded(() => {\n rerender();\n }),\n // `storageLoaded` never fires on teardown, so watch the storage state too.\n room.subscribe.connection(({ storage }) => {\n if (storage.state === storageState) return;\n\n storageState = storage.state;\n\n rerender();\n }),\n ];\n\n return () => {\n unsubscribes.forEach((unsubscribe) => {\n unsubscribe();\n });\n };\n }, [rerender, room]);\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => room.subscribe.storage(key, onStoreChange),\n [key, room],\n );\n\n const getSnapshot = useCallback((): InferJson<TDefs[\"storage\"]>[TKey] | null => {\n return room.getStorageJson(key);\n }, [key, room]);\n\n const _selector = useCallback(\n (snapshot: InferJson<TDefs[\"storage\"]>[TKey] | null) => {\n return snapshot === null ? null : selector(snapshot);\n },\n [selector],\n );\n\n const data = useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n _selector,\n hookOptions?.isEqual ?? fastDeepEqual,\n );\n\n const sharedType = room.getStorage(key) ?? null;\n\n throwIfStoragePending(room);\n\n if (data === null || sharedType === null) {\n if (options.suspense) {\n throw new Error(\"Storage is loaded but the requested field is missing\");\n }\n\n return [null, null];\n }\n\n return [data, sharedType];\n };\n\n const useTransact = () => {\n const room = useRoom();\n\n throwIfStoragePending(room);\n\n return room.transact.bind(room);\n };\n\n const useUndo = () => {\n const room = useRoom();\n\n throwIfStoragePending(room);\n\n return room.undo.bind(room);\n };\n\n return {\n // components\n MockedRoomProvider,\n PluvProvider,\n PluvRoomProvider,\n\n // proxies\n event,\n\n // hooks\n useBroadcast,\n useCanRedo,\n useCanUndo,\n useClient,\n useConnection,\n useDoc,\n useEvent,\n useMyPresence,\n useMyself,\n useOther,\n useOthers,\n usePresence,\n useRedo,\n useRoom,\n useRoomError,\n useRoomStats,\n useStorage,\n useStorageField,\n useTransact,\n useUndo,\n } as CreateBundle<TRoom, TSuspense>;\n};\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pluv/react",
|
|
3
|
+
"version": "0.0.0-canary-20261027022227",
|
|
4
|
+
"description": "@pluv/client React.js bindings",
|
|
5
|
+
"author": {
|
|
6
|
+
"email": "david@pluv.io",
|
|
7
|
+
"name": "i3dly",
|
|
8
|
+
"url": "https://github.com/i3dly"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"homepage": "https://github.com/pluv-io/pluv",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/pluv-io/pluv.git",
|
|
15
|
+
"directory": "packages/react"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@pluv/client": "^0.0.0-canary-20261027022227",
|
|
27
|
+
"@pluv/crdt": "^0.0.0-canary-20261027022227",
|
|
28
|
+
"@pluv/types": "^0.0.0-canary-20261027022227",
|
|
29
|
+
"fast-deep-equal": "^3.1.3"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@types/react": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@pluv/tsconfig": "^0.0.0-canary-20261027022227",
|
|
37
|
+
"@types/react": "^19.3.0",
|
|
38
|
+
"react": "^19.3.1",
|
|
39
|
+
"tsdown": "^0.23.2",
|
|
40
|
+
"typescript": "7.1.0-beta"
|
|
41
|
+
},
|
|
42
|
+
"exports": {
|
|
43
|
+
".": "./dist/index.mjs",
|
|
44
|
+
"./*": "./*"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsdown",
|
|
48
|
+
"dev": "tsdown --watch",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
50
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
51
|
+
}
|
|
52
|
+
}
|