Move server to TS

Fixes #5509
This commit is contained in:
Daniel Imms
2025-12-27 04:36:18 -08:00
parent e28db9e8a9
commit 1a1bdefc83
17 changed files with 95 additions and 58 deletions
+1
View File
@@ -2,6 +2,7 @@
"chat.tools.terminal.autoApprove": {
"npm run build": true,
"npm run esbuild": true,
"npm run dev": true,
"npm run lint": true,
"npm run lint-fix": true,
"npm run lint-api": true,
+15 -1
View File
@@ -12,6 +12,7 @@ const config = {
isProd: argv.includes('--prod'),
isWatch: argv.includes('--watch'),
isDemoClient: argv.includes('--demo-client'),
isDemoServer: argv.includes('--demo-server'),
isHeadless: argv.includes('--headless'),
addon: argv.find(e => e.startsWith('--addon='))?.replace(/^--addon=/, ''),
};
@@ -126,7 +127,7 @@ if (config.addon) {
} else if (config.isDemoClient) {
bundleConfig = {
...bundleConfig,
entryPoints: [`demo/client.ts`],
entryPoints: [`demo/client/client.ts`],
outfile: 'demo/dist/client-bundle.js',
external: ['util', 'os', 'fs', 'path', 'stream', 'Terminal'],
alias: {
@@ -152,6 +153,19 @@ if (config.addon) {
"@xterm/addon-ligatures": "./addons/addon-ligatures/out-esbuild/LigaturesAddon",
}
}
skipOut = true;
skipOutTest = true;
} else if (config.isDemoServer) {
bundleConfig = {
...bundleConfig,
entryPoints: [`demo/server/server.ts`],
outfile: 'demo/dist/server-bundle.js',
format: 'cjs',
platform: 'node',
external: ['node-pty'],
}
skipOut = true;
skipOutTest = true;
} else if (config.isHeadless) {
bundleConfig = {
...bundleConfig,
@@ -3,9 +3,9 @@
* @license MIT
*/
/// <reference path="../../../typings/xterm.d.ts"/>
/// <reference path="../../../../typings/xterm.d.ts"/>
import { writeUnicodeTable } from 'unicodeTable';
import { writeUnicodeTable } from '../../unicodeTable';
import type { IControlWindow } from '../controlBar';
import { BaseWindow } from './baseWindow';
import type { IDisposable, Terminal } from '@xterm/xterm';
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2021",
"rootDir": ".",
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@xterm/addon-attach": ["../../addons/addon-attach"],
"@xterm/addon-clipboard": ["../../addons/addon-clipboard"],
"@xterm/addon-fit": ["../../addons/addon-fit"],
"@xterm/addon-image": ["../../addons/addon-image"],
"@xterm/addon-progress": ["../../addons/addon-progress"],
"@xterm/addon-search": ["../../addons/addon-search"],
"@xterm/addon-serialize": ["../../addons/addon-serialize"],
"@xterm/addon-web-links": ["../../addons/addon-web-links"],
"@xterm/addon-webgl": ["../../addons/addon-webgl"],
"@xterm/addon-unicode11": ["../../addons/addon-unicode11"],
"@xterm/addon-unicode-graphemes": ["../../addons/addon-unicode-graphemes"],
"@xterm/addon-ligatures": ["../../addons/addon-ligatures"]
}
},
"include": [
"client.ts",
"../../typings/xterm.d.ts"
]
}
+33 -29
View File
@@ -4,54 +4,58 @@
* demo to the public as is would introduce security risks for the host.
**/
// @ts-check
import * as express from 'express';
import * as expressWs from 'express-ws';
import * as os from 'os';
import * as pty from 'node-pty';
import * as path from 'path';
import type { IPty } from 'node-pty';
const express = require('express');
const expressWs = require('express-ws');
const os = require('os');
const pty = require('node-pty');
interface IDisposable {
dispose(): void;
}
/** Whether to use binary transport. */
const USE_BINARY = os.platform() !== "win32";
function startServer() {
const demoRoot = path.join(__dirname, '..');
function startServer(): void {
const app = express();
const appWs = expressWs(app).app;
const terminals = {};
const unsentOutput = {};
const temporaryDisposable = {};
const terminals: { [pid: number]: IPty } = {};
const unsentOutput: { [pid: number]: string } = {};
const temporaryDisposable: { [pid: number]: IDisposable } = {};
app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css'));
app.use('/xterm.css', express.static(demoRoot + '/../css/xterm.css'));
app.get('/logo.png', (req, res) => {
res.sendFile(__dirname + '/logo.png');
res.sendFile(demoRoot + '/logo.png');
});
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
res.sendFile(demoRoot + '/index.html');
});
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/test.html');
res.sendFile(demoRoot + '/test.html');
});
app.get('/index.css', (req, res) => {
res.sendFile(__dirname + '/index.css');
res.sendFile(demoRoot + '/index.css');
});
app.use('/dist', express.static(__dirname + '/dist'));
app.use('/src', express.static(__dirname + '/src'));
app.use('/dist', express.static(demoRoot + '/dist'));
app.use('/src', express.static(demoRoot + '/src'));
app.post('/terminals', (req, res) => {
/** @type {{ [key: string]: string }} */
const env = {};
const env: { [key: string]: string } = {};
for (const k of Object.keys(process.env)) {
const v = process.env[k];
if (v) {
env[k] = v;
}
}
// const env = Object.assign({}, process.env);
env['COLORTERM'] = 'truecolor';
if (typeof req.query.cols !== 'string' || typeof req.query.rows !== 'string') {
console.error({ req });
@@ -108,10 +112,10 @@ function startServer() {
let userInput = false;
// string message buffering
function buffer(socket, timeout, maxSize) {
function buffer(socket: typeof ws, timeout: number, maxSize: number) {
let s = '';
let sender = null;
return (data) => {
let sender: ReturnType<typeof setTimeout> | null = null;
return (data: string) => {
s += data;
if (s.length > maxSize || userInput) {
userInput = false;
@@ -131,11 +135,11 @@ function startServer() {
};
}
// binary message buffering
function bufferUtf8(socket, timeout, maxSize) {
const chunks = [];
function bufferUtf8(socket: typeof ws, timeout: number, maxSize: number) {
const chunks: Buffer[] = [];
let length = 0;
let sender = null;
return (data) => {
let sender: ReturnType<typeof setTimeout> | null = null;
return (data: Buffer) => {
chunks.push(data);
length += data.length;
if (length > maxSize || userInput) {
@@ -164,13 +168,13 @@ function startServer() {
// the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/
term.onData(function(data) {
try {
send(data);
send(data as string & Buffer);
} catch (ex) {
// The WebSocket is not open, ignore
}
});
ws.on('message', function(msg) {
term.write(msg);
term.write(msg.toString());
userInput = true;
});
ws.on('close', function () {
@@ -196,4 +200,4 @@ process.on('uncaughtException', (error) => {
}
});
module.exports = startServer;
export default startServer;
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2021",
"rootDir": ".",
"sourceMap": true
},
"include": [
"server.ts"
]
}
+1 -1
View File
@@ -5,6 +5,6 @@
// @ts-check
const startServer = require('./server.js');
const startServer = require('./dist/server-bundle.js').default;
startServer();
+5 -25
View File
@@ -1,27 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2021",
"rootDir": ".",
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@xterm/addon-attach": ["../addons/addon-attach"],
"@xterm/addon-clipboard": ["../addons/addon-clipboard"],
"@xterm/addon-fit": ["../addons/addon-fit"],
"@xterm/addon-image": ["../addons/addon-image"],
"@xterm/addon-progress": ["../addons/addon-progress"],
"@xterm/addon-search": ["../addons/addon-search"],
"@xterm/addon-serialize": ["../addons/addon-serialize"],
"@xterm/addon-web-links": ["../addons/addon-web-links"],
"@xterm/addon-webgl": ["../addons/addon-webgl"],
"@xterm/addon-unicode11": ["../addons/addon-unicode11"],
"@xterm/addon-unicode-graphemes": ["../addons/addon-unicode-graphemes"],
"@xterm/addon-ligatures": ["../addons/addon-ligatures"]
}
},
"include": [
"client.ts",
"../typings/xterm.d.ts"
]
"references": [
{ "path": "./client" },
{ "path": "./server" }
],
"files": []
}