@pluv/react 2.1.0 → 2.2.1

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/react@2.1.0 build /home/runner/work/pluv/pluv/packages/react
2
+ > @pluv/react@2.2.1 build /home/runner/work/pluv/pluv/packages/react
3
3
  > tsup src/index.ts --format esm,cjs --dts --external react
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,11 +8,11 @@
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- ESM dist/index.mjs 16.72 KB
12
- ESM ⚡️ Build success in 105ms
13
- CJS dist/index.js 19.10 KB
14
- CJS ⚡️ Build success in 101ms
11
+ ESM dist/index.mjs 17.27 KB
12
+ ESM ⚡️ Build success in 93ms
13
+ CJS dist/index.js 19.66 KB
14
+ CJS ⚡️ Build success in 94ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 6294ms
17
- DTS dist/index.d.mts 5.70 KB
18
- DTS dist/index.d.ts 5.70 KB
16
+ DTS ⚡️ Build success in 5873ms
17
+ DTS dist/index.d.mts 6.00 KB
18
+ DTS dist/index.d.ts 6.00 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,70 @@
1
1
  # @pluv/react
2
2
 
3
+ ## 2.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @pluv/client@2.2.1
8
+ - @pluv/crdt@2.2.1
9
+ - @pluv/types@2.2.1
10
+
11
+ ## 2.2.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 3ce1547: Updated typings for `useDoc` to be narrower to get the native doc type.
16
+
17
+ ```ts
18
+ import { createClient } from "@pluv/client";
19
+ import { yjs } from "@pluv/crdt-yjs";
20
+ import { createBundle } from "@pluv/react";
21
+ import type { Doc as YDoc } from "yjs";
22
+
23
+ const client = createClient({
24
+ // ...
25
+ crdt: yjs,
26
+ });
27
+ const { useDoc } = createBundle(client);
28
+
29
+ const doc = useDoc();
30
+
31
+ // This is now typed as the native YDoc
32
+ doc.value;
33
+ // ^? const value: YDoc
34
+ ```
35
+
36
+ - b999c1c: Added a new type `InferBundleRoom` to get the type of the `PluvRoom` from the react bundle that is created.
37
+
38
+ ```ts
39
+ import { createClient } from "@pluv/client";
40
+ import type { InferBundleRoom } from "@pluv/react";
41
+ import { createBundle } from "@pluv/react";
42
+
43
+ const client = createclient({
44
+ /* ... */
45
+ });
46
+
47
+ const bundle = createBundle(client);
48
+
49
+ type RoomType = InferBundleRoom<typeof bundle>;
50
+
51
+ const { useStorage } = bundle;
52
+ ```
53
+
54
+ ### Patch Changes
55
+
56
+ - cb660cd: Fixed the room not changing if the room name was changed on the `PluvRoomProvider` without first unmounting the component.
57
+ - 4916c27: Fixed `useConnection` requiring a selector function (this is now optional).
58
+ - 48191fa: Fixed `useDoc` not returning a stable doc reference for a single connection.
59
+ - Updated dependencies [3ce1547]
60
+ - Updated dependencies [fd3bfe2]
61
+ - Updated dependencies [aee40cb]
62
+ - Updated dependencies [edd1789]
63
+ - Updated dependencies [48191fa]
64
+ - @pluv/client@2.2.0
65
+ - @pluv/crdt@2.2.0
66
+ - @pluv/types@2.2.0
67
+
3
68
  ## 2.1.0
4
69
 
5
70
  ### Minor Changes
package/dist/index.d.mts CHANGED
@@ -1,12 +1,12 @@
1
- import { PluvRouterEventConfig, PluvClient, PluvRoomAddon, PluvRouter, MockedRoomEvents, MergeEvents, PluvRoomDebug, WebSocketConnection, UserInfo } from '@pluv/client';
2
- import { AbstractCrdtDocFactory, NoopCrdtDocFactory, InferStorage, InferInitialStorageFn, InferCrdtJson } from '@pluv/crdt';
3
- import { IOLike, JsonObject, MaybePromise, InferIOOutput, Id, IOEventMessage, InferIOInput, CrdtDocLike, RoomLike } from '@pluv/types';
1
+ import { PluvRouterEventConfig, MockedRoomEvents, MergeEvents, PluvRoomDebug, PluvClient, WebSocketConnection, UserInfo, PluvRoomAddon, PluvRouter } from '@pluv/client';
2
+ import { AbstractCrdtDocFactory, NoopCrdtDocFactory, InferStorage, InferInitialStorageFn, InferDoc, InferCrdtJson } from '@pluv/crdt';
3
+ import { IOLike, JsonObject, MaybePromise, InferIOOutput, Id, IOEventMessage, InferIOInput, CrdtDocLike, UpdateMyPresenceAction, RoomLike } from '@pluv/types';
4
4
  import { FC, ReactNode, Dispatch } from 'react';
5
5
 
6
6
  interface PluvProviderProps {
7
7
  children?: ReactNode;
8
8
  }
9
- type BaseRoomProviderProps<TPresence extends JsonObject, TCrdt extends AbstractCrdtDocFactory<any>> = {
9
+ type BaseRoomProviderProps<TPresence extends Record<string, any>, TCrdt extends AbstractCrdtDocFactory<any, any>> = {
10
10
  children?: ReactNode;
11
11
  initialStorage?: keyof InferStorage<TCrdt> extends never ? never : InferInitialStorageFn<TCrdt>;
12
12
  room: string;
@@ -15,11 +15,19 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TCrdt extends AbstractC
15
15
  } : {
16
16
  initialPresence: TPresence;
17
17
  });
