@pluv/react 2.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/react@2.0.2 build /home/runner/work/pluv/pluv/packages/react
2
+ > @pluv/react@2.2.0 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.73 KB
12
- ESM ⚡️ Build success in 122ms
13
- CJS dist/index.js 19.10 KB
14
- CJS ⚡️ Build success in 124ms
11
+ ESM dist/index.mjs 17.27 KB
12
+ ESM ⚡️ Build success in 86ms
13
+ CJS dist/index.js 19.66 KB
14
+ CJS ⚡️ Build success in 86ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 6265ms
17
- DTS dist/index.d.mts 5.70 KB
18
- DTS dist/index.d.ts 5.70 KB
16
+ DTS ⚡️ Build success in 6218ms
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,81 @@
1
1
  # @pluv/react
2
2
 
3
+ ## 2.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3ce1547: Updated typings for `useDoc` to be narrower to get the native doc type.
8
+
9
+ ```ts
10
+ import { createClient } from "@pluv/client";
11
+ import { yjs } from "@pluv/crdt-yjs";
12
+ import { createBundle } from "@pluv/react";
13
+ import type { Doc as YDoc } from "yjs";
14
+
15
+ const client = createClient({
16
+ // ...
17
+ crdt: yjs,
18
+ });
19
+ const { useDoc } = createBundle(client);
20
+
21
+ const doc = useDoc();
22
+
23
+ // This is now typed as the native YDoc
24
+ doc.value;
25
+ // ^? const value: YDoc
26
+ ```
27
+
28
+ - b999c1c: Added a new type `InferBundleRoom` to get the type of the `PluvRoom` from the react bundle that is created.
29
+
30
+ ```ts
31
+ import { createClient } from "@pluv/client";
32
+ import type { InferBundleRoom } from "@pluv/react";
33
+ import { createBundle } from "@pluv/react";
34
+
35
+ const client = createclient({
36
+ /* ... */
37
+ });
38
+
39
+ const bundle = createBundle(client);
40
+
41
+ type RoomType = InferBundleRoom<typeof bundle>;
42
+
43
+ const { useStorage } = bundle;
44
+ ```
45
+
46
+ ### Patch Changes
47
+
48
+ - cb660cd: Fixed the room not changing if the room name was changed on the `PluvRoomProvider` without first unmounting the component.
49
+ - 4916c27: Fixed `useConnection` requiring a selector function (this is now optional).
50
+ - 48191fa: Fixed `useDoc` not returning a stable doc reference for a single connection.
51
+ - Updated dependencies [3ce1547]
52
+ - Updated dependencies [fd3bfe2]
53
+ - Updated dependencies [aee40cb]
54
+ - Updated dependencies [edd1789]
55
+ - Updated dependencies [48191fa]
56
+ - @pluv/client@2.2.0
57
+ - @pluv/crdt@2.2.0
58
+ - @pluv/types@2.2.0
59
+
60
+ ## 2.1.0
61
+
62
+ ### Minor Changes
63
+
64
+ - 8931f7b: Moved `@pluv/client` from `peerDependencies` to `dependencies` due to an unfortunate [changesets design decision](https://github.com/changesets/changesets/issues/1011).
65
+
66
+ ### Patch Changes
67
+
68
+ - e8d8fbc: Updated the internals of various hooks to use the new `PluvRoom.subscribe` object.
69
+ - Updated dependencies [e8d8fbc]
70
+ - Updated dependencies [a86ff5e]
71
+ - Updated dependencies [d5ac29e]
72
+ - Updated dependencies [5ca5cb6]
73
+ - Updated dependencies [4acbe1e]
74
+ - Updated dependencies [e8d8fbc]
75
+ - @pluv/client@2.1.0
76
+ - @pluv/crdt@2.1.0
77
+ - @pluv/types@2.1.0
78
+
3
79
  ## 2.0.2
4
80
 
5
81
  ### Patch 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,9 +272,14 @@ 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());
280
277
  (0, import_react6.useEffect)(() => {
281
- const unsubscribe = room.subscribe("connection", () => {
278
+ if (room.id === _room) return;
279
+ setRoom(createRoom());
280
+ }, [_room, createRoom, room]);
281
+ (0, import_react6.useEffect)(() => {
282
+ const unsubscribe = room.subscribe.connection(() => {
282
283
  rerender();
283
284
  });
284
285
  return () => {
@@ -351,7 +352,7 @@ var createBundle = (client, options = {}) => {
351
352
  const useCanRedo = () => {
352
353
  const room = useRoom();
353
354
  const subscribe = (0, import_react6.useCallback)(
354
- (onStoreChange) => room.storageRoot(onStoreChange),
355
+ (onStoreChange) => room.subscribe.storage(onStoreChange),
355
356
  [room]
356
357
  );
357
358
  const getSnapshot = (0, import_react6.useCallback)(() => room.canRedo(), [room]);
@@ -366,7 +367,7 @@ var createBundle = (client, options = {}) => {
366
367
  const useCanUndo = () => {
367
368
  const room = useRoom();
368
369
  const subscribe = (0, import_react6.useCallback)(
369
- (onStoreChange) => room.storageRoot(onStoreChange),
370
+ (onStoreChange) => room.subscribe.storage(onStoreChange),
370
371
  [room]
371
372
  );
372
373
  const getSnapshot = (0, import_react6.useCallback)(() => room.canUndo(), [room]);
@@ -383,7 +384,7 @@ var createBundle = (client, options = {}) => {
383
384
  const room = useRoom();
384
385
  const subscribe = (0, import_react6.useCallback)(
385
386
  (onStoreChange) => {
386
- return room.subscribe("connection", onStoreChange);
387
+ return room.subscribe.connection(onStoreChange);
387
388
  },
388
389
  [room]
389
390
  );
@@ -402,12 +403,19 @@ 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();
409
417
  (0, import_react6.useEffect)(() => {
410
- const unsubscribe = room.event(type, callback);
418
+ const unsubscribe = room.subscribe.event(type, callback);
411
419
  return () => {
412
420
  unsubscribe();
413
421
  };
@@ -432,7 +440,7 @@ var createBundle = (client, options = {}) => {
432
440
  const room = useRoom();
433
441
  const subscribe = (0, import_react6.useCallback)(
434
442
  (onStoreChange) => {
435
- return room.subscribe("my-presence", onStoreChange);
443
+ return room.subscribe.myPresence(onStoreChange);
436
444
  },
437
445
  [room]
438
446
  );
@@ -455,7 +463,7 @@ var createBundle = (client, options = {}) => {
455
463
  const room = useRoom();
456
464
  const subscribe = (0, import_react6.useCallback)(
457
465
  (onStoreChange) => {
458
- return room.subscribe("myself", onStoreChange);
466
+ return room.subscribe.myself(onStoreChange);
459
467
  },
460
468
  [room]
461
469
  );
@@ -478,9 +486,7 @@ var createBundle = (client, options = {}) => {
478
486
  var _a;
479
487
  const room = useRoom();
480
488
  const subscribe = (0, import_react6.useCallback)(
481
- (onStoreChange) => {
482
- return room.other(connectionId, onStoreChange);
483
- },
489
+ (onStoreChange) => room.subscribe.other(connectionId, onStoreChange),
484
490
  [room, connectionId]
485
491
  );
486
492
  const getSnapshot = (0, import_react6.useCallback)(() => room.getOther(connectionId), [room, connectionId]);
@@ -503,7 +509,7 @@ var createBundle = (client, options = {}) => {
503
509
  const room = useRoom();
504
510
  const subscribe = (0, import_react6.useCallback)(
505
511
  (onStoreChange) => {
506
- return room.subscribe("others", onStoreChange);
512
+ return room.subscribe.others(onStoreChange);
507
513
  },
508
514
  [room]
509
515
  );
@@ -529,11 +535,11 @@ var createBundle = (client, options = {}) => {
529
535
  var _a, _b;
530
536
  const room = useRoom();
531
537
  const rerender = useRerender();
532
- room.subscribe("storage-loaded", () => {
538
+ room.subscribe.storageLoaded(() => {
533
539
  rerender();
534
540
  });
535
541
  const subscribe = (0, import_react6.useCallback)(
536
- (onStoreChange) => room.storage(key, onStoreChange),
542
+ (onStoreChange) => room.subscribe.storage(key, onStoreChange),
537
543
  [key, room]
538
544
  );
539
545
  const getSnapshot = (0, import_react6.useCallback)(() => {
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,9 +248,14 @@ 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());
247
253
  useEffect3(() => {
248
- const unsubscribe = room.subscribe("connection", () => {
254
+ if (room.id === _room) return;
255
+ setRoom(createRoom());
256
+ }, [_room, createRoom, room]);
257
+ useEffect3(() => {
258
+ const unsubscribe = room.subscribe.connection(() => {
249
259
  rerender();
250
260
  });
251
261
  return () => {
@@ -318,7 +328,7 @@ var createBundle = (client, options = {}) => {
318
328
  const useCanRedo = () => {
319
329
  const room = useRoom();
320
330
  const subscribe = useCallback4(
321
- (onStoreChange) => room.storageRoot(onStoreChange),
331
+ (onStoreChange) => room.subscribe.storage(onStoreChange),
322
332
  [room]
323
333
  );
324
334
  const getSnapshot = useCallback4(() => room.canRedo(), [room]);
@@ -333,7 +343,7 @@ var createBundle = (client, options = {}) => {
333
343
  const useCanUndo = () => {
334
344
  const room = useRoom();
335
345
  const subscribe = useCallback4(
336
- (onStoreChange) => room.storageRoot(onStoreChange),
346
+ (onStoreChange) => room.subscribe.storage(onStoreChange),
337
347
  [room]
338
348
  );
339
349
  const getSnapshot = useCallback4(() => room.canUndo(), [room]);
@@ -350,7 +360,7 @@ var createBundle = (client, options = {}) => {
350
360
  const room = useRoom();
351
361
  const subscribe = useCallback4(
352
362
  (onStoreChange) => {
353
- return room.subscribe("connection", onStoreChange);
363
+ return room.subscribe.connection(onStoreChange);
354
364
  },
355
365
  [room]
356
366
  );
@@ -369,12 +379,19 @@ 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();
376
393
  useEffect3(() => {
377
- const unsubscribe = room.event(type, callback);
394
+ const unsubscribe = room.subscribe.event(type, callback);
378
395
  return () => {
379
396
  unsubscribe();
380
397
  };
@@ -399,7 +416,7 @@ var createBundle = (client, options = {}) => {
399
416
  const room = useRoom();
400
417
  const subscribe = useCallback4(
401
418
  (onStoreChange) => {
402
- return room.subscribe("my-presence", onStoreChange);
419
+ return room.subscribe.myPresence(onStoreChange);
403
420
  },
404
421
  [room]
405
422
  );
@@ -422,7 +439,7 @@ var createBundle = (client, options = {}) => {
422
439
  const room = useRoom();
423
440
  const subscribe = useCallback4(
424
441
  (onStoreChange) => {
425
- return room.subscribe("myself", onStoreChange);
442
+ return room.subscribe.myself(onStoreChange);
426
443
  },
427
444
  [room]
428
445
  );
@@ -445,9 +462,7 @@ var createBundle = (client, options = {}) => {
445
462
  var _a;
446
463
  const room = useRoom();
447
464
  const subscribe = useCallback4(
448
- (onStoreChange) => {
449
- return room.other(connectionId, onStoreChange);
450
- },
465
+ (onStoreChange) => room.subscribe.other(connectionId, onStoreChange),
451
466
  [room, connectionId]
452
467
  );
453
468
  const getSnapshot = useCallback4(() => room.getOther(connectionId), [room, connectionId]);
@@ -470,7 +485,7 @@ var createBundle = (client, options = {}) => {
470
485
  const room = useRoom();
471
486
  const subscribe = useCallback4(
472
487
  (onStoreChange) => {
473
- return room.subscribe("others", onStoreChange);
488
+ return room.subscribe.others(onStoreChange);
474
489
  },
475
490
  [room]
476
491
  );
@@ -496,11 +511,11 @@ var createBundle = (client, options = {}) => {
496
511
  var _a, _b;
497
512
  const room = useRoom();
498
513
  const rerender = useRerender();
499
- room.subscribe("storage-loaded", () => {
514
+ room.subscribe.storageLoaded(() => {
500
515
  rerender();
501
516
  });
502
517
  const subscribe = useCallback4(
503
- (onStoreChange) => room.storage(key, onStoreChange),
518
+ (onStoreChange) => room.subscribe.storage(key, onStoreChange),
504
519
  [key, room]
505
520
  );
506
521
  const getSnapshot = useCallback4(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "@pluv/client React.js bindings",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,23 +18,22 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "fast-deep-equal": "^3.1.3",
21
- "@pluv/crdt": "^2.0.2",
22
- "@pluv/types": "^2.0.2"
21
+ "@pluv/client": "^2.2.0",
22
+ "@pluv/crdt": "^2.2.0",
23
+ "@pluv/types": "^2.2.0"
23
24
  },
24
25
  "peerDependencies": {
25
26
  "@types/react": "^18.0.0 || ^19.0.0",
26
- "react": "^18.0.0 || ^19.0.0",
27
- "@pluv/client": "^2.0.2"
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/client": "^2.0.2",
36
- "@pluv/tsconfig": "^2.0.2",
37
- "eslint-config-pluv": "^2.0.2"
35
+ "@pluv/tsconfig": "^2.2.0",
36
+ "eslint-config-pluv": "^2.2.0"
38
37
  },
39
38
  "scripts": {
40
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,10 +172,18 @@ 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());
270
178
 
271
179
  useEffect(() => {
272
- const unsubscribe = room.subscribe("connection", () => {
180
+ if (room.id === _room) return;
181
+
182
+ setRoom(createRoom());
183
+ }, [_room, createRoom, room]);
184
+
185
+ useEffect(() => {
186
+ const unsubscribe = room.subscribe.connection(() => {
273
187
  rerender();
274
188
  });
275
189
 
@@ -378,7 +292,7 @@ export const createBundle = <
378
292
  const room = useRoom();
379
293
 
380
294
  const subscribe = useCallback(
381
- (onStoreChange: () => void) => room.storageRoot(onStoreChange),
295
+ (onStoreChange: () => void) => room.subscribe.storage(onStoreChange),
382
296
  [room],
383
297
  );
384
298
 
@@ -398,7 +312,7 @@ export const createBundle = <
398
312
  const room = useRoom();
399
313
 
400
314
  const subscribe = useCallback(
401
- (onStoreChange: () => void) => room.storageRoot(onStoreChange),
315
+ (onStoreChange: () => void) => room.subscribe.storage(onStoreChange),
402
316
  [room],
403
317
  );
404
318
 
@@ -422,7 +336,7 @@ export const createBundle = <
422
336
 
423
337
  const subscribe = useCallback(
424
338
  (onStoreChange: () => void) => {
425
- return room.subscribe("connection", onStoreChange);
339
+ return room.subscribe.connection(onStoreChange);
426
340
  },
427
341
  [room],
428
342
  );
@@ -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>>>(
@@ -456,7 +379,7 @@ export const createBundle = <
456
379
  const room = useRoom();
457
380
 
458
381
  useEffect(() => {
459
- const unsubscribe = room.event(type, callback);
382
+ const unsubscribe = room.subscribe.event(type, callback);
460
383
 
461
384
  return () => {
462
385
  unsubscribe();
@@ -497,7 +420,7 @@ export const createBundle = <
497
420
 
498
421
  const subscribe = useCallback(
499
422
  (onStoreChange: () => void) => {
500
- return room.subscribe("my-presence", onStoreChange);
423
+ return room.subscribe.myPresence(onStoreChange);
501
424
  },
502
425
  [room],
503
426
  );
@@ -528,7 +451,7 @@ export const createBundle = <
528
451
 
529
452
  const subscribe = useCallback(
530
453
  (onStoreChange: () => void) => {
531
- return room.subscribe("myself", onStoreChange);
454
+ return room.subscribe.myself(onStoreChange);
532
455
  },
533
456
  [room],
534
457
  );
@@ -559,9 +482,7 @@ export const createBundle = <
559
482
  const room = useRoom();
560
483
 
561
484
  const subscribe = useCallback(
562
- (onStoreChange: () => void) => {
563
- return room.other(connectionId, onStoreChange);
564
- },
485
+ (onStoreChange: () => void) => room.subscribe.other(connectionId, onStoreChange),
565
486
  [room, connectionId],
566
487
  );
567
488
 
@@ -591,7 +512,7 @@ export const createBundle = <
591
512
 
592
513
  const subscribe = useCallback(
593
514
  (onStoreChange: () => void) => {
594
- return room.subscribe("others", onStoreChange);
515
+ return room.subscribe.others(onStoreChange);
595
516
  },
596
517
  [room],
597
518
  );
@@ -639,12 +560,12 @@ export const createBundle = <
639
560
  const room = useRoom();
640
561
  const rerender = useRerender();
641
562
 
642
- room.subscribe("storage-loaded", () => {
563
+ room.subscribe.storageLoaded(() => {
643
564
  rerender();
644
565
  });
645
566
 
646
567
  const subscribe = useCallback(
647
- (onStoreChange: () => void) => room.storage(key, onStoreChange),
568
+ (onStoreChange: () => void) => room.subscribe.storage(key, onStoreChange),
648
569
  [key, room],
649
570
  );
650
571
 
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;