@pluv/io 0.22.0 → 0.24.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/src/PluvIO.ts CHANGED
@@ -1,14 +1,6 @@
1
1
  import type { AbstractCrdtDocFactory } from "@pluv/crdt";
2
2
  import { noop } from "@pluv/crdt";
3
- import type {
4
- BaseIOAuthorize,
5
- IOAuthorize,
6
- IOLike,
7
- InferIOAuthorize,
8
- InferIOAuthorizeUser,
9
- JsonObject,
10
- Maybe,
11
- } from "@pluv/types";
3
+ import type { BaseIOAuthorize, IOAuthorize, InferIOAuthorizeUser, JsonObject } from "@pluv/types";
12
4
  import type { AbstractPlatform, InferPlatformContextType, WebSocketRegistrationMode } from "./AbstractPlatform";
13
5
  import { PluvProcedure } from "./PluvProcedure";
14
6
  import type { MergedRouter, PluvRouterEventConfig } from "./PluvRouter";
@@ -16,7 +8,6 @@ import { PluvRouter } from "./PluvRouter";
16
8
  import { PluvServer } from "./PluvServer";
17
9
  import type { JWTEncodeParams } from "./authorize";
18
10
  import { authorize } from "./authorize";
19
- import { PING_TIMEOUT_MS } from "./constants";
20
11
  import type { GetInitialStorageFn, PluvIOListeners } from "./types";
21
12
  import { __PLUV_VERSION } from "./version";
22
13
 
@@ -24,187 +15,43 @@ export type PluvIOConfig<
24
15
  TPlatform extends AbstractPlatform<any>,
25
16
  TAuthorize extends IOAuthorize<any, any, InferPlatformContextType<TPlatform>>,
26
17
  TContext extends JsonObject,
27
- > = Partial<PluvIOListeners<TPlatform>> & {
18
+ > = {
28
19
  authorize?: TAuthorize;
29
20
  context?: TContext;
30
21
  crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
31
22
  debug?: boolean;
32
- getInitialStorage?: GetInitialStorageFn<TPlatform>;
33
23
  platform: TPlatform;
34
24
  };
35
25
 
36
- export interface ServerConfig<
26
+ export type ServerConfig<
37
27
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
38
28
  TAuthorize extends IOAuthorize<any, any, InferPlatformContextType<TPlatform>> = BaseIOAuthorize,
39
29
  TContext extends JsonObject = {},
40
30
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
41
- > {
31
+ > = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
32
+ getInitialStorage?: GetInitialStorageFn<TPlatform>;
42
33
  router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
43
- }
34
+ };
44
35
 
45
36
  export class PluvIO<
46
37
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
47
38
  TAuthorize extends IOAuthorize<any, any, InferPlatformContextType<TPlatform>> = BaseIOAuthorize,
48
39
  TContext extends JsonObject = {},
