@tui-sandbox/library 11.11.1 → 12.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.
Files changed (92) hide show
  1. package/dist/browser/assets/index-CYUPHpRk.css +1 -0
  2. package/dist/browser/assets/index-DPQpUaDL.js +9 -0
  3. package/dist/browser/index.html +2 -2
  4. package/dist/src/browser/neovim-client.d.ts +2 -0
  5. package/dist/src/browser/neovim-client.js +2 -0
  6. package/dist/src/browser/neovim-client.js.map +1 -1
  7. package/dist/src/client/index.d.ts +1 -0
  8. package/dist/src/client/neovim-terminal-client.d.ts +0 -2
  9. package/dist/src/client/neovim-terminal-client.js +0 -2
  10. package/dist/src/client/neovim-terminal-client.js.map +1 -1
  11. package/dist/src/client/startTerminal.d.ts +0 -2
  12. package/dist/src/client/startTerminal.js +0 -2
  13. package/dist/src/client/startTerminal.js.map +1 -1
  14. package/dist/src/client/terminal-terminal-client.d.ts +0 -1
  15. package/dist/src/client/terminal-terminal-client.js +0 -1
  16. package/dist/src/client/terminal-terminal-client.js.map +1 -1
  17. package/dist/src/server/cypress-support/contents.js +9 -11
  18. package/dist/src/server/cypress-support/contents.js.map +1 -1
  19. package/dist/src/server/index.d.ts +4 -0
  20. package/dist/src/server/index.js.map +1 -1
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/package.json +35 -9
  23. package/CHANGELOG.md +0 -933
  24. package/dist/browser/assets/index-BQzArJW3.js +0 -9
  25. package/dist/browser/assets/index-hkmOP7rU.css +0 -1
  26. package/index.html +0 -11
  27. package/src/browser/neovim-client.ts +0 -126
  28. package/src/client/MyNeovimConfigModification.test.ts +0 -25
  29. package/src/client/MyNeovimConfigModification.ts +0 -8
  30. package/src/client/color-utilities.test.ts +0 -10
  31. package/src/client/color-utilities.ts +0 -9
  32. package/src/client/cypress-assertions.ts +0 -35
  33. package/src/client/index.ts +0 -4
  34. package/src/client/neovim-terminal-client.ts +0 -130
  35. package/src/client/public/DejaVuSansMNerdFontMono-Regular.ttf +0 -0
  36. package/src/client/startTerminal.ts +0 -97
  37. package/src/client/style.css +0 -32
  38. package/src/client/terminal-config.ts +0 -25
  39. package/src/client/terminal-terminal-client.ts +0 -106
  40. package/src/client/validateMouseEvent.ts +0 -22
  41. package/src/scripts/commands/commandRun.ts +0 -68
  42. package/src/scripts/commands/commandTuiNeovimExec.ts +0 -36
  43. package/src/scripts/commands/commandTuiNeovimPrepare.ts +0 -12
  44. package/src/scripts/commands/commandTuiStart.ts +0 -36
  45. package/src/scripts/parseArguments.test.ts +0 -28
  46. package/src/scripts/parseArguments.ts +0 -66
  47. package/src/scripts/resolveConfig.test.ts +0 -78
  48. package/src/scripts/resolveTuiConfig.ts +0 -65
  49. package/src/scripts/tui.ts +0 -77
  50. package/src/server/TestServer.ts +0 -73
  51. package/src/server/applications/neovim/NeovimApplication.ts +0 -236
  52. package/src/server/applications/neovim/NeovimJavascriptApiClient.test.ts +0 -48
  53. package/src/server/applications/neovim/NeovimJavascriptApiClient.ts +0 -68
  54. package/src/server/applications/neovim/api.ts +0 -257
  55. package/src/server/applications/neovim/environment/TempDirectory.ts +0 -18
  56. package/src/server/applications/neovim/environment/createTempDir.test.ts +0 -29
  57. package/src/server/applications/neovim/environment/createTempDir.ts +0 -91
  58. package/src/server/applications/neovim/neovimRouter.ts +0 -100
  59. package/src/server/applications/neovim/prepareNewTestDirectory.test.ts +0 -32
  60. package/src/server/applications/neovim/prepareNewTestDirectory.ts +0 -21
  61. package/src/server/applications/terminal/TerminalTestApplication.ts +0 -101
  62. package/src/server/applications/terminal/api.ts +0 -89
  63. package/src/server/applications/terminal/runBlockingShellCommand.test.ts +0 -41
  64. package/src/server/applications/terminal/runBlockingShellCommand.ts +0 -64
  65. package/src/server/applications/terminal/terminalRouter.ts +0 -47
  66. package/src/server/blockingCommandInputSchema.test.ts +0 -24
  67. package/src/server/blockingCommandInputSchema.ts +0 -27
  68. package/src/server/config.test.ts +0 -7
  69. package/src/server/config.ts +0 -18
  70. package/src/server/connection/trpc.ts +0 -3
  71. package/src/server/cypress-support/contents.ts +0 -245
  72. package/src/server/cypress-support/createCypressSupportFile.test.ts +0 -69
  73. package/src/server/cypress-support/createCypressSupportFile.ts +0 -56
  74. package/src/server/dirtree/index.test.ts +0 -352
  75. package/src/server/dirtree/index.ts +0 -144
  76. package/src/server/dirtree/json-to-zod.ts +0 -60
  77. package/src/server/index.ts +0 -6
  78. package/src/server/server.ts +0 -34
  79. package/src/server/types.ts +0 -98
  80. package/src/server/updateTestdirectorySchemaFile.test.ts +0 -49
  81. package/src/server/updateTestdirectorySchemaFile.ts +0 -71
  82. package/src/server/utilities/DisposableSingleApplication.test.ts +0 -92
  83. package/src/server/utilities/DisposableSingleApplication.ts +0 -49
  84. package/src/server/utilities/Lazy.ts +0 -16
  85. package/src/server/utilities/TerminalApplication.ts +0 -100
  86. package/src/server/utilities/generator.test.ts +0 -50
  87. package/src/server/utilities/generator.ts +0 -12
  88. package/src/server/utilities/tabId.ts +0 -4
  89. package/src/server/utilities/timeout.ts +0 -3
  90. package/src/server/utilities/timeoutable.ts +0 -19
  91. package/tsconfig.json +0 -31
  92. package/vite.config.js +0 -27
