{"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/identity.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        // eslint-disable-next-line react-hooks/set-state-in-effect\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        // eslint-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    // eslint-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","export const identity = <T extends unknown>(x: T): T => x;\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    // eslint-disable-next-line react-hooks/refs\n    return typeof window !== \"undefined\" ? noSsr(result, _getServerSnapshot?.()) : result;\n};\n","import type {\n    CreateRoomOptions,\n    EnterRoomParams,\n    MergeEvents,\n    PluvClient,\n    PluvRoom,\n    PluvRoomAddon,\n    PluvRouter,\n    PluvRouterEventConfig,\n    UserInfo,\n    WebSocketConnection,\n} from \"@pluv/client\";\nimport { MockedRoom } from \"@pluv/client\";\nimport type { InferCrdtJson, InferDoc, InferStorage } from \"@pluv/crdt\";\nimport type {\n    Id,\n    InferIOCrdtKind,\n    InferIOInput,\n    InferIOOutput,\n    IOEventMessage,\n    IOLike,\n    JsonObject,\n    RoomLike,\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    useMemo,\n    useState,\n    useSyncExternalStore,\n} from \"react\";\nimport {\n    identity,\n    shallowArrayEqual,\n    useAsyncQueue,\n    useDeepAsyncMemo,\n    useRerender,\n    useSyncExternalStoreWithSelector,\n} from \"./internal\";\nimport type {\n    BroadcastProxy,\n    CreateBundle,\n    EventProxy,\n    MockedRoomProviderProps,\n    PluvProviderProps,\n    PluvRoomProviderProps,\n    SubscriptionHookOptions,\n} from \"./types\";\n\nexport type CreateBundleOptions<\n    TIO extends IOLike<any, any, any>,\n    TMetadata extends JsonObject = {},\n    TPresence extends Record<string, any> = {},\n    TCrdt extends InferIOCrdtKind<TIO> = InferIOCrdtKind<TIO>,\n    TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},\n> = {\n    addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];\n    router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;\n};\n\nexport const createBundle = <\n    TIO extends IOLike<any, any, any>,\n    TMetadata extends JsonObject = {},\n    TPresence extends Record<string, any> = {},\n    TCrdt extends InferIOCrdtKind<TIO> = InferIOCrdtKind<TIO>,\n    TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},\n>(\n    client: PluvClient<TIO, TPresence, TCrdt, TMetadata>,\n    options: CreateBundleOptions<TIO, TMetadata, TPresence, TCrdt, TEvents> = {},\n): CreateBundle<TIO, TMetadata, TPresence, TCrdt, TEvents> => {\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<TIO, TPresence, TCrdt, TMetadata>>(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<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>, TEvents>\n    >(null as any);\n\n    const MockedRoomContext = createContext<RoomLike<\n        TIO,\n        InferDoc<TCrdt>,\n        TPresence,\n        InferStorage<TCrdt>,\n        TEvents\n    > | null>(null);\n\n    const MockedRoomProvider = memo<MockedRoomProviderProps<TIO, TPresence, TCrdt>>((props) => {\n        const { children, events, initialPresence, initialStorage, room: _room } = props;\n\n        const [room] = useState<MockedRoom<TIO, TPresence, TCrdt, TEvents>>(() => {\n            return new MockedRoom<TIO, TPresence, TCrdt, TEvents>(_room, {\n                events,\n                initialPresence,\n                initialStorage:\n                    typeof initialStorage === \"function\"\n                        ? (client._defs.initialStorage?.getFactory(initialStorage) as TCrdt)\n                        : undefined,\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<TIO, TMetadata, TPresence, TCrdt>>(\n        (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<\n                TIO,\n                TMetadata,\n                TPresence,\n                TCrdt,\n                TEvents\n            > => {\n                return client.createRoom(_room, {\n                    addons: options.addons,\n                    debug,\n                    initialPresence,\n                    initialStorage:\n                        typeof initialStorage === \"function\"\n                            ? client._defs.initialStorage?.getFactory(initialStorage)\n                            : undefined,\n                    metadata,\n                    onAuthorizationFail,\n                    router: options.router,\n                } as CreateRoomOptions<TIO, TPresence, TCrdt, TMetadata, TEvents>);\n            }, [_room, debug, initialPresence, initialStorage, metadata, onAuthorizationFail]);\n\n            const [room, setRoom] = useState(() => createRoom());\n\n            useEffect(() => {\n                if (room.id === _room) return;\n\n                setRoom(createRoom());\n            }, [_room, createRoom, room]);\n\n            const resolvedMeta = useDeepAsyncMemo(async () => {\n                const resolved = await Promise.resolve(\n                    typeof metadata === \"function\" ? metadata() : metadata,\n                );\n\n                return !!room.metadata ? room.metadata.parse(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                    leaveRoom();\n                    return;\n                }\n\n                if (!resolvedMeta.isInitialized) {\n                    leaveRoom();\n                    return;\n                }\n\n                const resolved = resolvedMeta.value as TMetadata;\n\n                queue.push(\n                    client\n                        .enter(room, ...([{ metadata: resolved }] as EnterRoomParams<TMetadata>))\n                        .catch(async (error) => {\n                            console.error(error);\n                            await leaveRoom();\n                        }),\n                );\n\n                return () => {\n                    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\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<TIO, TPresence, TCrdt, TMetadata> => 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 useBroadcast = (): BroadcastProxy<MergeEvents<TEvents, TIO>> => {\n        const room = useRoom();\n\n        const broadcast = useCallback(\n            <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(\n                event: TEvent,\n                data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,\n            ) => {\n                room.broadcast(event, data);\n            },\n            [room],\n        );\n\n        return useMemo((): BroadcastProxy<MergeEvents<TEvents, TIO>> => {\n            return new Proxy(broadcast, {\n                get(fn, prop) {\n                    return (\n                        data: Id<\n                            InferIOInput<MergeEvents<TEvents, TIO>>[keyof InferIOInput<\n                                MergeEvents<TEvents, TIO>\n                            >]\n                        >,\n                    ): void => {\n                        return fn(prop as keyof InferIOInput<MergeEvents<TEvents, TIO>>, data);\n                    };\n                },\n            }) as BroadcastProxy<MergeEvents<TEvents, TIO>>;\n        }, [broadcast]);\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        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        return canUndo;\n    };\n\n    const useConnection = <T extends unknown = WebSocketConnection>(\n        selector = identity as (connection: WebSocketConnection) => T,\n        options?: SubscriptionHookOptions<Id<T>>,\n    ): Id<T> => {\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;\n\n        const _selector = useCallback(\n            (snapshot: WebSocketConnection) => selector(snapshot) as Id<T>,\n            [selector],\n        );\n\n        return useSyncExternalStoreWithSelector(\n            subscribe,\n            getSnapshot,\n            getSnapshot,\n            _selector,\n            options?.isEqual ?? fastDeepEqual,\n        );\n    };\n\n    const useDoc = () => {\n        const room = useRoom();\n\n        const subscribe = useCallback(\n            (onStoreChange: () => void) => room.subscribe.storageLoaded(onStoreChange),\n            [room],\n        );\n\n        const getSnapshot = room.getDoc;\n\n        return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n    };\n\n    const useEvent = <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(\n        type: TType,\n        callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,\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: (\n                        data: Id<\n                            IOEventMessage<\n                                MergeEvents<TEvents, TIO>,\n                                keyof InferIOOutput<MergeEvents<TEvents, TIO>>\n                            >\n                        >,\n                    ) => void,\n                ): void => {\n                    return useEvent(\n                        prop as keyof InferIOOutput<MergeEvents<TEvents, TIO>>,\n                        callback,\n                    );\n                };\n\n                return { useEvent: useProxyEvent };\n            },\n        },\n    ) as EventProxy<MergeEvents<TEvents, TIO>>;\n\n    const useMyPresence = <T extends unknown = TPresence>(\n        selector = identity as (myPresence: TPresence) => T,\n        options?: SubscriptionHookOptions<Id<T> | null>,\n    ): [Id<T>, 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;\n\n        const _selector = useCallback(\n            (snapshot: TPresence) => selector(snapshot) as Id<T>,\n            [selector],\n        );\n\n        const myPresence = useSyncExternalStoreWithSelector(\n            subscribe,\n            getSnapshot,\n            getSnapshot,\n            _selector,\n            options?.isEqual ?? fastDeepEqual,\n        );\n\n        return [myPresence, room.updateMyPresence];\n    };\n\n    const useMyself = <T extends unknown = UserInfo<TIO, TPresence>>(\n        selector = identity as (myself: Id<UserInfo<TIO, TPresence>>) => T,\n        options?: SubscriptionHookOptions<Id<T> | null>,\n    ): Id<T> | 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;\n\n        const _selector = useCallback(\n            (snapshot: Id<UserInfo<TIO, TPresence>> | null) => {\n                return !snapshot ? null : (selector(snapshot) as Id<T>);\n            },\n            [selector],\n        );\n\n        return useSyncExternalStoreWithSelector(\n            subscribe,\n            getSnapshot,\n            getSnapshot,\n            _selector,\n            options?.isEqual ?? fastDeepEqual,\n        );\n    };\n\n    const useOther = <T extends unknown = UserInfo<TIO, TPresence>>(\n        connectionId: string,\n        selector = identity as (other: UserInfo<TIO, TPresence>) => T,\n        options?: SubscriptionHookOptions<T | null>,\n    ): T | null => {\n        const room = useRoom();\n\n        const subscribe = useCallback(\n            (onStoreChange: () => void) => room.subscribe.other(connectionId, onStoreChange),\n            [room, connectionId],\n        );\n\n        const getSnapshot = useCallback(() => room.getOther(connectionId), [room, connectionId]);\n\n        const _selector = useCallback(\n            (snapshot: Id<UserInfo<TIO, TPresence>> | null) => {\n                return !snapshot ? null : (selector(snapshot) as T);\n            },\n            [selector],\n        );\n\n        return useSyncExternalStoreWithSelector(\n            subscribe,\n            getSnapshot,\n            getSnapshot,\n            _selector,\n            options?.isEqual ?? fastDeepEqual,\n        );\n    };\n\n    const useOthers = <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(\n        selector = identity as (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T,\n        options?: SubscriptionHookOptions<T>,\n    ): T => {\n        const room = useRoom();\n\n        const subscribe = useCallback(\n            (onStoreChange: () => void) => room.subscribe.others(onStoreChange),\n            [room],\n        );\n\n        const getSnapshot = room.getOthers;\n\n        return useSyncExternalStoreWithSelector(\n            subscribe,\n            getSnapshot,\n            getSnapshot,\n            selector as (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T,\n            options?.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        return room.redo;\n    };\n\n    const useStorage = <\n        TKey extends keyof InferStorage<TCrdt>,\n        TData extends unknown = InferCrdtJson<InferStorage<TCrdt>[TKey]>,\n    >(\n        key: TKey,\n        selector = identity as (data: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => TData,\n        options?: SubscriptionHookOptions<TData | null>,\n    ): [data: TData | null, sharedType: InferStorage<TCrdt>[TKey] | null] => {\n        const room = useRoom();\n        const rerender = useRerender();\n\n        useEffect(() => {\n            const unsubscribe = room.subscribe.storageLoaded(() => {\n                rerender();\n            });\n\n            return () => {\n                unsubscribe();\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((): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null => {\n            return room.getStorageJson(key);\n        }, [key, room]);\n\n        const _selector = useCallback(\n            (snapshot: InferCrdtJson<InferStorage<TCrdt>[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            options?.isEqual ?? fastDeepEqual,\n        );\n\n        const sharedType = room.getStorage(key) ?? null;\n\n        return [data, sharedType];\n    };\n\n    const useTransact = () => {\n        const room = useRoom();\n\n        return room.transact;\n    };\n\n    const useUndo = () => {\n        const room = useRoom();\n\n        return room.undo;\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        useRedo,\n        useRoom,\n        useStorage,\n        useTransact,\n        useUndo,\n    };\n};\n"],"mappings":"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"}