@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/dist/index.mjs
CHANGED
|
@@ -301,7 +301,9 @@ var PluvProcedure = class _PluvProcedure {
|
|
|
301
301
|
}));
|
|
302
302
|
}
|
|
303
303
|
input(input) {
|
|
304
|
-
return new _PluvProcedure({
|
|
304
|
+
return new _PluvProcedure({
|
|
305
|
+
input
|
|
306
|
+
});
|
|
305
307
|
}
|
|
306
308
|
self(resolver) {
|
|
307
309
|
const { self } = this.config;
|
|
@@ -338,7 +340,10 @@ var PluvRouter = class _PluvRouter {
|
|
|
338
340
|
this._defs = { events };
|
|
339
341
|
}
|
|
340
342
|
static merge(...routers) {
|
|
341
|
-
const events = Object.assign(
|
|
343
|
+
const events = Object.assign(
|
|
344
|
+
/* @__PURE__ */ Object.create(null),
|
|
345
|
+
...routers.map((router) => router._defs.events)
|
|
346
|
+
);
|
|
342
347
|
return new _PluvRouter(events);
|
|
343
348
|
}
|
|
344
349
|
_isValidEventName(name) {
|
|
@@ -541,7 +546,9 @@ var IORoom = class {
|
|
|
541
546
|
}));
|
|
542
547
|
this._addUserSession(user.id, pluvWs.sessionId);
|
|
543
548
|
}
|
|
544
|
-
this._logDebug(
|
|
549
|
+
this._logDebug(
|
|
550
|
+
`${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`
|
|
551
|
+
);
|
|
545
552
|
yield this._platform.acceptWebSocket(pluvWs);
|
|
546
553
|
this._sessions.set(pluvWs.sessionId, pluvWs);
|
|
547
554
|
yield this._platform.persistence.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
|
|
@@ -554,7 +561,9 @@ var IORoom = class {
|
|
|
554
561
|
}
|
|
555
562
|
yield this._emitRegistered(pluvWs);
|
|
556
563
|
const size = this.getSize();
|
|
557
|
-
this._logDebug(
|
|
564
|
+
this._logDebug(
|
|
565
|
+
`${colors.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`
|
|
566
|
+
);
|
|
558
567
|
this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
|
|
559
568
|
});
|
|
560
569
|
}
|
|
@@ -585,7 +594,9 @@ var IORoom = class {
|
|
|
585
594
|
const closeWebSocket = (webSocket) => __async(this, null, function* () {
|
|
586
595
|
webSocket.state = __spreadProps(__spreadValues({}, webSocket.state), { quit: true });
|
|
587
596
|
const sessionId = webSocket.sessionId;
|
|
588
|
-
this._logDebug(
|
|
597
|
+
this._logDebug(
|
|
598
|
+
`${colors.blue(`Unregistering connection for room ${this.id}:`)} ${sessionId}`
|
|
599
|
+
);
|
|
589
600
|
this._sessions.delete(sessionId);
|
|
590
601
|
yield this._platform.persistence.deleteUser(this.id, sessionId).catch(() => null);
|
|
591
602
|
yield this._broadcast({
|
|
@@ -609,7 +620,9 @@ var IORoom = class {
|
|
|
609
620
|
} catch (error) {
|
|
610
621
|
console.error(error);
|
|
611
622
|
}
|
|
612
|
-
this._logDebug(
|
|
623
|
+
this._logDebug(
|
|
624
|
+
`${colors.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`
|
|
625
|
+
);
|
|
613
626
|
});
|
|
614
627
|
const promises = webSockets.map((webSocket) => __async(this, null, function* () {
|
|
615
628
|
yield closeWebSocket(webSocket).catch(() => null);
|
|
@@ -626,9 +639,9 @@ var IORoom = class {
|
|
|
626
639
|
_emitQuitters() {
|
|
627
640
|
return __async(this, null, function* () {
|
|
628
641
|
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
629
|
-
const quitters = Array.from(this._sessions.values()).filter(
|
|
630
|
-
|
|
631
|
-
);
|
|
642
|
+
const quitters = Array.from(this._sessions.values()).filter((pluvWs) => {
|
|
643
|
+
return pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS;
|
|
644
|
+
});
|
|
632
645
|
this._closeWebSockets(quitters);
|
|
633
646
|
});
|
|
634
647
|
}
|
|
@@ -638,20 +651,21 @@ var IORoom = class {
|
|
|
638
651
|
const sessionId = session.id;
|
|
639
652
|
const presence = session.presence;
|
|
640
653
|
const user = session.user;
|
|
654
|
+
const doc = yield this._doc;
|
|
655
|
+
const encodedState = doc.getEncodedState();
|
|
641
656
|
yield this._sendSelfMessage(
|
|
642
657
|
{
|
|
643
658
|
type: "$registered",
|
|
644
659
|
data: {
|
|
645
660
|
presence,
|
|
646
661
|
sessionId,
|
|
662
|
+
state: encodedState,
|
|
647
663
|
timers: { presence: session.timers.presence }
|
|
648
664
|
}
|
|
649
665
|
},
|
|
650
666
|
{ sessionId, user }
|
|
651
667
|
);
|
|
652
668
|
try {
|
|
653
|
-
const doc = yield this._doc;
|
|
654
|
-
const encodedState = doc.getEncodedState();
|
|
655
669
|
yield Promise.resolve(
|
|
656
670
|
this._listeners.onUserConnected({
|
|
657
671
|
context: this._context,
|
|
@@ -698,7 +712,8 @@ var IORoom = class {
|
|
|
698
712
|
const ioAuthorize = this._getIOAuthorize(options);
|
|
699
713
|
if (!ioAuthorize) return null;
|
|
700
714
|
if (!token) return null;
|
|
701
|
-
if (!ioAuthorize.secret)
|
|
715
|
+
if (!ioAuthorize.secret)
|
|
716
|
+
throw new Error("`authorize` was specified without a valid secret");
|
|
702
717
|
const payload = yield authorize({
|
|
703
718
|
platform: this._platform,
|
|
704
719
|
secret: ioAuthorize.secret
|
|
@@ -1099,7 +1114,7 @@ var pickBy = (obj, by) => {
|
|
|
1099
1114
|
};
|
|
1100
1115
|
|
|
1101
1116
|
// src/version.generated.ts
|
|
1102
|
-
var version = "0.
|
|
1117
|
+
var version = "1.0.1";
|
|
1103
1118
|
|
|
1104
1119
|
// src/PluvServer.ts
|
|
1105
1120
|
var PluvServer = class {
|
|
@@ -1142,16 +1157,28 @@ var PluvServer = class {
|
|
|
1142
1157
|
}
|
|
1143
1158
|
};
|
|
1144
1159
|
}).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, room, session }) {
|
|
1145
|
-
var _a
|
|
1160
|
+
var _a;
|
|
1146
1161
|
const oldState = yield this._platform.persistence.getStorageState(room);
|
|
1147
1162
|
const update = data == null ? void 0 : data.update;
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1163
|
+
if (!oldState) {
|
|
1164
|
+
const loadedState = yield (_a = this._getInitialStorage) == null ? void 0 : _a.call(this, { context, room });
|
|
1165
|
+
if (!!loadedState) {
|
|
1166
|
+
doc.applyEncodedState({ update: loadedState }).getEncodedState();
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (!doc.isEmpty()) {
|
|
1170
|
+
const encodedState2 = doc.getEncodedState();
|
|
1171
|
+
return { $storageReceived: { state: encodedState2 } };
|
|
1172
|
+
}
|
|
1173
|
+
if (!!update) {
|
|
1174
|
+
const encodedState2 = doc.applyEncodedState({ update }).getEncodedState();
|
|
1175
|
+
const storageSize = new TextEncoder().encode(encodedState2).length;
|
|
1176
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1177
|
+
throw new Error("Storage has exceeded the size limit");
|
|
1178
|
+
}
|
|
1179
|
+
yield this._platform.persistence.setStorageState(room, encodedState2).catch((error) => {
|
|
1180
|
+
this._logDebug(error);
|
|
1181
|
+
});
|
|
1155
1182
|
this._getListeners().onStorageUpdated({
|
|
1156
1183
|
context,
|
|
1157
1184
|
encodedState: encodedState2,
|
|
@@ -1160,11 +1187,7 @@ var PluvServer = class {
|
|
|
1160
1187
|
webSocket: session == null ? void 0 : session.webSocket.webSocket
|
|
1161
1188
|
});
|
|
1162
1189
|
}
|
|
1163
|
-
const encodedState =
|
|
1164
|
-
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1165
|
-
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1166
|
-
throw new Error("Storage has exceeded the size limit");
|
|
1167
|
-
}
|
|
1190
|
+
const encodedState = doc.getEncodedState();
|
|
1168
1191
|
return { $storageReceived: { state: encodedState } };
|
|
1169
1192
|
})),
|
|
1170
1193
|
$ping: this._procedure.self((data, { session }) => {
|
|
@@ -1232,7 +1255,13 @@ var PluvServer = class {
|
|
|
1232
1255
|
platform,
|
|
1233
1256
|
router = new PluvRouter({})
|
|
1234
1257
|
} = options;
|
|
1235
|
-
const {
|
|
1258
|
+
const {
|
|
1259
|
+
onRoomDeleted,
|
|
1260
|
+
onRoomMessage,
|
|
1261
|
+
onStorageUpdated,
|
|
1262
|
+
onUserConnected,
|
|
1263
|
+
onUserDisconnected
|
|
1264
|
+
} = options;
|
|
1236
1265
|
platform.validateConfig(options);
|
|
1237
1266
|
this._crdt = crdt;
|
|
1238
1267
|
this._debug = debug;
|
|
@@ -1253,7 +1282,8 @@ var PluvServer = class {
|
|
|
1253
1282
|
}
|
|
1254
1283
|
get fetch() {
|
|
1255
1284
|
return (...args) => {
|
|
1256
|
-
if (!this._platform._fetch)
|
|
1285
|
+
if (!this._platform._fetch)
|
|
1286
|
+
throw new Error(`\`${this._platform._name}\` does not support \`fetch\``);
|
|
1257
1287
|
return this._platform._fetch(...args);
|
|
1258
1288
|
};
|
|
1259
1289
|
}
|
|
@@ -1279,7 +1309,8 @@ var PluvServer = class {
|
|
|
1279
1309
|
if (this._platform._config.handleMode !== "io") {
|
|
1280
1310
|
throw new Error(`\`createRoom\` is unsupported for \`${this._platform._name}\``);
|
|
1281
1311
|
}
|
|
1282
|
-
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
|
|
1312
|
+
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
|
|
1313
|
+
throw new Error("Unsupported room name");
|
|
1283
1314
|
const roomContext = platformRoomContext;
|
|
1284
1315
|
const context = typeof this._context === "function" ? this._context(roomContext) : this._context;
|
|
1285
1316
|
const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
|
|
@@ -1349,7 +1380,8 @@ var PluvIO = class {
|
|
|
1349
1380
|
createToken(params) {
|
|
1350
1381
|
return __async(this, null, function* () {
|
|
1351
1382
|
var _a;
|
|
1352
|
-
if (!this._authorize)
|
|
1383
|
+
if (!this._authorize)
|
|
1384
|
+
throw new Error("IO does not specify authorize during initialization.");
|
|
1353
1385
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
1354
1386
|
const user = params.user;
|
|
1355
1387
|
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
@@ -1367,7 +1399,9 @@ var PluvIO = class {
|
|
|
1367
1399
|
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1368
1400
|
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1369
1401
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1370
|
-
return yield authorize({ platform: this._platform, secret }).encode(
|
|
1402
|
+
return yield authorize({ platform: this._platform, secret }).encode(
|
|
1403
|
+
params
|
|
1404
|
+
);
|
|
1371
1405
|
});
|
|
1372
1406
|
}
|
|
1373
1407
|
mergeRouters(...routers) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "@pluv/io server-side websocket manager",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"jose": "^5.10.0",
|
|
22
22
|
"kleur": "^4.1.5",
|
|
23
23
|
"wonka": "^6.3.5",
|
|
24
|
-
"@pluv/crdt": "^0.
|
|
25
|
-
"@pluv/types": "^0.
|
|
24
|
+
"@pluv/crdt": "^1.0.1",
|
|
25
|
+
"@pluv/types": "^1.0.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"eslint": "^9.25.1",
|
|
29
29
|
"genversion": "^3.2.0",
|
|
30
30
|
"tsup": "^8.4.0",
|
|
31
31
|
"typescript": "^5.8.3",
|
|
32
|
-
"@pluv/tsconfig": "^0.
|
|
33
|
-
"eslint-config-pluv": "^0.
|
|
32
|
+
"@pluv/tsconfig": "^1.0.1",
|
|
33
|
+
"eslint-config-pluv": "^1.0.1"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { JsonObject } from "@pluv/types";
|
|
2
2
|
|
|
3
3
|
export abstract class AbstractPersistence {
|
|
4
|
-
public abstract addUser(
|
|
4
|
+
public abstract addUser(
|
|
5
|
+
room: string,
|
|
6
|
+
connectionId: string,
|
|
7
|
+
user: JsonObject | null,
|
|
8
|
+
): Promise<void>;
|
|
5
9
|
|
|
6
10
|
public abstract deleteStorageState(room: string): Promise<void>;
|
|
7
11
|
|
|
@@ -13,7 +17,9 @@ export abstract class AbstractPersistence {
|
|
|
13
17
|
|
|
14
18
|
public abstract getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
15
19
|
|
|
16
|
-
public abstract getUsers(
|
|
20
|
+
public abstract getUsers(
|
|
21
|
+
room: string,
|
|
22
|
+
): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
17
23
|
|
|
18
24
|
public abstract getUsersSize(room: string): Promise<number>;
|
|
19
25
|
|
package/src/AbstractPlatform.ts
CHANGED
|
@@ -10,7 +10,9 @@ export type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> =
|
|
|
10
10
|
TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;
|
|
11
11
|
|
|
12
12
|
export type InferPlatformWebSocketSource<TPlatform> =
|
|
13
|
-
TPlatform extends AbstractPlatform<infer IAbstractWebSocket>
|
|
13
|
+
TPlatform extends AbstractPlatform<infer IAbstractWebSocket>
|
|
14
|
+
? InferWebSocketSource<IAbstractWebSocket>
|
|
15
|
+
: never;
|
|
14
16
|
|
|
15
17
|
export type InferInitContextType<TPlatform extends AbstractPlatform> =
|
|
16
18
|
TPlatform extends AbstractPlatform<any, infer IInitContext> ? IInitContext : never;
|
|
@@ -66,7 +68,9 @@ export abstract class AbstractPlatform<
|
|
|
66
68
|
|
|
67
69
|
public abstract getLastPing(webSocket: AbstractWebSocket<any, any>): number | null;
|
|
68
70
|
|
|
69
|
-
public abstract getSerializedState(
|
|
71
|
+
public abstract getSerializedState(
|
|
72
|
+
webSocket: InferWebSocketSource<TWebSocket>,
|
|
73
|
+
): WebSocketSerializedState | null;
|
|
70
74
|
|
|
71
75
|
public abstract getSessionId(webSocket: InferWebSocketSource<TWebSocket>): string | null;
|
|
72
76
|
|
package/src/AbstractWebSocket.ts
CHANGED
|
@@ -54,7 +54,10 @@ export interface AbstractWebSocketConfig {
|
|
|
54
54
|
room: string;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
export abstract class AbstractWebSocket<
|
|
57
|
+
export abstract class AbstractWebSocket<
|
|
58
|
+
TWebSocket = any,
|
|
59
|
+
TAuthorize extends IOAuthorize<any, any> | null = null,
|
|
60
|
+
> {
|
|
58
61
|
/** The connection is not yet open. */
|
|
59
62
|
public readonly CONNECTING = 0;
|
|
60
63
|
/** The connection is open and ready to communicate. */
|
|
@@ -96,7 +99,10 @@ export abstract class AbstractWebSocket<TWebSocket = any, TAuthorize extends IOA
|
|
|
96
99
|
* result.
|
|
97
100
|
* @date August 28, 2022
|
|
98
101
|
*/
|
|
99
|
-
public abstract addEventListener<TType extends EventType>(
|
|
102
|
+
public abstract addEventListener<TType extends EventType>(
|
|
103
|
+
type: TType,
|
|
104
|
+
handler: AbstractListener<TType>,
|
|
105
|
+
): void;
|
|
100
106
|
|
|
101
107
|
public abstract close(code?: number | undefined, reason?: string | undefined): void;
|
|
102
108
|
|
|
@@ -120,7 +126,9 @@ export abstract class AbstractWebSocket<TWebSocket = any, TAuthorize extends IOA
|
|
|
120
126
|
});
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
public sendMessage<TMessage extends IOEventMessage<any> = IOEventMessage<any>>(
|
|
129
|
+
public sendMessage<TMessage extends IOEventMessage<any> = IOEventMessage<any>>(
|
|
130
|
+
data: TMessage,
|
|
131
|
+
): void {
|
|
124
132
|
return this.send(JSON.stringify(data));
|
|
125
133
|
}
|
|
126
134
|
}
|