@pluv/react 0.19.0 → 0.21.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.19.0 build /home/runner/work/pluv/pluv/packages/react
2
+ > @pluv/react@0.21.0 build /home/runner/work/pluv/pluv/packages/react
3
3
  > tsup src/index.ts --format esm,cjs --dts --external react
4
4
 
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.1.0
8
- CLI Target: es6
9
- ESM Build start
10
- CJS Build start
11
- ESM dist/index.mjs 11.61 KB
12
- ESM ⚡️ Build success in 85ms
13
- CJS dist/index.js 14.02 KB
14
- CJS ⚡️ Build success in 86ms
15
- DTS Build start
16
- DTS ⚡️ Build success in 4322ms
17
- DTS dist/index.d.mts 5.35 KB
18
- DTS dist/index.d.ts 5.35 KB
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.2.4
8
+ CLI Target: es6
9
+ ESM Build start
10
+ CJS Build start
11
+ ESM dist/index.mjs 11.87 KB
12
+ ESM ⚡️ Build success in 103ms
13
+ CJS dist/index.js 14.27 KB
14
+ CJS ⚡️ Build success in 106ms
15
+ DTS Build start
16
+ DTS ⚡️ Build success in 4500ms
17
+ DTS dist/index.d.mts 5.17 KB
18
+ DTS dist/index.d.ts 5.17 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,92 @@
1
1
  # @pluv/react
2
2
 
