@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,880 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks"
2
+ import assert from "node:assert/strict"
3
+ import { existsSync, readFileSync } from "node:fs"
4
+ import path from "node:path"
5
+ import { after, afterEach, before, beforeEach, describe as nodeDescribe, test as nodeTest } from "node:test"
6
+ import { fileURLToPath } from "node:url"
7
+ import { inspect, isDeepStrictEqual } from "node:util"
8
+
9
+ type AnyFunction = (...args: any[]) => any
10
+ type ThrowMatcher = RegExp | string | Error | ((value: unknown) => boolean) | (new (...args: any[]) => Error)
11
+ const asymmetricMatcher = Symbol("asymmetricMatcher")
12
+
13
+ interface AsymmetricMatcher {
14
+ readonly [asymmetricMatcher]: true
15
+ matches(received: unknown): boolean
16
+ }
17
+
18
+ interface SnapshotTestContext {
19
+ filePath: string
20
+ fullTestName: string
21
+ }
22
+
23
+ interface MockedFunction<Fn extends AnyFunction = AnyFunction> {
24
+ (...args: Parameters<Fn>): ReturnType<Fn>
25
+ mock: {
26
+ calls: unknown[][]
27
+ }
28
+ mockImplementation(implementation: Fn): MockedFunction<Fn>
29
+ mockRestore(): void
30
+ mockClear(): void
31
+ }
32
+
33
+ function fail(message: string): never {
34
+ throw new assert.AssertionError({ message })
35
+ }
36
+
37
+ function formatValue(value: unknown): string {
38
+ return inspect(value, { depth: 5 })
39
+ }
40
+
41
+ const snapshotContextStorage = new AsyncLocalStorage<SnapshotTestContext>()
42
+ const snapshotFileCache = new Map<string, Map<string, string>>()
43
+ const snapshotCounters = new Map<string, number>()
44
+ const describeNameStack: string[] = []
45
+
46
+ function normalizeNewlines(value: string): string {
47
+ return value.replace(/\r\n/g, "\n")
48
+ }
49
+
50
+ function serializeSnapshotValue(value: unknown): string {
51
+ if (typeof value === "string") {
52
+ return `"${normalizeNewlines(value).replace(/\\/g, "\\\\")}"`
53
+ }
54
+
55
+ return normalizeNewlines(formatValue(value))
56
+ }
57
+
58
+ function normalizeInlineSnapshot(snapshot: string): string {
59
+ const lines = normalizeNewlines(snapshot).split("\n")
60
+ const hasWrapperWhitespace = lines.length >= 2 && lines[0]?.trim() === "" && lines.at(-1)?.trim() === ""
61
+
62
+ if (!hasWrapperWhitespace) {
63
+ return lines.join("\n").replace(/\\/g, "\\\\")
64
+ }
65
+
66
+ lines.shift()
67
+ lines.pop()
68
+
69
+ const indentation = lines.reduce((smallest, line) => {
70
+ if (line.trim() === "") {
71
+ return smallest
72
+ }
73
+
74
+ const lineIndentation = line.match(/^\s*/)?.[0].length ?? 0
75
+ return Math.min(smallest, lineIndentation)
76
+ }, Number.POSITIVE_INFINITY)
77
+
78
+ if (!Number.isFinite(indentation)) {
79
+ return lines.join("\n")
80
+ }
81
+
82
+ return lines
83
+ .map((line) => line.slice(indentation))
84
+ .join("\n")
85
+ .replace(/\\/g, "\\\\")
86
+ }
87
+
88
+ function getSnapshotContext(): SnapshotTestContext {
89
+ const context = snapshotContextStorage.getStore()
90
+ if (!context) {
91
+ fail("Snapshot matchers can only be used inside a test callback")
92
+ }
93
+
94
+ return context
95
+ }
96
+
97
+ function getSnapshotFilePath(testFilePath: string): string {
98
+ const emittedMarker = `${path.sep}.node-test${path.sep}`
99
+ const normalizedFilePath = testFilePath.includes(emittedMarker)
100
+ ? testFilePath.replace(emittedMarker, path.sep)
101
+ : testFilePath
102
+ const sourceFilePath = normalizedFilePath.endsWith(".js")
103
+ ? `${normalizedFilePath.slice(0, -3)}.ts`
104
+ : normalizedFilePath
105
+
106
+ return path.join(path.dirname(sourceFilePath), "__snapshots__", `${path.basename(sourceFilePath)}.snap`)
107
+ }
108
+
109
+ function readSnapshotFile(snapshotPath: string): Map<string, string> {
110
+ const cached = snapshotFileCache.get(snapshotPath)
111
+ if (cached) {
112
+ return cached
113
+ }
114
+
115
+ const snapshots = new Map<string, string>()
116
+
117
+ if (existsSync(snapshotPath)) {
118
+ const contents = normalizeNewlines(readFileSync(snapshotPath, "utf8"))
119
+ const snapshotPattern = /exports\[`([\s\S]*?)`\] = `\n([\s\S]*?)\n`;/g
120
+
121
+ for (const match of contents.matchAll(snapshotPattern)) {
122
+ snapshots.set(match[1], match[2])
123
+ }
124
+ }
125
+
126
+ snapshotFileCache.set(snapshotPath, snapshots)
127
+ return snapshots
128
+ }
129
+
130
+ function nextSnapshotIndex(filePath: string, snapshotName: string): number {
131
+ const counterKey = `${filePath}\u0000${snapshotName}`
132
+ const nextIndex = (snapshotCounters.get(counterKey) ?? 0) + 1
133
+ snapshotCounters.set(counterKey, nextIndex)
134
+ return nextIndex
135
+ }
136
+
137
+ function assertSnapshotMatch(received: unknown, expected: string, label: string, inverted: boolean): void {
138
+ if (inverted) {
139
+ fail(`Snapshot negation is not supported for ${label}`)
140
+ }
141
+
142
+ const actual = serializeSnapshotValue(received)
143
+ if (actual !== expected) {
144
+ fail(`Expected ${label} to match\n\nExpected:\n${expected}\n\nReceived:\n${actual}`)
145
+ }
146
+ }
147
+
148
+ function parseStackFilePath(candidate: string): string {
149
+ return candidate.startsWith("file://") ? fileURLToPath(candidate) : candidate
150
+ }
151
+
152
+ function getRegistrationFilePath(): string {
153
+ const stack = new Error().stack?.split("\n") ?? []
154
+
155
+ for (const line of stack.slice(1)) {
156
+ const match = line.match(/\((file:\/\/.+?|\/.+?):\d+:\d+\)$/) ?? line.match(/at (file:\/\/.+?|\/.+?):\d+:\d+$/)
157
+ if (!match) {
158
+ continue
159
+ }
160
+
161
+ const filePath = parseStackFilePath(match[1])
162
+ const fileName = path.basename(filePath)
163
+ if (fileName === "bun-test-node.ts" || fileName === "bun-test-node.js") {
164
+ continue
165
+ }
166
+
167
+ return filePath
168
+ }
169
+
170
+ fail("Could not determine the current test file")
171
+ }
172
+
173
+ function wrapTestCallback(filePath: string, fullTestName: string, callback: AnyFunction): AnyFunction {
174
+ return function wrappedTestCallback(this: unknown, ...args: unknown[]) {
175
+ return snapshotContextStorage.run({ filePath, fullTestName }, () => callback.apply(this, args))
176
+ }
177
+ }
178
+
179
+ function getFullTestName(name: string): string {
180
+ return [...describeNameStack, name].join(" ")
181
+ }
182
+
183
+ function registerTest(base: AnyFunction, name: string, optionsOrFn?: unknown, maybeFn?: unknown): unknown {
184
+ const filePath = getRegistrationFilePath()
185
+ const fullTestName = getFullTestName(name)
186
+
187
+ if (typeof optionsOrFn === "function") {
188
+ return base(name, wrapTestCallback(filePath, fullTestName, optionsOrFn as AnyFunction))
189
+ }
190
+
191
+ if (typeof maybeFn === "function") {
192
+ return base(name, optionsOrFn, wrapTestCallback(filePath, fullTestName, maybeFn as AnyFunction))
193
+ }
194
+
195
+ return base(name, optionsOrFn, maybeFn)
196
+ }
197
+
198
+ function wrapDescribeCallback(name: string, callback: AnyFunction): AnyFunction {
199
+ return function wrappedDescribeCallback(this: unknown, ...args: unknown[]) {
200
+ describeNameStack.push(name)
201
+
202
+ try {
203
+ return callback.apply(this, args)
204
+ } finally {
205
+ describeNameStack.pop()
206
+ }
207
+ }
208
+ }
209
+
210
+ function registerDescribe(base: AnyFunction, name: string, optionsOrFn?: unknown, maybeFn?: unknown): unknown {
211
+ if (typeof optionsOrFn === "function") {
212
+ return base(name, wrapDescribeCallback(name, optionsOrFn as AnyFunction))
213
+ }
214
+
215
+ if (typeof maybeFn === "function") {
216
+ return base(name, optionsOrFn, wrapDescribeCallback(name, maybeFn as AnyFunction))
217
+ }
218
+
219
+ return base(name, optionsOrFn, maybeFn)
220
+ }
221
+
222
+ function createTestVariant(base: AnyFunction): AnyFunction {
223
+ return function wrappedTest(name: string, optionsOrFn?: unknown, maybeFn?: unknown): unknown {
224
+ return registerTest(base, name, optionsOrFn, maybeFn)
225
+ }
226
+ }
227
+
228
+ function formatEachName(name: string, args: readonly unknown[]): string {
229
+ let index = 0
230
+
231
+ return name.replace(/%s/g, () => String(args[index++]))
232
+ }
233
+
234
+ function createEach(base: AnyFunction) {
235
+ return (cases: readonly unknown[]) => {
236
+ return (name: string, optionsOrFn?: unknown, maybeFn?: unknown): void => {
237
+ for (const testCase of cases) {
238
+ const args = Array.isArray(testCase) ? [...testCase] : [testCase]
239
+ const testName = formatEachName(name, args)
240
+
241
+ if (typeof optionsOrFn === "function") {
242
+ registerTest(base, testName, function eachCallback(this: unknown) {
243
+ return (optionsOrFn as AnyFunction).apply(this, args)
244
+ })
245
+ continue
246
+ }
247
+
248
+ if (typeof maybeFn === "function") {
249
+ registerTest(base, testName, optionsOrFn, function eachCallback(this: unknown) {
250
+ return (maybeFn as AnyFunction).apply(this, args)
251
+ })
252
+ continue
253
+ }
254
+
255
+ registerTest(base, testName, optionsOrFn, maybeFn)
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ function attachEach(target: AnyFunction, base: AnyFunction): void {
262
+ ;(target as AnyFunction & { each?: ReturnType<typeof createEach> }).each = createEach(base)
263
+ }
264
+
265
+ function createTest(base: typeof nodeTest): typeof nodeTest {
266
+ const wrapped = createTestVariant(base) as typeof nodeTest
267
+
268
+ Object.assign(wrapped, base)
269
+ attachEach(wrapped, base)
270
+
271
+ if (typeof base.skip === "function") {
272
+ wrapped.skip = createTestVariant(base.skip) as typeof base.skip
273
+ attachEach(wrapped.skip as AnyFunction, base.skip)
274
+ }
275
+
276
+ if (typeof base.only === "function") {
277
+ wrapped.only = createTestVariant(base.only) as typeof base.only
278
+ attachEach(wrapped.only as AnyFunction, base.only)
279
+ }
280
+
281
+ if (typeof base.todo === "function") {
282
+ wrapped.todo = createTestVariant(base.todo) as typeof base.todo
283
+ attachEach(wrapped.todo as AnyFunction, base.todo)
284
+ }
285
+
286
+ return wrapped
287
+ }
288
+
289
+ function createDescribeVariant(base: AnyFunction): AnyFunction {
290
+ return function wrappedDescribe(name: string, optionsOrFn?: unknown, maybeFn?: unknown): unknown {
291
+ return registerDescribe(base, name, optionsOrFn, maybeFn)
292
+ }
293
+ }
294
+
295
+ function createDescribe(base: typeof nodeDescribe): typeof nodeDescribe {
296
+ const wrapped = createDescribeVariant(base) as typeof nodeDescribe
297
+
298
+ Object.assign(wrapped, base)
299
+
300
+ if (typeof base.skip === "function") {
301
+ wrapped.skip = createDescribeVariant(base.skip) as typeof base.skip
302
+ }
303
+
304
+ if (typeof base.only === "function") {
305
+ wrapped.only = createDescribeVariant(base.only) as typeof base.only
306
+ }
307
+
308
+ if (typeof base.todo === "function") {
309
+ wrapped.todo = createDescribeVariant(base.todo) as typeof base.todo
310
+ }
311
+
312
+ return wrapped
313
+ }
314
+
315
+ function hasLength(value: unknown): value is { length: number } {
316
+ return value != null && typeof (value as { length?: unknown }).length === "number"
317
+ }
318
+
319
+ function isComparable(value: unknown): value is number | bigint {
320
+ return typeof value === "number" || typeof value === "bigint"
321
+ }
322
+
323
+ function isObjectLike(value: unknown): value is Record<PropertyKey, unknown> {
324
+ return (typeof value === "object" || typeof value === "function") && value !== null
325
+ }
326
+
327
+ function isAsymmetricMatcher(value: unknown): value is AsymmetricMatcher {
328
+ return isObjectLike(value) && value[asymmetricMatcher] === true && typeof value.matches === "function"
329
+ }
330
+
331
+ function createAnyMatcher(expectedType: unknown): AsymmetricMatcher {
332
+ if (typeof expectedType !== "function") {
333
+ fail("expect.any() requires a constructor")
334
+ }
335
+
336
+ return {
337
+ [asymmetricMatcher]: true,
338
+ matches(received) {
339
+ if (expectedType === Number) return typeof received === "number" || received instanceof Number
340
+ if (expectedType === String) return typeof received === "string" || received instanceof String
341
+ if (expectedType === Boolean) return typeof received === "boolean" || received instanceof Boolean
342
+ if (expectedType === BigInt) return typeof received === "bigint"
343
+ if (expectedType === Symbol) return typeof received === "symbol"
344
+
345
+ return received instanceof expectedType
346
+ },
347
+ }
348
+ }
349
+
350
+ function valuesEqual(received: unknown, expected: unknown): boolean {
351
+ if (isAsymmetricMatcher(expected)) {
352
+ return expected.matches(received)
353
+ }
354
+
355
+ if (Array.isArray(received) && Array.isArray(expected)) {
356
+ if (received.length !== expected.length) {
357
+ return false
358
+ }
359
+
360
+ for (let index = 0; index < expected.length; index += 1) {
361
+ if (!valuesEqual(received[index], expected[index])) {
362
+ return false
363
+ }
364
+ }
365
+
366
+ return true
367
+ }
368
+
369
+ return isDeepStrictEqual(received, expected)
370
+ }
371
+
372
+ function objectMatches(received: unknown, expected: unknown): boolean {
373
+ if (isAsymmetricMatcher(expected)) {
374
+ return expected.matches(received)
375
+ }
376
+
377
+ if (!isObjectLike(expected)) {
378
+ return valuesEqual(received, expected)
379
+ }
380
+
381
+ if (!isObjectLike(received)) {
382
+ return false
383
+ }
384
+
385
+ for (const key of Reflect.ownKeys(expected)) {
386
+ if (!Object.hasOwn(received, key)) {
387
+ return false
388
+ }
389
+
390
+ if (!objectMatches(received[key], expected[key])) {
391
+ return false
392
+ }
393
+ }
394
+
395
+ return true
396
+ }
397
+
398
+ function createMock<Fn extends AnyFunction>(implementation: Fn, restore?: () => void): MockedFunction<Fn> {
399
+ const calls: unknown[][] = []
400
+ let currentImplementation = implementation
401
+
402
+ const mocked = function (this: unknown, ...args: Parameters<Fn>): ReturnType<Fn> {
403
+ calls.push(args)
404
+ return currentImplementation.apply(this, args)
405
+ } as MockedFunction<Fn>
406
+
407
+ mocked.mock = { calls }
408
+ mocked.mockImplementation = (nextImplementation: Fn) => {
409
+ currentImplementation = nextImplementation
410
+ return mocked
411
+ }
412
+ mocked.mockRestore = () => {
413
+ calls.length = 0
414
+ currentImplementation = implementation
415
+ restore?.()
416
+ }
417
+ mocked.mockClear = () => {
418
+ calls.length = 0
419
+ }
420
+
421
+ return mocked
422
+ }
423
+
424
+ function getMockCalls(received: unknown): unknown[][] {
425
+ const calls = (received as MockedFunction | undefined)?.mock?.calls
426
+
427
+ if (!Array.isArray(calls)) {
428
+ fail(`Expected ${formatValue(received)} to be a mock or spy`)
429
+ }
430
+
431
+ return calls
432
+ }
433
+
434
+ function isErrorConstructor(matcher: ThrowMatcher): matcher is new (...args: any[]) => Error {
435
+ return typeof matcher === "function" && matcher.prototype instanceof Error
436
+ }
437
+
438
+ function matchesThrow(error: unknown, expected?: ThrowMatcher): boolean {
439
+ if (expected == null) {
440
+ return true
441
+ }
442
+
443
+ if (typeof expected === "string") {
444
+ return String(error instanceof Error ? error.message : error).includes(expected)
445
+ }
446
+
447
+ if (expected instanceof RegExp) {
448
+ return expected.test(String(error instanceof Error ? error.message : error))
449
+ }
450
+
451
+ if (expected instanceof Error) {
452
+ return (
453
+ error === expected ||
454
+ (error instanceof Error && error.name === expected.name && error.message === expected.message)
455
+ )
456
+ }
457
+
458
+ if (isErrorConstructor(expected)) {
459
+ return error instanceof expected
460
+ }
461
+
462
+ return Boolean(expected(error))
463
+ }
464
+
465
+ function formatThrowMatcher(expected?: ThrowMatcher): string {
466
+ if (expected == null) {
467
+ return ""
468
+ }
469
+
470
+ return ` matching ${formatValue(expected)}`
471
+ }
472
+
473
+ class AsyncExpectation<T> {
474
+ constructor(
475
+ private readonly received: PromiseLike<T> | T,
476
+ private readonly mode: "resolves" | "rejects",
477
+ private readonly inverted = false,
478
+ ) {}
479
+
480
+ get not(): AsyncExpectation<T> {
481
+ return new AsyncExpectation(this.received, this.mode, !this.inverted)
482
+ }
483
+
484
+ async toBe(expected: unknown): Promise<void> {
485
+ new Expectation(await this.unwrap(), this.inverted).toBe(expected)
486
+ }
487
+
488
+ async toEqual(expected: unknown): Promise<void> {
489
+ new Expectation(await this.unwrap(), this.inverted).toEqual(expected)
490
+ }
491
+
492
+ async toBeNull(): Promise<void> {
493
+ new Expectation(await this.unwrap(), this.inverted).toBeNull()
494
+ }
495
+
496
+ async toBeUndefined(): Promise<void> {
497
+ new Expectation(await this.unwrap(), this.inverted).toBeUndefined()
498
+ }
499
+
500
+ async toContain(expected: unknown): Promise<void> {
501
+ new Expectation(await this.unwrap(), this.inverted).toContain(expected)
502
+ }
503
+
504
+ async toMatchSnapshot(snapshotName?: string): Promise<void> {
505
+ new Expectation(await this.unwrap(), this.inverted).toMatchSnapshot(snapshotName)
506
+ }
507
+
508
+ async toMatchInlineSnapshot(snapshot: string): Promise<void> {
509
+ new Expectation(await this.unwrap(), this.inverted).toMatchInlineSnapshot(snapshot)
510
+ }
511
+
512
+ async toThrow(expected?: ThrowMatcher): Promise<void> {
513
+ const error = await this.unwrap()
514
+ new Expectation(() => {
515
+ throw error
516
+ }, this.inverted).toThrow(expected)
517
+ }
518
+
519
+ async toHaveBeenCalled(): Promise<void> {
520
+ new Expectation(await this.unwrap(), this.inverted).toHaveBeenCalled()
521
+ }
522
+
523
+ async toHaveBeenCalledTimes(expected: number): Promise<void> {
524
+ new Expectation(await this.unwrap(), this.inverted).toHaveBeenCalledTimes(expected)
525
+ }
526
+
527
+ async toHaveBeenCalledWith(...expectedArgs: unknown[]): Promise<void> {
528
+ new Expectation(await this.unwrap(), this.inverted).toHaveBeenCalledWith(...expectedArgs)
529
+ }
530
+
531
+ private async unwrap(): Promise<unknown> {
532
+ try {
533
+ const value = await this.received
534
+
535
+ if (this.mode === "rejects") {
536
+ fail(`Expected promise to reject but it resolved to ${formatValue(value)}`)
537
+ }
538
+
539
+ return value
540
+ } catch (error) {
541
+ if (this.mode === "resolves") {
542
+ fail(`Expected promise to resolve but it rejected with ${formatValue(error)}`)
543
+ }
544
+
545
+ return error
546
+ }
547
+ }
548
+ }
549
+
550
+ class Expectation<T> {
551
+ constructor(
552
+ private readonly received: T,
553
+ private readonly inverted = false,
554
+ ) {}
555
+
556
+ get not(): Expectation<T> {
557
+ return new Expectation(this.received, !this.inverted)
558
+ }
559
+
560
+ get resolves(): AsyncExpectation<T> {
561
+ return new AsyncExpectation(this.received, "resolves", this.inverted)
562
+ }
563
+
564
+ get rejects(): AsyncExpectation<T> {
565
+ return new AsyncExpectation(this.received, "rejects", this.inverted)
566
+ }
567
+
568
+ toBe(expected: unknown): void {
569
+ this.assertMatch(
570
+ Object.is(this.received, expected),
571
+ `Expected ${formatValue(this.received)} to be ${formatValue(expected)}`,
572
+ `Expected ${formatValue(this.received)} not to be ${formatValue(expected)}`,
573
+ )
574
+ }
575
+
576
+ toEqual(expected: unknown): void {
577
+ this.assertMatch(
578
+ valuesEqual(this.received, expected),
579
+ `Expected ${formatValue(this.received)} to equal ${formatValue(expected)}`,
580
+ `Expected ${formatValue(this.received)} not to equal ${formatValue(expected)}`,
581
+ )
582
+ }
583
+
584
+ toBeNull(): void {
585
+ this.assertMatch(
586
+ this.received === null,
587
+ `Expected ${formatValue(this.received)} to be null`,
588
+ `Expected ${formatValue(this.received)} not to be null`,
589
+ )
590
+ }
591
+
592
+ toBeUndefined(): void {
593
+ this.assertMatch(
594
+ this.received === undefined,
595
+ `Expected ${formatValue(this.received)} to be undefined`,
596
+ `Expected ${formatValue(this.received)} not to be undefined`,
597
+ )
598
+ }
599
+
600
+ toBeDefined(): void {
601
+ this.assertMatch(
602
+ this.received !== undefined,
603
+ `Expected ${formatValue(this.received)} to be defined`,
604
+ `Expected ${formatValue(this.received)} not to be defined`,
605
+ )
606
+ }
607
+
608
+ toContain(expected: unknown): void {
609
+ if (typeof this.received === "string") {
610
+ this.assertMatch(
611
+ this.received.includes(String(expected)),
612
+ `Expected ${formatValue(this.received)} to contain ${formatValue(expected)}`,
613
+ `Expected ${formatValue(this.received)} not to contain ${formatValue(expected)}`,
614
+ )
615
+ return
616
+ }
617
+
618
+ if (Array.isArray(this.received)) {
619
+ this.assertMatch(
620
+ this.received.some((value) => isDeepStrictEqual(value, expected)),
621
+ `Expected ${formatValue(this.received)} to contain ${formatValue(expected)}`,
622
+ `Expected ${formatValue(this.received)} not to contain ${formatValue(expected)}`,
623
+ )
624
+ return
625
+ }
626
+
627
+ fail(`Expected ${formatValue(this.received)} to support toContain()`)
628
+ }
629
+
630
+ toHaveLength(expected: number): void {
631
+ if (!hasLength(this.received)) {
632
+ fail(`Expected ${formatValue(this.received)} to have a length property`)
633
+ }
634
+
635
+ this.assertMatch(
636
+ this.received.length === expected,
637
+ `Expected ${formatValue(this.received)} to have length ${expected} but got ${this.received.length}`,
638
+ `Expected ${formatValue(this.received)} not to have length ${expected}`,
639
+ )
640
+ }
641
+
642
+ toBeInstanceOf(expected: new (...args: any[]) => unknown): void {
643
+ this.assertMatch(
644
+ this.received instanceof expected,
645
+ `Expected ${formatValue(this.received)} to be an instance of ${expected.name}`,
646
+ `Expected ${formatValue(this.received)} not to be an instance of ${expected.name}`,
647
+ )
648
+ }
649
+
650
+ toMatchObject(expected: object): void {
651
+ this.assertMatch(
652
+ objectMatches(this.received, expected),
653
+ `Expected ${formatValue(this.received)} to match object ${formatValue(expected)}`,
654
+ `Expected ${formatValue(this.received)} not to match object ${formatValue(expected)}`,
655
+ )
656
+ }
657
+
658
+ toBeTruthy(): void {
659
+ this.assertMatch(
660
+ Boolean(this.received),
661
+ `Expected ${formatValue(this.received)} to be truthy`,
662
+ `Expected ${formatValue(this.received)} not to be truthy`,
663
+ )
664
+ }
665
+
666
+ toBeFalsy(): void {
667
+ this.assertMatch(
668
+ !this.received,
669
+ `Expected ${formatValue(this.received)} to be falsy`,
670
+ `Expected ${formatValue(this.received)} not to be falsy`,
671
+ )
672
+ }
673
+
674
+ toBeGreaterThan(expected: number | bigint): void {
675
+ if (!isComparable(this.received) || !isComparable(expected)) {
676
+ fail(`Expected ${formatValue(this.received)} and ${formatValue(expected)} to be comparable numbers`)
677
+ }
678
+
679
+ this.assertMatch(
680
+ this.received > expected,
681
+ `Expected ${formatValue(this.received)} to be greater than ${formatValue(expected)}`,
682
+ `Expected ${formatValue(this.received)} not to be greater than ${formatValue(expected)}`,
683
+ )
684
+ }
685
+
686
+ toBeGreaterThanOrEqual(expected: number | bigint): void {
687
+ if (!isComparable(this.received) || !isComparable(expected)) {
688
+ fail(`Expected ${formatValue(this.received)} and ${formatValue(expected)} to be comparable numbers`)
689
+ }
690
+
691
+ this.assertMatch(
692
+ this.received >= expected,
693
+ `Expected ${formatValue(this.received)} to be greater than or equal to ${formatValue(expected)}`,
694
+ `Expected ${formatValue(this.received)} not to be greater than or equal to ${formatValue(expected)}`,
695
+ )
696
+ }
697
+
698
+ toBeLessThan(expected: number | bigint): void {
699
+ if (!isComparable(this.received) || !isComparable(expected)) {
700
+ fail(`Expected ${formatValue(this.received)} and ${formatValue(expected)} to be comparable numbers`)
701
+ }
702
+
703
+ this.assertMatch(
704
+ this.received < expected,
705
+ `Expected ${formatValue(this.received)} to be less than ${formatValue(expected)}`,
706
+ `Expected ${formatValue(this.received)} not to be less than ${formatValue(expected)}`,
707
+ )
708
+ }
709
+
710
+ toBeLessThanOrEqual(expected: number | bigint): void {
711
+ if (!isComparable(this.received) || !isComparable(expected)) {
712
+ fail(`Expected ${formatValue(this.received)} and ${formatValue(expected)} to be comparable numbers`)
713
+ }
714
+
715
+ this.assertMatch(
716
+ this.received <= expected,
717
+ `Expected ${formatValue(this.received)} to be less than or equal to ${formatValue(expected)}`,
718
+ `Expected ${formatValue(this.received)} not to be less than or equal to ${formatValue(expected)}`,
719
+ )
720
+ }
721
+
722
+ toBeCloseTo(expected: number, precision = 2): void {
723
+ if (typeof this.received !== "number" || typeof expected !== "number") {
724
+ fail(`Expected ${formatValue(this.received)} and ${formatValue(expected)} to be numbers`)
725
+ }
726
+
727
+ const threshold = 0.5 * 10 ** -precision
728
+ const difference = Math.abs(this.received - expected)
729
+
730
+ this.assertMatch(
731
+ difference <= threshold,
732
+ `Expected ${formatValue(this.received)} to be close to ${formatValue(expected)}`,
733
+ `Expected ${formatValue(this.received)} not to be close to ${formatValue(expected)}`,
734
+ )
735
+ }
736
+
737
+ toMatch(expected: RegExp | string): void {
738
+ if (typeof this.received !== "string") {
739
+ fail(`Expected ${formatValue(this.received)} to be a string for toMatch()`)
740
+ }
741
+
742
+ const matches = typeof expected === "string" ? this.received.includes(expected) : expected.test(this.received)
743
+
744
+ this.assertMatch(
745
+ matches,
746
+ `Expected ${formatValue(this.received)} to match ${formatValue(expected)}`,
747
+ `Expected ${formatValue(this.received)} not to match ${formatValue(expected)}`,
748
+ )
749
+ }
750
+
751
+ toMatchSnapshot(snapshotName?: string): void {
752
+ const { filePath, fullTestName } = getSnapshotContext()
753
+ const snapshotBaseName = snapshotName ? `${fullTestName}: ${snapshotName}` : fullTestName
754
+ const snapshotIndex = nextSnapshotIndex(filePath, snapshotBaseName)
755
+ const snapshotKey = `${snapshotBaseName} ${snapshotIndex}`
756
+ const snapshotPath = getSnapshotFilePath(filePath)
757
+ const snapshot = readSnapshotFile(snapshotPath).get(snapshotKey)
758
+
759
+ if (snapshot === undefined) {
760
+ fail(`Snapshot ${formatValue(snapshotKey)} not found in ${snapshotPath}`)
761
+ }
762
+
763
+ assertSnapshotMatch(this.received, snapshot, `snapshot ${formatValue(snapshotKey)}`, this.inverted)
764
+ }
765
+
766
+ toMatchInlineSnapshot(snapshot: string): void {
767
+ assertSnapshotMatch(this.received, normalizeInlineSnapshot(snapshot), "inline snapshot", this.inverted)
768
+ }
769
+
770
+ toThrow(expected?: ThrowMatcher): void {
771
+ if (typeof this.received !== "function") {
772
+ fail(`Expected ${formatValue(this.received)} to be a function for toThrow()`)
773
+ }
774
+
775
+ let thrown = false
776
+ let error: unknown
777
+
778
+ try {
779
+ ;(this.received as AnyFunction)()
780
+ } catch (caughtError) {
781
+ thrown = true
782
+ error = caughtError
783
+ }
784
+
785
+ this.assertMatch(
786
+ thrown && matchesThrow(error, expected),
787
+ `Expected function to throw${formatThrowMatcher(expected)}`,
788
+ `Expected function not to throw${formatThrowMatcher(expected)}`,
789
+ )
790
+ }
791
+
792
+ toHaveBeenCalled(): void {
793
+ const calls = getMockCalls(this.received)
794
+
795
+ this.assertMatch(calls.length > 0, `Expected mock to have been called`, `Expected mock not to have been called`)
796
+ }
797
+
798
+ toHaveBeenCalledTimes(expected: number): void {
799
+ const calls = getMockCalls(this.received)
800
+
801
+ this.assertMatch(
802
+ calls.length === expected,
803
+ `Expected mock to have been called ${expected} times but it was called ${calls.length} times`,
804
+ `Expected mock not to have been called ${expected} times`,
805
+ )
806
+ }
807
+
808
+ toHaveBeenCalledWith(...expectedArgs: unknown[]): void {
809
+ const calls = getMockCalls(this.received)
810
+
811
+ this.assertMatch(
812
+ calls.some((args) => isDeepStrictEqual(args, expectedArgs)),
813
+ `Expected mock to have been called with ${formatValue(expectedArgs)} but got ${formatValue(calls)}`,
814
+ `Expected mock not to have been called with ${formatValue(expectedArgs)}`,
815
+ )
816
+ }
817
+
818
+ toHaveProperty(property: PropertyKey, expectedValue?: unknown): void {
819
+ if (!isObjectLike(this.received)) {
820
+ fail(`Expected ${formatValue(this.received)} to be an object for toHaveProperty()`)
821
+ }
822
+
823
+ const received = this.received as Record<PropertyKey, unknown>
824
+ const hasProperty = property in received
825
+ const matchesValue = arguments.length < 2 || valuesEqual(received[property], expectedValue)
826
+
827
+ this.assertMatch(
828
+ hasProperty && matchesValue,
829
+ `Expected ${formatValue(this.received)} to have property ${String(property)}`,
830
+ `Expected ${formatValue(this.received)} not to have property ${String(property)}`,
831
+ )
832
+ }
833
+
834
+ private assertMatch(condition: boolean, positiveMessage: string, negativeMessage: string): void {
835
+ if (this.inverted ? condition : !condition) {
836
+ fail(this.inverted ? negativeMessage : positiveMessage)
837
+ }
838
+ }
839
+ }
840
+
841
+ interface ExpectApi {
842
+ <T>(received: T): Expectation<T>
843
+ any(expectedType: unknown): AsymmetricMatcher
844
+ }
845
+
846
+ export const expect = Object.assign(<T>(received: T): Expectation<T> => new Expectation(received), {
847
+ any: createAnyMatcher,
848
+ }) as ExpectApi
849
+
850
+ export function mock<Fn extends AnyFunction = () => undefined>(
851
+ implementation = (() => undefined) as Fn,
852
+ ): MockedFunction<Fn> {
853
+ return createMock(implementation)
854
+ }
855
+
856
+ export function spyOn(object: object, key: string | symbol): MockedFunction {
857
+ const record = object as Record<string | symbol, unknown>
858
+ const original = record[key]
859
+
860
+ assert.strictEqual(typeof original, "function", `Cannot spy on ${String(key)} because it is not a function`)
861
+
862
+ const spy = createMock(
863
+ function (this: unknown, ...args: unknown[]) {
864
+ return (original as AnyFunction).apply(this, args)
865
+ },
866
+ () => {
867
+ record[key] = original
868
+ },
869
+ )
870
+
871
+ record[key] = spy
872
+ return spy
873
+ }
874
+
875
+ export const beforeAll = before
876
+ export const afterAll = after
877
+ export const test = createTest(nodeTest) as typeof nodeTest & { each: ReturnType<typeof createEach> }
878
+ export const it = test
879
+ export const describe = createDescribe(nodeDescribe)
880
+ export { afterEach, beforeEach }