bunmicro 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/LICENSE +22 -0
  2. package/PORTING.md +34 -0
  3. package/README.md +153 -0
  4. package/bmi +5 -0
  5. package/bun.lock +17 -0
  6. package/bunmicro +5 -0
  7. package/hlw.md +5 -0
  8. package/package.json +18 -0
  9. package/runtime/colorschemes/atom-dark.micro +33 -0
  10. package/runtime/colorschemes/bubblegum.micro +31 -0
  11. package/runtime/colorschemes/cmc-16.micro +47 -0
  12. package/runtime/colorschemes/cmc-tc.micro +43 -0
  13. package/runtime/colorschemes/darcula.micro +34 -0
  14. package/runtime/colorschemes/default.micro +1 -0
  15. package/runtime/colorschemes/dracula-tc.micro +49 -0
  16. package/runtime/colorschemes/dukedark-tc.micro +38 -0
  17. package/runtime/colorschemes/dukelight-tc.micro +38 -0
  18. package/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  19. package/runtime/colorschemes/geany.micro +29 -0
  20. package/runtime/colorschemes/gotham.micro +29 -0
  21. package/runtime/colorschemes/gruvbox-tc.micro +29 -0
  22. package/runtime/colorschemes/gruvbox.micro +26 -0
  23. package/runtime/colorschemes/material-tc.micro +36 -0
  24. package/runtime/colorschemes/monokai-dark.micro +28 -0
  25. package/runtime/colorschemes/monokai.micro +34 -0
  26. package/runtime/colorschemes/one-dark.micro +39 -0
  27. package/runtime/colorschemes/railscast.micro +37 -0
  28. package/runtime/colorschemes/simple.micro +33 -0
  29. package/runtime/colorschemes/solarized-tc.micro +31 -0
  30. package/runtime/colorschemes/solarized.micro +30 -0
  31. package/runtime/colorschemes/sunny-day.micro +29 -0
  32. package/runtime/colorschemes/twilight.micro +40 -0
  33. package/runtime/colorschemes/zenburn.micro +30 -0
  34. package/runtime/help/actions.md +161 -0
  35. package/runtime/help/colors.md +421 -0
  36. package/runtime/help/commands.md +161 -0
  37. package/runtime/help/copypaste.md +149 -0
  38. package/runtime/help/defaultkeys.md +141 -0
  39. package/runtime/help/help.md +63 -0
  40. package/runtime/help/keybindings.md +760 -0
  41. package/runtime/help/linter.md +90 -0
  42. package/runtime/help/options.md +701 -0
  43. package/runtime/help/plugins.md +544 -0
  44. package/runtime/help/tutorial.md +112 -0
  45. package/runtime/jsplugins/chapter/chapter.js +108 -0
  46. package/runtime/jsplugins/diff/diff.js +46 -0
  47. package/runtime/jsplugins/example/example.js +108 -0
  48. package/runtime/jsplugins/linter/linter.js +281 -0
  49. package/runtime/plugins/autoclose/autoclose.lua +75 -0
  50. package/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  51. package/runtime/plugins/literate/README.md +5 -0
  52. package/runtime/plugins/literate/literate.lua +55 -0
  53. package/runtime/plugins/status/help/status.md +21 -0
  54. package/runtime/plugins/status/status.lua +62 -0
  55. package/runtime/syntax/LICENSE +22 -0
  56. package/runtime/syntax/PowerShell.yaml +128 -0
  57. package/runtime/syntax/README.md +63 -0
  58. package/runtime/syntax/ada.yaml +43 -0
  59. package/runtime/syntax/apacheconf.yaml +59 -0
  60. package/runtime/syntax/arduino.yaml +101 -0
  61. package/runtime/syntax/asciidoc.yaml +51 -0
  62. package/runtime/syntax/asm.yaml +123 -0
  63. package/runtime/syntax/ats.yaml +99 -0
  64. package/runtime/syntax/awk.yaml +44 -0
  65. package/runtime/syntax/b.yaml +87 -0
  66. package/runtime/syntax/bat.yaml +57 -0
  67. package/runtime/syntax/c.yaml +60 -0
  68. package/runtime/syntax/caddyfile.yaml +23 -0
  69. package/runtime/syntax/cake.yaml +7 -0
  70. package/runtime/syntax/clojure.yaml +38 -0
  71. package/runtime/syntax/cmake.yaml +42 -0
  72. package/runtime/syntax/coffeescript.yaml +56 -0
  73. package/runtime/syntax/colortest.yaml +19 -0
  74. package/runtime/syntax/conky.yaml +17 -0
  75. package/runtime/syntax/cpp.yaml +91 -0
  76. package/runtime/syntax/crontab.yaml +36 -0
  77. package/runtime/syntax/crystal.yaml +72 -0
  78. package/runtime/syntax/csharp.yaml +52 -0
  79. package/runtime/syntax/css.yaml +44 -0
  80. package/runtime/syntax/csx.yaml +8 -0
  81. package/runtime/syntax/cuda.yaml +68 -0
  82. package/runtime/syntax/cython.yaml +52 -0
  83. package/runtime/syntax/d.yaml +121 -0
  84. package/runtime/syntax/dart.yaml +46 -0
  85. package/runtime/syntax/default.yaml +10 -0
  86. package/runtime/syntax/dockerfile.yaml +36 -0
  87. package/runtime/syntax/dot.yaml +29 -0
  88. package/runtime/syntax/elixir.yaml +30 -0
  89. package/runtime/syntax/elm.yaml +38 -0
  90. package/runtime/syntax/erb.yaml +42 -0
  91. package/runtime/syntax/erlang.yaml +45 -0
  92. package/runtime/syntax/fish.yaml +48 -0
  93. package/runtime/syntax/forth.yaml +34 -0
  94. package/runtime/syntax/fortran.yaml +64 -0
  95. package/runtime/syntax/freebsd-kernel.yaml +14 -0
  96. package/runtime/syntax/fsharp.yaml +48 -0
  97. package/runtime/syntax/gdscript.yaml +61 -0
  98. package/runtime/syntax/gemini.yaml +19 -0
  99. package/runtime/syntax/gentoo-ebuild.yaml +48 -0
  100. package/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  101. package/runtime/syntax/git-commit.yaml +35 -0
  102. package/runtime/syntax/git-config.yaml +14 -0
  103. package/runtime/syntax/git-rebase-todo.yaml +19 -0
  104. package/runtime/syntax/gleam.yaml +69 -0
  105. package/runtime/syntax/glsl.yaml +26 -0
  106. package/runtime/syntax/gnuplot.yaml +15 -0
  107. package/runtime/syntax/go.yaml +62 -0
  108. package/runtime/syntax/godoc.yaml +17 -0
  109. package/runtime/syntax/golo.yaml +73 -0
  110. package/runtime/syntax/gomod.yaml +31 -0
  111. package/runtime/syntax/graphql.yaml +47 -0
  112. package/runtime/syntax/groff.yaml +30 -0
  113. package/runtime/syntax/groovy.yaml +111 -0
  114. package/runtime/syntax/haml.yaml +16 -0
  115. package/runtime/syntax/hare.yaml +52 -0
  116. package/runtime/syntax/haskell.yaml +52 -0
  117. package/runtime/syntax/hc.yaml +52 -0
  118. package/runtime/syntax/html.yaml +70 -0
  119. package/runtime/syntax/html4.yaml +25 -0
  120. package/runtime/syntax/html5.yaml +25 -0
  121. package/runtime/syntax/ini.yaml +23 -0
  122. package/runtime/syntax/inputrc.yaml +14 -0
  123. package/runtime/syntax/java.yaml +37 -0
  124. package/runtime/syntax/javascript.yaml +76 -0
  125. package/runtime/syntax/jinja2.yaml +19 -0
  126. package/runtime/syntax/json.yaml +39 -0
  127. package/runtime/syntax/jsonnet.yaml +92 -0
  128. package/runtime/syntax/julia.yaml +57 -0
  129. package/runtime/syntax/justfile.yaml +40 -0
  130. package/runtime/syntax/keymap.yaml +27 -0
  131. package/runtime/syntax/kickstart.yaml +16 -0
  132. package/runtime/syntax/kotlin.yaml +66 -0
  133. package/runtime/syntax/kvlang.yaml +67 -0
  134. package/runtime/syntax/ledger.yaml +14 -0
  135. package/runtime/syntax/lfe.yaml +17 -0
  136. package/runtime/syntax/lilypond.yaml +26 -0
  137. package/runtime/syntax/lisp.yaml +17 -0
  138. package/runtime/syntax/log.yaml +92 -0
  139. package/runtime/syntax/lua.yaml +111 -0
  140. package/runtime/syntax/mail.yaml +25 -0
  141. package/runtime/syntax/makefile.yaml +38 -0
  142. package/runtime/syntax/man.yaml +12 -0
  143. package/runtime/syntax/markdown.yaml +49 -0
  144. package/runtime/syntax/mc.yaml +23 -0
  145. package/runtime/syntax/meson.yaml +51 -0
  146. package/runtime/syntax/micro.yaml +34 -0
  147. package/runtime/syntax/mpdconf.yaml +13 -0
  148. package/runtime/syntax/msbuild.yaml +6 -0
  149. package/runtime/syntax/nanorc.yaml +16 -0
  150. package/runtime/syntax/nftables.yaml +30 -0
  151. package/runtime/syntax/nginx.yaml +22 -0
  152. package/runtime/syntax/nim.yaml +27 -0
  153. package/runtime/syntax/nix.yaml +32 -0
  154. package/runtime/syntax/nu.yaml +114 -0
  155. package/runtime/syntax/objc.yaml +60 -0
  156. package/runtime/syntax/ocaml.yaml +43 -0
  157. package/runtime/syntax/octave.yaml +83 -0
  158. package/runtime/syntax/odin.yaml +64 -0
  159. package/runtime/syntax/pascal.yaml +45 -0
  160. package/runtime/syntax/patch.yaml +14 -0
  161. package/runtime/syntax/peg.yaml +16 -0
  162. package/runtime/syntax/perl.yaml +58 -0
  163. package/runtime/syntax/php.yaml +60 -0
  164. package/runtime/syntax/pkg-config.yaml +12 -0
  165. package/runtime/syntax/po.yaml +12 -0
  166. package/runtime/syntax/pony.yaml +37 -0
  167. package/runtime/syntax/pov.yaml +21 -0
  168. package/runtime/syntax/privoxy-action.yaml +14 -0
  169. package/runtime/syntax/privoxy-config.yaml +10 -0
  170. package/runtime/syntax/privoxy-filter.yaml +12 -0
  171. package/runtime/syntax/proto.yaml +40 -0
  172. package/runtime/syntax/prql.yaml +84 -0
  173. package/runtime/syntax/puppet.yaml +22 -0
  174. package/runtime/syntax/python2.yaml +60 -0
  175. package/runtime/syntax/python3.yaml +62 -0
  176. package/runtime/syntax/r.yaml +32 -0
  177. package/runtime/syntax/raku.yaml +42 -0
  178. package/runtime/syntax/reST.yaml +18 -0
  179. package/runtime/syntax/renpy.yaml +15 -0
  180. package/runtime/syntax/rpmspec.yaml +43 -0
  181. package/runtime/syntax/ruby.yaml +73 -0
  182. package/runtime/syntax/rust.yaml +78 -0
  183. package/runtime/syntax/sage.yaml +60 -0
  184. package/runtime/syntax/scad.yaml +53 -0
  185. package/runtime/syntax/scala.yaml +33 -0
  186. package/runtime/syntax/sed.yaml +13 -0
  187. package/runtime/syntax/sh.yaml +69 -0
  188. package/runtime/syntax/sls.yaml +15 -0
  189. package/runtime/syntax/smalltalk.yaml +55 -0
  190. package/runtime/syntax/solidity.yaml +41 -0
  191. package/runtime/syntax/sql.yaml +35 -0
  192. package/runtime/syntax/stata.yaml +67 -0
  193. package/runtime/syntax/svelte.yaml +27 -0
  194. package/runtime/syntax/swift.yaml +103 -0
  195. package/runtime/syntax/systemd.yaml +16 -0
  196. package/runtime/syntax/tcl.yaml +18 -0
  197. package/runtime/syntax/terraform.yaml +87 -0
  198. package/runtime/syntax/tex.yaml +32 -0
  199. package/runtime/syntax/toml.yaml +56 -0
  200. package/runtime/syntax/twig.yaml +55 -0
  201. package/runtime/syntax/typescript.yaml +49 -0
  202. package/runtime/syntax/v.yaml +80 -0
  203. package/runtime/syntax/vala.yaml +26 -0
  204. package/runtime/syntax/verilog.yaml +60 -0
  205. package/runtime/syntax/vhdl.yaml +37 -0
  206. package/runtime/syntax/vi.yaml +31 -0
  207. package/runtime/syntax/vue.yaml +64 -0
  208. package/runtime/syntax/xml.yaml +37 -0
  209. package/runtime/syntax/xresources.yaml +14 -0
  210. package/runtime/syntax/yaml.yaml +34 -0
  211. package/runtime/syntax/yum.yaml +12 -0
  212. package/runtime/syntax/zig.yaml +52 -0
  213. package/runtime/syntax/zscript.yaml +72 -0
  214. package/runtime/syntax/zsh.yaml +52 -0
  215. package/src/buffer/buffer.js +126 -0
  216. package/src/buffer/loc.js +38 -0
  217. package/src/buffer/message.js +29 -0
  218. package/src/config/colorscheme.js +109 -0
  219. package/src/config/config.js +118 -0
  220. package/src/config/defaults.js +102 -0
  221. package/src/display/ansi-style.js +60 -0
  222. package/src/highlight/highlighter.js +237 -0
  223. package/src/highlight/parser.js +137 -0
  224. package/src/index.js +5942 -0
  225. package/src/lua/engine.js +38 -0
  226. package/src/platform/archive.js +50 -0
  227. package/src/platform/clipboard.js +160 -0
  228. package/src/platform/commands.js +140 -0
  229. package/src/plugins/js-bridge.js +902 -0
  230. package/src/plugins/manager.js +619 -0
  231. package/src/runtime/registry.js +89 -0
  232. package/src/screen/cell-buffer.js +81 -0
  233. package/src/screen/events.js +263 -0
  234. package/src/screen/screen.js +118 -0
  235. package/src/screen/vt100.js +391 -0
  236. package/src/shell/shell.js +70 -0
  237. package/todo.txt +359 -0