@@ -1,91 +0,0 @@
1
- import assert from "assert"
2
- import { execSync } from "child_process"
3
- import { constants, readdirSync, statSync } from "fs"
4
- import { access, mkdir, mkdtemp, symlink } from "fs/promises"
5
- import path from "path"
6
- import { debuglog } from "util"
7
- import { convertDree, getDirectoryTree } from "../../../dirtree/index.js"
8
- import type { TestDirectory } from "../../../types.js"
9
- import type { TestServerConfig } from "../../../updateTestdirectorySchemaFile.js"
10
- import { updateTestdirectorySchemaFile } from "../../../updateTestdirectorySchemaFile.js"
11
-
12
- const log = debuglog("tui-sandbox.createTempDir")
13
-
14
- export async function createTempDir(config: TestServerConfig): Promise<TestDirectory> {
15
- try {
16
- // before calling this function, the testEnvironmentPath should already exist
17
- statSync(config.directories.testEnvironmentPath)
18
- const dir = await createUniqueDirectory(config.directories.testEnvironmentPath)
19
-
20
- readdirSync(config.directories.testEnvironmentPath).forEach(entry => {
21
- if (entry === ("testdirs" satisfies TestDirsPath)) return
22
- if (entry === ".repro") return
23
-
24
- execSync(`cp -R '${path.join(config.directories.testEnvironmentPath, entry)}' ${dir}/`)
25
- })
26
- log(`Created test directory at ${dir}`)
27
-
28
- const tree = convertDree(getDirectoryTree(dir).dree)
29
- assert(tree.type === "directory")
30
-
31
- const [_, latestEnvironmentSymlink] = await Promise.all([
32
- updateTestdirectorySchemaFile(config),
33
- createLatestSymlink(config, dir),
34
- ])
35
- return {
36
- rootPathAbsolute: dir,
37
- contents: tree.contents,
38
- testEnvironmentPath: config.directories.testEnvironmentPath,
39
- testEnvironmentPathRelative: path.relative(config.directories.testEnvironmentPath, dir),
40
- latestEnvironmentSymlink,
41
- }
42
- } catch (err) {
43
- console.error(err)
44
- throw err
45
- }
46
- }
47
-
48
- export type TestDirsPath = "testdirs"
49
-
50
- async function createUniqueDirectory(testEnvironmentPath: string): Promise<string> {
51
- const testdirs = path.join(testEnvironmentPath, "testdirs" satisfies TestDirsPath)
52
- try {
53
- await access(testdirs, constants.F_OK)
54
- } catch {
55
- await mkdir(testdirs)
56
- }
57
- const dir = await mkdtemp(path.join(testdirs, "dir-"))
58
- assert(typeof dir === "string")
59
-
60
- return dir
61
- }
62
-
63
- async function createLatestSymlink(config: TestServerConfig, uniqueTestDirectory: string): Promise<string> {
64
- const latestSymlinkPath = path.join(
65
- config.directories.testEnvironmentPath,
66
- "testdirs" satisfies TestDirsPath,
67
- config.directories.latestSymlinkName
68
- )
69
- try {
70
- await access(latestSymlinkPath, constants.F_OK)
71
- // If it already exists, remove it
72
- execSync(`rm -f '${latestSymlinkPath}'`)
73
- } catch {
74
- // It doesn't exist, that's fine
75
- }
76
-
77
- // recreate it
78
- await symlink(uniqueTestDirectory, latestSymlinkPath, "junction")
79
-
80
- return latestSymlinkPath
81
- }
82
-
83
- export async function removeTestDirectories(testEnvironmentPath: string): Promise<void> {
84
- try {
85
- const testdirs = path.join(testEnvironmentPath, "testdirs" satisfies TestDirsPath)
86
- await access(testdirs, constants.F_OK)
87
- execSync(`rm -rf ${testdirs}/*`)
88
- } catch (e) {
89
- console.debug("Could not remove test directories, maybe they don't exist yet", e)
90
- }
91
- }
@@ -1,100 +0,0 @@
1
- import type { Except } from "type-fest"
2
- import * as z from "zod"
3
- import { blockingCommandInputSchema } from "../../blockingCommandInputSchema.js"
4
- import { trpc } from "../../connection/trpc.js"
5
- import { serverTestDirectorySchema } from "../../types.js"
6
- import type { NeovimIntegrationDefaultAppName, TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
7
- import { tabIdSchema } from "../../utilities/tabId.js"
8
- import { timeoutable } from "../../utilities/timeoutable.js"
9
- import * as neovim from "./api.js"
10
-
11
- const luaCodeInputSchema = z.object({ tabId: tabIdSchema, luaCode: z.string() })
12
- export type LuaCodeClientInput = Except<LuaCodeInput, "tabId">
13
- export type LuaCodeInput = z.infer<typeof luaCodeInputSchema>
14
-
15
- const pollLuaCodeInputSchema = z.object({
16
- tabId: tabIdSchema,
17
- luaAssertion: z.string(),
18
- timeoutMs: z.number().optional().default(10_000),
19
- })
20
- export type PollLuaCodeClientInput = Except<z.input<typeof pollLuaCodeInputSchema>, "tabId">
21
-
22
- const exCommandInputSchema = z.object({
23
- tabId: tabIdSchema,
24
- command: z.string(),
25
- log: z.boolean().optional(),
26
- })
27
- export type ExCommandClientInput = Except<ExCommandInput, "tabId">
28
- export type ExCommandInput = z.infer<typeof exCommandInputSchema>
29
-
30
- const runLuaFileInputSchema = z.object({
31
- tabId: tabIdSchema,
32
- luaFile: z.string(),
33
- log: z.boolean().optional(),
34
- })
35
- export type RunLuaFileInput = z.output<typeof runLuaFileInputSchema>
36
- export type RunLuaFileClientInput = Except<RunLuaFileInput, "tabId">
37
-
38
- // let trpc infer the type as that is what it is designed to do
39
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
40
- export function createNeovimRouter(config: TestServerConfig) {
41
- return trpc.router({
42
- start: trpc.procedure
43
- .input(
44
- z.object({
45
- tabId: tabIdSchema,
46
- startNeovimArguments: z.object({
47
- filename: z.union([
48
- z.string(),
49
- z.object({
50
- openInVerticalSplits: z.array(z.string()),
51
- }),
52
- ]),
53
- startupScriptModifications: z.array(z.string()).optional(),
54
- additionalEnvironmentVariables: z.record(z.string(), z.string()).optional(),
55
- NVIM_APPNAME: z
56
- .string()
57
- .optional()
58
- .default("nvim" satisfies NeovimIntegrationDefaultAppName),
59
- }),
60
- terminalDimensions: z.object({
61
- cols: z.number(),
62
- rows: z.number(),
63
- }),
64
- })
65
- )
66
- .output(serverTestDirectorySchema)
67
- .mutation(async options => {
68
- return neovim.start(
69
- options.input.startNeovimArguments,
70
- options.input.terminalDimensions,
71
- options.input.tabId,
72
- config
73
- )
74
- }),
75
-
76
- onStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
77
- return neovim.initializeStdout(options.input, options.signal, config.directories.testEnvironmentPath)
78
- }),
79
- sendStdin: trpc.procedure.input(z.object({ tabId: tabIdSchema, data: z.string() })).mutation(options => {
80
- return neovim.sendStdin(options.input)
81
- }),
82
-
83
- runBlockingShellCommand: trpc.procedure.input(blockingCommandInputSchema).mutation(async options => {
84
- return neovim.runBlockingShellCommand(options.signal, options.input, options.input.allowFailure ?? false)
85
- }),
86
-
87
- runLuaCode: trpc.procedure.input(luaCodeInputSchema).mutation(options => {
88
- return timeoutable(10_000, neovim.runLuaCode(options.input))
89
- }),
90
-
91
- waitForLuaCode: trpc.procedure.input(pollLuaCodeInputSchema).mutation(async options => {
92
- const result = await timeoutable(options.input.timeoutMs, neovim.waitForLuaCode(options.input, options.signal))
93
- return result
94
- }),
95
-
96
- runExCommand: trpc.procedure.input(exCommandInputSchema).mutation(options => {
97
- return timeoutable(10_000, neovim.runExCommand(options.input))
98
- }),
99
- })
100
- }
@@ -1,32 +0,0 @@
1
- import { rm } from "fs/promises"
2
- import path from "path"
3
- import type { PartialDeep } from "type-fest"
4
- import { assert, describe, expect, it } from "vitest"
5
- import { createDefaultConfig } from "../../config.js"
6
- import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
7
- import { prepareNewTestDirectory } from "./prepareNewTestDirectory.js"
8
-
9
- describe("prepareNewTestDirectory when the testEnvironmentPath does not exist", () => {
10
- it("should be able to create a new test directory", async () => {
11
- // this happens when starting a new project, and the directory structure
12
- // has not been created yet
13
- const testEnvironmentPath = "/tmp/test"
14
- const testDirectory = await prepareNewTestDirectory({
15
- ...createDefaultConfig("cwd", {}),
16
- directories: {
17
- outputFilePath: path.join(testEnvironmentPath, "foo.ts"),
18
- testEnvironmentPath,
19
- latestSymlinkName: "latest",
20
- },
21
- } satisfies PartialDeep<TestServerConfig>)
22
-
23
- try {
24
- expect(testDirectory.contents).toEqual({})
25
- expect(testDirectory.testEnvironmentPath).toEqual("/tmp/test")
26
- } finally {
27
- // for safety, only remove inside /tmp
28
- assert(testEnvironmentPath.startsWith("/tmp/"))
29
- await rm(testEnvironmentPath, { recursive: true })
30
- }
31
- })
32
- })
@@ -1,21 +0,0 @@
1
- import { access, mkdir } from "fs/promises"
2
- import { debuglog } from "util"
3
- import type { TestDirectory } from "../../types.js"
4
- import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
5
- import { createTempDir, removeTestDirectories } from "./environment/createTempDir.js"
6
-
7
- const log = debuglog("tui-sandbox.neovim.prepareNewTestDirectory")
8
-
9
- export async function prepareNewTestDirectory(config: TestServerConfig): Promise<TestDirectory> {
10
- try {
11
- // if the directory does not exist, create it
12
- await access(config.directories.testEnvironmentPath)
13
- } catch {
14
- log(`Creating testEnvironmentPath directory at ${config.directories.testEnvironmentPath}`)
15
- await mkdir(config.directories.testEnvironmentPath, { recursive: true })
16
- }
17
-
18
- await removeTestDirectories(config.directories.testEnvironmentPath)
19
- const testDirectory = await createTempDir(config)
20
- return testDirectory
21
- }
@@ -1,101 +0,0 @@
1
- import assert from "assert"
2
- import { exec } from "child_process"
3
- import EventEmitter from "events"
4
- import { join } from "path"
5
- import { debuglog } from "util"
6
- import type { TestDirectory, TestEnvironmentCommonEnvironmentVariables } from "../../types.js"
7
- import { DisposableSingleApplication } from "../../utilities/DisposableSingleApplication.js"
8
- import { TerminalApplication } from "../../utilities/TerminalApplication.js"
9
- import type { StdoutOrStderrMessage, TerminalDimensions } from "../neovim/NeovimApplication.js"
10
-
11
- export type { StdoutOrStderrMessage }
12
-
13
- const log = debuglog("tui-sandbox.terminal.TerminalTestApplication")
14
-
15
- type ResettableState = {
16
- testDirectory: TestDirectory
17
- }
18
-
19
- export type StartTerminalGenericArguments = {
20
- commandToRun: string[]
21
- additionalEnvironmentVariables?: Record<string, string> | undefined
22
- }
23
-
24
- export default class TerminalTestApplication implements AsyncDisposable {
25
- public state: ResettableState | undefined
26
- public readonly events: EventEmitter
27
-
28
- public constructor(public readonly application: DisposableSingleApplication = new DisposableSingleApplication()) {
29
- this.events = new EventEmitter()
30
- }
31
-
32
- public async startNextAndKillCurrent(
33
- testDirectory: TestDirectory,
34
- startArgs: StartTerminalGenericArguments,
35
- terminalDimensions: TerminalDimensions
36
- ): Promise<void> {
37
- await this[Symbol.asyncDispose]()
38
- assert(
39
- this.state === undefined,
40
- "TerminalTestApplication state should be undefined after disposing so that no previous state is reused."
41
- )
42
-
43
- const command = startArgs.commandToRun[0]
44
- assert(command, "No command to run was provided.")
45
- // TODO could check if the command is executable
46
- const terminalArguments = startArgs.commandToRun.slice(1)
47
-
48
- const stdout = this.events
49
-
50
- await this.application.startNextAndKillCurrent(async () => {
51
- const env = this.getEnvironmentVariables(testDirectory, startArgs.additionalEnvironmentVariables)
52
- return TerminalApplication.start({
53
- command,
54
- args: terminalArguments,
55
-
56
- cwd: testDirectory.rootPathAbsolute,
57
- env,
58
- dimensions: terminalDimensions,
59
-
60
- onStdoutOrStderr(data) {
61
- data satisfies string
62
- stdout.emit("stdout" satisfies StdoutOrStderrMessage, data)
63
- },
64
- })
65
- })
66
-
67
- const processId = this.application.processId()
68
- assert(
69
- processId !== undefined,
70
- "TerminalApplication was started without a process ID. This is a bug - please open an issue."
71
- )
72
-
73
- this.state = { testDirectory }
74
-
75
- log(`🚀 Started Terminal instance ${processId}`)
76
- }
77
-
78
- public getEnvironmentVariables(
79
- testDirectory: TestDirectory,
80
- additionalEnvironmentVariables?: Record<string, string>
81
- ): NodeJS.ProcessEnv {
82
- return {
83
- ...process.env,
84
- HOME: testDirectory.rootPathAbsolute,
85
- XDG_CONFIG_HOME: join(testDirectory.rootPathAbsolute, ".config"),
86
- XDG_DATA_HOME: join(testDirectory.testEnvironmentPath, ".repro", "data"),
87
- TUI_SANDBOX_TEST_ENVIRONMENT_PATH: testDirectory.testEnvironmentPath,
88
- ...additionalEnvironmentVariables,
89
- } satisfies TestEnvironmentCommonEnvironmentVariables
90
- }
91
-
92
- async [Symbol.asyncDispose](): Promise<void> {
93
- await this.application[Symbol.asyncDispose]()
94
-
95
- if (!this.state) return
96
-
97
- exec(`rm -rf ${this.state.testDirectory.rootPathAbsolute}`)
98
-
99
- this.state = undefined
100
- }
101
- }
@@ -1,89 +0,0 @@
1
- import assert from "assert"
2
- import type { BlockingCommandInput } from "../../blockingCommandInputSchema.js"
3
- import type { BlockingShellCommandOutput, TestDirectory } from "../../types.js"
4
- import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
5
- import { convertEventEmitterToAsyncGenerator } from "../../utilities/generator.js"
6
- import { Lazy } from "../../utilities/Lazy.js"
7
- import type { TabId } from "../../utilities/tabId.js"
8
- import { prepareNewTestDirectory } from "../neovim/prepareNewTestDirectory.js"
9
- import { executeBlockingShellCommand } from "./runBlockingShellCommand.js"
10
- import type { StartTerminalInput } from "./terminalRouter.js"
11
- import type { StdoutOrStderrMessage } from "./TerminalTestApplication.js"
12
- import TerminalTestApplication from "./TerminalTestApplication.js"
13
-
14
- const terminals = new Map<TabId["tabId"], TerminalTestApplication>()
15
- const resources: Lazy<AsyncDisposableStack> = new Lazy(() => {
16
- return new AsyncDisposableStack()
17
- })
18
-
19
- export async function start(
20
- { tabId, startTerminalArguments }: StartTerminalInput,
21
- config: TestServerConfig
22
- ): Promise<TestDirectory> {
23
- const app = terminals.get(tabId.tabId)
24
- assert(app, `Terminal with tabId ${tabId.tabId} not found.`)
25
- const testDirectory = await prepareNewTestDirectory(config)
26
- await app.startNextAndKillCurrent(
27
- testDirectory,
28
- {
29
- commandToRun: startTerminalArguments.commandToRun,
30
- additionalEnvironmentVariables: startTerminalArguments.additionalEnvironmentVariables,
31
- },
32
- startTerminalArguments.terminalDimensions
33
- )
34
-
35
- return testDirectory
36
- }
37
-
38
- export async function initializeStdout(
39
- options: { client: TabId },
40
- signal: AbortSignal | undefined
41
- ): Promise<AsyncGenerator<string, void, unknown>> {
42
- const tabId = options.client.tabId
43
- const app = terminals.get(tabId) ?? new TerminalTestApplication()
44
- if (terminals.get(tabId) === undefined) {
45
- terminals.set(tabId, app)
46
- resources.get().use(app)
47
- }
48
-
49
- const stdout = convertEventEmitterToAsyncGenerator(app.events, "stdout" satisfies StdoutOrStderrMessage)
50
- signal?.addEventListener("abort", () => {
51
- void app[Symbol.asyncDispose]().finally(() => {
52
- terminals.delete(tabId)
53
- })
54
- })
55
-
56
- return stdout
57
- }
58
-
59
- export async function sendStdin(options: { tabId: TabId; data: string }): Promise<void> {
60
- const tabId = options.tabId.tabId
61
- const app = terminals.get(tabId)
62
- assert(app !== undefined, `Terminal instance for tabId not found - cannot send stdin. Maybe it's not started yet?`)
63
- assert(
64
- app.application,
65
- `Terminal application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
66
- )
67
-
68
- await app.application.write(options.data)
69
- }
70
-
71
- export async function runBlockingShellCommand(
72
- signal: AbortSignal | undefined,
73
- input: BlockingCommandInput,
74
- allowFailure: boolean
75
- ): Promise<BlockingShellCommandOutput> {
76
- const tabId = input.tabId.tabId
77
- const app = terminals.get(tabId)
78
- assert(
79
- app !== undefined,
80
- `Terminal instance for tabId ${input.tabId.tabId} not found - cannot send stdin. Maybe it's not started yet?`
81
- )
82
- assert(app.application, `Terminal application not found for tabId ${input.tabId.tabId}. Maybe it's not started yet?`)
83
-
84
- const testDirectory = app.state?.testDirectory
85
- assert(testDirectory, `Test directory not found for client id ${input.tabId.tabId}. Maybe neovim's not started yet?`)
86
-
87
- const env = app.getEnvironmentVariables(testDirectory, input.envOverrides)
88
- return executeBlockingShellCommand(testDirectory, input, signal, allowFailure, env)
89
- }
@@ -1,41 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { getCwd } from "./runBlockingShellCommand.js"
3
-
4
- describe("getCwd", () => {
5
- it("prefers cwdRelative if it is defined", () => {
6
- // in many cases this is what the user wants to do, so prefer that
7
-
8
- const result = getCwd({
9
- rootPathAbsolute: "/root-absolute",
10
- cwdRelative: "test-cwd-relative",
11
- cwdAbsolute: "/test-cwd",
12
- home: "/test-home",
13
- })
14
- expect(result).toEqual("/root-absolute/test-cwd-relative")
15
- })
16
-
17
- it("falls back to cwd if cwdRelative is not defined", () => {
18
- // this is used when the user wants to use absolute paths
19
-
20
- const result = getCwd({
21
- rootPathAbsolute: "/root-absolute",
22
- cwdRelative: undefined,
23
- cwdAbsolute: "/test-cwd",
24
- home: "/test-home",
25
- })
26
- expect(result).toEqual("/test-cwd")
27
- })
28
-
29
- it("falls back to HOME if neither cwdRelative nor cwd are defined", () => {
30
- // if the user doesn't specify anything, we default to the home directory
31
- // because a directory must be present when running the command
32
-
33
- const result = getCwd({
34
- rootPathAbsolute: "/root-absolute",
35
- cwdRelative: undefined,
36
- cwdAbsolute: undefined,
37
- home: "/test-home",
38
- })
39
- expect(result).toEqual("/test-home")
40
- })
41
- })
@@ -1,64 +0,0 @@
1
- import { exec } from "child_process"
2
- import { join } from "path"
3
- import util, { debuglog } from "util"
4
- import type { BlockingCommandInput } from "../../blockingCommandInputSchema.js"
5
- import type { BlockingShellCommandOutput, TestDirectory } from "../../types.js"
6
-
7
- const log = debuglog("tui-sandbox.terminal.runBlockingShellCommand")
8
-
9
- export async function executeBlockingShellCommand(
10
- testDirectory: TestDirectory,
11
- input: BlockingCommandInput,
12
- signal: AbortSignal | undefined,
13
- allowFailure: boolean,
14
- env: NodeJS.ProcessEnv
15
- ): Promise<BlockingShellCommandOutput> {
16
- const execPromise = util.promisify(exec)
17
-
18
- const cwd = getCwd({
19
- rootPathAbsolute: testDirectory.rootPathAbsolute,
20
- cwdRelative: input.cwdRelative,
21
- cwdAbsolute: input.cwd,
22
- home: testDirectory.rootPathAbsolute,
23
- })
24
-
25
- try {
26
- const result = await execPromise(input.command, {
27
- signal,
28
- shell: input.shell,
29
- uid: input.uid,
30
- gid: input.gid,
31
- cwd,
32
- env,
33
- })
34
- log(
35
- `Successfully ran shell blockingCommand (${input.command}) in cwd: '${cwd}' with stdout: ${result.stdout}, stderr: ${result.stderr}`
36
- )
37
- return {
38
- type: "success",
39
- stdout: result.stdout,
40
- stderr: result.stderr,
41
- } satisfies BlockingShellCommandOutput
42
- } catch (e) {
43
- console.warn(`Error running shell blockingCommand (${input.command})`, e)
44
- if (allowFailure) {
45
- return {
46
- type: "failed",
47
- }
48
- }
49
- throw new Error(`Error running shell blockingCommand (${input.command})`, { cause: e })
50
- }
51
- }
52
-
53
- export type GetCwdArguments = {
54
- rootPathAbsolute: string
55
- cwdRelative: string | undefined
56
- cwdAbsolute: string | undefined
57
- home: string
58
- }
59
- export function getCwd(args: GetCwdArguments): string {
60
- if (args.cwdRelative) {
61
- return join(args.rootPathAbsolute, args.cwdRelative)
62
- }
63
- return args.cwdAbsolute ?? args.home
64
- }
@@ -1,47 +0,0 @@
1
- import * as z from "zod"
2
- import { blockingCommandInputSchema } from "../../blockingCommandInputSchema.js"
3
- import { trpc } from "../../connection/trpc.js"
4
- import { serverTestDirectorySchema } from "../../types.js"
5
- import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
6
- import { tabIdSchema } from "../../utilities/tabId.js"
7
- import * as terminal from "./api.js"
8
-
9
- const startTerminalInputSchema = z.object({
10
- tabId: tabIdSchema,
11
- startTerminalArguments: z.object({
12
- commandToRun: z.array(z.string()),
13
- additionalEnvironmentVariables: z.record(z.string(), z.string()).optional(),
14
- terminalDimensions: z.object({
15
- cols: z.number(),
16
- rows: z.number(),
17
- }),
18
- }),
19
- })
20
- export type StartTerminalInput = z.infer<typeof startTerminalInputSchema>
21
-
22
- // let trpc infer the type as that is what it is designed to do
23
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
24
- export function createTerminalRouter(config: TestServerConfig) {
25
- const terminalRouter = trpc.router({
26
- onStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
27
- return terminal.initializeStdout(options.input, options.signal)
28
- }),
29
-
30
- start: trpc.procedure
31
- .input(startTerminalInputSchema)
32
- .output(serverTestDirectorySchema)
33
- .mutation(async options => {
34
- return terminal.start(options.input, config)
35
- }),
36
-
37
- sendStdin: trpc.procedure.input(z.object({ tabId: tabIdSchema, data: z.string() })).mutation(options => {
38
- return terminal.sendStdin(options.input)
39
- }),
40
-
41
- runBlockingShellCommand: trpc.procedure.input(blockingCommandInputSchema).mutation(async options => {
42
- return terminal.runBlockingShellCommand(options.signal, options.input, options.input.allowFailure ?? false)
43
- }),
44
- })
45
-
46
- return terminalRouter
47
- }
@@ -1,24 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { blockingCommandInputSchema, type BlockingCommandInput } from "./blockingCommandInputSchema.js"
3
-
4
- describe("blockingCommandInputSchema", () => {
5
- it("allows either cwd or cwdRelative but not both", () => {
6
- // It doesn't make sense to have two sources of truth for the cwd.
7
- const fails = blockingCommandInputSchema.safeParse({
8
- command: "command",
9
- tabId: { tabId: "123" },
10
- cwd: "cwd",
11
- cwdRelative: "cwdRelative",
12
- } satisfies Partial<BlockingCommandInput>)
13
-
14
- expect(fails.error).toMatchInlineSnapshot(`
15
- [ZodError: [
16
- {
17
- "code": "custom",
18
- "path": [],
19
- "message": "Both cwd and cwdRelative provided. Please provide either but not both at the same time."
20
- }
21
- ]]
22
- `)
23
- })
24
- })
@@ -1,27 +0,0 @@
1
- import type { Except } from "type-fest"
2
- import * as z from "zod"
3
- import { tabIdSchema } from "./utilities/tabId.js"
4
-
5
- export const blockingCommandInputSchema = z
6
- .object({
7
- command: z.string(),
8
- shell: z.string().optional(),
9
- tabId: tabIdSchema,
10
- allowFailure: z.boolean().optional(),
11
-
12
- // absolute cwd
13
- cwd: z.string().optional(),
14
- cwdRelative: z.string().optional(),
15
- envOverrides: z.record(z.string(), z.string()).optional(),
16
- uid: z.number().optional(),
17
- gid: z.number().optional(),
18
- })
19
- .refine(
20
- data =>
21
- // disallow both cwd and cwdRelative
22
- !(data.cwd && data.cwdRelative),
23
- { message: "Both cwd and cwdRelative provided. Please provide either but not both at the same time." }
24
- )
25
-
26
- export type BlockingCommandClientInput = Except<BlockingCommandInput, "tabId">
27
- export type BlockingCommandInput = z.infer<typeof blockingCommandInputSchema>
@@ -1,7 +0,0 @@
1
- import { createDefaultConfig } from "./config.js"
2
- import { testServerConfigSchema } from "./updateTestdirectorySchemaFile.js"
3
-
4
- it("is possible not to customize anything", () => {
5
- const defaultConfig = createDefaultConfig(process.cwd(), process.env)
6
- testServerConfigSchema.parse(defaultConfig)
7
- })
@@ -1,18 +0,0 @@
1
- import path from "path"
2
- import type { TestServerConfig } from "./updateTestdirectorySchemaFile.js"
3
-
4
- export const createDefaultConfig = (cwd: string, environment: NodeJS.ProcessEnv): TestServerConfig => {
5
- return {
6
- directories: {
7
- testEnvironmentPath: path.join(cwd, "test-environment/"),
8
- outputFilePath: path.join(cwd, "MyTestDirectory.ts"),
9
- latestSymlinkName: "latest",
10
- },
11
- port: environment["PORT"] ? parseInt(environment["PORT"]) : 3000,
12
- integrations: {
13
- neovim: {
14
- NVIM_APPNAMEs: ["nvim"],
15
- },
16
- },
17
- }
18
- }
@@ -1,3 +0,0 @@
1
- import { initTRPC } from "@trpc/server"
2
-
3
- export const trpc = initTRPC.context<object>().create()