@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.
@@ -1,6 +1,6 @@
1
1
  @font-face {
2
2
  font-family: "DejaVuSansMNerdFontMono";
3
- src: url("../public/DejaVuSansMNerdFontMono-Regular.ttf");
3
+ src: url("./public/DejaVuSansMNerdFontMono-Regular.ttf");
4
4
  font-weight: normal;
5
5
  font-style: normal;
6
6
  }
@@ -1,12 +1,8 @@
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"
6
5
  import z from "zod"
7
- import type { StartNeovimGenericArguments } from "../server/neovim/NeovimApplication.ts"
8
- import type { AppRouter } from "../server/server.ts"
9
- import type { TestDirectory } from "../server/types.ts"
10
6
  import type { TabId } from "../server/utilities/tabId.ts"
11
7
  import "./style.css"
12
8
  import { validateMouseEvent } from "./validateMouseEvent"
@@ -90,68 +86,3 @@ export function getTabId(): TabId {
90
86
 
91
87
  return { tabId }
92
88
  }
93
-
94
- export class NeovimClient {
95
- private readonly ready: Promise<void>
96
- private readonly tabId: { tabId: string }
97
- private readonly terminal: Terminal
98
- private readonly trpc: ReturnType<typeof createTRPCClient<AppRouter>>
99
-
100
- constructor(app: HTMLElement) {
101
- const wsClient = createWSClient({ url: `ws://localhost:3000`, WebSocket })
102
- const trpc = createTRPCClient<AppRouter>({
103
- links: [wsLink({ client: wsClient })],
104
- })
105
- this.trpc = trpc
106
-
107
- this.tabId = getTabId()
108
- const tabId = this.tabId
109
-
110
- const terminal = startTerminal(app, {
111
- onMouseEvent(data: string) {
112
- void trpc.neovim.sendStdin.mutate({ tabId, data }).catch((error: unknown) => {
113
- console.error(`Error sending mouse event`, error)
114
- })
115
- },
116
- onKeyPress(event) {
117
- void trpc.neovim.sendStdin.mutate({ tabId, data: event.key })
118
- },
119
- })
120
- this.terminal = terminal
121
-
122
- // start listening to Neovim stdout - this will take some (short) amount of
123
- // time to complete
124
- this.ready = new Promise<void>(resolve => {
125
- console.log("Subscribing to Neovim stdout")
126
- trpc.neovim.onStdout.subscribe(
127
- { client: tabId },
128
- {
129
- onStarted() {
130
- resolve()
131
- },
132
- onData(data: string) {
133
- terminal.write(data)
134
- },
135
- onError(err: unknown) {
136
- console.error(`Error from Neovim`, err)
137
- },
138
- }
139
- )
140
- })
141
- }
142
-
143
- public async startNeovim(args: StartNeovimGenericArguments): Promise<TestDirectory> {
144
- await this.ready
145
-
146
- const neovim = await this.trpc.neovim.start.mutate({
147
- startNeovimArguments: args,
148
- tabId: this.tabId,
149
- terminalDimensions: {
150
- cols: this.terminal.cols,
151
- rows: this.terminal.rows,
152
- },
153
- })
154
-
155
- return neovim
156
- }
157
- }
@@ -0,0 +1,54 @@
1
+ import type { AnyTRPCRouter } from "@trpc/server"
2
+ import { applyWSSHandler } from "@trpc/server/adapters/ws"
3
+ import "core-js/proposals/async-explicit-resource-management"
4
+ import { once } from "events"
5
+ import { WebSocketServer } from "ws"
6
+ import { createContext } from "./connection/trpc"
7
+ import type { TestServerConfig } from "./updateTestdirectorySchemaFile"
8
+ import { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile"
9
+
10
+ export class TestServer {
11
+ public constructor(private readonly port: number) {}
12
+
13
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
14
+ public async startAndRun<TRouter extends AnyTRPCRouter>(appRouter: TRouter, config: TestServerConfig): Promise<void> {
15
+ console.log("🚀 Server starting")
16
+
17
+ await updateTestdirectorySchemaFile(config)
18
+
19
+ const wss = new WebSocketServer({ port: this.port })
20
+ const handler = applyWSSHandler<TRouter>({
21
+ wss,
22
+ router: appRouter,
23
+ createContext,
24
+ // Enable heartbeat messages to keep connection open (disabled by default)
25
+ keepAlive: {
26
+ enabled: true,
27
+ // server ping message interval in milliseconds
28
+ pingMs: 30_000,
29
+ // connection is terminated if pong message is not received in this many milliseconds
30
+ pongWaitMs: 5000,
31
+ },
32
+ })
33
+
34
+ wss.on("connection", socket => {
35
+ console.log(`➕➕ Connection (${wss.clients.size})`)
36
+ socket.once("close", () => {
37
+ console.log(`➖➖ Connection (${wss.clients.size})`)
38
+ })
39
+ })
40
+ console.log(`✅ WebSocket Server listening on ws://localhost:${this.port}`)
41
+
42
+ await Promise.race([once(process, "SIGTERM"), once(process, "SIGINT")])
43
+ console.log("Shutting down...")
44
+ handler.broadcastReconnectNotification()
45
+ wss.close(error => {
46
+ if (error) {
47
+ console.error("Error closing WebSocket server", error)
48
+ process.exit(1)
49
+ }
50
+ console.log("WebSocket server closed")
51
+ process.exit(0)
52
+ })
53
+ }
54
+ }
@@ -54,13 +54,12 @@ export function convertDree(root: Dree): TreeNode {
54
54
  }
55
55
  }
