@pluv/client 0.18.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.
@@ -1,18 +1,18 @@
1
1
 
2
- > @pluv/client@0.18.0 build /home/runner/work/pluv/pluv/packages/client
2
+ > @pluv/client@0.20.0 build /home/runner/work/pluv/pluv/packages/client
3
3
  > tsup src/index.ts --format esm,cjs --dts
4
4
 
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.0.2
8
- CLI Target: es6
9
- ESM Build start
10
- CJS Build start
11
- ESM dist/index.mjs 41.27 KB
12
- ESM ⚡️ Build success in 104ms
13
- CJS dist/index.js 42.53 KB
14
- CJS ⚡️ Build success in 104ms
15
- DTS Build start
16
- DTS ⚡️ Build success in 3547ms
17
- DTS dist/index.d.mts 14.14 KB
18
- DTS dist/index.d.ts 14.14 KB
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 41.48 KB
12
+ ESM ⚡️ Build success in 74ms
13
+ CJS dist/index.js 42.74 KB
14
+ CJS ⚡️ Build success in 87ms
15
+ DTS Build start
16
+ DTS ⚡️ Build success in 3234ms
17
+ DTS dist/index.d.mts 14.64 KB
18
+ DTS dist/index.d.ts 14.64 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,106 @@
1
1
  # @pluv/client
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/crdt@0.20.0
78
+ - @pluv/types@0.20.0
79
+
80
+ ## 0.19.0
81
+
82
+ ### Patch Changes
83
+
84
+ - 137444b: Updated PluvRoom.updateMyPresence to allow passing in a callback function that exposes the previous presence value and returns a presence update.
85
+
86
+ ```ts
87
+ type Presence = {
88
+ selectionId: string | null;
89
+ };
90
+
91
+ const room: PluvRoom = /* ... */;
92
+ const newSelectionId: string | null = /* ... */;
93
+
94
+ room.updateMyPresence({ selectionId: newSelection });
95
+
96
+ // You can reference the previous presence and return a new presence based on it
97
+ room.updateMyPresence((previousPresence) => ({ selectionId: newSelection ?? previousPresence.selectionId }));
98
+ ```
99
+
100
+ - f5e4370: Fix others' presence not getting tracked in PluvRoom when the user is unauthorized.
101
+ - @pluv/crdt@0.19.0
102
+ - @pluv/types@0.19.0
103
+
3
104
  ## 0.18.0
4
105
 
5
106
  ### 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/client">
17
- <img src="https://img.shields.io/npm/v/@pluv/client" alt="npm @pluv/client" />
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/client`
29
2
 
30
3
  > Framework agnostic frontend client for [@pluv/io](https://www.npmjs.com/package/@pluv/io)
@@ -43,68 +16,3 @@ yarn add @pluv/client @pluv/crdt-yjs yjs
43
16
  # pnpm
44
17
  pnpm add @pluv/client @pluv/crdt-yjs yjs
45
18
  ```