18
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TCrdt extends AbstractCrdtDocFactory<any>, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = BaseRoomProviderProps<TPresence, TCrdt> & {
18
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends Record<string, any>, TCrdt extends AbstractCrdtDocFactory<any, any>, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = BaseRoomProviderProps<TPresence, TCrdt> & {
19
19
  events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
20
20
  };
21
+ type BroadcastProxy<TIO extends IOLike> = (<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => void) & {
22
+ [event in keyof InferIOInput<TIO>]: (input: Id<InferIOInput<TIO>[event]>) => void;
23
+ };
24
+ type EventProxy<TIO extends IOLike> = {
25
+ [event in keyof InferIOOutput<TIO>]: {
26
+ useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
27
+ };
28
+ };
21
29
  type MetadataGetter<TMetadata extends JsonObject> = TMetadata | (() => MaybePromise<TMetadata>);
22
- type PluvRoomProviderProps<TIO extends IOLike<any, any>, TMetadata extends JsonObject, TPresence extends JsonObject, TCrdt extends AbstractCrdtDocFactory<any>> = BaseRoomProviderProps<TPresence, TCrdt> & {
30
+ type PluvRoomProviderProps<TIO extends IOLike<any, any>, TMetadata extends JsonObject, TPresence extends Record<string, any>, TCrdt extends AbstractCrdtDocFactory<any, any>> = BaseRoomProviderProps<TPresence, TCrdt> & {
23
31
  connect?: boolean;
24
32
  debug?: boolean | PluvRoomDebug<TIO>;
25
33
  onAuthorizationFail?: (error: Error) => void;
@@ -31,16 +39,7 @@ type PluvRoomProviderProps<TIO extends IOLike<any, any>, TMetadata extends JsonO
31
39
  interface SubscriptionHookOptions<T extends unknown> {
32
40
  isEqual?: (a: T, b: T) => boolean;
33
41
  }
34
- type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
35
- type BroadcastProxy<TIO extends IOLike> = (<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => void) & {
36
- [event in keyof InferIOInput<TIO>]: (input: Id<InferIOInput<TIO>[event]>) => void;
37
- };
38
- type EventProxy<TIO extends IOLike> = {
39
- [event in keyof InferIOOutput<TIO>]: {
40
- useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
41
- };
42
- };
43
- interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject = {}, TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> {
42
+ interface CreateBundle<TIO extends IOLike<any>, TMetadata extends JsonObject, TPresence extends Record<string, any> = {}, TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> {
44
43
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TCrdt>>;
45
44
  PluvProvider: FC<PluvProviderProps>;
46
45
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TCrdt>>;
@@ -49,23 +48,25 @@ interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresen
49
48
  useCanRedo: () => boolean;
50
49
  useCanUndo: () => boolean;
51
50
  useClient: () => PluvClient<TIO, TPresence, TCrdt, TMetadata>;
52
- useConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
53
- useDoc: () => CrdtDocLike<InferStorage<TCrdt>>;
51
+ useConnection: <T extends unknown = WebSocketConnection>(selector?: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
52
+ useDoc: () => CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>>;
54
53
  useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(type: TType, callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void) => void;
55
54
  useMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
56
55
  useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
57
56
  useOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<T | null>) => T | null;
58
57
  useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T, options?: SubscriptionHookOptions<T>) => T;
59
58
  useRedo: () => () => void;
60
- useRoom: () => RoomLike<TIO, TPresence, InferStorage<TCrdt>>;
59
+ useRoom: () => RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>, TEvents>;
61
60
  useStorage: <TKey extends keyof InferStorage<TCrdt>, TData extends unknown = InferCrdtJson<InferStorage<TCrdt>[TKey]>>(key: TKey, selector?: (data: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: InferStorage<TCrdt>[TKey] | null];
62
61
  useTransact: () => (fn: (storage: InferStorage<TCrdt>) => void, origin?: string) => void;
63
62
  useUndo: () => () => void;
64
63
  }
65
- type CreateBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = {
64
+ type InferBundleRoom<TBundle extends CreateBundle<any, any, any, any, any>> = TBundle extends CreateBundle<infer IIO, any, infer IPresence, infer ICrdt, infer IEvents> ? RoomLike<IIO, InferDoc<ICrdt>, IPresence, InferStorage<ICrdt>, IEvents> : never;
65
+
66
+ type CreateBundleOptions<TIO extends IOLike<any>, TMetadata extends JsonObject = {}, TPresence extends Record<string, any> = {}, TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = {
66
67
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];
67
68
  router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
68
69
  };
69
- declare const createBundle: <TIO extends IOLike<any, any>, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}>(client: PluvClient<TIO, TPresence, TCrdt, TMetadata>, options?: CreateBundleOptions<TIO, TMetadata, TPresence, TCrdt, TEvents>) => CreateBundle<TIO, TMetadata, TPresence, TCrdt, TEvents>;
70
+ declare const createBundle: <TIO extends IOLike<any, any>, TMetadata extends JsonObject = {}, TPresence extends Record<string, any> = {}, TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}>(client: PluvClient<TIO, TPresence, TCrdt, TMetadata>, options?: CreateBundleOptions<TIO, TMetadata, TPresence, TCrdt, TEvents>) => CreateBundle<TIO, TMetadata, TPresence, TCrdt, TEvents>;
70
71
 
71
- export { type CreateBundle, type EventProxy, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
72
+ export { type CreateBundle, type EventProxy, type InferBundleRoom, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { PluvRouterEventConfig, PluvClient, PluvRoomAddon, PluvRouter, MockedRoomEvents, MergeEvents, PluvRoomDebug, WebSocketConnection, UserInfo } from '@pluv/client';
2
- import { AbstractCrdtDocFactory, NoopCrdtDocFactory, InferStorage, InferInitialStorageFn, InferCrdtJson } from '@pluv/crdt';
3
- import { IOLike, JsonObject, MaybePromise, InferIOOutput, Id, IOEventMessage, InferIOInput, CrdtDocLike, RoomLike } from '@pluv/types';
1
+ import { PluvRouterEventConfig, MockedRoomEvents, MergeEvents, PluvRoomDebug, PluvClient, WebSocketConnection, UserInfo, PluvRoomAddon, PluvRouter } from '@pluv/client';
2
+ import { AbstractCrdtDocFactory, NoopCrdtDocFactory, InferStorage, InferInitialStorageFn, InferDoc, InferCrdtJson } from '@pluv/crdt';
3
+ import { IOLike, JsonObject, MaybePromise, InferIOOutput, Id, IOEventMessage, InferIOInput, CrdtDocLike, UpdateMyPresenceAction, RoomLike } from '@pluv/types';
4
4
  import { FC, ReactNode, Dispatch } from 'react';
5
5
 
6
6
  interface PluvProviderProps {
7
7
  children?: ReactNode;
8
8
  }
9
- type BaseRoomProviderProps<TPresence extends JsonObject, TCrdt extends AbstractCrdtDocFactory<any>> = {
9
+ type BaseRoomProviderProps<TPresence extends Record<string, any>, TCrdt extends AbstractCrdtDocFactory<any, any>> = {
10
10
  children?: ReactNode;
11
11
  initialStorage?: keyof InferStorage<TCrdt> extends never ? never : InferInitialStorageFn<TCrdt>;
12
12
  room: string;
@@ -15,11 +15,19 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TCrdt extends AbstractC
15
15
  } : {
16
16
  initialPresence: TPresence;
17
17
  });
18
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TCrdt extends AbstractCrdtDocFactory<any>, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = BaseRoomProviderProps<TPresence, TCrdt> & {
18
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends Record<string, any>, TCrdt extends AbstractCrdtDocFactory<any, any>, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = BaseRoomProviderProps<TPresence, TCrdt> & {
19
19
  events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
20
20
  };
21
+ type BroadcastProxy<TIO extends IOLike> = (<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => void) & {
22
+ [event in keyof InferIOInput<TIO>]: (input: Id<InferIOInput<TIO>[event]>) => void;
23
+ };
24
+ type EventProxy<TIO extends IOLike> = {
25
+ [event in keyof InferIOOutput<TIO>]: {
26
+ useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
27
+ };
28
+ };
21
29
  type MetadataGetter<TMetadata extends JsonObject> = TMetadata | (() => MaybePromise<TMetadata>);
22
- type PluvRoomProviderProps<TIO extends IOLike<any, any>, TMetadata extends JsonObject, TPresence extends JsonObject, TCrdt extends AbstractCrdtDocFactory<any>> = BaseRoomProviderProps<TPresence, TCrdt> & {
30
+ type PluvRoomProviderProps<TIO extends IOLike<any, any>, TMetadata extends JsonObject, TPresence extends Record<string, any>, TCrdt extends AbstractCrdtDocFactory<any, any>> = BaseRoomProviderProps<TPresence, TCrdt> & {
23
31
  connect?: boolean;
24
32
  debug?: boolean | PluvRoomDebug<TIO>;
25
33
  onAuthorizationFail?: (error: Error) => void;
@@ -31,16 +39,7 @@ type PluvRoomProviderProps<TIO extends IOLike<any, any>, TMetadata extends JsonO
31
39
  interface SubscriptionHookOptions<T extends unknown> {
32
40
  isEqual?: (a: T, b: T) => boolean;
33
41
  }
34
- type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
35
- type BroadcastProxy<TIO extends IOLike> = (<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => void) & {
36
- [event in keyof InferIOInput<TIO>]: (input: Id<InferIOInput<TIO>[event]>) => void;
37
- };
38
- type EventProxy<TIO extends IOLike> = {
39
- [event in keyof InferIOOutput<TIO>]: {
40
- useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
41
- };
42
- };
43
- interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject = {}, TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> {
42
+ interface CreateBundle<TIO extends IOLike<any>, TMetadata extends JsonObject, TPresence extends Record<string, any> = {}, TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> {
44
43
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TCrdt>>;
45
44
  PluvProvider: FC<PluvProviderProps>;
46
45
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TCrdt>>;
@@ -49,23 +48,25 @@ interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresen
49
48
  useCanRedo: () => boolean;
50
49
  useCanUndo: () => boolean;
51
50
  useClient: () => PluvClient<TIO, TPresence, TCrdt, TMetadata>;
52
- useConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
53
- useDoc: () => CrdtDocLike<InferStorage<TCrdt>>;
51
+ useConnection: <T extends unknown = WebSocketConnection>(selector?: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
52
+ useDoc: () => CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>>;
54
53
  useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(type: TType, callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void) => void;
55
54
  useMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
56
55
  useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
57
56
  useOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<T | null>) => T | null;
58
57
  useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T, options?: SubscriptionHookOptions<T>) => T;
59
58
  useRedo: () => () => void;
60
- useRoom: () => RoomLike<TIO, TPresence, InferStorage<TCrdt>>;
59
+ useRoom: () => RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>, TEvents>;
61
60
  useStorage: <TKey extends keyof InferStorage<TCrdt>, TData extends unknown = InferCrdtJson<InferStorage<TCrdt>[TKey]>>(key: TKey, selector?: (data: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: InferStorage<TCrdt>[TKey] | null];
62
61
  useTransact: () => (fn: (storage: InferStorage<TCrdt>) => void, origin?: string) => void;
63
62
  useUndo: () => () => void;
64
63
  }
65
- type CreateBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = {
64
+ type InferBundleRoom<TBundle extends CreateBundle<any, any, any, any, any>> = TBundle extends CreateBundle<infer IIO, any, infer IPresence, infer ICrdt, infer IEvents> ? RoomLike<IIO, InferDoc<ICrdt>, IPresence, InferStorage<ICrdt>, IEvents> : never;
65
+
66
+ type CreateBundleOptions<TIO extends IOLike<any>, TMetadata extends JsonObject = {}, TPresence extends Record<string, any> = {}, TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}> = {
66
67
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];
67
68
  router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
68
69
  };
69
- declare const createBundle: <TIO extends IOLike<any, any>, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}>(client: PluvClient<TIO, TPresence, TCrdt, TMetadata>, options?: CreateBundleOptions<TIO, TMetadata, TPresence, TCrdt, TEvents>) => CreateBundle<TIO, TMetadata, TPresence, TCrdt, TEvents>;
70
+ declare const createBundle: <TIO extends IOLike<any, any>, TMetadata extends JsonObject = {}, TPresence extends Record<string, any> = {}, TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory, TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {}>(client: PluvClient<TIO, TPresence, TCrdt, TMetadata>, options?: CreateBundleOptions<TIO, TMetadata, TPresence, TCrdt, TEvents>) => CreateBundle<TIO, TMetadata, TPresence, TCrdt, TEvents>;
70
71
 
71
- export { type CreateBundle, type EventProxy, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
72
+ export { type CreateBundle, type EventProxy, type InferBundleRoom, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.js CHANGED
@@ -225,12 +225,8 @@ var useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerS
225
225
  var import_jsx_runtime = require("react/jsx-runtime");
226
226
  var createBundle = (client, options = {}) => {
227
227
  const PluvContext = (0, import_react6.createContext)(null);
228
- const PluvRoomContext = (0, import_react6.createContext)(
229
- null
230
- );
231
- const MockedRoomContext = (0, import_react6.createContext)(
232
- null
233
- );
228
+ const PluvRoomContext = (0, import_react6.createContext)(null);
229
+ const MockedRoomContext = (0, import_react6.createContext)(null);
234
230
  const MockedRoomProvider = (0, import_react6.memo)((props) => {
235
231
  const { children, events, initialPresence, initialStorage, room: _room } = props;
236
232
  const [room] = (0, import_react6.useState)(() => {
@@ -265,7 +261,7 @@ var createBundle = (client, options = {}) => {
265
261
  );
266
262
  return !!room.metadata ? room.metadata.parse(resolved) : resolved;
267
263
  }));
268
- const [room] = (0, import_react6.useState)(() => {
264
+ const createRoom = (0, import_react6.useCallback)(() => {
269
265
  var _a;
270
266
  return client.createRoom(_room, {
271
267
  addons: options.addons,
@@ -276,7 +272,12 @@ var createBundle = (client, options = {}) => {
276
272
  onAuthorizationFail,
277
273
  router: options.router
278
274
  });
279
- });
275
+ }, [_room, debug, initialPresence, initialStorage, metadata, onAuthorizationFail]);
276
+ const [room, setRoom] = (0, import_react6.useState)(() => createRoom());
277
+ (0, import_react6.useEffect)(() => {
278
+ if (room.id === _room) return;
279
+ setRoom(createRoom());
280
+ }, [_room, createRoom, room]);
280
281
  (0, import_react6.useEffect)(() => {
281
282
  const unsubscribe = room.subscribe.connection(() => {
282
283
  rerender();
@@ -402,7 +403,14 @@ var createBundle = (client, options = {}) => {
402
403
  };
403
404
  const useDoc = () => {
404
405
  const room = useRoom();
405
- return room.getDoc();
406
+ const subscribe = (0, import_react6.useCallback)(
407
+ (onStoreChange) => {
408
+ return room.subscribe.storageLoaded(onStoreChange);
409
+ },
410
+ [room]
411
+ );
412
+ const getSnapshot = room.getDoc;
413
+ return (0, import_react6.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
406
414
  };
407
415
  const useEvent = (type, callback) => {
408
416
  const room = useRoom();
package/dist/index.mjs CHANGED
@@ -41,7 +41,16 @@ var __async = (__this, __arguments, generator) => {
41
41
  // src/createBundle.tsx
42
42
  import { MockedRoom } from "@pluv/client";
43
43
  import fastDeepEqual2 from "fast-deep-equal";
44
- import { createContext, memo, useCallback as useCallback4, useContext, useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
44
+ import {
45
+ createContext,
46
+ memo,
47
+ useCallback as useCallback4,
48
+ useContext,
49
+ useEffect as useEffect3,
50
+ useMemo as useMemo2,
51
+ useState as useState4,
52
+ useSyncExternalStore as useSyncExternalStore2
53
+ } from "react";
45
54
 
46
55
  // src/internal/hooks/useAsyncQueue.ts
47
56
  import { useCallback, useMemo, useRef } from "react";
@@ -192,12 +201,8 @@ var useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerS
192
201
  import { jsx } from "react/jsx-runtime";
193
202
  var createBundle = (client, options = {}) => {
194
203
  const PluvContext = createContext(null);
195
- const PluvRoomContext = createContext(
196
- null
197
- );
198
- const MockedRoomContext = createContext(
199
- null
200
- );
204
+ const PluvRoomContext = createContext(null);
205
+ const MockedRoomContext = createContext(null);
201
206
  const MockedRoomProvider = memo((props) => {
202
207
  const { children, events, initialPresence, initialStorage, room: _room } = props;
203
208
  const [room] = useState4(() => {
@@ -232,7 +237,7 @@ var createBundle = (client, options = {}) => {
232
237
  );
233
238
  return !!room.metadata ? room.metadata.parse(resolved) : resolved;
234
239
  }));
235
- const [room] = useState4(() => {
240
+ const createRoom = useCallback4(() => {
236
241
  var _a;
237
242
  return client.createRoom(_room, {
238
243
  addons: options.addons,
@@ -243,7 +248,12 @@ var createBundle = (client, options = {}) => {
243
248
  onAuthorizationFail,
244
249
  router: options.router
245
250
  });
246
- });
251
+ }, [_room, debug, initialPresence, initialStorage, metadata, onAuthorizationFail]);
252
+ const [room, setRoom] = useState4(() => createRoom());
253
+ useEffect3(() => {
254
+ if (room.id === _room) return;
255
+ setRoom(createRoom());
256
+ }, [_room, createRoom, room]);
247
257
  useEffect3(() => {
248
258
  const unsubscribe = room.subscribe.connection(() => {
249
259
  rerender();
@@ -369,7 +379,14 @@ var createBundle = (client, options = {}) => {
369
379
  };
370
380
  const useDoc = () => {
371
381
  const room = useRoom();
372
- return room.getDoc();
382
+ const subscribe = useCallback4(
383
+ (onStoreChange) => {
384
+ return room.subscribe.storageLoaded(onStoreChange);
385
+ },
386
+ [room]
387
+ );
388
+ const getSnapshot = room.getDoc;
389
+ return useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
373
390
  };
374
391
  const useEvent = (type, callback) => {
375
392
  const room = useRoom();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "@pluv/client React.js bindings",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,22 +18,22 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "fast-deep-equal": "^3.1.3",
21
- "@pluv/client": "^2.1.0",
22
- "@pluv/crdt": "^2.1.0",
23
- "@pluv/types": "^2.1.0"
21
+ "@pluv/client": "^2.2.1",
22
+ "@pluv/crdt": "^2.2.1",
23
+ "@pluv/types": "^2.2.1"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@types/react": "^18.0.0 || ^19.0.0",
27
27
  "react": "^18.0.0 || ^19.0.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/react": "^19.1.3",
30
+ "@types/react": "^19.1.4",
31
31
  "eslint": "^9.26.0",
32
32
  "react": "^19.1.0",
33
33
  "tsup": "^8.4.0",
34
34
  "typescript": "^5.8.3",
35
- "@pluv/tsconfig": "^2.1.0",
36
- "eslint-config-pluv": "^2.1.0"
35
+ "@pluv/tsconfig": "^2.2.1",
36
+ "eslint-config-pluv": "^2.2.1"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup src/index.ts --format esm,cjs --dts --external react",
@@ -2,37 +2,44 @@ import type {
2
2
  CreateRoomOptions,
3
3
  EnterRoomParams,
4
4
  MergeEvents,
5
- MockedRoomEvents,
6
5
  PluvClient,
6
+ PluvRoom,
7
7
  PluvRoomAddon,
8
- PluvRoomDebug,
9
8
  PluvRouter,
10
9
  PluvRouterEventConfig,
11
10
  UserInfo,
12
11
  WebSocketConnection,
13
12
  } from "@pluv/client";
14
- import { MockedRoom, PluvRoom } from "@pluv/client";
13
+ import { MockedRoom } from "@pluv/client";
15
14
  import type {
16
15
  AbstractCrdtDocFactory,
17
16
  InferCrdtJson,
18
- InferInitialStorageFn,
17
+ InferDoc,
19
18
  InferStorage,
20
19
  NoopCrdtDocFactory,
21
20
  } from "@pluv/crdt";
22
21
  import type {
23
- CrdtDocLike,
24
22
  Id,
25
23
  InferIOInput,
26
24
  InferIOOutput,
27
25
  IOEventMessage,
28
26
  IOLike,
29
27
  JsonObject,
30
- MaybePromise,
31
28
  RoomLike,
29
+ UpdateMyPresenceAction,
32
30
  } from "@pluv/types";
33
31
  import fastDeepEqual from "fast-deep-equal";
34
- import type { Dispatch, FC, ReactNode } from "react";
35
- import { createContext, memo, useCallback, useContext, useEffect, useMemo, useState } from "react";
32
+ import type { Dispatch } from "react";
33
+ import {
34
+ createContext,
35
+ memo,
36
+ useCallback,
37
+ useContext,
38
+ useEffect,
39
+ useMemo,
40
+ useState,
41
+ useSyncExternalStore,
42
+ } from "react";
36
43
  import {
37
44
  identity,
38
45
  shallowArrayEqual,
@@ -41,132 +48,21 @@ import {
41
48
  useRerender,
42
49
  useSyncExternalStoreWithSelector,
43
50
  } from "./internal";
44
-
45
- export interface PluvProviderProps {
46
- children?: ReactNode;
47
- }
48
-
49
- type BaseRoomProviderProps<
50
- TPresence extends JsonObject,
51
- TCrdt extends AbstractCrdtDocFactory<any>,
52
- > = {
53
- children?: ReactNode;
54
- initialStorage?: keyof InferStorage<TCrdt> extends never ? never : InferInitialStorageFn<TCrdt>;
55
- room: string;
56
- } & (keyof TPresence extends never ? { initialPresence?: never } : { initialPresence: TPresence });
57
-
58
- export type MockedRoomProviderProps<
59
- TIO extends IOLike,
60
- TPresence extends JsonObject,
61
- TCrdt extends AbstractCrdtDocFactory<any>,
62
- TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
63
- > = BaseRoomProviderProps<TPresence, TCrdt> & {
64
- events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
65
- };
66
-
67
- export type MetadataGetter<TMetadata extends JsonObject> =
68
- | TMetadata
69
- | (() => MaybePromise<TMetadata>);
70
-
71
- export type PluvRoomProviderProps<
72
- TIO extends IOLike<any, any>,
73
- TMetadata extends JsonObject,
74
- TPresence extends JsonObject,
75
- TCrdt extends AbstractCrdtDocFactory<any>,
76
- > = BaseRoomProviderProps<TPresence, TCrdt> & {
77
- connect?: boolean;
78
- debug?: boolean | PluvRoomDebug<TIO>;
79
- onAuthorizationFail?: (error: Error) => void;
80
- } & (keyof TMetadata extends never
81
- ? { metadata?: undefined }
82
- : { metadata: MetadataGetter<TMetadata> });
83
-
84
- export interface SubscriptionHookOptions<T extends unknown> {
85
- isEqual?: (a: T, b: T) => boolean;
86
- }
87
-
88
- export type UpdateMyPresenceAction<TPresence extends JsonObject> =
89
- | Partial<TPresence>
90
- | ((oldPresence: TPresence | null) => Partial<TPresence>);
91
-
92
- export type BroadcastProxy<TIO extends IOLike> = (<TEvent extends keyof InferIOInput<TIO>>(
93
- event: TEvent,
94
- data: Id<InferIOInput<TIO>[TEvent]>,
95
- ) => void) & {
96
- [event in keyof InferIOInput<TIO>]: (input: Id<InferIOInput<TIO>[event]>) => void;
97
- };
98
-
99
- export type EventProxy<TIO extends IOLike> = {
100
- [event in keyof InferIOOutput<TIO>]: {
101
- useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
102
- };
103
- };
104
-
105
- export interface CreateBundle<
106
- TIO extends IOLike,
107
- TMetadata extends JsonObject,
108
- TPresence extends JsonObject = {},
109
- TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
110
- TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
111
- > {
112
- // components
113
- MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TCrdt>>;
114
- PluvProvider: FC<PluvProviderProps>;
115
- PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TCrdt>>;
116
-
117
- // proxies
118
- event: EventProxy<MergeEvents<TEvents, TIO>>;
119
-
120
- // hooks
121
- useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
122
- useCanRedo: () => boolean;
123
- useCanUndo: () => boolean;
124
- useClient: () => PluvClient<TIO, TPresence, TCrdt, TMetadata>;
125
- useConnection: <T extends unknown = WebSocketConnection>(
126
- selector: (connection: WebSocketConnection) => T,
127
- options?: SubscriptionHookOptions<Id<T>>,
128
- ) => Id<T>;
129
- useDoc: () => CrdtDocLike<InferStorage<TCrdt>>;
130
- useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
131
- type: TType,
132
- callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,
133
- ) => void;
134
- useMyPresence: <T extends unknown = TPresence>(
135
- selector?: (myPresence: TPresence) => T,
136
- options?: SubscriptionHookOptions<Id<T> | null>,
137
- ) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
138
- useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(
139
- selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T,
140
- options?: SubscriptionHookOptions<Id<T> | null>,
141
- ) => Id<T> | null;
142
- useOther: <T extends unknown = UserInfo<TIO, TPresence>>(
143
- connectionId: string,
144
- selector?: (other: UserInfo<TIO, TPresence>) => T,
145
- options?: SubscriptionHookOptions<T | null>,
146
- ) => T | null;
147
- useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(
148
- selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T,
149
- options?: SubscriptionHookOptions<T>,
150
- ) => T;
151
- useRedo: () => () => void;
152
- useRoom: () => RoomLike<TIO, TPresence, InferStorage<TCrdt>>;
153
- useStorage: <
154
- TKey extends keyof InferStorage<TCrdt>,
155
- TData extends unknown = InferCrdtJson<InferStorage<TCrdt>[TKey]>,
156
- >(
157
- key: TKey,
158
- selector?: (data: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => TData,
159
- options?: SubscriptionHookOptions<TData | null>,
160
- ) => [data: TData | null, sharedType: InferStorage<TCrdt>[TKey] | null];
161
- useTransact: () => (fn: (storage: InferStorage<TCrdt>) => void, origin?: string) => void;
162
- useUndo: () => () => void;
163
- }
51
+ import type {
52
+ BroadcastProxy,
53
+ CreateBundle,
54
+ EventProxy,
55
+ MockedRoomProviderProps,
56
+ PluvProviderProps,
57
+ PluvRoomProviderProps,
58
+ SubscriptionHookOptions,
59
+ } from "./types";
164
60
 
165
61
  export type CreateBundleOptions<
166
- TIO extends IOLike,
62
+ TIO extends IOLike<any>,
167
63
  TMetadata extends JsonObject = {},
168
- TPresence extends JsonObject = {},
169
- TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
64
+ TPresence extends Record<string, any> = {},
65
+ TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory,
170
66
  TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
171
67
  > = {
172
68
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];
@@ -176,8 +72,8 @@ export type CreateBundleOptions<
176
72
  export const createBundle = <
177
73
  TIO extends IOLike<any, any>,
178
74
  TMetadata extends JsonObject = {},
179
- TPresence extends JsonObject = {},
180
- TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
75
+ TPresence extends Record<string, any> = {},
76
+ TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory,
181
77
  TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
182
78
  >(
183
79
  client: PluvClient<TIO, TPresence, TCrdt, TMetadata>,
@@ -197,13 +93,17 @@ export const createBundle = <
197
93
  * and let the users deal with it.
198
94
  * @date November 11, 2022
199
95
  */
200
- const PluvRoomContext = createContext<RoomLike<TIO, TPresence, InferStorage<TCrdt>>>(
201
- null as any,
202
- );
203
-
204
- const MockedRoomContext = createContext<RoomLike<TIO, TPresence, InferStorage<TCrdt>> | null>(
205
- null,
206
- );
96
+ const PluvRoomContext = createContext<
97
+ RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>, TEvents>
98
+ >(null as any);
99
+
100
+ const MockedRoomContext = createContext<RoomLike<
101
+ TIO,
102
+ InferDoc<TCrdt>,
103
+ TPresence,
104
+ InferStorage<TCrdt>,
105
+ TEvents
106
+ > | null>(null);
207
107
 
208
108
  const MockedRoomProvider = memo<MockedRoomProviderProps<TIO, TPresence, TCrdt>>((props) => {
209
109
  const { children, events, initialPresence, initialStorage, room: _room } = props;
@@ -253,7 +153,13 @@ export const createBundle = <
253
153
  return !!room.metadata ? room.metadata.parse(resolved) : resolved;
254
154
  });
255
155
 
256
- const [room] = useState<PluvRoom<TIO, TMetadata, TPresence, TCrdt, TEvents>>(() => {
156
+ const createRoom = useCallback((): PluvRoom<
157
+ TIO,
158
+ TMetadata,
159
+ TPresence,
160
+ TCrdt,
161
+ TEvents
162
+ > => {
257
163
  return client.createRoom(_room, {
258
164
  addons: options.addons,
259
165
  debug,
@@ -266,7 +172,15 @@ export const createBundle = <
266
172
  onAuthorizationFail,
267
173
  router: options.router,
268
174
  } as CreateRoomOptions<TIO, TPresence, TCrdt, TMetadata, TEvents>);
269
- });
175
+ }, [_room, debug, initialPresence, initialStorage, metadata, onAuthorizationFail]);
176
+
177
+ const [room, setRoom] = useState(() => createRoom());
178
+
179
+ useEffect(() => {
180
+ if (room.id === _room) return;
181
+
182
+ setRoom(createRoom());
183
+ }, [_room, createRoom, room]);
270
184
 
271
185
  useEffect(() => {
272
186
  const unsubscribe = room.subscribe.connection(() => {
@@ -446,7 +360,16 @@ export const createBundle = <
446
360
  const useDoc = () => {
447
361
  const room = useRoom();
448
362
 
449
- return room.getDoc();
363
+ const subscribe = useCallback(
364
+ (onStoreChange: () => void) => {
365
+ return room.subscribe.storageLoaded(onStoreChange);
366
+ },
367
+ [room],
368
+ );
369
+
370
+ const getSnapshot = room.getDoc;
371
+
372
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
450
373
  };
451
374
 
452
375
  const useEvent = <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
package/src/index.ts CHANGED
@@ -2,7 +2,8 @@ export { createBundle } from "./createBundle";
2
2
  export type {
3
3
  CreateBundle,
4
4
  EventProxy,
5
+ InferBundleRoom,
5
6
  PluvProviderProps,
6
7
  PluvRoomProviderProps,
7
8
  SubscriptionHookOptions,
8
- } from "./createBundle";
9
+ } from "./types";
package/src/types.ts ADDED
@@ -0,0 +1,151 @@
1
+ import type {
2
+ MergeEvents,
3
+ MockedRoomEvents,
4
+ PluvClient,
5
+ PluvRoomDebug,
6
+ PluvRouterEventConfig,
7
+ UserInfo,
8
+ WebSocketConnection,
9
+ } from "@pluv/client";
10
+ import type {
11
+ AbstractCrdtDocFactory,
12
+ InferCrdtJson,
13
+ InferDoc,
14
+ InferInitialStorageFn,
15
+ InferStorage,
16
+ NoopCrdtDocFactory,
17
+ } from "@pluv/crdt";
18
+ import type {
19
+ CrdtDocLike,
20
+ Id,
21
+ InferIOInput,
22
+ InferIOOutput,
23
+ IOEventMessage,
24
+ IOLike,
25
+ JsonObject,
26
+ MaybePromise,
27
+ RoomLike,
28
+ UpdateMyPresenceAction,
29
+ } from "@pluv/types";
30
+ import type { Dispatch, FC, ReactNode } from "react";
31
+
32
+ export interface PluvProviderProps {
33
+ children?: ReactNode;
34
+ }
35
+
36
+ type BaseRoomProviderProps<
37
+ TPresence extends Record<string, any>,
38
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
39
+ > = {
40
+ children?: ReactNode;
41
+ initialStorage?: keyof InferStorage<TCrdt> extends never ? never : InferInitialStorageFn<TCrdt>;
42
+ room: string;
43
+ } & (keyof TPresence extends never ? { initialPresence?: never } : { initialPresence: TPresence });
44
+
45
+ export type MockedRoomProviderProps<
46
+ TIO extends IOLike,
47
+ TPresence extends Record<string, any>,
48
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
49
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
50
+ > = BaseRoomProviderProps<TPresence, TCrdt> & {
51
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
52
+ };
53
+
54
+ export type BroadcastProxy<TIO extends IOLike> = (<TEvent extends keyof InferIOInput<TIO>>(
55
+ event: TEvent,
56
+ data: Id<InferIOInput<TIO>[TEvent]>,
57
+ ) => void) & {
58
+ [event in keyof InferIOInput<TIO>]: (input: Id<InferIOInput<TIO>[event]>) => void;
59
+ };
60
+
61
+ export type EventProxy<TIO extends IOLike> = {
62
+ [event in keyof InferIOOutput<TIO>]: {
63
+ useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
64
+ };
65
+ };
66
+
67
+ export type MetadataGetter<TMetadata extends JsonObject> =
68
+ | TMetadata
69
+ | (() => MaybePromise<TMetadata>);
70
+
71
+ export type PluvRoomProviderProps<
72
+ TIO extends IOLike<any, any>,
73
+ TMetadata extends JsonObject,
74
+ TPresence extends Record<string, any>,
75
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
76
+ > = BaseRoomProviderProps<TPresence, TCrdt> & {
77
+ connect?: boolean;
78
+ debug?: boolean | PluvRoomDebug<TIO>;
79
+ onAuthorizationFail?: (error: Error) => void;
80
+ } & (keyof TMetadata extends never
81
+ ? { metadata?: undefined }
82
+ : { metadata: MetadataGetter<TMetadata> });
83
+
84
+ export interface SubscriptionHookOptions<T extends unknown> {
85
+ isEqual?: (a: T, b: T) => boolean;
86
+ }
87
+
88
+ export interface CreateBundle<
89
+ TIO extends IOLike<any>,
90
+ TMetadata extends JsonObject,
91
+ TPresence extends Record<string, any> = {},
92
+ TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory,
93
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
94
+ > {
95
+ // components
96
+ MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TCrdt>>;
97
+ PluvProvider: FC<PluvProviderProps>;
98
+ PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TCrdt>>;
99
+
100
+ // proxies
101
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
102
+
103
+ // hooks
104
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
105
+ useCanRedo: () => boolean;
106
+ useCanUndo: () => boolean;
107
+ useClient: () => PluvClient<TIO, TPresence, TCrdt, TMetadata>;
108
+ useConnection: <T extends unknown = WebSocketConnection>(
109
+ selector?: (connection: WebSocketConnection) => T,
110
+ options?: SubscriptionHookOptions<Id<T>>,
111
+ ) => Id<T>;
112
+ useDoc: () => CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>>;
113
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
114
+ type: TType,
115
+ callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,
116
+ ) => void;
117
+ useMyPresence: <T extends unknown = TPresence>(
118
+ selector?: (myPresence: TPresence) => T,
119
+ options?: SubscriptionHookOptions<Id<T> | null>,
120
+ ) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
121
+ useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(
122
+ selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T,
123
+ options?: SubscriptionHookOptions<Id<T> | null>,
124
+ ) => Id<T> | null;
125
+ useOther: <T extends unknown = UserInfo<TIO, TPresence>>(
126
+ connectionId: string,
127
+ selector?: (other: UserInfo<TIO, TPresence>) => T,
128
+ options?: SubscriptionHookOptions<T | null>,
129
+ ) => T | null;
130
+ useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(
131
+ selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T,
132
+ options?: SubscriptionHookOptions<T>,
133
+ ) => T;
134
+ useRedo: () => () => void;
135
+ useRoom: () => RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>, TEvents>;
136
+ useStorage: <
137
+ TKey extends keyof InferStorage<TCrdt>,
138
+ TData extends unknown = InferCrdtJson<InferStorage<TCrdt>[TKey]>,
139
+ >(
140
+ key: TKey,
141
+ selector?: (data: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => TData,
142
+ options?: SubscriptionHookOptions<TData | null>,
143
+ ) => [data: TData | null, sharedType: InferStorage<TCrdt>[TKey] | null];
144
+ useTransact: () => (fn: (storage: InferStorage<TCrdt>) => void, origin?: string) => void;
145
+ useUndo: () => () => void;
146
+ }
147
+
148
+ export type InferBundleRoom<TBundle extends CreateBundle<any, any, any, any, any>> =
149
+ TBundle extends CreateBundle<infer IIO, any, infer IPresence, infer ICrdt, infer IEvents>
150
+ ? RoomLike<IIO, InferDoc<ICrdt>, IPresence, InferStorage<ICrdt>, IEvents>
151
+ : never;