@pluv/react 0.26.0 → 0.27.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,18 +1,18 @@
1
1
 
2
- > @pluv/react@0.26.0 build /home/runner/work/pluv/pluv/packages/react
2
+ > @pluv/react@0.27.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
6
6
  CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.2.4
7
+ CLI tsup v8.3.0
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- CJS dist/index.js 14.30 KB
12
- CJS ⚡️ Build success in 127ms
13
- ESM dist/index.mjs 11.89 KB
14
- ESM ⚡️ Build success in 128ms
11
+ CJS dist/index.js 14.26 KB
12
+ CJS ⚡️ Build success in 101ms
13
+ ESM dist/index.mjs 11.85 KB
14
+ ESM ⚡️ Build success in 102ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 4697ms
17
- DTS dist/index.d.mts 5.50 KB
18
- DTS dist/index.d.ts 5.50 KB
16
+ DTS ⚡️ Build success in 5825ms
17
+ DTS dist/index.d.mts 6.12 KB
18
+ DTS dist/index.d.ts 6.12 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,125 @@
1
1
  # @pluv/react
2
2
 
3
+ ## 0.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 19ed36c: Updated `createClient` and `createRoomBundle` object params to better support automatic type inference.
8
+
9
+ ```ts
10
+ // Before
11
+ import { createClient } from "@pluv/react";
12
+ import { yjs } from "@pluv/crdt-yjs";
13
+ import { z } from "zod";
14
+ import type { ioServer } from "./server/ioServer";
15
+
16
+ const client = createClient<typeof ioServer>({
17
+ authEndpoint: ({ room }) => "MY_AUTH_URL",
18
+ wsEndpoint: ({ room }) => "MY_WEBSOCKET_URL",
19
+ });
20
+
21
+ client.createRoom("my-example-room", {
22
+ presence: z.object({
23
+ selectionId: z.string().nullish(),
24
+ }),
25
+ initialPresence: {
26
+ selectionId: null,
27
+ },
28
+ initialStorage: yjs.doc(() => ({
29
+ messages: yjs.array<string>([]),
30
+ })),
31
+ });
32
+
33
+ // After
34
+
35
+ import { createBundle, createClient } from "@pluv/react";
36
+
37
+ const client = createClient({
38
+ authEndpoint: ({ room }) => "MY_AUTH_URL",
39
+ wsEndpoint: ({ room }) => "MY_WEBSOCKET_URL",
40
+ /**
41
+ * @description The ioServer type now needs to be inferred on this property
42
+ * instead of within the generic on `createClient`. This is because of
43
+ * limitations with TypeScript not yet supporting partial type-inference
44
+ * for generics. Ensure to no-loonger include any generics on `createClient`
45
+ */
46
+ infer: (i) => ({ io: i<typeof ioServer> }),
47
+ // Now moved to `createClient` from `PluvClient.createRoom`
48
+ presence: z.object({
49
+ selectionId: z.string().nullish(),
50
+ }),
51
+ /**
52
+ * @description Now added to `createClient` to specify defaults for all
53
+ * rooms, and to infer types for storage. This value will be overwritten by
54
+ * `PluvClient.createRoom`.
55
+ */
56
+ initialStorage: yjs.doc(() => ({
57
+ messages: yjs.array<string>([]),
58
+ })),
59
+ });
60
+
61
+ const { createRoomBundle } = createBundle(client);
62
+ const pluv = createRoomBundle({});
63
+ ```
64
+
65
+ ### Patch Changes
66
+
67
+ - 19ed36c: Added client-side event router. This more-or-less mirrors the server-side event router. You can use both the server-side and client-side router simultaneously, but you should generally prefer only using one or the other.
68
+
69
+ ```ts
70
+ // Backend Server
71
+ import { createIO } from "@pluv/io";
72
+
73
+ const io = createIO({ /* ... */ });
74
+ const ioServer = io.server({
75
+ router: io.router({
76
+ multiply2: io.procedure
77
+ .input(z.object({ value: z.number() }))
78
+ .broadcast(({ value }) => ({
79
+ multiplied2: { value: value * 2 },
80
+ })),
81
+ }),
82
+ });
83
+
84
+ // Frontend Client
85
+ import { createBundle createClient } from "@pluv/react";
86
+
87
+ const io = createClient({ /* ... */ });
88
+
89
+ const { createRoomBundle } = createBundle(io);
90
+ const pluv = createRoomBundle({
91
+ // ...
92
+ router: io.router({
93
+ add5: io.procedure
94
+ .input(z.object({ value: z.number() }))
95
+ .broadcast(({ value }) => ({
96
+ /**
97
+ * @description Because `ioServer` does not specify `added5`,
98
+ * this will be forwarded to connected peers.
99
+ */
100
+ added5: { value: value + 5 },
101
+ })),
102
+ /**
103
+ * @description This is possible, but not recommended to do. Because
104
+ * `multiply2` is a procedure on `ioServer`, this will trigger the
105
+ * `multiply2` procedure on `ioServer`. Connected peers will not
106
+ * receive `multiply2`, but rather `multipled2`.
107
+ */
108
+ subtract5Multiply2: io.procedure
109
+ .input(z.object({ value: z.number() }))
110
+ .broadcast(({ value }) => ({
111
+ multiply2: { value: value - 5 },
112
+ })),
113
+ }),
114
+ });
115
+ ```
116
+
117
+ - Updated dependencies [19ed36c]
118
+ - Updated dependencies [19ed36c]
119
+ - @pluv/client@0.27.0
120
+ - @pluv/crdt@0.27.0
121
+ - @pluv/types@0.27.0
122
+
3
123
  ## 0.26.0