56
56
 
57
- assert(root.children, `Expected children for directory node ${root.name}`)
58
57
  const node: DirectoryNode = {
59
58
  name: root.name,
60
59
  type: root.type,
61
60
  contents: {},
62
61
  }
63
- for (const child of root.children) {
62
+ for (const child of root.children || []) {
64
63
  node.contents[child.name] = convertDree(child)
65
64
  }
66
65
 
@@ -1,54 +1,7 @@
1
- import type { AnyRouter } from "@trpc/server"
2
- import { applyWSSHandler } from "@trpc/server/adapters/ws"
3
1
  import "core-js/proposals/async-explicit-resource-management"
4
- import { once } from "events"
5
- import { WebSocketServer } from "ws"
6
- import { createContext } from "./connection/trpc"
7
- import type { TestServerConfig } from "./updateTestdirectorySchemaFile"
8
- import { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile"
9
2
 
10
- export class TestServer {
11
- public constructor(private readonly port: number) {}
3
+ // This is the public server api. Semantic versioning will be applied to this.
12
4
 
13
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
14
- public async startAndRun<TRouter extends AnyRouter>(appRouter: TRouter, config: TestServerConfig): Promise<void> {
15
- console.log("🚀 Server starting")
16
-
17
- await updateTestdirectorySchemaFile(config)
18
-
19
- const wss = new WebSocketServer({ port: this.port })
20
- const handler = applyWSSHandler<TRouter>({
21
- wss,
22
- router: appRouter,
23
- createContext,
24
- // Enable heartbeat messages to keep connection open (disabled by default)
25
- keepAlive: {
26
- enabled: true,
27
- // server ping message interval in milliseconds
28
- pingMs: 30_000,
29
- // connection is terminated if pong message is not received in this many milliseconds
30
- pongWaitMs: 5000,
31
- },
32
- })
33
-
34
- wss.on("connection", socket => {
35
- console.log(`➕➕ Connection (${wss.clients.size})`)
36
- socket.once("close", () => {
37
- console.log(`➖➖ Connection (${wss.clients.size})`)
38
- })
39
- })
40
- console.log(`✅ WebSocket Server listening on ws://localhost:${this.port}`)
41
-
42
- await Promise.race([once(process, "SIGTERM"), once(process, "SIGINT")])
43
- console.log("Shutting down...")
44
- handler.broadcastReconnectNotification()
45
- wss.close(error => {
46
- if (error) {
47
- console.error("Error closing WebSocket server", error)
48
- process.exit(1)
49
- }
50
- console.log("WebSocket server closed")
51
- process.exit(0)
52
- })
53
- }
54
- }
5
+ export { startTestServer } from "./server"
6
+ export { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile"
7
+ export type { TestServerConfig } from "./updateTestdirectorySchemaFile"
@@ -0,0 +1,34 @@
1
+ import fs, { rmdirSync } from "fs"
2
+ import nodePath from "path"
3
+ import { expect, it } from "vitest"
4
+ import type { TestDirsPath } from "./createTempDir"
5
+ import { createTempDir } from "./createTempDir"
6
+
7
+ type TestTempDirPrefix = "test-temp-dir-"
8
+
9
+ class TempDirectory implements Disposable {
10
+ constructor(public readonly path: string) {}
11
+
12
+ public static create(): TempDirectory {
13
+ const tmp = fs.mkdtempSync("test-temp-dir-" satisfies TestTempDirPrefix)
14
+ return new TempDirectory(tmp)
15
+ }
16
+
17
+ [Symbol.dispose](): void {
18
+ rmdirSync(this.path, { recursive: true })
19
+ }
20
+ }
21
+
22
+ it("should create a temp dir with no contents", async () => {
23
+ // typically the user will want to have contents, but this should not be an error
24
+ await using dir = TempDirectory.create()
25
+ const result = await createTempDir({
26
+ testEnvironmentPath: dir.path,
27
+ outputFilePath: nodePath.join(dir.path, "MyTestDirectory.ts"),
28
+ })
29
+
30
+ expect(result.contents).toEqual({})
31
+ expect(result.testEnvironmentPath).toEqual(dir.path)
32
+ expect(result.testEnvironmentPath.startsWith("test-temp-dir-" satisfies TestTempDirPrefix)).toBeTruthy()
33
+ expect(result.testEnvironmentPathRelative.startsWith("testdirs" satisfies TestDirsPath)).toBeTruthy()
34
+ })
@@ -14,7 +14,7 @@ export async function createTempDir(config: TestServerConfig): Promise<TestDirec
14
14
  const dir = await createUniqueDirectory(config.testEnvironmentPath)
15
15
 
16
16
  readdirSync(config.testEnvironmentPath).forEach(entry => {
17
- if (entry === "testdirs") return
17
+ if (entry === ("testdirs" satisfies TestDirsPath)) return
18
18
  if (entry === ".repro") return
19
19
 
20
20
  execSync(`cp -a '${path.join(config.testEnvironmentPath, entry)}' ${dir}/`)
@@ -37,8 +37,10 @@ export async function createTempDir(config: TestServerConfig): Promise<TestDirec
37
37
  }
38
38
  }
39
39
 
40
+ export type TestDirsPath = "testdirs"
41
+
40
42
  async function createUniqueDirectory(testEnvironmentPath: string): Promise<string> {
41
- const testdirs = path.join(testEnvironmentPath, "testdirs")
43
+ const testdirs = path.join(testEnvironmentPath, "testdirs" satisfies TestDirsPath)
42
44
  try {
43
45
  await access(testdirs, constants.F_OK)
44
46
  } catch {
@@ -1,8 +1,8 @@
1
1
  import type { inferRouterInputs } from "@trpc/server"
2
2
  import { z } from "zod"
3
- import { TestServer } from "."
4
3
  import { trpc } from "./connection/trpc"
5
4
  import * as neovim from "./neovim"
5
+ import { TestServer } from "./TestServer"
6
6
  import type { TestServerConfig } from "./updateTestdirectorySchemaFile"
7
7
  import { tabIdSchema } from "./utilities/tabId"
8
8