@diegopetrucci/pi-git-footer 0.1.2 → 0.1.4

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.
@@ -1 +1 @@
1
- 0.79.10
1
+ 0.80.6
package/index.ts CHANGED
@@ -31,6 +31,11 @@ type CommandRunner = (
31
31
  options: { cwd: string; signal: AbortSignal },
32
32
  ) => Promise<CommandResult>;
33
33
 
34
+ type RunCommandSafelyResult =
35
+ | { kind: "ok"; result: CommandResult }
36
+ | { kind: "transient" }
37
+ | { kind: "unavailable" };
38
+
34
39
  type TimerHandle = unknown;
35
40
 
36
41
  type Clock = {
@@ -286,6 +291,10 @@ function defaultClock(): Clock {
286
291
  };
287
292
  }
288
293
 
294
+ function isCommandUnavailableError(error: unknown): boolean {
295
+ return !!error && typeof error === "object" && (error as { code?: unknown }).code === "ENOENT";
296
+ }
297
+
289
298
  class GitFooterCache {
290
299
  private readonly cwd: () => string;
291
300
  private readonly runner: CommandRunner;
@@ -345,7 +354,7 @@ class GitFooterCache {
345
354
  const result = await this.fetchGitStatus();
346
355
  if (this.disposed) return;
347
356
  if (result.kind === "transient") return;
348
- if (result.kind === "not-a-repo") {
357
+ if (result.kind === "not-a-repo" || result.kind === "unavailable") {
349
358
  this.statusSnapshot = undefined;
350
359
  this.pullRequestSnapshot = undefined;
351
360
  this.lastSeenBranch = undefined;
@@ -371,8 +380,8 @@ class GitFooterCache {
371
380
 
372
381
  const pr = await this.fetchPullRequest();
373
382
  if (this.disposed) return;
374
- if (pr !== undefined) this.pullRequestSnapshot = pr;
375
- else if (branchChanged) this.pullRequestSnapshot = undefined;
383
+ if (pr.kind === "ok") this.pullRequestSnapshot = pr.pullRequest;
384
+ else if (pr.kind === "not-found" || pr.kind === "unavailable") this.pullRequestSnapshot = undefined;
376
385
 
377
386
  this.emitChangeIfSnapshotsChanged(previousStatusSnapshot, previousPullRequestSnapshot);
378
387
  }
@@ -399,32 +408,40 @@ class GitFooterCache {
399
408
  | { kind: "ok"; status: GitStatusSnapshot }
400
409
  | { kind: "not-a-repo" }
401
410
  | { kind: "transient" }
411
+ | { kind: "unavailable" }
402
412
  > {
403
413
  const result = await this.runCommandSafely("git", GIT_STATUS_ARGS, this.gitTimeoutMs);
404
- if (!result) return { kind: "transient" };
405
- if (result.exitCode !== 0) return { kind: "not-a-repo" };
406
- return { kind: "ok", status: parseGitStatusPorcelainV2(result.stdout) };
414
+ if (result.kind !== "ok") return result;
415
+ if (result.result.exitCode !== 0) return { kind: "not-a-repo" };
416
+ return { kind: "ok", status: parseGitStatusPorcelainV2(result.result.stdout) };
407
417
  }
408
418
 
409
- private async fetchPullRequest(): Promise<PullRequestSnapshot | undefined> {
419
+ private async fetchPullRequest(): Promise<
420
+ | { kind: "ok"; pullRequest: PullRequestSnapshot | undefined }
421
+ | { kind: "not-found" }
422
+ | { kind: "transient" }
423
+ | { kind: "unavailable" }
424
+ > {
410
425
  const result = await this.runCommandSafely("gh", GH_PR_VIEW_ARGS, this.ghTimeoutMs);
411
- if (!result || result.exitCode !== 0) return undefined;
412
- return parsePullRequestJson(result.stdout);
426
+ if (result.kind !== "ok") return result;
427
+ if (result.result.exitCode !== 0) return { kind: "not-found" };
428
+ return { kind: "ok", pullRequest: parsePullRequestJson(result.result.stdout) };
413
429
  }
414
430
 
415
431
  private async runCommandSafely(
416
432
  command: string,
417
433
  args: readonly string[],
418
434
  timeoutMs: number,
419
- ): Promise<CommandResult | undefined> {
420
- if (this.disposed) return undefined;
435
+ ): Promise<RunCommandSafelyResult> {
436
+ if (this.disposed) return { kind: "transient" };
421
437
  const controller = new AbortController();
422
438
  this.inflightControllers.add(controller);
423
439
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
424
440
  try {
425
- return await this.runner(command, args, { cwd: this.cwd(), signal: controller.signal });
426
- } catch {
427
- return undefined;
441
+ const result = await this.runner(command, args, { cwd: this.cwd(), signal: controller.signal });
442
+ return { kind: "ok", result };
443
+ } catch (error) {
444
+ return isCommandUnavailableError(error) ? { kind: "unavailable" } : { kind: "transient" };
428
445
  } finally {
429
446
  clearTimeout(timeoutId);
430
447
  this.inflightControllers.delete(controller);
@@ -478,3 +495,14 @@ export default function (pi: ExtensionAPI) {
478
495
  ctx.ui.setStatus(STATUS_KEY, undefined);
479
496
  });
480
497
  }
498
+
499
+ export const __testing = {
500
+ GitFooterCache,
501
+ GIT_STATUS_ARGS,
502
+ GH_PR_VIEW_ARGS,
503
+ parseGitStatusPorcelainV2,
504
+ parsePullRequestJson,
505
+ formatGitStatusFooterSegment,
506
+ formatPullRequestFooterSegment,
507
+ formatGitFooterStatus,
508
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-git-footer",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "A TLH-style git status add-on for pi's built-in footer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -31,5 +31,9 @@
31
31
  "peerDependencies": {
32
32
  "@earendil-works/pi-coding-agent": "*",
33
33
  "@earendil-works/pi-tui": "*"
34
+ },
35
+ "type": "module",
36
+ "engines": {
37
+ "node": ">=22.19.0"
34
38
  }
35
39
  }