@hyperdrive.bot/fleet-client 0.3.218 → 0.3.221
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/README.md +39 -7
- package/dist/compat/normalize-provider-models.d.ts +2 -2
- package/dist/compat/normalize-provider-models.js +13 -4
- package/dist/connection/index.d.ts +50 -0
- package/dist/connection/index.js +119 -0
- package/dist/connection/legacy-workspaces.d.ts +19 -0
- package/dist/connection/legacy-workspaces.js +160 -0
- package/dist/connection/legacy.d.ts +31 -0
- package/dist/connection/legacy.js +245 -0
- package/dist/connection/owned.d.ts +36 -0
- package/dist/connection/owned.js +210 -0
- package/dist/creation/index.d.ts +40 -0
- package/dist/creation/index.js +197 -0
- package/dist/daemon-client-relay-e2ee-transport.d.ts +1 -0
- package/dist/daemon-client-relay-e2ee-transport.js +10 -6
- package/dist/daemon-client-transport-types.d.ts +1 -1
- package/dist/daemon-client-transport-utils.d.ts +5 -1
- package/dist/daemon-client-transport-utils.js +8 -6
- package/dist/daemon-client-transport.d.ts +2 -2
- package/dist/daemon-client-transport.js +2 -2
- package/dist/daemon-client-websocket-transport.d.ts +4 -0
- package/dist/daemon-client-websocket-transport.js +23 -3
- package/dist/daemon-client.d.ts +1372 -925
- package/dist/daemon-client.js +4073 -2691
- package/dist/index.d.ts +197 -66
- package/dist/index.js +435 -72
- package/dist/legacy-usage-icons.d.ts +2 -0
- package/dist/legacy-usage-icons.js +16 -0
- package/dist/provider-snapshots/index.d.ts +21 -0
- package/dist/provider-snapshots/index.js +56 -0
- package/dist/terminal-stream-router.d.ts +7 -10
- package/dist/terminal-stream-router.js +41 -87
- package/dist/terminals/index.d.ts +69 -0
- package/dist/terminals/index.js +103 -0
- package/dist/timeline-subscription/index.d.ts +25 -0
- package/dist/timeline-subscription/index.js +56 -0
- package/package.json +10 -6
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/** Client-owned observation and compatibility. UI callbacks never advance creation. */
|
|
2
|
+
export class CreationClient {
|
|
3
|
+
constructor(deps) {
|
|
4
|
+
this.deps = deps;
|
|
5
|
+
this.operations = new Map();
|
|
6
|
+
}
|
|
7
|
+
createAgent(input) {
|
|
8
|
+
const { onEvent, ...request } = input;
|
|
9
|
+
return this.start("agent", request, onEvent, () => this.legacyAgent(request));
|
|
10
|
+
}
|
|
11
|
+
createWorkspace(input) {
|
|
12
|
+
const { onEvent, ...request } = input;
|
|
13
|
+
return this.start("workspace", request, onEvent, async (operation) => {
|
|
14
|
+
if (request.workspaceId || request.agent?.agentId)
|
|
15
|
+
throw new Error("Update the host to use caller-selected creation IDs.");
|
|
16
|
+
const { agent, ...workspaceInput } = request;
|
|
17
|
+
const sourceCwd = request.source.kind === "directory" ? request.source.path : request.source.cwd;
|
|
18
|
+
const relativeCwd = agent && sourceCwd ? relativeDirectory(agent.config.cwd, sourceCwd) : undefined;
|
|
19
|
+
const workspace = await this.deps.legacyWorkspace(workspaceInput);
|
|
20
|
+
if (workspace.error || !workspace.workspace)
|
|
21
|
+
return workspace;
|
|
22
|
+
this.receive({
|
|
23
|
+
kind: "workspace",
|
|
24
|
+
idempotencyKey: operation.key,
|
|
25
|
+
revision: 1,
|
|
26
|
+
phase: "workspace_ready",
|
|
27
|
+
workspaceId: workspace.workspace.id,
|
|
28
|
+
agentId: null,
|
|
29
|
+
workspace: workspace.workspace,
|
|
30
|
+
setupSkippedReason: workspace.setupSkippedReason,
|
|
31
|
+
error: null,
|
|
32
|
+
});
|
|
33
|
+
if (!agent)
|
|
34
|
+
return workspace;
|
|
35
|
+
const cwd = `${workspace.workspace.workspaceDirectory.replace(/[\\/]+$/, "")}${relativeCwd ?? relativeDirectory(agent.config.cwd, workspace.workspace.projectRootPath)}`;
|
|
36
|
+
const created = await this.legacyAgent({
|
|
37
|
+
...agent,
|
|
38
|
+
config: { ...agent.config, cwd },
|
|
39
|
+
workspaceId: workspace.workspace.id,
|
|
40
|
+
idempotencyKey: `${operation.key}:agent`,
|
|
41
|
+
});
|
|
42
|
+
return { ...workspace, agent: created.agent };
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async legacyAgent(input) {
|
|
46
|
+
if (input.agentId)
|
|
47
|
+
throw new Error("Update the host to use caller-selected creation IDs.");
|
|
48
|
+
const { idempotencyKey: _key, ...unkeyed } = input;
|
|
49
|
+
const hasPrompt = Boolean(input.initialPrompt || input.images?.length || input.attachments?.length);
|
|
50
|
+
// COMPAT(creationLifecycle): added in v0.8.0, remove after 2027-03-11 once the daemon floor supports creationLifecycle.
|
|
51
|
+
// Preserve the original create-and-prompt RPC: old hosts use its prompt for
|
|
52
|
+
// titles, creation context and startup. Their keyed create cannot accept it.
|
|
53
|
+
const request = !hasPrompt && this.deps.supports("agentRequestReceipts") ? input : unkeyed;
|
|
54
|
+
return { agent: await this.deps.legacyAgent(request), error: null };
|
|
55
|
+
}
|
|
56
|
+
start(kind, input, observer, legacy) {
|
|
57
|
+
const key = typeof input.idempotencyKey === "string" ? input.idempotencyKey : this.deps.requestId();
|
|
58
|
+
const identity = JSON.stringify([kind, key]);
|
|
59
|
+
const { requestId: _requestId, ...intent } = input;
|
|
60
|
+
const fingerprint = JSON.stringify(intent, (_key, value) => value && typeof value === "object" && !Array.isArray(value)
|
|
61
|
+
? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)))
|
|
62
|
+
: value);
|
|
63
|
+
const previous = this.operations.get(identity);
|
|
64
|
+
if (previous) {
|
|
65
|
+
if (previous.fingerprint !== fingerprint)
|
|
66
|
+
return Promise.reject(new Error(`${kind}_request_key_conflict`));
|
|
67
|
+
if (observer) {
|
|
68
|
+
previous.observers.add(observer);
|
|
69
|
+
if (previous.snapshot)
|
|
70
|
+
this.notify(observer, previous.snapshot);
|
|
71
|
+
}
|
|
72
|
+
return previous.result;
|
|
73
|
+
}
|
|
74
|
+
let resolve;
|
|
75
|
+
let reject;
|
|
76
|
+
const result = new Promise((done, fail) => {
|
|
77
|
+
resolve = done;
|
|
78
|
+
reject = fail;
|
|
79
|
+
});
|
|
80
|
+
const operation = {
|
|
81
|
+
kind,
|
|
82
|
+
key,
|
|
83
|
+
input: { ...input, idempotencyKey: key },
|
|
84
|
+
fingerprint,
|
|
85
|
+
observers: new Set(observer ? [observer] : []),
|
|
86
|
+
result,
|
|
87
|
+
resolve,
|
|
88
|
+
reject,
|
|
89
|
+
settled: false,
|
|
90
|
+
modern: this.deps.supports("creationLifecycle"),
|
|
91
|
+
recovering: false,
|
|
92
|
+
};
|
|
93
|
+
this.operations.set(identity, operation);
|
|
94
|
+
if (operation.modern)
|
|
95
|
+
void this.submit(operation);
|
|
96
|
+
else
|
|
97
|
+
void legacy(operation).then((value) => this.finish(operation, value), (error) => this.fail(operation, error));
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
async submit(operation) {
|
|
101
|
+
try {
|
|
102
|
+
const result = await this.deps.request(operation.kind, {
|
|
103
|
+
...operation.input,
|
|
104
|
+
subscribe: true,
|
|
105
|
+
});
|
|
106
|
+
if (result.creation)
|
|
107
|
+
this.receive(result.creation);
|
|
108
|
+
this.finish(operation, result);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (operation.settled ||
|
|
112
|
+
(error instanceof Error && "code" in error && error.code === "DAEMON_CONNECTION_LOST"))
|
|
113
|
+
return;
|
|
114
|
+
this.fail(operation, error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
receive(snapshot) {
|
|
118
|
+
const operation = this.operations.get(JSON.stringify([snapshot.kind, snapshot.idempotencyKey]));
|
|
119
|
+
if (!operation || (operation.snapshot && operation.snapshot.revision > snapshot.revision))
|
|
120
|
+
return;
|
|
121
|
+
if (operation.snapshot?.revision !== snapshot.revision) {
|
|
122
|
+
operation.snapshot = snapshot;
|
|
123
|
+
for (const observer of operation.observers)
|
|
124
|
+
this.notify(observer, snapshot);
|
|
125
|
+
}
|
|
126
|
+
if (operation.recovering && (snapshot.phase === "completed" || snapshot.phase === "failed")) {
|
|
127
|
+
this.finish(operation, {
|
|
128
|
+
agent: snapshot.agent,
|
|
129
|
+
workspace: snapshot.workspace,
|
|
130
|
+
creation: snapshot,
|
|
131
|
+
setupSkippedReason: snapshot.setupSkippedReason,
|
|
132
|
+
error: snapshot.error,
|
|
133
|
+
errorCode: snapshot.errorCode,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
reconnect() {
|
|
138
|
+
for (const operation of this.operations.values()) {
|
|
139
|
+
if (operation.settled || !operation.modern || operation.recovering)
|
|
140
|
+
continue;
|
|
141
|
+
operation.recovering = true;
|
|
142
|
+
operation.stopObserving = this.deps.observe(operation.kind, operation.key, (snapshot) => {
|
|
143
|
+
if (!snapshot)
|
|
144
|
+
void this.submit(operation);
|
|
145
|
+
else {
|
|
146
|
+
// Re-subscription is authoritative even if its revision matches the last live event.
|
|
147
|
+
this.receive(snapshot);
|
|
148
|
+
}
|
|
149
|
+
}, (error) => this.fail(operation, error));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
close() {
|
|
153
|
+
for (const operation of this.operations.values())
|
|
154
|
+
if (!operation.settled)
|
|
155
|
+
this.fail(operation, new Error("Daemon client closed"));
|
|
156
|
+
this.operations.clear();
|
|
157
|
+
}
|
|
158
|
+
finish(operation, result) {
|
|
159
|
+
if (operation.settled)
|
|
160
|
+
return;
|
|
161
|
+
operation.settled = true;
|
|
162
|
+
operation.stopObserving?.();
|
|
163
|
+
operation.resolve(result);
|
|
164
|
+
operation.observers.clear();
|
|
165
|
+
this.operations.delete(JSON.stringify([operation.kind, operation.key]));
|
|
166
|
+
}
|
|
167
|
+
fail(operation, error) {
|
|
168
|
+
if (operation.settled)
|
|
169
|
+
return;
|
|
170
|
+
operation.settled = true;
|
|
171
|
+
operation.stopObserving?.();
|
|
172
|
+
operation.reject(error instanceof Error ? error : new Error(String(error)));
|
|
173
|
+
operation.observers.clear();
|
|
174
|
+
this.operations.delete(JSON.stringify([operation.kind, operation.key]));
|
|
175
|
+
}
|
|
176
|
+
notify(observer, snapshot) {
|
|
177
|
+
try {
|
|
178
|
+
observer(snapshot);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
/* Observation does not control execution. */
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function relativeDirectory(cwd, source) {
|
|
186
|
+
const normalize = (value) => value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
187
|
+
const normalized = normalize(cwd), root = normalize(source);
|
|
188
|
+
// Parent traversal must never survive mapping into the newly created checkout.
|
|
189
|
+
if ([normalized, root].some((path) => path.split("/").includes("..")))
|
|
190
|
+
throw new Error("Agent directory must be inside the workspace source");
|
|
191
|
+
if (normalized === root)
|
|
192
|
+
return "";
|
|
193
|
+
if (!normalized.startsWith(`${root}/`))
|
|
194
|
+
throw new Error("Agent directory must be inside the workspace source");
|
|
195
|
+
return normalized.slice(root.length);
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -4,5 +4,6 @@ export declare function createRelayE2eeTransportFactory(args: {
|
|
|
4
4
|
daemonPublicKeyB64: string;
|
|
5
5
|
logger: TransportLogger;
|
|
6
6
|
}): DaemonTransportFactory;
|
|
7
|
+
export declare function createRelayTransportFactory(baseFactory: DaemonTransportFactory): DaemonTransportFactory;
|
|
7
8
|
export declare function createEncryptedTransport(base: DaemonTransport, daemonPublicKeyB64: string, logger: TransportLogger): DaemonTransport;
|
|
8
9
|
//# sourceMappingURL=daemon-client-relay-e2ee-transport.d.ts.map
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { createClientChannel, } from "@hyperdrive.bot/fleet-relay/e2ee";
|
|
2
|
-
import {
|
|
2
|
+
import { extractRelayMessage, normalizeTransportPayload } from "./daemon-client-transport-utils.js";
|
|
3
3
|
export function createRelayE2eeTransportFactory(args) {
|
|
4
|
-
return ({ url
|
|
5
|
-
const base = args.baseFactory({ url
|
|
4
|
+
return ({ url }) => {
|
|
5
|
+
const base = args.baseFactory({ url });
|
|
6
6
|
return createEncryptedTransport(base, args.daemonPublicKeyB64, args.logger);
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
+
export function createRelayTransportFactory(baseFactory) {
|
|
10
|
+
// The relay upgrade precedes E2EE. Only the encrypted hello may carry daemon credentials.
|
|
11
|
+
return ({ url }) => baseFactory({ url });
|
|
12
|
+
}
|
|
9
13
|
export function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
|
|
10
14
|
let channel = null;
|
|
11
15
|
let opened = false;
|
|
@@ -38,7 +42,7 @@ export function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
|
|
|
38
42
|
if (closed) {
|
|
39
43
|
return;
|
|
40
44
|
}
|
|
41
|
-
emitHandlers(messageHandlers, data);
|
|
45
|
+
emitHandlers(messageHandlers, data, data instanceof ArrayBuffer);
|
|
42
46
|
};
|
|
43
47
|
const relayTransport = {
|
|
44
48
|
send: (data) => {
|
|
@@ -81,8 +85,8 @@ export function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
|
|
|
81
85
|
base.onOpen(() => {
|
|
82
86
|
void startHandshake();
|
|
83
87
|
});
|
|
84
|
-
base.onMessage((
|
|
85
|
-
relayTransport.onmessage?.(
|
|
88
|
+
base.onMessage((data, isBinary) => {
|
|
89
|
+
relayTransport.onmessage?.(extractRelayMessage(data, isBinary));
|
|
86
90
|
});
|
|
87
91
|
base.onClose((event) => {
|
|
88
92
|
const record = event;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface DaemonTransport {
|
|
2
2
|
send: (data: string | Uint8Array | ArrayBuffer) => void;
|
|
3
3
|
close: (code?: number, reason?: string) => void;
|
|
4
|
-
onMessage: (handler: (data: unknown) => void) => () => void;
|
|
4
|
+
onMessage: (handler: (data: unknown, isBinary: boolean) => void) => () => void;
|
|
5
5
|
onOpen: (handler: () => void) => () => void;
|
|
6
6
|
onClose: (handler: (event?: unknown) => void) => () => void;
|
|
7
7
|
onError: (handler: (event?: unknown) => void) => () => void;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export declare function copyArrayBufferViewToBuffer(data: ArrayBufferView): ArrayBuffer;
|
|
2
2
|
export declare function normalizeTransportPayload(data: string | Uint8Array | ArrayBuffer): string | ArrayBuffer;
|
|
3
|
-
export
|
|
3
|
+
export interface RelayTransportMessage {
|
|
4
|
+
data: string | ArrayBuffer;
|
|
5
|
+
isBinary: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function extractRelayMessage(event: unknown, nodeIsBinary?: boolean): RelayTransportMessage;
|
|
4
8
|
export declare function describeTransportClose(event?: unknown): string;
|
|
5
9
|
export declare function describeTransportError(event?: unknown): string;
|
|
6
10
|
export declare function safeRandomId(): string;
|
|
@@ -10,18 +10,20 @@ export function normalizeTransportPayload(data) {
|
|
|
10
10
|
}
|
|
11
11
|
return copyArrayBufferViewToBuffer(data);
|
|
12
12
|
}
|
|
13
|
-
export function
|
|
13
|
+
export function extractRelayMessage(event, nodeIsBinary) {
|
|
14
14
|
const raw = event && typeof event === "object" && "data" in event
|
|
15
15
|
? event.data
|
|
16
16
|
: event;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const isBinary = nodeIsBinary ?? typeof raw !== "string";
|
|
18
|
+
if (!isBinary) {
|
|
19
|
+
return { data: decodeMessageData(raw) ?? String(raw ?? ""), isBinary: false };
|
|
20
|
+
}
|
|
19
21
|
if (raw instanceof ArrayBuffer)
|
|
20
|
-
return raw;
|
|
22
|
+
return { data: raw, isBinary: true };
|
|
21
23
|
if (ArrayBuffer.isView(raw)) {
|
|
22
|
-
return copyArrayBufferViewToBuffer(raw);
|
|
24
|
+
return { data: copyArrayBufferViewToBuffer(raw), isBinary: true };
|
|
23
25
|
}
|
|
24
|
-
return String(raw ?? "");
|
|
26
|
+
return { data: String(raw ?? ""), isBinary: true };
|
|
25
27
|
}
|
|
26
28
|
export function describeTransportClose(event) {
|
|
27
29
|
if (!event) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { DaemonTransport, DaemonTransportFactory, TransportLogger, WebSocketFactory, WebSocketLike, } from "./daemon-client-transport-types.js";
|
|
2
|
-
export { decodeMessageData, describeTransportClose, describeTransportError, encodeUtf8String,
|
|
3
|
-
export { createEncryptedTransport, createRelayE2eeTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
|
|
2
|
+
export { decodeMessageData, describeTransportClose, describeTransportError, encodeUtf8String, extractRelayMessage, normalizeTransportPayload, safeRandomId, } from "./daemon-client-transport-utils.js";
|
|
3
|
+
export { createEncryptedTransport, createRelayE2eeTransportFactory, createRelayTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
|
|
4
4
|
export { bindWsHandler, createWebSocketTransportFactory, defaultWebSocketFactory, } from "./daemon-client-websocket-transport.js";
|
|
5
5
|
//# sourceMappingURL=daemon-client-transport.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { decodeMessageData, describeTransportClose, describeTransportError, encodeUtf8String,
|
|
2
|
-
export { createEncryptedTransport, createRelayE2eeTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
|
|
1
|
+
export { decodeMessageData, describeTransportClose, describeTransportError, encodeUtf8String, extractRelayMessage, normalizeTransportPayload, safeRandomId, } from "./daemon-client-transport-utils.js";
|
|
2
|
+
export { createEncryptedTransport, createRelayE2eeTransportFactory, createRelayTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
|
|
3
3
|
export { bindWsHandler, createWebSocketTransportFactory, defaultWebSocketFactory, } from "./daemon-client-websocket-transport.js";
|
|
4
4
|
//# sourceMappingURL=daemon-client-transport.js.map
|
|
@@ -3,6 +3,10 @@ export declare function defaultWebSocketFactory(url: string, options?: {
|
|
|
3
3
|
headers?: Record<string, string>;
|
|
4
4
|
protocols?: string[];
|
|
5
5
|
}): WebSocketLike;
|
|
6
|
+
export declare function nativeWebSocketFactory(url: string, options?: {
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
protocols?: string[];
|
|
9
|
+
}): WebSocketLike;
|
|
6
10
|
export declare function createWebSocketTransportFactory(factory: WebSocketFactory): DaemonTransportFactory;
|
|
7
11
|
export declare function bindWsHandler(ws: WebSocketLike, event: "open" | "close" | "error" | "message", handler: (...args: unknown[]) => void): () => void;
|
|
8
12
|
//# sourceMappingURL=daemon-client-websocket-transport.d.ts.map
|
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
import { extractRelayMessage } from "./daemon-client-transport-utils.js";
|
|
2
|
+
function getGlobalWebSocket() {
|
|
2
3
|
const globalWs = globalThis.WebSocket;
|
|
3
4
|
if (!globalWs) {
|
|
4
5
|
throw new Error("WebSocket is not available in this runtime");
|
|
5
6
|
}
|
|
6
|
-
return
|
|
7
|
+
return globalWs;
|
|
8
|
+
}
|
|
9
|
+
export function defaultWebSocketFactory(url, options) {
|
|
10
|
+
const globalWs = getGlobalWebSocket();
|
|
11
|
+
if (options?.protocols === undefined) {
|
|
12
|
+
return new globalWs(url);
|
|
13
|
+
}
|
|
14
|
+
return new globalWs(url, options.protocols);
|
|
15
|
+
}
|
|
16
|
+
// React Native and Node-compatible WebSocket implementations accept this extra options object.
|
|
17
|
+
// Keep it out of the standard browser factory above.
|
|
18
|
+
export function nativeWebSocketFactory(url, options) {
|
|
19
|
+
return new (getGlobalWebSocket())(url, options?.protocols, { headers: options?.headers });
|
|
7
20
|
}
|
|
8
21
|
export function createWebSocketTransportFactory(factory) {
|
|
9
22
|
return ({ url, headers, protocols }) => {
|
|
@@ -40,10 +53,17 @@ export function createWebSocketTransportFactory(factory) {
|
|
|
40
53
|
onOpen: (handler) => bindWsHandler(ws, "open", handler),
|
|
41
54
|
onClose: (handler) => bindWsHandler(ws, "close", handler),
|
|
42
55
|
onError: (handler) => bindWsHandler(ws, "error", handler),
|
|
43
|
-
onMessage: (handler) =>
|
|
56
|
+
onMessage: (handler) => bindWsMessageHandler(ws, handler),
|
|
44
57
|
};
|
|
45
58
|
};
|
|
46
59
|
}
|
|
60
|
+
function bindWsMessageHandler(ws, handler) {
|
|
61
|
+
const listener = (...args) => {
|
|
62
|
+
const message = extractRelayMessage(args[0], typeof args[1] === "boolean" ? args[1] : undefined);
|
|
63
|
+
handler(message.data, message.isBinary);
|
|
64
|
+
};
|
|
65
|
+
return bindWsHandler(ws, "message", listener);
|
|
66
|
+
}
|
|
47
67
|
function bindTemporaryEarlyCloseErrorHandler(ws) {
|
|
48
68
|
const noop = () => { };
|
|
49
69
|
if (typeof ws.addEventListener === "function") {
|