@@ -0,0 +1,38 @@
1
+ export async function createLuaEngine() {
2
+ const wasmoon = await tryImport("wasmoon");
3
+ if (!wasmoon?.LuaFactory) {
4
+ throw new Error("Lua support requires the WASM runtime `wasmoon`. Install it with `bun add wasmoon`.");
5
+ }
6
+
7
+ const factory = new wasmoon.LuaFactory();
8
+ const lua = await factory.createEngine();
9
+ return new WasmoonEngine(lua);
10
+ }
11
+
12
+ async function tryImport(name) {
13
+ try {
14
+ return await import(name);
15
+ } catch (error) {
16
+ if (error?.code === "ERR_MODULE_NOT_FOUND" || /Cannot find package/.test(String(error?.message))) return null;
17
+ throw error;
18
+ }
19
+ }
20
+
21
+ class WasmoonEngine {
22
+ constructor(lua) {
23
+ this.kind = "wasmoon";
24
+ this.lua = lua;
25
+ }
26
+
27
+ async doString(source, chunkName = "chunk") {
28
+ return this.lua.doString(source, chunkName);
29
+ }
30
+
31
+ setGlobal(name, value) {
32
+ this.lua.global.set(name, value);
33
+ }
34
+
35
+ getGlobal(name) {
36
+ return this.lua.global.get(name);
37
+ }
38
+ }
@@ -0,0 +1,50 @@
1
+ import { mkdir, readdir, rename, rm } from "node:fs/promises";
2
+ import { join, dirname } from "node:path";
3
+ import { hasCommand, isLinuxLike, platformId, run } from "./commands.js";
4
+
5
+ export async function extractZip(zipPath, destDir) {
6
+ await mkdir(destDir, { recursive: true });
7
+ const platform = platformId();
8
+
9
+ if (platform === "darwin" || platform === "win32") {
10
+ await run(["tar", "-xf", zipPath, "-C", destDir]);
11
+ return;
12
+ }
13
+
14
+ if (isLinuxLike()) {
15
+ if (!hasCommand("unzip")) {
16
+ throw new Error("Installing zip plugins requires `unzip`. Please install unzip first.");
17
+ }
18
+ await run(["unzip", "-q", zipPath, "-d", destDir]);
19
+ return;
20
+ }
21
+
22
+ if (hasCommand("unzip")) {
23
+ await run(["unzip", "-q", zipPath, "-d", destDir]);
24
+ return;
25
+ }
26
+
27
+ throw new Error(`Unsupported platform for zip extraction: ${platform}`);
28
+ }
29
+
30
+ // Extract zip to destDir, stripping a single top-level prefix directory if present
31
+ // (matches Go micro behavior: zips containing e.g. autoclose-1.0.0/ are stripped).
32
+ export async function extractAndStrip(zipPath, destDir) {
33
+ const tmpDir = destDir + ".__install_tmp__";
34
+ await rm(tmpDir, { recursive: true, force: true });
35
+ await extractZip(zipPath, tmpDir);
36
+
37
+ const entries = await readdir(tmpDir, { withFileTypes: true });
38
+ const subdirs = entries.filter((e) => e.isDirectory());
39
+ const files = entries.filter((e) => e.isFile());
40
+
41
+ await rm(destDir, { recursive: true, force: true });
42
+ await mkdir(dirname(destDir), { recursive: true });
43
+
44
+ if (subdirs.length === 1 && files.length === 0) {
45
+ await rename(join(tmpDir, subdirs[0].name), destDir);
46
+ await rm(tmpDir, { recursive: true, force: true });
47
+ } else {
48
+ await rename(tmpDir, destDir);
49
+ }
50
+ }
@@ -0,0 +1,160 @@
1
+ import { firstCommand, isLinuxLike, platformId, runSync } from "./commands.js";
2
+
3
+ const CLIPBOARD_TIMEOUT_MS = 2000;
4
+ const internalRegisters = new Map();
5
+
6
+ export class ClipboardManager {
7
+ constructor() {
8
+ this.backend = detectClipboardBackend();
9
+ }
10
+
11
+ methodName() {
12
+ return this.backend.name;
13
+ }
14
+
15
+ fallbackToInternal() {
16
+ this.backend = internalClipboard();
17
+ return this.backend;
18
+ }
19
+
20
+ read(register = "clipboard") {
21
+ if (register !== "clipboard" && register !== "primary") {
22
+ return internalRegisters.get(register) ?? "";
23
+ }
24
+ try {
25
+ const text = this.backend.read?.(register);
26
+ if (text == null) return internalRegisters.get(register) ?? "";
27
+ return text;
28
+ } catch {
29
+ return this.fallbackToInternal().read(register);
30
+ }
31
+ }
32
+
33
+ write(text, register = "clipboard") {
34
+ internalRegisters.set(register, text);
35
+ if (register !== "clipboard" && register !== "primary") return true;
36
+ try {
37
+ const ok = this.backend.write?.(text, register) ?? true;
38
+ if (!ok) this.fallbackToInternal();
39
+ return true;
40
+ } catch {
41
+ this.fallbackToInternal();
42
+ return true;
43
+ }
44
+ }
45
+ }
46
+
47
+ function detectClipboardBackend() {
48
+ const platform = platformId();
49
+
50
+ if (platform === "android") {
51
+ const termuxSet = firstCommand(["termux-clipboard-set"]);
52
+ const termuxGet = firstCommand(["termux-clipboard-get"]);
53
+ if (termuxSet && termuxGet) return termuxClipboard(termuxSet, termuxGet);
54
+ }
55
+
56
+ if (isLinuxLike()) {
57
+ const wlCopy = firstCommand(["wl-copy"]);
58
+ const wlPaste = firstCommand(["wl-paste"]);
59
+ if (wlCopy && wlPaste) return wlClipboard(wlCopy, wlPaste);
60
+
61
+ const xclip = firstCommand(["xclip"]);
62
+ if (xclip) return xclipClipboard(xclip);
63
+
64
+ const xsel = firstCommand(["xsel"]);
65
+ if (xsel) return xselClipboard(xsel);
66
+
67
+ return internalClipboard();
68
+ }
69
+
70
+ if (platform === "darwin") {
71
+ const pbcopy = firstCommand(["pbcopy"]);
72
+ const pbpaste = firstCommand(["pbpaste"]);
73
+ if (pbcopy && pbpaste) return commandClipboard("pbcopy/pbpaste", [pbcopy], [pbpaste]);
74
+ return internalClipboard();
75
+ }
76
+
77
+ if (platform === "win32") {
78
+ const shell = firstCommand(["pwsh.exe", "powershell.exe", "pwsh", "powershell"]);
79
+ if (shell) return powershellClipboard(shell);
80
+ return internalClipboard();
81
+ }
82
+
83
+ return internalClipboard();
84
+ }
85
+
86
+ function internalClipboard() {
87
+ return {
88
+ name: "internal",
89
+ read: (register) => internalRegisters.get(register) ?? "",
90
+ write: (text, register) => {
91
+ internalRegisters.set(register, text);
92
+ return true;
93
+ },
94
+ };
95
+ }
96
+
97
+ function commandClipboard(name, writeCommand, readCommand) {
98
+ return {
99
+ name,
100
+ read: () => outputOrThrow(runSync(readCommand, { timeout: CLIPBOARD_TIMEOUT_MS })),
101
+ write: (text) => runSync(writeCommand, { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok,
102
+ };
103
+ }
104
+
105
+ function termuxClipboard(set, get) {
106
+ return commandClipboard("termux", [set], [get]);
107
+ }
108
+
109
+ function wlClipboard(wlCopy, wlPaste) {
110
+ return {
111
+ name: "wl-clipboard",
112
+ read: () => outputOrThrow(runSync([wlPaste, "--no-newline"], { timeout: CLIPBOARD_TIMEOUT_MS })),
113
+ write: (text) => runSync([wlCopy], { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok,
114
+ };
115
+ }
116
+
117
+ function xclipClipboard(xclip) {
118
+ return {
119
+ name: "xclip",
120
+ read: (register) => {
121
+ const selection = register === "primary" ? "primary" : "clipboard";
122
+ return outputOrThrow(runSync([xclip, "-selection", selection, "-o"], { timeout: CLIPBOARD_TIMEOUT_MS }));
123
+ },
124
+ write: (text, register) => {
125
+ const selection = register === "primary" ? "primary" : "clipboard";
126
+ return runSync([xclip, "-selection", selection], { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok;
127
+ },
128
+ };
129
+ }
130
+
131
+ function xselClipboard(xsel) {
132
+ return {
133
+ name: "xsel",
134
+ read: (register) => {
135
+ const selection = register === "primary" ? "--primary" : "--clipboard";
136
+ return outputOrThrow(runSync([xsel, selection, "--output"], { timeout: CLIPBOARD_TIMEOUT_MS }));
137
+ },
138
+ write: (text, register) => {
139
+ const selection = register === "primary" ? "--primary" : "--clipboard";
140
+ return runSync([xsel, selection, "--input"], { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok;
141
+ },
142
+ };
143
+ }
144
+
145
+ function powershellClipboard(shell) {
146
+ return {
147
+ name: "powershell",
148
+ read: () => outputOrThrow(runSync([shell, "-NoProfile", "-Command", "Get-Clipboard -Raw"], { timeout: CLIPBOARD_TIMEOUT_MS })),
149
+ write: (text) => runSync([shell, "-NoProfile", "-Command", "Set-Clipboard"], { stdin: text, stdout: "ignore", timeout: CLIPBOARD_TIMEOUT_MS }).ok,
150
+ };
151
+ }
152
+
153
+ function outputOrThrow(result) {
154
+ if (!result.ok) throw new Error(result.stderr || result.stdout || "clipboard command failed");
155
+ return trimOneTrailingNewline(result.stdout);
156
+ }
157
+
158
+ function trimOneTrailingNewline(text) {
159
+ return text.replace(/\r?\n$/, "");
160
+ }
@@ -0,0 +1,140 @@
1
+ import process from "node:process";
2
+
3
+ const decoder = new TextDecoder();
4
+
5
+ let _httpBackend = null;
6
+
7
+ export function detectHttpBackend() {
8
+ if (_httpBackend !== null) return _httpBackend;
9
+ if (Bun.which("curl")) { _httpBackend = "curl"; return _httpBackend; }
10
+ if (Bun.which("wget")) { _httpBackend = "wget"; return _httpBackend; }
11
+ _httpBackend = "fetch";
12
+ return _httpBackend;
13
+ }
14
+
15
+ export async function fetchHttp(url) {
16
+ return decoder.decode(await fetchHttpBytes(url));
17
+ }
18
+
19
+ export async function fetchHttpBytes(url) {
20
+ const b = detectHttpBackend();
21
+ if (b === "curl") {
22
+ const r = await runBytes(["curl", "-kL", "--silent", "--fail", url], { allowFailure: true });
23
+ if (!r.ok) throw new Error(`curl: ${r.stderr.trim() || "failed"} (${url})`);
24
+ return r.stdout;
25
+ }
26
+ if (b === "wget") {
27
+ const r = await runBytes(["wget", "--no-check-certificate", "-q", "-O", "-", url], { allowFailure: true });
28
+ if (!r.ok) throw new Error(`wget: ${r.stderr.trim() || "failed"} (${url})`);
29
+ return r.stdout;
30
+ }
31
+ const resp = await fetch(url);
32
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${url}`);
33
+ return new Uint8Array(await resp.arrayBuffer());
34
+ }
35
+
36
+ export async function downloadFile(url, outPath) {
37
+ const b = detectHttpBackend();
38
+ if (b === "curl") {
39
+ await run(["curl", "-kL", "--silent", "--fail", "-o", outPath, url]);
40
+ return;
41
+ }
42
+ if (b === "wget") {
43
+ await run(["wget", "--no-check-certificate", "-q", "-O", outPath, url]);
44
+ return;
45
+ }
46
+ const resp = await fetch(url);
47
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${url}`);
48
+ await Bun.write(outPath, resp);
49
+ }
50
+
51
+ export function platformId() {
52
+ return process.platform;
53
+ }
54
+
55
+ export function isLinuxLike() {
56
+ const platform = platformId();
57
+ return platform === "linux" || platform === "android";
58
+ }
59
+
60
+ export function runSync(command, options = {}) {
61
+ try {
62
+ const spawnOpts = {
63
+ stdio: [stdioInput(options.stdin), options.stdout ?? "pipe", options.stderr ?? "pipe"],
64
+ env: options.env ?? process.env,
65
+ cwd: options.cwd,
66
+ };
67
+ if (options.timeout != null) spawnOpts.timeout = options.timeout;
68
+ const proc = Bun.spawnSync(command, spawnOpts);
69
+ return {
70
+ ok: proc.success,
71
+ code: proc.exitCode,
72
+ stdout: proc.stdout ? decoder.decode(proc.stdout) : "",
73
+ stderr: proc.stderr ? decoder.decode(proc.stderr) : "",
74
+ };
75
+ } catch (error) {
76
+ return {
77
+ ok: false,
78
+ code: -1,
79
+ stdout: "",
80
+ stderr: String(error?.message || error),
81
+ };
82
+ }
83
+ }
84
+
85
+ export async function run(command, options = {}) {
86
+ const proc = Bun.spawn(command, {
87
+ stdio: [stdioInput(options.stdin), options.stdout ?? "pipe", options.stderr ?? "pipe"],
88
+ env: options.env ?? process.env,
89
+ cwd: options.cwd,
90
+ });
91
+ const [stdout, stderr, code] = await Promise.all([
92
+ proc.stdout ? new Response(proc.stdout).text() : Promise.resolve(""),
93
+ proc.stderr ? new Response(proc.stderr).text() : Promise.resolve(""),
94
+ proc.exited,
95
+ ]);
96
+ if (code !== 0 && !options.allowFailure) {
97
+ throw new Error(`${command[0]} exited with ${code}: ${stderr || stdout}`);
98
+ }
99
+ return { ok: code === 0, code, stdout, stderr };
100
+ }
101
+
102
+ export async function runBytes(command, options = {}) {
103
+ const proc = Bun.spawn(command, {
104
+ stdio: [stdioInput(options.stdin), options.stdout ?? "pipe", options.stderr ?? "pipe"],
105
+ env: options.env ?? process.env,
106
+ cwd: options.cwd,
107
+ });
108
+ const [stdoutBuf, stderr, code] = await Promise.all([
109
+ proc.stdout ? new Response(proc.stdout).arrayBuffer() : Promise.resolve(new ArrayBuffer(0)),
110
+ proc.stderr ? new Response(proc.stderr).text() : Promise.resolve(""),
111
+ proc.exited,
112
+ ]);
113
+ const stdout = new Uint8Array(stdoutBuf);
114
+ if (code !== 0 && !options.allowFailure) {
115
+ throw new Error(`${command[0]} exited with ${code}: ${stderr || decoder.decode(stdout)}`);
116
+ }
117
+ return { ok: code === 0, code, stdout, stderr };
118
+ }
119
+
120
+ export function hasCommand(name) {
121
+ if (platformId() === "win32") {
122
+ return runSync(["where.exe", name], { stdout: "ignore", stderr: "ignore" }).ok;
123
+ }
124
+ return runSync(["sh", "-c", `command -v ${shellQuote(name)}`], { stdout: "ignore", stderr: "ignore" }).ok;
125
+ }
126
+
127
+ export function firstCommand(names) {
128
+ return names.find((name) => hasCommand(name)) ?? null;
129
+ }
130
+
131
+ export function shellQuote(value) {
132
+ return `'${String(value).replaceAll("'", "'\\''")}'`;
133
+ }
134
+
135
+ function stdioInput(input) {
136
+ if (input == null) return "ignore";
137
+ if (input === "pipe" || input === "inherit" || input === "ignore") return input;
138
+ if (typeof input === "string" || input instanceof Uint8Array || input instanceof ArrayBuffer) return new Blob([input]);
139
+ return input;
140
+ }