46
-
47
- If you plan on using presence and/or storage, make sure to also install `zod`.
48
-
49
- ## Basic Example
50
-
51
- ```ts
52
- import { createClient } from "@pluv/client";
53
- import { yjs } from "@pluv/crdt-yjs";
54
- import { z } from "zod";
55
- // Import the PluvIO instance as a type from your server file
56
- import { type io } from "./server";
57
-
58
- // Initialize the @pluv/client client
59
- const client = createClient<typeof io>({
60
- authEndpoint: (room) => {
61
- return `https://localhost:3000/api/authorize?room=${room}`;
62
- },
63
- wsEndpoint: (room) => {
64
- return `ws://localhost:3000/api/room/${room}`;
65
- },
66
- });
67
-
68
- interface Presence {
69
- cursor: { x: number; y: number } | null;
70
- }
71
-
72
- interface Storage {
73
- messages: readonly { name: string; message: string }[]
74
- }
75
-
76
- // Create a room to join
77
- const room = client.createRoom<Presence, Storage>("my-room", {
78
- initialPresence: { cursor: null },
79
- initialStorage: yjs.doc(() => ({ messages: yjs.array([]) })),
80
- onAuthorizationFail: (error) => {
81
- console.log(error.message);
82
- },
83
- presence: z.object({
84
- cursor: z.nullable(
85
- z.object({
86
- x: z.number(),
87
- y: z.number(),
88
- })
89
- ),
90
- }),
91
- });
92
-
93
- const main = async () => {
94
- await client.enter(room);
95
-
96
- const unsubscribe = client.event.RECEIVE_MESSAGE(({ data }) => {
97
- console.log(data.message);
98
- });
99
-
100
- client.broadcast.SEND_MESSAGE({ message: "Hello world!" });
101
-
102
- unsubscribe();
103
-
104
- client.leave(room);
105
- };
106
- ```
107
-
108
- ## Reference
109
-
110
- 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,5 +1,5 @@
1
- import { AbstractCrdtType, AbstractCrdtDoc, InferCrdtStorageJson, AbstractCrdtDocFactory } from '@pluv/crdt';
2
- export { AbstractCrdtType, InferCrdtStorageJson } from '@pluv/crdt';
1
+ import { CrdtType, AbstractCrdtDoc, InferCrdtJson, AbstractCrdtDocFactory } from '@pluv/crdt';
2
+ export { CrdtType, InferCrdtJson } from '@pluv/crdt';
3
3
  import * as _pluv_types from '@pluv/types';
4
4
  import { IOLike, InferIOOutput, Id, IOEventMessage, JsonObject, InferIOAuthorizeUser, InferIOAuthorize, InferIOInput, InputZodLike, InferIOAuthorizeRequired } from '@pluv/types';
5
5
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
@@ -18,6 +18,12 @@ declare enum ConnectionState {
18
18
  Unavailable = "Unavailable",
19
19
  Untouched = "Untouched"
20
20
  }
21
+ type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
22
+ interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
23
+ connectionId: string;
24
+ presence: TPresence;
25
+ user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
26
+ }
21
27
  interface WebSocketConnection {
22
28
  count: number;
23
29
  id: string | null;
@@ -28,11 +34,6 @@ interface WebSocketState<TIO extends IOLike> {
28
34
  connection: WebSocketConnection;
29
35
  webSocket: WebSocket | null;
30
36
  }
31
- interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
32
- connectionId: string;
33
- presence: TPresence;
34
- user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
35
- }
36
37
 
37
38
  type OtherNotifierSubscriptionCallback<TIO extends IOLike> = (value: Id<UserInfo<TIO>> | null) => void;
38
39
 
@@ -46,7 +47,7 @@ interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject
46
47
  type InferSubjectValue<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
47
48
  type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
48
49
 
49
- declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> {
50
+ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
50
51
  readonly id: string;
51
52
  constructor(room: string);
52
53
  abstract storageLoaded: boolean;
@@ -60,17 +61,19 @@ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonOb
60
61
  abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
61
62
  abstract getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
62
63
  abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
63
- abstract getStorage<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null;
64
+ abstract getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
65
+ abstract getStorageJson(): InferCrdtJson<TStorage> | null;
66
+ abstract getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
64
67
  abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
65
68
  abstract redo(): void;
66
- abstract storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void): () => void;
69
+ abstract storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void): () => void;
67
70
  abstract storageRoot(fn: (value: {
68
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
71
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
69
72
  }) => void): () => void;
70
73
  abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
71
74
  abstract transact(fn: (storage: TStorage) => void, origin?: string): void;
72
75
  abstract undo(): void;
73
- abstract updateMyPresence(presence: Partial<TPresence>): void;
76
+ abstract updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
74
77
  }
75
78
 
76
79
  declare abstract class AbstractStorageStore {
@@ -84,7 +87,7 @@ declare abstract class AbstractStorageStore {
84
87
  abstract initialize(): Promise<void>;
85
88
  }
86
89
 
87
- type CrdtManagerOptions<TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
90
+ type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
88
91
  encodedState?: string | Uint8Array | null;
89
92
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
90
93
  };
@@ -97,10 +100,10 @@ type UsersManagerConfig<TPresence extends JsonObject = {}> = {
97
100
  type MockedRoomEvents<TIO extends IOLike> = Partial<{
98
101
  [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
99
102
  }>;
100
- type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
103
+ type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
101
104
  events?: MockedRoomEvents<TIO>;
102
105
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
103
- declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
106
+ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
104
107
  private _crdtManager;
105
108
  private _crdtNotifier;
106
109
  private _eventNotifier;
@@ -116,21 +119,23 @@ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {},
116
119
  canRedo: () => boolean;
117
120
  canUndo: () => boolean;
118
121
  event: <TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => (() => void);
119
- getConnection(): WebSocketConnection;
122
+ getConnection: () => WebSocketConnection;
120
123
  getDoc(): AbstractCrdtDoc<TStorage>;
121
124
  getMyPresence: () => TPresence;
122
125
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
123
126
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
124
127
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
125
- getStorage: <TKey extends keyof TStorage>(key: TKey) => TStorage[TKey] | null;
128
+ getStorage: <TKey extends keyof TStorage>(type: TKey) => TStorage[TKey] | null;
129
+ getStorageJson(): InferCrdtJson<TStorage> | null;
130
+ getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
126
131
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
127
132
  redo: () => void;
128
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
129
- storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>; }) => void) => (() => void);
133
+ storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void) => (() => void);
134
+ storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtJson<TStorage[P]>; }) => void) => (() => void);
130
135
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
131
136
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
132
137
  undo: () => void;
133
- updateMyPresence: (presence: Partial<TPresence>) => void;
138
+ updateMyPresence: (presence: UpdateMyPresenceAction<TPresence>) => void;
134
139
  private _observeCrdt;
135
140
  }
136
141
 
@@ -154,8 +159,8 @@ type RoomEndpoints<TIO extends IOLike> = {
154
159
  } : {
155
160
  authEndpoint?: undefined;
156
161
  });
157
- type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
158
- interface PluvRoomAddonInput<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> {
162
+ type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
163
+ interface PluvRoomAddonInput<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
159
164
  room: PluvRoom<TIO, TPresence, TStorage>;
160
165
  }
161
166
  interface PluvRoomAddonResult {
@@ -165,13 +170,13 @@ type PluvRoomDebug<TIO extends IOLike> = Id<{
165
170
  output: readonly (keyof InferIOOutput<TIO>)[];
166
171
  input: readonly (keyof InferIOInput<TIO>)[];
167
172
  }>;
168
- type PluvRoomOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
173
+ type PluvRoomOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
169
174
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
170
175
  debug?: boolean | PluvRoomDebug<TIO>;
171
176
  onAuthorizationFail?: (error: Error) => void;
172
177
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
173
- type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
174
- declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
178
+ type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
179
+ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
175
180
  readonly _endpoints: RoomEndpoints<TIO>;
176
181
  private _crdtManager;
177
182
  private _crdtNotifier;
@@ -197,21 +202,23 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
197
202
  connect(): Promise<void>;
198
203
  disconnect(): Promise<void>;
199
204
  event: (<TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => () => void) & { [event in keyof InferIOOutput<TIO>]: (callback: EventNotifierSubscriptionCallback<TIO, event>) => () => void; };
200
- getConnection(): WebSocketConnection;
201
- getDoc(): AbstractCrdtDoc<TStorage>;
205
+ getConnection: () => WebSocketConnection;
206
+ getDoc: () => AbstractCrdtDoc<TStorage>;
202
207
  getMyPresence: () => TPresence;
203
208
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
204
209
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
205
210
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
206
- getStorage: <TKey extends keyof TStorage>(key: TKey) => TStorage[TKey] | null;
211
+ getStorage: <TKey extends keyof TStorage>(type: TKey) => TStorage[TKey] | null;
212
+ getStorageJson(): InferCrdtJson<TStorage> | null;
213
+ getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
207
214
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
208
215
  redo: () => void;
209
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
210
- storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>; }) => void) => (() => void);
216
+ storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void) => (() => void);
217
+ storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtJson<TStorage[P]>; }) => void) => (() => void);
211
218
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
212
219
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
213
220
  undo: () => void;
214
- updateMyPresence: (presence: Partial<TPresence>) => void;
221
+ updateMyPresence: (presence: UpdateMyPresenceAction<TPresence>) => void;
215
222
  private _addToStorageStore;
216
223
  private _applyStorageStore;
217
224
  private _attachWindowListeners;
@@ -272,9 +279,9 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
272
279
  private _wsEndpoint;
273
280
  private _rooms;
274
281
  constructor(options: PluvClientOptions<TIO>);
275
- createRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}>(room: string, options: PluvRoomOptions<TIO, TPresence, TStorage>) => PluvRoom<TIO, TPresence, TStorage>;
282
+ createRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(room: string, options: PluvRoomOptions<TIO, TPresence, TStorage>) => PluvRoom<TIO, TPresence, TStorage>;
276
283
  enter: (room: string | PluvRoom<TIO, any, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
277
- getRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}>(room: string) => PluvRoom<TIO, TPresence, TStorage> | null;
284
+ getRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(room: string) => PluvRoom<TIO, TPresence, TStorage> | null;
278
285
  getRooms: () => readonly PluvRoom<TIO, JsonObject, any>[];
279
286
  leave: (room: string | PluvRoom<TIO, any, any>) => Promise<void>;
280
287
  private _logDebug;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AbstractCrdtType, AbstractCrdtDoc, InferCrdtStorageJson, AbstractCrdtDocFactory } from '@pluv/crdt';
2
- export { AbstractCrdtType, InferCrdtStorageJson } from '@pluv/crdt';
1
+ import { CrdtType, AbstractCrdtDoc, InferCrdtJson, AbstractCrdtDocFactory } from '@pluv/crdt';
2
+ export { CrdtType, InferCrdtJson } from '@pluv/crdt';
3
3
  import * as _pluv_types from '@pluv/types';
4
4
  import { IOLike, InferIOOutput, Id, IOEventMessage, JsonObject, InferIOAuthorizeUser, InferIOAuthorize, InferIOInput, InputZodLike, InferIOAuthorizeRequired } from '@pluv/types';
5
5
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
@@ -18,6 +18,12 @@ declare enum ConnectionState {
18
18
  Unavailable = "Unavailable",
19
19
  Untouched = "Untouched"
20
20
  }
21
+ type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
22
+ interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
23
+ connectionId: string;
24
+ presence: TPresence;
25
+ user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
26
+ }
21
27
  interface WebSocketConnection {
22
28
  count: number;
23
29
  id: string | null;
@@ -28,11 +34,6 @@ interface WebSocketState<TIO extends IOLike> {
28
34
  connection: WebSocketConnection;
29
35
  webSocket: WebSocket | null;
30
36
  }
31
- interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
32
- connectionId: string;
33
- presence: TPresence;
34
- user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
35
- }
36
37
 
37
38
  type OtherNotifierSubscriptionCallback<TIO extends IOLike> = (value: Id<UserInfo<TIO>> | null) => void;
38
39
 
@@ -46,7 +47,7 @@ interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject
46
47
  type InferSubjectValue<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
47
48
  type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
48
49
 
49
- declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> {
50
+ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
50
51
  readonly id: string;
51
52
  constructor(room: string);
52
53
  abstract storageLoaded: boolean;
@@ -60,17 +61,19 @@ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonOb
60
61
  abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
61
62
  abstract getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
62
63
  abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
63
- abstract getStorage<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null;
64
+ abstract getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
65
+ abstract getStorageJson(): InferCrdtJson<TStorage> | null;
66
+ abstract getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
64
67
  abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
65
68
  abstract redo(): void;
66
- abstract storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void): () => void;
69
+ abstract storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void): () => void;
67
70
  abstract storageRoot(fn: (value: {
68
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
71
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
69
72
  }) => void): () => void;
70
73
  abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
71
74
  abstract transact(fn: (storage: TStorage) => void, origin?: string): void;
72
75
  abstract undo(): void;
73
- abstract updateMyPresence(presence: Partial<TPresence>): void;
76
+ abstract updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
74
77
  }
75
78
 
76
79
  declare abstract class AbstractStorageStore {
@@ -84,7 +87,7 @@ declare abstract class AbstractStorageStore {
84
87
  abstract initialize(): Promise<void>;
85
88
  }
86
89
 
87
- type CrdtManagerOptions<TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
90
+ type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
88
91
  encodedState?: string | Uint8Array | null;
89
92
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
90
93
  };
@@ -97,10 +100,10 @@ type UsersManagerConfig<TPresence extends JsonObject = {}> = {
97
100
  type MockedRoomEvents<TIO extends IOLike> = Partial<{
98
101
  [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
99
102
  }>;
100
- type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
103
+ type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
101
104
  events?: MockedRoomEvents<TIO>;
102
105
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
103
- declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
106
+ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
104
107
  private _crdtManager;
105
108
  private _crdtNotifier;
106
109
  private _eventNotifier;
@@ -116,21 +119,23 @@ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {},
116
119
  canRedo: () => boolean;
117
120
  canUndo: () => boolean;
118
121
  event: <TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => (() => void);
119
- getConnection(): WebSocketConnection;
122
+ getConnection: () => WebSocketConnection;
120
123
  getDoc(): AbstractCrdtDoc<TStorage>;
121
124
  getMyPresence: () => TPresence;
122
125
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
123
126
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
124
127
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
125
- getStorage: <TKey extends keyof TStorage>(key: TKey) => TStorage[TKey] | null;
128
+ getStorage: <TKey extends keyof TStorage>(type: TKey) => TStorage[TKey] | null;
129
+ getStorageJson(): InferCrdtJson<TStorage> | null;
130
+ getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
126
131
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
127
132
  redo: () => void;
128
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
129
- storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>; }) => void) => (() => void);
133
+ storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void) => (() => void);
134
+ storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtJson<TStorage[P]>; }) => void) => (() => void);
130
135
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
131
136
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
132
137
  undo: () => void;
133
- updateMyPresence: (presence: Partial<TPresence>) => void;
138
+ updateMyPresence: (presence: UpdateMyPresenceAction<TPresence>) => void;
134
139
  private _observeCrdt;
135
140
  }
136
141
 
@@ -154,8 +159,8 @@ type RoomEndpoints<TIO extends IOLike> = {
154
159
  } : {
155
160
  authEndpoint?: undefined;
156
161
  });
157
- type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
158
- interface PluvRoomAddonInput<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> {
162
+ type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
163
+ interface PluvRoomAddonInput<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> {
159
164
  room: PluvRoom<TIO, TPresence, TStorage>;
160
165
  }
161
166
  interface PluvRoomAddonResult {
@@ -165,13 +170,13 @@ type PluvRoomDebug<TIO extends IOLike> = Id<{
165
170
  output: readonly (keyof InferIOOutput<TIO>)[];
166
171
  input: readonly (keyof InferIOInput<TIO>)[];
167
172
  }>;
168
- type PluvRoomOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
173
+ type PluvRoomOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = {
169
174
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
170
175
  debug?: boolean | PluvRoomDebug<TIO>;
171
176
  onAuthorizationFail?: (error: Error) => void;
172
177
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
173
- type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
174
- declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
178
+ type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
179
+ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
175
180
  readonly _endpoints: RoomEndpoints<TIO>;
176
181
  private _crdtManager;
177
182
  private _crdtNotifier;
@@ -197,21 +202,23 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
197
202
  connect(): Promise<void>;
198
203
  disconnect(): Promise<void>;
199
204
  event: (<TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => () => void) & { [event in keyof InferIOOutput<TIO>]: (callback: EventNotifierSubscriptionCallback<TIO, event>) => () => void; };
200
- getConnection(): WebSocketConnection;
201
- getDoc(): AbstractCrdtDoc<TStorage>;
205
+ getConnection: () => WebSocketConnection;
206
+ getDoc: () => AbstractCrdtDoc<TStorage>;
202
207
  getMyPresence: () => TPresence;
203
208
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
204
209
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
205
210
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
206
- getStorage: <TKey extends keyof TStorage>(key: TKey) => TStorage[TKey] | null;
211
+ getStorage: <TKey extends keyof TStorage>(type: TKey) => TStorage[TKey] | null;
212
+ getStorageJson(): InferCrdtJson<TStorage> | null;
213
+ getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
207
214
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
208
215
  redo: () => void;
209
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
210
- storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>; }) => void) => (() => void);
216
+ storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void) => (() => void);
217
+ storageRoot: (fn: (value: { [P in keyof TStorage]: InferCrdtJson<TStorage[P]>; }) => void) => (() => void);
211
218
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
212
219
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
213
220
  undo: () => void;
214
- updateMyPresence: (presence: Partial<TPresence>) => void;
221
+ updateMyPresence: (presence: UpdateMyPresenceAction<TPresence>) => void;
215
222
  private _addToStorageStore;
216
223
  private _applyStorageStore;
217
224
  private _attachWindowListeners;
@@ -272,9 +279,9 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
272
279
  private _wsEndpoint;
273
280
  private _rooms;
274
281
  constructor(options: PluvClientOptions<TIO>);
275
- createRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}>(room: string, options: PluvRoomOptions<TIO, TPresence, TStorage>) => PluvRoom<TIO, TPresence, TStorage>;
282
+ createRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(room: string, options: PluvRoomOptions<TIO, TPresence, TStorage>) => PluvRoom<TIO, TPresence, TStorage>;
276
283
  enter: (room: string | PluvRoom<TIO, any, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
277
- getRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}>(room: string) => PluvRoom<TIO, TPresence, TStorage> | null;
284
+ getRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(room: string) => PluvRoom<TIO, TPresence, TStorage> | null;
278
285
  getRooms: () => readonly PluvRoom<TIO, JsonObject, any>[];
279
286
  leave: (room: string | PluvRoom<TIO, any, any>) => Promise<void>;
280
287
  private _logDebug;