@pluv/io 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -77,8 +77,8 @@ var __async = (__this, __arguments, generator) => {
77
77
  };
78
78
 
79
79
  // src/index.ts
80
- var src_exports = {};
81
- __export(src_exports, {
80
+ var index_exports = {};
81
+ __export(index_exports, {
82
82
  AbstractPersistence: () => AbstractPersistence,
83
83
  AbstractPlatform: () => AbstractPlatform,
84
84
  AbstractPubSub: () => AbstractPubSub,
@@ -90,7 +90,7 @@ __export(src_exports, {
90
90
  authorize: () => authorize,
91
91
  createIO: () => createIO
92
92
  });
93
- module.exports = __toCommonJS(src_exports);
93
+ module.exports = __toCommonJS(index_exports);
94
94
 
95
95
  // src/AbstractPersistence.ts
96
96
  var AbstractPersistence = class {
@@ -224,6 +224,8 @@ var AbstractPlatform = class {
224
224
  this.persistence = persistence != null ? persistence : new Persistence();
225
225
  this.pubSub = pubSub != null ? pubSub : new PubSub();
226
226
  }
227
+ validateConfig(config) {
228
+ }
227
229
  _initialize() {
228
230
  if (this._initialized) throw new Error("Platform is already initialized");
229
231
  this._initialized = true;
@@ -553,7 +555,7 @@ var IORoom = class {
553
555
  }
554
556
  this._sessions.set(pluvWs.sessionId, pluvWs);
555
557
  yield this._platform.persistence.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
556
- if (this._platform._registrationMode === "attached") {
558
+ if (this._platform._config.registrationMode === "attached") {
557
559
  const onClose = this._onClose(pluvWs).bind(this);
558
560
  const onMessage = this._onMessage(pluvWs).bind(this);
559
561
  pluvWs.addEventListener("close", onClose);
@@ -590,13 +592,7 @@ var IORoom = class {
590
592
  this._sessions.delete(pluvWs.sessionId);
591
593
  });
592
594
  const promises = quitters.map((quitter) => __async(this, null, function* () {
593
- yield this._broadcast({
594
- message: {
595
- type: "$EXIT",
596
- data: { sessionId: quitter.sessionId }
597
- },
598
- senderId: quitter.sessionId
599
- });
595
+ yield this._exitWebsocket(quitter);
600
596
  }));
601
597
  yield Promise.all(promises);
602
598
  });
@@ -624,7 +620,7 @@ var IORoom = class {
624
620
  });
625
621
  }
626
622
  _ensureDetached() {
627
- if (this._platform._registrationMode === "detached") return;
623
+ if (this._platform._config.registrationMode === "detached") return;
628
624
  throw new Error("Platform must use detached mode");
629
625
  }
630
626
  _exitWebsocket(webSocket) {
@@ -669,6 +665,7 @@ var IORoom = class {
669
665
  const ioAuthorize = this._getIOAuthorize(options);
670
666
  if (!ioAuthorize) return null;
671
667
  if (!token) return null;
668
+ if (!ioAuthorize.secret) throw new Error("`authorize` was specified without a valid secret");
672
669
  const payload = yield authorize({
673
670
  platform: this._platform,
674
671
  secret: ioAuthorize.secret
@@ -795,13 +792,7 @@ var IORoom = class {
795
792
  this._logDebug(`${import_kleur.default.blue(`Unregistering connection for room ${this.id}:`)} ${webSocket.sessionId}`);
796
793
  this._sessions.delete(webSocket.sessionId);
797
794
  this._platform.persistence.deleteUser(this.id, webSocket.sessionId).finally(() => __async(this, null, function* () {
798
- yield this._broadcast({
799
- message: {
800
- type: "$EXIT",
801
- data: { sessionId: webSocket.sessionId }
802
- },
803
- senderId: webSocket.sessionId
804
- });
795
+ yield this._exitWebsocket(webSocket);
805
796
  const size = this.getSize();
806
797
  this._logDebug(`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`);
807
798
  this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
@@ -1002,7 +993,7 @@ var IORoom = class {
1002
993
  };
1003
994
 
1004
995
  // src/version.generated.ts
1005
- var version = "0.33.0";
996
+ var version = "0.34.0";
1006
997
 
1007
998
  // src/PluvServer.ts
1008
999
  var PluvServer = class {
@@ -1106,14 +1097,11 @@ var PluvServer = class {
1106
1097
  crdt = import_crdt2.noop,
1107
1098
  debug = false,
1108
1099
  getInitialStorage,
1109
- onRoomDeleted,
1110
- onRoomMessage,
1111
- onStorageUpdated,
1112
- onUserConnected,
1113
- onUserDisconnected,
1114
1100
  platform,
1115
1101
  router = new PluvRouter({})
1116
1102
  } = options;
1103
+ const { onRoomDeleted, onRoomMessage, onStorageUpdated, onUserConnected, onUserDisconnected } = options;
1104
+ platform.validateConfig(options);
1117
1105
  this._crdt = crdt;
1118
1106
  this._debug = debug;
1119
1107
  this._platform = platform;
@@ -1129,6 +1117,10 @@ var PluvServer = class {
1129
1117
  onUserDisconnected: (event) => onUserDisconnected == null ? void 0 : onUserDisconnected(event)
1130
1118
  };
1131
1119
  }
1120
+ get fetch() {
1121
+ if (!this._platform._fetch) throw new Error(`\`${this._platform._name}\` does not support \`fetch\``);
1122
+ return this._platform._fetch;
1123
+ }
1132
1124
  /**
1133
1125
  * @ignore
1134
1126
  * @readonly
@@ -1148,6 +1140,9 @@ var PluvServer = class {
1148
1140
  createRoom(room, ...options) {
1149
1141
  var _a, _c;
1150
1142
  const _b = (_a = options[0]) != null ? _a : {}, { _meta, debug, onDestroy, onMessage } = _b, platformRoomContext = __objRest(_b, ["_meta", "debug", "onDestroy", "onMessage"]);
1143
+ if (this._platform._config.handleMode !== "io") {
1144
+ throw new Error(`\`createRoom' is unsupported for \`${this._platform._name}\``);
1145
+ }
1151
1146
  if (!/^[a-z0-9]+[a-z0-9\-_]+[a-z0-9]+$/i.test(room)) throw new Error("Unsupported room name");
1152
1147
  const roomContext = platformRoomContext;
1153
1148
  const context = typeof this._context === "function" ? this._context(roomContext) : this._context;
@@ -1182,17 +1177,23 @@ var PluvServer = class {
1182
1177
  }
1183
1178
  createToken(params) {
1184
1179
  return __async(this, null, function* () {
1180
+ var _a;
1185
1181
  if (!this._authorize) {
1186
1182
  throw new Error("IO does not specify authorize during initialization.");
1187
1183
  }
1188
- const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
1189
- const secret = ioAuthorize.secret;
1190
- return yield authorize({
1191
- platform: this._platform,
1192
- secret
1193
- }).encode(params);
1184
+ if (this._platform._createToken) return yield this._platform._createToken(params);
1185
+ const ioAuthorize = this._getIOAuthorize(params);
1186
+ const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
1187
+ if (!secret) throw new Error("`authorize` was specified without a valid secret");
1188
+ return yield authorize({ platform: this._platform, secret }).encode(params);
1194
1189
  });
1195
1190
  }
1191
+ _getIOAuthorize(options) {
1192
+ if (typeof this._authorize === "function") {
1193
+ return this._authorize(options);
1194
+ }
1195
+ return this._authorize;
1196
+ }
1196
1197
  _getListeners() {
1197
1198
  return this._listeners;
1198
1199
  }
@@ -1219,13 +1220,12 @@ var PluvIO = class {
1219
1220
  }
1220
1221
  createToken(params) {
1221
1222
  return __async(this, null, function* () {
1223
+ var _a;
1222
1224
  if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
1223
- const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
1224
- const secret = ioAuthorize.secret;
1225
- return yield authorize({
1226
- platform: this._platform,
1227
- secret
1228
- }).encode(params);
1225
+ const ioAuthorize = this._getIOAuthorize(params);
1226
+ const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
1227
+ if (!secret) throw new Error("`authorize` was specified without a valid secret");
1228
+ return yield authorize({ platform: this._platform, secret }).encode(params);
1229
1229
  });
1230
1230
  }
1231
1231
  mergeRouters(...routers) {
@@ -1244,6 +1244,12 @@ var PluvIO = class {
1244
1244
  platform: this._platform
1245
1245
  }));
1246
1246
  }
1247
+ _getIOAuthorize(options) {
1248
+ if (typeof this._authorize === "function") {
1249
+ return this._authorize(options);
1250
+ }
1251
+ return this._authorize;
1252
+ }
1247
1253
  };
1248
1254
 
1249
1255
  // src/createIO.ts
package/dist/index.mjs CHANGED
@@ -182,6 +182,8 @@ var AbstractPlatform = class {
182
182
  this.persistence = persistence != null ? persistence : new Persistence();
183
183
  this.pubSub = pubSub != null ? pubSub : new PubSub();
184
184
  }
185
+ validateConfig(config) {
186
+ }
185
187
  _initialize() {
186
188
  if (this._initialized) throw new Error("Platform is already initialized");
187
189
  this._initialized = true;
@@ -511,7 +513,7 @@ var IORoom = class {
511
513
  }
512
514
  this._sessions.set(pluvWs.sessionId, pluvWs);
513
515
  yield this._platform.persistence.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
514
- if (this._platform._registrationMode === "attached") {
516
+ if (this._platform._config.registrationMode === "attached") {
515
517
  const onClose = this._onClose(pluvWs).bind(this);
516
518
  const onMessage = this._onMessage(pluvWs).bind(this);
517
519
  pluvWs.addEventListener("close", onClose);
@@ -548,13 +550,7 @@ var IORoom = class {
548
550
  this._sessions.delete(pluvWs.sessionId);
549
551
  });
550
552
  const promises = quitters.map((quitter) => __async(this, null, function* () {
551
- yield this._broadcast({
552
- message: {
553
- type: "$EXIT",
554
- data: { sessionId: quitter.sessionId }
555
- },
556
- senderId: quitter.sessionId
557
- });
553
+ yield this._exitWebsocket(quitter);
558
554
  }));
559
555
  yield Promise.all(promises);
560
556
  });
@@ -582,7 +578,7 @@ var IORoom = class {
582
578
  });
583
579
  }
584
580
  _ensureDetached() {
585
- if (this._platform._registrationMode === "detached") return;
581
+ if (this._platform._config.registrationMode === "detached") return;
586
582
  throw new Error("Platform must use detached mode");
587
583
  }
588
584
  _exitWebsocket(webSocket) {
@@ -627,6 +623,7 @@ var IORoom = class {
627
623
  const ioAuthorize = this._getIOAuthorize(options);
628
624
  if (!ioAuthorize) return null;
629
625
  if (!token) return null;
626
+ if (!ioAuthorize.secret) throw new Error("`authorize` was specified without a valid secret");
630
627
  const payload = yield authorize({
631
628
  platform: this._platform,
632
629
  secret: ioAuthorize.secret
@@ -753,13 +750,7 @@ var IORoom = class {
753
750
  this._logDebug(`${colors.blue(`Unregistering connection for room ${this.id}:`)} ${webSocket.sessionId}`);
754
751
  this._sessions.delete(webSocket.sessionId);
755
752
  this._platform.persistence.deleteUser(this.id, webSocket.sessionId).finally(() => __async(this, null, function* () {
756
- yield this._broadcast({
757
- message: {
758
- type: "$EXIT",
759
- data: { sessionId: webSocket.sessionId }
760
- },
761
- senderId: webSocket.sessionId
762
- });
753
+ yield this._exitWebsocket(webSocket);
763
754
  const size = this.getSize();
764
755
  this._logDebug(`${colors.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`);
765
756
  this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
@@ -960,7 +951,7 @@ var IORoom = class {
960
951
  };
961
952
 
962
953
  // src/version.generated.ts
963
- var version = "0.33.0";
954
+ var version = "0.34.0";
964
955
 
965
956
  // src/PluvServer.ts
966
957
  var PluvServer = class {
@@ -1064,14 +1055,11 @@ var PluvServer = class {
1064
1055
  crdt = noop2,
1065
1056
  debug = false,
1066
1057
  getInitialStorage,
1067
- onRoomDeleted,
1068
- onRoomMessage,
1069
- onStorageUpdated,
1070
- onUserConnected,
1071
- onUserDisconnected,
1072
1058
  platform,
1073
1059
  router = new PluvRouter({})
1074
1060
  } = options;
1061
+ const { onRoomDeleted, onRoomMessage, onStorageUpdated, onUserConnected, onUserDisconnected } = options;
1062
+ platform.validateConfig(options);
1075
1063
  this._crdt = crdt;
1076
1064
  this._debug = debug;
1077
1065
  this._platform = platform;
@@ -1087,6 +1075,10 @@ var PluvServer = class {
1087
1075
  onUserDisconnected: (event) => onUserDisconnected == null ? void 0 : onUserDisconnected(event)
1088
1076
  };
1089
1077
  }
1078
+ get fetch() {
1079
+ if (!this._platform._fetch) throw new Error(`\`${this._platform._name}\` does not support \`fetch\``);
1080
+ return this._platform._fetch;
1081
+ }
1090
1082
  /**
1091
1083
  * @ignore
1092
1084
  * @readonly
@@ -1106,6 +1098,9 @@ var PluvServer = class {
1106
1098
  createRoom(room, ...options) {
1107
1099
  var _a, _c;
1108
1100
  const _b = (_a = options[0]) != null ? _a : {}, { _meta, debug, onDestroy, onMessage } = _b, platformRoomContext = __objRest(_b, ["_meta", "debug", "onDestroy", "onMessage"]);
1101
+ if (this._platform._config.handleMode !== "io") {
1102
+ throw new Error(`\`createRoom' is unsupported for \`${this._platform._name}\``);
1103
+ }
1109
1104
  if (!/^[a-z0-9]+[a-z0-9\-_]+[a-z0-9]+$/i.test(room)) throw new Error("Unsupported room name");
1110
1105
  const roomContext = platformRoomContext;
1111
1106
  const context = typeof this._context === "function" ? this._context(roomContext) : this._context;
@@ -1140,17 +1135,23 @@ var PluvServer = class {
1140
1135
  }
1141
1136
  createToken(params) {
1142
1137
  return __async(this, null, function* () {
1138
+ var _a;
1143
1139
  if (!this._authorize) {
1144
1140
  throw new Error("IO does not specify authorize during initialization.");
1145
1141
  }
1146
- const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
1147
- const secret = ioAuthorize.secret;
1148
- return yield authorize({
1149
- platform: this._platform,
1150
- secret
1151
- }).encode(params);
1142
+ if (this._platform._createToken) return yield this._platform._createToken(params);
1143
+ const ioAuthorize = this._getIOAuthorize(params);
1144
+ const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
1145
+ if (!secret) throw new Error("`authorize` was specified without a valid secret");
1146
+ return yield authorize({ platform: this._platform, secret }).encode(params);
1152
1147
  });
1153
1148
  }
1149
+ _getIOAuthorize(options) {
1150
+ if (typeof this._authorize === "function") {
1151
+ return this._authorize(options);
1152
+ }
1153
+ return this._authorize;
1154
+ }
1154
1155
  _getListeners() {
1155
1156
  return this._listeners;
1156
1157
  }
@@ -1177,13 +1178,12 @@ var PluvIO = class {
1177
1178
  }
1178
1179
  createToken(params) {
1179
1180
  return __async(this, null, function* () {
1181
+ var _a;
1180
1182
  if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
1181
- const ioAuthorize = typeof this._authorize === "function" ? this._authorize(params) : this._authorize;
1182
- const secret = ioAuthorize.secret;
1183
- return yield authorize({
1184
- platform: this._platform,
1185
- secret
1186
- }).encode(params);
1183
+ const ioAuthorize = this._getIOAuthorize(params);
1184
+ const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
1185
+ if (!secret) throw new Error("`authorize` was specified without a valid secret");
1186
+ return yield authorize({ platform: this._platform, secret }).encode(params);
1187
1187
  });
1188
1188
  }
1189
1189
  mergeRouters(...routers) {
@@ -1202,6 +1202,12 @@ var PluvIO = class {
1202
1202
  platform: this._platform
1203
1203
  }));
1204
1204
  }
1205
+ _getIOAuthorize(options) {
1206
+ if (typeof this._authorize === "function") {
1207
+ return this._authorize(options);
1208
+ }
1209
+ return this._authorize;
1210
+ }
1205
1211
  };
1206
1212
 
1207
1213
  // src/createIO.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/io",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
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.9.6",
22
22
  "kleur": "^4.1.5",
23
23
  "wonka": "^6.3.4",
24
- "@pluv/crdt": "^0.33.0",
25
- "@pluv/types": "^0.33.0"
24
+ "@pluv/crdt": "^0.34.0",
25
+ "@pluv/types": "^0.34.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "eslint": "^8.57.1",
29
29
  "genversion": "^3.2.0",
30
30
  "tsup": "^8.3.5",
31
31
  "typescript": "^5.7.2",
32
- "@pluv/tsconfig": "^0.33.0",
33
- "eslint-config-pluv": "^0.33.0"
32
+ "@pluv/tsconfig": "^0.34.0",
33
+ "eslint-config-pluv": "^0.34.0"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
@@ -1,11 +1,10 @@
1
1
  import type { AbstractPersistence } from "./AbstractPersistence";
2
2
  import type { AbstractPubSub } from "./AbstractPubSub";
3
3
  import type { AbstractWebSocket, InferWebSocketSource } from "./AbstractWebSocket";
4
+ import type { JWTEncodeParams } from "./authorize";
4
5
  import { Persistence } from "./Persistence";
5
6
  import { PubSub } from "./PubSub";
6
- import type { WebSocketSerializedState } from "./types";
7
-
8
- export type WebSocketRegistrationMode = "attached" | "detached";
7
+ import type { PlatformConfig, WebSocketSerializedState } from "./types";
9
8
 
10
9
  export type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> =
11
10
  TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;
@@ -33,6 +32,7 @@ export abstract class AbstractPlatform<
33
32
  TWebSocket extends AbstractWebSocket = AbstractWebSocket,
34
33
  TInitContext extends Record<string, any> = {},
35
34
  TRoomContext extends Record<string, any> = {},
35
+ TConfig extends PlatformConfig = PlatformConfig,
36
36
  > {
37
37
  protected readonly _initContext: TInitContext | undefined;
38
38
  protected readonly _roomContext: TRoomContext | undefined;
@@ -42,7 +42,10 @@ export abstract class AbstractPlatform<
42
42
  public persistence: AbstractPersistence;
43
43
  public pubSub: AbstractPubSub;
44
44
 
45
- public abstract readonly _registrationMode: WebSocketRegistrationMode;
45
+ public readonly _createToken?: (params: JWTEncodeParams<any, any>) => Promise<string>;
46
+ public _fetch?: (req: any) => Promise<any>;
47
+ public abstract readonly _config: TConfig;
48
+ public abstract readonly _name: string;
46
49
 
47
50
  constructor(config: AbstractPlatformConfig<TRoomContext> = {}) {
48
51
  const { context, persistence, pubSub } = config;
@@ -77,6 +80,8 @@ export abstract class AbstractPlatform<
77
80
 
78
81
  public abstract setSerializedState(webSocket: AbstractWebSocket, state: WebSocketSerializedState): void;
79
82
 
83
+ public validateConfig(config: any): void {}
84
+
80
85
  protected _initialize(): this {
81
86
  if (this._initialized) throw new Error("Platform is already initialized");
82
87
 
package/src/IORoom.ts CHANGED
@@ -34,6 +34,8 @@ import type {
34
34
  IORoomMessageEvent,
35
35
  IOUserConnectedEvent,
36
36
  IOUserDisconnectedEvent,
37
+ PluvIOAuthorize,
38
+ ResolvedPluvIOAuthorize,
37
39
  WebSocketSession,
38
40
  WebSocketType,
39
41
  } from "./types";
@@ -49,7 +51,7 @@ interface BroadcastParams<TIO extends IORoom<any, any, any, any>> {
49
51
 
50
52
  export interface IORoomListeners<
51
53
  TPlatform extends AbstractPlatform<any>,
52
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>>,
54
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>>,
53
55
  TContext extends Record<string, any>,
54
56
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
55
57
  > {
@@ -68,7 +70,7 @@ export type BroadcastProxy<TIO extends IORoom<any, any, any, any>> = (<TEvent ex
68
70
 
69
71
  export type IORoomConfig<
70
72
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
71
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
73
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
72
74
  TContext extends Record<string, any> = {},
73
75
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
74
76
  > = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
@@ -92,7 +94,7 @@ export type WebsocketRegisterConfig<TPlatform extends AbstractPlatform<any> = Ab
92
94
 
93
95
  export class IORoom<
94
96
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
95
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
97
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
96
98
  TContext extends Record<string, any> = {},
97
99
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
98
100
  > implements IOLike<TAuthorize, TEvents>
@@ -320,7 +322,7 @@ export class IORoom<
320
322
 
321
323
  await this._platform.persistence.addUser(this.id, pluvWs.sessionId, user ?? {});
322
324
 
323
- if (this._platform._registrationMode === "attached") {
325
+ if (this._platform._config.registrationMode === "attached") {
324
326
  const onClose = this._onClose(pluvWs).bind(this);
325
327
  const onMessage = this._onMessage(pluvWs).bind(this);
326
328
 
@@ -364,13 +366,7 @@ export class IORoom<
364
366
  });
365
367
 
366
368
  const promises = quitters.map(async (quitter) => {
367
- await this._broadcast({
368
- message: {
369
- type: "$EXIT",
370
- data: { sessionId: quitter.sessionId },
371
- },
372
- senderId: quitter.sessionId,
373
- });
369
+ await this._exitWebsocket(quitter);
374
370
  });
375
371
 
376
372
  await Promise.all(promises);
@@ -400,7 +396,7 @@ export class IORoom<
400
396
  }
401
397
 
402
398
  private _ensureDetached(): void {
403
- if (this._platform._registrationMode === "detached") return;
399
+ if (this._platform._config.registrationMode === "detached") return;
404
400
 
405
401
  throw new Error("Platform must use detached mode");
406
402
  }
@@ -456,6 +452,8 @@ export class IORoom<
456
452
  if (!ioAuthorize) return null as InferIOAuthorizeUser<InferIOAuthorize<this>>;
457
453
  if (!token) return null as InferIOAuthorizeUser<InferIOAuthorize<this>>;
458
454
 
455
+ if (!ioAuthorize.secret) throw new Error("`authorize` was specified without a valid secret");
456
+
459
457
  const payload = await authorize({
460
458
  platform: this._platform,
461
459
  secret: ioAuthorize.secret,
@@ -494,16 +492,14 @@ export class IORoom<
494
492
  return doc;
495
493
  }
496
494
 
497
- private _getIOAuthorize(options: WebsocketRegisterConfig<TPlatform>) {
495
+ private _getIOAuthorize(
496
+ options: WebsocketRegisterConfig<TPlatform>,
497
+ ): ResolvedPluvIOAuthorize<any, any, any> | null {
498
498
  if (typeof this._authorize === "function") {
499
499
  return this._authorize(options);
500
500
  }
501
501
 
502
- return this._authorize as {
503
- required: any;
504
- secret: string;
505
- user: InputZodLike<any>;
506
- } | null;
502
+ return this._authorize as ResolvedPluvIOAuthorize<any, any, any> | null;
507
503
  }
508
504
 
509
505
  private _getProcedure(
@@ -619,13 +615,7 @@ export class IORoom<
619
615
  this._sessions.delete(webSocket.sessionId);
620
616
 
621
617
  this._platform.persistence.deleteUser(this.id, webSocket.sessionId).finally(async () => {
622
- await this._broadcast({
623
- message: {
624
- type: "$EXIT",
625
- data: { sessionId: webSocket.sessionId },
626
- },
627
- senderId: webSocket.sessionId,
628
- });
618
+ await this._exitWebsocket(webSocket);
629
619
 
630
620
  const size = this.getSize();
631
621
 
package/src/PluvIO.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { AbstractCrdtDocFactory } from "@pluv/crdt";
2
2
  import { noop } from "@pluv/crdt";
3
- import type { BaseIOAuthorize, IOAuthorize, InferIOAuthorizeUser } from "@pluv/types";
3
+ import type { IOAuthorize, InferIOAuthorizeUser, InputZodLike } from "@pluv/types";
4
4
  import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
5
5
  import { PluvProcedure } from "./PluvProcedure";
6
6
  import type { MergedRouter, PluvRouterEventConfig } from "./PluvRouter";
@@ -8,24 +8,34 @@ import { PluvRouter } from "./PluvRouter";
8
8
  import { PluvServer } from "./PluvServer";
9
9
  import type { JWTEncodeParams } from "./authorize";
10
10
  import { authorize } from "./authorize";
11
- import type { CrdtLibraryType, GetInitialStorageFn, PluvContext, PluvIOListeners } from "./types";
11
+ import type {
12
+ CrdtLibraryType,
13
+ GetInitialStorageFn,
14
+ InferPlatformConfig,
15
+ PluvContext,
16
+ PluvIOAuthorize,
17
+ PluvIOListeners,
18
+ ResolvedPluvIOAuthorize,
19
+ } from "./types";
12
20
  import { __PLUV_VERSION } from "./version";
21
+ import { WebsocketRegisterConfig } from "./IORoom";
13
22
 
14
23
  export type PluvIOConfig<
15
24
  TPlatform extends AbstractPlatform<any>,
16
25
  TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>>,
17
26
  TContext extends Record<string, any>,
18
27
  > = {
19
- authorize?: TAuthorize;
20
28
  context?: PluvContext<TPlatform, TContext>;
21
29
  crdt?: CrdtLibraryType;
22
30
  debug?: boolean;
23
31
  platform: TPlatform;
24
- };
32
+ } & (InferPlatformConfig<TPlatform>["requireAuth"] extends true
33
+ ? { authorize: TAuthorize }
34
+ : { authorize?: TAuthorize });
25
35
 
26
36
  export type ServerConfig<
27
37
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
28
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
38
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
29
39
  TContext extends Record<string, any> = {},
30
40
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
31
41
  > = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
@@ -35,7 +45,7 @@ export type ServerConfig<
35
45
 
36
46
  export class PluvIO<
37
47
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
38
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
48
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
39
49
  TContext extends Record<string, any> = {},
40
50
  > {
41
51
  public readonly version: string = __PLUV_VERSION as any;
@@ -64,15 +74,12 @@ export class PluvIO<
64
74
  public async createToken(params: JWTEncodeParams<InferIOAuthorizeUser<TAuthorize>, TPlatform>): Promise<string> {
65
75
  if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
66
76
 
67
- const ioAuthorize =
68
- typeof this._authorize === "function" ? this._authorize(params) : (this._authorize as { secret: string });
77
+ const ioAuthorize = this._getIOAuthorize(params);
78
+ const secret = ioAuthorize?.secret ?? null;
69
79
 
70
- const secret = ioAuthorize.secret;
80
+ if (!secret) throw new Error("`authorize` was specified without a valid secret");
71
81
 
72
- return await authorize({
73
- platform: this._platform,
74
- secret,
75
- }).encode(params);
82
+ return await authorize({ platform: this._platform, secret }).encode(params);
76
83
  }
77
84
 
78
85
  public mergeRouters<TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[]>(
@@ -99,4 +106,14 @@ export class PluvIO<
99
106
  platform: this._platform,
100
107
  });
101
108
  }
109
+
110
+ private _getIOAuthorize(
111
+ options: WebsocketRegisterConfig<TPlatform>,
112
+ ): ResolvedPluvIOAuthorize<any, any, any> | null {
113
+ if (typeof this._authorize === "function") {
114
+ return this._authorize(options);
115
+ }
116
+
117
+ return this._authorize as ResolvedPluvIOAuthorize<any, any, any> | null;
118
+ }
102
119
  }
package/src/PluvRouter.ts CHANGED
@@ -1,17 +1,18 @@
1
- import type { BaseIOAuthorize, IOAuthorize, IORouterLike } from "@pluv/types";
1
+ import type { IORouterLike } from "@pluv/types";
2
2
  import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
3
3
  import type { PluvProcedure } from "./PluvProcedure";
4
+ import type { PluvIOAuthorize } from "./types";
4
5
 
5
6
  export type PluvRouterEventConfig<
6
7
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
7
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
8
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
8
9
  TContext extends Record<string, any> = {},
9
10
  > = { [P: string]: Pick<PluvProcedure<TPlatform, TAuthorize, TContext, any, any>, "config"> };
10
11
 
11
12
  export type MergedRouter<
12
13
  TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[] = [],
13
14
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
14
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
15
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
15
16
  TContext extends Record<string, any> = {},
16
17
  TRoot extends TRouters[0]["_defs"]["events"] = {},
17
18
  > = TRouters extends [
@@ -23,7 +24,7 @@ export type MergedRouter<
23
24
 
24
25
  export class PluvRouter<
25
26
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
26
- TAuthorize extends IOAuthorize<any, any, InferInitContextType<TPlatform>> = BaseIOAuthorize,
27
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, any, InferInitContextType<TPlatform>> = any,
27
28
  TContext extends Record<string, any> = {},
28
29
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
29
30
  > implements IORouterLike<TEvents>