@tui-sandbox/library 9.5.0 → 9.6.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 +14 -0
- package/dist/browser/assets/{index-06LPWRXA.js → index-Dm7lp9YI.js} +6 -6
- package/dist/browser/index.html +1 -1
- package/dist/src/browser/neovim-client.d.ts +2 -1
- package/dist/src/browser/neovim-client.js +3 -0
- package/dist/src/browser/neovim-client.js.map +1 -1
- package/dist/src/client/neovim-terminal-client.d.ts +2 -1
- package/dist/src/client/neovim-terminal-client.js +9 -0
- package/dist/src/client/neovim-terminal-client.js.map +1 -1
- package/dist/src/server/cypress-support/contents.js +20 -1
- package/dist/src/server/cypress-support/contents.js.map +1 -1
- package/dist/src/server/dirtree/index.test.js +5 -0
- package/dist/src/server/dirtree/index.test.js.map +1 -1
- package/dist/src/server/neovim/index.d.ts +5 -0
- package/dist/src/server/neovim/index.js +33 -0
- package/dist/src/server/neovim/index.js.map +1 -1
- package/dist/src/server/server.d.ts +34 -0
- package/dist/src/server/server.js +17 -2
- package/dist/src/server/server.js.map +1 -1
- package/dist/src/server/utilities/timeout.js +1 -1
- package/dist/src/server/utilities/timeout.js.map +1 -1
- package/dist/src/server/utilities/timeoutable.d.ts +1 -0
- package/dist/src/server/utilities/timeoutable.js +18 -0
- package/dist/src/server/utilities/timeoutable.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/browser/neovim-client.ts +5 -1
- package/src/client/neovim-terminal-client.ts +10 -1
- package/src/server/cypress-support/contents.ts +20 -1
- package/src/server/dirtree/index.test.ts +5 -0
- package/src/server/neovim/index.ts +56 -0
- package/src/server/server.ts +22 -2
- package/src/server/utilities/timeout.ts +1 -1
- package/src/server/utilities/timeoutable.ts +19 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tui-sandbox/library",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,21 +8,21 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@catppuccin/palette": "1.7.1",
|
|
11
|
-
"@trpc/client": "11.0.0-rc.
|
|
12
|
-
"@trpc/server": "11.0.0-rc.
|
|
11
|
+
"@trpc/client": "11.0.0-rc.824",
|
|
12
|
+
"@trpc/server": "11.0.0-rc.824",
|
|
13
13
|
"@xterm/addon-attach": "0.11.0",
|
|
14
14
|
"@xterm/addon-fit": "0.10.0",
|
|
15
15
|
"@xterm/xterm": "5.5.0",
|
|
16
16
|
"command-exists": "1.2.9",
|
|
17
|
-
"core-js": "3.
|
|
17
|
+
"core-js": "3.41.0",
|
|
18
18
|
"cors": "2.8.5",
|
|
19
19
|
"dree": "5.1.5",
|
|
20
20
|
"express": "4.21.2",
|
|
21
21
|
"neovim": "5.3.0",
|
|
22
22
|
"node-pty": "1.0.0",
|
|
23
|
-
"prettier": "3.5.
|
|
23
|
+
"prettier": "3.5.3",
|
|
24
24
|
"tsx": "4.19.3",
|
|
25
|
-
"type-fest": "4.
|
|
25
|
+
"type-fest": "4.37.0",
|
|
26
26
|
"winston": "3.17.0",
|
|
27
27
|
"zod": "3.24.2"
|
|
28
28
|
},
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"@types/command-exists": "1.2.3",
|
|
32
32
|
"@types/cors": "2.8.17",
|
|
33
33
|
"@types/express": "5.0.0",
|
|
34
|
-
"@types/node": "22.13.
|
|
34
|
+
"@types/node": "22.13.9",
|
|
35
35
|
"nodemon": "3.1.9",
|
|
36
|
-
"vite": "6.
|
|
37
|
-
"vitest": "3.0.
|
|
36
|
+
"vite": "6.2.0",
|
|
37
|
+
"vitest": "3.0.7"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"cypress": "^13 || ^14",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TerminalClient as NeovimTerminalClient } from "../client/index.js"
|
|
2
2
|
import { TerminalTerminalClient } from "../client/terminal-terminal-client.js"
|
|
3
3
|
import type { BlockingCommandClientInput } from "../server/blockingCommandInputSchema.js"
|
|
4
|
-
import type { ExCommandClientInput, LuaCodeClientInput } from "../server/server.js"
|
|
4
|
+
import type { ExCommandClientInput, LuaCodeClientInput, PollLuaCodeClientInput } from "../server/server.js"
|
|
5
5
|
import type { StartTerminalGenericArguments } from "../server/terminal/TerminalTestApplication.js"
|
|
6
6
|
import type {
|
|
7
7
|
BlockingShellCommandOutput,
|
|
@@ -24,6 +24,7 @@ const terminalClient = new Lazy(() => new TerminalTerminalClient(app))
|
|
|
24
24
|
export type GenericNeovimBrowserApi = {
|
|
25
25
|
runBlockingShellCommand(input: BlockingCommandClientInput): Promise<BlockingShellCommandOutput>
|
|
26
26
|
runLuaCode(input: LuaCodeClientInput): Promise<RunLuaCodeOutput>
|
|
27
|
+
waitForLuaCode(input: PollLuaCodeClientInput): Promise<RunLuaCodeOutput>
|
|
27
28
|
runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput>
|
|
28
29
|
dir: TestDirectory
|
|
29
30
|
}
|
|
@@ -44,6 +45,9 @@ window.startNeovim = async function (startArgs?: StartNeovimGenericArguments): P
|
|
|
44
45
|
runLuaCode(input) {
|
|
45
46
|
return neovim.runLuaCode(input)
|
|
46
47
|
},
|
|
48
|
+
waitForLuaCode(input) {
|
|
49
|
+
return neovim.waitForLuaCode(input)
|
|
50
|
+
},
|
|
47
51
|
runExCommand(input) {
|
|
48
52
|
return neovim.runExCommand(input)
|
|
49
53
|
},
|
|
@@ -2,7 +2,7 @@ import { createTRPCClient, httpBatchLink, splitLink, unstable_httpSubscriptionLi
|
|
|
2
2
|
import type { Terminal } from "@xterm/xterm"
|
|
3
3
|
import "@xterm/xterm/css/xterm.css"
|
|
4
4
|
import type { BlockingCommandClientInput } from "../server/blockingCommandInputSchema.js"
|
|
5
|
-
import type { AppRouter, ExCommandClientInput, LuaCodeClientInput } from "../server/server.js"
|
|
5
|
+
import type { AppRouter, ExCommandClientInput, LuaCodeClientInput, PollLuaCodeClientInput } from "../server/server.js"
|
|
6
6
|
import type {
|
|
7
7
|
BlockingShellCommandOutput,
|
|
8
8
|
RunExCommandOutput,
|
|
@@ -102,6 +102,15 @@ export class NeovimTerminalClient {
|
|
|
102
102
|
return this.trpc.neovim.runLuaCode.mutate({ ...input, tabId: this.tabId })
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
public async waitForLuaCode(input: PollLuaCodeClientInput): Promise<RunLuaCodeOutput> {
|
|
106
|
+
await this.ready
|
|
107
|
+
try {
|
|
108
|
+
return await this.trpc.neovim.waitForLuaCode.mutate({ ...input, tabId: this.tabId })
|
|
109
|
+
} catch (e) {
|
|
110
|
+
throw e
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
105
114
|
public async runExCommand(input: ExCommandClientInput): Promise<RunExCommandOutput> {
|
|
106
115
|
await this.ready
|
|
107
116
|
return this.trpc.neovim.runExCommand.mutate({ ...input, tabId: this.tabId })
|
|
@@ -14,7 +14,11 @@ import type {
|
|
|
14
14
|
GenericNeovimBrowserApi,
|
|
15
15
|
GenericTerminalBrowserApi,
|
|
16
16
|
} from "@tui-sandbox/library/dist/src/browser/neovim-client"
|
|
17
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
ExCommandClientInput,
|
|
19
|
+
LuaCodeClientInput,
|
|
20
|
+
PollLuaCodeClientInput,
|
|
21
|
+
} from "@tui-sandbox/library/dist/src/server/server"
|
|
18
22
|
import type {
|
|
19
23
|
BlockingShellCommandOutput,
|
|
20
24
|
RunExCommandOutput,
|
|
@@ -54,6 +58,16 @@ export type NeovimContext = {
|
|
|
54
58
|
* finish before returning. Requires neovim to be running. */
|
|
55
59
|
runLuaCode(input: LuaCodeClientInput): Cypress.Chainable<RunLuaCodeOutput>
|
|
56
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Like runLuaCode, but waits until the given code (maybe using lua's return
|
|
63
|
+
* assert()) does not raise an error, and returns the first successful result.
|
|
64
|
+
*
|
|
65
|
+
* Useful for waiting until Neovim's internal state has changed in a way that
|
|
66
|
+
* means the test can continue executing. This can avoid timing issues that are
|
|
67
|
+
* otherwise hard to catch.
|
|
68
|
+
*/
|
|
69
|
+
waitForLuaCode(input: PollLuaCodeClientInput): Cypress.Chainable<RunLuaCodeOutput>
|
|
70
|
+
|
|
57
71
|
/** Run an ex command in neovim.
|
|
58
72
|
* @example "echo expand('%:.')" current file, relative to the cwd
|
|
59
73
|
*/
|
|
@@ -85,6 +99,7 @@ Cypress.Commands.add("startNeovim", (startArguments?: MyStartNeovimServerArgumen
|
|
|
85
99
|
nvim_runBlockingShellCommand: underlyingNeovim.runBlockingShellCommand,
|
|
86
100
|
nvim_runExCommand: underlyingNeovim.runExCommand,
|
|
87
101
|
nvim_runLuaCode: underlyingNeovim.runLuaCode,
|
|
102
|
+
nvim_waitForLuaCode: underlyingNeovim.waitForLuaCode,
|
|
88
103
|
})
|
|
89
104
|
|
|
90
105
|
const api: NeovimContext = {
|
|
@@ -97,6 +112,9 @@ Cypress.Commands.add("startNeovim", (startArguments?: MyStartNeovimServerArgumen
|
|
|
97
112
|
runLuaCode(input) {
|
|
98
113
|
return cy.nvim_runLuaCode(input)
|
|
99
114
|
},
|
|
115
|
+
waitForLuaCode(input) {
|
|
116
|
+
return cy.nvim_waitForLuaCode(input)
|
|
117
|
+
},
|
|
100
118
|
typeIntoTerminal(text, options) {
|
|
101
119
|
cy.typeIntoTerminal(text, options)
|
|
102
120
|
},
|
|
@@ -164,6 +182,7 @@ declare global {
|
|
|
164
182
|
nvim_runBlockingShellCommand(input: MyBlockingCommandClientInput): Chainable<BlockingShellCommandOutput>
|
|
165
183
|
|
|
166
184
|
nvim_runLuaCode(input: LuaCodeClientInput): Chainable<RunLuaCodeOutput>
|
|
185
|
+
nvim_waitForLuaCode(input: PollLuaCodeClientInput): Chainable<RunLuaCodeOutput>
|
|
167
186
|
|
|
168
187
|
/** Run an ex command in neovim.
|
|
169
188
|
* @example "echo expand('%:.')" current file, relative to the cwd
|
|
@@ -63,6 +63,10 @@ describe("dirtree", () => {
|
|
|
63
63
|
name: z.literal("add_command_to_count_open_buffers.lua"),
|
|
64
64
|
type: z.literal("file"),
|
|
65
65
|
}),
|
|
66
|
+
"add_command_to_update_buffer_after_timeout.lua": z.object({
|
|
67
|
+
name: z.literal("add_command_to_update_buffer_after_timeout.lua"),
|
|
68
|
+
type: z.literal("file"),
|
|
69
|
+
}),
|
|
66
70
|
"don't_crash_when_modification_contains_unescaped_characters\\".lua": z.object({
|
|
67
71
|
name: z.literal("don't_crash_when_modification_contains_unescaped_characters\\".lua"),
|
|
68
72
|
type: z.literal("file"),
|
|
@@ -135,6 +139,7 @@ describe("dirtree", () => {
|
|
|
135
139
|
".config/nvim",
|
|
136
140
|
".config",
|
|
137
141
|
"config-modifications/add_command_to_count_open_buffers.lua",
|
|
142
|
+
"config-modifications/add_command_to_update_buffer_after_timeout.lua",
|
|
138
143
|
"config-modifications/don't_crash_when_modification_contains_unescaped_characters\\".lua",
|
|
139
144
|
"config-modifications",
|
|
140
145
|
"dir with spaces/file1.txt",
|
|
@@ -16,6 +16,7 @@ import type { DirectoriesConfig } from "../updateTestdirectorySchemaFile.js"
|
|
|
16
16
|
import { convertEventEmitterToAsyncGenerator } from "../utilities/generator.js"
|
|
17
17
|
import { Lazy } from "../utilities/Lazy.js"
|
|
18
18
|
import type { TabId } from "../utilities/tabId.js"
|
|
19
|
+
import { timeout } from "../utilities/timeout.js"
|
|
19
20
|
import type { StdoutOrStderrMessage, TerminalDimensions } from "./NeovimApplication.js"
|
|
20
21
|
import { NeovimApplication } from "./NeovimApplication.js"
|
|
21
22
|
import { prepareNewTestDirectory } from "./prepareNewTestDirectory.js"
|
|
@@ -154,6 +155,61 @@ export async function runLuaCode(options: LuaCodeInput): Promise<RunLuaCodeOutpu
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
export type PollLuaCodeInput = {
|
|
159
|
+
luaAssertion: string
|
|
160
|
+
tabId: TabId
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function waitForLuaCode(
|
|
164
|
+
options: PollLuaCodeInput,
|
|
165
|
+
signal: AbortSignal | undefined
|
|
166
|
+
): Promise<RunLuaCodeOutput> {
|
|
167
|
+
const neovim = neovims.get(options.tabId.tabId)
|
|
168
|
+
assert(
|
|
169
|
+
neovim !== undefined,
|
|
170
|
+
`Neovim instance for clientId not found - cannot pollLuaCode. Maybe neovim's not started yet?`
|
|
171
|
+
)
|
|
172
|
+
assert(
|
|
173
|
+
neovim.application,
|
|
174
|
+
`Neovim application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
const api = await neovim.state?.client.get()
|
|
178
|
+
if (!api) {
|
|
179
|
+
throw new Error(`Neovim API not available for client id ${options.tabId.tabId}. Maybe it's not started yet?`)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log(`Neovim ${neovim.application.processId()} polling Lua code: ${options.luaAssertion}`)
|
|
183
|
+
|
|
184
|
+
let running: boolean = true
|
|
185
|
+
signal?.addEventListener("abort", () => {
|
|
186
|
+
console.log(`Polling Lua code: '${options.luaAssertion}' was aborted via signal`)
|
|
187
|
+
running = false
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const maxIterations = 100
|
|
191
|
+
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
193
|
+
if (!running) {
|
|
194
|
+
throw new Error(`Polling Lua code: '${options.luaAssertion}' was aborted after ${iteration} iterations`)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const value = await api.lua(options.luaAssertion)
|
|
199
|
+
console.log(`Lua code assertion passed: ${options.luaAssertion} (iteration ${iteration})`)
|
|
200
|
+
|
|
201
|
+
return { value }
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.error(`Caught error in iteration ${iteration}:`, e)
|
|
204
|
+
await timeout(100)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Polling Lua code: '${options.luaAssertion}' always raised an error after ${maxIterations} iterations`
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
157
213
|
export async function runExCommand(options: ExCommandInput): Promise<RunExCommandOutput> {
|
|
158
214
|
const neovim = neovims.get(options.tabId.tabId)
|
|
159
215
|
assert(
|
package/src/server/server.ts
CHANGED
|
@@ -9,11 +9,19 @@ import * as terminal from "./terminal/index.js"
|
|
|
9
9
|
import { TestServer } from "./TestServer.js"
|
|
10
10
|
import type { DirectoriesConfig, TestServerConfig } from "./updateTestdirectorySchemaFile.js"
|
|
11
11
|
import { tabIdSchema } from "./utilities/tabId.js"
|
|
12
|
+
import { timeoutable } from "./utilities/timeoutable.js"
|
|
12
13
|
|
|
13
14
|
const luaCodeInputSchema = z.object({ tabId: tabIdSchema, luaCode: z.string() })
|
|
14
15
|
export type LuaCodeClientInput = Except<LuaCodeInput, "tabId">
|
|
15
16
|
export type LuaCodeInput = z.infer<typeof luaCodeInputSchema>
|
|
16
17
|
|
|
18
|
+
const pollLuaCodeInputSchema = z.object({
|
|
19
|
+
tabId: tabIdSchema,
|
|
20
|
+
luaAssertion: z.string(),
|
|
21
|
+
timeoutMs: z.number().optional().default(10_000),
|
|
22
|
+
})
|
|
23
|
+
export type PollLuaCodeClientInput = Except<z.input<typeof pollLuaCodeInputSchema>, "tabId">
|
|
24
|
+
|
|
17
25
|
const exCommandInputSchema = z.object({
|
|
18
26
|
tabId: tabIdSchema,
|
|
19
27
|
command: z.string(),
|
|
@@ -104,11 +112,23 @@ export async function createAppRouter(config: DirectoriesConfig) {
|
|
|
104
112
|
}),
|
|
105
113
|
|
|
106
114
|
runLuaCode: trpc.procedure.input(luaCodeInputSchema).mutation(options => {
|
|
107
|
-
return neovim.runLuaCode(options.input)
|
|
115
|
+
return timeoutable(10_000, neovim.runLuaCode(options.input))
|
|
116
|
+
}),
|
|
117
|
+
|
|
118
|
+
waitForLuaCode: trpc.procedure.input(pollLuaCodeInputSchema).mutation(async options => {
|
|
119
|
+
try {
|
|
120
|
+
const result = await timeoutable(
|
|
121
|
+
options.input.timeoutMs,
|
|
122
|
+
neovim.waitForLuaCode(options.input, options.signal)
|
|
123
|
+
)
|
|
124
|
+
return result
|
|
125
|
+
} catch (e) {
|
|
126
|
+
throw e
|
|
127
|
+
}
|
|
108
128
|
}),
|
|
109
129
|
|
|
110
130
|
runExCommand: trpc.procedure.input(exCommandInputSchema).mutation(options => {
|
|
111
|
-
return neovim.runExCommand(options.input)
|
|
131
|
+
return timeoutable(10_000, neovim.runExCommand(options.input))
|
|
112
132
|
}),
|
|
113
133
|
}),
|
|
114
134
|
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from "node:assert"
|
|
2
|
+
|
|
3
|
+
export async function timeoutable<T>(timeoutMs: number, promise: Promise<T>): Promise<T> {
|
|
4
|
+
let timeoutHandle: NodeJS.Timeout
|
|
5
|
+
|
|
6
|
+
const timeoutPromise = new Promise<T>((_, reject) => {
|
|
7
|
+
timeoutHandle = setTimeout(() => {
|
|
8
|
+
reject(new Error(`Operation timed out after ${timeoutMs}ms`))
|
|
9
|
+
}, timeoutMs)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
return await Promise.race([promise, timeoutPromise])
|
|
14
|
+
} finally {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16
|
+
assert(timeoutHandle!)
|
|
17
|
+
clearTimeout(timeoutHandle)
|
|
18
|
+
}
|
|
19
|
+
}
|