3
+ ## 0.21.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [b98ab6b]
8
+ - @pluv/client@0.21.0
9
+ - @pluv/crdt@0.21.0
10
+ - @pluv/types@0.21.0
11
+
12
+ ## 0.20.0
13
+
14
+ ### Minor Changes
15
+
16
+ - 9492085: **BREAKING**: `@pluv/crdt-yjs` and `@pluv/crdt-loro` have been updated so that the utilities to create shared-types/containers no-longer return a wrapper around the underlying shared-types and containers, but rather return the shared-types/containers directly.
17
+
18
+ This means that for methods such as `getStorage` from `@pluv/client` and `useStorage` from `@pluv/react`, the shared-types/containers are also returned instead of the wrapper types.
19
+
20
+ The motivation for these changes are so that pluv.io is supplementary to `yjs` and `loro-crdt`, instead of having these libraries be an internal implementation of pluv.io.
21
+
22
+ ```ts
23
+ // Before
24
+
25
+ import { yjs } from "@pluv/crdt-yjs";
26
+
27
+ yjs.array([]); // Returns CrdtYjsArray
28
+ yjs.object([]); // Returns CrdtYjsObject
29
+
30
+ const room: PluvRoom = /* ... */;
31
+
32
+ room.getStorage("messages"); // Returns AbstractCrdtType
33
+
34
+ const [, sharedType] = useStorage("messages"); // sharedType is an AbstractCrdtType
35
+ ```
36
+
37
+ ```ts
38
+ // Now
39
+
40
+ import { yjs } from "@pluv/crdt-yjs";
41
+
42
+ yjs.array([]); // Returns yjs.Array
43
+ yjs.object([]); // Returns yjs.Map
44
+
45
+ const room: PluvRoom = /* ... */;
46
+
47
+ room.getStorage("messages"); // Returns yjs.AbstractType
48
+
49
+ const [, sharedType] = useStorage("messages"); // sharedType is a yjs.AbstractType
50
+ ```
51
+
52
+ For `@pluv/crdt-loro` specifically, `@pluv/client` relies on [loro events and subscriptions](https://www.loro.dev/docs/tutorial/get_started#event) to detect changes. The `AbstractLoroCrdt` types previously called `Loro.commit` after each change as an abstraction, but now this no-longer happens. To ensure that changes to loro containers are properly handled in `@pluv/react`, make sure to commit your changes whenever possible:
53
+
54
+ ```ts
55
+ // Before
56
+
57
+ const [data, container] = useStorage("messages");
58
+
59
+ // This automatically called Loro.commit under the hood and rerendered the page with updated data.
60
+ container.push(loro.object({ name: "John Doe", age: 35 }));
61
+ ```
62
+
63
+ ```ts
64
+ // Now
65
+
66
+ const [data, container] = useStorage("messages");
67
+
68
+ const doc = useDoc();
69
+ const transact = useTransact();
70
+
71
+ // Updates need to be committed back to the doc whenever the changes need to be emitted to other users
72
+ // The returned data also will not update until the changes are commited
73
+
74
+ // The two operations below are functionally equivalent
75
+ transact(() => {
76
+ container.push(loro.object({ name: "John Doe", age: 35 }));
77
+ });
78
+
79
+ container.push(loro.object({ name: "John Doe", age: 35 }));
80
+ doc.value.commit();
81
+ ```
82
+
83
+ ### Patch Changes
84
+
85
+ - Updated dependencies [9492085]
86
+ - @pluv/client@0.20.0
87
+ - @pluv/crdt@0.20.0
88
+ - @pluv/types@0.20.0
89
+
3
90
  ## 0.19.0
4
91
 
5
92
  ### Minor Changes
package/README.md CHANGED
@@ -1,30 +1,3 @@
1
- > **Disclaimer:**
2
- > This package is currently in preview and may have breaking changes between versions. Please wait for a `v1.0.0` stable release before using this in production.
3
-
4
- <h1 align="center">
5
- <br />
6
- <img src="https://github.com/pluv-io/pluv/blob/master/assets/pluv-icon-192x192.png?raw=true" alt="Pluv.IO" width="180" style="border-radius:16px" />
7
- <br />
8
- <a href="https://pluv.io/docs/introduction">Pluv.IO (preview)</a>
9
- <br />
10
- </h1>
11
-
12
- <h3 align="center">Multi-platform, E2E type-safe realtime packages</h3>
13
- <h4 align="center">💕 Inspired by <a href="https://trpc.io">trpc</a> 💕 <a href="https://docs.yjs.dev/">yjs</a> 💕 and <a href="https://developers.cloudflare.com/">Cloudflare</a> 💕 </h4>
14
-
15
- <p align="center">
16
- <a href="https://www.npmjs.com/package/@pluv/react">
17
- <img src="https://img.shields.io/npm/v/@pluv/react" alt="npm @pluv/react" />
18
- </a>
19
- <a href="https://github.com/pluv-io/pluv/blob/master/LICENSE">
20
- <img alt="GitHub" src="https://img.shields.io/github/license/pluv-io/pluv" alt="License MIT" />
21
- </a>
22
- <a href="https://commitizen.github.io/cz-cli/">
23
- <img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="Commitizen friendly" />
24
- </a>
25
- <img src="https://badgen.net/badge/-/TypeScript?icon=typescript&label&labelColor=blue&color=555555" alt="TypeScript" />
26
- </p>
27
-
28
1
  ## `@pluv/react`
29
2
 
30
3
  > [React.js](https://reactjs.org/) bindings for [@pluv/client](https://www.npmjs.com/package/@pluv/client).
@@ -43,114 +16,3 @@ yarn add @pluv/react @pluv/crdt-yjs yjs zod
43
16
  # pnpm
44
17
  pnpm add @pluv/react @pluv/crdt-yjs yjs zod
45
18
  ```
46
-
47
- ## Basic Example
48
-
49
- ```tsx
50
- import { yjs } from "@pluv/crdt-yjs";
51
- import { createBundle, createClient } from "@pluv/react";
52
- import { z } from "zod";
53
- // Import the PluvIO instance as a type from your server file
54
- import { type io } from "./server";
55
-
56
- // Initialize the @pluv/client client
57
- const client = createClient<typeof io>({
58
- authEndpoint: (room) => {
59
- return `https://localhost:3000/api/authorize?room=${room}`;
60
- },
61
- wsEndpoint: (room) => {
62
- return `ws://localhost:3000/api/room/${room}`;
63
- },
64
- });
65
-
66
- export const {
67
- // factories
68
- createRoomBundle,
69
-
70
- // components
71
- PluvProvider,
72
-
73
- // hooks
74
- useClient,
75
- } = createBundle(client);
76
-
77
- export const {
78
- // components
79
- MockedRoomProvider,
80
- PluvRoomProvider,
81
-
82
- // utils
83
- event,
84
-
85
- // hooks
86
- useBroadcast,
87
- useCanRedo,
88
- useCanUndo,
89
- useConnection,
90
- useDoc,
91
- useEvent,
92
- useMyPresence,
93
- useMyself,
94
- useOther,
95
- useOthers,
96
- useRedo,
97
- useRoom,
98
- useStorage,
99
- useTransact,
100
- useUndo,
101
- } = createRoomBundle({
102
- initialStorage: yjs.doc(() => ({
103
- messages: yjs.array([
104
- yjs.object({
105
- message: "Hello World!",
106
- name: "johnathan_doe",
107
- }),
108
- ]),
109
- })),
110
- presence: z.object({
111
- cursor: z.nullable(
112
- z.object({
113
- x: z.number(),
114
- y: z.number(),
115
- })
116
- ),
117
- }),
118
- });
119
-
120
- import { FC } from "react";
121
-
122
- export const MyPage: FC<Record<string, never>> = () => {
123
- return (
124
- <PluvProvider>
125
- <MyRoom />
126
- </PluvProvider>
127
- );
128
- };
129
-
130
- export const MyRoom: FC<Record<string, never>> = () => {
131
- const broadcast = useBroadcast();
132
-
133
- event.RECEIVE_MESSAGE.useEvent(({ data }) => {
134
- // data is typed as { message: string }
135
- console.log(data.message);
136
- });
137
-
138
- return (
139
- <button
140
- onClick={() => {
141
- broadcast.SEND_MESSAGE({
142
- // TypeScript will require that message is typed as string
143
- message: "Hello world!",
144
- });
145
- }}
146
- type="button"
147
- >
148
- Send Message
149
- </button>
150
- );
151
- };
152
- ```
153
-
154
- ## Reference
155
-
156
- Check us out on [GitHub](https://github.com/pluv-io/pluv) for more information on how to use `@pluv/io`.
package/dist/index.d.mts CHANGED
@@ -1,18 +1,17 @@
1
1
  import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { ConnectionState, createClient } from '@pluv/client';
3
- import { AbstractCrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtStorageJson } from '@pluv/crdt';
4
- export { AbstractCrdtType, InferCrdtStorageJson } from '@pluv/crdt';
5
- import * as _pluv_types from '@pluv/types';
3
+ import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
4
+ export { CrdtType, InferCrdtJson } from '@pluv/crdt';
6
5
  import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
7
6
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
8
7
  import { FC, Dispatch, ReactNode } from 'react';
9
8
 
10
- type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
9
+ type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
11
10
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
12
11
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
13
12
  presence?: InputZodLike<TPresence>;
14
13
  };
15
- type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> = {
14
+ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
16
15
  children?: ReactNode;
17
16
  initialStorage?: keyof TStorage extends never ? never : () => TStorage;
18
17
  room: string;
@@ -21,10 +20,10 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
21
20
  } : {
22
21
  initialPresence: TPresence;
23
22
  });
24
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
23
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
25
24
  events?: MockedRoomEvents<TIO>;
26
25
  };
27
- type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
26
+ type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
28
27
  debug?: boolean | PluvRoomDebug<TIO>;
29
28
  onAuthorizationFail?: (error: Error) => void;
30
29
  };
@@ -40,7 +39,7 @@ type EventProxy<TIO extends IOLike> = {
40
39
  useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
41
40
  };
42
41
  };
43
- interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> {
42
+ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
44
43
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
45
44
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
46
45
  event: EventProxy<TIO>;
@@ -56,7 +55,7 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
56
55
  useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T, options?: SubscriptionHookOptions<T>) => T;
57
56
  useRedo: () => () => void;
58
57
  useRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
59
- useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtStorageJson<TStorage[TKey]>>(key: TKey, selector?: (data: InferCrdtStorageJson<TStorage[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: TStorage[TKey] | null];
58
+ useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtJson<TStorage[TKey]>>(key: TKey, selector?: (data: InferCrdtJson<TStorage[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: TStorage[TKey] | null];
60
59
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
61
60
  useUndo: () => () => void;
62
61
  }
@@ -66,10 +65,10 @@ interface PluvProviderProps {
66
65
  children?: ReactNode;
67
66
  }
68
67
  interface CreateBundle<TIO extends IOLike> {
69
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
68
+ createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
70
69
  PluvProvider: FC<PluvProviderProps>;
71
70
  useClient: () => PluvClient<TIO>;
72
71
  }
73
- declare const createBundle: <TIO extends IOLike<_pluv_types.IOAuthorize<any, any, any>, {}>>(client: PluvClient<TIO>) => CreateBundle<TIO>;
72
+ declare const createBundle: <TIO extends IOLike>(client: PluvClient<TIO>) => CreateBundle<TIO>;
74
73
 
75
74
  export { type CreateBundle, type CreateRoomBundle, type CreateRoomBundleOptions, type InferRoomStorage, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,17 @@
1
1
  import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { ConnectionState, createClient } from '@pluv/client';
3
- import { AbstractCrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtStorageJson } from '@pluv/crdt';
4
- export { AbstractCrdtType, InferCrdtStorageJson } from '@pluv/crdt';
5
- import * as _pluv_types from '@pluv/types';
3
+ import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
4
+ export { CrdtType, InferCrdtJson } from '@pluv/crdt';
6
5
  import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
7
6
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
8
7
  import { FC, Dispatch, ReactNode } from 'react';
9
8
 
10
- type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
9
+ type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
11
10
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
12
11
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
13
12
  presence?: InputZodLike<TPresence>;
14
13
  };
15
- type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> = {
14
+ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
16
15
  children?: ReactNode;
17
16
  initialStorage?: keyof TStorage extends never ? never : () => TStorage;
18
17
  room: string;
@@ -21,10 +20,10 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
21
20
  } : {
22
21
  initialPresence: TPresence;
23
22
  });
24
- type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
23
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
25
24
  events?: MockedRoomEvents<TIO>;
26
25
  };
27
- type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
26
+ type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
28
27
  debug?: boolean | PluvRoomDebug<TIO>;
29
28
  onAuthorizationFail?: (error: Error) => void;
30
29
  };
@@ -40,7 +39,7 @@ type EventProxy<TIO extends IOLike> = {
40
39
  useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
41
40
  };
42
41
  };
43
- interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractCrdtType<any, any>>> {
42
+ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
44
43
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
45
44
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
46
45
  event: EventProxy<TIO>;
@@ -56,7 +55,7 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
56
55
  useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T, options?: SubscriptionHookOptions<T>) => T;
57
56
  useRedo: () => () => void;
58
57
  useRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
59
- useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtStorageJson<TStorage[TKey]>>(key: TKey, selector?: (data: InferCrdtStorageJson<TStorage[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: TStorage[TKey] | null];
58
+ useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtJson<TStorage[TKey]>>(key: TKey, selector?: (data: InferCrdtJson<TStorage[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: TStorage[TKey] | null];
60
59
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
61
60
  useUndo: () => () => void;
62
61
  }
@@ -66,10 +65,10 @@ interface PluvProviderProps {
66
65
  children?: ReactNode;
67
66
  }
68
67
  interface CreateBundle<TIO extends IOLike> {
69
- createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
68
+ createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
70
69
  PluvProvider: FC<PluvProviderProps>;
71
70
  useClient: () => PluvClient<TIO>;
72
71
  }
73
- declare const createBundle: <TIO extends IOLike<_pluv_types.IOAuthorize<any, any, any>, {}>>(client: PluvClient<TIO>) => CreateBundle<TIO>;
72
+ declare const createBundle: <TIO extends IOLike>(client: PluvClient<TIO>) => CreateBundle<TIO>;
74
73
 
75
74
  export { type CreateBundle, type CreateRoomBundle, type CreateRoomBundleOptions, type InferRoomStorage, type PluvProviderProps, type PluvRoomProviderProps, type SubscriptionHookOptions, createBundle };
package/dist/index.js CHANGED
@@ -89,7 +89,12 @@ var useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerS
89
89
  const cacheRef = (0, import_react3.useRef)(null);
90
90
  const _getServerSnapshot = getServerSnapshot ? () => {
91
91
  const data = getServerSnapshot();
92
- return selector(data);
92
+ const snapshot = selector(data);
93
+ const compare = isEqual != null ? isEqual : shallowEqual;
94
+ const cached = cacheRef.current;
95
+ const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
96
+ cacheRef.current = isUnchanged ? cached != null ? cached : snapshot : snapshot;
97
+ return cacheRef.current;
93
98
  } : void 0;
94
99
  const _getClientSnapshot = () => {
95
100
  const data = getClientSnapshot();
@@ -335,8 +340,7 @@ var createRoomBundle = (client, options) => {
335
340
  });
336
341
  const subscribe = (0, import_react4.useCallback)((onStoreChange) => room.storage(key, onStoreChange), [key, room]);
337
342
  const getSnapshot = (0, import_react4.useCallback)(() => {
338
- var _a3;
339
- return (_a3 = room.getStorage(key)) == null ? void 0 : _a3.toJson();
343
+ return room.getStorageJson(key);
340
344
  }, [key, room]);
341
345
  const _selector = (0, import_react4.useCallback)(
342
346
  (snapshot) => {
package/dist/index.mjs CHANGED
@@ -53,7 +53,12 @@ var useSyncExternalStoreWithSelector = (subscribe, getClientSnapshot, getServerS
53
53
  const cacheRef = useRef(null);
54
54
  const _getServerSnapshot = getServerSnapshot ? () => {
55
55
  const data = getServerSnapshot();
56
- return selector(data);
56
+ const snapshot = selector(data);
57
+ const compare = isEqual != null ? isEqual : shallowEqual;
58
+ const cached = cacheRef.current;
59
+ const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
60
+ cacheRef.current = isUnchanged ? cached != null ? cached : snapshot : snapshot;
61
+ return cacheRef.current;
57
62
  } : void 0;
58
63
  const _getClientSnapshot = () => {
59
64
  const data = getClientSnapshot();
@@ -299,8 +304,7 @@ var createRoomBundle = (client, options) => {
299
304
  });
300
305
  const subscribe = useCallback3((onStoreChange) => room.storage(key, onStoreChange), [key, room]);
301
306
  const getSnapshot = useCallback3(() => {
302
- var _a3;
303
- return (_a3 = room.getStorage(key)) == null ? void 0 : _a3.toJson();
307
+ return room.getStorageJson(key);
304
308
  }, [key, room]);
305
309
  const _selector = useCallback3(
306
310
  (snapshot) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "@pluv/client React.js bindings",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,9 +18,9 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "fast-deep-equal": "^3.1.3",
21
- "@pluv/client": "^0.19.0",
22
- "@pluv/crdt": "^0.19.0",
23
- "@pluv/types": "^0.19.0"
21
+ "@pluv/client": "^0.21.0",
22
+ "@pluv/crdt": "^0.21.0",
23
+ "@pluv/types": "^0.21.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@types/react": "^18.0.0",
@@ -30,10 +30,10 @@
30
30
  "@types/react": "^18.3.3",
31
31
  "eslint": "^8.57.0",
32
32
  "react": "^18.3.1",
33
- "tsup": "^8.1.0",
34
- "typescript": "^5.4.5",
35
- "@pluv/tsconfig": "^0.19.0",
36
- "eslint-config-pluv": "^0.19.0"
33
+ "tsup": "^8.2.4",
34
+ "typescript": "^5.5.4",
35
+ "@pluv/tsconfig": "^0.21.0",
36
+ "eslint-config-pluv": "^0.21.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup src/index.ts --format esm,cjs --dts --external react",
@@ -1,5 +1,5 @@
1
1
  import type { PluvClient } from "@pluv/client";
2
- import type { AbstractCrdtType } from "@pluv/crdt";
2
+ import type { CrdtType } from "@pluv/crdt";
3
3
  import type { IOLike, JsonObject } from "@pluv/types";
4
4
  import type { FC, ReactNode } from "react";
5
5
  import { createContext, memo, useContext } from "react";
@@ -12,10 +12,7 @@ export interface PluvProviderProps {
12
12
 
13
13
  export interface CreateBundle<TIO extends IOLike> {
14
14
  // factories
15
- createRoomBundle: <
16
- TPresence extends JsonObject = {},
17
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
18
- >(
15
+ createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
19
16
  options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
20
17
  ) => CreateRoomBundle<TIO, TPresence, TStorage>;
21
18
 
@@ -48,7 +45,7 @@ export const createBundle = <TIO extends IOLike>(client: PluvClient<TIO>): Creat
48
45
 
49
46
  const createRoomBundle = <
50
47
  TPresence extends JsonObject = {},
51
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
48
+ TStorage extends Record<string, CrdtType<any, any>> = {},
52
49
  >(
53
50
  options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
54
51
  ): CreateRoomBundle<TIO, TPresence, TStorage> => _createRoomBundle(client, options);
@@ -9,7 +9,7 @@ import type {
9
9
  WebSocketConnection,
10
10
  } from "@pluv/client";
11
11
  import { MockedRoom } from "@pluv/client";
12
- import type { AbstractCrdtDoc, AbstractCrdtDocFactory, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
12
+ import type { AbstractCrdtDoc, AbstractCrdtDocFactory, CrdtType, InferCrdtJson } from "@pluv/crdt";
13
13
  import { noop } from "@pluv/crdt";
14
14
  import type { IOEventMessage, IOLike, Id, InferIOInput, InferIOOutput, InputZodLike, JsonObject } from "@pluv/types";
15
15
  import fastDeepEqual from "fast-deep-equal";
@@ -20,17 +20,14 @@ import { identity, shallowArrayEqual, useRerender, useSyncExternalStoreWithSelec
20
20
  export type CreateRoomBundleOptions<
21
21
  TIO extends IOLike,
22
22
  TPresence extends JsonObject = {},
23
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
23
+ TStorage extends Record<string, CrdtType<any, any>> = {},
24
24
  > = {
25
25
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
26
26
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
27
27
  presence?: InputZodLike<TPresence>;
28
28
  };
29
29
 
30
- type BaseRoomProviderProps<
31
- TPresence extends JsonObject,
32
- TStorage extends Record<string, AbstractCrdtType<any, any>>,
33
- > = {
30
+ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
34
31
  children?: ReactNode;
35
32
  initialStorage?: keyof TStorage extends never ? never : () => TStorage;
36
33
  room: string;
@@ -39,7 +36,7 @@ type BaseRoomProviderProps<
39
36
  export type MockedRoomProviderProps<
40
37
  TIO extends IOLike,
41
38
  TPresence extends JsonObject,
42
- TStorage extends Record<string, AbstractCrdtType<any, any>>,
39
+ TStorage extends Record<string, CrdtType<any, any>>,
43
40
  > = BaseRoomProviderProps<TPresence, TStorage> & {
44
41
  events?: MockedRoomEvents<TIO>;
45
42
  };
@@ -47,7 +44,7 @@ export type MockedRoomProviderProps<
47
44
  export type PluvRoomProviderProps<
48
45
  TIO extends IOLike,
49
46
  TPresence extends JsonObject,
50
- TStorage extends Record<string, AbstractCrdtType<any, any>>,
47
+ TStorage extends Record<string, CrdtType<any, any>>,
51
48
  > = BaseRoomProviderProps<TPresence, TStorage> & {
52
49
  debug?: boolean | PluvRoomDebug<TIO>;
53
50
  onAuthorizationFail?: (error: Error) => void;
@@ -77,7 +74,7 @@ export type EventProxy<TIO extends IOLike> = {
77
74
  export interface CreateRoomBundle<
78
75
  TIO extends IOLike,
79
76
  TPresence extends JsonObject,
80
- TStorage extends Record<string, AbstractCrdtType<any, any>>,
77
+ TStorage extends Record<string, CrdtType<any, any>>,
81
78
  > {
82
79
  // components
83
80
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
@@ -118,9 +115,9 @@ export interface CreateRoomBundle<
118
115
  ) => T;
119
116
  useRedo: () => () => void;
120
117
  useRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
121
- useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtStorageJson<TStorage[TKey]>>(
118
+ useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtJson<TStorage[TKey]>>(
122
119
  key: TKey,
123
- selector?: (data: InferCrdtStorageJson<TStorage[TKey]>) => TData,
120
+ selector?: (data: InferCrdtJson<TStorage[TKey]>) => TData,
124
121
  options?: SubscriptionHookOptions<TData | null>,
125
122
  ) => [data: TData | null, sharedType: TStorage[TKey] | null];
126
123
  useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
@@ -133,7 +130,7 @@ export type InferRoomStorage<TRoomBundle extends CreateRoomBundle<any, any, any>
133
130
  export const createRoomBundle = <
134
131
  TIO extends IOLike,
135
132
  TPresence extends JsonObject = {},
136
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
133
+ TStorage extends Record<string, CrdtType<any, any>> = {},
137
134
  >(
138
135
  client: PluvClient<TIO>,
139
136
  options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
@@ -461,9 +458,9 @@ export const createRoomBundle = <
461
458
  return room.redo;
462
459
  };
463
460
 
464
- const useStorage = <TKey extends keyof TStorage, TData extends unknown = InferCrdtStorageJson<TStorage[TKey]>>(
461
+ const useStorage = <TKey extends keyof TStorage, TData extends unknown = InferCrdtJson<TStorage[TKey]>>(
465
462
  key: TKey,
466
- selector = identity as (data: InferCrdtStorageJson<TStorage[TKey]>) => TData,
463
+ selector = identity as (data: InferCrdtJson<TStorage[TKey]>) => TData,
467
464
  options?: SubscriptionHookOptions<TData | null>,
468
465
  ): [data: TData | null, sharedType: TStorage[TKey] | null] => {
469
466
  const room = useRoom();
@@ -475,12 +472,12 @@ export const createRoomBundle = <
475
472
 
476
473
  const subscribe = useCallback((onStoreChange: () => void) => room.storage(key, onStoreChange), [key, room]);
477
474
 
478
- const getSnapshot = useCallback((): InferCrdtStorageJson<TStorage[TKey]> | null => {
479
- return room.getStorage(key)?.toJson();
475
+ const getSnapshot = useCallback((): InferCrdtJson<TStorage[TKey]> | null => {
476
+ return room.getStorageJson(key);
480
477
  }, [key, room]);
481
478
 
482
479
  const _selector = useCallback(
483
- (snapshot: InferCrdtStorageJson<TStorage[TKey]> | null) => {
480
+ (snapshot: InferCrdtJson<TStorage[TKey]> | null) => {
484
481
  return snapshot === null ? null : selector(snapshot);
485
482
  },
486
483
  [selector],
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ConnectionState, createClient } from "@pluv/client";
2
- export type { AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
2
+ export type { CrdtType, InferCrdtJson } from "@pluv/crdt";
3
3
  export type { BaseUser, EventMessage, EventRecord, IOEventMessage } from "@pluv/types";
4
4
  export { createBundle } from "./createBundle";
5
5
  export type { CreateBundle, PluvProviderProps } from "./createBundle";
@@ -15,8 +15,20 @@ export const useSyncExternalStoreWithSelector = <TSnapshot, TSelection>(
15
15
  const _getServerSnapshot = getServerSnapshot
16
16
  ? () => {
17
17
  const data = getServerSnapshot();
18
+ const snapshot = selector(data);
19
+ const compare = isEqual ?? shallowEqual;
20
+ const cached = cacheRef.current;
18
21
 
19
- return selector(data);
22
+ const isUnchanged = cached === null ? snapshot === null : compare(cached, snapshot);
23
+
24
+ /**
25
+ * !HACK
26
+ * @description Memoize only the last snapshot so that we can only rerender on changes
27
+ * @date June 22, 2024
28
+ */
29
+ cacheRef.current = isUnchanged ? cached ?? snapshot : snapshot;
30
+
31
+ return cacheRef.current;
20
32
  }
21
33
  : undefined;
22
34