@pluv/io 0.19.0 → 0.21.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/io",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "@pluv/io server-side websocket manager",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -17,20 +17,20 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@panva/hkdf": "^1.2.0",
21
- "jose": "^5.6.2",
20
+ "@panva/hkdf": "^1.2.1",
21
+ "jose": "^5.6.3",
22
22
  "kleur": "^4.1.5",
23
23
  "wonka": "^6.3.4",
24
- "@pluv/crdt": "^0.19.0",
25
- "@pluv/types": "^0.19.0"
24
+ "@pluv/crdt": "^0.21.0",
25
+ "@pluv/types": "^0.21.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "eslint": "^8.57.0",
29
29
  "genversion": "^3.2.0",
30
- "tsup": "^8.1.0",
31
- "typescript": "^5.4.5",
32
- "@pluv/tsconfig": "^0.19.0",
33
- "eslint-config-pluv": "^0.19.0"
30
+ "tsup": "^8.2.4",
31
+ "typescript": "^5.5.4",
32
+ "@pluv/tsconfig": "^0.21.0",
33
+ "eslint-config-pluv": "^0.21.0"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
@@ -1,48 +1,92 @@
1
- import { AbstractPersistance } from "./AbstractPersistance";
1
+ import type { AbstractPersistance } from "./AbstractPersistance";
2
2
  import type { AbstractPubSub } from "./AbstractPubSub";
3
- import type { AbstractWebSocket, AbstractWebSocketConfig } from "./AbstractWebSocket";
3
+ import type { AbstractWebSocket, InferWebSocketSource } from "./AbstractWebSocket";
4
4
  import { Persistance } from "./Persistance";
5
5
  import { PubSub } from "./PubSub";
6
+ import type { WebSocketSerializedState } from "./types";
7
+
8
+ export type WebSocketRegistrationMode = "attached" | "detached";
6
9
 
7
10
  export type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> =
8
- TPlatform extends AbstractPlatform<infer IWebSocket> ? IWebSocket : never;
11
+ TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;
9
12
 
10
- export type InferPlatformRoomContextType<TPlatform extends AbstractPlatform> =
11
- TPlatform extends AbstractPlatform<any, infer IIOContext> ? IIOContext : never;
13
+ export type InferPlatformWebSocketSource<TPlatform> =
14
+ TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? InferWebSocketSource<IAbstractWebSocket> : never;
12
15
 
13
- export type InferPlatformEventContextType<TPlatform extends AbstractPlatform> =
14
- TPlatform extends AbstractPlatform<any, any, infer IRoomContext> ? IRoomContext : never;
16
+ export type InferPlatformContextType<TPlatform extends AbstractPlatform> =
17
+ TPlatform extends AbstractPlatform<any, infer IPlatformContext> ? IPlatformContext : never;
15
18
 
16
- export type AbstractPlatformConfig =
17
- | { persistance?: undefined; pubSub?: undefined }
18
- | {
19
- persistance: AbstractPersistance;
20
- pubSub: AbstractPubSub;
21
- };
19
+ export type InferRoomContextType<TPlatform extends AbstractPlatform> =
20
+ TPlatform extends AbstractPlatform<any, infer IPlatformContext, infer IRoomContext>
21
+ ? IPlatformContext & IRoomContext
22
+ : never;
22
23
 
23
- export type ConvertWebSocketConfig = AbstractWebSocketConfig;
24
+ export type AbstractPlatformConfig<
25
+ TPlatformContext extends Record<string, any> = {},
26
+ TRoomContext extends Record<string, any> = {},
27
+ > = {
28
+ context?: TPlatformContext & TRoomContext;
29
+ persistance?: AbstractPersistance;
30
+ pubSub?: AbstractPubSub;
31
+ };
32
+
33
+ export interface ConvertWebSocketConfig {
34
+ room: string;
35
+ }
24
36
 
25
37
  export abstract class AbstractPlatform<
