@pluv/io 0.0.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/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +17 -0
- package/dist/index.d.ts +257 -0
- package/dist/index.js +891 -0
- package/dist/index.mjs +857 -0
- package/package.json +34 -0
- package/src/AbstractPersistance.ts +31 -0
- package/src/AbstractPlatform.ts +40 -0
- package/src/AbstractPubSub.ts +23 -0
- package/src/AbstractWebSocket.ts +117 -0
- package/src/IORoom.ts +630 -0
- package/src/Persistance.ts +77 -0
- package/src/PluvIO.ts +322 -0
- package/src/PubSub.ts +79 -0
- package/src/authorize.ts +81 -0
- package/src/createIO.ts +51 -0
- package/src/index.ts +34 -0
- package/src/types.ts +93 -0
- package/tsconfig.json +5 -0
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pluv/io",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"module": "./dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@panva/hkdf": "^1.0.2",
|
|
12
|
+
"@pluv/crdt-yjs": "^0.0.0",
|
|
13
|
+
"@pluv/types": "^0.0.0",
|
|
14
|
+
"chalk": "^4.1.2",
|
|
15
|
+
"jose": "^4.11.2",
|
|
16
|
+
"wonka": "^6.1.2"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"yjs": "^13.0.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@pluv/tsconfig": "^0.0.0",
|
|
23
|
+
"eslint-config-pluv": "^0.0.0",
|
|
24
|
+
"tsup": "^6.5.0",
|
|
25
|
+
"typescript": "^4.9.4",
|
|
26
|
+
"yjs": "^13.5.44"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
30
|
+
"dev": "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
31
|
+
"lint": "eslint src/**/*.ts* --fix",
|
|
32
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { JsonObject } from "@pluv/types";
|
|
2
|
+
|
|
3
|
+
export abstract class AbstractPersistance {
|
|
4
|
+
public abstract addUser(
|
|
5
|
+
room: string,
|
|
6
|
+
connectionId: string,
|
|
7
|
+
user: JsonObject | null
|
|
8
|
+
): Promise<void>;
|
|
9
|
+
|
|
10
|
+
public abstract deleteStorageState(room: string): Promise<void>;
|
|
11
|
+
|
|
12
|
+
public abstract deleteUser(
|
|
13
|
+
room: string,
|
|
14
|
+
connectionId: string
|
|
15
|
+
): Promise<void>;
|
|
16
|
+
|
|
17
|
+
public abstract deleteUsers(room: string): Promise<void>;
|
|
18
|
+
|
|
19
|
+
public abstract getStorageState(room: string): Promise<string | null>;
|
|
20
|
+
|
|
21
|
+
public abstract getUser(
|
|
22
|
+
room: string,
|
|
23
|
+
connectionId: string
|
|
24
|
+
): Promise<JsonObject | null>;
|
|
25
|
+
|
|
26
|
+
public abstract getUsers(
|
|
27
|
+
room: string
|
|
28
|
+
): Promise<readonly (JsonObject | null)[]>;
|
|
29
|
+
|
|
30
|
+
public abstract setStorageState(room: string, state: string): Promise<void>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AbstractPersistance } from "./AbstractPersistance";
|
|
2
|
+
import type { AbstractPubSub } from "./AbstractPubSub";
|
|
3
|
+
import type { AbstractWebSocket } from "./AbstractWebSocket";
|
|
4
|
+
import { Persistance } from "./Persistance";
|
|
5
|
+
import { PubSub } from "./PubSub";
|
|
6
|
+
|
|
7
|
+
export type InferWebSocketType<TPlatform extends AbstractPlatform<any>> =
|
|
8
|
+
TPlatform extends AbstractPlatform<infer IWebSocket> ? IWebSocket : never;
|
|
9
|
+
|
|
10
|
+
export type AbstractPlatformConfig =
|
|
11
|
+
| { persistance?: undefined; pubSub?: undefined }
|
|
12
|
+
| {
|
|
13
|
+
persistance: AbstractPersistance;
|
|
14
|
+
pubSub: AbstractPubSub;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface ConvertWebSocketConfig {
|
|
18
|
+
room: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export abstract class AbstractPlatform<TWebSocket = any> {
|
|
22
|
+
public persistance: AbstractPersistance;
|
|
23
|
+
public pubSub: AbstractPubSub;
|
|
24
|
+
|
|
25
|
+
constructor(config: AbstractPlatformConfig = {}) {
|
|
26
|
+
const { persistance, pubSub } = config;
|
|
27
|
+
|
|
28
|
+
this.persistance = persistance ?? new Persistance();
|
|
29
|
+
this.pubSub = pubSub ?? new PubSub();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public abstract convertWebSocket(
|
|
33
|
+
webSocket: TWebSocket,
|
|
34
|
+
config: ConvertWebSocketConfig
|
|
35
|
+
): AbstractWebSocket;
|
|
36
|
+
|
|
37
|
+
public abstract parseData(data: string | ArrayBuffer): Record<string, any>;
|
|
38
|
+
|
|
39
|
+
public abstract randomUUID(): string;
|
|
40
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IOEventMessage, IOLike, InferIOOutput } from "@pluv/types";
|
|
2
|
+
import type { SendMessageOptions } from "./types";
|
|
3
|
+
|
|
4
|
+
export type IOPubSubEventMessage<
|
|
5
|
+
TIO extends IOLike,
|
|
6
|
+
TEvent extends keyof InferIOOutput<TIO> = keyof InferIOOutput<TIO>
|
|
7
|
+
> = IOEventMessage<TIO, TEvent> & {
|
|
8
|
+
options?: SendMessageOptions;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export abstract class AbstractPubSub {
|
|
12
|
+
public abstract publish(
|
|
13
|
+
roomName: string,
|
|
14
|
+
payload: IOPubSubEventMessage<any>
|
|
15
|
+
): Promise<void>;
|
|
16
|
+
|
|
17
|
+
public abstract subscribe(
|
|
18
|
+
roomName: string,
|
|
19
|
+
onMessage: (message: IOPubSubEventMessage<any>) => void
|
|
20
|
+
): Promise<number>;
|
|
21
|
+
|
|
22
|
+
public abstract unsubscribe(subscriptionId: number): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { IOEventMessage, MaybePromise } from "@pluv/types";
|
|
2
|
+
import type { AbstractPubSub } from "./AbstractPubSub";
|
|
3
|
+
import type { WebSocketSession } from "./types";
|
|
4
|
+
|
|
5
|
+
export type AbstractEvent = {};
|
|
6
|
+
|
|
7
|
+
export type AbstractCloseEvent = AbstractEvent & {
|
|
8
|
+
readonly code: number;
|
|
9
|
+
readonly reason: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type AbstractMessageEvent<T = any> = AbstractEvent & {
|
|
13
|
+
readonly data: T;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type AbstractErrorEvent = AbstractEvent & {
|
|
17
|
+
error: unknown;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface AbstractEventMap {
|
|
22
|
+
close: AbstractCloseEvent;
|
|
23
|
+
message: AbstractMessageEvent;
|
|
24
|
+
error: AbstractErrorEvent;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type EventType = keyof AbstractEventMap;
|
|
28
|
+
|
|
29
|
+
export interface AbstractWebSocketHandleErrorParams {
|
|
30
|
+
error: unknown;
|
|
31
|
+
message?: string;
|
|
32
|
+
session?: WebSocketSession;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type AbstractListener<TType extends keyof AbstractEventMap> = (
|
|
36
|
+
event: AbstractEventMap[TType]
|
|
37
|
+
) => MaybePromise<void>;
|
|
38
|
+
|
|
39
|
+
export interface AbstractWebSocketConfig {
|
|
40
|
+
pubSub?: AbstractPubSub;
|
|
41
|
+
room: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export abstract class AbstractWebSocket {
|
|
45
|
+
/** The connection is not yet open. */
|
|
46
|
+
public readonly CONNECTING = 0;
|
|
47
|
+
/** The connection is open and ready to communicate. */
|
|
48
|
+
public readonly OPEN = 1;
|
|
49
|
+
/** The connection is in the process of closing. */
|
|
50
|
+
public readonly CLOSING = 2;
|
|
51
|
+
/** The connection is closed. */
|
|
52
|
+
public readonly CLOSED = 3;
|
|
53
|
+
|
|
54
|
+
public pubSub?: AbstractPubSub;
|
|
55
|
+
public room: string;
|
|
56
|
+
|
|
57
|
+
public abstract get readyState(): 0 | 1 | 2 | 3;
|
|
58
|
+
|
|
59
|
+
constructor(config: AbstractWebSocketConfig) {
|
|
60
|
+
const { room } = config;
|
|
61
|
+
|
|
62
|
+
this.room = room;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* TODO
|
|
67
|
+
* @description Return an unsubscribe method to remove the event-listener.
|
|
68
|
+
* This is preferred over adding an explicit abstract removeEventListener
|
|
69
|
+
* method, because the implementing class might have to transform the
|
|
70
|
+
* listener, and removing the same listener might be complicated as a
|
|
71
|
+
* result.
|
|
72
|
+
* @author leedavidcs
|
|
73
|
+
* @date August 28, 2022
|
|
74
|
+
*/
|
|
75
|
+
public abstract addEventListener<TType extends EventType>(
|
|
76
|
+
type: TType,
|
|
77
|
+
handler: AbstractListener<TType>
|
|
78
|
+
): void;
|
|
79
|
+
|
|
80
|
+
public abstract close(
|
|
81
|
+
code?: number | undefined,
|
|
82
|
+
reason?: string | undefined
|
|
83
|
+
): void;
|
|
84
|
+
|
|
85
|
+
public abstract initialize(): Promise<() => void>;
|
|
86
|
+
|
|
87
|
+
public abstract send(message: ArrayBuffer | ArrayBufferView | string): void;
|
|
88
|
+
|
|
89
|
+
public abstract terminate(): void;
|
|
90
|
+
|
|
91
|
+
public handleError(params: AbstractWebSocketHandleErrorParams): void {
|
|
92
|
+
const {
|
|
93
|
+
error,
|
|
94
|
+
message = error instanceof Error
|
|
95
|
+
? error.message
|
|
96
|
+
: "Unexpected error",
|
|
97
|
+
session,
|
|
98
|
+
} = params;
|
|
99
|
+
|
|
100
|
+
const stack: string | null =
|
|
101
|
+
error instanceof Error ? error.stack ?? null : null;
|
|
102
|
+
|
|
103
|
+
return this.sendMessage({
|
|
104
|
+
connectionId: session?.id ?? null,
|
|
105
|
+
data: { message, stack },
|
|
106
|
+
room: this.room,
|
|
107
|
+
type: "$ERROR",
|
|
108
|
+
user: session?.user ?? null,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public sendMessage<
|
|
113
|
+
TMessage extends IOEventMessage<any> = IOEventMessage<any>
|
|
114
|
+
>(data: TMessage): void {
|
|
115
|
+
return this.send(JSON.stringify(data));
|
|
116
|
+
}
|
|
117
|
+
}
|