4
124
 
5
125
  ### Minor Changes
package/dist/index.d.mts CHANGED
@@ -1,15 +1,15 @@
1
- import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
1
+ import { PluvRouterEventConfig, PluvRoomAddon, PluvRouter, PluvRoomDebug, MergeEvents, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { ConnectionState, createClient } from '@pluv/client';
3
3
  import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
4
4
  export { CrdtType, InferCrdtJson } from '@pluv/crdt';
5
- import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
5
+ import { IOLike, JsonObject, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
6
6
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
7
7
  import { FC, Dispatch, ReactNode } from 'react';
8
8
 
9
- type CreateRoomBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
9
+ type CreateRoomBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = {
10
10
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
11
11
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
12
- presence?: InputZodLike<TPresence>;
12
+ router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
13
13
  };
14
14
  type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
15
15
  children?: ReactNode;
@@ -20,8 +20,8 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
20
20
  } : {
21
21
  initialPresence: TPresence;
22
22
  });
23
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
24
- events?: MockedRoomEvents<TIO>;
23
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = BaseRoomProviderProps<TPresence, TStorage> & {
24
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
25
25
  };
26
26
  type PluvRoomProviderProps<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
27
27
  debug?: boolean | PluvRoomDebug<TIO>;
@@ -43,16 +43,16 @@ type EventProxy<TIO extends IOLike> = {
43
43
  useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
44
44
  };
45
45
  };
46
- interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
46
+ interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> {
47
47
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
48
48
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>;
49
- event: EventProxy<TIO>;
50
- useBroadcast: () => BroadcastProxy<TIO>;
49
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
50
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
51
51
  useCanRedo: () => boolean;
52
52
  useCanUndo: () => boolean;
53
53
  useConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
54
54
  useDoc: () => AbstractCrdtDoc<TStorage>;
55
- useEvent: <TType extends keyof InferIOOutput<TIO>>(type: TType, callback: (data: Id<IOEventMessage<TIO, TType>>) => void) => void;
55
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(type: TType, callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void) => void;
56
56
  useMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
57
57
  useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
58
58
  useOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<T | null>) => T | null;
@@ -63,16 +63,16 @@ interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPr
63
63
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
64
64
  useUndo: () => () => void;
65
65
  }
66
- type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
66
+ type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
67
67
 
68
68
  interface PluvProviderProps {
69
69
  children?: ReactNode;
70
70
  }
71
- interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject> {
72
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage>) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage>;
71
+ interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
72
+ createRoomBundle: <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents>;
73
73
  PluvProvider: FC<PluvProviderProps>;
74
- useClient: () => PluvClient<TIO, TMetadata>;
74
+ useClient: () => PluvClient<TIO, TPresence, TStorage, TMetadata>;
75
75
  }
76
- declare const createBundle: <TIO extends IOLike, TMetadata extends JsonObject>(client: PluvClient<TIO, TMetadata>) => CreateBundle<TIO, TMetadata>;
76
+ declare const createBundle: <TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(client: PluvClient<TIO, TPresence, TStorage, TMetadata>) => CreateBundle<TIO, TMetadata, TPresence, TStorage>;
77
77
 