26
- TWebSocket = any,
27
- TIOContext extends Record<string, any> = {},
38
+ TWebSocket extends AbstractWebSocket = AbstractWebSocket,
39
+ TPlatformContext extends Record<string, any> = {},
28
40
  TRoomContext extends Record<string, any> = {},
29
41
  > {
30
- readonly _ioContext: TIOContext | undefined;
42
+ private _initialized: boolean = false;
43
+
44
+ readonly _ioContext: TPlatformContext | undefined;
31
45
  readonly _roomContext: TRoomContext | undefined;
32
46
 
47
+ abstract readonly _registrationMode: WebSocketRegistrationMode;
48
+
33
49
  public persistance: AbstractPersistance;
34
50
  public pubSub: AbstractPubSub;
35
51
 
36
- constructor(config: AbstractPlatformConfig = {}) {
37
- const { persistance, pubSub } = config;
52
+ constructor(config: AbstractPlatformConfig<TPlatformContext, TRoomContext> = {}) {
53
+ const { context, persistance, pubSub } = config;
54
+
55
+ context && (this._ioContext = context);
56
+ context && (this._roomContext = context);
38
57
 
39
58
  this.persistance = persistance ?? new Persistance();
40
59
  this.pubSub = pubSub ?? new PubSub();
41
60
  }
42
61
 
43
- public abstract convertWebSocket(webSocket: TWebSocket, config: ConvertWebSocketConfig): AbstractWebSocket;
62
+ public abstract acceptWebSocket(webSocket: AbstractWebSocket): Promise<void>;
63
+
64
+ public abstract convertWebSocket(
65
+ webSocket: InferWebSocketSource<TWebSocket>,
66
+ config: ConvertWebSocketConfig,
67
+ ): AbstractWebSocket;
68
+
69
+ public abstract getLastPing(webSocket: AbstractWebSocket): number | null;
70
+
71
+ public abstract getSerializedState(webSocket: AbstractWebSocket): WebSocketSerializedState | null;
72
+
73
+ public abstract getSessionId(webSocket: InferWebSocketSource<TWebSocket>): string | null;
74
+
75
+ public abstract getWebSockets(): readonly InferWebSocketSource<TWebSocket>[];
76
+
77
+ public abstract initialize(config: AbstractPlatformConfig<TPlatformContext, TRoomContext>): this;
44
78
 
45
79
  public abstract parseData(data: string | ArrayBuffer): Record<string, any>;
46
80
 
47
81
  public abstract randomUUID(): string;
82
+
83
+ public abstract setSerializedState(webSocket: AbstractWebSocket, state: WebSocketSerializedState): void;
84
+
85
+ protected _initialize(): this {
86
+ if (this._initialized) throw new Error("Platform is already initialized");
87
+
88
+ this._initialized = true;
89
+
90
+ return this;
91
+ }
48
92
  }
@@ -1,5 +1,13 @@
1
- import type { IOEventMessage, MaybePromise } from "@pluv/types";
2
- import type { WebSocketSession } from "./types";
1
+ import type {
2
+ BaseUser,
3
+ InferIOAuthorizeUser,
4
+ IOAuthorize,
5
+ IOEventMessage,
6
+ JsonObject,
7
+ MaybePromise,
8
+ } from "@pluv/types";
9
+ import type { AbstractPersistance } from "./AbstractPersistance";
10
+ import type { WebSocketSerializedState, WebSocketSession } from "./types";
3
11
 
4
12
  export type AbstractEvent = {};
5
13
 
@@ -14,7 +22,7 @@ export type AbstractMessageEvent<T = any> = AbstractEvent & {
14
22
 
15
23
  export type AbstractErrorEvent = AbstractEvent & {
16
24
  error: unknown;
17
- message: string;
25
+ message?: string;
18
26
  };
19
27
 
20
28
  export interface AbstractEventMap {
@@ -28,6 +36,7 @@ export type EventType = keyof AbstractEventMap;
28
36
  export interface AbstractWebSocketHandleErrorParams {
29
37
  error: unknown;
30
38
  message?: string;
39
+ room: string;
31
40
  session?: WebSocketSession<any>;
32
41
  }
33
42
 
@@ -35,13 +44,15 @@ export type AbstractListener<TType extends keyof AbstractEventMap> = (
35
44
  event: AbstractEventMap[TType],
36
45
  ) => MaybePromise<void>;
37
46
 
47
+ export type InferWebSocketSource<TAbstractWebSocket extends AbstractWebSocket> =
48
+ TAbstractWebSocket extends AbstractWebSocket<infer IWebSocket> ? IWebSocket : never;
49
+
38
50
  export interface AbstractWebSocketConfig {
51
+ persistance: AbstractPersistance;
39
52
  room: string;
40
- sessionId: string;
41
- userId: string | null;
42
53
  }
43
54
 
44
- export abstract class AbstractWebSocket {
55
+ export abstract class AbstractWebSocket<TWebSocket = any> {
45
56
  /** The connection is not yet open. */
46
57
  public readonly CONNECTING = 0;
47
58
  /** The connection is open and ready to communicate. */
@@ -51,16 +62,23 @@ export abstract class AbstractWebSocket {
51
62
  /** The connection is closed. */
52
63
  public readonly CLOSED = 3;
53
64
 
54
- public room: string;
55
- public sessionId: string;
65
+ public readonly persistance: AbstractPersistance;
66
+ public readonly room: string;
67
+ public readonly webSocket: TWebSocket;
56
68
 
69
+ private _user: BaseUser | null = null;
70
+
71
+ public abstract set presence(presence: JsonObject | null);
57
72
  public abstract get readyState(): 0 | 1 | 2 | 3;
73
+ public abstract get sessionId(): string;
74
+ public abstract get state(): WebSocketSerializedState;
58
75
 
59
- constructor(config: AbstractWebSocketConfig) {
60
- const { room, sessionId } = config;
76
+ constructor(webSocket: TWebSocket, config: AbstractWebSocketConfig) {
77
+ const { persistance, room } = config;
61
78
 
79
+ this.persistance = persistance;
62
80
  this.room = room;
63
- this.sessionId = sessionId;
81
+ this.webSocket = webSocket;
64
82
  }
65
83
 
66
84
  /**
@@ -77,21 +95,37 @@ export abstract class AbstractWebSocket {
77
95
 
78
96
  public abstract close(code?: number | undefined, reason?: string | undefined): void;
79
97
 
80
- public abstract initialize(): Promise<() => void>;
81
-
82
98
  public abstract send(message: ArrayBuffer | ArrayBufferView | string): void;
83
99
 
84
100
  public abstract terminate(): void;
85
101
 
102
+ public async getSession<TAuthorize extends IOAuthorize<any, any, any>>(): Promise<WebSocketSession<TAuthorize>> {
103
+ const sessionId = this.sessionId;
104
+ const state = this.state;
105
+ const room = state.room;
106
+
107
+ const user = this._user ?? (await this.persistance.getUser(room, sessionId));
108
+
109
+ this._user = user as BaseUser;
110
+
111
+ return {
112
+ ...state,
113
+ id: sessionId,
114
+ user: user as InferIOAuthorizeUser<TAuthorize>,
115
+ webSocket: this,
116
+ };
117
+ }
118
+
86
119
  public handleError(params: AbstractWebSocketHandleErrorParams): void {
87
- const { error, message = error instanceof Error ? error.message : "Unexpected error", session } = params;
120
+ const { error, message: _message, room, session } = params;
88
121
 
89
- const stack: string | null = error instanceof Error ? error.stack ?? null : null;
122
+ const message = error instanceof Error ? error.message : "Unexpected error";
123
+ const stack: string | null = error instanceof Error ? (error.stack ?? null) : null;
90
124
 
91
125
  return this.sendMessage({
92
126
  connectionId: session?.id ?? null,
93
127
  data: { message, stack },
94
- room: this.room,
128
+ room,
95
129
  type: "$ERROR",
96
130
  user: session?.user ?? null,
97
131
  });