@xincli/opentui-core 0.4.6

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 (476) hide show
  1. package/README.md +63 -0
  2. package/dev/keypress-debug-renderer.ts +148 -0
  3. package/dev/keypress-debug.ts +43 -0
  4. package/dev/print-env-vars.ts +32 -0
  5. package/dev/test-tmux-graphics-334.sh +68 -0
  6. package/dev/test-tmux-theme-queries.sh +151 -0
  7. package/dev/thai-debug-test.ts +68 -0
  8. package/dev/theme-mode.ts +290 -0
  9. package/docs/development.md +144 -0
  10. package/package.json +102 -0
  11. package/prebuilt/aarch64-android/libopentui.so +0 -0
  12. package/scripts/build-android.ts +226 -0
  13. package/scripts/build-native-termux.sh +525 -0
  14. package/scripts/build.ts +530 -0
  15. package/scripts/dist-test.ts +312 -0
  16. package/scripts/package-prebuilt.ts +110 -0
  17. package/scripts/publish.ts +60 -0
  18. package/scripts/test-node-hook.mjs +16 -0
  19. package/scripts/test-node.ts +241 -0
  20. package/scripts/vendor-deps.sh +73 -0
  21. package/src/NativeSpanFeed.ts +375 -0
  22. package/src/Renderable.ts +1878 -0
  23. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  24. package/src/animation/Timeline.test.ts +2709 -0
  25. package/src/animation/Timeline.ts +598 -0
  26. package/src/ansi.ts +22 -0
  27. package/src/audio.ts +479 -0
  28. package/src/benchmark/attenuation-benchmark.ts +81 -0
  29. package/src/benchmark/audio-playback-benchmark.ts +344 -0
  30. package/src/benchmark/box-draw-benchmark.ts +1042 -0
  31. package/src/benchmark/colormatrix-benchmark.ts +123 -0
  32. package/src/benchmark/gain-benchmark.ts +80 -0
  33. package/src/benchmark/latest-all-bench-run.json +707 -0
  34. package/src/benchmark/latest-async-bench-run.json +336 -0
  35. package/src/benchmark/latest-default-bench-run.json +657 -0
  36. package/src/benchmark/latest-large-bench-run.json +707 -0
  37. package/src/benchmark/latest-quick-bench-run.json +207 -0
  38. package/src/benchmark/layout-benchmark.ts +2547 -0
  39. package/src/benchmark/markdown-benchmark.ts +1796 -0
  40. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  41. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  42. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  43. package/src/benchmark/native-span-feed-compare.ts +280 -0
  44. package/src/benchmark/render-traversal-benchmark.ts +928 -0
  45. package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +292 -0
  48. package/src/buffer.ts +564 -0
  49. package/src/console.test.ts +629 -0
  50. package/src/console.ts +1253 -0
  51. package/src/edit-buffer.test.ts +1852 -0
  52. package/src/edit-buffer.ts +417 -0
  53. package/src/editor-view.test.ts +1056 -0
  54. package/src/editor-view.ts +290 -0
  55. package/src/index.ts +25 -0
  56. package/src/lib/KeyHandler.integration.test.ts +292 -0
  57. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  58. package/src/lib/KeyHandler.test.ts +662 -0
  59. package/src/lib/KeyHandler.ts +222 -0
  60. package/src/lib/RGBA.test.ts +60 -0
  61. package/src/lib/RGBA.ts +343 -0
  62. package/src/lib/ascii.font.ts +330 -0
  63. package/src/lib/border.test.ts +83 -0
  64. package/src/lib/border.ts +170 -0
  65. package/src/lib/bunfs.test.ts +27 -0
  66. package/src/lib/bunfs.ts +18 -0
  67. package/src/lib/clipboard.test.ts +63 -0
  68. package/src/lib/clipboard.ts +41 -0
  69. package/src/lib/clock.ts +35 -0
  70. package/src/lib/data-paths.test.ts +133 -0
  71. package/src/lib/data-paths.ts +109 -0
  72. package/src/lib/debounce.ts +106 -0
  73. package/src/lib/detect-links.test.ts +98 -0
  74. package/src/lib/detect-links.ts +56 -0
  75. package/src/lib/env.test.ts +228 -0
  76. package/src/lib/env.ts +209 -0
  77. package/src/lib/extmarks-history.ts +51 -0
  78. package/src/lib/extmarks-multiwidth.test.ts +324 -0
  79. package/src/lib/extmarks.test.ts +3547 -0
  80. package/src/lib/extmarks.ts +861 -0
  81. package/src/lib/fonts/block.json +405 -0
  82. package/src/lib/fonts/grid.json +265 -0
  83. package/src/lib/fonts/huge.json +741 -0
  84. package/src/lib/fonts/pallet.json +314 -0
  85. package/src/lib/fonts/shade.json +591 -0
  86. package/src/lib/fonts/slick.json +321 -0
  87. package/src/lib/fonts/tiny.json +69 -0
  88. package/src/lib/hast-styled-text.ts +59 -0
  89. package/src/lib/index.ts +22 -0
  90. package/src/lib/keybinding.internal.test.ts +342 -0
  91. package/src/lib/keybinding.internal.ts +182 -0
  92. package/src/lib/objects-in-viewport.test.ts +789 -0
  93. package/src/lib/objects-in-viewport.ts +153 -0
  94. package/src/lib/output.capture.ts +58 -0
  95. package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
  96. package/src/lib/parse.keypress-kitty.test.ts +712 -0
  97. package/src/lib/parse.keypress-kitty.ts +470 -0
  98. package/src/lib/parse.keypress.test.ts +2020 -0
  99. package/src/lib/parse.keypress.ts +480 -0
  100. package/src/lib/parse.mouse.test.ts +552 -0
  101. package/src/lib/parse.mouse.ts +232 -0
  102. package/src/lib/paste.ts +18 -0
  103. package/src/lib/queue.ts +65 -0
  104. package/src/lib/render-geometry.ts +36 -0
  105. package/src/lib/renderable.validations.test.ts +87 -0
  106. package/src/lib/renderable.validations.ts +83 -0
  107. package/src/lib/scroll-acceleration.ts +98 -0
  108. package/src/lib/selection.ts +261 -0
  109. package/src/lib/singleton.ts +34 -0
  110. package/src/lib/stdin-parser.test.ts +2439 -0
  111. package/src/lib/stdin-parser.ts +2007 -0
  112. package/src/lib/styled-text.ts +178 -0
  113. package/src/lib/terminal-capability-detection.test.ts +226 -0
  114. package/src/lib/terminal-capability-detection.ts +98 -0
  115. package/src/lib/terminal-palette.test.ts +954 -0
  116. package/src/lib/terminal-palette.ts +441 -0
  117. package/src/lib/tree-sitter/assets/README.md +119 -0
  118. package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
  119. package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
  120. package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
  121. package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
  122. package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
  123. package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
  124. package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  125. package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
  126. package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
  127. package/src/lib/tree-sitter/assets/update.ts +360 -0
  128. package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
  129. package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
  130. package/src/lib/tree-sitter/assets.d.ts +9 -0
  131. package/src/lib/tree-sitter/cache.test.ts +291 -0
  132. package/src/lib/tree-sitter/client.test.ts +1434 -0
  133. package/src/lib/tree-sitter/client.ts +847 -0
  134. package/src/lib/tree-sitter/default-parsers.ts +133 -0
  135. package/src/lib/tree-sitter/download-utils.ts +148 -0
  136. package/src/lib/tree-sitter/index.ts +75 -0
  137. package/src/lib/tree-sitter/parser.worker.ts +1098 -0
  138. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  139. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  140. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  141. package/src/lib/tree-sitter/types.ts +132 -0
  142. package/src/lib/tree-sitter/update-assets.ts +10 -0
  143. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  144. package/src/lib/tree-sitter-styled-text.ts +314 -0
  145. package/src/lib/validate-dir-name.ts +55 -0
  146. package/src/lib/yoga.options.test.ts +628 -0
  147. package/src/lib/yoga.options.ts +346 -0
  148. package/src/native-event-worker-repro.test.ts +48 -0
  149. package/src/native-event-worker-repro.worker.ts +9 -0
  150. package/src/native-handle.test.ts +221 -0
  151. package/src/platform/ffi.test.ts +516 -0
  152. package/src/platform/ffi.ts +653 -0
  153. package/src/platform/runtime.test.ts +52 -0
  154. package/src/platform/runtime.ts +121 -0
  155. package/src/platform/test.ts +45 -0
  156. package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
  157. package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
  158. package/src/platform/worker-startup.fixture.ts +9 -0
  159. package/src/platform/worker.node-test.ts +153 -0
  160. package/src/platform/worker.ts +425 -0
  161. package/src/plugins/core-slot.ts +579 -0
  162. package/src/plugins/registry.ts +402 -0
  163. package/src/plugins/types.ts +46 -0
  164. package/src/post/effects.ts +948 -0
  165. package/src/post/filters.ts +512 -0
  166. package/src/post/matrices.ts +288 -0
  167. package/src/renderables/ASCIIFont.ts +219 -0
  168. package/src/renderables/Box.test.ts +474 -0
  169. package/src/renderables/Box.ts +353 -0
  170. package/src/renderables/Code.test.ts +2388 -0
  171. package/src/renderables/Code.ts +555 -0
  172. package/src/renderables/Diff.regression.test.ts +381 -0
  173. package/src/renderables/Diff.test.ts +3212 -0
  174. package/src/renderables/Diff.ts +1278 -0
  175. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  176. package/src/renderables/EditBufferRenderable.ts +1152 -0
  177. package/src/renderables/FrameBuffer.ts +47 -0
  178. package/src/renderables/Input.test.ts +1310 -0
  179. package/src/renderables/Input.ts +273 -0
  180. package/src/renderables/LineNumberRenderable.ts +725 -0
  181. package/src/renderables/Markdown.ts +2144 -0
  182. package/src/renderables/ScrollBar.ts +423 -0
  183. package/src/renderables/ScrollBox.ts +903 -0
  184. package/src/renderables/Select.test.ts +1134 -0
  185. package/src/renderables/Select.ts +535 -0
  186. package/src/renderables/Slider.test.ts +456 -0
  187. package/src/renderables/Slider.ts +342 -0
  188. package/src/renderables/TabSelect.test.ts +238 -0
  189. package/src/renderables/TabSelect.ts +447 -0
  190. package/src/renderables/Text.selection-buffer.test.ts +118 -0
  191. package/src/renderables/Text.test.ts +2662 -0
  192. package/src/renderables/Text.ts +147 -0
  193. package/src/renderables/TextBufferRenderable.ts +504 -0
  194. package/src/renderables/TextNode.test.ts +1137 -0
  195. package/src/renderables/TextNode.ts +379 -0
  196. package/src/renderables/TextTable.test.ts +1411 -0
  197. package/src/renderables/TextTable.ts +1434 -0
  198. package/src/renderables/Textarea.ts +425 -0
  199. package/src/renderables/TimeToFirstDraw.ts +89 -0
  200. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  201. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  202. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  203. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  204. package/src/renderables/__tests__/Code.test.ts +55 -0
  205. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  206. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
  207. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
  208. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  209. package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
  210. package/src/renderables/__tests__/Markdown.test.ts +4175 -0
  211. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  212. package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
  213. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  214. package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
  215. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  216. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  217. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  218. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
  219. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  220. package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
  221. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  222. package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
  223. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  224. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  225. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  226. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  227. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  228. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  229. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  230. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  231. package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
  232. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  233. package/src/renderables/composition/README.md +8 -0
  234. package/src/renderables/composition/VRenderable.ts +32 -0
  235. package/src/renderables/composition/constructs.ts +127 -0
  236. package/src/renderables/composition/vnode.ts +277 -0
  237. package/src/renderables/index.ts +23 -0
  238. package/src/renderables/markdown-parser.ts +79 -0
  239. package/src/renderer-theme-mode.ts +199 -0
  240. package/src/renderer.ts +4975 -0
  241. package/src/runtime-plugin-support-configure.ts +63 -0
  242. package/src/runtime-plugin-support.ts +13 -0
  243. package/src/runtime-plugin.ts +626 -0
  244. package/src/specs/terminal-startup.md +64 -0
  245. package/src/syntax-style.test.ts +841 -0
  246. package/src/syntax-style.ts +274 -0
  247. package/src/testing/README.md +219 -0
  248. package/src/testing/bun-test-node.ts +880 -0
  249. package/src/testing/capture-spans.test.ts +216 -0
  250. package/src/testing/integration.test.ts +276 -0
  251. package/src/testing/manual-clock.ts +117 -0
  252. package/src/testing/mock-keys.test.ts +1378 -0
  253. package/src/testing/mock-keys.ts +457 -0
  254. package/src/testing/mock-mouse.test.ts +218 -0
  255. package/src/testing/mock-mouse.ts +247 -0
  256. package/src/testing/mock-tree-sitter-client.ts +81 -0
  257. package/src/testing/spy.ts +13 -0
  258. package/src/testing/terminal-capabilities.ts +47 -0
  259. package/src/testing/test-recorder.test.ts +410 -0
  260. package/src/testing/test-recorder.ts +133 -0
  261. package/src/testing/test-renderer.ts +375 -0
  262. package/src/testing/test-renderer.wait.test.ts +278 -0
  263. package/src/testing/test-streams.ts +31 -0
  264. package/src/testing.ts +9 -0
  265. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  266. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  267. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  268. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  269. package/src/tests/allocator-stats.test.ts +53 -0
  270. package/src/tests/audio.test.ts +303 -0
  271. package/src/tests/destroy-during-render.test.ts +200 -0
  272. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  273. package/src/tests/destroy-on-exit.test.ts +62 -0
  274. package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
  275. package/src/tests/hover-cursor.test.ts +98 -0
  276. package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
  277. package/src/tests/native-backed-measurement-parity.test.ts +322 -0
  278. package/src/tests/native-span-feed-async.test.ts +173 -0
  279. package/src/tests/native-span-feed-close.test.ts +120 -0
  280. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  281. package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
  282. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  283. package/src/tests/opacity.test.ts +123 -0
  284. package/src/tests/renderable.snapshot.test.ts +524 -0
  285. package/src/tests/renderable.test.ts +1851 -0
  286. package/src/tests/renderer.clock.test.ts +270 -0
  287. package/src/tests/renderer.console-startup.test.ts +2151 -0
  288. package/src/tests/renderer.control.test.ts +458 -0
  289. package/src/tests/renderer.core-slot-binding.test.ts +962 -0
  290. package/src/tests/renderer.cursor.test.ts +26 -0
  291. package/src/tests/renderer.custom-stdout.test.ts +1112 -0
  292. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  293. package/src/tests/renderer.focus-restore.test.ts +257 -0
  294. package/src/tests/renderer.focus.test.ts +329 -0
  295. package/src/tests/renderer.idle.test.ts +219 -0
  296. package/src/tests/renderer.input.test.ts +2615 -0
  297. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  298. package/src/tests/renderer.lifecycle.test.ts +34 -0
  299. package/src/tests/renderer.mouse.test.ts +1315 -0
  300. package/src/tests/renderer.notifications.test.ts +27 -0
  301. package/src/tests/renderer.palette.test.ts +1199 -0
  302. package/src/tests/renderer.remote.test.ts +247 -0
  303. package/src/tests/renderer.render-stats.test.ts +146 -0
  304. package/src/tests/renderer.scrollback-surface.test.ts +474 -0
  305. package/src/tests/renderer.selection.test.ts +138 -0
  306. package/src/tests/renderer.slot-registry.test.ts +684 -0
  307. package/src/tests/renderer.tracker.test.ts +202 -0
  308. package/src/tests/renderer.useMouse.test.ts +83 -0
  309. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  310. package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
  311. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  312. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  313. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  314. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  315. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  316. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  317. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  318. package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
  319. package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
  320. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  321. package/src/tests/runtime-plugin-support.test.ts +54 -0
  322. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  323. package/src/tests/runtime-plugin.fixture.ts +40 -0
  324. package/src/tests/runtime-plugin.test.ts +369 -0
  325. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  326. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  327. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  328. package/src/tests/scrollbox.test.ts +1895 -0
  329. package/src/tests/wrap-resize-perf.test.ts +276 -0
  330. package/src/tests/yoga-api-coverage.test.ts +409 -0
  331. package/src/tests/yoga-callback-stress.test.ts +122 -0
  332. package/src/tests/yoga-ffi.test.ts +117 -0
  333. package/src/tests/yoga-setters.test.ts +921 -0
  334. package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
  335. package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
  336. package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
  337. package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
  338. package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
  339. package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
  340. package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
  341. package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
  342. package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
  343. package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
  344. package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
  345. package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
  346. package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
  347. package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
  348. package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
  349. package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
  350. package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
  351. package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
  352. package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
  353. package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
  354. package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
  355. package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
  356. package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
  357. package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
  358. package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
  359. package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
  360. package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
  361. package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
  362. package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
  363. package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
  364. package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
  365. package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
  366. package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
  367. package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
  368. package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
  369. package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
  370. package/src/tests/yoga-upstream/tools/utils.ts +81 -0
  371. package/src/text-buffer-view.test.ts +722 -0
  372. package/src/text-buffer-view.ts +193 -0
  373. package/src/text-buffer.test.ts +512 -0
  374. package/src/text-buffer.ts +250 -0
  375. package/src/types.ts +199 -0
  376. package/src/utils.ts +88 -0
  377. package/src/yoga.ts +1146 -0
  378. package/src/zig/ansi.zig +502 -0
  379. package/src/zig/audio.zig +1206 -0
  380. package/src/zig/bench/README.md +50 -0
  381. package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
  382. package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
  383. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
  384. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  385. package/src/zig/bench/editor-view_bench.zig +169 -0
  386. package/src/zig/bench/native-span-feed_bench.zig +107 -0
  387. package/src/zig/bench/rope-markers_bench.zig +713 -0
  388. package/src/zig/bench/rope_bench.zig +514 -0
  389. package/src/zig/bench/styled-text_bench.zig +476 -0
  390. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  391. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  392. package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
  393. package/src/zig/bench/utf8_bench.zig +799 -0
  394. package/src/zig/bench-utils.zig +431 -0
  395. package/src/zig/bench.zig +223 -0
  396. package/src/zig/buffer-methods.zig +215 -0
  397. package/src/zig/buffer.zig +2515 -0
  398. package/src/zig/build.zig +700 -0
  399. package/src/zig/build.zig.zon +19 -0
  400. package/src/zig/edit-buffer.zig +900 -0
  401. package/src/zig/editor-view.zig +812 -0
  402. package/src/zig/event-bus.zig +28 -0
  403. package/src/zig/event-emitter.zig +65 -0
  404. package/src/zig/file-logger.zig +92 -0
  405. package/src/zig/grapheme.zig +838 -0
  406. package/src/zig/handles.zig +267 -0
  407. package/src/zig/lib.zig +2813 -0
  408. package/src/zig/link.zig +349 -0
  409. package/src/zig/logger.zig +43 -0
  410. package/src/zig/mem-registry.zig +126 -0
  411. package/src/zig/miniaudio_shim.c +21 -0
  412. package/src/zig/native-renderable.zig +112 -0
  413. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  414. package/src/zig/native-span-feed.zig +747 -0
  415. package/src/zig/renderer-output.zig +677 -0
  416. package/src/zig/renderer.zig +2106 -0
  417. package/src/zig/rope.zig +1220 -0
  418. package/src/zig/split-scrollback.zig +88 -0
  419. package/src/zig/syntax-style.zig +174 -0
  420. package/src/zig/terminal.zig +1652 -0
  421. package/src/zig/test.zig +82 -0
  422. package/src/zig/tests/README.md +18 -0
  423. package/src/zig/tests/ansi_test.zig +18 -0
  424. package/src/zig/tests/audio_test.zig +439 -0
  425. package/src/zig/tests/buffer-methods_test.zig +1111 -0
  426. package/src/zig/tests/buffer_test.zig +2926 -0
  427. package/src/zig/tests/edit-buffer-history_test.zig +299 -0
  428. package/src/zig/tests/edit-buffer_test.zig +1688 -0
  429. package/src/zig/tests/editor-view_test.zig +3454 -0
  430. package/src/zig/tests/event-emitter_test.zig +249 -0
  431. package/src/zig/tests/grapheme_test.zig +1329 -0
  432. package/src/zig/tests/handles_test.zig +123 -0
  433. package/src/zig/tests/link_test.zig +220 -0
  434. package/src/zig/tests/mem-registry_test.zig +473 -0
  435. package/src/zig/tests/memory_leak_regression_test.zig +234 -0
  436. package/src/zig/tests/native-renderable_test.zig +16 -0
  437. package/src/zig/tests/native-span-feed_test.zig +1266 -0
  438. package/src/zig/tests/renderer_test.zig +2604 -0
  439. package/src/zig/tests/rope-nested_test.zig +712 -0
  440. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  441. package/src/zig/tests/rope_test.zig +2362 -0
  442. package/src/zig/tests/segment-merge.test.zig +148 -0
  443. package/src/zig/tests/split-scrollback_test.zig +70 -0
  444. package/src/zig/tests/syntax-style_test.zig +558 -0
  445. package/src/zig/tests/terminal_test.zig +1489 -0
  446. package/src/zig/tests/test-renderer.zig +126 -0
  447. package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
  448. package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
  449. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  450. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  451. package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
  452. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  453. package/src/zig/tests/text-buffer-view_test.zig +3672 -0
  454. package/src/zig/tests/text-buffer_test.zig +2191 -0
  455. package/src/zig/tests/unicode-width-map.zon +3909 -0
  456. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  457. package/src/zig/tests/utf8_test.zig +4060 -0
  458. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  459. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  460. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  461. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  462. package/src/zig/tests/yoga_test.zig +65 -0
  463. package/src/zig/text-buffer-iterators.zig +496 -0
  464. package/src/zig/text-buffer-segment.zig +403 -0
  465. package/src/zig/text-buffer-view.zig +1428 -0
  466. package/src/zig/text-buffer.zig +1272 -0
  467. package/src/zig/utf8.zig +1954 -0
  468. package/src/zig/utils.zig +12 -0
  469. package/src/zig/uucode-stub.zig +76 -0
  470. package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
  471. package/src/zig/yoga.zig +658 -0
  472. package/src/zig-structs.ts +381 -0
  473. package/src/zig.ts +5260 -0
  474. package/tsconfig.build.json +17 -0
  475. package/tsconfig.json +27 -0
  476. package/tsconfig.node-test.json +138 -0
