@pluv/io 0.44.2 → 1.0.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 +24 -0
- package/README.md +137 -67
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +65 -31
- package/dist/index.mjs +65 -31
- package/package.json +5 -5
- package/src/AbstractPersistence.ts +8 -2
- package/src/AbstractPlatform.ts +6 -2
- package/src/AbstractWebSocket.ts +11 -3
- package/src/IORoom.ts +113 -37
- package/src/PluvIO.ts +38 -11
- package/src/PluvProcedure.ts +57 -10
- package/src/PluvRouter.ts +14 -3
- package/src/PluvServer.ts +98 -36
- package/src/PubSub.ts +4 -1
- package/src/authorize.ts +4 -1
- package/src/createIO.ts +10 -2
- package/src/types.ts +25 -11
- package/src/utils/pickBy.ts +4 -1
- package/src/version.generated.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/io@0.
|
|
2
|
+
> @pluv/io@1.0.1 build /home/runner/work/pluv/pluv/packages/io
|
|
3
3
|
> pnpm run version && tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @pluv/io@0.
|
|
6
|
+
> @pluv/io@1.0.1 version /home/runner/work/pluv/pluv/packages/io
|
|
7
7
|
> genversion ./src/version.generated.ts --esm
|
|
8
8
|
|
|
9
9
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
[34mCLI[39m Target: es6
|
|
13
13
|
[34mESM[39m Build start
|
|
14
14
|
[34mCJS[39m Build start
|
|
15
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
16
|
-
[32mCJS[39m ⚡️ Build success in
|
|
17
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
15
|
+
[32mCJS[39m [1mdist/index.js [22m[32m53.34 KB[39m
|
|
16
|
+
[32mCJS[39m ⚡️ Build success in 119ms
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m51.16 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 132ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m32.
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m32.
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 3284ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m32.20 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m32.20 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @pluv/io
|
|
2
2
|
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c583be9: Fixed storage race condition that occurred when 2 users joined a room near simultaneously that would cause users to have mismatched storage states from the server (and thereby preventing updates from properly registering) until the user reconnected.
|
|
8
|
+
- @pluv/crdt@1.0.1
|
|
9
|
+
- @pluv/types@1.0.1
|
|
10
|
+
|
|
11
|
+
## 1.0.0
|
|
12
|
+
|
|
13
|
+
### Major Changes
|
|
14
|
+
|
|
15
|
+
- af94706: pluv.io is now stable and production ready!
|
|
16
|
+
|
|
17
|
+
With this v1 release, pluv.io will now follow [semantic versioning](https://semver.org/) with more comprehensive release notes for future changes to the library.
|
|
18
|
+
|
|
19
|
+
Checkout the [full documentation here](https://pluv.io/docs/introduction) to get started today!
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [af94706]
|
|
24
|
+
- @pluv/crdt@1.0.0
|
|
25
|
+
- @pluv/types@1.0.0
|
|
26
|
+
|
|
3
27
|
## 0.44.2
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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>
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://pluv.io/docs/introduction">
|
|
3
|
+
<img src="https://github.com/pluv-io/pluv/blob/master/assets/pluv-icon-192x192.png?raw=true" alt="Pluv.IO" width="156" style="border-radius:16px" />
|
|
4
|
+
</a>
|
|
5
|
+
</div>
|
|
11
6
|
|
|
12
|
-
<
|
|
13
|
-
<
|
|
7
|
+
<h1 align="center">pluv.io</h1>
|
|
8
|
+
<h3 align="center">TypeSafe Primitives for a Realtime Web</h3>
|
|
14
9
|
|
|
15
10
|
<p align="center">
|
|
16
11
|
<a href="https://www.npmjs.com/package/@pluv/io">
|
|
@@ -25,92 +20,167 @@
|
|
|
25
20
|
<img src="https://badgen.net/badge/-/TypeScript?icon=typescript&label&labelColor=blue&color=555555" alt="TypeScript" />
|
|
26
21
|
</p>
|
|
27
22
|
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<a href="#related">Related</a> •
|
|
32
|
-
<a href="#credits">Credits</a> •
|
|
33
|
-
<a href="#license">License</a>
|
|
34
|
-
</p>
|
|
35
|
-
|
|
36
|
-
## Intro
|
|
37
|
-
|
|
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/)**.
|
|
23
|
+
<div align="center">
|
|
24
|
+
<a href="https://www.producthunt.com/posts/pluv-io?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-pluv-io" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=961147&theme=light&t=1746462368894" alt="pluv.io - Open source multiplayer APIs, powered by TypeScript | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
|
25
|
+
</div>
|
|
39
26
|
|
|
40
|
-
|
|
27
|
+
<h4 align="center">💕 Inspired by <a href="https://trpc.io">trpc</a> 💕 <a href="https://docs.yjs.dev/">yjs</a> 💕 <br />and <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> 💕</h4>
|
|
41
28
|
|
|
42
|
-
|
|
29
|
+
## Intro
|
|
43
30
|
|
|
44
|
-
|
|
31
|
+
**[pluv.io](https://pluv.io) allows you to more easily build realtime multiplayer experiences with a fully typesafe API and powerful abstractions as primitives, so that you can focus on building for your end users.**
|
|
32
|
+
|
|
33
|
+
Self-host on [Cloudflare Workers](https://pluv.io/docs/quickstart/cloudflare-workers) or [Node.js](https://pluv.io/docs/quickstart/nodejs); or [get started on the pluv.io network](https://pluv.io/signup).
|
|
34
|
+
|
|
35
|
+
## Preview
|
|
36
|
+
|
|
37
|
+
Create your pluv.io backend
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// backend
|
|
41
|
+
const io = createIO(platformNode({ crdt: yjs }));
|
|
42
|
+
|
|
43
|
+
export const ioServer = io.server({
|
|
44
|
+
router: io.router({
|
|
45
|
+
sendGreeting: io.procedure
|
|
46
|
+
.input(z.object({ message: z.string() }))
|
|
47
|
+
.broadcast(({ message }) => ({
|
|
48
|
+
receiveGreeting: { message }
|
|
49
|
+
}))
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Create your frontend client with your backend types
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// frontend
|
|
58
|
+
const types = infer((i) => ({ io: i<typeof ioServer> }));
|
|
59
|
+
const io = createClient({
|
|
60
|
+
types,
|
|
61
|
+
initialStorage: yjs.doc(() => ({
|
|
62
|
+
messages: yjs.array<string>(),
|
|
63
|
+
})),
|
|
64
|
+
presence: z.object({
|
|
65
|
+
selectionId: z.string().nullable()
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const {
|
|
70
|
+
event,
|
|
71
|
+
useBroadcast,
|
|
72
|
+
useMyPresence,
|
|
73
|
+
useOthers,
|
|
74
|
+
useStorage
|
|
75
|
+
} = createBundle(io);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Use powerful primitives to build realtime features
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
// react
|
|
82
|
+
event.receiveGreeting.useEvent(({ data }) => { /* ... */});
|
|
83
|
+
// ^? const data: { message: string }
|
|
84
|
+
|
|
85
|
+
const broadcast = useBroadcast();
|
|
86
|
+
|
|
87
|
+
broadcast.sendGreeting({ message: "hello world" });
|
|
88
|
+
// ^? const sendGreeting: (data: { message: string }) => void
|
|
89
|
+
|
|
90
|
+
const [mySelection, update] = useMyPresence((presence) => {
|
|
91
|
+
// ^? const mySelection: string | null
|
|
92
|
+
return presence.selectionId;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const others = useOthers((others) => {
|
|
96
|
+
// ^? const others = string[]
|
|
97
|
+
return others.map((other) => other.presence.selectionId);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const [
|
|
101
|
+
messages,
|
|
102
|
+
// ^? const messages: string[] | null
|
|
103
|
+
sharedType
|
|
104
|
+
// ^? YArray<string> | null
|
|
105
|
+
] = useStorage("messages");
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Documentation
|
|
109
|
+
|
|
110
|
+
The full documentation is available at [pluv.io](https://pluv.io/docs/introduction).
|
|
111
|
+
|
|
112
|
+
## Features
|
|
113
|
+
|
|
114
|
+
- ✅ Automatic type-safety
|
|
45
115
|
- ✅ Basic events
|
|
46
116
|
- ✅ Rooms
|
|
47
117
|
- ✅ Authentication
|
|
48
118
|
- ✅ Awareness + Presence
|
|
49
119
|
- ✅ [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- ✅
|
|
60
|
-
- ✅
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
120
|
+
- ✅ [Yjs](https://docs.yjs.dev/)
|
|
121
|
+
- ⬜ [Provider](https://github.com/yjs/yjs?tab=readme-ov-file#providers) (Coming soon!)
|
|
122
|
+
- ✅ **Shared Types**
|
|
123
|
+
- ✅ [Map](https://docs.yjs.dev/api/shared-types/y.map)
|
|
124
|
+
- ✅ [Array](https://docs.yjs.dev/api/shared-types/y.array)
|
|
125
|
+
- ✅ [Text](https://docs.yjs.dev/api/shared-types/y.text)
|
|
126
|
+
- ✅ [XmlFragment](https://docs.yjs.dev/api/shared-types/y.xmlfragment)
|
|
127
|
+
- ✅ [XmlElement](https://docs.yjs.dev/api/shared-types/y.xmlelement)
|
|
128
|
+
- ✅ [XmlText](https://docs.yjs.dev/api/shared-types/y.xmltext)
|
|
129
|
+
- ✅ [Loro](https://loro.dev/) (preview)
|
|
130
|
+
- ✅ **Containers**
|
|
131
|
+
- ✅ List
|
|
132
|
+
- ✅ Map
|
|
133
|
+
- ✅ Text
|
|
134
|
+
- ⬜ Tree
|
|
64
135
|
- ⬜ Studio (admin & developer panel)
|
|
65
136
|
|
|
66
|
-
|
|
137
|
+
## Runtimes
|
|
67
138
|
|
|
68
139
|
- ✅ [Cloudflare Workers](https://workers.cloudflare.com/)
|
|
140
|
+
- **WebSocket API**
|
|
141
|
+
- ✅ [Hibernation API](https://developers.cloudflare.com/durable-objects/best-practices/websockets/#websocket-hibernation-api) (default, recommended)
|
|
142
|
+
- ✅ [Standard API](https://developers.cloudflare.com/durable-objects/best-practices/websockets/#websocket-standard-api)
|
|
143
|
+
- **State Persistence**
|
|
144
|
+
- ✅ [SQLite-backed Durable Objects](https://developers.cloudflare.com/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) (default, recommended)
|
|
145
|
+
- ✅ [Key-value storage-backed Durable Object](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage)
|
|
69
146
|
- ✅ [Node.js](https://nodejs.org/)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- ⬜ [Prisma](https://www.prisma.io/)
|
|
147
|
+
- ✅ **PubSubs**
|
|
148
|
+
- ✅ [Redis](https://redis.io/) (preview)
|
|
149
|
+
- ⬜ [MQTT](https://mqtt.org/)
|
|
150
|
+
- ✅ **State Persistence**
|
|
151
|
+
- ✅ In-memory (default)
|
|
152
|
+
- ✅ [Redis](https://redis.io/) (preview)
|
|
77
153
|
|
|
78
|
-
|
|
154
|
+
## Frontends Frameworks
|
|
79
155
|
|
|
80
156
|
- ✅ [React.js](https://react.dev/)
|
|
81
157
|
- ⬜ [Solid.js](https://www.solidjs.com/)
|
|
82
158
|
- ⬜ [Vue.js](https://vuejs.org/)
|
|
83
159
|
- ⬜ [Svelte](https://svelte.dev/)
|
|
84
160
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
Before diving into documentation, check out usage instructions for your selected platform:
|
|
88
|
-
|
|
89
|
-
### Documentation
|
|
90
|
-
|
|
91
|
-
Documentation is available at [pluv.io](https://pluv.io/docs/introduction).
|
|
92
|
-
|
|
93
|
-
## Related
|
|
161
|
+
## Packages
|
|
94
162
|
|
|
163
|
+
- [@pluv/io](https://www.npmjs.com/package/@pluv/io) - Server
|
|
95
164
|
- [@pluv/client](https://www.npmjs.com/package/@pluv/client) - Framework agnostic client
|
|
96
|
-
- [@pluv/
|
|
97
|
-
- [@pluv/
|
|
165
|
+
- [@pluv/react](https://www.npmjs.com/package/@pluv/react) - Integrate @pluv/client with React.js
|
|
166
|
+
- [@pluv/platform-pluv](https://www.npmjs.com/package/@pluv/platform-node) - Adapter to run on pluv.io
|
|
167
|
+
- [@pluv/platform-cloudflare](https://www.npmjs.com/package/@pluv/platform-cloudflare) - Adapter to run on Cloudflare Workers
|
|
168
|
+
- [@pluv/platform-node](https://www.npmjs.com/package/@pluv/platform-node) - Adapter to run on Node.js
|
|
169
|
+
- [@pluv/crdt-yjs](https://www.npmjs.com/package/@pluv/crdt-yjs) - Yjs CRDT adapter
|
|
170
|
+
- [@pluv/crdt-loro](https://www.npmjs.com/package/@pluv/crdt-loro) - Loro CRDT adapter
|
|
98
171
|
- [@pluv/persistence-redis](https://www.npmjs.com/package/@pluv/persistence-redis) - Persistence for storage on distributed systems (Node.js only)
|
|
99
|
-
- [@pluv/
|
|
100
|
-
- [@pluv/platform-node](https://www.npmjs.com/package/@pluv/platform-node) - Adapter to run @pluv/io on Node.js
|
|
172
|
+
- [@pluv/persistence-cloudflare-transactional-storage](https://www.npmjs.com/package/@pluv/persistence-cloudflare-transactional-storage) - Durable Object persistent state for WebSocket hibernation
|
|
101
173
|
- [@pluv/pubsub-redis](https://www.npmjs.com/package/@pluv/pubsub-redis) - PubSub for rooms across distributed systems
|
|
102
|
-
- [@pluv/react](https://www.npmjs.com/package/@pluv/react) - Integrate @pluv/client with React.js
|
|
103
174
|
|
|
104
175
|
## Credits
|
|
105
176
|
|
|
106
177
|
This software uses the following open source tooling and libraries:
|
|
107
178
|
|
|
108
|
-
- [Loro](https://loro.dev/)
|
|
109
|
-
- [Yjs](https://yjs.dev/)
|
|
110
|
-
- [Node.js](https://nodejs.org/)
|
|
111
179
|
- [Cloudflare Workers](https://workers.cloudflare.com/)
|
|
112
|
-
- [
|
|
180
|
+
- [Node.js](https://nodejs.org/)
|
|
113
181
|
- [React.js](https://reactjs.org/)
|
|
182
|
+
- [Yjs](https://yjs.dev/)
|
|
183
|
+
- [Loro](https://loro.dev/)
|
|
114
184
|
|
|
115
185
|
## License
|
|
116
186
|
|
package/dist/index.d.mts
CHANGED
|
@@ -445,7 +445,9 @@ type PluvServerConfig<TPlatform extends AbstractPlatform<any, any> = AbstractPla
|
|
|
445
445
|
type BaseCreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
|
|
446
446
|
debug?: boolean;
|
|
447
447
|
};
|
|
448
|
-
type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [
|
|
448
|
+
type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [
|
|
449
|
+
Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>
|
|
450
|
+
];
|
|
449
451
|
declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
|
|
450
452
|
readonly version: string;
|
|
451
453
|
private readonly _authorize;
|
package/dist/index.d.ts
CHANGED
|
@@ -445,7 +445,9 @@ type PluvServerConfig<TPlatform extends AbstractPlatform<any, any> = AbstractPla
|
|
|
445
445
|
type BaseCreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
|
|
446
446
|
debug?: boolean;
|
|
447
447
|
};
|
|
448
|
-
type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [
|
|
448
|
+
type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [
|
|
449
|
+
Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>
|
|
450
|
+
];
|
|
449
451
|
declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
|
|
450
452
|
readonly version: string;
|
|
451
453
|
private readonly _authorize;
|
package/dist/index.js
CHANGED
|
@@ -343,7 +343,9 @@ var PluvProcedure = class _PluvProcedure {
|
|
|
343
343
|
}));
|
|
344
344
|
}
|
|
345
345
|
input(input) {
|
|
346
|
-
return new _PluvProcedure({
|
|
346
|
+
return new _PluvProcedure({
|
|
347
|
+
input
|
|
348
|
+
});
|
|
347
349
|
}
|
|
348
350
|
self(resolver) {
|
|
349
351
|
const { self } = this.config;
|
|
@@ -380,7 +382,10 @@ var PluvRouter = class _PluvRouter {
|
|
|
380
382
|
this._defs = { events };
|
|
381
383
|
}
|
|
382
384
|
static merge(...routers) {
|
|
383
|
-
const events = Object.assign(
|
|
385
|
+
const events = Object.assign(
|
|
386
|
+
/* @__PURE__ */ Object.create(null),
|
|
387
|
+
...routers.map((router) => router._defs.events)
|
|
388
|
+
);
|
|
384
389
|
return new _PluvRouter(events);
|
|
385
390
|
}
|
|
386
391
|
_isValidEventName(name) {
|
|
@@ -583,7 +588,9 @@ var IORoom = class {
|
|
|
583
588
|
}));
|
|
584
589
|
this._addUserSession(user.id, pluvWs.sessionId);
|
|
585
590
|
}
|
|
586
|
-
this._logDebug(
|
|
591
|
+
this._logDebug(
|
|
592
|
+
`${import_kleur.default.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`
|
|
593
|
+
);
|
|
587
594
|
yield this._platform.acceptWebSocket(pluvWs);
|
|
588
595
|
this._sessions.set(pluvWs.sessionId, pluvWs);
|
|
589
596
|
yield this._platform.persistence.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
|
|
@@ -596,7 +603,9 @@ var IORoom = class {
|
|
|
596
603
|
}
|
|
597
604
|
yield this._emitRegistered(pluvWs);
|
|
598
605
|
const size = this.getSize();
|
|
599
|
-
this._logDebug(
|
|
606
|
+
this._logDebug(
|
|
607
|
+
`${import_kleur.default.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`
|
|
608
|
+
);
|
|
600
609
|
this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
|
|
601
610
|
});
|
|
602
611
|
}
|
|
@@ -627,7 +636,9 @@ var IORoom = class {
|
|
|
627
636
|
const closeWebSocket = (webSocket) => __async(this, null, function* () {
|
|
628
637
|
webSocket.state = __spreadProps(__spreadValues({}, webSocket.state), { quit: true });
|
|
629
638
|
const sessionId = webSocket.sessionId;
|
|
630
|
-
this._logDebug(
|
|
639
|
+
this._logDebug(
|
|
640
|
+
`${import_kleur.default.blue(`Unregistering connection for room ${this.id}:`)} ${sessionId}`
|
|
641
|
+
);
|
|
631
642
|
this._sessions.delete(sessionId);
|
|
632
643
|
yield this._platform.persistence.deleteUser(this.id, sessionId).catch(() => null);
|
|
633
644
|
yield this._broadcast({
|
|
@@ -651,7 +662,9 @@ var IORoom = class {
|
|
|
651
662
|
} catch (error) {
|
|
652
663
|
console.error(error);
|
|
653
664
|
}
|
|
654
|
-
this._logDebug(
|
|
665
|
+
this._logDebug(
|
|
666
|
+
`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`
|
|
667
|
+
);
|
|
655
668
|
});
|
|
656
669
|
const promises = webSockets.map((webSocket) => __async(this, null, function* () {
|
|
657
670
|
yield closeWebSocket(webSocket).catch(() => null);
|
|
@@ -668,9 +681,9 @@ var IORoom = class {
|
|
|
668
681
|
_emitQuitters() {
|
|
669
682
|
return __async(this, null, function* () {
|
|
670
683
|
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
671
|
-
const quitters = Array.from(this._sessions.values()).filter(
|
|
672
|
-
|
|
673
|
-
);
|
|
684
|
+
const quitters = Array.from(this._sessions.values()).filter((pluvWs) => {
|
|
685
|
+
return pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS;
|
|
686
|
+
});
|
|
674
687
|
this._closeWebSockets(quitters);
|
|
675
688
|
});
|
|
676
689
|
}
|
|
@@ -680,20 +693,21 @@ var IORoom = class {
|
|
|
680
693
|
const sessionId = session.id;
|
|
681
694
|
const presence = session.presence;
|
|
682
695
|
const user = session.user;
|
|
696
|
+
const doc = yield this._doc;
|
|
697
|
+
const encodedState = doc.getEncodedState();
|
|
683
698
|
yield this._sendSelfMessage(
|
|
684
699
|
{
|
|
685
700
|
type: "$registered",
|
|
686
701
|
data: {
|
|
687
702
|
presence,
|
|
688
703
|
sessionId,
|
|
704
|
+
state: encodedState,
|
|
689
705
|
timers: { presence: session.timers.presence }
|
|
690
706
|
}
|
|
691
707
|
},
|
|
692
708
|
{ sessionId, user }
|
|
693
709
|
);
|
|
694
710
|
try {
|
|
695
|
-
const doc = yield this._doc;
|
|
696
|
-
const encodedState = doc.getEncodedState();
|
|
697
711
|
yield Promise.resolve(
|
|
698
712
|
this._listeners.onUserConnected({
|
|
699
713
|
context: this._context,
|
|
@@ -740,7 +754,8 @@ var IORoom = class {
|
|
|
740
754
|
const ioAuthorize = this._getIOAuthorize(options);
|
|
741
755
|
if (!ioAuthorize) return null;
|
|
742
756
|
if (!token) return null;
|
|
743
|
-
if (!ioAuthorize.secret)
|
|
757
|
+
if (!ioAuthorize.secret)
|
|
758
|
+
throw new Error("`authorize` was specified without a valid secret");
|
|
744
759
|
const payload = yield authorize({
|
|
745
760
|
platform: this._platform,
|
|
746
761
|
secret: ioAuthorize.secret
|
|
@@ -1141,7 +1156,7 @@ var pickBy = (obj, by) => {
|
|
|
1141
1156
|
};
|
|
1142
1157
|
|
|
1143
1158
|
// src/version.generated.ts
|
|
1144
|
-
var version = "0.
|
|
1159
|
+
var version = "1.0.1";
|
|
1145
1160
|
|
|
1146
1161
|
// src/PluvServer.ts
|
|
1147
1162
|
var PluvServer = class {
|
|
@@ -1184,16 +1199,28 @@ var PluvServer = class {
|
|
|
1184
1199
|
}
|
|
1185
1200
|
};
|
|
1186
1201
|
}).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, room, session }) {
|
|
1187
|
-
var _a
|
|
1202
|
+
var _a;
|
|
1188
1203
|
const oldState = yield this._platform.persistence.getStorageState(room);
|
|
1189
1204
|
const update = data == null ? void 0 : data.update;
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1205
|
+
if (!oldState) {
|
|
1206
|
+
const loadedState = yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, { context, room });
|
|
1207
|
+
if (!!loadedState) {
|
|
1208
|
+
doc.applyEncodedState({ update: loadedState }).getEncodedState();
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
if (!doc.isEmpty()) {
|
|
1212
|
+
const encodedState2 = doc.getEncodedState();
|
|
1213
|
+
return { $storageReceived: { state: encodedState2 } };
|
|
1214
|
+
}
|
|
1215
|
+
if (!!update) {
|
|
1216
|
+
const encodedState2 = doc.applyEncodedState({ update }).getEncodedState();
|
|
1217
|
+
const storageSize = new TextEncoder().encode(encodedState2).length;
|
|
1218
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1219
|
+
throw new Error("Storage has exceeded the size limit");
|
|
1220
|
+
}
|
|
1221
|
+
yield this._platform.persistence.setStorageState(room, encodedState2).catch((error) => {
|
|
1222
|
+
this._logDebug(error);
|
|
1223
|
+
});
|
|
1197
1224
|
this._getListeners().onStorageUpdated({
|
|
1198
1225
|
context,
|
|
1199
1226
|
encodedState: encodedState2,
|
|
@@ -1202,11 +1229,7 @@ var PluvServer = class {
|
|
|
1202
1229
|
webSocket: session == null ? void 0 : session.webSocket.webSocket
|
|
1203
1230
|
});
|
|
1204
1231
|
}
|
|
1205
|
-
const encodedState =
|
|
1206
|
-
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1207
|
-
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1208
|
-
throw new Error("Storage has exceeded the size limit");
|
|
1209
|
-
}
|
|
1232
|
+
const encodedState = doc.getEncodedState();
|
|
1210
1233
|
return { $storageReceived: { state: encodedState } };
|
|
1211
1234
|
})),
|
|
1212
1235
|
$ping: this._procedure.self((data, { session }) => {
|
|
@@ -1274,7 +1297,13 @@ var PluvServer = class {
|
|
|
1274
1297
|
platform,
|
|
1275
1298
|
router = new PluvRouter({})
|
|
1276
1299
|
} = options;
|
|
1277
|
-
const {
|
|
1300
|
+
const {
|
|
1301
|
+
onRoomDeleted,
|
|
1302
|
+
onRoomMessage,
|
|
1303
|
+
onStorageUpdated,
|
|
1304
|
+
onUserConnected,
|
|
1305
|
+
onUserDisconnected
|
|
1306
|
+
} = options;
|
|
1278
1307
|
platform.validateConfig(options);
|
|
1279
1308
|
this._crdt = crdt;
|
|
1280
1309
|
this._debug = debug;
|
|
@@ -1295,7 +1324,8 @@ var PluvServer = class {
|
|
|
1295
1324
|
}
|
|
1296
1325
|
get fetch() {
|
|
1297
1326
|
return (...args) => {
|
|
1298
|
-
if (!this._platform._fetch)
|
|
1327
|
+
if (!this._platform._fetch)
|
|
1328
|
+
throw new Error(`\`${this._platform._name}\` does not support \`fetch\``);
|
|
1299
1329
|
return this._platform._fetch(...args);
|
|
1300
1330
|
};
|
|
1301
1331
|
}
|
|
@@ -1321,7 +1351,8 @@ var PluvServer = class {
|
|
|
1321
1351
|
if (this._platform._config.handleMode !== "io") {
|
|
1322
1352
|
throw new Error(`\`createRoom\` is unsupported for \`${this._platform._name}\``);
|
|
1323
1353
|
}
|
|
1324
|
-
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
|
|
1354
|
+
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
|
|
1355
|
+
throw new Error("Unsupported room name");
|
|
1325
1356
|
const roomContext = platformRoomContext;
|
|
1326
1357
|
const context = typeof this._context === "function" ? this._context(roomContext) : this._context;
|
|
1327
1358
|
const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
|
|
@@ -1391,7 +1422,8 @@ var PluvIO = class {
|
|
|
1391
1422
|
createToken(params) {
|
|
1392
1423
|
return __async(this, null, function* () {
|
|
1393
1424
|
var _a;
|
|
1394
|
-
if (!this._authorize)
|
|
1425
|
+
if (!this._authorize)
|
|
1426
|
+
throw new Error("IO does not specify authorize during initialization.");
|
|
1395
1427
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
1396
1428
|
const user = params.user;
|
|
1397
1429
|
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
@@ -1409,7 +1441,9 @@ var PluvIO = class {
|
|
|
1409
1441
|
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1410
1442
|
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1411
1443
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1412
|
-
return yield authorize({ platform: this._platform, secret }).encode(
|
|
1444
|
+
return yield authorize({ platform: this._platform, secret }).encode(
|
|
1445
|
+
params
|
|
1446
|
+
);
|
|
1413
1447
|
});
|
|
1414
1448
|
}
|
|
1415
1449
|
mergeRouters(...routers) {
|