78
78
  export { type CreateBundle, type CreateRoomBundle, type CreateRoomBundleOptions, type InferRoomStorage, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
1
+ import { PluvRouterEventConfig, PluvRoomAddon, PluvRouter, PluvRoomDebug, MergeEvents, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { ConnectionState, createClient } from '@pluv/client';
3
3
  import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
4
4
  export { CrdtType, InferCrdtJson } from '@pluv/crdt';
5
- import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
5
+ import { IOLike, JsonObject, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
6
6
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
7
7
  import { FC, Dispatch, ReactNode } from 'react';
8
8
 
9
- type CreateRoomBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
9
+ type CreateRoomBundleOptions<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = {
10
10
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
11
11
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
12
- presence?: InputZodLike<TPresence>;
12
+ router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
13
13
  };
14
14
  type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
15
15
  children?: ReactNode;
@@ -20,8 +20,8 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
20
20
  } : {
21
21
  initialPresence: TPresence;
22
22
  });
23
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
24
- events?: MockedRoomEvents<TIO>;
23
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = BaseRoomProviderProps<TPresence, TStorage> & {
24
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
25
25
  };
26
26
  type PluvRoomProviderProps<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
27
27
  debug?: boolean | PluvRoomDebug<TIO>;
@@ -43,16 +43,16 @@ type EventProxy<TIO extends IOLike> = {
43
43
  useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
44
44
  };
45
45
  };
46
- interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
46
+ interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> {
47
47
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
48
48
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>;
49
- event: EventProxy<TIO>;
50
- useBroadcast: () => BroadcastProxy<TIO>;
49
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
50
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
51
51
  useCanRedo: () => boolean;
52
52
  useCanUndo: () => boolean;
53
53
  useConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
54
54
  useDoc: () => AbstractCrdtDoc<TStorage>;
55
- useEvent: <TType extends keyof InferIOOutput<TIO>>(type: TType, callback: (data: Id<IOEventMessage<TIO, TType>>) => void) => void;
55
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(type: TType, callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void) => void;
56
56
  useMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [myPresence: Id<T>, updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>];
57
57
  useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
58
58
  useOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<T | null>) => T | null;
@@ -63,16 +63,16 @@ interface CreateRoomBundle<TIO extends IOLike, TMetadata extends JsonObject, TPr
63
63
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
64
64
  useUndo: () => () => void;
65
65
  }
66
- type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
66
+ type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any, any>> = TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
67
67
 
68
68
  interface PluvProviderProps {
69
69
  children?: ReactNode;
70
70
  }
71
- interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject> {
72
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage>) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage>;
71
+ interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
72
+ createRoomBundle: <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents>;
73
73
  PluvProvider: FC<PluvProviderProps>;
74
- useClient: () => PluvClient<TIO, TMetadata>;
74
+ useClient: () => PluvClient<TIO, TPresence, TStorage, TMetadata>;
75
75
  }
76
- declare const createBundle: <TIO extends IOLike, TMetadata extends JsonObject>(client: PluvClient<TIO, TMetadata>) => CreateBundle<TIO, TMetadata>;
76
+ declare const createBundle: <TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(client: PluvClient<TIO, TPresence, TStorage, TMetadata>) => CreateBundle<TIO, TMetadata, TPresence, TStorage>;
77
77
 
78
78
  export { type CreateBundle, type CreateRoomBundle, type CreateRoomBundleOptions, type InferRoomStorage, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.js CHANGED
@@ -122,8 +122,7 @@ var createRoomBundle = (client, options) => {
122
122
  return new import_client.MockedRoom(_room, {
123
123
  events,
124
124
  initialPresence,
125
- initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
126
- presence: options.presence
125
+ initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage
127
126
  });
128
127
  });
129
128
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MockedRoomContext.Provider, { value: { room }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PluvRoomContext.Provider, { value: room, children }) });
@@ -140,8 +139,8 @@ var createRoomBundle = (client, options) => {
140
139
  initialPresence,
141
140
  initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
142
141
  metadata,
143
- presence: options.presence,
144
- onAuthorizationFail
142
+ onAuthorizationFail,
143
+ router: options.router
145
144
  });
146
145
  });
