@pluv/io 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +159 -0
- package/README.md +23 -42
- package/dist/index.d.mts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +30 -12
- package/dist/index.mjs +30 -12
- package/package.json +8 -12
- package/src/AbstractPersistance.ts +1 -1
- package/src/IORoom.ts +20 -9
- package/src/PluvIO.ts +19 -8
- package/src/authorize.ts +2 -2
- package/src/createIO.ts +4 -2
- package/src/types.ts +2 -10
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/io@0.
|
|
2
|
+
> @pluv/io@0.16.1 build /home/runner/work/pluv/pluv/packages/io
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.0.
|
|
7
|
+
[34mCLI[39m tsup v8.0.2
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m31.65 KB[39m
|
|
12
|
+
[32mCJS[39m ⚡️ Build success in 90ms
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m29.59 KB[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 94ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m17.
|
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m17.
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 3337ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m17.61 KB[39m
|
|
18
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m17.61 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,164 @@
|
|
|
1
1
|
# @pluv/io
|
|
2
2
|
|
|
3
|
+
## 0.16.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cd05d96: Updated outdated README
|
|
8
|
+
- @pluv/crdt@0.16.1
|
|
9
|
+
- @pluv/types@0.16.1
|
|
10
|
+
|
|
11
|
+
## 0.16.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 4280220: ## Breaking Changes
|
|
16
|
+
|
|
17
|
+
- Storage types are now kept on the root of the document.
|
|
18
|
+
- Previously, `@pluv/crdt-yjs` kept all shared-types on a hidden Yjs Map called `storage` on the root of the Yjs Doc. Now all shared-types are kept on the root of the Yjs Doc instead. This behavior should be more in-line with how shared-types are documented to be used from Yjs.
|
|
19
|
+
- `@pluv/client` and `@pluv/react` no-longer re-export `@pluv/crdt-yjs`. This package will now need to be installed separately.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# if installing @pluv/client
|
|
23
|
+
npm install @pluv/client @pluv/crdt-yjs
|
|
24
|
+
# if installing @pluv/react
|
|
25
|
+
npm install @pluv/react @pluv/crdt-yjs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// Before:
|
|
30
|
+
import { createClient, y } from "@pluv/client";
|
|
31
|
+
// After:
|
|
32
|
+
import { createClient } from "@pluv/client";
|
|
33
|
+
import { yjs } from "@pluv/crdt-yjs"; // y renamed to yjs
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- The `y` import has been renamed to `yjs`.
|
|
37
|
+
```ts
|
|
38
|
+
// Before:
|
|
39
|
+
import { y } from "@pluv/crdt-yjs";
|
|
40
|
+
// After:
|
|
41
|
+
import { yjs } from "@pluv/crdt-yjs";
|
|
42
|
+
```
|
|
43
|
+
- `@pluv/crdt-yjs` functions no-longer return Yjs shared-types directly, but instead return an `AbstractCrdtType` from `@pluv/crdt` (new package). This also affects methods that return storage shared types in both `@pluv/client` and `@pluv/react` from functions like `PluvRoom.getStorage` and `PluvRoom.useStorage`.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// Before:
|
|
47
|
+
import { y } from "@pluv/crdt-yjs";
|
|
48
|
+
import type { Array as YArray, Map as YMap } from "yjs";
|
|
49
|
+
|
|
50
|
+
const array: YArray<YMap<{ message: string }>> = y.array([
|
|
51
|
+
y.object({ message: "Hello" }),
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
array.push([y.object({ message: "World!" })]);
|
|
55
|
+
|
|
56
|
+
// After:
|
|
57
|
+
import type { CrdtYjsArray, CrdtYjsObject } from "@pluv/crdt-yjs";
|
|
58
|
+
import { yjs } from "@pluv/crdt-yjs";
|
|
59
|
+
import type { Array as YArray } from "yjs";
|
|
60
|
+
|
|
61
|
+
const array: CrdtYjsArray<CrdtYjsObject<{ message: string }>> = yjs.array([
|
|
62
|
+
yjs.object({ message: "Hello" }),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
array.push(yjs.object({ message: "World!" }));
|
|
66
|
+
|
|
67
|
+
// use .value to get the underlying yjs type
|
|
68
|
+
const yarray: YArray<YMap<{ message: string }>> = array.value;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- `initialStorage` must now use the `doc` function from `@pluv/crdt-yjs` to be defined. This also affects `@pluv/react`.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
// Before:
|
|
75
|
+
import { createClient, y } from "@pluv/client";
|
|
76
|
+
|
|
77
|
+
const client = createClient({
|
|
78
|
+
/* ... */
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
client.createRoom({
|
|
82
|
+
// ...
|
|
83
|
+
initialStorage: () => ({
|
|
84
|
+
messages: y.array([y.object({ message: "Hello world!" })]),
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// After:
|
|
89
|
+
import { createClient } from "@pluv/client";
|
|
90
|
+
// This is now imported separately
|
|
91
|
+
import { yjs } from "@pluv/crdt-yjs";
|
|
92
|
+
|
|
93
|
+
const client = createClient({
|
|
94
|
+
/* ... */
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
client.createRoom({
|
|
98
|
+
// ...
|
|
99
|
+
initialStorage: yjs.doc(() => ({
|
|
100
|
+
messages: yjs.array([yjs.object({ message: "Hello world!" })]),
|
|
101
|
+
})),
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- `@pluv/client` can now instead use [loro](https://github.com/loro-dev/loro) instead of [yjs](https://github.com/yjs/yjs).
|
|
106
|
+
```bash
|
|
107
|
+
npm install @pluv/client @pluv/crdt-loro loro-crdt
|
|
108
|
+
```
|
|
109
|
+
- If you are using storage features with pluv, `@pluv/io` must now specify which crdt library (i.e. `@pluv/crdt-yjs` or `@pluv/crdt-loro`) it is meant to use.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { loro } from "@pluv/crdt-loro";
|
|
113
|
+
import { yjs } from "@pluv/crdt-yjs";
|
|
114
|
+
import { createIO } from "@pluv/io";
|
|
115
|
+
|
|
116
|
+
const io = createIO({
|
|
117
|
+
// ...
|
|
118
|
+
// If using loro
|
|
119
|
+
crdt: loro,
|
|
120
|
+
// If using yjs
|
|
121
|
+
crdt: yjs,
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- `PluvRoom.getDoc()` from `@pluv/client` no-longer returns the Yjs Doc instance, but rather a `AbstractCrdtDoc` instance from `@pluv/crdt`. This also affects the hook from `@pluv/react`.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// Before:
|
|
129
|
+
import { Doc as YDoc } from "yjs";
|
|
130
|
+
|
|
131
|
+
const room: PluvRoom = client.createRoom({
|
|
132
|
+
/* ... */
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const doc: YDoc = room.getDoc();
|
|
136
|
+
|
|
137
|
+
// After:
|
|
138
|
+
import { CrdtYjsDoc } from "@pluv/crdt-yjs";
|
|
139
|
+
import { Doc as YDoc } from "yjs";
|
|
140
|
+
|
|
141
|
+
const room: PluvRoom = client.createRoom({
|
|
142
|
+
/* ... */
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const doc: CrdtYjsDoc<any> = room.getDoc();
|
|
146
|
+
const ydoc: YDoc = doc.value;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- `@pluv/client` and `@pluv/react` now may return `null` when retrieving storage while storage is being initialized.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const sharedType = room.getStorage("messages"); // This may be null
|
|
153
|
+
|
|
154
|
+
const [data, sharedType] = useStorage("messages"); // data and sharedType may both be null
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Patch Changes
|
|
158
|
+
|
|
159
|
+
- @pluv/crdt@0.16.0
|
|
160
|
+
- @pluv/types@0.16.0
|
|
161
|
+
|
|
3
162
|
## 0.15.0
|
|
4
163
|
|
|
5
164
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<br />
|
|
6
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
7
|
<br />
|
|
8
|
-
Pluv.IO (preview)
|
|
8
|
+
<a href="https://pluv.io/docs/introduction">Pluv.IO (preview)</a>
|
|
9
9
|
<br />
|
|
10
10
|
</h1>
|
|
11
11
|
|
|
12
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> 💕
|
|
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
14
|
|
|
15
15
|
<p align="center">
|
|
16
16
|
<a href="https://www.npmjs.com/package/@pluv/io">
|
|
@@ -22,13 +22,7 @@
|
|
|
22
22
|
<a href="https://commitizen.github.io/cz-cli/">
|
|
23
23
|
<img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="Commitizen friendly" />
|
|
24
24
|
</a>
|
|
25
|
-
</p>
|
|
26
|
-
|
|
27
|
-
<p align="center">
|
|
28
25
|
<img src="https://badgen.net/badge/-/TypeScript?icon=typescript&label&labelColor=blue&color=555555" alt="TypeScript" />
|
|
29
|
-
<a href="https://openbase.com/js/@pluv/io?utm_source=embedded&utm_medium=badge&utm_campaign=rate-badge">
|
|
30
|
-
<img src="https://badges.openbase.com/js/featured/@pluv/io.svg?token=FdWfpo90mCUb7InAjDG0KeXM/uH+KdHK3us2pNRgWf0=" alt="Featured on Openbase" />
|
|
31
|
-
</a>
|
|
32
26
|
</p>
|
|
33
27
|
|
|
34
28
|
<p align="center">
|
|
@@ -39,32 +33,12 @@
|
|
|
39
33
|
<a href="#license">License</a>
|
|
40
34
|
</p>
|
|
41
35
|
|
|
42
|
-
<p align="center">
|
|
43
|
-
<img src="https://github.com/pluv-io/pluv/blob/master/assets/demo-events.gif?raw=true" alt="Demo" />
|
|
44
|
-
</p>
|
|
45
|
-
|
|
46
36
|
## Intro
|
|
47
37
|
|
|
48
|
-
|
|
38
|
+
pluv.io allows you to more easily build real-time collaborative experiences with a fully end-to-end type-safe api and the ecosystem of existing CRDT implementations such as **[yjs](https://docs.yjs.dev/)**.
|
|
49
39
|
|
|
50
40
|
**👉 See full documentation on [pluv.io](https://pluv.io/docs/introduction). 👈**
|
|
51
41
|
|
|
52
|
-
### Why?
|
|
53
|
-
|
|
54
|
-
So you can do this:
|
|
55
|
-
|
|
56
|
-
```tsx
|
|
57
|
-
const broadcast = usePluvBroadcast();
|
|
58
|
-
|
|
59
|
-
usePluvEvent("RECEIVE_MESSAGE", ({ data }) => {
|
|
60
|
-
setMessages([...messages, data.message]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
broadcast("SEND_MESSAGE", { message: "Hello world!" });
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
And more. With **E2E type-safety**, **great intellisense** and the **[yjs](https://docs.yjs.dev/) ecosystem**.
|
|
67
|
-
|
|
68
42
|
### Features
|
|
69
43
|
|
|
70
44
|
- ✅ Automatic type safety
|
|
@@ -72,14 +46,21 @@ And more. With **E2E type-safety**, **great intellisense** and the **[yjs](https
|
|
|
72
46
|
- ✅ Rooms
|
|
73
47
|
- ✅ Authentication
|
|
74
48
|
- ✅ Awareness + Presence
|
|
75
|
-
- ✅ [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
|
|
76
|
-
- ✅
|
|
77
|
-
- ✅
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
49
|
+
- ✅ [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
|
|
50
|
+
- ✅ [Yjs](https://docs.yjs.dev/)
|
|
51
|
+
- ✅ **Shared Types**
|
|
52
|
+
- ✅ [Map](https://docs.yjs.dev/api/shared-types/y.map)
|
|
53
|
+
- ✅ [Array](https://docs.yjs.dev/api/shared-types/y.array)
|
|
54
|
+
- ✅ [Text](https://docs.yjs.dev/api/shared-types/y.text)
|
|
55
|
+
- ✅ [XmlFragment](https://docs.yjs.dev/api/shared-types/y.xmlfragment)
|
|
56
|
+
- ✅ [XmlElement](https://docs.yjs.dev/api/shared-types/y.xmlelement)
|
|
57
|
+
- ✅ [XmlText](https://docs.yjs.dev/api/shared-types/y.xmltext)
|
|
58
|
+
- ✅ [Loro](https://loro.dev/)
|
|
59
|
+
- ✅ **Containers**
|
|
60
|
+
- ✅ List
|
|
61
|
+
- ✅ Map
|
|
62
|
+
- ✅ Text
|
|
63
|
+
- ⬜ Tree
|
|
83
64
|
- ⬜ Studio (admin & developer panel)
|
|
84
65
|
|
|
85
66
|
### Runtimes
|
|
@@ -96,7 +77,8 @@ And more. With **E2E type-safety**, **great intellisense** and the **[yjs](https
|
|
|
96
77
|
|
|
97
78
|
### Frontends
|
|
98
79
|
|
|
99
|
-
- ✅ [React.js](https://
|
|
80
|
+
- ✅ [React.js](https://react.dev/)
|
|
81
|
+
- ⬜ [Solid.js](https://www.solidjs.com/)
|
|
100
82
|
- ⬜ [Vue.js](https://vuejs.org/)
|
|
101
83
|
- ⬜ [Svelte](https://svelte.dev/)
|
|
102
84
|
|
|
@@ -104,9 +86,6 @@ And more. With **E2E type-safety**, **great intellisense** and the **[yjs](https
|
|
|
104
86
|
|
|
105
87
|
Before diving into documentation, check out usage instructions for your selected platform:
|
|
106
88
|
|
|
107
|
-
> **Note:**
|
|
108
|
-
> `@pluv/io`, `@pluv/client`, `@pluv/crdt-yjs` and `@pluv/react` all require [yjs](https://docs.yjs.dev/) as a peer dependency.
|
|
109
|
-
|
|
110
89
|
### Documentation
|
|
111
90
|
|
|
112
91
|
Documentation is available at [pluv.io](https://pluv.io/docs/introduction).
|
|
@@ -114,7 +93,8 @@ Documentation is available at [pluv.io](https://pluv.io/docs/introduction).
|
|
|
114
93
|
## Related
|
|
115
94
|
|
|
116
95
|
- [@pluv/client](https://www.npmjs.com/package/@pluv/client) - Framework agnostic client
|
|
117
|
-
- [@pluv/crdt-
|
|
96
|
+
- [@pluv/crdt-loro](https://www.npmjs.com/package/@pluv/crdt-loro) - Loro CRDT for Pluv.IO
|
|
97
|
+
- [@pluv/crdt-yjs](https://www.npmjs.com/package/@pluv/crdt-yjs) - Yjs CRDT for Pluv.IO
|
|
118
98
|
- [@pluv/persistance-redis](https://www.npmjs.com/package/@pluv/persistance-redis) - Persistance for storage on distributed systems (Node.js only)
|
|
119
99
|
- [@pluv/platform-cloudflare](https://www.npmjs.com/package/@pluv/platform-cloudflare) - Adapter to run @pluv/io on Cloudflare Workers
|
|
120
100
|
- [@pluv/platform-node](https://www.npmjs.com/package/@pluv/platform-node) - Adapter to run @pluv/io on Node.js
|
|
@@ -125,6 +105,7 @@ Documentation is available at [pluv.io](https://pluv.io/docs/introduction).
|
|
|
125
105
|
|
|
126
106
|
This software uses the following open source tooling and libraries:
|
|
127
107
|
|
|
108
|
+
- [Loro](https://loro.dev/)
|
|
128
109
|
- [Yjs](https://yjs.dev/)
|
|
129
110
|
- [Node.js](https://nodejs.org/)
|
|
130
111
|
- [Cloudflare Workers](https://workers.cloudflare.com/)
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonObject, MaybePromise, IOEventMessage, EventRecord, InputZodLike, Id, IOLike, InferIOOutput, IOAuthorize, BaseIOAuthorize, InferIOInput, BaseUser, InferIOAuthorizeUser, InferIOAuthorize, Spread, InferIOOutputBroadcast, InferIOOutputSelf, InferIOOutputSync, Maybe, BaseClientEventRecord, BaseIOEventRecord } from '@pluv/types';
|
|
2
2
|
export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
|
|
3
|
-
import {
|
|
3
|
+
import { AbstractCrdtDoc, AbstractCrdtDocFactory } from '@pluv/crdt';
|
|
4
4
|
|
|
5
5
|
declare abstract class AbstractPersistance {
|
|
6
6
|
abstract addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
|
|
@@ -88,7 +88,7 @@ type SyncEventResolver<TContext extends Record<string, any> = {}, TData extends
|
|
|
88
88
|
type EventResolver<TContext extends Record<string, any> = {}, TData extends JsonObject = {}, TResultBroadcast extends EventRecord<string, any> = {}> = (data: TData, context: EventResolverContext<TContext>) => MaybePromise<TResultBroadcast | void>;
|
|
89
89
|
interface EventResolverContext<TContext extends Record<string, any> = {}> {
|
|
90
90
|
context: TContext;
|
|
91
|
-
doc:
|
|
91
|
+
doc: AbstractCrdtDoc<any>;
|
|
92
92
|
room: string;
|
|
93
93
|
/**
|
|
94
94
|
* !HACK
|
|
@@ -175,6 +175,9 @@ interface IORoomListeners<TPlatform extends AbstractPlatform> {
|
|
|
175
175
|
type IORoomConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize, TContext extends JsonObject = {}, TInput extends EventRecord<string, any> = {}, TOutputBroadcast extends EventRecord<string, any> = {}, TOutputSelf extends EventRecord<string, any> = {}, TOutputSync extends EventRecord<string, any> = {}> = Partial<IORoomListeners<TPlatform>> & {
|
|
176
176
|
authorize?: TAuthorize;
|
|
177
177
|
context: TContext & InferPlatformRoomContextType<TPlatform>;
|
|
178
|
+
crdt?: {
|
|
179
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
180
|
+
};
|
|
178
181
|
debug: boolean;
|
|
179
182
|
events: InferEventConfig<TPlatform, TContext, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>;
|
|
180
183
|
platform: TPlatform;
|
|
@@ -185,6 +188,7 @@ type WebsocketRegisterOptions<TPlatform extends AbstractPlatform> = {
|
|
|
185
188
|
declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize, TContext extends Record<string, any> = {}, TInput extends EventRecord<string, any> = {}, TOutputBroadcast extends EventRecord<string, any> = {}, TOutputSelf extends EventRecord<string, any> = {}, TOutputSync extends EventRecord<string, any> = {}> implements IOLike<TAuthorize, TInput, TOutputBroadcast, TOutputSelf, TOutputSync> {
|
|
186
189
|
private readonly _context;
|
|
187
190
|
private readonly _debug;
|
|
191
|
+
private readonly _docFactory;
|
|
188
192
|
private readonly _platform;
|
|
189
193
|
private _doc;
|
|
190
194
|
private _listeners;
|
|
@@ -248,6 +252,9 @@ interface PluvIOListeners<TPlatform extends AbstractPlatform> {
|
|
|
248
252
|
type PluvIOConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends IOAuthorize<any, any> | null = null, TContext extends JsonObject = {}, TInput extends EventRecord<string, any> = {}, TOutputBroadcast extends EventRecord<string, any> = {}, TOutputSelf extends EventRecord<string, any> = {}, TOutputSync extends EventRecord<string, any> = {}> = Partial<PluvIOListeners<TPlatform>> & {
|
|
249
253
|
authorize?: TAuthorize;
|
|
250
254
|
context?: TContext;
|
|
255
|
+
crdt?: {
|
|
256
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
257
|
+
};
|
|
251
258
|
debug?: boolean;
|
|
252
259
|
events?: InferEventConfig<TPlatform, TContext, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>;
|
|
253
260
|
getInitialStorage?: GetInitialStorageFn<TPlatform>;
|
|
@@ -260,6 +267,9 @@ declare class PluvIO<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
260
267
|
private _rooms;
|
|
261
268
|
readonly _authorize: TAuthorize | null;
|
|
262
269
|
readonly _context: TContext;
|
|
270
|
+
readonly _crdt: {
|
|
271
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
272
|
+
};
|
|
263
273
|
readonly _debug: boolean;
|
|
264
274
|
readonly _events: InferEventConfig<TPlatform, TContext, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>;
|
|
265
275
|
readonly _getInitialStorage: GetInitialStorageFn<TPlatform> | null;
|
|
@@ -277,6 +287,9 @@ declare class PluvIO<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
277
287
|
type CreateIOParams<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TContext extends JsonObject = {}, TAuthorizeUser extends BaseUser = BaseUser, TAuthorizeRequired extends boolean = false> = Partial<PluvIOListeners<TPlatform>> & {
|
|
278
288
|
authorize?: IOAuthorize<TAuthorizeUser, TAuthorizeRequired>;
|
|
279
289
|
context?: TContext;
|
|
290
|
+
crdt?: {
|
|
291
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
292
|
+
};
|
|
280
293
|
debug?: boolean;
|
|
281
294
|
getInitialStorage?: GetInitialStorageFn<TPlatform>;
|
|
282
295
|
platform: TPlatform;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonObject, MaybePromise, IOEventMessage, EventRecord, InputZodLike, Id, IOLike, InferIOOutput, IOAuthorize, BaseIOAuthorize, InferIOInput, BaseUser, InferIOAuthorizeUser, InferIOAuthorize, Spread, InferIOOutputBroadcast, InferIOOutputSelf, InferIOOutputSync, Maybe, BaseClientEventRecord, BaseIOEventRecord } from '@pluv/types';
|
|
2
2
|
export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
|
|
3
|
-
import {
|
|
3
|
+
import { AbstractCrdtDoc, AbstractCrdtDocFactory } from '@pluv/crdt';
|
|
4
4
|
|
|
5
5
|
declare abstract class AbstractPersistance {
|
|
6
6
|
abstract addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
|
|
@@ -88,7 +88,7 @@ type SyncEventResolver<TContext extends Record<string, any> = {}, TData extends
|
|
|
88
88
|
type EventResolver<TContext extends Record<string, any> = {}, TData extends JsonObject = {}, TResultBroadcast extends EventRecord<string, any> = {}> = (data: TData, context: EventResolverContext<TContext>) => MaybePromise<TResultBroadcast | void>;
|
|
89
89
|
interface EventResolverContext<TContext extends Record<string, any> = {}> {
|
|
90
90
|
context: TContext;
|
|
91
|
-
doc:
|
|
91
|
+
doc: AbstractCrdtDoc<any>;
|
|
92
92
|
room: string;
|
|
93
93
|
/**
|
|
94
94
|
* !HACK
|
|
@@ -175,6 +175,9 @@ interface IORoomListeners<TPlatform extends AbstractPlatform> {
|
|
|
175
175
|
type IORoomConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize, TContext extends JsonObject = {}, TInput extends EventRecord<string, any> = {}, TOutputBroadcast extends EventRecord<string, any> = {}, TOutputSelf extends EventRecord<string, any> = {}, TOutputSync extends EventRecord<string, any> = {}> = Partial<IORoomListeners<TPlatform>> & {
|
|
176
176
|
authorize?: TAuthorize;
|
|
177
177
|
context: TContext & InferPlatformRoomContextType<TPlatform>;
|
|
178
|
+
crdt?: {
|
|
179
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
180
|
+
};
|
|
178
181
|
debug: boolean;
|
|
179
182
|
events: InferEventConfig<TPlatform, TContext, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>;
|
|
180
183
|
platform: TPlatform;
|
|
@@ -185,6 +188,7 @@ type WebsocketRegisterOptions<TPlatform extends AbstractPlatform> = {
|
|
|
185
188
|
declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize, TContext extends Record<string, any> = {}, TInput extends EventRecord<string, any> = {}, TOutputBroadcast extends EventRecord<string, any> = {}, TOutputSelf extends EventRecord<string, any> = {}, TOutputSync extends EventRecord<string, any> = {}> implements IOLike<TAuthorize, TInput, TOutputBroadcast, TOutputSelf, TOutputSync> {
|
|
186
189
|
private readonly _context;
|
|
187
190
|
private readonly _debug;
|
|
191
|
+
private readonly _docFactory;
|
|
188
192
|
private readonly _platform;
|
|
189
193
|
private _doc;
|
|
190
194
|
private _listeners;
|
|
@@ -248,6 +252,9 @@ interface PluvIOListeners<TPlatform extends AbstractPlatform> {
|
|
|
248
252
|
type PluvIOConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends IOAuthorize<any, any> | null = null, TContext extends JsonObject = {}, TInput extends EventRecord<string, any> = {}, TOutputBroadcast extends EventRecord<string, any> = {}, TOutputSelf extends EventRecord<string, any> = {}, TOutputSync extends EventRecord<string, any> = {}> = Partial<PluvIOListeners<TPlatform>> & {
|
|
249
253
|
authorize?: TAuthorize;
|
|
250
254
|
context?: TContext;
|
|
255
|
+
crdt?: {
|
|
256
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
257
|
+
};
|
|
251
258
|
debug?: boolean;
|
|
252
259
|
events?: InferEventConfig<TPlatform, TContext, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>;
|
|
253
260
|
getInitialStorage?: GetInitialStorageFn<TPlatform>;
|
|
@@ -260,6 +267,9 @@ declare class PluvIO<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
260
267
|
private _rooms;
|
|
261
268
|
readonly _authorize: TAuthorize | null;
|
|
262
269
|
readonly _context: TContext;
|
|
270
|
+
readonly _crdt: {
|
|
271
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
272
|
+
};
|
|
263
273
|
readonly _debug: boolean;
|
|
264
274
|
readonly _events: InferEventConfig<TPlatform, TContext, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>;
|
|
265
275
|
readonly _getInitialStorage: GetInitialStorageFn<TPlatform> | null;
|
|
@@ -277,6 +287,9 @@ declare class PluvIO<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
277
287
|
type CreateIOParams<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TContext extends JsonObject = {}, TAuthorizeUser extends BaseUser = BaseUser, TAuthorizeRequired extends boolean = false> = Partial<PluvIOListeners<TPlatform>> & {
|
|
278
288
|
authorize?: IOAuthorize<TAuthorizeUser, TAuthorizeRequired>;
|
|
279
289
|
context?: TContext;
|
|
290
|
+
crdt?: {
|
|
291
|
+
doc: (value: any) => AbstractCrdtDocFactory<any>;
|
|
292
|
+
};
|
|
280
293
|
debug?: boolean;
|
|
281
294
|
getInitialStorage?: GetInitialStorageFn<TPlatform>;
|
|
282
295
|
platform: TPlatform;
|
package/dist/index.js
CHANGED
|
@@ -261,10 +261,11 @@ var AbstractWebSocket = class {
|
|
|
261
261
|
};
|
|
262
262
|
|
|
263
263
|
// src/PluvIO.ts
|
|
264
|
+
var import_crdt2 = require("@pluv/crdt");
|
|
264
265
|
var import_kleur2 = __toESM(require("kleur"));
|
|
265
266
|
|
|
266
267
|
// src/IORoom.ts
|
|
267
|
-
var
|
|
268
|
+
var import_crdt = require("@pluv/crdt");
|
|
268
269
|
var import_kleur = __toESM(require("kleur"));
|
|
269
270
|
|
|
270
271
|
// src/authorize.ts
|
|
@@ -307,13 +308,22 @@ var authorize = (params) => {
|
|
|
307
308
|
var PING_TIMEOUT_MS = 3e4;
|
|
308
309
|
var IORoom = class {
|
|
309
310
|
constructor(id, config) {
|
|
310
|
-
this._doc = (0, import_crdt_yjs.doc)();
|
|
311
311
|
this._sessions = /* @__PURE__ */ new Map();
|
|
312
312
|
this._uninitialize = null;
|
|
313
313
|
this._authorize = null;
|
|
314
|
-
const {
|
|
314
|
+
const {
|
|
315
|
+
authorize: authorize2,
|
|
316
|
+
context,
|
|
317
|
+
crdt = import_crdt.noop,
|
|
318
|
+
debug,
|
|
319
|
+
events,
|
|
320
|
+
onDestroy,
|
|
321
|
+
platform
|
|
322
|
+
} = config;
|
|
315
323
|
this._context = context;
|
|
316
324
|
this._debug = debug;
|
|
325
|
+
this._docFactory = crdt.doc(() => ({}));
|
|
326
|
+
this._doc = this._docFactory.getEmpty();
|
|
317
327
|
this._events = events;
|
|
318
328
|
this._platform = platform;
|
|
319
329
|
this.id = id;
|
|
@@ -516,9 +526,9 @@ var IORoom = class {
|
|
|
516
526
|
);
|
|
517
527
|
this._uninitialize = () => __async(this, null, function* () {
|
|
518
528
|
this._platform.pubSub.unsubscribe(pubSubId);
|
|
519
|
-
const encodedState = this._doc.
|
|
529
|
+
const encodedState = this._doc.getEncodedState();
|
|
520
530
|
this._doc.destroy();
|
|
521
|
-
this._doc =
|
|
531
|
+
this._doc = this._docFactory.getEmpty();
|
|
522
532
|
this._listeners.onDestroy(__spreadProps(__spreadValues({}, this._context), {
|
|
523
533
|
encodedState,
|
|
524
534
|
room: this.id
|
|
@@ -769,7 +779,7 @@ var PluvIO = class _PluvIO {
|
|
|
769
779
|
}
|
|
770
780
|
};
|
|
771
781
|
},
|
|
772
|
-
self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { context, doc
|
|
782
|
+
self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { context, doc, room }) {
|
|
773
783
|
var _a, _b;
|
|
774
784
|
const oldState = yield this._platform.persistance.getStorageState(room);
|
|
775
785
|
const updates = [
|
|
@@ -779,7 +789,7 @@ var PluvIO = class _PluvIO {
|
|
|
779
789
|
update
|
|
780
790
|
];
|
|
781
791
|
if (updates.some((_update) => !!_update)) {
|
|
782
|
-
const encodedState =
|
|
792
|
+
const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
|
|
783
793
|
yield this._platform.persistance.setStorageState(
|
|
784
794
|
room,
|
|
785
795
|
encodedState
|
|
@@ -791,7 +801,7 @@ var PluvIO = class _PluvIO {
|
|
|
791
801
|
}
|
|
792
802
|
const state = (_b = yield this._platform.persistance.getStorageState(
|
|
793
803
|
room
|
|
794
|
-
)) != null ? _b :
|
|
804
|
+
)) != null ? _b : doc.getEncodedState();
|
|
795
805
|
return { $STORAGE_RECEIVED: { state } };
|
|
796
806
|
})
|
|
797
807
|
}
|
|
@@ -820,12 +830,12 @@ var PluvIO = class _PluvIO {
|
|
|
820
830
|
}
|
|
821
831
|
},
|
|
822
832
|
$UPDATE_STORAGE: {
|
|
823
|
-
resolver: ({ origin, update }, { context, doc
|
|
824
|
-
if (origin === "$INITIALIZED" && Object.keys(
|
|
833
|
+
resolver: ({ origin, update }, { context, doc, room }) => {
|
|
834
|
+
if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
|
|
825
835
|
return;
|
|
826
836
|
}
|
|
827
|
-
const updated =
|
|
828
|
-
const encodedState = updated.
|
|
837
|
+
const updated = doc.applyEncodedState({ update });
|
|
838
|
+
const encodedState = updated.getEncodedState();
|
|
829
839
|
this._platform.persistance.setStorageState(room, encodedState).then(() => {
|
|
830
840
|
this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
|
|
831
841
|
encodedState,
|
|
@@ -840,6 +850,7 @@ var PluvIO = class _PluvIO {
|
|
|
840
850
|
const {
|
|
841
851
|
authorize: authorize2,
|
|
842
852
|
context,
|
|
853
|
+
crdt = import_crdt2.noop,
|
|
843
854
|
debug = false,
|
|
844
855
|
events,
|
|
845
856
|
getInitialStorage,
|
|
@@ -847,6 +858,7 @@ var PluvIO = class _PluvIO {
|
|
|
847
858
|
onStorageUpdated,
|
|
848
859
|
platform
|
|
849
860
|
} = options;
|
|
861
|
+
this._crdt = crdt;
|
|
850
862
|
this._debug = debug;
|
|
851
863
|
this._platform = platform;
|
|
852
864
|
if (authorize2)
|
|
@@ -885,6 +897,7 @@ var PluvIO = class _PluvIO {
|
|
|
885
897
|
return _PluvIO.merge(
|
|
886
898
|
this,
|
|
887
899
|
new _PluvIO({
|
|
900
|
+
crdt: this._crdt,
|
|
888
901
|
events: newEvent,
|
|
889
902
|
platform: this._platform
|
|
890
903
|
})
|
|
@@ -905,6 +918,7 @@ var PluvIO = class _PluvIO {
|
|
|
905
918
|
const newRoom = new IORoom(room, {
|
|
906
919
|
authorize: (_b = this._authorize) != null ? _b : void 0,
|
|
907
920
|
context: roomContext,
|
|
921
|
+
crdt: this._crdt,
|
|
908
922
|
debug: debug != null ? debug : this._debug,
|
|
909
923
|
events: this._events,
|
|
910
924
|
onDestroy: ({ encodedState, room: room2 }) => {
|
|
@@ -943,6 +957,7 @@ var PluvIO = class _PluvIO {
|
|
|
943
957
|
var _a;
|
|
944
958
|
const authorize2 = (_a = first._authorize) != null ? _a : void 0;
|
|
945
959
|
const context = first._context;
|
|
960
|
+
const crdt = first._crdt;
|
|
946
961
|
const debug = first._debug;
|
|
947
962
|
const platform = first._platform;
|
|
948
963
|
const getInitialStorage = first._getInitialStorage;
|
|
@@ -953,6 +968,7 @@ var PluvIO = class _PluvIO {
|
|
|
953
968
|
return new _PluvIO({
|
|
954
969
|
authorize: authorize2,
|
|
955
970
|
context,
|
|
971
|
+
crdt,
|
|
956
972
|
debug,
|
|
957
973
|
events,
|
|
958
974
|
getInitialStorage,
|
|
@@ -979,6 +995,7 @@ var createIO = (params) => {
|
|
|
979
995
|
const {
|
|
980
996
|
authorize: authorize2,
|
|
981
997
|
context,
|
|
998
|
+
crdt,
|
|
982
999
|
debug,
|
|
983
1000
|
getInitialStorage,
|
|
984
1001
|
onRoomDeleted,
|
|
@@ -988,6 +1005,7 @@ var createIO = (params) => {
|
|
|
988
1005
|
return new PluvIO({
|
|
989
1006
|
authorize: authorize2,
|
|
990
1007
|
context,
|
|
1008
|
+
crdt,
|
|
991
1009
|
debug,
|
|
992
1010
|
getInitialStorage,
|
|
993
1011
|
onRoomDeleted,
|
package/dist/index.mjs
CHANGED
|
@@ -223,10 +223,11 @@ var AbstractWebSocket = class {
|
|
|
223
223
|
};
|
|
224
224
|
|
|
225
225
|
// src/PluvIO.ts
|
|
226
|
+
import { noop as noop2 } from "@pluv/crdt";
|
|
226
227
|
import colors2 from "kleur";
|
|
227
228
|
|
|
228
229
|
// src/IORoom.ts
|
|
229
|
-
import {
|
|
230
|
+
import { noop } from "@pluv/crdt";
|
|
230
231
|
import colors from "kleur";
|
|
231
232
|
|
|
232
233
|
// src/authorize.ts
|
|
@@ -269,13 +270,22 @@ var authorize = (params) => {
|
|
|
269
270
|
var PING_TIMEOUT_MS = 3e4;
|
|
270
271
|
var IORoom = class {
|
|
271
272
|
constructor(id, config) {
|
|
272
|
-
this._doc = doc();
|
|
273
273
|
this._sessions = /* @__PURE__ */ new Map();
|
|
274
274
|
this._uninitialize = null;
|
|
275
275
|
this._authorize = null;
|
|
276
|
-
const {
|
|
276
|
+
const {
|
|
277
|
+
authorize: authorize2,
|
|
278
|
+
context,
|
|
279
|
+
crdt = noop,
|
|
280
|
+
debug,
|
|
281
|
+
events,
|
|
282
|
+
onDestroy,
|
|
283
|
+
platform
|
|
284
|
+
} = config;
|
|
277
285
|
this._context = context;
|
|
278
286
|
this._debug = debug;
|
|
287
|
+
this._docFactory = crdt.doc(() => ({}));
|
|
288
|
+
this._doc = this._docFactory.getEmpty();
|
|
279
289
|
this._events = events;
|
|
280
290
|
this._platform = platform;
|
|
281
291
|
this.id = id;
|
|
@@ -478,9 +488,9 @@ var IORoom = class {
|
|
|
478
488
|
);
|
|
479
489
|
this._uninitialize = () => __async(this, null, function* () {
|
|
480
490
|
this._platform.pubSub.unsubscribe(pubSubId);
|
|
481
|
-
const encodedState = this._doc.
|
|
491
|
+
const encodedState = this._doc.getEncodedState();
|
|
482
492
|
this._doc.destroy();
|
|
483
|
-
this._doc =
|
|
493
|
+
this._doc = this._docFactory.getEmpty();
|
|
484
494
|
this._listeners.onDestroy(__spreadProps(__spreadValues({}, this._context), {
|
|
485
495
|
encodedState,
|
|
486
496
|
room: this.id
|
|
@@ -731,7 +741,7 @@ var PluvIO = class _PluvIO {
|
|
|
731
741
|
}
|
|
732
742
|
};
|
|
733
743
|
},
|
|
734
|
-
self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { context, doc
|
|
744
|
+
self: (_0, _1) => __async(this, [_0, _1], function* ({ update }, { context, doc, room }) {
|
|
735
745
|
var _a, _b;
|
|
736
746
|
const oldState = yield this._platform.persistance.getStorageState(room);
|
|
737
747
|
const updates = [
|
|
@@ -741,7 +751,7 @@ var PluvIO = class _PluvIO {
|
|
|
741
751
|
update
|
|
742
752
|
];
|
|
743
753
|
if (updates.some((_update) => !!_update)) {
|
|
744
|
-
const encodedState =
|
|
754
|
+
const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
|
|
745
755
|
yield this._platform.persistance.setStorageState(
|
|
746
756
|
room,
|
|
747
757
|
encodedState
|
|
@@ -753,7 +763,7 @@ var PluvIO = class _PluvIO {
|
|
|
753
763
|
}
|
|
754
764
|
const state = (_b = yield this._platform.persistance.getStorageState(
|
|
755
765
|
room
|
|
756
|
-
)) != null ? _b :
|
|
766
|
+
)) != null ? _b : doc.getEncodedState();
|
|
757
767
|
return { $STORAGE_RECEIVED: { state } };
|
|
758
768
|
})
|
|
759
769
|
}
|
|
@@ -782,12 +792,12 @@ var PluvIO = class _PluvIO {
|
|
|
782
792
|
}
|
|
783
793
|
},
|
|
784
794
|
$UPDATE_STORAGE: {
|
|
785
|
-
resolver: ({ origin, update }, { context, doc
|
|
786
|
-
if (origin === "$INITIALIZED" && Object.keys(
|
|
795
|
+
resolver: ({ origin, update }, { context, doc, room }) => {
|
|
796
|
+
if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
|
|
787
797
|
return;
|
|
788
798
|
}
|
|
789
|
-
const updated =
|
|
790
|
-
const encodedState = updated.
|
|
799
|
+
const updated = doc.applyEncodedState({ update });
|
|
800
|
+
const encodedState = updated.getEncodedState();
|
|
791
801
|
this._platform.persistance.setStorageState(room, encodedState).then(() => {
|
|
792
802
|
this._listeners.onStorageUpdated(__spreadProps(__spreadValues({}, context), {
|
|
793
803
|
encodedState,
|
|
@@ -802,6 +812,7 @@ var PluvIO = class _PluvIO {
|
|
|
802
812
|
const {
|
|
803
813
|
authorize: authorize2,
|
|
804
814
|
context,
|
|
815
|
+
crdt = noop2,
|
|
805
816
|
debug = false,
|
|
806
817
|
events,
|
|
807
818
|
getInitialStorage,
|
|
@@ -809,6 +820,7 @@ var PluvIO = class _PluvIO {
|
|
|
809
820
|
onStorageUpdated,
|
|
810
821
|
platform
|
|
811
822
|
} = options;
|
|
823
|
+
this._crdt = crdt;
|
|
812
824
|
this._debug = debug;
|
|
813
825
|
this._platform = platform;
|
|
814
826
|
if (authorize2)
|
|
@@ -847,6 +859,7 @@ var PluvIO = class _PluvIO {
|
|
|
847
859
|
return _PluvIO.merge(
|
|
848
860
|
this,
|
|
849
861
|
new _PluvIO({
|
|
862
|
+
crdt: this._crdt,
|
|
850
863
|
events: newEvent,
|
|
851
864
|
platform: this._platform
|
|
852
865
|
})
|
|
@@ -867,6 +880,7 @@ var PluvIO = class _PluvIO {
|
|
|
867
880
|
const newRoom = new IORoom(room, {
|
|
868
881
|
authorize: (_b = this._authorize) != null ? _b : void 0,
|
|
869
882
|
context: roomContext,
|
|
883
|
+
crdt: this._crdt,
|
|
870
884
|
debug: debug != null ? debug : this._debug,
|
|
871
885
|
events: this._events,
|
|
872
886
|
onDestroy: ({ encodedState, room: room2 }) => {
|
|
@@ -905,6 +919,7 @@ var PluvIO = class _PluvIO {
|
|
|
905
919
|
var _a;
|
|
906
920
|
const authorize2 = (_a = first._authorize) != null ? _a : void 0;
|
|
907
921
|
const context = first._context;
|
|
922
|
+
const crdt = first._crdt;
|
|
908
923
|
const debug = first._debug;
|
|
909
924
|
const platform = first._platform;
|
|
910
925
|
const getInitialStorage = first._getInitialStorage;
|
|
@@ -915,6 +930,7 @@ var PluvIO = class _PluvIO {
|
|
|
915
930
|
return new _PluvIO({
|
|
916
931
|
authorize: authorize2,
|
|
917
932
|
context,
|
|
933
|
+
crdt,
|
|
918
934
|
debug,
|
|
919
935
|
events,
|
|
920
936
|
getInitialStorage,
|
|
@@ -941,6 +957,7 @@ var createIO = (params) => {
|
|
|
941
957
|
const {
|
|
942
958
|
authorize: authorize2,
|
|
943
959
|
context,
|
|
960
|
+
crdt,
|
|
944
961
|
debug,
|
|
945
962
|
getInitialStorage,
|
|
946
963
|
onRoomDeleted,
|
|
@@ -950,6 +967,7 @@ var createIO = (params) => {
|
|
|
950
967
|
return new PluvIO({
|
|
951
968
|
authorize: authorize2,
|
|
952
969
|
context,
|
|
970
|
+
crdt,
|
|
953
971
|
debug,
|
|
954
972
|
getInitialStorage,
|
|
955
973
|
onRoomDeleted,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "@pluv/io server-side websocket manager",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,22 +18,18 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@panva/hkdf": "^1.1.1",
|
|
21
|
-
"jose": "^5.
|
|
21
|
+
"jose": "^5.2.2",
|
|
22
22
|
"kleur": "^4.1.5",
|
|
23
23
|
"wonka": "^6.3.4",
|
|
24
|
-
"@pluv/crdt
|
|
25
|
-
"@pluv/types": "^0.
|
|
26
|
-
},
|
|
27
|
-
"peerDependencies": {
|
|
28
|
-
"yjs": "^13.0.0"
|
|
24
|
+
"@pluv/crdt": "^0.16.1",
|
|
25
|
+
"@pluv/types": "^0.16.1"
|
|
29
26
|
},
|
|
30
27
|
"devDependencies": {
|
|
31
28
|
"eslint": "^8.56.0",
|
|
32
|
-
"tsup": "^8.0.
|
|
33
|
-
"typescript": "^5.
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"eslint-config-pluv": "^0.15.0"
|
|
29
|
+
"tsup": "^8.0.2",
|
|
30
|
+
"typescript": "^5.3.3",
|
|
31
|
+
"@pluv/tsconfig": "^0.16.1",
|
|
32
|
+
"eslint-config-pluv": "^0.16.1"
|
|
37
33
|
},
|
|
38
34
|
"scripts": {
|
|
39
35
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/IORoom.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { AbstractCrdtDoc, AbstractCrdtDocFactory } from "@pluv/crdt";
|
|
2
|
+
import { noop } from "@pluv/crdt";
|
|
3
3
|
import type {
|
|
4
4
|
BaseIOAuthorize,
|
|
5
5
|
BaseIOEventRecord,
|
|
6
6
|
EventMessage,
|
|
7
7
|
EventRecord,
|
|
8
|
+
IOAuthorize,
|
|
9
|
+
IOLike,
|
|
8
10
|
Id,
|
|
9
11
|
InferEventMessage,
|
|
10
12
|
InferIOAuthorize,
|
|
11
13
|
InferIOAuthorizeUser,
|
|
12
14
|
InferIOInput,
|
|
13
|
-
IOAuthorize,
|
|
14
|
-
IOLike,
|
|
15
15
|
JsonObject,
|
|
16
16
|
Maybe,
|
|
17
17
|
} from "@pluv/types";
|
|
@@ -64,6 +64,7 @@ export type IORoomConfig<
|
|
|
64
64
|
> = Partial<IORoomListeners<TPlatform>> & {
|
|
65
65
|
authorize?: TAuthorize;
|
|
66
66
|
context: TContext & InferPlatformRoomContextType<TPlatform>;
|
|
67
|
+
crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
67
68
|
debug: boolean;
|
|
68
69
|
events: InferEventConfig<
|
|
69
70
|
TPlatform,
|
|
@@ -100,9 +101,10 @@ export class IORoom<
|
|
|
100
101
|
private readonly _context: TContext &
|
|
101
102
|
InferPlatformRoomContextType<TPlatform>;
|
|
102
103
|
private readonly _debug: boolean;
|
|
104
|
+
private readonly _docFactory: AbstractCrdtDocFactory<any>;
|
|
103
105
|
private readonly _platform: TPlatform;
|
|
104
106
|
|
|
105
|
-
private _doc:
|
|
107
|
+
private _doc: AbstractCrdtDoc<any>;
|
|
106
108
|
private _listeners: IORoomListeners<TPlatform>;
|
|
107
109
|
private _sessions = new Map<string, WebSocketSession>();
|
|
108
110
|
private _uninitialize: (() => Promise<void>) | null = null;
|
|
@@ -131,11 +133,20 @@ export class IORoom<
|
|
|
131
133
|
TOutputSync
|
|
132
134
|
>,
|
|
133
135
|
) {
|
|
134
|
-
const {
|
|
135
|
-
|
|
136
|
+
const {
|
|
137
|
+
authorize,
|
|
138
|
+
context,
|
|
139
|
+
crdt = noop,
|
|
140
|
+
debug,
|
|
141
|
+
events,
|
|
142
|
+
onDestroy,
|
|
143
|
+
platform,
|
|
144
|
+
} = config;
|
|
136
145
|
|
|
137
146
|
this._context = context;
|
|
138
147
|
this._debug = debug;
|
|
148
|
+
this._docFactory = crdt.doc(() => ({}));
|
|
149
|
+
this._doc = this._docFactory.getEmpty();
|
|
139
150
|
this._events = events;
|
|
140
151
|
this._platform = platform;
|
|
141
152
|
|
|
@@ -449,10 +460,10 @@ export class IORoom<
|
|
|
449
460
|
this._uninitialize = async () => {
|
|
450
461
|
this._platform.pubSub.unsubscribe(pubSubId);
|
|
451
462
|
|
|
452
|
-
const encodedState = this._doc.
|
|
463
|
+
const encodedState = this._doc.getEncodedState();
|
|
453
464
|
|
|
454
465
|
this._doc.destroy();
|
|
455
|
-
this._doc =
|
|
466
|
+
this._doc = this._docFactory.getEmpty();
|
|
456
467
|
|
|
457
468
|
this._listeners.onDestroy({
|
|
458
469
|
...this._context,
|
package/src/PluvIO.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AbstractCrdtDocFactory } from "@pluv/crdt";
|
|
2
|
+
import { noop } from "@pluv/crdt";
|
|
1
3
|
import type {
|
|
2
4
|
BaseIOAuthorize,
|
|
3
5
|
EventRecord,
|
|
@@ -19,7 +21,8 @@ import type {
|
|
|
19
21
|
AbstractPlatform,
|
|
20
22
|
InferPlatformRoomContextType,
|
|
21
23
|
} from "./AbstractPlatform";
|
|
22
|
-
import {
|
|
24
|
+
import type { IORoomListenerEvent } from "./IORoom";
|
|
25
|
+
import { IORoom } from "./IORoom";
|
|
23
26
|
import type { JWTEncodeParams } from "./authorize";
|
|
24
27
|
import { authorize } from "./authorize";
|
|
25
28
|
import type { EventConfig, InferEventConfig } from "./types";
|
|
@@ -79,6 +82,7 @@ export type PluvIOConfig<
|
|
|
79
82
|
> = Partial<PluvIOListeners<TPlatform>> & {
|
|
80
83
|
authorize?: TAuthorize;
|
|
81
84
|
context?: TContext;
|
|
85
|
+
crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
82
86
|
debug?: boolean;
|
|
83
87
|
events?: InferEventConfig<
|
|
84
88
|
TPlatform,
|
|
@@ -122,6 +126,7 @@ export class PluvIO<
|
|
|
122
126
|
|
|
123
127
|
readonly _authorize: TAuthorize | null = null;
|
|
124
128
|
readonly _context: TContext = {} as TContext;
|
|
129
|
+
readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
125
130
|
readonly _debug: boolean;
|
|
126
131
|
readonly _events: InferEventConfig<
|
|
127
132
|
TPlatform,
|
|
@@ -189,9 +194,9 @@ export class PluvIO<
|
|
|
189
194
|
];
|
|
190
195
|
|
|
191
196
|
if (updates.some((_update) => !!_update)) {
|
|
192
|
-
const encodedState =
|
|
193
|
-
.
|
|
194
|
-
.
|
|
197
|
+
const encodedState = doc
|
|
198
|
+
.batchApplyEncodedState(updates)
|
|
199
|
+
.getEncodedState();
|
|
195
200
|
|
|
196
201
|
await this._platform.persistance.setStorageState(
|
|
197
202
|
room,
|
|
@@ -208,7 +213,7 @@ export class PluvIO<
|
|
|
208
213
|
const state =
|
|
209
214
|
(await this._platform.persistance.getStorageState(
|
|
210
215
|
room,
|
|
211
|
-
)) ?? doc.
|
|
216
|
+
)) ?? doc.getEncodedState();
|
|
212
217
|
|
|
213
218
|
return { $STORAGE_RECEIVED: { state } };
|
|
214
219
|
},
|
|
@@ -244,13 +249,13 @@ export class PluvIO<
|
|
|
244
249
|
resolver: ({ origin, update }, { context, doc, room }) => {
|
|
245
250
|
if (
|
|
246
251
|
origin === "$INITIALIZED" &&
|
|
247
|
-
Object.keys(doc.
|
|
252
|
+
Object.keys(doc.toJson()).length
|
|
248
253
|
) {
|
|
249
254
|
return;
|
|
250
255
|
}
|
|
251
256
|
|
|
252
|
-
const updated = doc.
|
|
253
|
-
const encodedState = updated.
|
|
257
|
+
const updated = doc.applyEncodedState({ update });
|
|
258
|
+
const encodedState = updated.getEncodedState();
|
|
254
259
|
|
|
255
260
|
this._platform.persistance
|
|
256
261
|
.setStorageState(room, encodedState)
|
|
@@ -291,6 +296,7 @@ export class PluvIO<
|
|
|
291
296
|
const {
|
|
292
297
|
authorize,
|
|
293
298
|
context,
|
|
299
|
+
crdt = noop,
|
|
294
300
|
debug = false,
|
|
295
301
|
events,
|
|
296
302
|
getInitialStorage,
|
|
@@ -299,6 +305,7 @@ export class PluvIO<
|
|
|
299
305
|
platform,
|
|
300
306
|
} = options;
|
|
301
307
|
|
|
308
|
+
this._crdt = crdt;
|
|
302
309
|
this._debug = debug;
|
|
303
310
|
this._platform = platform;
|
|
304
311
|
|
|
@@ -386,6 +393,7 @@ export class PluvIO<
|
|
|
386
393
|
return PluvIO.merge(
|
|
387
394
|
this as any,
|
|
388
395
|
new PluvIO({
|
|
396
|
+
crdt: this._crdt,
|
|
389
397
|
events: newEvent as any,
|
|
390
398
|
platform: this._platform,
|
|
391
399
|
}),
|
|
@@ -433,6 +441,7 @@ export class PluvIO<
|
|
|
433
441
|
>(room, {
|
|
434
442
|
authorize: this._authorize ?? undefined,
|
|
435
443
|
context: roomContext,
|
|
444
|
+
crdt: this._crdt,
|
|
436
445
|
debug: debug ?? this._debug,
|
|
437
446
|
events: this._events,
|
|
438
447
|
onDestroy: ({ encodedState, room }) => {
|
|
@@ -493,6 +502,7 @@ export class PluvIO<
|
|
|
493
502
|
const authorize = (first._authorize ??
|
|
494
503
|
undefined) as InferIOAuthorize<TIO1>;
|
|
495
504
|
const context = first._context as InferIOContext<TIO1>;
|
|
505
|
+
const crdt = first._crdt;
|
|
496
506
|
const debug = first._debug;
|
|
497
507
|
const platform = first._platform as InferIOPlatform<TIO1>;
|
|
498
508
|
|
|
@@ -511,6 +521,7 @@ export class PluvIO<
|
|
|
511
521
|
return new PluvIO({
|
|
512
522
|
authorize,
|
|
513
523
|
context,
|
|
524
|
+
crdt,
|
|
514
525
|
debug,
|
|
515
526
|
events,
|
|
516
527
|
getInitialStorage,
|
package/src/authorize.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import hkdf from "@panva/hkdf";
|
|
2
|
-
import { BaseUser } from "@pluv/types";
|
|
2
|
+
import type { BaseUser } from "@pluv/types";
|
|
3
3
|
import { EncryptJWT, jwtDecrypt } from "jose";
|
|
4
|
-
import { AbstractPlatform } from "./AbstractPlatform";
|
|
4
|
+
import type { AbstractPlatform } from "./AbstractPlatform";
|
|
5
5
|
|
|
6
6
|
const DEFAULT_MAX_AGE_MS = 60_000;
|
|
7
7
|
|
package/src/createIO.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import type { AbstractCrdtDocFactory } from "@pluv/crdt";
|
|
1
2
|
import type {
|
|
2
3
|
BaseClientEventRecord,
|
|
3
4
|
BaseIOEventRecord,
|
|
4
5
|
BaseUser,
|
|
5
6
|
IOAuthorize,
|
|
6
7
|
JsonObject,
|
|
7
|
-
Maybe,
|
|
8
|
-
MaybePromise,
|
|
9
8
|
} from "@pluv/types";
|
|
10
9
|
import type { AbstractPlatform } from "./AbstractPlatform";
|
|
11
10
|
import type { GetInitialStorageFn, PluvIOListeners } from "./PluvIO";
|
|
@@ -19,6 +18,7 @@ export type CreateIOParams<
|
|
|
19
18
|
> = Partial<PluvIOListeners<TPlatform>> & {
|
|
20
19
|
authorize?: IOAuthorize<TAuthorizeUser, TAuthorizeRequired>;
|
|
21
20
|
context?: TContext;
|
|
21
|
+
crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
22
22
|
debug?: boolean;
|
|
23
23
|
getInitialStorage?: GetInitialStorageFn<TPlatform>;
|
|
24
24
|
platform: TPlatform;
|
|
@@ -46,6 +46,7 @@ export const createIO = <
|
|
|
46
46
|
const {
|
|
47
47
|
authorize,
|
|
48
48
|
context,
|
|
49
|
+
crdt,
|
|
49
50
|
debug,
|
|
50
51
|
getInitialStorage,
|
|
51
52
|
onRoomDeleted,
|
|
@@ -62,6 +63,7 @@ export const createIO = <
|
|
|
62
63
|
>({
|
|
63
64
|
authorize,
|
|
64
65
|
context,
|
|
66
|
+
crdt,
|
|
65
67
|
debug,
|
|
66
68
|
getInitialStorage,
|
|
67
69
|
onRoomDeleted,
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AbstractCrdtDoc } from "@pluv/crdt";
|
|
2
2
|
import type {
|
|
3
3
|
EventRecord,
|
|
4
4
|
Id,
|
|
@@ -13,14 +13,6 @@ import {
|
|
|
13
13
|
} from "./AbstractPlatform";
|
|
14
14
|
import type { AbstractWebSocket } from "./AbstractWebSocket";
|
|
15
15
|
|
|
16
|
-
export interface Console {
|
|
17
|
-
debug(...data: any[]): void;
|
|
18
|
-
error(...data: any[]): void;
|
|
19
|
-
info(...data: any[]): void;
|
|
20
|
-
log(...data: any[]): void;
|
|
21
|
-
warn(...data: any[]): void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
16
|
declare global {
|
|
25
17
|
var console: {
|
|
26
18
|
log: (...data: any[]) => void;
|
|
@@ -78,7 +70,7 @@ export interface EventResolverContext<
|
|
|
78
70
|
TContext extends Record<string, any> = {},
|
|
79
71
|
> {
|
|
80
72
|
context: TContext;
|
|
81
|
-
doc:
|
|
73
|
+
doc: AbstractCrdtDoc<any>;
|
|
82
74
|
room: string;
|
|
83
75
|
/**
|
|
84
76
|
* !HACK
|