49
- > implements IOLike<TAuthorize, {}>
50
- {
51
- private _router: PluvRouter<TPlatform, TAuthorize, TContext, {}> = new PluvRouter({
52
- $GET_OTHERS: this.procedure.sync((data, { room, session, sessions }) => {
53
- const currentTime = new Date().getTime();
54
-
55
- const others = sessions
56
- .filter((wsSession) => {
57
- if (wsSession.id === session?.id) return false;
58
- if (wsSession.quit) return false;
59
- if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;
60
-
61
- return true;
62
- })
63
- .reduce<{
64
- [connectionId: string]: {
65
- connectionId: string;
66
- room: string | null;
67
- user: JsonObject;
68
- };
69
- }>(
70
- (acc, { id, presence, user }) => ({
71
- ...acc,
72
- [id]: {
73
- connectionId: id,
74
- presence,
75
- room,
76
- user,
77
- },
78
- }),
79
- {},
80
- );
81
-
82
- return { $OTHERS_RECEIVED: { others } };
83
- }),
84
- $INITIALIZE_SESSION: this.procedure
85
- .broadcast((data, { session }) => {
86
- const presence = (data as any)?.presence;
87
-
88
- if (!session) return {};
89
-
90
- session.presence = presence;
91
-
92
- return {
93
- $USER_JOINED: {
94
- connectionId: session.id,
95
- user: session.user,
96
- presence,
97
- },
98
- };
99
- })
100
- .self(async (data, { context, doc, room }) => {
101
- const oldState = await this._platform.persistance.getStorageState(room);
102
- const update = (data as any)?.update as Maybe<string>;
103
-
104
- const updates: readonly Maybe<string>[] = [
105
- oldState
106
- ? null
107
- : await this._getInitialStorage?.({
108
- ...context,
109
- room,
110
- }),
111
- update,
112
- ];
113
-
114
- if (updates.some((_update) => !!_update)) {
115
- const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
116
-
117
- await this._platform.persistance.setStorageState(room, encodedState);
118
-
119
- this._listeners.onStorageUpdated({ ...context, encodedState, room });
120
- }
121
-
122
- const state = (await this._platform.persistance.getStorageState(room)) ?? doc.getEncodedState();
123
-
124
- return { $STORAGE_RECEIVED: { state } };
125
- }),
126
- $PING: this.procedure.self((data, { session }) => {
127
- if (!session) return {};
128
-
129
- const currentTime = new Date().getTime();
130
- const prevState = session.webSocket.state;
131
-
132
- session.webSocket.state = {
133
- ...prevState,
134
- timers: {
135
- ...prevState.timers,
136
- ping: currentTime,
137
- },
138
- };
139
-
140
- return { $PONG: {} };
141
- }),
142
- $UPDATE_PRESENCE: this.procedure.broadcast((data, { session }) => {
143
- const presence = (data as any)?.presence;
144
-
145
- if (!session) return {};
146
-
147
- const updated = Object.assign(Object.create(null), session.presence, presence);
148
-
149
- session.webSocket.presence = updated;
150
-
151
- return { $PRESENCE_UPDATED: { presence: updated } };
152
- }),
153
- $UPDATE_STORAGE: this.procedure.broadcast((data, { context, doc, room }) => {
154
- const origin = (data as any)?.origin as Maybe<string>;
155
- const update: string | null = (data as any)?.update ?? null;
156
-
157
- if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) {
158
- return {};
159
- }
160
-
161
- const updated = update === null ? doc : doc.applyEncodedState({ update });
162
- const encodedState = updated.getEncodedState();
163
-
164
- this._platform.persistance.setStorageState(room, encodedState).then(() => {
165
- this._listeners.onStorageUpdated({
166
- ...context,
167
- encodedState,
168
- room,
169
- });
170
- });
171
-
172
- return { $STORAGE_UPDATED: { state: encodedState } };
173
- }),
174
- });
175
-
176
- readonly _authorize: TAuthorize | null = null;
177
- readonly _context: TContext = {} as TContext;
178
- readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
179
- readonly _debug: boolean;
180
- readonly _getInitialStorage: GetInitialStorageFn<TPlatform> | null = null;
181
- readonly _listeners: PluvIOListeners<TPlatform>;
182
- readonly _platform: TPlatform;
183
- readonly _version: string = __PLUV_VERSION as any;
184
-
185
- public get _events() {
186
- return this._router._events;
187
- }
40
+ > {
41
+ public readonly version: string = __PLUV_VERSION as any;
42
+ public readonly _authorize: TAuthorize | null = null;
188
43
 
189
- public get _registrationMode(): WebSocketRegistrationMode {
190
- return this._platform._registrationMode;
191
- }
44
+ private readonly _context: TContext = {} as TContext;
45
+ private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
46
+ private readonly _debug: boolean;
47
+ private readonly _platform: TPlatform;
192
48
 
193
49
  public get procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}> {
194
50
  return new PluvProcedure();
195
51
  }
196
52
 
