@blit-sh/core 0.17.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/dist/BlitConnection.d.ts +132 -0
- package/dist/BlitConnection.d.ts.map +1 -0
- package/dist/BlitConnection.js +875 -0
- package/dist/BlitConnection.js.map +1 -0
- package/dist/BlitWorkspace.d.ts +65 -0
- package/dist/BlitWorkspace.d.ts.map +1 -0
- package/dist/BlitWorkspace.js +298 -0
- package/dist/BlitWorkspace.js.map +1 -0
- package/dist/TerminalStore.d.ts +125 -0
- package/dist/TerminalStore.d.ts.map +1 -0
- package/dist/TerminalStore.js +468 -0
- package/dist/TerminalStore.js.map +1 -0
- package/dist/bsp/dsl.d.ts +56 -0
- package/dist/bsp/dsl.d.ts.map +1 -0
- package/dist/bsp/dsl.js +247 -0
- package/dist/bsp/dsl.js.map +1 -0
- package/dist/bsp/index.d.ts +5 -0
- package/dist/bsp/index.d.ts.map +1 -0
- package/dist/bsp/index.js +3 -0
- package/dist/bsp/index.js.map +1 -0
- package/dist/bsp/layout.d.ts +32 -0
- package/dist/bsp/layout.d.ts.map +1 -0
- package/dist/bsp/layout.js +84 -0
- package/dist/bsp/layout.js.map +1 -0
- package/dist/gl-renderer.d.ts +10 -0
- package/dist/gl-renderer.d.ts.map +1 -0
- package/dist/gl-renderer.js +297 -0
- package/dist/gl-renderer.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/keyboard.d.ts +7 -0
- package/dist/keyboard.d.ts.map +1 -0
- package/dist/keyboard.js +118 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/measure.d.ts +19 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +55 -0
- package/dist/measure.js.map +1 -0
- package/dist/palettes.d.ts +3 -0
- package/dist/palettes.d.ts.map +1 -0
- package/dist/palettes.js +803 -0
- package/dist/palettes.js.map +1 -0
- package/dist/protocol.d.ts +34 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +195 -0
- package/dist/protocol.js.map +1 -0
- package/dist/transports/index.d.ts +8 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +5 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/transports/webrtc-share.d.ts +11 -0
- package/dist/transports/webrtc-share.d.ts.map +1 -0
- package/dist/transports/webrtc-share.js +299 -0
- package/dist/transports/webrtc-share.js.map +1 -0
- package/dist/transports/webrtc.d.ts +11 -0
- package/dist/transports/webrtc.d.ts.map +1 -0
- package/dist/transports/webrtc.js +234 -0
- package/dist/transports/webrtc.js.map +1 -0
- package/dist/transports/websocket.d.ts +37 -0
- package/dist/transports/websocket.d.ts.map +1 -0
- package/dist/transports/websocket.js +200 -0
- package/dist/transports/websocket.js.map +1 -0
- package/dist/transports/webtransport.d.ts +53 -0
- package/dist/transports/webtransport.d.ts.map +1 -0
- package/dist/transports/webtransport.js +277 -0
- package/dist/transports/webtransport.js.map +1 -0
- package/dist/types.d.ts +163 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +42 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import { FEATURE_COPY_RANGE, FEATURE_CREATE_NONCE, FEATURE_RESIZE_BATCH, FEATURE_RESTART, PROTOCOL_VERSION, S2C_CLOSED, S2C_CREATED, S2C_CREATED_N, S2C_EXITED, S2C_HELLO, S2C_LIST, S2C_SEARCH_RESULTS, S2C_TEXT, S2C_TITLE, S2C_UPDATE, } from "./types";
|
|
2
|
+
import { buildCloseMessage, buildClearResizeBatchMessage, buildClearResizeMessage, buildCopyRangeMessage, buildCreate2Message, buildFocusMessage, buildInputMessage, buildMouseMessage, buildResizeBatchMessage, buildResizeMessage, buildKillMessage, buildRestartMessage, buildScrollMessage, buildSearchMessage, } from "./protocol";
|
|
3
|
+
import { TerminalStore } from "./TerminalStore";
|
|
4
|
+
const textDecoder = new TextDecoder();
|
|
5
|
+
export const SEARCH_SOURCE_TITLE = 0;
|
|
6
|
+
export const SEARCH_SOURCE_VISIBLE = 1;
|
|
7
|
+
export const SEARCH_SOURCE_SCROLLBACK = 2;
|
|
8
|
+
export const SEARCH_MATCH_TITLE = 1 << 0;
|
|
9
|
+
export const SEARCH_MATCH_VISIBLE = 1 << 1;
|
|
10
|
+
export const SEARCH_MATCH_SCROLLBACK = 1 << 2;
|
|
11
|
+
function connectionError(message) {
|
|
12
|
+
return new Error(message);
|
|
13
|
+
}
|
|
14
|
+
function isLiveSession(session) {
|
|
15
|
+
return (session.state === "creating" ||
|
|
16
|
+
session.state === "active" ||
|
|
17
|
+
session.state === "exited");
|
|
18
|
+
}
|
|
19
|
+
function toPublicSession(s) {
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
export class BlitConnection {
|
|
23
|
+
constructor({ id, transport, wasm, autoConnect = true, }) {
|
|
24
|
+
this.listeners = new Set();
|
|
25
|
+
this.sessionsById = new Map();
|
|
26
|
+
this.currentSessionIdByPtyId = new Map();
|
|
27
|
+
this.pendingCreates = new Map();
|
|
28
|
+
this.pendingCloses = new Map();
|
|
29
|
+
this.pendingSearches = new Map();
|
|
30
|
+
this.pendingReads = new Map();
|
|
31
|
+
this.sessionCounter = 0;
|
|
32
|
+
this.nonceCounter = 0;
|
|
33
|
+
this.searchCounter = 0;
|
|
34
|
+
this.features = 0;
|
|
35
|
+
this.disposed = false;
|
|
36
|
+
/** Per-session, per-view size registry for computing minimum resize. */
|
|
37
|
+
this.viewSizes = new Map();
|
|
38
|
+
this.viewIdCounter = 0;
|
|
39
|
+
this.hasConnected = false;
|
|
40
|
+
this.retryCount = 0;
|
|
41
|
+
this.lastError = null;
|
|
42
|
+
this.sessions = [];
|
|
43
|
+
this._publicSessions = [];
|
|
44
|
+
this._publicSessionsDirty = false;
|
|
45
|
+
this.subscribe = (listener) => {
|
|
46
|
+
this.listeners.add(listener);
|
|
47
|
+
return () => {
|
|
48
|
+
this.listeners.delete(listener);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
this.getSnapshot = () => this.snapshot;
|
|
52
|
+
this.handleMessage = (data) => {
|
|
53
|
+
const bytes = new Uint8Array(data);
|
|
54
|
+
if (bytes.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
const type = bytes[0];
|
|
57
|
+
switch (type) {
|
|
58
|
+
case S2C_UPDATE: {
|
|
59
|
+
if (bytes.length < 3)
|
|
60
|
+
return;
|
|
61
|
+
const ptyId = bytes[1] | (bytes[2] << 8);
|
|
62
|
+
this.store.handleUpdate(ptyId, bytes.subarray(3));
|
|
63
|
+
this.syncTitleFromTerminal(ptyId);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
case S2C_CREATED: {
|
|
67
|
+
if (bytes.length < 3)
|
|
68
|
+
return;
|
|
69
|
+
const ptyId = bytes[1] | (bytes[2] << 8);
|
|
70
|
+
const tag = textDecoder.decode(bytes.subarray(3));
|
|
71
|
+
const session = this.upsertLiveSession(ptyId, tag, "active");
|
|
72
|
+
if ((this.features & FEATURE_CREATE_NONCE) === 0 &&
|
|
73
|
+
this.pendingCreates.size > 0) {
|
|
74
|
+
const [firstNonce, pending] = this.pendingCreates.entries().next()
|
|
75
|
+
.value;
|
|
76
|
+
this.pendingCreates.delete(firstNonce);
|
|
77
|
+
pending.resolve(toPublicSession(session));
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
case S2C_CREATED_N: {
|
|
82
|
+
if (bytes.length < 5)
|
|
83
|
+
return;
|
|
84
|
+
const nonce = bytes[1] | (bytes[2] << 8);
|
|
85
|
+
const ptyId = bytes[3] | (bytes[4] << 8);
|
|
86
|
+
const tag = textDecoder.decode(bytes.subarray(5));
|
|
87
|
+
const session = this.upsertLiveSession(ptyId, tag, "active");
|
|
88
|
+
const pending = this.pendingCreates.get(nonce);
|
|
89
|
+
if (pending) {
|
|
90
|
+
this.pendingCreates.delete(nonce);
|
|
91
|
+
pending.resolve(toPublicSession(session));
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
case S2C_CLOSED: {
|
|
96
|
+
if (bytes.length < 3)
|
|
97
|
+
return;
|
|
98
|
+
const ptyId = bytes[1] | (bytes[2] << 8);
|
|
99
|
+
const sessionId = this.currentSessionIdByPtyId.get(ptyId);
|
|
100
|
+
if (sessionId) {
|
|
101
|
+
this.markSessionClosed(sessionId);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
case S2C_EXITED: {
|
|
106
|
+
if (bytes.length < 3)
|
|
107
|
+
return;
|
|
108
|
+
const ptyId = bytes[1] | (bytes[2] << 8);
|
|
109
|
+
const sessionId = this.currentSessionIdByPtyId.get(ptyId);
|
|
110
|
+
if (sessionId) {
|
|
111
|
+
this.updateSession(sessionId, { state: "exited" });
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
case S2C_LIST: {
|
|
116
|
+
this.handleListMessage(bytes);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
case S2C_TITLE: {
|
|
120
|
+
if (bytes.length < 3)
|
|
121
|
+
return;
|
|
122
|
+
const ptyId = bytes[1] | (bytes[2] << 8);
|
|
123
|
+
const sessionId = this.currentSessionIdByPtyId.get(ptyId);
|
|
124
|
+
if (!sessionId)
|
|
125
|
+
return;
|
|
126
|
+
this.updateSession(sessionId, {
|
|
127
|
+
title: textDecoder.decode(bytes.subarray(3)),
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
case S2C_SEARCH_RESULTS: {
|
|
132
|
+
this.handleSearchResults(bytes);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
case S2C_HELLO: {
|
|
136
|
+
if (bytes.length < 7)
|
|
137
|
+
return;
|
|
138
|
+
const version = bytes[1] | (bytes[2] << 8);
|
|
139
|
+
const features = bytes[3] | (bytes[4] << 8) | (bytes[5] << 16) | (bytes[6] << 24);
|
|
140
|
+
if (version > PROTOCOL_VERSION) {
|
|
141
|
+
this.transport.close();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.features = features;
|
|
145
|
+
this.snapshot = {
|
|
146
|
+
...this.snapshot,
|
|
147
|
+
supportsRestart: (features & FEATURE_RESTART) !== 0,
|
|
148
|
+
supportsCopyRange: (features & FEATURE_COPY_RANGE) !== 0,
|
|
149
|
+
};
|
|
150
|
+
this.emit();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
case S2C_TEXT: {
|
|
154
|
+
if (bytes.length < 13)
|
|
155
|
+
return;
|
|
156
|
+
const nonce = bytes[1] | (bytes[2] << 8);
|
|
157
|
+
const text = textDecoder.decode(bytes.subarray(13));
|
|
158
|
+
const pending = this.pendingReads.get(nonce);
|
|
159
|
+
if (pending) {
|
|
160
|
+
this.pendingReads.delete(nonce);
|
|
161
|
+
pending.resolve(text);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
default:
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
this.handleStatusChange = (status) => {
|
|
170
|
+
this.store.handleStatusChange(status);
|
|
171
|
+
const lastError = status === "error" && this.transport.lastError
|
|
172
|
+
? this.transport.lastError
|
|
173
|
+
: null;
|
|
174
|
+
const authRejected = status === "error" && this.transport.authRejected;
|
|
175
|
+
if (status === "connected") {
|
|
176
|
+
this.hasConnected = true;
|
|
177
|
+
this.retryCount = 0;
|
|
178
|
+
this.lastError = null;
|
|
179
|
+
}
|
|
180
|
+
else if ((status === "error" || status === "disconnected" || status === "closed") &&
|
|
181
|
+
(this.snapshot.status === "connecting" ||
|
|
182
|
+
this.snapshot.status === "authenticating")) {
|
|
183
|
+
this.retryCount++;
|
|
184
|
+
}
|
|
185
|
+
// Persist the error until a successful connection clears it.
|
|
186
|
+
if (authRejected) {
|
|
187
|
+
this.lastError = "auth";
|
|
188
|
+
}
|
|
189
|
+
else if (lastError) {
|
|
190
|
+
this.lastError = lastError;
|
|
191
|
+
}
|
|
192
|
+
this.snapshot = {
|
|
193
|
+
...this.snapshot,
|
|
194
|
+
status,
|
|
195
|
+
retryCount: this.retryCount,
|
|
196
|
+
error: this.lastError,
|
|
197
|
+
};
|
|
198
|
+
if (status === "disconnected" || status === "closed" || status === "error") {
|
|
199
|
+
this.rejectPendingCreates(connectionError(`Transport ${status} before PTY creation completed`));
|
|
200
|
+
this.rejectPendingSearches(connectionError(`Transport ${status}`));
|
|
201
|
+
this.rejectPendingReads(connectionError(`Transport ${status}`));
|
|
202
|
+
this.resolveAllPendingCloses();
|
|
203
|
+
}
|
|
204
|
+
this.emit();
|
|
205
|
+
};
|
|
206
|
+
this.id = id;
|
|
207
|
+
this.transport = transport;
|
|
208
|
+
this.store = new TerminalStore({
|
|
209
|
+
send: (data) => {
|
|
210
|
+
if (this.transport.status === "connected") {
|
|
211
|
+
this.transport.send(data);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
getStatus: () => this.transport.status,
|
|
215
|
+
}, wasm);
|
|
216
|
+
this.snapshot = {
|
|
217
|
+
id,
|
|
218
|
+
status: transport.status,
|
|
219
|
+
ready: false,
|
|
220
|
+
supportsRestart: false,
|
|
221
|
+
supportsCopyRange: false,
|
|
222
|
+
retryCount: 0,
|
|
223
|
+
error: null,
|
|
224
|
+
sessions: [],
|
|
225
|
+
focusedSessionId: null,
|
|
226
|
+
};
|
|
227
|
+
if (transport.status === "connected") {
|
|
228
|
+
this.hasConnected = true;
|
|
229
|
+
}
|
|
230
|
+
this.transport.addEventListener("message", this.handleMessage);
|
|
231
|
+
this.transport.addEventListener("statuschange", this.handleStatusChange);
|
|
232
|
+
this.store.handleStatusChange(this.transport.status);
|
|
233
|
+
if (autoConnect) {
|
|
234
|
+
this.connect();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
get publicSessions() {
|
|
238
|
+
if (this._publicSessionsDirty) {
|
|
239
|
+
this._publicSessions = this.sessions.map(toPublicSession);
|
|
240
|
+
this._publicSessionsDirty = false;
|
|
241
|
+
}
|
|
242
|
+
return this._publicSessions;
|
|
243
|
+
}
|
|
244
|
+
invalidatePublicSessions() {
|
|
245
|
+
this._publicSessionsDirty = true;
|
|
246
|
+
}
|
|
247
|
+
connect() {
|
|
248
|
+
if (this.disposed)
|
|
249
|
+
return;
|
|
250
|
+
this.transport.connect();
|
|
251
|
+
}
|
|
252
|
+
reconnect() {
|
|
253
|
+
this.connect();
|
|
254
|
+
}
|
|
255
|
+
close() {
|
|
256
|
+
this.transport.close();
|
|
257
|
+
}
|
|
258
|
+
dispose() {
|
|
259
|
+
if (this.disposed)
|
|
260
|
+
return;
|
|
261
|
+
this.disposed = true;
|
|
262
|
+
this.transport.removeEventListener("message", this.handleMessage);
|
|
263
|
+
this.transport.removeEventListener("statuschange", this.handleStatusChange);
|
|
264
|
+
this.rejectPendingCreates(connectionError("Connection disposed before PTY creation completed"));
|
|
265
|
+
this.rejectPendingSearches(connectionError("Connection disposed"));
|
|
266
|
+
this.rejectPendingReads(connectionError("Connection disposed"));
|
|
267
|
+
this.resolveAllPendingCloses();
|
|
268
|
+
this.store.destroy();
|
|
269
|
+
}
|
|
270
|
+
setVisibleSessionIds(sessionIds) {
|
|
271
|
+
const desired = new Set();
|
|
272
|
+
for (const sessionId of sessionIds) {
|
|
273
|
+
const session = this.sessionsById.get(sessionId);
|
|
274
|
+
if (session && session.state !== "closed") {
|
|
275
|
+
desired.add(session.ptyId);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
this.store.setDesiredSubscriptions(desired);
|
|
279
|
+
}
|
|
280
|
+
getSession(sessionId) {
|
|
281
|
+
const s = this.sessionsById.get(sessionId);
|
|
282
|
+
return s ? toPublicSession(s) : null;
|
|
283
|
+
}
|
|
284
|
+
getDebugStats(sessionId) {
|
|
285
|
+
const session = sessionId ? this.sessionsById.get(sessionId) : null;
|
|
286
|
+
return this.store.getDebugStats(session?.ptyId ?? null);
|
|
287
|
+
}
|
|
288
|
+
async createSession(options) {
|
|
289
|
+
if (this.transport.status !== "connected") {
|
|
290
|
+
throw connectionError(`Cannot create PTY while transport is ${this.transport.status}`);
|
|
291
|
+
}
|
|
292
|
+
return new Promise((resolve, reject) => {
|
|
293
|
+
let nonce = 0;
|
|
294
|
+
do {
|
|
295
|
+
nonce = this.nonceCounter = (this.nonceCounter + 1) & 0xffff;
|
|
296
|
+
} while (this.pendingCreates.has(nonce));
|
|
297
|
+
let srcPtyId;
|
|
298
|
+
if (options.cwdFromSessionId) {
|
|
299
|
+
const src = this.sessionsById.get(options.cwdFromSessionId);
|
|
300
|
+
if (src)
|
|
301
|
+
srcPtyId = src.ptyId;
|
|
302
|
+
}
|
|
303
|
+
this.pendingCreates.set(nonce, { resolve, reject });
|
|
304
|
+
this.transport.send(buildCreate2Message(nonce, options.rows, options.cols, {
|
|
305
|
+
tag: options.tag,
|
|
306
|
+
command: options.command,
|
|
307
|
+
srcPtyId,
|
|
308
|
+
}));
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
copyRange(sessionId, startTail, startCol, endTail, endCol) {
|
|
312
|
+
if (this.transport.status !== "connected") {
|
|
313
|
+
return Promise.reject(connectionError(`Cannot copy while transport is ${this.transport.status}`));
|
|
314
|
+
}
|
|
315
|
+
const session = this.sessionsById.get(sessionId);
|
|
316
|
+
if (!session) {
|
|
317
|
+
return Promise.reject(connectionError("Unknown session"));
|
|
318
|
+
}
|
|
319
|
+
return new Promise((resolve, reject) => {
|
|
320
|
+
let nonce = 0;
|
|
321
|
+
do {
|
|
322
|
+
nonce = this.nonceCounter = (this.nonceCounter + 1) & 0xffff;
|
|
323
|
+
} while (this.pendingCreates.has(nonce) ||
|
|
324
|
+
this.pendingReads.has(nonce));
|
|
325
|
+
this.pendingReads.set(nonce, { resolve, reject });
|
|
326
|
+
this.transport.send(buildCopyRangeMessage(nonce, session.ptyId, startTail, startCol, endTail, endCol));
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
supportsCopyRange() {
|
|
330
|
+
return (this.features & FEATURE_COPY_RANGE) !== 0;
|
|
331
|
+
}
|
|
332
|
+
async closeSession(sessionId) {
|
|
333
|
+
const session = this.sessionsById.get(sessionId);
|
|
334
|
+
if (!session || session.state === "closed")
|
|
335
|
+
return;
|
|
336
|
+
if (this.transport.status !== "connected")
|
|
337
|
+
return;
|
|
338
|
+
return new Promise((resolve) => {
|
|
339
|
+
const resolvers = this.pendingCloses.get(sessionId);
|
|
340
|
+
if (resolvers) {
|
|
341
|
+
resolvers.push(resolve);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
this.pendingCloses.set(sessionId, [resolve]);
|
|
345
|
+
}
|
|
346
|
+
this.transport.send(buildCloseMessage(session.ptyId));
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
restartSession(sessionId) {
|
|
350
|
+
const session = this.sessionsById.get(sessionId);
|
|
351
|
+
if (!session ||
|
|
352
|
+
session.state === "closed" ||
|
|
353
|
+
this.transport.status !== "connected") {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.transport.send(buildRestartMessage(session.ptyId));
|
|
357
|
+
}
|
|
358
|
+
killSession(sessionId, signal = 15) {
|
|
359
|
+
const session = this.sessionsById.get(sessionId);
|
|
360
|
+
if (!session ||
|
|
361
|
+
session.state !== "active" ||
|
|
362
|
+
this.transport.status !== "connected") {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
this.transport.send(buildKillMessage(session.ptyId, signal));
|
|
366
|
+
}
|
|
367
|
+
focusSession(sessionId) {
|
|
368
|
+
if (sessionId === null) {
|
|
369
|
+
if (this.snapshot.focusedSessionId !== null) {
|
|
370
|
+
this.snapshot = {
|
|
371
|
+
...this.snapshot,
|
|
372
|
+
focusedSessionId: null,
|
|
373
|
+
};
|
|
374
|
+
this.store.setLead(null);
|
|
375
|
+
this.emit();
|
|
376
|
+
}
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const session = this.sessionsById.get(sessionId);
|
|
380
|
+
if (!session || session.state === "closed")
|
|
381
|
+
return;
|
|
382
|
+
const changed = this.snapshot.focusedSessionId !== sessionId;
|
|
383
|
+
this.snapshot = {
|
|
384
|
+
...this.snapshot,
|
|
385
|
+
focusedSessionId: sessionId,
|
|
386
|
+
};
|
|
387
|
+
this.store.setLead(session.ptyId);
|
|
388
|
+
if (this.transport.status === "connected") {
|
|
389
|
+
this.transport.send(buildFocusMessage(session.ptyId));
|
|
390
|
+
}
|
|
391
|
+
if (changed) {
|
|
392
|
+
this.emit();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
sendInput(sessionId, data) {
|
|
396
|
+
const session = this.sessionsById.get(sessionId);
|
|
397
|
+
if (!session ||
|
|
398
|
+
!isLiveSession(session) ||
|
|
399
|
+
this.transport.status !== "connected") {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
this.transport.send(buildInputMessage(session.ptyId, data));
|
|
403
|
+
}
|
|
404
|
+
resizeSession(sessionId, rows, cols) {
|
|
405
|
+
this.resizeSessions([{ sessionId, rows, cols }]);
|
|
406
|
+
}
|
|
407
|
+
clearSessionSize(sessionId) {
|
|
408
|
+
this.clearSessionSizes([sessionId]);
|
|
409
|
+
}
|
|
410
|
+
clearSessionSizes(sessionIds) {
|
|
411
|
+
if (this.transport.status !== "connected") {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const ptyIds = [];
|
|
415
|
+
for (const sessionId of sessionIds) {
|
|
416
|
+
const session = this.sessionsById.get(sessionId);
|
|
417
|
+
if (!session || !isLiveSession(session)) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
ptyIds.push(session.ptyId);
|
|
421
|
+
}
|
|
422
|
+
if (ptyIds.length === 0 || (this.features & FEATURE_RESIZE_BATCH) === 0) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (ptyIds.length === 1) {
|
|
426
|
+
this.transport.send(buildClearResizeMessage(ptyIds[0]));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
this.transport.send(buildClearResizeBatchMessage(ptyIds));
|
|
430
|
+
}
|
|
431
|
+
resizeSessions(entries) {
|
|
432
|
+
if (this.transport.status !== "connected") {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const resolved = [];
|
|
436
|
+
for (const entry of entries) {
|
|
437
|
+
const session = this.sessionsById.get(entry.sessionId);
|
|
438
|
+
if (!session || !isLiveSession(session)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
resolved.push({
|
|
442
|
+
ptyId: session.ptyId,
|
|
443
|
+
rows: entry.rows,
|
|
444
|
+
cols: entry.cols,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (resolved.length === 0) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if ((this.features & FEATURE_RESIZE_BATCH) !== 0) {
|
|
451
|
+
this.transport.send(buildResizeBatchMessage(resolved));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
for (const entry of resolved) {
|
|
455
|
+
this.transport.send(buildResizeMessage(entry.ptyId, entry.rows, entry.cols));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
scrollSession(sessionId, offset) {
|
|
459
|
+
const session = this.sessionsById.get(sessionId);
|
|
460
|
+
if (!session ||
|
|
461
|
+
!isLiveSession(session) ||
|
|
462
|
+
this.transport.status !== "connected") {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
this.transport.send(buildScrollMessage(session.ptyId, offset));
|
|
466
|
+
}
|
|
467
|
+
sendMouse(sessionId, type, button, col, row) {
|
|
468
|
+
const session = this.sessionsById.get(sessionId);
|
|
469
|
+
if (!session ||
|
|
470
|
+
!isLiveSession(session) ||
|
|
471
|
+
this.transport.status !== "connected") {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
this.transport.send(buildMouseMessage(session.ptyId, type, button, col, row));
|
|
475
|
+
}
|
|
476
|
+
async search(query) {
|
|
477
|
+
if (this.transport.status !== "connected") {
|
|
478
|
+
throw connectionError(`Cannot search while transport is ${this.transport.status}`);
|
|
479
|
+
}
|
|
480
|
+
return new Promise((resolve, reject) => {
|
|
481
|
+
let requestId = 0;
|
|
482
|
+
do {
|
|
483
|
+
requestId = this.searchCounter = (this.searchCounter + 1) & 0xffff;
|
|
484
|
+
} while (this.pendingSearches.has(requestId));
|
|
485
|
+
this.pendingSearches.set(requestId, { resolve, reject });
|
|
486
|
+
this.transport.send(buildSearchMessage(requestId, query));
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
ptyId(sessionId) {
|
|
490
|
+
return this.sessionsById.get(sessionId)?.ptyId;
|
|
491
|
+
}
|
|
492
|
+
getTerminal(sessionId) {
|
|
493
|
+
const id = this.ptyId(sessionId);
|
|
494
|
+
return id != null ? this.store.getTerminal(id) : null;
|
|
495
|
+
}
|
|
496
|
+
/** Allocate a unique view ID for multi-pane size tracking. */
|
|
497
|
+
allocViewId() {
|
|
498
|
+
return `v${++this.viewIdCounter}`;
|
|
499
|
+
}
|
|
500
|
+
/** Register/update a view's size for a session. Sends the minimum to the server. */
|
|
501
|
+
setViewSize(sessionId, viewId, rows, cols) {
|
|
502
|
+
let views = this.viewSizes.get(sessionId);
|
|
503
|
+
if (!views) {
|
|
504
|
+
views = new Map();
|
|
505
|
+
this.viewSizes.set(sessionId, views);
|
|
506
|
+
}
|
|
507
|
+
views.set(viewId, { rows, cols });
|
|
508
|
+
this.sendMinSize(sessionId);
|
|
509
|
+
}
|
|
510
|
+
/** Unregister a view. Recalculates and sends the new minimum. */
|
|
511
|
+
removeView(sessionId, viewId) {
|
|
512
|
+
const views = this.viewSizes.get(sessionId);
|
|
513
|
+
if (!views)
|
|
514
|
+
return;
|
|
515
|
+
views.delete(viewId);
|
|
516
|
+
if (views.size === 0) {
|
|
517
|
+
this.viewSizes.delete(sessionId);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
this.sendMinSize(sessionId);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
sendMinSize(sessionId) {
|
|
524
|
+
const views = this.viewSizes.get(sessionId);
|
|
525
|
+
if (!views || views.size === 0)
|
|
526
|
+
return;
|
|
527
|
+
let minRows = Infinity;
|
|
528
|
+
let minCols = Infinity;
|
|
529
|
+
for (const { rows, cols } of views.values()) {
|
|
530
|
+
if (rows < minRows)
|
|
531
|
+
minRows = rows;
|
|
532
|
+
if (cols < minCols)
|
|
533
|
+
minCols = cols;
|
|
534
|
+
}
|
|
535
|
+
// views.size > 0 guarantees minRows/minCols are finite.
|
|
536
|
+
if (minRows > 0 && minCols > 0) {
|
|
537
|
+
this.resizeSession(sessionId, minRows, minCols);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
metricsGeneration() {
|
|
541
|
+
return this.store.metricsGeneration;
|
|
542
|
+
}
|
|
543
|
+
bumpMetricsGeneration() {
|
|
544
|
+
return ++this.store.metricsGeneration;
|
|
545
|
+
}
|
|
546
|
+
getRetainCount(sessionId) {
|
|
547
|
+
const id = this.ptyId(sessionId);
|
|
548
|
+
return id != null ? this.store.getRetainCount(id) : 0;
|
|
549
|
+
}
|
|
550
|
+
retain(sessionId) {
|
|
551
|
+
const id = this.ptyId(sessionId);
|
|
552
|
+
if (id != null)
|
|
553
|
+
this.store.retain(id);
|
|
554
|
+
}
|
|
555
|
+
release(sessionId) {
|
|
556
|
+
const id = this.ptyId(sessionId);
|
|
557
|
+
if (id != null)
|
|
558
|
+
this.store.release(id);
|
|
559
|
+
}
|
|
560
|
+
freeze(sessionId) {
|
|
561
|
+
const id = this.ptyId(sessionId);
|
|
562
|
+
if (id != null)
|
|
563
|
+
this.store.freeze(id);
|
|
564
|
+
}
|
|
565
|
+
thaw(sessionId) {
|
|
566
|
+
const id = this.ptyId(sessionId);
|
|
567
|
+
if (id != null)
|
|
568
|
+
this.store.thaw(id);
|
|
569
|
+
}
|
|
570
|
+
isFrozen(sessionId) {
|
|
571
|
+
const id = this.ptyId(sessionId);
|
|
572
|
+
return id != null && this.store.isFrozen(id);
|
|
573
|
+
}
|
|
574
|
+
addDirtyListener(sessionId, listener) {
|
|
575
|
+
const id = this.ptyId(sessionId);
|
|
576
|
+
if (id == null)
|
|
577
|
+
return () => { };
|
|
578
|
+
return this.store.addDirtyListener((dirtyId) => {
|
|
579
|
+
if (dirtyId === id)
|
|
580
|
+
listener();
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
drainPending(sessionId) {
|
|
584
|
+
const id = this.ptyId(sessionId);
|
|
585
|
+
return id != null ? this.store.drainPending(id) : false;
|
|
586
|
+
}
|
|
587
|
+
getSharedRenderer() {
|
|
588
|
+
return this.store.getSharedRenderer();
|
|
589
|
+
}
|
|
590
|
+
setCellSize(pw, ph) {
|
|
591
|
+
this.store.setCellSize(pw, ph);
|
|
592
|
+
}
|
|
593
|
+
getCellSize() {
|
|
594
|
+
return this.store.getCellSize();
|
|
595
|
+
}
|
|
596
|
+
wasmMemory() {
|
|
597
|
+
return this.store.wasmMemory();
|
|
598
|
+
}
|
|
599
|
+
noteFrameRendered() {
|
|
600
|
+
this.store.noteFrameRendered();
|
|
601
|
+
}
|
|
602
|
+
invalidateAtlas() {
|
|
603
|
+
this.store.invalidateAtlas();
|
|
604
|
+
}
|
|
605
|
+
setFontFamily(f) {
|
|
606
|
+
this.store.setFontFamily(f);
|
|
607
|
+
}
|
|
608
|
+
setFontSize(s) {
|
|
609
|
+
this.store.setFontSize(s);
|
|
610
|
+
}
|
|
611
|
+
setPalette(p) {
|
|
612
|
+
this.store.setPalette(p);
|
|
613
|
+
}
|
|
614
|
+
isReady() {
|
|
615
|
+
return this.store.isReady();
|
|
616
|
+
}
|
|
617
|
+
onReady(listener) {
|
|
618
|
+
return this.store.onReady(listener);
|
|
619
|
+
}
|
|
620
|
+
emit() {
|
|
621
|
+
for (const listener of this.listeners)
|
|
622
|
+
listener();
|
|
623
|
+
}
|
|
624
|
+
handleListMessage(bytes) {
|
|
625
|
+
if (bytes.length < 3)
|
|
626
|
+
return;
|
|
627
|
+
const count = bytes[1] | (bytes[2] << 8);
|
|
628
|
+
const entries = [];
|
|
629
|
+
let offset = 3;
|
|
630
|
+
for (let index = 0; index < count; index++) {
|
|
631
|
+
if (offset + 4 > bytes.length)
|
|
632
|
+
break;
|
|
633
|
+
const ptyId = bytes[offset] | (bytes[offset + 1] << 8);
|
|
634
|
+
const tagLen = bytes[offset + 2] | (bytes[offset + 3] << 8);
|
|
635
|
+
offset += 4;
|
|
636
|
+
const tag = textDecoder.decode(bytes.subarray(offset, offset + tagLen));
|
|
637
|
+
offset += tagLen;
|
|
638
|
+
let command = null;
|
|
639
|
+
if (offset + 2 <= bytes.length) {
|
|
640
|
+
const cmdLen = bytes[offset] | (bytes[offset + 1] << 8);
|
|
641
|
+
offset += 2;
|
|
642
|
+
if (cmdLen > 0 && offset + cmdLen <= bytes.length) {
|
|
643
|
+
command = textDecoder.decode(bytes.subarray(offset, offset + cmdLen));
|
|
644
|
+
}
|
|
645
|
+
offset += cmdLen;
|
|
646
|
+
}
|
|
647
|
+
entries.push({ ptyId, tag, command });
|
|
648
|
+
}
|
|
649
|
+
const livePtys = new Set(entries.map((entry) => entry.ptyId));
|
|
650
|
+
for (const session of this.sessions) {
|
|
651
|
+
if (isLiveSession(session) && !livePtys.has(session.ptyId)) {
|
|
652
|
+
this.markSessionClosed(session.id, false);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
for (const entry of entries) {
|
|
656
|
+
const existingSessionId = this.currentSessionIdByPtyId.get(entry.ptyId);
|
|
657
|
+
const existingSession = existingSessionId
|
|
658
|
+
? (this.sessionsById.get(existingSessionId) ?? null)
|
|
659
|
+
: null;
|
|
660
|
+
if (!existingSession || existingSession.state === "closed") {
|
|
661
|
+
this.upsertLiveSession(entry.ptyId, entry.tag, "active", entry.command);
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
this.updateSession(existingSession.id, {
|
|
665
|
+
tag: entry.tag,
|
|
666
|
+
command: entry.command,
|
|
667
|
+
state: existingSession.state === "exited" ? "exited" : "active",
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
const previousFocus = this.snapshot.focusedSessionId;
|
|
671
|
+
const nextFocus = this.snapshot.focusedSessionId &&
|
|
672
|
+
this.sessionsById.get(this.snapshot.focusedSessionId)?.state !== "closed"
|
|
673
|
+
? this.snapshot.focusedSessionId
|
|
674
|
+
: null;
|
|
675
|
+
this.snapshot = {
|
|
676
|
+
...this.snapshot,
|
|
677
|
+
ready: true,
|
|
678
|
+
focusedSessionId: nextFocus,
|
|
679
|
+
};
|
|
680
|
+
this.store.setLead(nextFocus ? (this.sessionsById.get(nextFocus)?.ptyId ?? null) : null);
|
|
681
|
+
this.emit();
|
|
682
|
+
if (nextFocus && nextFocus !== previousFocus) {
|
|
683
|
+
const session = this.sessionsById.get(nextFocus);
|
|
684
|
+
if (session && this.transport.status === "connected") {
|
|
685
|
+
this.transport.send(buildFocusMessage(session.ptyId));
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
handleSearchResults(bytes) {
|
|
690
|
+
if (bytes.length < 5)
|
|
691
|
+
return;
|
|
692
|
+
const requestId = bytes[1] | (bytes[2] << 8);
|
|
693
|
+
const count = bytes[3] | (bytes[4] << 8);
|
|
694
|
+
const pending = this.pendingSearches.get(requestId);
|
|
695
|
+
if (!pending)
|
|
696
|
+
return;
|
|
697
|
+
const results = [];
|
|
698
|
+
let offset = 5;
|
|
699
|
+
for (let index = 0; index < count; index++) {
|
|
700
|
+
if (offset + 14 > bytes.length)
|
|
701
|
+
break;
|
|
702
|
+
const ptyId = bytes[offset] | (bytes[offset + 1] << 8);
|
|
703
|
+
const score = bytes[offset + 2] |
|
|
704
|
+
(bytes[offset + 3] << 8) |
|
|
705
|
+
(bytes[offset + 4] << 16) |
|
|
706
|
+
((bytes[offset + 5] << 24) >>> 0);
|
|
707
|
+
const primarySource = bytes[offset + 6];
|
|
708
|
+
const matchedSources = bytes[offset + 7];
|
|
709
|
+
const rawScroll = (bytes[offset + 8] |
|
|
710
|
+
(bytes[offset + 9] << 8) |
|
|
711
|
+
(bytes[offset + 10] << 16) |
|
|
712
|
+
(bytes[offset + 11] << 24)) >>>
|
|
713
|
+
0;
|
|
714
|
+
const scrollOffset = rawScroll === 0xffffffff ? null : rawScroll;
|
|
715
|
+
const contextLen = bytes[offset + 12] | (bytes[offset + 13] << 8);
|
|
716
|
+
offset += 14;
|
|
717
|
+
const context = textDecoder.decode(bytes.subarray(offset, offset + contextLen));
|
|
718
|
+
offset += contextLen;
|
|
719
|
+
const sessionId = this.currentSessionIdByPtyId.get(ptyId);
|
|
720
|
+
if (!sessionId)
|
|
721
|
+
continue;
|
|
722
|
+
results.push({
|
|
723
|
+
sessionId,
|
|
724
|
+
connectionId: this.id,
|
|
725
|
+
score,
|
|
726
|
+
primarySource,
|
|
727
|
+
matchedSources,
|
|
728
|
+
scrollOffset,
|
|
729
|
+
context,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
this.pendingSearches.delete(requestId);
|
|
733
|
+
pending.resolve(results);
|
|
734
|
+
}
|
|
735
|
+
syncTitleFromTerminal(ptyId) {
|
|
736
|
+
const sessionId = this.currentSessionIdByPtyId.get(ptyId);
|
|
737
|
+
if (!sessionId)
|
|
738
|
+
return;
|
|
739
|
+
queueMicrotask(() => {
|
|
740
|
+
const currentSessionId = this.currentSessionIdByPtyId.get(ptyId);
|
|
741
|
+
if (currentSessionId !== sessionId)
|
|
742
|
+
return;
|
|
743
|
+
const terminal = this.store.getTerminal(ptyId);
|
|
744
|
+
if (!terminal)
|
|
745
|
+
return;
|
|
746
|
+
const title = terminal.title();
|
|
747
|
+
const session = this.sessionsById.get(sessionId);
|
|
748
|
+
if (!session || session.title === title)
|
|
749
|
+
return;
|
|
750
|
+
this.updateSession(sessionId, { title });
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
upsertLiveSession(ptyId, tag, state, command = null) {
|
|
754
|
+
const currentId = this.currentSessionIdByPtyId.get(ptyId);
|
|
755
|
+
const current = currentId
|
|
756
|
+
? (this.sessionsById.get(currentId) ?? null)
|
|
757
|
+
: null;
|
|
758
|
+
if (current && current.state !== "closed") {
|
|
759
|
+
return this.updateSession(current.id, { tag, command, state });
|
|
760
|
+
}
|
|
761
|
+
const session = {
|
|
762
|
+
id: `${this.id}:${++this.sessionCounter}`,
|
|
763
|
+
connectionId: this.id,
|
|
764
|
+
ptyId,
|
|
765
|
+
tag,
|
|
766
|
+
title: current?.title ?? null,
|
|
767
|
+
command,
|
|
768
|
+
state,
|
|
769
|
+
};
|
|
770
|
+
this.currentSessionIdByPtyId.set(ptyId, session.id);
|
|
771
|
+
this.sessionsById.set(session.id, session);
|
|
772
|
+
this.sessions = [...this.sessions, session];
|
|
773
|
+
this.invalidatePublicSessions();
|
|
774
|
+
this.snapshot = {
|
|
775
|
+
...this.snapshot,
|
|
776
|
+
sessions: this.publicSessions,
|
|
777
|
+
};
|
|
778
|
+
this.emit();
|
|
779
|
+
return session;
|
|
780
|
+
}
|
|
781
|
+
updateSession(sessionId, patch) {
|
|
782
|
+
const current = this.sessionsById.get(sessionId);
|
|
783
|
+
if (!current) {
|
|
784
|
+
throw connectionError(`Unknown session ${sessionId}`);
|
|
785
|
+
}
|
|
786
|
+
// Skip no-op updates.
|
|
787
|
+
if (Object.keys(patch).every((k) => current[k] ===
|
|
788
|
+
patch[k])) {
|
|
789
|
+
return current;
|
|
790
|
+
}
|
|
791
|
+
const next = { ...current, ...patch };
|
|
792
|
+
this.sessionsById.set(sessionId, next);
|
|
793
|
+
this.sessions = this.sessions.map((session) => session.id === sessionId ? next : session);
|
|
794
|
+
this.invalidatePublicSessions();
|
|
795
|
+
this.snapshot = {
|
|
796
|
+
...this.snapshot,
|
|
797
|
+
sessions: this.publicSessions,
|
|
798
|
+
};
|
|
799
|
+
this.emit();
|
|
800
|
+
return next;
|
|
801
|
+
}
|
|
802
|
+
markSessionClosed(sessionId, emit = true) {
|
|
803
|
+
const session = this.sessionsById.get(sessionId);
|
|
804
|
+
if (!session || session.state === "closed")
|
|
805
|
+
return;
|
|
806
|
+
const next = {
|
|
807
|
+
...session,
|
|
808
|
+
state: "closed",
|
|
809
|
+
};
|
|
810
|
+
this.sessionsById.set(sessionId, next);
|
|
811
|
+
this.invalidatePublicSessions();
|
|
812
|
+
this.sessions = this.sessions.map((entry) => entry.id === sessionId ? next : entry);
|
|
813
|
+
if (this.currentSessionIdByPtyId.get(session.ptyId) === sessionId) {
|
|
814
|
+
this.currentSessionIdByPtyId.delete(session.ptyId);
|
|
815
|
+
}
|
|
816
|
+
this.store.freeTerminal(session.ptyId);
|
|
817
|
+
const focusedWasClosed = this.snapshot.focusedSessionId === sessionId;
|
|
818
|
+
const nextFocus = focusedWasClosed
|
|
819
|
+
? this.firstLiveSessionId()
|
|
820
|
+
: this.snapshot.focusedSessionId;
|
|
821
|
+
this.snapshot = {
|
|
822
|
+
...this.snapshot,
|
|
823
|
+
sessions: this.publicSessions,
|
|
824
|
+
focusedSessionId: nextFocus ?? null,
|
|
825
|
+
};
|
|
826
|
+
this.store.setLead(nextFocus ? (this.sessionsById.get(nextFocus)?.ptyId ?? null) : null);
|
|
827
|
+
const resolvers = this.pendingCloses.get(sessionId);
|
|
828
|
+
if (resolvers) {
|
|
829
|
+
this.pendingCloses.delete(sessionId);
|
|
830
|
+
for (const resolve of resolvers)
|
|
831
|
+
resolve();
|
|
832
|
+
}
|
|
833
|
+
if (emit) {
|
|
834
|
+
if (focusedWasClosed &&
|
|
835
|
+
nextFocus &&
|
|
836
|
+
this.transport.status === "connected") {
|
|
837
|
+
const nextSession = this.sessionsById.get(nextFocus);
|
|
838
|
+
if (nextSession) {
|
|
839
|
+
this.transport.send(buildFocusMessage(nextSession.ptyId));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
this.emit();
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
firstLiveSessionId() {
|
|
846
|
+
const session = this.sessions.find((entry) => entry.state !== "closed");
|
|
847
|
+
return session?.id ?? null;
|
|
848
|
+
}
|
|
849
|
+
rejectPendingCreates(error) {
|
|
850
|
+
for (const pending of this.pendingCreates.values()) {
|
|
851
|
+
pending.reject(error);
|
|
852
|
+
}
|
|
853
|
+
this.pendingCreates.clear();
|
|
854
|
+
}
|
|
855
|
+
rejectPendingSearches(error) {
|
|
856
|
+
for (const pending of this.pendingSearches.values()) {
|
|
857
|
+
pending.reject(error);
|
|
858
|
+
}
|
|
859
|
+
this.pendingSearches.clear();
|
|
860
|
+
}
|
|
861
|
+
rejectPendingReads(error) {
|
|
862
|
+
for (const pending of this.pendingReads.values()) {
|
|
863
|
+
pending.reject(error);
|
|
864
|
+
}
|
|
865
|
+
this.pendingReads.clear();
|
|
866
|
+
}
|
|
867
|
+
resolveAllPendingCloses() {
|
|
868
|
+
for (const resolvers of this.pendingCloses.values()) {
|
|
869
|
+
for (const resolve of resolvers)
|
|
870
|
+
resolve();
|
|
871
|
+
}
|
|
872
|
+
this.pendingCloses.clear();
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
//# sourceMappingURL=BlitConnection.js.map
|