@forwardimpact/libutil 0.1.88 → 0.1.90

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/libutil",
3
- "version": "0.1.88",
3
+ "version": "0.1.90",
4
4
  "description": "Cross-cutting utilities: retry, hashing, token counting, and project discovery.",
5
5
  "keywords": [
6
6
  "util",
package/src/finder.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from "path";
2
2
  import { createRequire } from "node:module";
3
+ import { LIBCLI_IS_COMPILED } from "@forwardimpact/libcli";
3
4
 
4
5
  const NOOP_LOGGER = { debug() {} };
5
6
 
@@ -17,6 +18,7 @@ export class Finder {
17
18
  #existsSync;
18
19
  #logger;
19
20
  #proc;
21
+ #isCompiled;
20
22
 
21
23
  /**
22
24
  * @param {object} config - Injected collaborators.
@@ -25,6 +27,10 @@ export class Finder {
25
27
  * falls back to `fs` when omitted.
26
28
  * @param {object} config.proc - Process collaborator (cwd provider).
27
29
  * @param {object} [config.logger] - Optional logger; defaults to a no-op.
30
+ * @param {boolean} [config.isCompiled] - Whether the host is a
31
+ * `bun build --compile` binary; defaults to libcli's `LIBCLI_IS_COMPILED`.
32
+ * Injectable so tests can exercise the compiled branch of
33
+ * {@link Finder#findProjectRoot} without a real binary.
28
34
  */
29
35
  constructor(config = {}) {
30
36
  // Finder is the one module that legitimately bridges the sync and async
@@ -45,6 +51,7 @@ export class Finder {
45
51
  this.#existsSync = existsTarget.existsSync.bind(existsTarget);
46
52
  this.#proc = proc;
47
53
  this.#logger = config.logger ?? NOOP_LOGGER;
54
+ this.#isCompiled = config.isCompiled ?? LIBCLI_IS_COMPILED;
48
55
  }
49
56
 
50
57
  /**
@@ -62,6 +69,7 @@ export class Finder {
62
69
  fsSync: this.#fsSync,
63
70
  proc: this.#proc,
64
71
  logger,
72
+ isCompiled: this.#isCompiled,
65
73
  });
66
74
  }
67
75
 
@@ -106,12 +114,28 @@ export class Finder {
106
114
  }
107
115
 
108
116
  /**
109
- * Find the project root directory.
110
- * @param {string} startPath - Starting directory path
117
+ * Find the project root a tool operates against, transparently handling
118
+ * compiled binaries.
119
+ *
120
+ * In a `bun build --compile` binary the entry module lives in the virtual
121
+ * `/$bunfs` root, so `import.meta.url`/`__dirname`-relative traversal is
122
+ * meaningless — the binary operates on whatever project tree it is launched
123
+ * from, so the working directory *is* the project root. In source/npx
124
+ * execution the working directory may sit anywhere inside the project, so we
125
+ * walk upward from `startPath` for the nearest `package.json`.
126
+ *
127
+ * Folding the compiled check in here keeps it out of every consumer: callers
128
+ * just ask the injected `runtime.finder` for the project root and get the
129
+ * right answer in both worlds.
130
+ *
131
+ * @param {string} [startPath] - Source-mode search origin; defaults to cwd.
111
132
  * @returns {string} Project root directory path
112
133
  */
113
134
  findProjectRoot(startPath) {
114
- const projectRoot = this.findUpward(startPath, "package.json", 5);
135
+ if (this.#isCompiled) return this.#proc.cwd();
136
+
137
+ const start = startPath ?? this.#proc.cwd();
138
+ const projectRoot = this.findUpward(start, "package.json", 5);
115
139
  if (projectRoot) {
116
140
  return path.dirname(projectRoot);
117
141
  }
package/src/runtime.js CHANGED
@@ -20,7 +20,7 @@ import { Finder } from "./finder.js";
20
20
  * @property {Object} fs
21
21
  * Async filesystem surface (the `node:fs/promises` shape): `readFile`,
22
22
  * `writeFile`, `readdir`, `stat`, `mkdir`, `access`, `copyFile`, `cp`, `rm`,
23
- * `lstat`, `unlink`, `symlink`, `utimes`, `chmod`, plus the two stream
23
+ * `lstat`, `unlink`, `rename`, `symlink`, `utimes`, `chmod`, plus the two stream
24
24
  * factories `createReadStream` / `createWriteStream` (the `node:fs` shape —
25
25
  * the promises API has no stream factories, so they live on the async
26
26
  * surface as the canonical streaming seam). A module destructures `fs` xor