@pluv/react 0.19.0 → 0.20.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.
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +78 -0
- package/README.md +0 -138
- package/dist/index.d.mts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +7 -3
- package/dist/index.mjs +7 -3
- package/package.json +6 -6
- package/src/createBundle.tsx +3 -6
- package/src/createRoomBundle.tsx +14 -17
- package/src/index.ts +1 -1
- package/src/internal/hooks/useSyncExternalStoreWithSelector.ts +13 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/react@0.
|
|
2
|
+
> @pluv/react@0.20.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 @@ CLI tsup v8.1.0
|
|
|
8
8
|
CLI Target: es6
|
|
9
9
|
ESM Build start
|
|
10
10
|
CJS Build start
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
CJS dist/index.js 14.27 KB
|
|
12
|
+
CJS ⚡️ Build success in 64ms
|
|
13
|
+
ESM dist/index.mjs 11.87 KB
|
|
14
|
+
ESM ⚡️ Build success in 67ms
|
|
15
15
|
DTS Build start
|
|
16
|
-
DTS ⚡️ Build success in
|
|
17
|
-
DTS dist/index.d.mts 5.
|
|
18
|
-
DTS dist/index.d.ts 5.
|
|
16
|
+
DTS ⚡️ Build success in 4545ms
|
|
17
|
+
DTS dist/index.d.mts 5.26 KB
|
|
18
|
+
DTS dist/index.d.ts 5.26 KB
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# @pluv/react
|
|
2
2
|
|
|
3
|
+
## 0.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// Before
|
|
15
|
+
|
|
16
|
+
import { yjs } from "@pluv/crdt-yjs";
|
|
17
|
+
|
|
18
|
+
yjs.array([]); // Returns CrdtYjsArray
|
|
19
|
+
yjs.object([]); // Returns CrdtYjsObject
|
|
20
|
+
|
|
21
|
+
const room: PluvRoom = /* ... */;
|
|
22
|
+
|
|
23
|
+
room.getStorage("messages"); // Returns AbstractCrdtType
|
|
24
|
+
|
|
25
|
+
const [, sharedType] = useStorage("messages"); // sharedType is an AbstractCrdtType
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// Now
|
|
30
|
+
|
|
31
|
+
import { yjs } from "@pluv/crdt-yjs";
|
|
32
|
+
|
|
33
|
+
yjs.array([]); // Returns yjs.Array
|
|
34
|
+
yjs.object([]); // Returns yjs.Map
|
|
35
|
+
|
|
36
|
+
const room: PluvRoom = /* ... */;
|
|
37
|
+
|
|
38
|
+
room.getStorage("messages"); // Returns yjs.AbstractType
|
|
39
|
+
|
|
40
|
+
const [, sharedType] = useStorage("messages"); // sharedType is a yjs.AbstractType
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
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:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// Before
|
|
47
|
+
|
|
48
|
+
const [data, container] = useStorage("messages");
|
|
49
|
+
|
|
50
|
+
// This automatically called Loro.commit under the hood and rerendered the page with updated data.
|
|
51
|
+
container.push(loro.object({ name: "John Doe", age: 35 }));
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// Now
|
|
56
|
+
|
|
57
|
+
const [data, container] = useStorage("messages");
|
|
58
|
+
|
|
59
|
+
const doc = useDoc();
|
|
60
|
+
const transact = useTransact();
|
|
61
|
+
|
|
62
|
+
// Updates need to be committed back to the doc whenever the changes need to be emitted to other users
|
|
63
|
+
// The returned data also will not update until the changes are commited
|
|
64
|
+
|
|
65
|
+
// The two operations below are functionally equivalent
|
|
66
|
+
transact(() => {
|
|
67
|
+
container.push(loro.object({ name: "John Doe", age: 35 }));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
container.push(loro.object({ name: "John Doe", age: 35 }));
|
|
71
|
+
doc.value.commit();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Patch Changes
|
|
75
|
+
|
|
76
|
+
- Updated dependencies [9492085]
|
|
77
|
+
- @pluv/client@0.20.0
|
|
78
|
+
- @pluv/crdt@0.20.0
|
|
79
|
+
- @pluv/types@0.20.0
|
|
80
|
+
|
|
3
81
|
## 0.19.0
|
|
4
82
|
|
|
5
83
|
### 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,18 @@
|
|
|
1
1
|
import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
|
|
2
2
|
export { ConnectionState, createClient } from '@pluv/client';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
3
|
+
import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
|
|
4
|
+
export { CrdtType, InferCrdtJson } from '@pluv/crdt';
|
|
5
5
|
import * as _pluv_types from '@pluv/types';
|
|
6
6
|
import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
|
|
7
7
|
export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
|
|
8
8
|
import { FC, Dispatch, ReactNode } from 'react';
|
|
9
9
|
|
|
10
|
-
type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string,
|
|
10
|
+
type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
|
|
11
11
|
addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
|
|
12
12
|
initialStorage?: AbstractCrdtDocFactory<TStorage>;
|
|
13
13
|
presence?: InputZodLike<TPresence>;
|
|
14
14
|
};
|
|
15
|
-
type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string,
|
|
15
|
+
type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
|
|
16
16
|
children?: ReactNode;
|
|
17
17
|
initialStorage?: keyof TStorage extends never ? never : () => TStorage;
|
|
18
18
|
room: string;
|
|
@@ -21,10 +21,10 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
|
|
|
21
21
|
} : {
|
|
22
22
|
initialPresence: TPresence;
|
|
23
23
|
});
|
|
24
|
-
type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string,
|
|
24
|
+
type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
|
|
25
25
|
events?: MockedRoomEvents<TIO>;
|
|
26
26
|
};
|
|
27
|
-
type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string,
|
|
27
|
+
type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
|
|
28
28
|
debug?: boolean | PluvRoomDebug<TIO>;
|
|
29
29
|
onAuthorizationFail?: (error: Error) => void;
|
|
30
30
|
};
|
|
@@ -40,7 +40,7 @@ type EventProxy<TIO extends IOLike> = {
|
|
|
40
40
|
useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
-
interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string,
|
|
43
|
+
interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
|
|
44
44
|
MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
|
|
45
45
|
PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
|
|
46
46
|
event: EventProxy<TIO>;
|
|
@@ -56,7 +56,7 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
|
|
|
56
56
|
useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T, options?: SubscriptionHookOptions<T>) => T;
|
|
57
57
|
useRedo: () => () => void;
|
|
58
58
|
useRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
|
|
59
|
-
useStorage: <TKey extends keyof TStorage, TData extends unknown =
|
|
59
|
+
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
60
|
useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
|
|
61
61
|
useUndo: () => () => void;
|
|
62
62
|
}
|
|
@@ -66,7 +66,7 @@ interface PluvProviderProps {
|
|
|
66
66
|
children?: ReactNode;
|
|
67
67
|
}
|
|
68
68
|
interface CreateBundle<TIO extends IOLike> {
|
|
69
|
-
createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string,
|
|
69
|
+
createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
|
|
70
70
|
PluvProvider: FC<PluvProviderProps>;
|
|
71
71
|
useClient: () => PluvClient<TIO>;
|
|
72
72
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { PluvRoomAddon, PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, MockedRoomEvents, PluvClient } from '@pluv/client';
|
|
2
2
|
export { ConnectionState, createClient } from '@pluv/client';
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
3
|
+
import { CrdtType, AbstractCrdtDocFactory, AbstractCrdtDoc, InferCrdtJson } from '@pluv/crdt';
|
|
4
|
+
export { CrdtType, InferCrdtJson } from '@pluv/crdt';
|
|
5
5
|
import * as _pluv_types from '@pluv/types';
|
|
6
6
|
import { IOLike, JsonObject, InputZodLike, Id, InferIOOutput, IOEventMessage, InferIOInput } from '@pluv/types';
|
|
7
7
|
export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
|
|
8
8
|
import { FC, Dispatch, ReactNode } from 'react';
|
|
9
9
|
|
|
10
|
-
type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string,
|
|
10
|
+
type CreateRoomBundleOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
|
|
11
11
|
addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
|
|
12
12
|
initialStorage?: AbstractCrdtDocFactory<TStorage>;
|
|
13
13
|
presence?: InputZodLike<TPresence>;
|
|
14
14
|
};
|
|
15
|
-
type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string,
|
|
15
|
+
type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = {
|
|
16
16
|
children?: ReactNode;
|
|
17
17
|
initialStorage?: keyof TStorage extends never ? never : () => TStorage;
|
|
18
18
|
room: string;
|
|
@@ -21,10 +21,10 @@ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record
|
|
|
21
21
|
} : {
|
|
22
22
|
initialPresence: TPresence;
|
|
23
23
|
});
|
|
24
|
-
type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string,
|
|
24
|
+
type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
|
|
25
25
|
events?: MockedRoomEvents<TIO>;
|
|
26
26
|
};
|
|
27
|
-
type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string,
|
|
27
|
+
type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
|
|
28
28
|
debug?: boolean | PluvRoomDebug<TIO>;
|
|
29
29
|
onAuthorizationFail?: (error: Error) => void;
|
|
30
30
|
};
|
|
@@ -40,7 +40,7 @@ type EventProxy<TIO extends IOLike> = {
|
|
|
40
40
|
useEvent: (callback: (data: Id<IOEventMessage<TIO, event>>) => void) => void;
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
-
interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string,
|
|
43
|
+
interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>> {
|
|
44
44
|
MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
|
|
45
45
|
PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
|
|
46
46
|
event: EventProxy<TIO>;
|
|
@@ -56,7 +56,7 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
|
|
|
56
56
|
useOthers: <T extends unknown = readonly UserInfo<TIO, TPresence>[]>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T, options?: SubscriptionHookOptions<T>) => T;
|
|
57
57
|
useRedo: () => () => void;
|
|
58
58
|
useRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
|
|
59
|
-
useStorage: <TKey extends keyof TStorage, TData extends unknown =
|
|
59
|
+
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
60
|
useTransact: () => (fn: (storage: TStorage) => void, origin?: string) => void;
|
|
61
61
|
useUndo: () => () => void;
|
|
62
62
|
}
|
|
@@ -66,7 +66,7 @@ interface PluvProviderProps {
|
|
|
66
66
|
children?: ReactNode;
|
|
67
67
|
}
|
|
68
68
|
interface CreateBundle<TIO extends IOLike> {
|
|
69
|
-
createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string,
|
|
69
|
+
createRoomBundle: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(options: CreateRoomBundleOptions<TIO, TPresence, TStorage>) => CreateRoomBundle<TIO, TPresence, TStorage>;
|
|
70
70
|
PluvProvider: FC<PluvProviderProps>;
|
|
71
71
|
useClient: () => PluvClient<TIO>;
|
|
72
72
|
}
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.20.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.
|
|
22
|
-
"@pluv/crdt": "^0.
|
|
23
|
-
"@pluv/types": "^0.
|
|
21
|
+
"@pluv/client": "^0.20.0",
|
|
22
|
+
"@pluv/crdt": "^0.20.0",
|
|
23
|
+
"@pluv/types": "^0.20.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"@types/react": "^18.0.0",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"react": "^18.3.1",
|
|
33
33
|
"tsup": "^8.1.0",
|
|
34
34
|
"typescript": "^5.4.5",
|
|
35
|
-
"@pluv/tsconfig": "^0.
|
|
36
|
-
"eslint-config-pluv": "^0.
|
|
35
|
+
"@pluv/tsconfig": "^0.20.0",
|
|
36
|
+
"eslint-config-pluv": "^0.20.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsup src/index.ts --format esm,cjs --dts --external react",
|
package/src/createBundle.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PluvClient } from "@pluv/client";
|
|
2
|
-
import type {
|
|
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,
|
|
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);
|
package/src/createRoomBundle.tsx
CHANGED
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
118
|
+
useStorage: <TKey extends keyof TStorage, TData extends unknown = InferCrdtJson<TStorage[TKey]>>(
|
|
122
119
|
key: TKey,
|
|
123
|
-
selector?: (data:
|
|
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,
|
|
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 =
|
|
461
|
+
const useStorage = <TKey extends keyof TStorage, TData extends unknown = InferCrdtJson<TStorage[TKey]>>(
|
|
465
462
|
key: TKey,
|
|
466
|
-
selector = identity as (data:
|
|
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(():
|
|
479
|
-
return room.
|
|
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:
|
|
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 {
|
|
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
|
-
|
|
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
|
|