@@ -0,0 +1,954 @@
1
+ import { test, expect } from "bun:test"
2
+ import { TerminalPalette, normalizeTerminalPalette } from "./terminal-palette.js"
3
+ import { EventEmitter } from "events"
4
+ import { Buffer } from "node:buffer"
5
+ import { ManualClock } from "../testing/manual-clock.js"
6
+
7
+ class MockStream extends EventEmitter {
8
+ isTTY = true
9
+ isRaw = false
10
+ isPaused() {
11
+ return false
12
+ }
13
+ write(_data: string) {
14
+ return true
15
+ }
16
+ }
17
+
18
+ function createPaletteHarness(
19
+ options: {
20
+ writeFn?: (data: string | Buffer) => boolean
21
+ isLegacyTmux?: boolean
22
+ isTmux?: boolean
23
+ oscSource?: {
24
+ subscribeOsc(handler: (sequence: string) => void): () => void
25
+ }
26
+ } = {},
27
+ ) {
28
+ const stdin = new MockStream() as any
29
+ const stdout = new MockStream() as any
30
+ const clock = new ManualClock()
31
+ const palette = new TerminalPalette({
32
+ stdin,
33
+ stdout,
34
+ writeFn: options.writeFn,
35
+ isLegacyTmux: options.isLegacyTmux ?? false,
36
+ isTmux: options.isTmux,
37
+ oscSource: options.oscSource,
38
+ clock,
39
+ })
40
+
41
+ return { stdin, stdout, clock, palette }
42
+ }
43
+
44
+ async function flushAsync(): Promise<void> {
45
+ await Promise.resolve()
46
+ await Promise.resolve()
47
+ }
48
+
49
+ async function startPaletteDetection(
50
+ options: {
51
+ timeout?: number
52
+ size?: number
53
+ writeFn?: (data: string | Buffer) => boolean
54
+ isLegacyTmux?: boolean
55
+ isTmux?: boolean
56
+ } = {},
57
+ ) {
58
+ const timeout = options.timeout ?? 2000
59
+ const harness = createPaletteHarness({
60
+ writeFn: options.writeFn,
61
+ isLegacyTmux: options.isLegacyTmux,
62
+ isTmux: options.isTmux,
63
+ })
64
+ const detectPromise = harness.palette.detect({
65
+ timeout,
66
+ size: options.size ?? 256,
67
+ })
68
+
69
+ harness.stdin.emit("data", Buffer.from("\x1b]4;0;#000000\x07"))
70
+ await flushAsync()
71
+
72
+ return { ...harness, detectPromise, timeout }
73
+ }
74
+
75
+ async function advanceClock(clock: ManualClock, ms: number): Promise<void> {
76
+ await flushAsync()
77
+ // Flush queued 0ms mock terminal responses before advancing the real timeout window.
78
+ clock.advance(0)
79
+ await flushAsync()
80
+ clock.advance(ms)
81
+ await flushAsync()
82
+ }
83
+
84
+ test("normalizeTerminalPalette returns detached fallback colors", () => {
85
+ const normalized = normalizeTerminalPalette(null)
86
+ normalized.palette[0].r = 1
87
+ normalized.defaultForeground.g = 0
88
+
89
+ const fresh = normalizeTerminalPalette(null)
90
+
91
+ expect(fresh.palette[0].toInts()).toEqual([0, 0, 0, 255])
92
+ expect(fresh.defaultForeground.toInts()).toEqual([255, 255, 255, 255])
93
+ })
94
+
95
+ test("TerminalPalette detectOSCSupport returns true on response", async () => {
96
+ const { stdin, clock, palette } = createPaletteHarness()
97
+
98
+ const detectPromise = palette.detectOSCSupport(500)
99
+
100
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
101
+
102
+ await advanceClock(clock, 500)
103
+
104
+ const result = await detectPromise
105
+
106
+ expect(result).toBe(true)
107
+ })
108
+
109
+ test("TerminalPalette detectOSCSupport returns false on timeout", async () => {
110
+ const { clock, palette } = createPaletteHarness()
111
+
112
+ const detectPromise = palette.detectOSCSupport(100)
113
+
114
+ await advanceClock(clock, 300)
115
+
116
+ const result = await detectPromise
117
+
118
+ expect(result).toBe(false)
119
+ })
120
+
121
+ test("TerminalPalette parses OSC 4 hex format correctly", async () => {
122
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
123
+
124
+ clock.setTimeout(() => {
125
+ for (let i = 0; i < 256; i++) {
126
+ const color = i === 0 ? "#ff00aa" : i === 1 ? "#00ff00" : i === 2 ? "#0000ff" : "#000000"
127
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};${color}\x07`))
128
+ }
129
+ stdin.emit("data", Buffer.from("\x1b]10;#aabbcc\x07"))
130
+ stdin.emit("data", Buffer.from("\x1b]11;#ddeeff\x07"))
131
+ }, 0)
132
+
133
+ await advanceClock(clock, timeout)
134
+
135
+ const result = await detectPromise
136
+
137
+ expect(result.palette[0]).toBe("#ff00aa")
138
+ expect(result.palette[1]).toBe("#00ff00")
139
+ expect(result.palette[2]).toBe("#0000ff")
140
+ expect(result.defaultForeground).toBe("#aabbcc")
141
+ expect(result.defaultBackground).toBe("#ddeeff")
142
+ })
143
+
144
+ test("TerminalPalette parses OSC 4 rgb format with 4 hex digits", async () => {
145
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
146
+
147
+ clock.setTimeout(() => {
148
+ stdin.emit("data", Buffer.from("\x1b]4;0;rgb:ffff/0000/aaaa\x07"))
149
+ for (let i = 1; i < 256; i++) {
150
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
151
+ }
152
+ }, 0)
153
+
154
+ await advanceClock(clock, timeout)
155
+
156
+ const result = await detectPromise
157
+
158
+ expect(result.palette[0]).toMatch(/^#[0-9a-f]{6}$/)
159
+ expect(result.palette[0]).toBe("#ff00aa")
160
+ })
161
+
162
+ test("TerminalPalette parses OSC 4 rgb format with 2 hex digits", async () => {
163
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
164
+
165
+ clock.setTimeout(() => {
166
+ stdin.emit("data", Buffer.from("\x1b]4;0;rgb:ff/00/aa\x07"))
167
+ for (let i = 1; i < 256; i++) {
168
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
169
+ }
170
+ }, 0)
171
+
172
+ await advanceClock(clock, timeout)
173
+
174
+ const result = await detectPromise
175
+
176
+ expect(result.palette[0]).toMatch(/^#[0-9a-f]{6}$/)
177
+ expect(result.palette[0]).toBe("#ff00aa")
178
+ })
179
+
180
+ test("TerminalPalette handles multiple color responses in single buffer", async () => {
181
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
182
+
183
+ clock.setTimeout(() => {
184
+ stdin.emit(
185
+ "data",
186
+ Buffer.from(
187
+ "\x1b]4;0;rgb:0000/0000/0000\x07" +
188
+ "\x1b]4;1;rgb:aa00/0000/0000\x07" +
189
+ "\x1b]4;2;rgb:0000/aa00/0000\x07" +
190
+ "\x1b]4;3;rgb:aa00/aa00/0000\x07",
191
+ ),
192
+ )
193
+
194
+ for (let i = 4; i < 256; i++) {
195
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
196
+ }
197
+ }, 0)
198
+
199
+ await advanceClock(clock, timeout)
200
+
201
+ const result = await detectPromise
202
+
203
+ expect(result.palette[0]).toBe("#000000")
204
+ expect(result.palette[1]).toBe("#a90000")
205
+ expect(result.palette[2]).toBe("#00a900")
206
+ expect(result.palette[3]).toBe("#a9a900")
207
+ })
208
+
209
+ test("TerminalPalette handles BEL terminator", async () => {
210
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
211
+
212
+ clock.setTimeout(() => {
213
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
214
+ for (let i = 1; i < 256; i++) {
215
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
216
+ }
217
+ }, 0)
218
+
219
+ await advanceClock(clock, timeout)
220
+
221
+ const result = await detectPromise
222
+
223
+ expect(result.palette[0]).toBe("#ff0000")
224
+ })
225
+
226
+ test("TerminalPalette handles ST terminator", async () => {
227
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
228
+
229
+ clock.setTimeout(() => {
230
+ stdin.emit("data", Buffer.from("\x1b]4;0;#00ff00\x1b\\"))
231
+ for (let i = 1; i < 256; i++) {
232
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
233
+ }
234
+ }, 0)
235
+
236
+ await advanceClock(clock, timeout)
237
+
238
+ const result = await detectPromise
239
+
240
+ expect(result.palette[0]).toBe("#00ff00")
241
+ })
242
+
243
+ test("TerminalPalette scales color components correctly", async () => {
244
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
245
+
246
+ clock.setTimeout(() => {
247
+ stdin.emit("data", Buffer.from("\x1b]4;0;rgb:ffff/0000/0000\x07"))
248
+ for (let i = 1; i < 256; i++) {
249
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
250
+ }
251
+ }, 0)
252
+
253
+ await advanceClock(clock, timeout)
254
+
255
+ const result = await detectPromise
256
+
257
+ expect(result.palette[0]).toBe("#ff0000")
258
+ })
259
+
260
+ test("TerminalPalette returns null for colors that don't respond", async () => {
261
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection({ timeout: 1000 })
262
+
263
+ clock.setTimeout(() => {
264
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
265
+ }, 0)
266
+
267
+ await advanceClock(clock, timeout)
268
+
269
+ const result = await detectPromise
270
+
271
+ expect(result.palette[0]).toBe("#ff0000")
272
+ expect(result.palette.some((color: string | null) => color === null)).toBe(true)
273
+ })
274
+
275
+ test("TerminalPalette handles response split across chunks", async () => {
276
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
277
+
278
+ clock.setTimeout(() => {
279
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff"))
280
+ stdin.emit("data", Buffer.from("00aa\x07"))
281
+
282
+ stdin.emit("data", Buffer.from("\x1b]4;1;rgb:0000/"))
283
+ stdin.emit("data", Buffer.from("ffff/"))
284
+ stdin.emit("data", Buffer.from("0000\x07"))
285
+
286
+ for (let i = 2; i < 256; i++) {
287
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
288
+ }
289
+ }, 0)
290
+
291
+ await advanceClock(clock, timeout)
292
+
293
+ const result = await detectPromise
294
+
295
+ expect(result.palette[0]).toBe("#ff00aa")
296
+ expect(result.palette[1]).toBe("#00ff00")
297
+ })
298
+
299
+ test("TerminalPalette handles OSC response mixed with mouse events", async () => {
300
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
301
+
302
+ clock.setTimeout(() => {
303
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
304
+ stdin.emit("data", Buffer.from("\x1b[<0;10;5M"))
305
+ stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
306
+ stdin.emit("data", Buffer.from("\x1b[<0;11;5M"))
307
+ stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
308
+ stdin.emit("data", Buffer.from("\x1b[<0;12;5m"))
309
+
310
+ for (let i = 3; i < 256; i++) {
311
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
312
+ }
313
+ }, 0)
314
+
315
+ await advanceClock(clock, timeout)
316
+
317
+ const result = await detectPromise
318
+
319
+ expect(result.palette[0]).toBe("#ff00aa")
320
+ expect(result.palette[1]).toBe("#00ff00")
321
+ expect(result.palette[2]).toBe("#0000ff")
322
+ })
323
+
324
+ test("TerminalPalette handles OSC response mixed with key events", async () => {
325
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
326
+
327
+ clock.setTimeout(() => {
328
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
329
+ stdin.emit("data", Buffer.from("hello"))
330
+ stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
331
+ stdin.emit("data", Buffer.from("\x1b[A"))
332
+ stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
333
+ stdin.emit("data", Buffer.from("\x1b[B"))
334
+ stdin.emit("data", Buffer.from("\x1b]4;3;#ffff00\x07"))
335
+
336
+ for (let i = 4; i < 256; i++) {
337
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
338
+ }
339
+ }, 0)
340
+
341
+ await advanceClock(clock, timeout)
342
+
343
+ const result = await detectPromise
344
+
345
+ expect(result.palette[0]).toBe("#ff00aa")
346
+ expect(result.palette[1]).toBe("#00ff00")
347
+ expect(result.palette[2]).toBe("#0000ff")
348
+ expect(result.palette[3]).toBe("#ffff00")
349
+ })
350
+
351
+ test("TerminalPalette handles response split mid-escape sequence", async () => {
352
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
353
+
354
+ clock.setTimeout(() => {
355
+ stdin.emit("data", Buffer.from("\x1b"))
356
+ stdin.emit("data", Buffer.from("]4;0;#ff00aa\x07"))
357
+
358
+ stdin.emit("data", Buffer.from("\x1b]"))
359
+ stdin.emit("data", Buffer.from("4;1;#00ff00\x07"))
360
+
361
+ stdin.emit("data", Buffer.from("\x1b]4"))
362
+ stdin.emit("data", Buffer.from(";2;#0000ff\x07"))
363
+
364
+ stdin.emit("data", Buffer.from("\x1b]4;"))
365
+ stdin.emit("data", Buffer.from("3;#ffff00\x07"))
366
+
367
+ for (let i = 4; i < 256; i++) {
368
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
369
+ }
370
+ }, 0)
371
+
372
+ await advanceClock(clock, timeout)
373
+
374
+ const result = await detectPromise
375
+
376
+ expect(result.palette[0]).toBe("#ff00aa")
377
+ expect(result.palette[1]).toBe("#00ff00")
378
+ expect(result.palette[2]).toBe("#0000ff")
379
+ expect(result.palette[3]).toBe("#ffff00")
380
+ })
381
+
382
+ test("TerminalPalette handles mixed ANSI sequences and OSC responses", async () => {
383
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
384
+
385
+ clock.setTimeout(() => {
386
+ stdin.emit("data", Buffer.from("\x1b[2J"))
387
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
388
+ stdin.emit("data", Buffer.from("\x1b[H"))
389
+ stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
390
+ stdin.emit("data", Buffer.from("\x1b[31m"))
391
+ stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
392
+ stdin.emit("data", Buffer.from("\x1b[0m"))
393
+
394
+ for (let i = 3; i < 256; i++) {
395
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
396
+ }
397
+ }, 0)
398
+
399
+ await advanceClock(clock, timeout)
400
+
401
+ const result = await detectPromise
402
+
403
+ expect(result.palette[0]).toBe("#ff00aa")
404
+ expect(result.palette[1]).toBe("#00ff00")
405
+ expect(result.palette[2]).toBe("#0000ff")
406
+ })
407
+
408
+ test("TerminalPalette handles complex chunking with partial responses", async () => {
409
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
410
+
411
+ clock.setTimeout(() => {
412
+ const response0 = "\x1b]4;0;rgb:ffff/0000/aaaa\x07"
413
+ for (let i = 0; i < response0.length; i += 3) {
414
+ stdin.emit("data", Buffer.from(response0.slice(i, i + 3)))
415
+ }
416
+
417
+ stdin.emit("data", Buffer.from("\x1b]4;1"))
418
+ stdin.emit("data", Buffer.from(";#00"))
419
+ stdin.emit("data", Buffer.from("some junk data"))
420
+ stdin.emit("data", Buffer.from("ff00"))
421
+ stdin.emit("data", Buffer.from("\x1b[D"))
422
+ stdin.emit("data", Buffer.from("\x07"))
423
+
424
+ stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
425
+
426
+ for (let i = 2; i < 256; i++) {
427
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
428
+ }
429
+ }, 0)
430
+
431
+ await advanceClock(clock, timeout)
432
+
433
+ const result = await detectPromise
434
+
435
+ expect(result.palette[0]).toBe("#ff00aa")
436
+ expect(result.palette[1]).toBe("#00ff00")
437
+ })
438
+
439
+ test("TerminalPalette ignores malformed responses and waits for valid ones", async () => {
440
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
441
+
442
+ clock.setTimeout(() => {
443
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff00\x07"))
444
+ stdin.emit("data", Buffer.from("\x1b]4;1;rgb:gg00/0000/0000\x07"))
445
+ stdin.emit("data", Buffer.from("\x1b]4;2;#zzzzzz\x07"))
446
+
447
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
448
+ stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
449
+ stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
450
+
451
+ for (let i = 3; i < 256; i++) {
452
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
453
+ }
454
+ }, 0)
455
+
456
+ await advanceClock(clock, timeout)
457
+
458
+ const result = await detectPromise
459
+
460
+ expect(result.palette[0]).toBe("#ff00aa")
461
+ expect(result.palette[1]).toBe("#00ff00")
462
+ expect(result.palette[2]).toBe("#0000ff")
463
+ })
464
+
465
+ test("TerminalPalette handles buffer overflow gracefully", async () => {
466
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
467
+
468
+ clock.setTimeout(() => {
469
+ const junkData = "x".repeat(10000)
470
+ stdin.emit("data", Buffer.from(junkData))
471
+
472
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
473
+ stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
474
+
475
+ for (let i = 2; i < 256; i++) {
476
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
477
+ }
478
+ }, 0)
479
+
480
+ await advanceClock(clock, timeout)
481
+
482
+ const result = await detectPromise
483
+
484
+ expect(result.palette[0]).toBe("#ff00aa")
485
+ expect(result.palette[1]).toBe("#00ff00")
486
+ })
487
+
488
+ test("TerminalPalette handles all 256 colors in a single blob", async () => {
489
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
490
+
491
+ clock.setTimeout(() => {
492
+ let blob = ""
493
+ for (let i = 0; i < 256; i++) {
494
+ const color = i === 0 ? "#ff0011" : i === 1 ? "#00ff22" : i === 255 ? "#aabbcc" : "#000000"
495
+ blob += `\x1b]4;${i};${color}\x07`
496
+ }
497
+
498
+ stdin.emit("data", Buffer.from(blob))
499
+ }, 0)
500
+
501
+ await advanceClock(clock, timeout)
502
+
503
+ const result = await detectPromise
504
+
505
+ expect(result.palette[0]).toBe("#ff0011")
506
+ expect(result.palette[1]).toBe("#00ff22")
507
+ expect(result.palette[255]).toBe("#aabbcc")
508
+ expect(result.palette.length).toBe(256)
509
+ })
510
+
511
+ test("TerminalPalette handles blob split across multiple chunks", async () => {
512
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
513
+
514
+ clock.setTimeout(() => {
515
+ let blob = ""
516
+ for (let i = 0; i < 256; i++) {
517
+ const color = i === 5 ? "#112233" : i === 100 ? "#445566" : i === 200 ? "#778899" : "#000000"
518
+ blob += `\x1b]4;${i};${color}\x07`
519
+ }
520
+
521
+ const chunkSize = 500
522
+ for (let i = 0; i < blob.length; i += chunkSize) {
523
+ stdin.emit("data", Buffer.from(blob.slice(i, i + chunkSize)))
524
+ }
525
+ }, 0)
526
+
527
+ await advanceClock(clock, timeout)
528
+
529
+ const result = await detectPromise
530
+
531
+ expect(result.palette[5]).toBe("#112233")
532
+ expect(result.palette[100]).toBe("#445566")
533
+ expect(result.palette[200]).toBe("#778899")
534
+ expect(result.palette.length).toBe(256)
535
+ })
536
+
537
+ test("TerminalPalette handles blob with mixed junk data", async () => {
538
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
539
+
540
+ clock.setTimeout(() => {
541
+ let blob = ""
542
+ for (let i = 0; i < 256; i++) {
543
+ const color = i === 10 ? "#abcdef" : i === 50 ? "#fedcba" : "#000000"
544
+ blob += `\x1b]4;${i};${color}\x07`
545
+
546
+ if (i % 20 === 0) {
547
+ blob += "JUNK_DATA_HERE"
548
+ }
549
+ if (i % 30 === 0) {
550
+ blob += "\x1b[2J\x1b[H"
551
+ }
552
+ if (i % 40 === 0) {
553
+ blob += "\x1b[<0;10;5M"
554
+ }
555
+ }
556
+
557
+ stdin.emit("data", Buffer.from(blob))
558
+ }, 0)
559
+
560
+ await advanceClock(clock, timeout)
561
+
562
+ const result = await detectPromise
563
+
564
+ expect(result.palette[10]).toBe("#abcdef")
565
+ expect(result.palette[50]).toBe("#fedcba")
566
+ expect(result.palette.length).toBe(256)
567
+ })
568
+
569
+ test("TerminalPalette handles realistic terminal response pattern", async () => {
570
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
571
+
572
+ clock.setTimeout(() => {
573
+ let chunk1 = ""
574
+ for (let i = 0; i <= 5; i++) {
575
+ chunk1 += `\x1b]4;${i};#ff0000\x07`
576
+ }
577
+ stdin.emit("data", Buffer.from(chunk1))
578
+
579
+ let chunk2 = ""
580
+ for (let i = 6; i <= 50; i++) {
581
+ chunk2 += `\x1b]4;${i};#00ff00\x07`
582
+ }
583
+ stdin.emit("data", Buffer.from(chunk2.slice(0, 200)))
584
+ stdin.emit("data", Buffer.from(chunk2.slice(200)))
585
+
586
+ stdin.emit("data", Buffer.from("\x1b[<35;20;10M"))
587
+ let chunk3 = ""
588
+ for (let i = 51; i <= 150; i++) {
589
+ chunk3 += `\x1b]4;${i};#0000ff\x07`
590
+ }
591
+ stdin.emit("data", Buffer.from(chunk3))
592
+
593
+ let chunk4 = ""
594
+ for (let i = 151; i <= 255; i++) {
595
+ chunk4 += `\x1b]4;${i};#ffffff\x07`
596
+ }
597
+ stdin.emit("data", Buffer.from(chunk4))
598
+ }, 0)
599
+
600
+ await advanceClock(clock, timeout)
601
+
602
+ const result = await detectPromise
603
+
604
+ expect(result.palette[0]).toBe("#ff0000")
605
+ expect(result.palette[5]).toBe("#ff0000")
606
+ expect(result.palette[6]).toBe("#00ff00")
607
+ expect(result.palette[50]).toBe("#00ff00")
608
+ expect(result.palette[51]).toBe("#0000ff")
609
+ expect(result.palette[150]).toBe("#0000ff")
610
+ expect(result.palette[151]).toBe("#ffffff")
611
+ expect(result.palette[255]).toBe("#ffffff")
612
+ expect(result.palette.length).toBe(256)
613
+ })
614
+
615
+ test("TerminalPalette uses custom write function when provided", async () => {
616
+ const writtenData: string[] = []
617
+
618
+ const customWrite = (data: string | Buffer) => {
619
+ writtenData.push(data.toString())
620
+ return true
621
+ }
622
+
623
+ const { stdin, clock, palette } = createPaletteHarness({ writeFn: customWrite })
624
+
625
+ const detectPromise = palette.detectOSCSupport(500)
626
+
627
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
628
+
629
+ await advanceClock(clock, 500)
630
+
631
+ const result = await detectPromise
632
+
633
+ expect(result).toBe(true)
634
+ expect(writtenData.length).toBe(1)
635
+ expect(writtenData[0]).toBe("\x1b]4;0;?\x07")
636
+ })
637
+
638
+ test("TerminalPalette uses custom write function for palette detection", async () => {
639
+ const writtenData: string[] = []
640
+
641
+ const customWrite = (data: string | Buffer) => {
642
+ writtenData.push(data.toString())
643
+ return true
644
+ }
645
+
646
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection({ writeFn: customWrite })
647
+
648
+ clock.setTimeout(() => {
649
+ for (let i = 0; i < 256; i++) {
650
+ const color = "#aabbcc"
651
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};${color}\x07`))
652
+ }
653
+ }, 0)
654
+
655
+ await advanceClock(clock, timeout)
656
+
657
+ await detectPromise
658
+
659
+ expect(writtenData.length).toBe(3)
660
+ expect(writtenData[0]).toBe("\x1b]4;0;?\x07")
661
+
662
+ const paletteQuery = writtenData[1]
663
+ for (let i = 0; i < 256; i++) {
664
+ expect(paletteQuery).toContain(`\x1b]4;${i};?\x07`)
665
+ }
666
+
667
+ const specialQuery = writtenData[2]
668
+ expect(specialQuery).toContain("\x1b]10;?\x07")
669
+ expect(specialQuery).toContain("\x1b]11;?\x07")
670
+ })
671
+
672
+ test("TerminalPalette falls back to stdout.write when no custom write function provided", async () => {
673
+ const clock = new ManualClock()
674
+ const stdin = new MockStream() as any
675
+ const writtenData: string[] = []
676
+
677
+ const stdout = new MockStream() as any
678
+ stdout.write = (data: string) => {
679
+ writtenData.push(data)
680
+ return true
681
+ }
682
+
683
+ const palette = new TerminalPalette({
684
+ stdin,
685
+ stdout,
686
+ isLegacyTmux: false,
687
+ clock,
688
+ })
689
+
690
+ const detectPromise = palette.detectOSCSupport(500)
691
+
692
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
693
+
694
+ await advanceClock(clock, 500)
695
+
696
+ const result = await detectPromise
697
+
698
+ expect(result).toBe(true)
699
+ expect(writtenData.length).toBe(1)
700
+ expect(writtenData[0]).toBe("\x1b]4;0;?\x07")
701
+ })
702
+
703
+ test("TerminalPalette custom write function can intercept and modify output", async () => {
704
+ const interceptedWrites: string[] = []
705
+ let actualWrites = 0
706
+
707
+ const customWrite = (data: string | Buffer) => {
708
+ interceptedWrites.push(data.toString())
709
+ actualWrites++
710
+ return true
711
+ }
712
+
713
+ const { stdin, clock, palette } = createPaletteHarness({ writeFn: customWrite })
714
+
715
+ const detectPromise = palette.detectOSCSupport(500)
716
+
717
+ stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
718
+
719
+ await advanceClock(clock, 500)
720
+
721
+ await detectPromise
722
+
723
+ expect(actualWrites).toBe(1)
724
+ expect(interceptedWrites.length).toBe(1)
725
+ expect(interceptedWrites[0]).toBe("\x1b]4;0;?\x07")
726
+ })
727
+
728
+ test("TerminalPalette detects all special OSC colors (10-19)", async () => {
729
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
730
+
731
+ clock.setTimeout(() => {
732
+ for (let i = 0; i < 256; i++) {
733
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
734
+ }
735
+ stdin.emit("data", Buffer.from("\x1b]10;#ff0001\x07"))
736
+ stdin.emit("data", Buffer.from("\x1b]11;#ff0002\x07"))
737
+ stdin.emit("data", Buffer.from("\x1b]12;#ff0003\x07"))
738
+ stdin.emit("data", Buffer.from("\x1b]13;#ff0004\x07"))
739
+ stdin.emit("data", Buffer.from("\x1b]14;#ff0005\x07"))
740
+ stdin.emit("data", Buffer.from("\x1b]15;#ff0006\x07"))
741
+ stdin.emit("data", Buffer.from("\x1b]16;#ff0007\x07"))
742
+ stdin.emit("data", Buffer.from("\x1b]17;#ff0008\x07"))
743
+ stdin.emit("data", Buffer.from("\x1b]19;#ff0009\x07"))
744
+ }, 0)
745
+
746
+ await advanceClock(clock, timeout)
747
+
748
+ const result = await detectPromise
749
+
750
+ expect(result.defaultForeground).toBe("#ff0001")
751
+ expect(result.defaultBackground).toBe("#ff0002")
752
+ expect(result.cursorColor).toBe("#ff0003")
753
+ expect(result.mouseForeground).toBe("#ff0004")
754
+ expect(result.mouseBackground).toBe("#ff0005")
755
+ expect(result.tekForeground).toBe("#ff0006")
756
+ expect(result.tekBackground).toBe("#ff0007")
757
+ expect(result.highlightBackground).toBe("#ff0008")
758
+ expect(result.highlightForeground).toBe("#ff0009")
759
+ })
760
+
761
+ test("TerminalPalette only wraps OSC 4 and only queries tmux-supported special colors in legacy tmux", async () => {
762
+ const writes: string[] = []
763
+ const { clock, detectPromise, timeout } = await startPaletteDetection({
764
+ isLegacyTmux: true,
765
+ isTmux: true,
766
+ writeFn: (data) => {
767
+ writes.push(data.toString())
768
+ return true
769
+ },
770
+ })
771
+
772
+ expect(writes.some((write) => write.startsWith("\x1bPtmux;\x1b\x1b]4;"))).toBe(true)
773
+ expect(writes).toContain("\x1b]10;?\x07\x1b]11;?\x07\x1b]12;?\x07")
774
+ expect(writes.some((write) => write.includes("\x1b]13;?"))).toBe(false)
775
+
776
+ await advanceClock(clock, timeout)
777
+ await detectPromise
778
+ })
779
+
780
+ test("TerminalPalette uses plain OSC 4 but limits special color queries in modern tmux", async () => {
781
+ const writes: string[] = []
782
+ const { clock, detectPromise, timeout } = await startPaletteDetection({
783
+ isTmux: true,
784
+ writeFn: (data) => {
785
+ writes.push(data.toString())
786
+ return true
787
+ },
788
+ })
789
+
790
+ expect(writes).toContain("\x1b]4;0;?\x07")
791
+ expect(writes.some((write) => write.startsWith("\x1bPtmux;"))).toBe(false)
792
+ expect(writes).toContain("\x1b]10;?\x07\x1b]11;?\x07\x1b]12;?\x07")
793
+ expect(writes.some((write) => write.includes("\x1b]13;?"))).toBe(false)
794
+
795
+ await advanceClock(clock, timeout)
796
+ await detectPromise
797
+ })
798
+
799
+ test("TerminalPalette handles special colors in rgb format", async () => {
800
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
801
+
802
+ clock.setTimeout(() => {
803
+ for (let i = 0; i < 256; i++) {
804
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
805
+ }
806
+ stdin.emit("data", Buffer.from("\x1b]10;rgb:ffff/0000/0000\x07"))
807
+ stdin.emit("data", Buffer.from("\x1b]11;rgb:0000/ffff/0000\x07"))
808
+ }, 0)
809
+
810
+ await advanceClock(clock, timeout)
811
+
812
+ const result = await detectPromise
813
+
814
+ expect(result.defaultForeground).toBe("#ff0000")
815
+ expect(result.defaultBackground).toBe("#00ff00")
816
+ })
817
+
818
+ test("TerminalPalette handles missing special colors gracefully", async () => {
819
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
820
+
821
+ clock.setTimeout(() => {
822
+ for (let i = 0; i < 256; i++) {
823
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
824
+ }
825
+ stdin.emit("data", Buffer.from("\x1b]10;#ff0001\x07"))
826
+ stdin.emit("data", Buffer.from("\x1b]11;#ff0002\x07"))
827
+ }, 0)
828
+
829
+ await advanceClock(clock, timeout)
830
+
831
+ const result = await detectPromise
832
+
833
+ expect(result.defaultForeground).toBe("#ff0001")
834
+ expect(result.defaultBackground).toBe("#ff0002")
835
+ expect(result.cursorColor).toBe(null)
836
+ expect(result.mouseForeground).toBe(null)
837
+ expect(result.mouseBackground).toBe(null)
838
+ })
839
+
840
+ test("TerminalPalette special colors with ST terminator", async () => {
841
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
842
+
843
+ clock.setTimeout(() => {
844
+ for (let i = 0; i < 256; i++) {
845
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
846
+ }
847
+ stdin.emit("data", Buffer.from("\x1b]10;#aabbcc\x1b\\"))
848
+ stdin.emit("data", Buffer.from("\x1b]11;#ddeeff\x1b\\"))
849
+ }, 0)
850
+
851
+ await advanceClock(clock, timeout)
852
+
853
+ const result = await detectPromise
854
+
855
+ expect(result.defaultForeground).toBe("#aabbcc")
856
+ expect(result.defaultBackground).toBe("#ddeeff")
857
+ })
858
+
859
+ test("TerminalPalette handles mixed palette and special color responses", async () => {
860
+ const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
861
+
862
+ clock.setTimeout(() => {
863
+ stdin.emit("data", Buffer.from("\x1b]4;0;#010203\x07"))
864
+ stdin.emit("data", Buffer.from("\x1b]10;#aabbcc\x07"))
865
+ stdin.emit("data", Buffer.from("\x1b]4;1;#040506\x07"))
866
+ stdin.emit("data", Buffer.from("\x1b]11;#ddeeff\x07"))
867
+ for (let i = 2; i < 256; i++) {
868
+ stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
869
+ }
870
+ }, 0)
871
+
872
+ await advanceClock(clock, timeout)
873
+
874
+ const result = await detectPromise
875
+
876
+ expect(result.palette[0]).toBe("#010203")
877
+ expect(result.palette[1]).toBe("#040506")
878
+ expect(result.defaultForeground).toBe("#aabbcc")
879
+ expect(result.defaultBackground).toBe("#ddeeff")
880
+ })
881
+
882
+ test("TerminalPalette returns null special colors on non-TTY", async () => {
883
+ const { stdin, stdout, clock, palette } = createPaletteHarness()
884
+ stdin.isTTY = false
885
+ stdout.isTTY = false
886
+
887
+ const detectPromise = palette.detect({ timeout: 100 })
888
+
889
+ await advanceClock(clock, 100)
890
+
891
+ const result = await detectPromise
892
+
893
+ expect(result.defaultForeground).toBe(null)
894
+ expect(result.defaultBackground).toBe(null)
895
+ expect(result.cursorColor).toBe(null)
896
+ expect(result.palette.every((c: string | null) => c === null)).toBe(true)
897
+ })
898
+
899
+ test("TerminalPalette returns null special colors on OSC not supported", async () => {
900
+ const { clock, palette } = createPaletteHarness()
901
+
902
+ const detectPromise = palette.detect({ timeout: 100 })
903
+
904
+ await advanceClock(clock, 300)
905
+
906
+ const result = await detectPromise
907
+
908
+ expect(result.defaultForeground).toBe(null)
909
+ expect(result.defaultBackground).toBe(null)
910
+ expect(result.cursorColor).toBe(null)
911
+ expect(result.palette.every((c: string | null) => c === null)).toBe(true)
912
+ })
913
+
914
+ test("TerminalPalette can read OSC from router subscription source", async () => {
915
+ const stdin = new MockStream() as any
916
+
917
+ const handlers = new Set<(sequence: string) => void>()
918
+ let subscribeCount = 0
919
+ let unsubscribeCount = 0
920
+
921
+ const oscSource = {
922
+ subscribeOsc(handler: (sequence: string) => void) {
923
+ subscribeCount++
924
+ handlers.add(handler)
925
+ return () => {
926
+ unsubscribeCount++
927
+ handlers.delete(handler)
928
+ }
929
+ },
930
+ }
931
+
932
+ const clock = new ManualClock()
933
+ const stdout = new MockStream() as any
934
+ const palette = new TerminalPalette({
935
+ stdin,
936
+ stdout,
937
+ isLegacyTmux: false,
938
+ oscSource,
939
+ clock,
940
+ })
941
+
942
+ const detectPromise = palette.detectOSCSupport(500)
943
+ for (const handler of handlers) {
944
+ handler("\x1b]4;0;#ff0000\x07")
945
+ }
946
+
947
+ await advanceClock(clock, 500)
948
+
949
+ const supported = await detectPromise
950
+ expect(supported).toBe(true)
951
+ expect(subscribeCount).toBe(1)
952
+ expect(unsubscribeCount).toBe(1)
953
+ expect(stdin.listenerCount("data")).toBe(0)
954
+ })