@slopus/ghostty-web 0.1.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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/dist/GhosttyRemoteTerminal.d.ts +69 -0
  4. package/dist/GhosttyRemoteTerminal.d.ts.map +1 -0
  5. package/dist/GhosttyRemoteTerminal.js +212 -0
  6. package/dist/RemoteTerminalProtocolClient.d.ts +30 -0
  7. package/dist/RemoteTerminalProtocolClient.d.ts.map +1 -0
  8. package/dist/RemoteTerminalProtocolClient.js +350 -0
  9. package/dist/RemoteTerminalProtocolServer.d.ts +75 -0
  10. package/dist/RemoteTerminalProtocolServer.d.ts.map +1 -0
  11. package/dist/RemoteTerminalProtocolServer.js +794 -0
  12. package/dist/WirePacket.d.ts +29 -0
  13. package/dist/WirePacket.d.ts.map +1 -0
  14. package/dist/WirePacket.js +24 -0
  15. package/dist/WirePacketDecoder.d.ts +8 -0
  16. package/dist/WirePacketDecoder.d.ts.map +1 -0
  17. package/dist/WirePacketDecoder.js +130 -0
  18. package/dist/applyGridPatch.d.ts +3 -0
  19. package/dist/applyGridPatch.d.ts.map +1 -0
  20. package/dist/applyGridPatch.js +19 -0
  21. package/dist/diffGridState.d.ts +3 -0
  22. package/dist/diffGridState.d.ts.map +1 -0
  23. package/dist/diffGridState.js +26 -0
  24. package/dist/encodeWirePacket.d.ts +7 -0
  25. package/dist/encodeWirePacket.d.ts.map +1 -0
  26. package/dist/encodeWirePacket.js +25 -0
  27. package/dist/index.d.ts +13 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +9 -0
  30. package/dist/jsonPayload.d.ts +3 -0
  31. package/dist/jsonPayload.d.ts.map +1 -0
  32. package/dist/jsonPayload.js +8 -0
  33. package/dist/testing/ThrottledTcpProxy.d.ts +15 -0
  34. package/dist/testing/ThrottledTcpProxy.d.ts.map +1 -0
  35. package/dist/testing/ThrottledTcpProxy.js +83 -0
  36. package/dist/types.d.ts +105 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/dist/types.js +1 -0
  39. package/package.json +32 -0