197
53
  constructor(options: PluvIOConfig<TPlatform, TAuthorize, TContext>) {
198
- const {
199
- authorize,
200
- context,
201
- crdt = noop,
202
- debug = false,
203
- getInitialStorage,
204
- onRoomDeleted,
205
- onStorageUpdated,
206
- platform,
207
- } = options;
54
+ const { authorize, context, crdt = noop, debug = false, platform } = options;
208
55
 
209
56
  this._crdt = crdt;
210
57
  this._debug = debug;
@@ -212,17 +59,9 @@ export class PluvIO<
212
59
 
213
60
  if (authorize) this._authorize = authorize;
214
61
  if (context) this._context = context;
215
- if (getInitialStorage) this._getInitialStorage = getInitialStorage;
216
-
217
- this._listeners = {
218
- onRoomDeleted: (event) => onRoomDeleted?.(event),
219
- onStorageUpdated: (event) => onStorageUpdated?.(event),
220
- };
221
62
  }
222
63
 
223
- public async createToken(
224
- params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>,
225
- ): Promise<string> {
64
+ public async createToken(params: JWTEncodeParams<InferIOAuthorizeUser<TAuthorize>, TPlatform>): Promise<string> {
226
65
  if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
227
66
 
228
67
  const ioAuthorize =
@@ -251,21 +90,13 @@ export class PluvIO<
251
90
  public server<TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>>(
252
91
  config: ServerConfig<TPlatform, TAuthorize, TContext, TEvents> = {},
253
92
  ): PluvServer<TPlatform, TAuthorize, TContext, TEvents> {
254
- const router = (config.router ? this.mergeRouters(this._router, config.router) : this._router) as PluvRouter<
255
- TPlatform,
256
- TAuthorize,
257
- TContext,
258
- TEvents
259
- >;
260
-
261
93
  return new PluvServer<TPlatform, TAuthorize, TContext, TEvents>({
262
- ...this._listeners,
94
+ ...config,
263
95
  authorize: this._authorize ?? undefined,
264
96
  context: this._context,
265
97
  crdt: this._crdt,
266
98
  debug: this._debug,
267
99
  platform: this._platform,
268
- router,
269
100
  });
270
101
  }
271
102
  }
package/src/PluvRouter.ts CHANGED
@@ -6,9 +6,7 @@ export type PluvRouterEventConfig<
6
6
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
7
7
  TAuthorize extends IOAuthorize<any, any, InferRoomContextType<TPlatform>> = BaseIOAuthorize,
8
8
  TContext extends JsonObject = {},
9
- > = {
10
- [P: string]: Pick<PluvProcedure<TPlatform, TAuthorize, TContext, any, any>, "config">;
11
- };
9
+ > = { [P: string]: Pick<PluvProcedure<TPlatform, TAuthorize, TContext, any, any>, "config"> };
12
10
 
13
11
  export type MergedRouter<
14
12
  TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[] = [],
package/src/PluvServer.ts CHANGED
@@ -8,6 +8,7 @@ import type {
8
8
  InferIOAuthorize,
9
9
  InferIOAuthorizeUser,
10
10
  JsonObject,
11
+ Maybe,
11
12
  } from "@pluv/types";
12
13
  import colors from "kleur";
13
14
  import type {
@@ -16,12 +17,15 @@ import type {
16
17
  InferRoomContextType,
17
18
  WebSocketRegistrationMode,
18
19
  } from "./AbstractPlatform";
20
+ import type { IORoomListeners } from "./IORoom";
19
21
  import { IORoom } from "./IORoom";
22
+ import { PluvProcedure } from "./PluvProcedure";
20
23
  import type { PluvRouterEventConfig } from "./PluvRouter";
21
24
  import { PluvRouter } from "./PluvRouter";
22
25
  import type { JWTEncodeParams } from "./authorize";
23
26
  import { authorize } from "./authorize";
24
- import type { IORoomListenerEvent, PluvIOListeners } from "./types";
27
+ import { PING_TIMEOUT_MS } from "./constants";
28
+ import type { GetInitialStorageFn, PluvIOListeners } from "./types";
25
29
  import { __PLUV_VERSION } from "./version";
26
30
 
27
31
  export type InferIORoom<TServer extends PluvServer<any, any, any, any>> =
@@ -34,18 +38,33 @@ export type PluvServerConfig<
34
38
  TAuthorize extends IOAuthorize<any, any, InferPlatformContextType<TPlatform>> = BaseIOAuthorize,
35
39
  TContext extends JsonObject = {},
36
40
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
37
- > = Partial<PluvIOListeners<TPlatform>> & {
41
+ > = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
38
42
  authorize?: TAuthorize;
39
43
  context?: TContext;
40
44
  crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
41
45
  debug?: boolean;
46
+ getInitialStorage?: GetInitialStorageFn<TPlatform>;
42
47
  platform: TPlatform;
43
48
  router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
44
49
  };
45
50
 
46
- export type GetRoomOptions<TPlatform extends AbstractPlatform> = keyof InferRoomContextType<TPlatform> extends never
47
- ? [{ debug?: boolean }] | []
48
- : [Id<{ debug?: boolean } & InferRoomContextType<TPlatform>>];
51
+ type BaseCreateRoomOptions<
52
+ TPlatform extends AbstractPlatform<any>,
53
+ TAuthorize extends IOAuthorize<any, any, InferPlatformContextType<TPlatform>>,
54
+ TContext extends JsonObject,
55
+ TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
56
+ > = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
57
+ debug?: boolean;
58
+ };
59
+
60
+ export type CreateRoomOptions<
61
+ TPlatform extends AbstractPlatform<any>,
62
+ TAuthorize extends IOAuthorize<any, any, InferPlatformContextType<TPlatform>>,
63
+ TContext extends JsonObject,
64
+ TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
65
+ > = keyof InferRoomContextType<TPlatform> extends never
66
+ ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | []
67
+ : [Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>];
49
68
 
50
69
  export class PluvServer<
51
70
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
@@ -54,16 +73,134 @@ export class PluvServer<
54
73
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
55
74
  > implements IORouterLike<TEvents>
56
75
  {
57
- private _rooms = new Map<string, IORoom<TPlatform, TAuthorize, TContext, TEvents>>();
76
+ public _listeners: PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>;
77
+
78
+ public readonly version: string = __PLUV_VERSION as any;
79
+ public readonly _authorize: TAuthorize | null = null;
80
+
81
+ private readonly _baseRouter: PluvRouter<TPlatform, TAuthorize, TContext, {}> = new PluvRouter({
82
+ $GET_OTHERS: this._procedure.sync((data, { room, session, sessions }) => {
83
+ const currentTime = new Date().getTime();
84
+
85
+ const others = sessions
86
+ .filter((wsSession) => {
87
+ if (wsSession.id === session?.id) return false;
88
+ if (wsSession.quit) return false;
89
+ if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;
90
+
91
+ return true;
92
+ })
93
+ .reduce<{
94
+ [connectionId: string]: {
95
+ connectionId: string;
96
+ room: string | null;
97
+ user: JsonObject;
98
+ };
99
+ }>(
100
+ (acc, { id, presence, user }) => ({
101
+ ...acc,
102
+ [id]: {
103
+ connectionId: id,
104
+ presence,
105
+ room,
106
+ user,
107
+ },
108
+ }),
109
+ {},
110
+ );
111
+
112
+ return { $OTHERS_RECEIVED: { others } };
113
+ }),
114
+ $INITIALIZE_SESSION: this._procedure
115
+ .broadcast((data, { session }) => {
116
+ const presence = (data as any)?.presence;
117
+
118
+ if (!session) return {};
119
+
120
+ session.presence = presence;
121
+
122
+ return {
123
+ $USER_JOINED: {
124
+ connectionId: session.id,
125
+ user: session.user,
126
+ presence,
127
+ },
128
+ };
129
+ })
130
+ .self(async (data, { context, doc, room }) => {
131
+ const oldState = await this._platform.persistance.getStorageState(room);
132
+ const update = (data as any)?.update as Maybe<string>;
133
+
134
+ const updates: readonly Maybe<string>[] = [
135
+ oldState ? null : await this._getInitialStorage?.({ ...context, room }),
136
+ update,
137
+ ];
138
+
139
+ if (updates.some((_update) => !!_update)) {
140
+ const encodedState = doc.batchApplyEncodedState(updates).getEncodedState();
141
+
142
+ await this._platform.persistance.setStorageState(room, encodedState);
143
+
144
+ this._listeners.onStorageUpdated({ context, encodedState, room });
145
+ }
146
+
147
+ const state = (await this._platform.persistance.getStorageState(room)) ?? doc.getEncodedState();
148
+
149
+ return { $STORAGE_RECEIVED: { state } };
150
+ }),
151
+ $PING: this._procedure.self((data, { session }) => {
152
+ if (!session) return {};
153
+
154
+ const currentTime = new Date().getTime();
155
+ const prevState = session.webSocket.state;
156
+
157
+ session.webSocket.state = {
158
+ ...prevState,
159
+ timers: {
160
+ ...prevState.timers,
161
+ ping: currentTime,
162
+ },
163
+ };
164
+
165
+ return { $PONG: {} };
166
+ }),
167
+ $UPDATE_PRESENCE: this._procedure.broadcast((data, { session }) => {
168
+ const presence = (data as any)?.presence;
58
169
 
59
- readonly _authorize: TAuthorize | null = null;
60
- readonly _context: TContext = {} as TContext;
61
- readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
62
- readonly _debug: boolean;
63
- readonly _listeners: PluvIOListeners<TPlatform>;
64
- readonly _platform: TPlatform;
65
- readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
66
- readonly _version: string = __PLUV_VERSION as any;
170
+ if (!session) return {};
171
+
172
+ const updated = Object.assign(Object.create(null), session.presence, presence);
173
+
174
+ session.webSocket.presence = updated;
175
+
176
+ return { $PRESENCE_UPDATED: { presence: updated } };
177
+ }),
178
+ $UPDATE_STORAGE: this._procedure.broadcast((data, { context, doc, room }) => {
179
+ const origin = (data as any)?.origin as Maybe<string>;
180
+ const update: string | null = (data as any)?.update ?? null;
181
+
182
+ if (origin === "$INITIALIZED" && Object.keys(doc.toJson()).length) return {};
183
+
184
+ const updated = update === null ? doc : doc.applyEncodedState({ update });
185
+ const encodedState = updated.getEncodedState();
186
+
187
+ this._platform.persistance.setStorageState(room, encodedState).then(() => {
188
+ this._listeners.onStorageUpdated({
189
+ context,
190
+ encodedState,
191
+ room,
192
+ });
193
+ });
194
+
195
+ return { $STORAGE_UPDATED: { state: encodedState } };
196
+ }),
197
+ });
198
+ private readonly _context: TContext = {} as TContext;
199
+ private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
200
+ private readonly _debug: boolean;
201
+ private readonly _getInitialStorage: GetInitialStorageFn<TPlatform> | null = null;
202
+ private readonly _platform: TPlatform;
203
+ private readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
67
204
 
68
205
  public get _events() {
69
206
  return this._router._events;
@@ -73,13 +210,19 @@ export class PluvServer<
73
210
  return this._platform._registrationMode;
74
211
  }
75
212
 
213
+ private get _procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}> {
214
+ return new PluvProcedure();
215
+ }
216
+
76
217
  constructor(options: PluvServerConfig<TPlatform, TAuthorize, TContext, TEvents>) {
77
218
  const {
78
219
  authorize,
79
220
  context,
80
221
  crdt = noop,
81
222
  debug = false,
223
+ getInitialStorage,
82
224
  onRoomDeleted,
225
+ onRoomMessage,
83
226
  onStorageUpdated,
84
227
  platform,
85
228
  router = new PluvRouter<TPlatform, TAuthorize, TContext, TEvents>({} as TEvents),
@@ -88,52 +231,34 @@ export class PluvServer<
88
231
  this._crdt = crdt;
89
232
  this._debug = debug;
90
233
  this._platform = platform;
91
- this._router = router ?? null;
234
+ this._router = (router ? this._baseRouter : PluvRouter.merge(this._baseRouter, router)) as PluvRouter<
235
+ TPlatform,
236
+ TAuthorize,
237
+ TContext,
238
+ TEvents
239
+ >;
92
240
 
93
241
  if (authorize) this._authorize = authorize;
94
242
  if (context) this._context = context;
243
+ if (getInitialStorage) this._getInitialStorage = getInitialStorage;
95
244
 
96
245
  this._listeners = {
97
246
  onRoomDeleted: (event) => onRoomDeleted?.(event),
247
+ onRoomMessage: (event) => onRoomMessage?.(event),
98
248
  onStorageUpdated: (event) => onStorageUpdated?.(event),
99
249
  };
100
250
  }
101
251
 
102
- public async createToken(
103
- params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>,
104
- ): Promise<string> {
105
- if (!this._authorize) {
106
- throw new Error("IO does not specify authorize during initialization.");
107
- }
108
-
109
- const ioAuthorize =
110
- typeof this._authorize === "function" ? this._authorize(params) : (this._authorize as { secret: string });
111
-
112
- const secret = ioAuthorize.secret;
113
-
114
- return await authorize({
115
- platform: this._platform,
116
- secret,
117
- }).encode(params);
118
- }
119
-
120
- public getRoom(
252
+ public createRoom(
121
253
  room: string,
122
- ...options: GetRoomOptions<TPlatform>
254
+ ...options: CreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>
123
255
  ): IORoom<TPlatform, TAuthorize, TContext, TEvents> {
124
- const { debug, ...platformRoomContext } = options[0] ?? {};
125
-
126
- this._purgeEmptyRooms();
256
+ const { debug, onDestroy, onMessage, ...platformRoomContext } = options[0] ?? {};
127
257
 
128
258
  if (!/^[a-z0-9]+[a-z0-9\-_]+[a-z0-9]+$/i.test(room)) {
129
259
  throw new Error("Unsupported room name");
130
260
  }
131
261
 
132
- const platform = this._platform;
133
- const oldRoom = this._rooms.get(room);
134
-
135
- if (oldRoom) return oldRoom;
136
-
137
262
  const roomContext = {
138
263
  ...this._context,
139
264
  ...platformRoomContext,
@@ -144,54 +269,47 @@ export class PluvServer<
144
269
  context: roomContext,
145
270
  crdt: this._crdt,
146
271
  debug: debug ?? this._debug,
147
- onDestroy: async ({ encodedState, room }) => {
272
+ onDestroy: async (event) => {
148
273
  this._logDebug(`${colors.blue("Deleting empty room:")} ${room}`);
149
274
 
150
- const roomContext = {
151
- room,
152
- encodedState,
153
- ...platformRoomContext,
154
- } as IORoomListenerEvent<TPlatform> & InferRoomContextType<TPlatform>;
155
-
156
- this._rooms.delete(room);
157
-
158
- await Promise.resolve(this._listeners.onRoomDeleted(roomContext));
275
+ await Promise.resolve(onDestroy?.(event));
276
+ await Promise.resolve(this._listeners.onRoomDeleted(event));
159
277
  await this._platform.persistance.deleteStorageState(room);
160
278
 
161
- if (this._debug) {
162
- const rooms = Array.from(this._rooms.keys());
163
-
164
- this._logDebug(`${colors.blue("Deleted room:")} ${room}`);
165
- this._logDebug(`${colors.blue("Rooms available:")} ${rooms.join(", ")}`);
166
- }
279
+ this._logDebug(`${colors.blue("Deleted room:")} ${room}`);
167
280
  },
168
- platform,
281
+ onMessage: async (event) => {
282
+ await Promise.resolve(onMessage?.(event));
283
+ await Promise.resolve(this._listeners.onRoomMessage(event));
284
+ },
285
+ platform: this._platform,
169
286
  router: this._router,
170
287
  });
171
288
 
172
- this._rooms.set(room, newRoom);
289
+ this._logDebug(`${colors.blue("Created room:")} ${room}`);
173
290
 
174
- if (this._debug) {
175
- const rooms = Array.from(this._rooms.keys());
291
+ return newRoom;
292
+ }
176
293
 
177
- this._logDebug(`${colors.blue("Created room:")} ${room}`);
178
- this._logDebug(`${colors.blue("Rooms available:")} ${rooms.join(", ")}`);
294
+ public async createToken(
295
+ params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>,
296
+ ): Promise<string> {
297
+ if (!this._authorize) {
298
+ throw new Error("IO does not specify authorize during initialization.");
179
299
  }
180
300
 
181
- return newRoom;
301
+ const ioAuthorize =
302
+ typeof this._authorize === "function" ? this._authorize(params) : (this._authorize as { secret: string });
303
+
304
+ const secret = ioAuthorize.secret;
305
+
306
+ return await authorize({
307
+ platform: this._platform,
308
+ secret,
309
+ }).encode(params);
182
310
  }
183
311
 
184
312
  private _logDebug(...data: any[]): void {
185
313
  this._debug && console.log(...data);
186
314
  }
187
-
188
- private _purgeEmptyRooms(): void {
189
- const rooms = Array.from(this._rooms.entries());
190
-
191
- rooms.forEach(([id, room]) => {
192
- if (!!room?.getSize()) return;
193
-
194
- this._rooms.delete(id);
195
- });
196
- }
197
315
  }
package/src/createIO.ts CHANGED
@@ -2,19 +2,18 @@ import type { AbstractCrdtDocFactory } from "@pluv/crdt";
2
2
  import type { BaseUser, IOAuthorize, JsonObject } from "@pluv/types";
3
3
  import type { AbstractPlatform, InferPlatformContextType } from "./AbstractPlatform";
4
4
  import { PluvIO } from "./PluvIO";
5
- import type { GetInitialStorageFn, PluvIOListeners } from "./types";
5
+ import type { GetInitialStorageFn } from "./types";
6
6
 
7
7
  export type CreateIOParams<
8
8
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
9
9
  TContext extends JsonObject = {},
10
10
  TAuthorizeUser extends BaseUser = BaseUser,
11
11
  TAuthorizeRequired extends boolean = false,
12
- > = Partial<PluvIOListeners<TPlatform>> & {
12
+ > = {
13
13
  authorize?: IOAuthorize<TAuthorizeUser, TAuthorizeRequired, InferPlatformContextType<TPlatform>>;
14
14
  context?: TContext;
15
15
  crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
16
16
  debug?: boolean;
17
- getInitialStorage?: GetInitialStorageFn<TPlatform>;
18
17
  platform: TPlatform;
19
18
  };
20
19
 
@@ -30,7 +29,7 @@ export const createIO = <
30
29
  IOAuthorize<TAuthorizeUser, TAuthorizeRequired, InferPlatformContextType<TPlatform>>,
31
30
  TContext
32
31
  > => {
33
- const { authorize, context, crdt, debug, getInitialStorage, onRoomDeleted, onStorageUpdated, platform } = params;
32
+ const { authorize, context, crdt, debug, platform } = params;
34
33
 
35
34
  return new PluvIO<
36
35
  TPlatform,
@@ -41,9 +40,6 @@ export const createIO = <
41
40
  context,
42
41
  crdt,
43
42
  debug,
44
- getInitialStorage,
45
- onRoomDeleted,
46
- onStorageUpdated,
47
43
  platform,
48
44
  });
49
45
  };