bunmicro 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.3] - 2026-06-27
4
+ - Added hex3 series encodings
5
+ * hex3gz / hex3zst add compressed binary-view encodings
6
+ - Added experimental single-file exe support
7
+ * bundled assets loader
8
+ * internal assets fallback to external file tree when needed
9
+ - Added build helpers for single-exe packaging
10
+ * `--build-exe` runs the asset pack + compile flow
11
+ * `--build-for <target>` runs the same flow with an explicit Bun build target
12
+
13
+ ## [1.0.2] - 2026-06-24
14
+ - Added startup profiling flags
15
+ * -profile / --profile
16
+ - Added docs flags
17
+ * --changelog alongside --docs / --readme
18
+ - Disabled OSC 52 probing
19
+ * clipboard now treats OSC 52 as available without detection
20
+ - Added hex3 aliases
21
+ * --xxd / --hexdump => --cat -encoding hex3
22
+ * --hex3 => -encoding hex3
23
+ - Fixed stdin encoding handling
24
+ * hex3 now applies when reading from stdin
25
+ - Parallelized startup loading
26
+ * Lua, JS, and buffer init now run in parallel with safe degradation
27
+ * startup performance improves a lot
28
+
3
29
  ## [1.0.0] - 2026-06-22
4
30
  - Changed command -v to Bun.which
5
31
  * for performance improvement
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunmicro",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Bun JavaScript rewrite of the micro editor originally in Golang",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -0,0 +1,49 @@
1
+ # This is completely optional
2
+ - I still recommend using the methods in the root README.md
3
+ - Bun's Android build is currently not supported
4
+
5
+ # Usage
6
+ - First run `bun ./packAssets.sh` to bundle the assets into `assets.tar`
7
+ - Then run the build script like this
8
+
9
+ ```shell
10
+ bun build --compile --bytecode --minify ./entry.mjs --outfile=bmi
11
+ ```
12
+
13
+ - You'll get a bmi executable
14
+
15
+ # Single Executable Intro
16
+
17
+ This folder contains the Bun single-exe bootstrap used by the project.
18
+
19
+ ## Entry Flow
20
+
21
+ - `entry.mjs` imports `assetsLoader.mjs` first
22
+ - `assetsLoader.mjs` loads `assets.tar` with `Bun.Archive` and mounts it as `globalThis.internalAssets`
23
+ - `assetsLoaderPromise` is exposed on `globalThis`
24
+ - `../src/index.js` waits for `assetsLoaderPromise` if it exists
25
+
26
+ That keeps the main program bootable even if asset loading reports errors.
27
+
28
+ ## Assets Loading
29
+
30
+ - Bundled assets are loaded sequentially with `await file.bytes()`
31
+ - Load failures are collected and printed to `stderr`
32
+ - Asset loading never rejects the bootstrap promise
33
+ - When loading succeeds, the archive is available through `globalThis.internalAssets`
34
+
35
+ ## CLI Flags
36
+
37
+ - `--assets-list`
38
+ - Lists all entries inside bundled `assets.tar`
39
+ - Exits early before the main program starts
40
+
41
+ - `--assets-extract`
42
+ - Extracts bundled assets to the same directory as the executable
43
+ - Exits early before the main program starts
44
+
45
+ - `--assets-external`
46
+ - Skips loading bundled assets into `globalThis.internalAssets`
47
+ - Forces `../src/index.js` and runtime helpers to use the external file tree
48
+ - Keeps the bootstrap alive while leaving `internalAssets` falsy
49
+
Binary file
@@ -0,0 +1,85 @@
1
+ import assets from "./assets.tar" with { type: "file" };
2
+ import { resolveCompiledBaseDir, isCompiledBinary } from "../src/runtime/compiled.js";
3
+
4
+ const forceExternalAssets = process.argv.includes("--assets-external");
5
+ const debugAssetsLoader = Boolean(process.env.BUNMICRO_DEBUG);
6
+ if (forceExternalAssets) {
7
+ const flagIndex = process.argv.indexOf("--assets-external");
8
+ if (flagIndex >= 0) process.argv.splice(flagIndex, 1);
9
+ }
10
+
11
+ globalThis.internalAssets = forceExternalAssets ? null : Object.create(null);
12
+ globalThis.assetsLoaderPromise = main(process.argv).catch((error) => {
13
+ console.error("# assets loader failed");
14
+ console.error(error);
15
+ return globalThis.internalAssets;
16
+ });
17
+
18
+ async function main(argv) {
19
+
20
+ if (forceExternalAssets &&
21
+ !argv.includes("--assets-list") &&
22
+ !argv.includes("--assets-extract")) {
23
+ return null;
24
+ }
25
+
26
+ const startedAt = debugAssetsLoader ? Bun.nanoseconds() : 0;
27
+
28
+ const tarball = await Bun.file(assets).bytes();
29
+ const archive = new Bun.Archive(tarball);
30
+
31
+ await cliEarlyExit(archive, argv);
32
+
33
+ const files = await archive.files();
34
+ const entries = [...files.entries()];
35
+ const assetsByPath = Object.create(null);
36
+ const errors = [];
37
+
38
+ for (const [path, file] of entries) {
39
+ try {
40
+ assetsByPath[path] = await file.bytes();
41
+ } catch (reason) {
42
+ errors.push({ path, reason });
43
+ }
44
+ }
45
+
46
+ if (errors.length > 0) {
47
+ console.error(`# Failed to load ${errors.length} bundled asset(s):`);
48
+ for (const { path, reason } of errors) {
49
+ console.error(`- ${path}`);
50
+ if (reason) {
51
+ console.error(reason);
52
+ }
53
+ }
54
+ }
55
+ if (debugAssetsLoader) {
56
+ const elapsedMs = (Bun.nanoseconds() - startedAt) / 1e6;
57
+ console.error(`Loaded assets: ${elapsedMs.toFixed(3)} ms`);
58
+ }
59
+
60
+ globalThis.internalAssets = assetsByPath;
61
+ return assetsByPath;
62
+ }
63
+
64
+ async function cliEarlyExit(archive, argv) {
65
+ if (argv.includes("--assets-list")) {
66
+ const files = await archive.files();
67
+ for (const path of [...files.keys()].sort()) {
68
+ console.log(path);
69
+ }
70
+ process.exit(0);
71
+ }
72
+
73
+ if (argv.includes("--assets-extract")) {
74
+ if (isCompiledBinary(argv))
75
+ {
76
+ const targetDir = resolveCompiledBaseDir({ argv });
77
+ const extracted = await archive.extract(targetDir);
78
+ console.log(`Extracted ${extracted} asset(s) to ${targetDir}`);
79
+ } // if isCompiled
80
+ else
81
+ console.log("Assets extraction should only be used in a Bun compiled single-file executable");
82
+
83
+ process.exit(0);
84
+ }
85
+ }
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bun
2
+
3
+ // 1. Injects assets to global.internalAssets
4
+ // as { "./path/in/tar":file.bytes() }
5
+ // 2. Sets global.assetsLoaderPromise
6
+ import "./assetsLoader.mjs"
7
+
8
+ import "../src/index.js"
9
+
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ sd=$(dirname "$0")
4
+
5
+ cd "$sd"/..
6
+
7
+ tar -cvf single-exe/assets.tar runtime README.md CHANGELOG.md node_modules/wasmoon/dist/glue.wasm
@@ -4,6 +4,7 @@ import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  //import { Glob } from "bun";
6
6
  import { defaultAllSettings, OPTION_CHOICES, LOCAL_SETTINGS } from "./defaults.js";
