@pluv/client 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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/client@0.19.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
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
- ESM dist/index.mjs 40.97 KB
12
- ESM ⚡️ Build success in 85ms
13
- CJS dist/index.js 42.24 KB
14
- CJS ⚡️ Build success in 108ms
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
15
  DTS Build start
16
- DTS ⚡️ Build success in 3379ms
17
- DTS dist/index.d.mts 14.33 KB
18
- DTS dist/index.d.ts 14.33 KB
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,82 @@
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
+
3
80
  ## 0.19.0
4
81
 
5
82
  ### Patch 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';
@@ -47,7 +47,7 @@ interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject
47
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;
48
48
  type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
49
49
 
50
- 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>> = {}> {
51
51
  readonly id: string;
52
52
  constructor(room: string);
53
53
  abstract storageLoaded: boolean;
@@ -61,12 +61,14 @@ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonOb
61
61
  abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
62
62
  abstract getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
63
63
  abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
64
- 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;
65
67
  abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
66
68
  abstract redo(): void;
67
- 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;
68
70
  abstract storageRoot(fn: (value: {
69
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
71
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
70
72
  }) => void): () => void;
71
73
  abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
72
74
  abstract transact(fn: (storage: TStorage) => void, origin?: string): void;
@@ -85,7 +87,7 @@ declare abstract class AbstractStorageStore {
85
87
  abstract initialize(): Promise<void>;
86
88
  }
87
89
 
88
- type CrdtManagerOptions<TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
90
+ type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
89
91
  encodedState?: string | Uint8Array | null;
90
92
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
91
93
  };
