@unbound-app/debugger 0.6.0 → 0.7.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 (2) hide show
  1. package/dist/index.mjs +42 -34
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -8,6 +8,7 @@ import { cli } from "cleye";
8
8
  const HISTORY_PATH = join(__dirname, "..", "..", ".history.json");
9
9
  const HISTORY_FILE = Bun.file(HISTORY_PATH);
10
10
  let history = [];
11
+ /** @description Loads the persisted REPL history from disk, if present. */
11
12
  async function loadHistory() {
12
13
  try {
13
14
  if (await HISTORY_FILE.exists()) history = await HISTORY_FILE.json();
@@ -15,10 +16,12 @@ async function loadHistory() {
15
16
  terminal.red.bold(`Failed to load history.\n`);
16
17
  }
17
18
  }
19
+ /** @description Appends an entry to the REPL history and persists it. */
18
20
  function addHistoryItem(item) {
19
21
  history.push(item);
20
22
  persistHistory();
21
23
  }
24
+ /** @description Writes the REPL history to disk. */
22
25
  function persistHistory() {
23
26
  HISTORY_FILE.write(JSON.stringify(history, null, 2));
24
27
  }
@@ -218,29 +221,41 @@ function handleDeviceMessage$1(message) {
218
221
  entry.deliver(message);
219
222
  return;
220
223
  }
221
- if (message.type === "log") {
222
- for (const socket of state$2.controllers) socket.send(JSON.stringify(message));
223
- for (const listener of logListeners) listener(message);
224
- }
224
+ if (message.type !== "log") return;
225
+ for (const socket of state$2.controllers) socket.send(JSON.stringify(message));
226
+ for (const listener of logListeners) listener(message);
225
227
  }
226
- /** @description Subscribes to device log lines; returns an unsubscribe function. */
228
+ /**
229
+ * @description Subscribes to device log lines.
230
+ * @param listener Called with each log line.
231
+ * @returns An unsubscribe function.
232
+ */
227
233
  function onLog(listener) {
228
234
  logListeners.add(listener);
229
235
  return () => void logListeners.delete(listener);
230
236
  }
231
- /** @description Subscribes to code a controller (e.g. the MCP) sends to evaluate; returns an unsubscribe function. */
237
+ /**
238
+ * @description Subscribes to code a controller (e.g. the MCP) sends to evaluate.
239
+ * @param listener Called with each controller's code.
240
+ * @returns An unsubscribe function.
241
+ */
232
242
  function onControllerEval(listener) {
233
243
  evalListeners.add(listener);
234
244
  return () => void evalListeners.delete(listener);
235
245
  }
236
- /** @description Subscribes to the results of controller evals; returns an unsubscribe function. */
246
+ /**
247
+ * @description Subscribes to the results of controller evals.
248
+ * @param listener Called with each controller eval's result.
249
+ * @returns An unsubscribe function.
250
+ */
237
251
  function onControllerResult(listener) {
238
252
  resultListeners.add(listener);
239
253
  return () => void resultListeners.delete(listener);
240
254
  }
241
255
  /**
242
- * @description Subscribes to controller (MCP) connect/disconnect transitions, with the resulting
243
- * controller count; returns an unsubscribe function.
256
+ * @description Subscribes to controller (MCP) connect/disconnect transitions.
257
+ * @param listener Called with the transition and the resulting controller count.
258
+ * @returns An unsubscribe function.
244
259
  */