147
146
  (0, import_react4.useEffect)(() => {
package/dist/index.mjs CHANGED
@@ -86,8 +86,7 @@ var createRoomBundle = (client, options) => {
86
86
  return new MockedRoom(_room, {
87
87
  events,
88
88
  initialPresence,
89
- initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
90
- presence: options.presence
89
+ initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage
91
90
  });
92
91
  });
93
92
  return /* @__PURE__ */ jsx(MockedRoomContext.Provider, { value: { room }, children: /* @__PURE__ */ jsx(PluvRoomContext.Provider, { value: room, children }) });
@@ -104,8 +103,8 @@ var createRoomBundle = (client, options) => {
104
103
  initialPresence,
105
104
  initialStorage: typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
106
105
  metadata,
107
- presence: options.presence,
108
- onAuthorizationFail
106
+ onAuthorizationFail,
107
+ router: options.router
109
108
  });
110
109
  });
111
110
  useEffect2(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
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": "^0.26.0",
22
- "@pluv/crdt": "^0.26.0",
23
- "@pluv/types": "^0.26.0"
21
+ "@pluv/crdt": "^0.27.0",
22
+ "@pluv/types": "^0.27.0",
23
+ "@pluv/client": "^0.27.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@types/react": "^18.0.0",
27
27
  "react": "^18.0.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/react": "^18.3.4",
30
+ "@types/react": "^18.3.6",
31
31
  "eslint": "^8.57.0",
32
32
  "react": "^18.3.1",
33
- "tsup": "^8.2.4",
34
- "typescript": "^5.5.4",
35
- "@pluv/tsconfig": "^0.26.0",
36
- "eslint-config-pluv": "^0.26.0"
33
+ "tsup": "^8.3.0",
34
+ "typescript": "^5.6.2",
35
+ "@pluv/tsconfig": "^0.27.0",
36
+ "eslint-config-pluv": "^0.27.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup src/index.ts --format esm,cjs --dts --external react",
@@ -1,4 +1,4 @@
1
- import type { PluvClient } from "@pluv/client";
1
+ import type { PluvClient, PluvRouterEventConfig } from "@pluv/client";
2
2
  import type { CrdtType } from "@pluv/crdt";
3
3
  import type { IOLike, JsonObject } from "@pluv/types";
4
4
  import type { FC, ReactNode } from "react";
@@ -10,22 +10,32 @@ export interface PluvProviderProps {
10
10
  children?: ReactNode;
11
11
  }
12
12
 
13
- export interface CreateBundle<TIO extends IOLike, TMetadata extends JsonObject> {
13
+ export interface CreateBundle<
14
+ TIO extends IOLike,
15
+ TMetadata extends JsonObject,
16
+ TPresence extends JsonObject = {},
17
+ TStorage extends Record<string, CrdtType<any, any>> = {},
18
+ > {
14
19
  // factories
15
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
16
- options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage>,
17
- ) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage>;
20
+ createRoomBundle: <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(
21
+ options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>,
22
+ ) => CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents>;
18
23
 
19
24
  // components
20
25
  PluvProvider: FC<PluvProviderProps>;
21
26
 
22
27
  // hooks
23
- useClient: () => PluvClient<TIO, TMetadata>;
28
+ useClient: () => PluvClient<TIO, TPresence, TStorage, TMetadata>;
24
29
  }
25
30
 
26
- export const createBundle = <TIO extends IOLike, TMetadata extends JsonObject>(
27
- client: PluvClient<TIO, TMetadata>,
28
- ): CreateBundle<TIO, TMetadata> => {
31
+ export const createBundle = <
32
+ TIO extends IOLike,
33
+ TMetadata extends JsonObject = {},
34
+ TPresence extends JsonObject = {},
35
+ TStorage extends Record<string, CrdtType<any, any>> = {},
36
+ >(
37
+ client: PluvClient<TIO, TPresence, TStorage, TMetadata>,
38
+ ): CreateBundle<TIO, TMetadata, TPresence, TStorage> => {
29
39
  /**
30
40
  * !HACK
31
41
  * @description We'll let the context error out if client is not provided,
@@ -33,7 +43,7 @@ export const createBundle = <TIO extends IOLike, TMetadata extends JsonObject>(
33
43
  * @author leedavidcs
34
44
  * @date October 27, 2022
35
45
  */
36
- const PluvContext = createContext<PluvClient<TIO, TMetadata>>(null as any);
46
+ const PluvContext = createContext<PluvClient<TIO, TPresence, TStorage, TMetadata>>(null as any);
37
47
 
38
48
  const PluvProvider = memo<PluvProviderProps>((props) => {
39
49
  const { children } = props;
@@ -43,14 +53,11 @@ export const createBundle = <TIO extends IOLike, TMetadata extends JsonObject>(
43
53
 
44
54
  PluvProvider.displayName = "PluvProvider";
45
55
 
46
- const useClient = (): PluvClient<TIO, TMetadata> => useContext(PluvContext);
56
+ const useClient = (): PluvClient<TIO, TPresence, TStorage, TMetadata> => useContext(PluvContext);
47
57
 
48
- const createRoomBundle = <
49
- TPresence extends JsonObject = {},
50
- TStorage extends Record<string, CrdtType<any, any>> = {},
51
- >(
52
- options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage>,
53
- ): CreateRoomBundle<TIO, TMetadata, TPresence, TStorage> => _createRoomBundle(client, options);
58
+ const createRoomBundle = <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(
59
+ options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>,
60
+ ): CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents> => _createRoomBundle(client, options);
54
61
 
55
62
  return {
56
63
  createRoomBundle,
@@ -1,18 +1,21 @@
1
1
  import type {
2
2
  AbstractRoom,
3
+ CreateRoomOptions,
4
+ MergeEvents,
3
5
  MockedRoomEvents,
4
6
  PluvClient,
5
7
  PluvRoom,
6
8
  PluvRoomAddon,
7
9
  PluvRoomDebug,
8
- PluvRoomOptions,
10
+ PluvRouter,
11
+ PluvRouterEventConfig,
9
12
  UserInfo,
10
13
  WebSocketConnection,
11
14
  } from "@pluv/client";
12
15
  import { MockedRoom } from "@pluv/client";
13
16
  import type { AbstractCrdtDoc, AbstractCrdtDocFactory, CrdtType, InferCrdtJson } from "@pluv/crdt";
14
17
  import { noop } from "@pluv/crdt";
15
- import type { IOEventMessage, IOLike, Id, InferIOInput, InferIOOutput, InputZodLike, JsonObject } from "@pluv/types";
18
+ import type { IOEventMessage, IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
16
19
  import fastDeepEqual from "fast-deep-equal";
17
20
  import type { Dispatch, FC, ReactNode } from "react";
18
21
  import { createContext, memo, useCallback, useContext, useEffect, useMemo, useState } from "react";
@@ -23,10 +26,11 @@ export type CreateRoomBundleOptions<
23
26
  TMetadata extends JsonObject = {},
24
27
  TPresence extends JsonObject = {},
25
28
  TStorage extends Record<string, CrdtType<any, any>> = {},
29
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
26
30
  > = {
27
31
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
28
32
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
29
- presence?: InputZodLike<TPresence>;
33
+ router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
30
34
  };
31
35
 
32
36
  type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
@@ -39,8 +43,9 @@ export type MockedRoomProviderProps<
39
43
  TIO extends IOLike,
40
44
  TPresence extends JsonObject,
41
45
  TStorage extends Record<string, CrdtType<any, any>>,
46
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
42
47
  > = BaseRoomProviderProps<TPresence, TStorage> & {
43
- events?: MockedRoomEvents<TIO>;
48
+ events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
44
49
  };
45
50
 
46
51
  export type PluvRoomProviderProps<
@@ -79,16 +84,17 @@ export interface CreateRoomBundle<
79
84
  TMetadata extends JsonObject,
80
85
  TPresence extends JsonObject,
81
86
  TStorage extends Record<string, CrdtType<any, any>>,
87
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
82
88
  > {
83
89
  // components
84
90
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
85
91
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TMetadata, TPresence, TStorage>>;
86
92
 
87
93
  // proxies
88
- event: EventProxy<TIO>;
94
+ event: EventProxy<MergeEvents<TEvents, TIO>>;
89
95
 
90
96
  // hooks
91
- useBroadcast: () => BroadcastProxy<TIO>;
97
+ useBroadcast: () => BroadcastProxy<MergeEvents<TEvents, TIO>>;
92
98
  useCanRedo: () => boolean;
93
99
  useCanUndo: () => boolean;
94
100
  useConnection: <T extends unknown = WebSocketConnection>(
@@ -96,9 +102,9 @@ export interface CreateRoomBundle<
96
102
  options?: SubscriptionHookOptions<Id<T>>,
97
103
  ) => Id<T>;
98
104
  useDoc: () => AbstractCrdtDoc<TStorage>;
99
- useEvent: <TType extends keyof InferIOOutput<TIO>>(
105
+ useEvent: <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
100
106
  type: TType,
101
- callback: (data: Id<IOEventMessage<TIO, TType>>) => void,
107
+ callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,
102
108
  ) => void;
103
109
  useMyPresence: <T extends unknown = TPresence>(
104
110
  selector?: (myPresence: TPresence) => T,
@@ -128,7 +134,7 @@ export interface CreateRoomBundle<
128
134
  useUndo: () => () => void;
129
135
  }
130
136
 
131
- export type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any>> =
137
+ export type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any, any, any>> =
132
138
  TRoomBundle extends CreateRoomBundle<any, any, any, infer IStorage> ? IStorage : never;
133
139
 
134
140
  export const createRoomBundle = <
@@ -136,10 +142,11 @@ export const createRoomBundle = <
136
142
  TMetadata extends JsonObject = {},
137
143
  TPresence extends JsonObject = {},
138
144
  TStorage extends Record<string, CrdtType<any, any>> = {},
145
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
139
146
  >(
140
- client: PluvClient<TIO, TMetadata>,
141
- options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage>,
142
- ): CreateRoomBundle<TIO, TMetadata, TPresence, TStorage> => {
147
+ client: PluvClient<TIO, TPresence, TStorage, TMetadata>,
148
+ options: CreateRoomBundleOptions<TIO, TMetadata, TPresence, TStorage, TEvents>,
149
+ ): CreateRoomBundle<TIO, TMetadata, TPresence, TStorage, TEvents> => {
143
150
  const _initialStorage = (options.initialStorage ?? noop.doc()) as AbstractCrdtDocFactory<TStorage>;
144
151
 
145
152
  /**
@@ -158,13 +165,12 @@ export const createRoomBundle = <
158
165
  const MockedRoomProvider = memo<MockedRoomProviderProps<TIO, TPresence, TStorage>>((props) => {
159
166
  const { children, events, initialPresence, initialStorage, room: _room } = props;
160
167
 
161
- const [room] = useState<MockedRoom<TIO, TPresence, TStorage>>(() => {
162
- return new MockedRoom<TIO, TPresence, TStorage>(_room, {
168
+ const [room] = useState<MockedRoom<TIO, TPresence, TStorage, TEvents>>(() => {
169
+ return new MockedRoom<TIO, TPresence, TStorage, TEvents>(_room, {
163
170
  events,
164
171
  initialPresence,
165
172
  initialStorage:
166
173
  typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
167
- presence: options.presence,
168
174
  });
169
175
  });
170
176
 
@@ -183,17 +189,17 @@ export const createRoomBundle = <
183
189
  const rerender = useRerender();
184
190
  const { room: mockedRoom } = useContext(MockedRoomContext);
185
191
 
186
- const [room] = useState<PluvRoom<TIO, TMetadata, TPresence, TStorage>>(() => {
187
- return client.createRoom<TPresence, TStorage>(_room, {
192
+ const [room] = useState<PluvRoom<TIO, TMetadata, TPresence, TStorage, TEvents>>(() => {
193
+ return client.createRoom(_room, {
188
194
  addons: options.addons,
189
195
  debug,
190
196
  initialPresence,
191
197
  initialStorage:
192
198
  typeof initialStorage === "function" ? _initialStorage.getFactory(initialStorage) : _initialStorage,
193
199
  metadata,
194
- presence: options.presence,
195
200
  onAuthorizationFail,
196
- } as PluvRoomOptions<TIO, TMetadata, TPresence, TStorage>);
201
+ router: options.router,
202
+ } as CreateRoomOptions<TIO, TPresence, TStorage, TMetadata, TEvents>);
197
203
  });
198
204
 
199
205
  useEffect(() => {
@@ -221,24 +227,31 @@ export const createRoomBundle = <
221
227
 
222
228
  const useRoom = () => useContext(PluvRoomContext);
223
229
 
224
- const useBroadcast = (): BroadcastProxy<TIO> => {
230
+ const useBroadcast = (): BroadcastProxy<MergeEvents<TEvents, TIO>> => {
225
231
  const room = useRoom();
226
232
 
227
233
  const broadcast = useCallback(
228
- <TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => {
234
+ <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
235
+ event: TEvent,
236
+ data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
237
+ ) => {
229
238
  room.broadcast(event, data);
230
239
  },
231
240
  [room],
232
241
  );
233
242
 
234
- return useMemo((): BroadcastProxy<TIO> => {
243
+ return useMemo((): BroadcastProxy<MergeEvents<TEvents, TIO>> => {
235
244
  return new Proxy(broadcast, {
236
245
  get(fn, prop) {
237
- return (data: Id<InferIOInput<TIO>[keyof InferIOInput<TIO>]>): void => {
238
- return fn(prop as keyof InferIOInput<TIO>, data);
246
+ return (
247
+ data: Id<
248
+ InferIOInput<MergeEvents<TEvents, TIO>>[keyof InferIOInput<MergeEvents<TEvents, TIO>>]
249
+ >,
250
+ ): void => {
251
+ return fn(prop as keyof InferIOInput<MergeEvents<TEvents, TIO>>, data);
239
252
  };
240
253
  },
241
- }) as BroadcastProxy<TIO>;
254
+ }) as BroadcastProxy<MergeEvents<TEvents, TIO>>;
242
255
  }, [broadcast]);
243
256
  };
244
257
 
@@ -298,9 +311,9 @@ export const createRoomBundle = <
298
311
  return room.getDoc();
299
312
  };
300
313
 
301
- const useEvent = <TType extends keyof InferIOOutput<TIO>>(
314
+ const useEvent = <TType extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
302
315
  type: TType,
303
- callback: (data: Id<IOEventMessage<TIO, TType>>) => void,
316
+ callback: (data: Id<IOEventMessage<MergeEvents<TEvents, TIO>, TType>>) => void,
304
317
  ): void => {
305
318
  const room = useRoom();
306
319
 
@@ -318,15 +331,19 @@ export const createRoomBundle = <
318
331
  {
319
332
  get(_, prop) {
320
333
  const useProxyEvent = (
321
- callback: (data: Id<IOEventMessage<TIO, keyof InferIOOutput<TIO>>>) => void,
334
+ callback: (
335
+ data: Id<
336
+ IOEventMessage<MergeEvents<TEvents, TIO>, keyof InferIOOutput<MergeEvents<TEvents, TIO>>>
337
+ >,
338
+ ) => void,
322
339
  ): void => {
323
- return useEvent(prop as keyof InferIOOutput<TIO>, callback);
340
+ return useEvent(prop as keyof InferIOOutput<MergeEvents<TEvents, TIO>>, callback);
324
341
  };
325
342
 
326
343
  return { useEvent: useProxyEvent };
327
344
  },
328
345
  },
329
- ) as EventProxy<TIO>;
346
+ ) as EventProxy<MergeEvents<TEvents, TIO>>;
330
347
 
331
348
  const useMyPresence = <T extends unknown = TPresence>(
332
349
  selector = identity as (myPresence: TPresence) => T,
@@ -26,7 +26,7 @@ export const useSyncExternalStoreWithSelector = <TSnapshot, TSelection>(
26
26
  * @description Memoize only the last snapshot so that we can only rerender on changes
27
27
  * @date June 22, 2024
28
28
  */
29
- cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
29
+ cacheRef.current = isUnchanged ? (cached ?? snapshot) : snapshot;
30
30
 
31
31
  return cacheRef.current;
32
32
  }
@@ -45,7 +45,7 @@ export const useSyncExternalStoreWithSelector = <TSnapshot, TSelection>(
45
45
  * @description Memoize only the last snapshot so that we can only rerender on changes
46
46
  * @date June 22, 2024
47
47
  */
48
- cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
48
+ cacheRef.current = isUnchanged ? (cached ?? snapshot) : snapshot;
49
49
 
50
50
  return cacheRef.current;
51
51
  };
@@ -1,2 +1,2 @@
1
- export { useRerender, useSyncExternalStore, useSyncExternalStoreWithSelector } from "./hooks";
1
+ export { useRerender, useSyncExternalStoreWithSelector } from "./hooks";
2
2
  export { identity, shallowArrayEqual } from "./utils";