@delorenj/pjangler 1.5.27 โ†’ 1.5.29

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/dist/crank/tui.js CHANGED
@@ -435,6 +435,7 @@ function CrankDashboard({ controller, refreshMs = 5e3, clock = Date.now, onQuit
435
435
  }
436
436
  async function runCrankDashboard(controller, run = {}) {
437
437
  const { refreshMs, ...ink } = run;
438
+ controller.read();
438
439
  await runInk((finish) => /* @__PURE__ */ jsx3(CrankDashboard, { controller, refreshMs, onQuit: () => finish() }), { alternateScreen: true, ...ink });
439
440
  }
440
441
  export {
package/dist/index.js CHANGED
@@ -6064,7 +6064,7 @@ var init_capture = __esm({
6064
6064
 
6065
6065
  // src/index.ts
6066
6066
  import { spawnSync as spawnSync17 } from "node:child_process";
6067
- import { existsSync as existsSync25, readdirSync as readdirSync12, readFileSync as readFileSync29, statSync as statSync8 } from "node:fs";
6067
+ import { existsSync as existsSync26, readdirSync as readdirSync12, readFileSync as readFileSync29, statSync as statSync8 } from "node:fs";
6068
6068
  import { basename as basename11, join as join34, resolve as resolve25 } from "node:path";
6069
6069
  import { Command as Command3, CommanderError, Option } from "commander";
6070
6070
 
@@ -20377,6 +20377,8 @@ init_project();
20377
20377
  init_style();
20378
20378
 
20379
20379
  // src/tui/load.ts
20380
+ import { existsSync as existsSync24 } from "node:fs";
20381
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
20380
20382
  var TuiUnavailableError = class extends Error {
20381
20383
  };
20382
20384
  function isInteractive(streams = {}) {
@@ -20384,6 +20386,9 @@ function isInteractive(streams = {}) {
20384
20386
  const stdout = streams.stdout ?? process.stdout;
20385
20387
  return Boolean(stdin.isTTY && stdout.isTTY);
20386
20388
  }
20389
+ var RESOLVER_FRAME = /node:internal\/modules\/(?:esm\/resolve|package_json_reader)/;
20390
+ var CANNOT_FIND = /^Cannot find (package|module) '([^']+)' imported from (.+)$/;
20391
+ var INSTALL_DEPS = "run npm ci in this checkout, or reinstall the global package (npm i -g @delorenj/pjangler)";
20387
20392
  async function loadScreen(relativeToDist, base) {
20388
20393
  const url = new URL(relativeToDist, base);
20389
20394
  try {
@@ -20392,10 +20397,15 @@ async function loadScreen(relativeToDist, base) {
20392
20397
  url.href
20393
20398
  );
20394
20399
  } catch (error) {
20395
- if (error?.code === "ERR_MODULE_NOT_FOUND") {
20400
+ const err = error;
20401
+ if (err?.code !== "ERR_MODULE_NOT_FOUND") throw error;
20402
+ if (!existsSync24(fileURLToPath6(url))) {
20396
20403
  throw new TuiUnavailableError(`TUI screen ${relativeToDist} is not built (run: npm run build); looked for ${url.pathname}`);
20397
20404
  }
20398
- throw error;
20405
+ if (typeof err.stack !== "string" || !RESOLVER_FRAME.test(err.stack)) throw error;
20406
+ const missing = CANNOT_FIND.exec(err.message);
20407
+ const detail = missing ? `cannot find ${missing[1]} '${missing[2]}' imported by ${missing[3]}` : "an import of it cannot be resolved";
20408
+ throw new TuiUnavailableError(`TUI screen ${relativeToDist} is built but its dependencies are not installed: ${detail}; ${INSTALL_DEPS}`);
20399
20409
  }
20400
20410
  }
20401
20411
 
@@ -20420,7 +20430,7 @@ var TAB_STATUS_SUFFIX = /[\s๐ŸŸข๐ŸŸก๐Ÿ”ด๐Ÿ””โšซโœณโœปโ—ยทโ”€]+$/u;
20420
20430
  var defaultShell = (command, args) => {
20421
20431
  const result2 = spawnSync16(command, args, { encoding: "utf8", timeout: 1e4 });
20422
20432
  if (result2.error) return { stdout: "", status: 127 };
20423
- return { stdout: result2.stdout ?? "", status: result2.status ?? 1 };
20433
+ return { stdout: result2.stdout ?? "", status: result2.status ?? 1, stderr: result2.stderr ?? "" };
20424
20434
  };
20425
20435
  function tabDisplay(name) {
20426
20436
  const trimmed2 = (name ?? "").trim();
@@ -20503,10 +20513,28 @@ function redis(deps = {}) {
20503
20513
  const endpoint = redisEndpoint();
20504
20514
  const host = deps.host ?? endpoint.host;
20505
20515
  const port = deps.port ?? endpoint.port;
20506
- const call = (args) => shell("redis-cli", ["--raw", "-h", host, "-p", String(port), ...args]);
20516
+ const call = (args) => shell("redis-cli", ["--raw", "-e", "-h", host, "-p", String(port), ...args]);
20517
+ let errorExitSupport = "unprobed";
20518
+ const requireErrorExit = () => {
20519
+ if (errorExitSupport === "unprobed") {
20520
+ const help = shell("redis-cli", ["--help"]);
20521
+ errorExitSupport = help.status === 0 && /^usage: redis-cli/im.test(help.stdout) ? /(^|\n)\s+-e\s/.test(help.stdout) ? "yes" : "no" : "unknown";
20522
+ }
20523
+ if (errorExitSupport === "no") {
20524
+ throw new Error(
20525
+ `redis-cli is too old for the crank store at ${host}:${port}: no -e flag (needs redis-tools >= 6.2) to fail on Redis error replies \u2014 upgrade redis-tools`
20526
+ );
20527
+ }
20528
+ };
20529
+ const notAnswering = (stderr) => !stderr || stderr.startsWith("Could not connect to Redis at") || stderr.startsWith("Error: ");
20507
20530
  const run = (...args) => {
20531
+ requireErrorExit();
20508
20532
  const result2 = call(args);
20509
- if (result2.status !== 0) throw new Error(`Redis is not answering at ${host}:${port} (${args[0]} failed)`);
20533
+ if (result2.status !== 0) {
20534
+ const stderr = (result2.stderr ?? "").trim();
20535
+ if (notAnswering(stderr)) throw new Error(`Redis is not answering at ${host}:${port} (${args[0]} failed)`);
20536
+ throw new Error(`Redis error reply at ${host}:${port}: ${stderr.split("\n")[0]} (${args.join(" ")})`);
20537
+ }
20510
20538
  return result2.stdout.replace(/\r?\n$/, "");
20511
20539
  };
20512
20540
  const lines = (out) => out.split("\n").map((line) => line.replace(/\r$/, "")).filter(Boolean);
@@ -20515,6 +20543,7 @@ function redis(deps = {}) {
20515
20543
  endpoint: `${host}:${port}`,
20516
20544
  /** PING -> true only when Redis answers PONG. */
20517
20545
  ping: () => {
20546
+ requireErrorExit();
20518
20547
  const result2 = call(["PING"]);
20519
20548
  return result2.status === 0 && result2.stdout.replace(/\r?\n$/, "") === "PONG";
20520
20549
  },
@@ -20970,24 +20999,23 @@ function registerCrankCli(program2) {
20970
20999
  console.error(`${xmark} --tail needs a positive integer, got "${options.tail}"`);
20971
21000
  process.exit(1);
20972
21001
  }
20973
- let current;
20974
21002
  try {
20975
- current = resolveCurrentTab();
21003
+ const current = resolveCurrentTab();
21004
+ const store = redis();
21005
+ const rows = store.logRows(current.tab);
21006
+ if (!rows.length) {
21007
+ console.log(` ${dim(`no crank log rows for ${current.tab}`)}`);
21008
+ return;
21009
+ }
21010
+ const shown = rows.slice(-tail);
21011
+ console.log(` ${bold("crank log")} ${dim(glyph.dot)} ${cyan(current.tab)} ${dim(`(${shown.length}/${rows.length} rows, newest last)`)}`);
21012
+ for (const raw of shown) {
21013
+ console.log(renderLogRow(raw) ?? raw);
21014
+ }
20976
21015
  } catch (error) {
20977
21016
  console.error(`${xmark} ${error instanceof Error ? error.message : String(error)}`);
20978
21017
  process.exit(1);
20979
21018
  }
20980
- const store = redis();
20981
- const rows = store.logRows(current.tab);
20982
- if (!rows.length) {
20983
- console.log(` ${dim(`no crank log rows for ${current.tab}`)}`);
20984
- return;
20985
- }
20986
- const shown = rows.slice(-tail);
20987
- console.log(` ${bold("crank log")} ${dim(glyph.dot)} ${cyan(current.tab)} ${dim(`(${shown.length}/${rows.length} rows, newest last)`)}`);
20988
- for (const raw of shown) {
20989
- console.log(renderLogRow(raw) ?? raw);
20990
- }
20991
21019
  });
20992
21020
  }
20993
21021
  function fail(error, what) {
@@ -21100,7 +21128,7 @@ async function promptForRuleIds(rules) {
21100
21128
  return selected;
21101
21129
  }
21102
21130
  function readJson2(path) {
21103
- if (!existsSync25(path)) return void 0;
21131
+ if (!existsSync26(path)) return void 0;
21104
21132
  try {
21105
21133
  const parsed = JSON.parse(readFileSync29(path, "utf8"));
21106
21134
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
@@ -21195,7 +21223,7 @@ function actionNeedsRun(plan, kind, syncMode) {
21195
21223
  if (!action || action.kind !== "project.write-manifest") return false;
21196
21224
  const next = `${JSON.stringify(action.manifest, null, 2)}
21197
21225
  `;
21198
- return !existsSync25(action.path) || readFileSync29(action.path, "utf8") !== next;
21226
+ return !existsSync26(action.path) || readFileSync29(action.path, "utf8") !== next;
21199
21227
  }
21200
21228
  if (kind === "copier.copy.commonproject") return true;
21201
21229
  if (kind === "ticket-provider.create-or-link") return plan.actions.some((action) => action.kind === kind && action.enabled);
@@ -21252,7 +21280,7 @@ async function resolveProjectInitTarget(name, options) {
21252
21280
  if (!targetDir && cwdGitRoot) {
21253
21281
  targetDir = cwdGitRoot;
21254
21282
  }
21255
- if (!targetDir && !name && existsSync25(join34(cwd, ".project.json"))) {
21283
+ if (!targetDir && !name && existsSync26(join34(cwd, ".project.json"))) {
21256
21284
  targetDir = cwd;
21257
21285
  }
21258
21286
  if (!targetDir && interactive) {
@@ -21263,10 +21291,10 @@ async function resolveProjectInitTarget(name, options) {
21263
21291
  if (!targetDir) {
21264
21292
  throw new Error("Project name or --target-dir is required when project init is not run inside a git repo or a directory carrying .project.json");
21265
21293
  }
21266
- const targetExists = existsSync25(targetDir);
21294
+ const targetExists = existsSync26(targetDir);
21267
21295
  if (targetExists && !statSync8(targetDir).isDirectory()) throw new Error(`Target path is not a directory: ${targetDir}`);
21268
21296
  const targetGitRoot = targetExists ? findGitRoot(targetDir) : void 0;
21269
- const alreadyScaffolded = targetExists && (existsSync25(join34(targetDir, ".project.json")) || existsSync25(join34(targetDir, ".copier-answers.yml")));
21297
+ const alreadyScaffolded = targetExists && (existsSync26(join34(targetDir, ".project.json")) || existsSync26(join34(targetDir, ".copier-answers.yml")));
21270
21298
  const syncMode = Boolean(
21271
21299
  targetGitRoot && resolve25(targetGitRoot) === resolve25(targetDir) || alreadyScaffolded
21272
21300
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delorenj/pjangler",
3
- "version": "1.5.27",
3
+ "version": "1.5.29",
4
4
  "description": "Project subsystem bootstrapper CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",