245
260
  function onController(listener) {
246
261
  controllerListeners.add(listener);
@@ -270,12 +285,14 @@ const state$1 = {
270
285
  repl: null,
271
286
  currentInput: ""
272
287
  };
288
+ /** @description Exits the debugger on Ctrl+C. */
273
289
  function setupReplListeners() {
274
- terminal.on("key", (name, ...t) => {
290
+ terminal.on("key", (name) => {
275
291
  if (name !== "CTRL_C") return;
276
292
  process.exit(0);
277
293
  });
278
294
  }
295
+ /** @description Draws a fresh prompt and evaluates the submitted line on the device. */
279
296
  async function takeReplInput() {
280
297
  abortCurrentReplInput();
281
298
  terminal.bold.cyan("» ");
@@ -297,27 +314,17 @@ async function takeReplInput() {
297
314
  });
298
315
  await state$1.repl.promise;
299
316
  }
317
+ /** @description Cancels the active prompt, optionally erasing it and the pending input. */
300
318
  function abortCurrentReplInput({ deletePrompt = false, deleteCurrentInput = false } = {}) {
301
- if (state$1.repl) {
302
- state$1.repl.abort();
303
- state$1.repl = null;
304
- if (deletePrompt) terminal.deleteLine(-1);
305
- if (deleteCurrentInput) state$1.currentInput = "";
306
- }
319
+ if (!state$1.repl) return;
320
+ state$1.repl.abort();
321
+ state$1.repl = null;
322
+ if (deletePrompt) terminal.deleteLine(-1);
323
+ if (deleteCurrentInput) state$1.currentInput = "";
307
324
  }
308
325
  //#endregion
309
326
  //#region src/lib/format-log.ts
310
327
  /**
311
- * Reformats a log line as Hermes hands it to `nativeLoggingHook`. Hermes inspects each `console.*`
312
- * argument and joins them with `, ` — quoting string args (`'a', 'b', { x: 1 }`). That reads badly
313
- * in the debugger terminal, so we split on the top-level commas Hermes inserted, unquote the plain
314
- * string segments, and rejoin with spaces — leaving inspected objects/arrays untouched.
315
- *
316
- * This is best-effort: it only rewrites what parses cleanly as Hermes's join format. A segment it
317
- * can't confidently classify is left exactly as received, so worst case a line is unchanged, never
318
- * mangled.
319
- */
320
- /**
321
328
  * @description Cleans a Hermes-joined console line into space-separated, unquoted output.
322
329
  * @param message The raw `nativeLoggingHook` message string.
323
330
  * @returns The reformatted line, or the original if it doesn't look like a Hermes join.
@@ -380,9 +387,8 @@ function unquoteSegment(segment) {
380
387
  //#endregion
381
388
  //#region src/lib/banner.ts
382
389
  /**
383
- * Renders the debugger's startup banner: a rounded box, tinted in the brand pink, that adapts to the
384
- * terminal's width. The box shrinks to fit narrow terminals and caps at a comfortable reading width
385
- * on wide ones; when the terminal is too narrow for a box, it falls back to plain starred lines.
390
+ * Renders the debugger's startup banner: a pink rounded box sized to the terminal, capped on wide
391
+ * ones, falling back to plain starred lines when the terminal is too narrow for a box.
386
392
  */
387
393
  /** Brand pink used for the banner frame and title. */
388
394
  const PINK = "#ff5fbf";
@@ -450,7 +456,7 @@ function printFallback(title, lines) {
450
456
  /** The usable terminal width: terminal-kit's when it's a real TTY, else stdout's, else a default. */
451
457
  function terminalWidth() {
452
458
  const fromKit = terminal.width;
453
- if (typeof fromKit === "number" && Number.isFinite(fromKit)) return fromKit;
459
+ if (Number.isFinite(fromKit)) return fromKit;
454
460
  const fromStdout = process.stdout.columns;
455
461
  if (typeof fromStdout === "number" && fromStdout > 0) return fromStdout;
456
462
  return 80;
@@ -494,9 +500,7 @@ const SYSTEM_COLORS = {
494
500
  gray: "#9ca3af"
495
501
  };
496
502
  /**
497
- * @description Prints a bold, glyph-prefixed banner line for a bridge lifecycle event, tinted with
498
- * the given hex colour. Uses terminal-kit's runtime `colorRgbHex`, which `@types/terminal-kit`
499
- * doesn't declare, so the chain is reached through a narrow cast.
503
+ * @description Prints a bold, glyph-prefixed banner line for a bridge lifecycle event.
500
504
  * @param color The banner colour, keyed into {@link SYSTEM_COLORS}.
501
505
  * @param text The line to print (the glyph is prepended and a trailing newline added).
502
506
  */
@@ -595,6 +599,10 @@ function handleControllerMessage(ws, raw) {
595
599
  }
596
600
  if (message?.type === "plugin-push") pushPluginForController(ws, message);
597
601
  }
602
+ /**
603
+ * @description Finds the machine's first external IPv4 address, for devices on the LAN to dial.
604
+ * @returns The address, or `undefined` if the machine has none.
605
+ */
598
606
  function getLocalAddress() {
599
607
  const interfaces = os.networkInterfaces();
600
608
  for (const ifaces of Object.values(interfaces)) {
@@ -605,6 +613,6 @@ function getLocalAddress() {
605
613
  //#endregion
606
614
  //#region src/index.ts
607
615
  await loadHistory();
608
- await setupWebSocket();
616
+ setupWebSocket();
609
617
  //#endregion
610
618
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbound-app/debugger",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "A REPL debugger for @unbound-app/client runtime instances.",
5
5
  "author": {
6
6
  "name": "Mario P.",
@@ -24,7 +24,7 @@
24
24
  "build": "tsdown"
25
25
  },
26
26
  "dependencies": {
27
- "@unbound-app/debugger-protocol": "0.6.0",
27
+ "@unbound-app/debugger-protocol": "0.7.0",
28
28
  "cleye": "^1.3.4",
29
29
  "terminal-kit": "^3.1.2"
30
30
  },