@@ -98,10 +100,10 @@ type UsersManagerConfig<TPresence extends JsonObject = {}> = {
98
100
  type MockedRoomEvents<TIO extends IOLike> = Partial<{
99
101
  [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
100
102
  }>;
101
- 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>> = {}> = {
102
104
  events?: MockedRoomEvents<TIO>;
103
105
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
104
- 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> {
105
107
  private _crdtManager;
106
108
  private _crdtNotifier;
107
109
  private _eventNotifier;
@@ -123,11 +125,13 @@ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {},
123
125
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
124
126
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
125
127
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
126
- 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;
127
131
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
128
132
  redo: () => void;
129
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
130
- 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);
131
135
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
132
136
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
133
137
  undo: () => void;
@@ -155,8 +159,8 @@ type RoomEndpoints<TIO extends IOLike> = {
155
159
  } : {
156
160
  authEndpoint?: undefined;
157
161
  });
158
- type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
159
- 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>> = {}> {
160
164
  room: PluvRoom<TIO, TPresence, TStorage>;
161
165
  }
162
166
  interface PluvRoomAddonResult {
@@ -166,13 +170,13 @@ type PluvRoomDebug<TIO extends IOLike> = Id<{
166
170
  output: readonly (keyof InferIOOutput<TIO>)[];
167
171
  input: readonly (keyof InferIOInput<TIO>)[];
168
172
  }>;
169
- 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>> = {}> = {
170
174
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
171
175
  debug?: boolean | PluvRoomDebug<TIO>;
172
176
  onAuthorizationFail?: (error: Error) => void;
173
177
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
174
- type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
175
- 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> {
176
180
  readonly _endpoints: RoomEndpoints<TIO>;
177
181
  private _crdtManager;
178
182
  private _crdtNotifier;
@@ -204,11 +208,13 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
204
208
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
205
209
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
206
210
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
207
- 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;
208
214
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
209
215
  redo: () => void;
210
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
211
- 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);
212
218
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
213
219
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
214
220
  undo: () => void;
@@ -273,9 +279,9 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
273
279
  private _wsEndpoint;
274
280
  private _rooms;
275
281
  constructor(options: PluvClientOptions<TIO>);
276
- 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>;
277
283
  enter: (room: string | PluvRoom<TIO, any, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
278
- 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;
279
285
  getRooms: () => readonly PluvRoom<TIO, JsonObject, any>[];
280
286
  leave: (room: string | PluvRoom<TIO, any, any>) => Promise<void>;
281
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';
@@ -47,7 +47,7 @@ interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject
47
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;
48
48
  type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
49
49
 
50
- 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>> = {}> {
51
51
  readonly id: string;
52
52
  constructor(room: string);
53
53
  abstract storageLoaded: boolean;
@@ -61,12 +61,14 @@ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonOb
61
61
  abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
62
62
  abstract getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
63
63
  abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
64
- 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;
65
67
  abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
66
68
  abstract redo(): void;
67
- 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;
68
70
  abstract storageRoot(fn: (value: {
69
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
71
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
70
72
  }) => void): () => void;
71
73
  abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
72
74
  abstract transact(fn: (storage: TStorage) => void, origin?: string): void;
@@ -85,7 +87,7 @@ declare abstract class AbstractStorageStore {
85
87
  abstract initialize(): Promise<void>;
86
88
  }
87
89
 
88
- type CrdtManagerOptions<TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
90
+ type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
89
91
  encodedState?: string | Uint8Array | null;
90
92
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
91
93
  };
@@ -98,10 +100,10 @@ type UsersManagerConfig<TPresence extends JsonObject = {}> = {
98
100
  type MockedRoomEvents<TIO extends IOLike> = Partial<{
99
101
  [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
100
102
  }>;
101
- 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>> = {}> = {
102
104
  events?: MockedRoomEvents<TIO>;
103
105
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
104
- 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> {
105
107
  private _crdtManager;
106
108
  private _crdtNotifier;
107
109
  private _eventNotifier;
@@ -123,11 +125,13 @@ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {},
123
125
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
124
126
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
125
127
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
126
- 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;
127
131
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
128
132
  redo: () => void;
129
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
130
- 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);
131
135
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
132
136
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
133
137
  undo: () => void;
@@ -155,8 +159,8 @@ type RoomEndpoints<TIO extends IOLike> = {
155
159
  } : {
156
160
  authEndpoint?: undefined;
157
161
  });
158
- type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
159
- 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>> = {}> {
160
164
  room: PluvRoom<TIO, TPresence, TStorage>;
161
165
  }
162
166
  interface PluvRoomAddonResult {
@@ -166,13 +170,13 @@ type PluvRoomDebug<TIO extends IOLike> = Id<{
166
170
  output: readonly (keyof InferIOOutput<TIO>)[];
167
171
  input: readonly (keyof InferIOInput<TIO>)[];
168
172
  }>;
169
- 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>> = {}> = {
170
174
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
171
175
  debug?: boolean | PluvRoomDebug<TIO>;
172
176
  onAuthorizationFail?: (error: Error) => void;
173
177
  } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
174
- type RoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
175
- 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> {
176
180
  readonly _endpoints: RoomEndpoints<TIO>;
177
181
  private _crdtManager;
178
182
  private _crdtNotifier;
@@ -204,11 +208,13 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
204
208
  getMyself: () => Id<UserInfo<TIO, TPresence>> | null;
205
209
  getOther: (connectionId: string) => Id<UserInfo<TIO, TPresence>> | null;
206
210
  getOthers: () => readonly Id<UserInfo<TIO, TPresence>>[];
207
- 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;
208
214
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
209
215
  redo: () => void;
210
- storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void) => (() => void);
211
- 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);
212
218
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
213
219
  transact: (fn: (storage: TStorage) => void, origin?: string) => void;
214
220
  undo: () => void;
@@ -273,9 +279,9 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
273
279
  private _wsEndpoint;
274
280
  private _rooms;
275
281
  constructor(options: PluvClientOptions<TIO>);
276
- 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>;
277
283
  enter: (room: string | PluvRoom<TIO, any, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
278
- 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;
279
285
  getRooms: () => readonly PluvRoom<TIO, JsonObject, any>[];
280
286
  leave: (room: string | PluvRoom<TIO, any, any>) => Promise<void>;
281
287
  private _logDebug;
package/dist/index.js CHANGED
@@ -369,10 +369,10 @@ var MockedRoom = class extends AbstractRoom {
369
369
  this.getOthers = () => {
370
370
  return this._usersManager.getOthers();
371
371
  };
372
- this.getStorage = (key) => {
373
- const sharedType = this._crdtManager.get(key);
372
+ this.getStorage = (type) => {
373
+ const sharedType = this._crdtManager.get(type);
374
374
  if (typeof sharedType === "undefined") return null;
375
- return sharedType.value;
375
+ return sharedType;
376
376
  };
377
377
  this.other = (connectionId, callback) => {
378
378
  return this._otherNotifier.subscribe(connectionId, callback);
@@ -439,6 +439,11 @@ var MockedRoom = class extends AbstractRoom {
439
439
  getDoc() {
440
440
  return this._crdtManager.doc;
441
441
  }
442
+ getStorageJson(type) {
443
+ if (this._state.connection.id === null) return null;
444
+ if (typeof type === "undefined") return this._crdtManager.doc.toJson();
445
+ return this._crdtManager.doc.toJson(type);
446
+ }
442
447
  _observeCrdt() {
443
448
  var _a, _b;
444
449
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
@@ -449,10 +454,10 @@ var MockedRoom = class extends AbstractRoom {
449
454
  if (!this._crdtManager) return;
450
455
  if (origin === "$STORAGE_UPDATED") return;
451
456
  const sharedTypes = this._crdtManager.doc.get();
452
- const storageRoot = Object.entries(sharedTypes).reduce(
453
- (acc, [prop, sharedType]) => {
457
+ const storageRoot = Object.keys(sharedTypes).reduce(
458
+ (acc, prop) => {
454
459
  if (!this._crdtManager) return acc;
455
- const serialized = sharedType.toJson();
460
+ const serialized = this._crdtManager.doc.toJson(prop);
456
461
  this._crdtNotifier.subject(prop).next(serialized);
457
462
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
458
463
  },
@@ -606,8 +611,8 @@ var PluvRoom = class extends AbstractRoom {
606
611
  this.getOthers = () => {
607
612
  return this._usersManager.getOthers();
608
613
  };
609
- this.getStorage = (key) => {
610
- const sharedType = this._crdtManager.get(key);
614
+ this.getStorage = (type) => {
615
+ const sharedType = this._crdtManager.get(type);
611
616
  if (typeof sharedType === "undefined") return null;
612
617
  return sharedType;
613
618
  };
@@ -744,6 +749,13 @@ var PluvRoom = class extends AbstractRoom {
744
749
  this._stateNotifier.subjects["my-presence"].next(null);
745
750
  });
746
751
  }
752
+ getStorageJson(type) {
753
+ if (this._state.connection.id === null) return null;
754
+ if (typeof type === "undefined") return this._crdtManager.doc.toJson();
755
+ const sharedType = this._crdtManager.get(type);
756
+ if (typeof sharedType === "undefined") return null;
757
+ return this._crdtManager.doc.toJson(type);
758
+ }
747
759
  _addToStorageStore(update) {
748
760
  return __async(this, null, function* () {
749
761
  yield this._storageStore.addUpdate(update);
@@ -830,9 +842,9 @@ var PluvRoom = class extends AbstractRoom {
830
842
  }
831
843
  _emitSharedTypes() {
832
844
  const sharedTypes = this._crdtManager.doc.get();
833
- const storageRoot = Object.entries(sharedTypes).reduce(
834
- (acc, [prop, sharedType]) => {
835
- const serialized = sharedType.toJson();
845
+ const storageRoot = Object.keys(sharedTypes).reduce(
846
+ (acc, prop) => {
847
+ const serialized = this._crdtManager.doc.toJson(prop);
836
848
  this._crdtNotifier.subject(prop).next(serialized);
837
849
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
838
850
  },
@@ -985,10 +997,10 @@ var PluvRoom = class extends AbstractRoom {
985
997
  update: data.state
986
998
  });
987
999
  const sharedTypes = this._crdtManager.doc.get();
988
- const storageRoot = Object.entries(sharedTypes).reduce(
989
- (acc, [prop, sharedType]) => {
1000
+ const storageRoot = Object.keys(sharedTypes).reduce(
1001
+ (acc, prop) => {
990
1002
  if (!this._crdtManager) return acc;
991
- const serialized = sharedType.toJson();
1003
+ const serialized = this._crdtManager.doc.toJson(prop);
992
1004
  this._crdtNotifier.subject(prop).next(serialized);
993
1005
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
994
1006
  },
package/dist/index.mjs CHANGED
@@ -340,10 +340,10 @@ var MockedRoom = class extends AbstractRoom {
340
340
  this.getOthers = () => {
341
341
  return this._usersManager.getOthers();
342
342
  };
343
- this.getStorage = (key) => {
344
- const sharedType = this._crdtManager.get(key);
343
+ this.getStorage = (type) => {
344
+ const sharedType = this._crdtManager.get(type);
345
345
  if (typeof sharedType === "undefined") return null;
346
- return sharedType.value;
346
+ return sharedType;
347
347
  };
348
348
  this.other = (connectionId, callback) => {
349
349
  return this._otherNotifier.subscribe(connectionId, callback);
@@ -410,6 +410,11 @@ var MockedRoom = class extends AbstractRoom {
410
410
  getDoc() {
411
411
  return this._crdtManager.doc;
412
412
  }
413
+ getStorageJson(type) {
414
+ if (this._state.connection.id === null) return null;
415
+ if (typeof type === "undefined") return this._crdtManager.doc.toJson();
416
+ return this._crdtManager.doc.toJson(type);
417
+ }
413
418
  _observeCrdt() {
414
419
  var _a, _b;
415
420
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
@@ -420,10 +425,10 @@ var MockedRoom = class extends AbstractRoom {
420
425
  if (!this._crdtManager) return;
421
426
  if (origin === "$STORAGE_UPDATED") return;
422
427
  const sharedTypes = this._crdtManager.doc.get();
423
- const storageRoot = Object.entries(sharedTypes).reduce(
424
- (acc, [prop, sharedType]) => {
428
+ const storageRoot = Object.keys(sharedTypes).reduce(
429
+ (acc, prop) => {
425
430
  if (!this._crdtManager) return acc;
426
- const serialized = sharedType.toJson();
431
+ const serialized = this._crdtManager.doc.toJson(prop);
427
432
  this._crdtNotifier.subject(prop).next(serialized);
428
433
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
429
434
  },
@@ -577,8 +582,8 @@ var PluvRoom = class extends AbstractRoom {
577
582
  this.getOthers = () => {
578
583
  return this._usersManager.getOthers();
579
584
  };
580
- this.getStorage = (key) => {
581
- const sharedType = this._crdtManager.get(key);
585
+ this.getStorage = (type) => {
586
+ const sharedType = this._crdtManager.get(type);
582
587
  if (typeof sharedType === "undefined") return null;
583
588
  return sharedType;
584
589
  };
@@ -715,6 +720,13 @@ var PluvRoom = class extends AbstractRoom {
715
720
  this._stateNotifier.subjects["my-presence"].next(null);
716
721
  });
717
722
  }
723
+ getStorageJson(type) {
724
+ if (this._state.connection.id === null) return null;
725
+ if (typeof type === "undefined") return this._crdtManager.doc.toJson();
726
+ const sharedType = this._crdtManager.get(type);
727
+ if (typeof sharedType === "undefined") return null;
728
+ return this._crdtManager.doc.toJson(type);
729
+ }
718
730
  _addToStorageStore(update) {
719
731
  return __async(this, null, function* () {
720
732
  yield this._storageStore.addUpdate(update);
@@ -801,9 +813,9 @@ var PluvRoom = class extends AbstractRoom {
801
813
  }
802
814
  _emitSharedTypes() {
803
815
  const sharedTypes = this._crdtManager.doc.get();
804
- const storageRoot = Object.entries(sharedTypes).reduce(
805
- (acc, [prop, sharedType]) => {
806
- const serialized = sharedType.toJson();
816
+ const storageRoot = Object.keys(sharedTypes).reduce(
817
+ (acc, prop) => {
818
+ const serialized = this._crdtManager.doc.toJson(prop);
807
819
  this._crdtNotifier.subject(prop).next(serialized);
808
820
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
809
821
  },
@@ -956,10 +968,10 @@ var PluvRoom = class extends AbstractRoom {
956
968
  update: data.state
957
969
  });
958
970
  const sharedTypes = this._crdtManager.doc.get();
959
- const storageRoot = Object.entries(sharedTypes).reduce(
960
- (acc, [prop, sharedType]) => {
971
+ const storageRoot = Object.keys(sharedTypes).reduce(
972
+ (acc, prop) => {
961
973
  if (!this._crdtManager) return acc;
962
- const serialized = sharedType.toJson();
974
+ const serialized = this._crdtManager.doc.toJson(prop);
963
975
  this._crdtNotifier.subject(prop).next(serialized);
964
976
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
965
977
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,15 +18,15 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "wonka": "^6.3.4",
21
- "@pluv/crdt": "^0.19.0",
22
- "@pluv/types": "^0.19.0"
21
+ "@pluv/crdt": "^0.20.0",
22
+ "@pluv/types": "^0.20.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "eslint": "^8.57.0",
26
26
  "tsup": "^8.1.0",
27
27
  "typescript": "^5.4.5",
28
- "@pluv/tsconfig": "^0.19.0",
29
- "eslint-config-pluv": "^0.19.0"
28
+ "@pluv/tsconfig": "^0.20.0",
29
+ "eslint-config-pluv": "^0.20.0"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,4 +1,4 @@
1
- import type { AbstractCrdtDoc, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
1
+ import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
2
2
  import type { IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
3
3
  import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
4
4
  import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
@@ -8,7 +8,7 @@ import type { UpdateMyPresenceAction, UserInfo, WebSocketConnection } from "./ty
8
8
  export abstract class AbstractRoom<
9
9
  TIO extends IOLike,
10
10
  TPresence extends JsonObject = {},
11
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
11
+ TStorage extends Record<string, CrdtType<any, any>> = {},
12
12
  > {
13
13
  public readonly id: string;
14
14
 
@@ -44,7 +44,10 @@ export abstract class AbstractRoom<
44
44
 
45
45
  public abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
46
46
 
47
- public abstract getStorage<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null;
47
+ public abstract getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
48
+
49
+ public abstract getStorageJson(): InferCrdtJson<TStorage> | null;
50
+ public abstract getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
48
51
 
49
52
  public abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
50
53
 
@@ -52,12 +55,12 @@ export abstract class AbstractRoom<
52
55
 
53
56
  public abstract storage<TKey extends keyof TStorage>(
54
57
  key: TKey,
55
- fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
58
+ fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
56
59
  ): () => void;
57
60
 
58
61
  public abstract storageRoot(
59
62
  fn: (value: {
60
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
63
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
61
64
  }) => void,
62
65
  ): () => void;
63
66
 
@@ -1,7 +1,7 @@
1
- import type { AbstractCrdtDoc, AbstractCrdtDocFactory, AbstractCrdtType } from "@pluv/crdt";
1
+ import type { AbstractCrdtDoc, AbstractCrdtDocFactory, CrdtType } from "@pluv/crdt";
2
2
  import { noop } from "@pluv/crdt";
3
3
 
4
- export type CrdtManagerOptions<TStorage extends Record<string, AbstractCrdtType<any, any>> = {}> = {
4
+ export type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
5
5
  encodedState?: string | Uint8Array | null;
6
6
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
7
7
  };
@@ -12,7 +12,7 @@ type CrdtManagerInitializeParams = {
12
12
  update: string | readonly string[];
13
13
  };
14
14
 
15
- export class CrdtManager<TStorage extends Record<string, AbstractCrdtType<any, any>>> {
15
+ export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
16
16
  public doc: AbstractCrdtDoc<TStorage>;
17
17
  public initialized: boolean = false;
18
18
 
@@ -1,31 +1,31 @@
1
- import type { AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
1
+ import type { CrdtType, InferCrdtJson } from "@pluv/crdt";
2
2
  import type { Subject } from "wonka";
3
3
  import { makeSubject, subscribe } from "wonka";
4
4
 
5
- export class CrdtNotifier<TStorage extends Record<string, AbstractCrdtType<any, any>>> {
5
+ export class CrdtNotifier<TStorage extends Record<string, CrdtType<any, any>>> {
6
6
  public rootSubject = makeSubject<{
7
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
7
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
8
8
  }>();
9
9
 
10
- public subjects = new Map<keyof TStorage, Subject<InferCrdtStorageJson<TStorage[keyof TStorage]>> | null>();
10
+ public subjects = new Map<keyof TStorage, Subject<InferCrdtJson<TStorage[keyof TStorage]>> | null>();
11
11
 
12
- public subject<TKey extends keyof TStorage>(key: TKey): Subject<InferCrdtStorageJson<TStorage[TKey]>> {
12
+ public subject<TKey extends keyof TStorage>(key: TKey): Subject<InferCrdtJson<TStorage[TKey]>> {
13
13
  const subject = this.subjects.get(key);
14
14
 
15
15
  if (subject) {
16
- return subject as Subject<InferCrdtStorageJson<TStorage[TKey]>>;
16
+ return subject as Subject<InferCrdtJson<TStorage[TKey]>>;
17
17
  }
18
18
 
19
- const newSubject = makeSubject<InferCrdtStorageJson<TStorage[TKey]>>();
19
+ const newSubject = makeSubject<InferCrdtJson<TStorage[TKey]>>();
20
20
 
21
21
  this.subjects.set(key, newSubject);
22
22
 
23
- return newSubject as Subject<InferCrdtStorageJson<TStorage[TKey]>>;
23
+ return newSubject as Subject<InferCrdtJson<TStorage[TKey]>>;
24
24
  }
25
25
 
26
26
  public subcribeRoot(
27
27
  callback: (value: {
28
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
28
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
29
29
  }) => void,
30
30
  ): () => void {
31
31
  return subscribe(callback)(this.rootSubject.source).unsubscribe;
@@ -33,7 +33,7 @@ export class CrdtNotifier<TStorage extends Record<string, AbstractCrdtType<any,
33
33
 
34
34
  public subscribe<TKey extends keyof TStorage>(
35
35
  key: TKey,
36
- callback: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
36
+ callback: (value: InferCrdtJson<TStorage[TKey]>) => void,
37
37
  ): () => void {
38
38
  const subject = this.subject(key);
39
39
 
package/src/MockedRoom.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AbstractCrdtDoc, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
1
+ import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
2
2
  import type { IOLike, Id, InferIOInput, InferIOOutput, JsonObject } from "@pluv/types";
3
3
  import { AbstractRoom } from "./AbstractRoom";
4
4
  import type { CrdtManagerOptions } from "./CrdtManager";
@@ -28,14 +28,14 @@ export type MockedRoomEvents<TIO extends IOLike> = Partial<{
28
28
  export type MockedRoomConfig<
29
29
  TIO extends IOLike,
30
30
  TPresence extends JsonObject = {},
31
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
31
+ TStorage extends Record<string, CrdtType<any, any>> = {},
32
32
  > = { events?: MockedRoomEvents<TIO> } & Omit<CrdtManagerOptions<TStorage>, "encodedState"> &
33
33
  UsersManagerConfig<TPresence>;
34
34
 
35
35
  export class MockedRoom<
36
36
  TIO extends IOLike,
37
37
  TPresence extends JsonObject = {},
38
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
38
+ TStorage extends Record<string, CrdtType<any, any>> = {},
39
39
  > extends AbstractRoom<TIO, TPresence, TStorage> {
40
40
  private _crdtManager: CrdtManager<TStorage>;
41
41
  private _crdtNotifier = new CrdtNotifier<TStorage>();
@@ -142,14 +142,24 @@ export class MockedRoom<
142
142
  return this._usersManager.getOthers();
143
143
  };
144
144
 
145
- public getStorage = <TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null => {
146
- const sharedType = this._crdtManager.get(key);
145
+ public getStorage = <TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null => {
146
+ const sharedType = this._crdtManager.get(type);
147
147
 
148
148
  if (typeof sharedType === "undefined") return null;
149
149
 
150
- return sharedType.value;
150
+ return sharedType;
151
151
  };
152
152
 
153
+ public getStorageJson(): InferCrdtJson<TStorage> | null;
154
+ public getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
155
+ public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
156
+ if (this._state.connection.id === null) return null;
157
+
158
+ if (typeof type === "undefined") return this._crdtManager.doc.toJson();
159
+
160
+ return this._crdtManager.doc.toJson(type);
161
+ }
162
+
153
163
  public other = (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): (() => void) => {
154
164
  return this._otherNotifier.subscribe(connectionId, callback);
155
165
  };
@@ -160,14 +170,14 @@ export class MockedRoom<
160
170
 
161
171
  public storage = <TKey extends keyof TStorage>(
162
172
  key: TKey,
163
- fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
173
+ fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
164
174
  ): (() => void) => {
165
175
  return this._crdtNotifier.subscribe(key, fn);
166
176
  };
167
177
 
168
178
  public storageRoot = (
169
179
  fn: (value: {
170
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
180
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
171
181
  }) => void,
172
182
  ): (() => void) => {
173
183
  return this._crdtNotifier.subcribeRoot(fn);
@@ -230,18 +240,18 @@ export class MockedRoom<
230
240
 
231
241
  const sharedTypes = this._crdtManager.doc.get();
232
242
 
233
- const storageRoot = Object.entries(sharedTypes).reduce(
234
- (acc, [prop, sharedType]) => {
243
+ const storageRoot = Object.keys(sharedTypes).reduce(
244
+ (acc, prop) => {
235
245
  if (!this._crdtManager) return acc;
236
246
 
237
- const serialized = sharedType.toJson();
247
+ const serialized = this._crdtManager.doc.toJson(prop);
238
248
 
239
249
  this._crdtNotifier.subject(prop).next(serialized);
240
250
 
241
251
  return { ...acc, [prop]: serialized };
242
252
  },
243
253
  {} as {
244
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
254
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
245
255
  },
246
256
  );
247
257
 
package/src/PluvClient.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { AbstractCrdtType } from "@pluv/crdt";
1
+ import type { CrdtType } from "@pluv/crdt";
2
2
  import type { IOLike, JsonObject } from "@pluv/types";
3
- import { AuthEndpoint, PluvRoom, PluvRoomOptions, RoomConfig, RoomEndpoints, WsEndpoint } from "./PluvRoom";
3
+ import type { AuthEndpoint, PluvRoomOptions, RoomConfig, RoomEndpoints, WsEndpoint } from "./PluvRoom";
4
+ import { PluvRoom } from "./PluvRoom";
4
5
 
5
6
  export type PluvClientOptions<TIO extends IOLike> = RoomEndpoints<TIO> & {
6
7
  debug?: boolean;
@@ -21,10 +22,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
21
22
  this._wsEndpoint = wsEndpoint;
22
23
  }
23
24
 
24
- public createRoom = <
25
- TPresence extends JsonObject = {},
26
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
27
- >(
25
+ public createRoom = <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
28
26
  room: string,
29
27
  options: PluvRoomOptions<TIO, TPresence, TStorage>,
30
28
  ): PluvRoom<TIO, TPresence, TStorage> => {
@@ -65,10 +63,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
65
63
  return toEnter;
66
64
  };
67
65
 
68
- public getRoom = <
69
- TPresence extends JsonObject = {},
70
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
71
- >(
66
+ public getRoom = <TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}>(
72
67
  room: string,
73
68
  ): PluvRoom<TIO, TPresence, TStorage> | null => {
74
69
  const found = this._rooms.get(room) as PluvRoom<TIO, TPresence, TStorage> | undefined;
package/src/PluvRoom.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AbstractCrdtDoc, AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
1
+ import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
2
2
  import type {
3
3
  BaseIOEventRecord,
4
4
  EventMessage,
@@ -47,7 +47,7 @@ const ORIGIN_STORAGE_UPDATED = "$STORAGE_UPDATED";
47
47
  export const DEFAULT_PLUV_CLIENT_ADDON = <
48
48
  TIO extends IOLike = IOLike,
49
49
  TPresence extends JsonObject = {},
50
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
50
+ TStorage extends Record<string, CrdtType<any, any>> = {},
51
51
  >(
52
52
  input: PluvRoomAddonInput<TIO, TPresence, TStorage>,
53
53
  ): PluvRoomAddonResult => ({
@@ -101,13 +101,13 @@ interface InternalListeners {
101
101
  export type PluvRoomAddon<
102
102
  TIO extends IOLike = IOLike,
103
103
  TPresence extends JsonObject = {},
104
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
104
+ TStorage extends Record<string, CrdtType<any, any>> = {},
105
105
  > = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
106
106
 
107
107
  export interface PluvRoomAddonInput<
108
108
  TIO extends IOLike = IOLike,
109
109
  TPresence extends JsonObject = {},
110
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
110
+ TStorage extends Record<string, CrdtType<any, any>> = {},
111
111
  > {
112
112
  room: PluvRoom<TIO, TPresence, TStorage>;
113
113
  }
@@ -124,7 +124,7 @@ export type PluvRoomDebug<TIO extends IOLike> = Id<{
124
124
  export type PluvRoomOptions<
125
125
  TIO extends IOLike,
126
126
  TPresence extends JsonObject = {},
127
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
127
+ TStorage extends Record<string, CrdtType<any, any>> = {},
128
128
  > = {
129
129
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
130
130
  debug?: boolean | PluvRoomDebug<TIO>;
@@ -135,13 +135,13 @@ export type PluvRoomOptions<
135
135
  export type RoomConfig<
136
136
  TIO extends IOLike,
137
137
  TPresence extends JsonObject = {},
138
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
138
+ TStorage extends Record<string, CrdtType<any, any>> = {},
139
139
  > = RoomEndpoints<TIO> & PluvRoomOptions<TIO, TPresence, TStorage>;
140
140
 
141
141
  export class PluvRoom<
142
142
  TIO extends IOLike,
143
143
  TPresence extends JsonObject = {},
144
- TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
144
+ TStorage extends Record<string, CrdtType<any, any>> = {},
145
145
  > extends AbstractRoom<TIO, TPresence, TStorage> {
146
146
  readonly _endpoints: RoomEndpoints<TIO>;
147
147
 
@@ -379,14 +379,28 @@ export class PluvRoom<
379
379
  return this._usersManager.getOthers();
380
380
  };
381
381
 
382
- public getStorage = <TKey extends keyof TStorage>(key: TKey): TStorage[TKey] | null => {
383
- const sharedType = this._crdtManager.get(key);
382
+ public getStorage = <TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null => {
383
+ const sharedType = this._crdtManager.get(type);
384
384
 
385
385
  if (typeof sharedType === "undefined") return null;
386
386
 
387
387
  return sharedType;
388
388
  };
389
389
 
390
+ public getStorageJson(): InferCrdtJson<TStorage> | null;
391
+ public getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
392
+ public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
393
+ if (this._state.connection.id === null) return null;
394
+
395
+ if (typeof type === "undefined") return this._crdtManager.doc.toJson();
396
+
397
+ const sharedType = this._crdtManager.get(type);
398
+
399
+ if (typeof sharedType === "undefined") return null;
400
+
401
+ return this._crdtManager.doc.toJson(type);
402
+ }
403
+
390
404
  public other = (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): (() => void) => {
391
405
  return this._otherNotifier.subscribe(connectionId, callback);
392
406
  };
@@ -397,14 +411,14 @@ export class PluvRoom<
397
411
 
398
412
  public storage = <TKey extends keyof TStorage>(
399
413
  key: TKey,
400
- fn: (value: InferCrdtStorageJson<TStorage[TKey]>) => void,
414
+ fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
401
415
  ): (() => void) => {
402
416
  return this._crdtNotifier.subscribe(key, fn);
403
417
  };
404
418
 
405
419
  public storageRoot = (
406
420
  fn: (value: {
407
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
421
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
408
422
  }) => void,
409
423
  ): (() => void) => {
410
424
  return this._crdtNotifier.subcribeRoot(fn);
@@ -562,17 +576,15 @@ export class PluvRoom<
562
576
  private _emitSharedTypes(): void {
563
577
  const sharedTypes = this._crdtManager.doc.get();
564
578
 
565
- const storageRoot = Object.entries(sharedTypes).reduce(
566
- (acc, [prop, sharedType]) => {
567
- const serialized = sharedType.toJson();
579
+ const storageRoot = Object.keys(sharedTypes).reduce(
580
+ (acc, prop) => {
581
+ const serialized = this._crdtManager.doc.toJson(prop);
568
582
 
569
583
  this._crdtNotifier.subject(prop).next(serialized);
570
584
 
571
585
  return { ...acc, [prop]: serialized };
572
586
  },
573
- {} as {
574
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
575
- },
587
+ {} as { [P in keyof TStorage]: InferCrdtJson<TStorage[P]> },
576
588
  );
577
589
 
578
590
  this._crdtNotifier.rootSubject.next(storageRoot);
@@ -812,18 +824,18 @@ export class PluvRoom<
812
824
 
813
825
  const sharedTypes = this._crdtManager.doc.get();
814
826
 
815
- const storageRoot = Object.entries(sharedTypes).reduce(
816
- (acc, [prop, sharedType]) => {
827
+ const storageRoot = Object.keys(sharedTypes).reduce(
828
+ (acc, prop) => {
817
829
  if (!this._crdtManager) return acc;
818
830
 
819
- const serialized = sharedType.toJson();
831
+ const serialized = this._crdtManager.doc.toJson(prop);
820
832
 
821
833
  this._crdtNotifier.subject(prop).next(serialized);
822
834
 
823
835
  return { ...acc, [prop]: serialized };
824
836
  },
825
837
  {} as {
826
- [P in keyof TStorage]: InferCrdtStorageJson<TStorage[P]>;
838
+ [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
827
839
  },
828
840
  );
829
841
 
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
1
+ export type { CrdtType, InferCrdtJson } from "@pluv/crdt";
2
2
  export type { BaseUser, EventMessage, EventRecord, IOEventMessage } from "@pluv/types";
3
3
  export { AbstractRoom } from "./AbstractRoom";
4
4
  export { AbstractStorageStore } from "./AbstractStorageStore";