7
+ import { isHex3Encoding } from "../runtime/encodings.js";
7
8
 
8
9
  export class Config {
9
10
  constructor({ configDir = "" } = {}) {
@@ -105,7 +106,7 @@ function normalizeSetting(key, value) {
105
106
  function validateOption(option, value) {
106
107
  if (option === "encoding") {
107
108
  const encoding = String(value || "utf-8");
108
- if (encoding === "hex3") return;
109
+ if (isHex3Encoding(encoding)) return;
109
110
  try { new TextDecoder(encoding); }
110
111
  catch { throw new Error(`Invalid encoding: ${value}`); }
111
112
  }
@@ -40,15 +40,19 @@ export class SyntaxDefinition {
40
40
 
41
41
  export async function loadSyntaxDefinitions(runtime) {
42
42
  const headers = new Map();
43
- for (const file of runtime.list(4)) {
44
- headers.set(file.name, parseHeaderFile(await file.text()));
45
- }
43
+ const headerPromises = runtime.list(4).map(async (file) => {
44
+ try {
45
+ const text = await file.text();
46
+ headers.set(file.name, parseHeaderFile(text));
47
+ } catch {}
48
+ });
49
+ await Promise.allSettled(headerPromises);
46
50
 
47
- const definitions = [];
48
- for (const file of runtime.list(1)) {
51
+ const defPromises = runtime.list(1).map(async (file) => {
49
52
  let text = "";
50
53
  let activeFile = file;
51
54
  let source = null;
55
+ let usedFallback = false;
52
56
  try {
53
57
  text = await file.text();
54
58
  source = Bun.YAML.parse(text);
@@ -59,21 +63,26 @@ export async function loadSyntaxDefinitions(runtime) {
59
63
  text = await fallback.text();
60
64
  source = Bun.YAML.parse(text);
61
65
  activeFile = fallback;
66
+ usedFallback = true;
62
67
  console.error("Failed to load user syntax yaml, using built-in fallback:", file.name);
63
68
  } catch {}
64
69
  }
65
70
  }
66
-
71
+ const header = headers.get(activeFile.name) ?? (source ? parseHeaderYaml(source) : parseHeaderTextFallback(text, activeFile.name));
67
72
  if (!source) {
68
73
  console.error("Failed to load syntax yaml:", file.name);
69
74
  console.error(" Will not highlight this kind of file");
70
75
  console.error(" @ loadSyntaxDefinitions ");
76
+ } else if (usedFallback) {
77
+ // keep the fallback path visible in logs, but do not fail the load
71
78
  }
72
-
73
- const header = headers.get(activeFile.name) ?? (source ? parseHeaderYaml(source) : parseHeaderTextFallback(text, activeFile.name));
74
- definitions.push(new SyntaxDefinition(header, source ?? { rules: [] }, text));
75
- }
76
- return definitions;
79
+ return new SyntaxDefinition(header, source ?? { rules: [] }, text);
80
+ });
81
+
82
+ const definitions = await Promise.allSettled(defPromises);
83
+ return definitions
84
+ .filter((result) => result.status === "fulfilled")
85
+ .map((result) => result.value);
77
86
  }
78
87
 
79
88
  export function detectSyntax(definitions, { path = "", firstLine = "", lines = [] } = {}) {