@tui-sandbox/library 1.2.0 → 2.0.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/CHANGELOG.md +19 -0
- package/dist/src/client/index.d.ts +1 -0
- package/dist/src/client/index.js +2 -0
- package/dist/src/client/neovim-client.d.ts +12 -0
- package/dist/src/client/neovim-client.js +58 -0
- package/dist/src/client/websocket-client.d.ts +0 -10
- package/dist/src/client/websocket-client.js +0 -55
- package/dist/src/server/TestServer.d.ts +8 -0
- package/dist/src/server/TestServer.js +49 -0
- package/dist/src/server/dirtree/index.js +1 -2
- package/dist/src/server/index.d.ts +3 -7
- package/dist/src/server/index.js +3 -48
- package/dist/src/server/neovim/environment/createTempDir.d.ts +1 -0
- package/dist/src/server/neovim/environment/createTempDir.test.d.ts +1 -0
- package/dist/src/server/neovim/environment/createTempDir.test.js +29 -0
- package/dist/src/server/server.d.ts +2 -2
- package/dist/src/server/server.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/src/client/index.ts +3 -0
- package/src/client/neovim-client.ts +73 -0
- package/src/client/style.css +1 -1
- package/src/client/websocket-client.ts +0 -69
- package/src/server/TestServer.ts +54 -0
- package/src/server/dirtree/index.ts +1 -2
- package/src/server/index.ts +4 -51
- package/src/server/neovim/environment/createTempDir.test.ts +34 -0
- package/src/server/neovim/environment/createTempDir.ts +4 -2
- package/src/server/server.ts +1 -1
- /package/src/{public → client/public}/DejaVuSansMNerdFontMono-Regular.ttf +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v1.3.0...library-v2.0.0) (2024-09-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* add public client and server APIs ([#46](https://github.com/mikavilpas/tui-sandbox/issues/46))
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add public client and server APIs ([#46](https://github.com/mikavilpas/tui-sandbox/issues/46)) ([3f13f53](https://github.com/mikavilpas/tui-sandbox/commit/3f13f5386f31de9bb5bf6fc099e2e404261d31b0))
|
|
13
|
+
|
|
14
|
+
## [1.3.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v1.2.0...library-v1.3.0) (2024-09-23)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* add license (MIT) to library package ([#42](https://github.com/mikavilpas/tui-sandbox/issues/42)) ([8c72eb1](https://github.com/mikavilpas/tui-sandbox/commit/8c72eb13e17c5e6838220a5a16da0d44a6aba792))
|
|
20
|
+
* allow test dirs that have no contents ([#40](https://github.com/mikavilpas/tui-sandbox/issues/40)) ([863e9f0](https://github.com/mikavilpas/tui-sandbox/commit/863e9f0731b1565bf5f9afdb6834275598196f22))
|
|
21
|
+
|
|
3
22
|
## [1.2.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v1.1.0...library-v1.2.0) (2024-09-22)
|
|
4
23
|
|
|
5
24
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NeovimClient } from "./neovim-client";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "@xterm/xterm/css/xterm.css";
|
|
2
|
+
import type { StartNeovimGenericArguments } from "../server/neovim/NeovimApplication.ts";
|
|
3
|
+
import type { TestDirectory } from "../server/types.ts";
|
|
4
|
+
import "./style.css";
|
|
5
|
+
export declare class NeovimClient {
|
|
6
|
+
private readonly ready;
|
|
7
|
+
private readonly tabId;
|
|
8
|
+
private readonly terminal;
|
|
9
|
+
private readonly trpc;
|
|
10
|
+
constructor(app: HTMLElement);
|
|
11
|
+
startNeovim(args: StartNeovimGenericArguments): Promise<TestDirectory>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createTRPCClient, createWSClient, wsLink } from "@trpc/client";
|
|
2
|
+
import "@xterm/xterm/css/xterm.css";
|
|
3
|
+
import "./style.css";
|
|
4
|
+
import { getTabId, startTerminal } from "./websocket-client.js";
|
|
5
|
+
export class NeovimClient {
|
|
6
|
+
ready;
|
|
7
|
+
tabId;
|
|
8
|
+
terminal;
|
|
9
|
+
trpc;
|
|
10
|
+
constructor(app) {
|
|
11
|
+
const wsClient = createWSClient({ url: `ws://localhost:3000`, WebSocket });
|
|
12
|
+
const trpc = createTRPCClient({
|
|
13
|
+
links: [wsLink({ client: wsClient })],
|
|
14
|
+
});
|
|
15
|
+
this.trpc = trpc;
|
|
16
|
+
this.tabId = getTabId();
|
|
17
|
+
const tabId = this.tabId;
|
|
18
|
+
const terminal = startTerminal(app, {
|
|
19
|
+
onMouseEvent(data) {
|
|
20
|
+
void trpc.neovim.sendStdin.mutate({ tabId, data }).catch((error) => {
|
|
21
|
+
console.error(`Error sending mouse event`, error);
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
onKeyPress(event) {
|
|
25
|
+
void trpc.neovim.sendStdin.mutate({ tabId, data: event.key });
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
this.terminal = terminal;
|
|
29
|
+
// start listening to Neovim stdout - this will take some (short) amount of
|
|
30
|
+
// time to complete
|
|
31
|
+
this.ready = new Promise(resolve => {
|
|
32
|
+
console.log("Subscribing to Neovim stdout");
|
|
33
|
+
trpc.neovim.onStdout.subscribe({ client: tabId }, {
|
|
34
|
+
onStarted() {
|
|
35
|
+
resolve();
|
|
36
|
+
},
|
|
37
|
+
onData(data) {
|
|
38
|
+
terminal.write(data);
|
|
39
|
+
},
|
|
40
|
+
onError(err) {
|
|
41
|
+
console.error(`Error from Neovim`, err);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async startNeovim(args) {
|
|
47
|
+
await this.ready;
|
|
48
|
+
const neovim = await this.trpc.neovim.start.mutate({
|
|
49
|
+
startNeovimArguments: args,
|
|
50
|
+
tabId: this.tabId,
|
|
51
|
+
terminalDimensions: {
|
|
52
|
+
cols: this.terminal.cols,
|
|
53
|
+
rows: this.terminal.rows,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return neovim;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Terminal } from "@xterm/xterm";
|
|
2
2
|
import "@xterm/xterm/css/xterm.css";
|
|
3
|
-
import type { StartNeovimGenericArguments } from "../server/neovim/NeovimApplication.ts";
|
|
4
|
-
import type { TestDirectory } from "../server/types.ts";
|
|
5
3
|
import type { TabId } from "../server/utilities/tabId.ts";
|
|
6
4
|
import "./style.css";
|
|
7
5
|
export type StartTerminalOptions = {
|
|
@@ -15,11 +13,3 @@ export declare function startTerminal(app: HTMLElement, options: StartTerminalOp
|
|
|
15
13
|
/** An identifier unique to a browser tab, so that each tab can have its own
|
|
16
14
|
* unique session that persists across page reloads. */
|
|
17
15
|
export declare function getTabId(): TabId;
|
|
18
|
-
export declare class NeovimClient {
|
|
19
|
-
private readonly ready;
|
|
20
|
-
private readonly tabId;
|
|
21
|
-
private readonly terminal;
|
|
22
|
-
private readonly trpc;
|
|
23
|
-
constructor(app: HTMLElement);
|
|
24
|
-
startNeovim(args: StartNeovimGenericArguments): Promise<TestDirectory>;
|
|
25
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { flavors } from "@catppuccin/palette";
|
|
2
|
-
import { createTRPCClient, createWSClient, wsLink } from "@trpc/client";
|
|
3
2
|
import { FitAddon } from "@xterm/addon-fit";
|
|
4
3
|
import { Terminal } from "@xterm/xterm";
|
|
5
4
|
import "@xterm/xterm/css/xterm.css";
|
|
@@ -71,57 +70,3 @@ export function getTabId() {
|
|
|
71
70
|
}
|
|
72
71
|
return { tabId };
|
|
73
72
|
}
|
|
74
|
-
export class NeovimClient {
|
|
75
|
-
ready;
|
|
76
|
-
tabId;
|
|
77
|
-
terminal;
|
|
78
|
-
trpc;
|
|
79
|
-
constructor(app) {
|
|
80
|
-
const wsClient = createWSClient({ url: `ws://localhost:3000`, WebSocket });
|
|
81
|
-
const trpc = createTRPCClient({
|
|
82
|
-
links: [wsLink({ client: wsClient })],
|
|
83
|
-
});
|
|
84
|
-
this.trpc = trpc;
|
|
85
|
-
this.tabId = getTabId();
|
|
86
|
-
const tabId = this.tabId;
|
|
87
|
-
const terminal = startTerminal(app, {
|
|
88
|
-
onMouseEvent(data) {
|
|
89
|
-
void trpc.neovim.sendStdin.mutate({ tabId, data }).catch((error) => {
|
|
90
|
-
console.error(`Error sending mouse event`, error);
|
|
91
|
-
});
|
|
92
|
-
},
|
|
93
|
-
onKeyPress(event) {
|
|
94
|
-
void trpc.neovim.sendStdin.mutate({ tabId, data: event.key });
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
this.terminal = terminal;
|
|
98
|
-
// start listening to Neovim stdout - this will take some (short) amount of
|
|
99
|
-
// time to complete
|
|
100
|
-
this.ready = new Promise(resolve => {
|
|
101
|
-
console.log("Subscribing to Neovim stdout");
|
|
102
|
-
trpc.neovim.onStdout.subscribe({ client: tabId }, {
|
|
103
|
-
onStarted() {
|
|
104
|
-
resolve();
|
|
105
|
-
},
|
|
106
|
-
onData(data) {
|
|
107
|
-
terminal.write(data);
|
|
108
|
-
},
|
|
109
|
-
onError(err) {
|
|
110
|
-
console.error(`Error from Neovim`, err);
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
async startNeovim(args) {
|
|
116
|
-
await this.ready;
|
|
117
|
-
const neovim = await this.trpc.neovim.start.mutate({
|
|
118
|
-
startNeovimArguments: args,
|
|
119
|
-
tabId: this.tabId,
|
|
120
|
-
terminalDimensions: {
|
|
121
|
-
cols: this.terminal.cols,
|
|
122
|
-
rows: this.terminal.rows,
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
return neovim;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AnyTRPCRouter } from "@trpc/server";
|
|
2
|
+
import "core-js/proposals/async-explicit-resource-management";
|
|
3
|
+
import type { TestServerConfig } from "./updateTestdirectorySchemaFile";
|
|
4
|
+
export declare class TestServer {
|
|
5
|
+
private readonly port;
|
|
6
|
+
constructor(port: number);
|
|
7
|
+
startAndRun<TRouter extends AnyTRPCRouter>(appRouter: TRouter, config: TestServerConfig): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
|
2
|
+
import "core-js/proposals/async-explicit-resource-management";
|
|
3
|
+
import { once } from "events";
|
|
4
|
+
import { WebSocketServer } from "ws";
|
|
5
|
+
import { createContext } from "./connection/trpc";
|
|
6
|
+
import { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile";
|
|
7
|
+
export class TestServer {
|
|
8
|
+
port;
|
|
9
|
+
constructor(port) {
|
|
10
|
+
this.port = port;
|
|
11
|
+
}
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
13
|
+
async startAndRun(appRouter, config) {
|
|
14
|
+
console.log("🚀 Server starting");
|
|
15
|
+
await updateTestdirectorySchemaFile(config);
|
|
16
|
+
const wss = new WebSocketServer({ port: this.port });
|
|
17
|
+
const handler = applyWSSHandler({
|
|
18
|
+
wss,
|
|
19
|
+
router: appRouter,
|
|
20
|
+
createContext,
|
|
21
|
+
// Enable heartbeat messages to keep connection open (disabled by default)
|
|
22
|
+
keepAlive: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
// server ping message interval in milliseconds
|
|
25
|
+
pingMs: 30_000,
|
|
26
|
+
// connection is terminated if pong message is not received in this many milliseconds
|
|
27
|
+
pongWaitMs: 5000,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
wss.on("connection", socket => {
|
|
31
|
+
console.log(`➕➕ Connection (${wss.clients.size})`);
|
|
32
|
+
socket.once("close", () => {
|
|
33
|
+
console.log(`➖➖ Connection (${wss.clients.size})`);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
console.log(`✅ WebSocket Server listening on ws://localhost:${this.port}`);
|
|
37
|
+
await Promise.race([once(process, "SIGTERM"), once(process, "SIGINT")]);
|
|
38
|
+
console.log("Shutting down...");
|
|
39
|
+
handler.broadcastReconnectNotification();
|
|
40
|
+
wss.close(error => {
|
|
41
|
+
if (error) {
|
|
42
|
+
console.error("Error closing WebSocket server", error);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
console.log("WebSocket server closed");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -28,13 +28,12 @@ export function convertDree(root) {
|
|
|
28
28
|
stem: root.extension ? root.name.slice(0, -root.extension.length) : root.name,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
assert(root.children, `Expected children for directory node ${root.name}`);
|
|
32
31
|
const node = {
|
|
33
32
|
name: root.name,
|
|
34
33
|
type: root.type,
|
|
35
34
|
contents: {},
|
|
36
35
|
};
|
|
37
|
-
for (const child of root.children) {
|
|
36
|
+
for (const child of root.children || []) {
|
|
38
37
|
node.contents[child.name] = convertDree(child);
|
|
39
38
|
}
|
|
40
39
|
return node;
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type { AnyRouter } from "@trpc/server";
|
|
2
1
|
import "core-js/proposals/async-explicit-resource-management";
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
constructor(port: number);
|
|
7
|
-
startAndRun<TRouter extends AnyRouter>(appRouter: TRouter, config: TestServerConfig): Promise<void>;
|
|
8
|
-
}
|
|
2
|
+
export { startTestServer } from "./server";
|
|
3
|
+
export { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile";
|
|
4
|
+
export type { TestServerConfig } from "./updateTestdirectorySchemaFile";
|
package/dist/src/server/index.js
CHANGED
|
@@ -1,49 +1,4 @@
|
|
|
1
|
-
import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
|
2
1
|
import "core-js/proposals/async-explicit-resource-management";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile";
|
|
7
|
-
export class TestServer {
|
|
8
|
-
port;
|
|
9
|
-
constructor(port) {
|
|
10
|
-
this.port = port;
|
|
11
|
-
}
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
13
|
-
async startAndRun(appRouter, config) {
|
|
14
|
-
console.log("🚀 Server starting");
|
|
15
|
-
await updateTestdirectorySchemaFile(config);
|
|
16
|
-
const wss = new WebSocketServer({ port: this.port });
|
|
17
|
-
const handler = applyWSSHandler({
|
|
18
|
-
wss,
|
|
19
|
-
router: appRouter,
|
|
20
|
-
createContext,
|
|
21
|
-
// Enable heartbeat messages to keep connection open (disabled by default)
|
|
22
|
-
keepAlive: {
|
|
23
|
-
enabled: true,
|
|
24
|
-
// server ping message interval in milliseconds
|
|
25
|
-
pingMs: 30_000,
|
|
26
|
-
// connection is terminated if pong message is not received in this many milliseconds
|
|
27
|
-
pongWaitMs: 5000,
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
wss.on("connection", socket => {
|
|
31
|
-
console.log(`➕➕ Connection (${wss.clients.size})`);
|
|
32
|
-
socket.once("close", () => {
|
|
33
|
-
console.log(`➖➖ Connection (${wss.clients.size})`);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
console.log(`✅ WebSocket Server listening on ws://localhost:${this.port}`);
|
|
37
|
-
await Promise.race([once(process, "SIGTERM"), once(process, "SIGINT")]);
|
|
38
|
-
console.log("Shutting down...");
|
|
39
|
-
handler.broadcastReconnectNotification();
|
|
40
|
-
wss.close(error => {
|
|
41
|
-
if (error) {
|
|
42
|
-
console.error("Error closing WebSocket server", error);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
console.log("WebSocket server closed");
|
|
46
|
-
process.exit(0);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
2
|
+
// This is the public server api. Semantic versioning will be applied to this.
|
|
3
|
+
export { startTestServer } from "./server";
|
|
4
|
+
export { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs, { rmdirSync } from "fs";
|
|
2
|
+
import nodePath from "path";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
import { createTempDir } from "./createTempDir";
|
|
5
|
+
class TempDirectory {
|
|
6
|
+
path;
|
|
7
|
+
constructor(path) {
|
|
8
|
+
this.path = path;
|
|
9
|
+
}
|
|
10
|
+
static create() {
|
|
11
|
+
const tmp = fs.mkdtempSync("test-temp-dir-");
|
|
12
|
+
return new TempDirectory(tmp);
|
|
13
|
+
}
|
|
14
|
+
[Symbol.dispose]() {
|
|
15
|
+
rmdirSync(this.path, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
it("should create a temp dir with no contents", async () => {
|
|
19
|
+
// typically the user will want to have contents, but this should not be an error
|
|
20
|
+
await using dir = TempDirectory.create();
|
|
21
|
+
const result = await createTempDir({
|
|
22
|
+
testEnvironmentPath: dir.path,
|
|
23
|
+
outputFilePath: nodePath.join(dir.path, "MyTestDirectory.ts"),
|
|
24
|
+
});
|
|
25
|
+
expect(result.contents).toEqual({});
|
|
26
|
+
expect(result.testEnvironmentPath).toEqual(dir.path);
|
|
27
|
+
expect(result.testEnvironmentPath.startsWith("test-temp-dir-")).toBeTruthy();
|
|
28
|
+
expect(result.testEnvironmentPathRelative.startsWith("testdirs")).toBeTruthy();
|
|
29
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { inferRouterInputs } from "@trpc/server";
|
|
2
|
-
import { TestServer } from "
|
|
2
|
+
import { TestServer } from "./TestServer";
|
|
3
3
|
import type { TestServerConfig } from "./updateTestdirectorySchemaFile";
|
|
4
4
|
/** Stack for managing resources that need to be disposed of when the server
|
|
5
5
|
* shuts down */
|
|
@@ -35,7 +35,7 @@ declare function createAppRouter(config: TestServerConfig): import("@trpc/server
|
|
|
35
35
|
};
|
|
36
36
|
output: import("./types").TestDirectory;
|
|
37
37
|
}>;
|
|
38
|
-
onStdout: import("@trpc/server").
|
|
38
|
+
onStdout: import("@trpc/server/unstable-core-do-not-import").LegacyObservableSubscriptionProcedure<{
|
|
39
39
|
input: {
|
|
40
40
|
client: {
|
|
41
41
|
tabId: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { TestServer } from ".";
|
|
3
2
|
import { trpc } from "./connection/trpc";
|
|
4
3
|
import * as neovim from "./neovim";
|
|
4
|
+
import { TestServer } from "./TestServer";
|
|
5
5
|
import { tabIdSchema } from "./utilities/tabId";
|
|
6
6
|
/** Stack for managing resources that need to be disposed of when the server
|
|
7
7
|
* shuts down */
|