cojson-transport-ws 0.7.18 → 0.7.23
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 +2 -2
- package/.turbo/turbo-lint.log +4 -0
- package/CHANGELOG.md +13 -0
- package/dist/index.js +30 -43
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +86 -75
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> cojson-transport-ws@0.7.
|
|
2
|
+
> cojson-transport-ws@0.7.22 build /Users/anselm/jazz/jazz/packages/cojson-transport-ws
|
|
3
3
|
> npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> cojson-transport-ws@0.7.
|
|
6
|
+
> cojson-transport-ws@0.7.22 lint
|
|
7
7
|
> eslint . --ext ts,tsx
|
|
8
8
|
|
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,59 +1,46 @@
|
|
|
1
1
|
import { DisconnectedError, PingTimeoutError } from "cojson";
|
|
2
|
-
import {
|
|
2
|
+
import { Stream, Queue, Effect, Console } from "effect";
|
|
3
|
+
const g = globalThis;
|
|
3
4
|
export function createWebSocketPeer(options) {
|
|
4
5
|
return Effect.gen(function* () {
|
|
5
6
|
const ws = options.websocket;
|
|
6
|
-
const
|
|
7
|
+
const ws_ = ws;
|
|
7
8
|
const outgoing = yield* Queue.unbounded();
|
|
8
|
-
ws
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const msg = JSON.parse(event.data);
|
|
18
|
-
if (pingTimeout) {
|
|
19
|
-
clearTimeout(pingTimeout);
|
|
20
|
-
}
|
|
21
|
-
pingTimeout = setTimeout(() => {
|
|
22
|
-
console.debug("Ping timeout");
|
|
23
|
-
void Effect.runPromise(Queue.offer(incoming, Either.left(new PingTimeoutError())));
|
|
24
|
-
try {
|
|
25
|
-
ws.close();
|
|
26
|
-
}
|
|
27
|
-
catch (e) {
|
|
28
|
-
console.error("Error while trying to close ws on ping timeout", e);
|
|
29
|
-
}
|
|
30
|
-
}, 2500);
|
|
31
|
-
if (msg.type === "ping") {
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
-
globalThis.jazzPings =
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
globalThis.jazzPings || [];
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
-
globalThis.jazzPings.push({
|
|
9
|
+
const closed = once(ws, "close").pipe(Effect.flatMap((event) => new DisconnectedError({
|
|
10
|
+
message: `${event.code}: ${event.reason}`,
|
|
11
|
+
})), Stream.fromEffect);
|
|
12
|
+
const isSyncMessage = (msg) => {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
if (msg?.type === "ping") {
|
|
15
|
+
const ping = msg;
|
|
16
|
+
g.jazzPings || (g.jazzPings = []);
|
|
17
|
+
g.jazzPings.push({
|
|
38
18
|
received: Date.now(),
|
|
39
|
-
sent:
|
|
40
|
-
dc:
|
|
19
|
+
sent: ping.time,
|
|
20
|
+
dc: ping.dc,
|
|
41
21
|
});
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
void Effect.runPromise(Queue.offer(incoming, Either.right(msg)));
|
|
22
|
+
return false;
|
|
46
23
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
26
|
+
yield* Effect.forkDaemon(Effect.gen(function* () {
|
|
27
|
+
yield* once(ws, "open");
|
|
28
|
+
yield* Queue.take(outgoing).pipe(Effect.andThen((message) => ws.send(JSON.stringify(message))), Effect.forever);
|
|
29
|
+
}));
|
|
30
|
+
const messages = Stream.fromEventListener(ws_, "message").pipe(Stream.timeoutFail(() => new PingTimeoutError(), "10 seconds"), Stream.tapError((_e) => Console.warn("Ping timeout").pipe(Effect.andThen(Effect.try(() => ws.close())), Effect.catchAll((e) => Console.error("Error while trying to close ws on ping timeout", e)))), Stream.mergeLeft(closed), Stream.map((_) => JSON.parse(_.data)), Stream.filter(isSyncMessage), Stream.buffer({ capacity: "unbounded" }), Stream.onDone(() => Queue.shutdown(outgoing)));
|
|
51
31
|
return {
|
|
52
32
|
id: options.id,
|
|
53
|
-
incoming:
|
|
33
|
+
incoming: messages,
|
|
54
34
|
outgoing,
|
|
55
35
|
role: options.role,
|
|
56
36
|
};
|
|
57
37
|
});
|
|
58
38
|
}
|
|
39
|
+
const once = (ws, event) => Effect.async((register) => {
|
|
40
|
+
const cb = (msg) => {
|
|
41
|
+
ws.removeEventListener(event, cb);
|
|
42
|
+
register(Effect.succeed(msg));
|
|
43
|
+
};
|
|
44
|
+
ws.addEventListener(event, cb);
|
|
45
|
+
});
|
|
59
46
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAQ,gBAAgB,EAAe,MAAM,QAAQ,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cojson-transport-ws",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.23",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "src/index.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"effect": "^3.5.2",
|
|
10
10
|
"typescript": "^5.1.6",
|
|
11
|
-
"cojson": "0.7.
|
|
11
|
+
"cojson": "0.7.23"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@types/ws": "^8.5.5"
|
package/src/index.ts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
import { DisconnectedError, Peer, PingTimeoutError, SyncMessage } from "cojson";
|
|
2
|
-
import {
|
|
2
|
+
import { Stream, Queue, Effect, Console } from "effect";
|
|
3
|
+
|
|
4
|
+
interface WebsocketEvents {
|
|
5
|
+
close: { code: number; reason: string };
|
|
6
|
+
message: { data: unknown };
|
|
7
|
+
open: void;
|
|
8
|
+
}
|
|
9
|
+
interface PingMsg {
|
|
10
|
+
time: number;
|
|
11
|
+
dc: string;
|
|
12
|
+
}
|
|
3
13
|
|
|
4
14
|
interface AnyWebSocket {
|
|
5
|
-
addEventListener(
|
|
6
|
-
type:
|
|
7
|
-
listener: (event:
|
|
15
|
+
addEventListener<K extends keyof WebsocketEvents>(
|
|
16
|
+
type: K,
|
|
17
|
+
listener: (event: WebsocketEvents[K]) => void,
|
|
8
18
|
): void;
|
|
9
|
-
|
|
10
|
-
type:
|
|
11
|
-
listener: (event:
|
|
19
|
+
removeEventListener<K extends keyof WebsocketEvents>(
|
|
20
|
+
type: K,
|
|
21
|
+
listener: (event: WebsocketEvents[K]) => void,
|
|
12
22
|
): void;
|
|
13
|
-
addEventListener(type: "open", listener: () => void): void;
|
|
14
23
|
close(): void;
|
|
15
24
|
send(data: string): void;
|
|
16
25
|
}
|
|
17
26
|
|
|
27
|
+
const g: typeof globalThis & {
|
|
28
|
+
jazzPings?: {
|
|
29
|
+
received: number;
|
|
30
|
+
sent: number;
|
|
31
|
+
dc: string;
|
|
32
|
+
}[];
|
|
33
|
+
} = globalThis;
|
|
34
|
+
|
|
18
35
|
export function createWebSocketPeer(options: {
|
|
19
36
|
id: string;
|
|
20
37
|
websocket: AnyWebSocket;
|
|
@@ -22,87 +39,81 @@ export function createWebSocketPeer(options: {
|
|
|
22
39
|
}): Effect.Effect<Peer> {
|
|
23
40
|
return Effect.gen(function* () {
|
|
24
41
|
const ws = options.websocket;
|
|
42
|
+
const ws_ = ws as unknown as Stream.EventListener<WebsocketEvents["message"]>;
|
|
25
43
|
|
|
26
|
-
const incoming =
|
|
27
|
-
yield* Queue.unbounded<
|
|
28
|
-
Either.Either<SyncMessage, DisconnectedError | PingTimeoutError>
|
|
29
|
-
>();
|
|
30
44
|
const outgoing = yield* Queue.unbounded<SyncMessage>();
|
|
31
45
|
|
|
32
|
-
ws
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (Exit.isFailure(e) && !Exit.isInterrupted(e)) {
|
|
42
|
-
console.warn("Failed closing ws", e);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
let pingTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
48
|
-
|
|
49
|
-
ws.addEventListener("message", (event) => {
|
|
50
|
-
const msg = JSON.parse(event.data as string);
|
|
51
|
-
|
|
52
|
-
if (pingTimeout) {
|
|
53
|
-
clearTimeout(pingTimeout);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
pingTimeout = setTimeout(() => {
|
|
57
|
-
console.debug("Ping timeout");
|
|
58
|
-
void Effect.runPromise(
|
|
59
|
-
Queue.offer(incoming, Either.left(new PingTimeoutError())),
|
|
60
|
-
);
|
|
61
|
-
try {
|
|
62
|
-
ws.close();
|
|
63
|
-
} catch (e) {
|
|
64
|
-
console.error(
|
|
65
|
-
"Error while trying to close ws on ping timeout",
|
|
66
|
-
e,
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
}, 2500);
|
|
46
|
+
const closed = once(ws, "close").pipe(
|
|
47
|
+
Effect.flatMap(
|
|
48
|
+
(event) =>
|
|
49
|
+
new DisconnectedError({
|
|
50
|
+
message: `${event.code}: ${event.reason}`,
|
|
51
|
+
}),
|
|
52
|
+
),
|
|
53
|
+
Stream.fromEffect,
|
|
54
|
+
);
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
(globalThis as any).jazzPings.push({
|
|
56
|
+
const isSyncMessage = (msg: unknown): msg is SyncMessage => {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
if ((msg as any)?.type === "ping") {
|
|
59
|
+
const ping = msg as PingMsg;
|
|
60
|
+
g.jazzPings ||= [];
|
|
61
|
+
g.jazzPings.push({
|
|
78
62
|
received: Date.now(),
|
|
79
|
-
sent:
|
|
80
|
-
dc:
|
|
63
|
+
sent: ping.time,
|
|
64
|
+
dc: ping.dc,
|
|
81
65
|
});
|
|
82
|
-
return;
|
|
83
|
-
} else {
|
|
84
|
-
void Effect.runPromise(
|
|
85
|
-
Queue.offer(incoming, Either.right(msg)),
|
|
86
|
-
);
|
|
66
|
+
return false;
|
|
87
67
|
}
|
|
88
|
-
|
|
68
|
+
return true;
|
|
69
|
+
};
|
|
89
70
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
Effect.runPromise,
|
|
71
|
+
yield* Effect.forkDaemon(Effect.gen(function* () {
|
|
72
|
+
yield* once(ws, "open");
|
|
73
|
+
yield* Queue.take(outgoing).pipe(
|
|
74
|
+
Effect.andThen((message) => ws.send(JSON.stringify(message))),
|
|
75
|
+
Effect.forever,
|
|
96
76
|
);
|
|
97
|
-
});
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
type E = WebsocketEvents["message"];
|
|
80
|
+
const messages = Stream.fromEventListener<E>(ws_, "message").pipe(
|
|
81
|
+
Stream.timeoutFail(() => new PingTimeoutError(), "10 seconds"),
|
|
82
|
+
Stream.tapError((_e) =>
|
|
83
|
+
Console.warn("Ping timeout").pipe(
|
|
84
|
+
Effect.andThen(Effect.try(() => ws.close())),
|
|
85
|
+
Effect.catchAll((e) =>
|
|
86
|
+
Console.error(
|
|
87
|
+
"Error while trying to close ws on ping timeout",
|
|
88
|
+
e,
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
Stream.mergeLeft(closed),
|
|
94
|
+
Stream.map((_) => JSON.parse(_.data as string)),
|
|
95
|
+
Stream.filter(isSyncMessage),
|
|
96
|
+
Stream.buffer({ capacity: "unbounded" }),
|
|
97
|
+
Stream.onDone(() => Queue.shutdown(outgoing)),
|
|
98
|
+
);
|
|
98
99
|
|
|
99
100
|
return {
|
|
100
101
|
id: options.id,
|
|
101
|
-
incoming:
|
|
102
|
-
Stream.mapEffect((either) => either),
|
|
103
|
-
),
|
|
102
|
+
incoming: messages,
|
|
104
103
|
outgoing,
|
|
105
104
|
role: options.role,
|
|
106
105
|
};
|
|
107
106
|
});
|
|
108
107
|
}
|
|
108
|
+
|
|
109
|
+
const once = <Event extends keyof WebsocketEvents>(
|
|
110
|
+
ws: AnyWebSocket,
|
|
111
|
+
event: Event,
|
|
112
|
+
) =>
|
|
113
|
+
Effect.async<WebsocketEvents[Event]>((register) => {
|
|
114
|
+
const cb = (msg: WebsocketEvents[Event]) => {
|
|
115
|
+
ws.removeEventListener(event, cb);
|
|
116
|
+
register(Effect.succeed(msg));
|
|
117
|
+
};
|
|
118
|
+
ws.addEventListener(event, cb);
|
|
119
|
+
});
|