@@ -0,0 +1,350 @@
1
+ import { applyGridPatch } from "./applyGridPatch.js";
2
+ import { encodeWirePacket } from "./encodeWirePacket.js";
3
+ import { decodeJsonPayload, encodeJsonPayload } from "./jsonPayload.js";
4
+ import { WirePacketDecoder } from "./WirePacketDecoder.js";
5
+ import { WirePacketType } from "./WirePacket.js";
6
+ export class RemoteTerminalProtocolClient {
7
+ appliedOutputOffset;
8
+ epoch;
9
+ grid;
10
+ inputLease;
11
+ mode;
12
+ ready;
13
+ #decoder = new WirePacketDecoder();
14
+ #inputSequence;
15
+ #lastInputAck = 0;
16
+ #options;
17
+ #operation = Promise.resolve();
18
+ #pendingInputs = new Map();
19
+ #pendingResizes = new Map();
20
+ #pendingScrollback = new Map();
21
+ #readySettled = false;
22
+ #rejectReady;
23
+ #resizeSequence = 0;
24
+ #resolveReady;
25
+ #scrollbackSequence = 0;
26
+ #stream;
27
+ constructor(options) {
28
+ validateClientOptions(options);
29
+ this.#options = options;
30
+ this.#stream = options.stream;
31
+ this.epoch = options.epoch;
32
+ this.inputLease = options.inputLease;
33
+ this.appliedOutputOffset = options.resumeOutputOffset ?? 0;
34
+ this.#inputSequence = options.resumeInputSequence ?? 0;
35
+ for (const pending of options.pendingInputs ?? []) {
36
+ if (!Number.isSafeInteger(pending.sequence) ||
37
+ pending.sequence < 1 ||
38
+ pending.sequence > this.#inputSequence)
39
+ throw new Error("Invalid pending input sequence.");
40
+ this.#pendingInputs.set(pending.sequence, Buffer.from(pending.data));
41
+ }
42
+ this.ready = new Promise((resolve, reject) => {
43
+ this.#resolveReady = resolve;
44
+ this.#rejectReady = reject;
45
+ });
46
+ options.stream.on("data", (data) => {
47
+ try {
48
+ const packets = this.#decoder.push(data);
49
+ if (packets.length > 0)
50
+ options.stream.pause();
51
+ for (const packet of packets)
52
+ this.#operation = this.#operation.then(() => this.#receive(packet));
53
+ void this.#operation
54
+ .then(() => {
55
+ if (!options.stream.destroyed)
56
+ options.stream.resume();
57
+ })
58
+ .catch((error) => options.stream.destroy(error));
59
+ }
60
+ catch (error) {
61
+ options.stream.destroy(error);
62
+ }
63
+ });
64
+ options.stream.once("error", (error) => this.#settleClosed(error));
65
+ options.stream.once("close", () => this.#settleClosed(new Error("Remote terminal connection closed.")));
66
+ this.#send({
67
+ payload: encodeJsonPayload({
68
+ capabilities: options.capabilities ?? { grid: true, vt: true },
69
+ clientId: options.clientId,
70
+ creditBytes: options.creditBytes ?? 256 * 1024,
71
+ ...(options.epoch === undefined ? {} : { epoch: options.epoch }),
72
+ ...(options.inputLease === undefined ? {} : { inputLease: options.inputLease }),
73
+ resumeOutputOffset: this.appliedOutputOffset,
74
+ parserFingerprint: options.parserFingerprint ?? "libghostty-vt/0.2/defaults",
75
+ }),
76
+ sequence: this.appliedOutputOffset,
77
+ type: WirePacketType.ClientHello,
78
+ });
79
+ }
80
+ close() {
81
+ this.#stream.destroy();
82
+ }
83
+ reconnectState() {
84
+ return {
85
+ epoch: this.epoch,
86
+ inputLease: this.inputLease,
87
+ pendingInputs: [...this.#pendingInputs].map(([sequence, data]) => ({
88
+ data: Buffer.from(data),
89
+ sequence,
90
+ })),
91
+ resumeInputSequence: this.#inputSequence,
92
+ resumeOutputOffset: this.appliedOutputOffset,
93
+ };
94
+ }
95
+ requestScrollback(start, count, basis) {
96
+ if (!Number.isSafeInteger(start) ||
97
+ start < 0 ||
98
+ !Number.isSafeInteger(count) ||
99
+ count < 1 ||
100
+ count > 1_000 ||
101
+ this.#pendingScrollback.size >= 128) {
102
+ return Promise.reject(new Error("Invalid scrollback request."));
103
+ }
104
+ const sequence = ++this.#scrollbackSequence;
105
+ return new Promise((resolve, reject) => {
106
+ this.#pendingScrollback.set(sequence, { reject, resolve });
107
+ this.#send({
108
+ payload: encodeJsonPayload({
109
+ ...(basis === undefined ? {} : { basis }),
110
+ count,
111
+ start,
112
+ }),
113
+ sequence,
114
+ type: WirePacketType.ScrollbackRequest,
115
+ });
116
+ });
117
+ }
118
+ resize(cols, rows) {
119
+ validateDimensions(cols, rows);
120
+ if (this.#pendingResizes.size >= 32)
121
+ return Promise.reject(new Error("Too many pending terminal resizes."));
122
+ const sequence = ++this.#resizeSequence;
123
+ return new Promise((resolve, reject) => {
124
+ this.#pendingResizes.set(sequence, { reject, resolve });
125
+ this.#send({
126
+ payload: encodeJsonPayload({ cols, rows }),
127
+ sequence,
128
+ type: WirePacketType.Resize,
129
+ });
130
+ });
131
+ }
132
+ writeInput(data) {
133
+ const bytes = typeof data === "string" ? Buffer.from(data) : Buffer.from(data);
134
+ if (bytes.length > 64 * 1024)
135
+ throw new Error("Terminal input is too large.");
136
+ if (this.#pendingInputs.size >= 1_024)
137
+ throw new Error("Too many unacknowledged terminal inputs.");
138
+ this.#inputSequence += 1;
139
+ this.#pendingInputs.set(this.#inputSequence, bytes);
140
+ this.#send({ payload: bytes, sequence: this.#inputSequence, type: WirePacketType.Input });
141
+ return this.#inputSequence;
142
+ }
143
+ #send(packet) {
144
+ if (this.#stream.destroyed)
145
+ throw new Error("Remote terminal connection is closed.");
146
+ this.#stream.write(encodeWirePacket(packet).data);
147
+ }
148
+ async #receive(packet) {
149
+ if (packet.type === WirePacketType.Error) {
150
+ const value = decodeJsonPayload(packet.payload);
151
+ throw new Error(value.error);
152
+ }
153
+ if (packet.type === WirePacketType.Welcome) {
154
+ if (this.#readySettled)
155
+ throw new Error("Duplicate server welcome.");
156
+ const welcome = decodeJsonPayload(packet.payload);
157
+ validateWelcome(welcome);
158
+ if (this.epoch !== undefined && this.epoch !== welcome.epoch)
159
+ this.appliedOutputOffset = 0;
160
+ this.epoch = welcome.epoch;
161
+ this.inputLease = welcome.inputLease;
162
+ this.mode = welcome.mode;
163
+ this.#options.onMode?.(welcome.mode);
164
+ await this.#options.replica.resize(welcome.cols, welcome.rows);
165
+ this.#send({
166
+ payload: Buffer.alloc(0),
167
+ sequence: welcome.resizeRevision,
168
+ type: WirePacketType.ResizeApplied,
169
+ });
170
+ for (const [sequence, data] of this.#pendingInputs) {
171
+ if (sequence > welcome.inputSequence)
172
+ this.#send({ payload: data, sequence, type: WirePacketType.Input });
173
+ else
174
+ this.#pendingInputs.delete(sequence);
175
+ }
176
+ this.#lastInputAck = welcome.inputSequence;
177
+ this.#readySettled = true;
178
+ this.#resolveReady();
179
+ return;
180
+ }
181
+ if (!this.#readySettled)
182
+ throw new Error("Server welcome is required.");
183
+ if (packet.type === WirePacketType.Output) {
184
+ if (this.mode !== "vt")
185
+ throw new Error("VT output received outside VT mode.");
186
+ const start = packet.sequence - packet.payload.length;
187
+ if (packet.sequence <= this.appliedOutputOffset)
188
+ return;
189
+ if (start !== this.appliedOutputOffset) {
190
+ this.#send({ payload: Buffer.alloc(0), sequence: 0, type: WirePacketType.Resync });
191
+ return;
192
+ }
193
+ await this.#options.replica.applyVt(packet.payload);
194
+ this.appliedOutputOffset = packet.sequence;
195
+ this.#send({
196
+ payload: Buffer.alloc(0),
197
+ sequence: this.appliedOutputOffset,
198
+ type: WirePacketType.OutputAck,
199
+ });
200
+ return;
201
+ }
202
+ if (packet.type === WirePacketType.Mode) {
203
+ const value = decodeJsonPayload(packet.payload);
204
+ if (value.mode !== "grid")
205
+ throw new Error("Invalid terminal mode transition.");
206
+ this.mode = value.mode;
207
+ this.#options.onMode?.(value.mode);
208
+ return;
209
+ }
210
+ if (packet.type === WirePacketType.GridKeyframe) {
211
+ const grid = decodeJsonPayload(packet.payload);
212
+ validateGrid(grid);
213
+ this.grid = grid;
214
+ this.mode = "grid";
215
+ await this.#options.replica.applyGrid(grid);
216
+ this.#send({
217
+ payload: Buffer.alloc(0),
218
+ sequence: grid.revision,
219
+ type: WirePacketType.GridAck,
220
+ });
221
+ return;
222
+ }
223
+ if (packet.type === WirePacketType.GridPatch) {
224
+ const patch = decodeJsonPayload(packet.payload);
225
+ if (this.grid === undefined || this.grid.revision !== patch.baseRevision) {
226
+ this.#send({ payload: Buffer.alloc(0), sequence: 0, type: WirePacketType.Resync });
227
+ return;
228
+ }
229
+ this.grid = applyGridPatch(this.grid, patch);
230
+ validateGrid(this.grid);
231
+ await this.#options.replica.applyGrid(this.grid);
232
+ this.#send({
233
+ payload: Buffer.alloc(0),
234
+ sequence: this.grid.revision,
235
+ type: WirePacketType.GridAck,
236
+ });
237
+ return;
238
+ }
239
+ if (packet.type === WirePacketType.InputAck) {
240
+ if (packet.payload.length !== 0 ||
241
+ packet.sequence < this.#lastInputAck ||
242
+ packet.sequence > this.#inputSequence)
243
+ throw new Error("Invalid input acknowledgement.");
244
+ this.#lastInputAck = packet.sequence;
245
+ for (const sequence of this.#pendingInputs.keys())
246
+ if (sequence <= packet.sequence)
247
+ this.#pendingInputs.delete(sequence);
248
+ return;
249
+ }
250
+ if (packet.type === WirePacketType.ResizeAck) {
251
+ const value = decodeJsonPayload(packet.payload);
252
+ validateDimensions(value.cols, value.rows);
253
+ if (!Number.isSafeInteger(value.barrier) ||
254
+ value.barrier < 0 ||
255
+ (this.mode === "vt" && this.appliedOutputOffset !== value.barrier))
256
+ throw new Error("Resize output barrier mismatch.");
257
+ await this.#options.replica.resize(value.cols, value.rows);
258
+ this.#send({
259
+ payload: Buffer.alloc(0),
260
+ sequence: value.resizeRevision,
261
+ type: WirePacketType.ResizeApplied,
262
+ });
263
+ if (value.requestSequence > 0) {
264
+ const pending = this.#pendingResizes.get(value.requestSequence);
265
+ if (pending === undefined)
266
+ throw new Error("Unknown resize acknowledgement.");
267
+ this.#pendingResizes.delete(value.requestSequence);
268
+ pending.resolve();
269
+ }
270
+ return;
271
+ }
272
+ if (packet.type === WirePacketType.ScrollbackPage) {
273
+ const pending = this.#pendingScrollback.get(packet.sequence);
274
+ if (pending === undefined)
275
+ throw new Error("Unknown scrollback response.");
276
+ this.#pendingScrollback.delete(packet.sequence);
277
+ pending.resolve(decodeJsonPayload(packet.payload));
278
+ return;
279
+ }
280
+ if (packet.type === WirePacketType.Exit) {
281
+ const value = decodeJsonPayload(packet.payload);
282
+ if (this.mode === "vt" && this.appliedOutputOffset !== value.outputOffset)
283
+ throw new Error("Exit output barrier mismatch.");
284
+ if (this.mode === "grid" && (this.grid?.coversOutputOffset ?? -1) < value.outputOffset)
285
+ throw new Error("Exit grid barrier mismatch.");
286
+ this.#options.onExit?.(value.exitCode);
287
+ return;
288
+ }
289
+ throw new Error("Packet is invalid in the current client state.");
290
+ }
291
+ #settleClosed(error) {
292
+ if (!this.#readySettled) {
293
+ this.#readySettled = true;
294
+ this.#rejectReady(error);
295
+ }
296
+ for (const pending of this.#pendingResizes.values())
297
+ pending.reject(error);
298
+ for (const pending of this.#pendingScrollback.values())
299
+ pending.reject(error);
300
+ this.#pendingResizes.clear();
301
+ this.#pendingScrollback.clear();
302
+ }
303
+ }
304
+ function validateClientOptions(options) {
305
+ if (typeof options.clientId !== "string" ||
306
+ options.clientId.length < 1 ||
307
+ options.clientId.length > 128)
308
+ throw new Error("Invalid client identifier.");
309
+ const credit = options.creditBytes ?? 256 * 1024;
310
+ if (!Number.isSafeInteger(credit) || credit < 1 || credit > 64 * 1024 * 1024)
311
+ throw new Error("Invalid client credit.");
312
+ }
313
+ function validateDimensions(cols, rows) {
314
+ if (!Number.isSafeInteger(cols) ||
315
+ cols < 1 ||
316
+ cols > 1_000 ||
317
+ !Number.isSafeInteger(rows) ||
318
+ rows < 1 ||
319
+ rows > 1_000)
320
+ throw new Error("Invalid terminal dimensions.");
321
+ }
322
+ function validateWelcome(value) {
323
+ validateDimensions(value.cols, value.rows);
324
+ if (typeof value.epoch !== "string" ||
325
+ value.epoch.length < 1 ||
326
+ value.epoch.length > 128 ||
327
+ typeof value.inputLease !== "string" ||
328
+ value.inputLease.length < 1 ||
329
+ value.inputLease.length > 128 ||
330
+ !Number.isSafeInteger(value.inputSequence) ||
331
+ value.inputSequence < 0 ||
332
+ !Number.isSafeInteger(value.resizeRevision) ||
333
+ value.resizeRevision < 0 ||
334
+ (value.mode !== "vt" && value.mode !== "grid"))
335
+ throw new Error("Invalid server welcome.");
336
+ }
337
+ function validateGrid(grid) {
338
+ if (!Number.isSafeInteger(grid.revision) ||
339
+ grid.revision < 1 ||
340
+ !Number.isSafeInteger(grid.coversOutputOffset) ||
341
+ grid.coversOutputOffset < 0 ||
342
+ !Number.isSafeInteger(grid.cols) ||
343
+ grid.cols < 1 ||
344
+ grid.cols > 1_000 ||
345
+ !Array.isArray(grid.rows) ||
346
+ grid.rows.length > 1_000 ||
347
+ !Array.isArray(grid.styles) ||
348
+ grid.styles.length > 4_096)
349
+ throw new Error("Invalid terminal grid.");
350
+ }
@@ -0,0 +1,75 @@
1
+ import type { Duplex } from "node:stream";
2
+ import type { RemoteTerminalGridState, RemoteTerminalProtocolMetrics, RemoteTerminalServerOptions } from "./types.js";
3
+ import { type WirePacket } from "./WirePacket.js";
4
+ interface EncodedPacket {
5
+ data: Buffer;
6
+ }
7
+ interface OutputChunk {
8
+ data: Buffer;
9
+ encoded: EncodedPacket;
10
+ end: number;
11
+ start: number;
12
+ }
13
+ interface ExitState {
14
+ exitCode: number | null;
15
+ outputOffset: number;
16
+ }
17
+ export declare class RemoteTerminalProtocolServer {
18
+ #private;
19
+ readonly epoch: string;
20
+ readonly metrics: RemoteTerminalProtocolMetrics;
21
+ constructor(options: RemoteTerminalServerOptions);
22
+ attach(stream: Duplex): () => void;
23
+ publishExit(exitCode: number | null): void;
24
+ publishGrid(state: Omit<RemoteTerminalGridState, "revision">): void;
25
+ publishOutput(data: Uint8Array): void;
26
+ /** Parse output into the canonical emulator first, then publish bytes and its matching grid atomically. */
27
+ publishUpdate(data: Uint8Array, state: Omit<RemoteTerminalGridState, "coversOutputOffset" | "revision">): void;
28
+ claimInputLease(requested: string | undefined, connection: ServerConnection): {
29
+ outputOffset: number;
30
+ resizeRevision: number;
31
+ sequence: number;
32
+ token: string;
33
+ };
34
+ releaseInputLease(connection: ServerConnection): void;
35
+ receiveInput(token: string, sequence: number, data: Uint8Array): Promise<void>;
36
+ acknowledgeOutput(token: string, outputOffset: number): void;
37
+ acknowledgeResize(token: string, revision: number): void;
38
+ requestResize(requester: ServerConnection | undefined, requestSequence: number, cols: number, rows: number): Promise<void>;
39
+ resize(cols: number, rows: number): Promise<void>;
40
+ scrollback(start: number, count: number, basis?: {
41
+ historyEpoch: string;
42
+ historyRevision: number;
43
+ }): import("./types.js").RemoteTerminalScrollbackPage | Promise<import("./types.js").RemoteTerminalScrollbackPage>;
44
+ setFlowPaused(connection: ServerConnection, paused: boolean): void;
45
+ encode(packet: WirePacket): EncodedPacket;
46
+ gridPacket(grid: RemoteTerminalGridState): EncodedPacket;
47
+ dimensions(): {
48
+ cols: number;
49
+ rows: number;
50
+ };
51
+ maxBufferedBytes(): number;
52
+ exitState(): ExitState | undefined;
53
+ grid(): RemoteTerminalGridState | undefined;
54
+ lastResizeBarrier(): number;
55
+ maxUnacknowledgedBytes(): number;
56
+ parserFingerprint(): string;
57
+ oldestOutputOffset(): number;
58
+ outputOffset(): number;
59
+ resizeRevision(): number;
60
+ replayAfter(offset: number): readonly OutputChunk[];
61
+ wireChunkBytes(): number;
62
+ fail(error: unknown): void;
63
+ }
64
+ declare class ServerConnection {
65
+ #private;
66
+ constructor(server: RemoteTerminalProtocolServer, stream: Duplex, maxFrameBytes?: number);
67
+ close(): void;
68
+ fail(error: unknown): void;
69
+ publishGrid(previous: RemoteTerminalGridState | undefined, next: RemoteTerminalGridState): void;
70
+ publishOutput(chunk: OutputChunk): void;
71
+ sendResize(cols: number, rows: number, barrier: number, resizeRevision: number, requestSequence: number): void;
72
+ maybeSendExit(): void;
73
+ }
74
+ export {};
75
+ //# sourceMappingURL=RemoteTerminalProtocolServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteTerminalProtocolServer.d.ts","sourceRoot":"","sources":["../sources/RemoteTerminalProtocolServer.ts"],"names":[],"mappings":"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"}