peakurl 0.1.1 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PeakURL Command-Line Interface and Abd Ur Rehman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
- # PeakURL CLI
1
+ # PeakURL - Command-Line Interface
2
2
 
3
3
  The official command-line interface for PeakURL.
4
4
 
5
5
  Use `peakurl` to create short links, inspect existing links, and manage your PeakURL account from the terminal.
6
6
 
7
+ Learn more in the full CLI docs: <https://peakurl.org/docs/cli>
8
+
7
9
  ## Install
8
10
 
9
11
  Node.js 20 or later is required.
@@ -127,6 +129,7 @@ export PEAKURL_DISABLE_UPDATE_CHECK=1
127
129
  ## Links
128
130
 
129
131
  - Website: <https://peakurl.org/>
132
+ - CLI docs: <https://peakurl.org/docs/cli>
130
133
  - API docs: <https://peakurl.org/docs/api>
131
134
  - npm package: <https://www.npmjs.com/package/peakurl>
132
135
  - Issues: <https://github.com/PeakURL/PeakURL-CLI/issues>
package/bin/peakurl.js CHANGED
@@ -13,7 +13,7 @@ var CliError = class extends Error {
13
13
  this.exitCode = exitCode;
14
14
  }
15
15
  };
16
- function toCliError(error) {
16
+ function wrapCliError(error) {
17
17
  if (error instanceof CliError) {
18
18
  return error;
19
19
  }
@@ -24,6 +24,14 @@ function toCliError(error) {
24
24
  }
25
25
 
26
26
  // src/lib/url.ts
27
+ function validateHttpUrl(parsed, label) {
28
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
29
+ throw new CliError(`${label} must use http or https.`);
30
+ }
31
+ if (parsed.username || parsed.password) {
32
+ throw new CliError(`${label} must not include embedded credentials.`);
33
+ }
34
+ }
27
35
  function normalizeBaseUrl(value) {
28
36
  const input = value.trim();
29
37
  if (!input) {
@@ -35,9 +43,7 @@ function normalizeBaseUrl(value) {
35
43
  } catch {
36
44
  throw new CliError(`Invalid base URL: ${value}`);
37
45
  }
38
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
39
- throw new CliError("PeakURL base URLs must use http or https.");
40
- }
46
+ validateHttpUrl(parsed, "PeakURL base URL");
41
47
  parsed.hash = "";
42
48
  parsed.search = "";
43
49
  const pathname = parsed.pathname.replace(/\/+$/, "");
@@ -62,8 +68,13 @@ function normalizeDestinationUrl(value) {
62
68
  throw new CliError("A destination URL is required.");
63
69
  }
64
70
  try {
65
- return new URL(input).toString();
66
- } catch {
71
+ const parsed = new URL(input);
72
+ validateHttpUrl(parsed, "Destination URL");
73
+ return parsed.toString();
74
+ } catch (error) {
75
+ if (error instanceof CliError) {
76
+ throw error;
77
+ }
67
78
  throw new CliError(`Invalid destination URL: ${value}`);
68
79
  }
69
80
  }
@@ -241,7 +252,7 @@ function defaultConfigPath() {
241
252
  function defaultStatePath() {
242
253
  return join(envPaths("peakurl", { suffix: "" }).config, STATE_FILENAME);
243
254
  }
244
- async function ensureParentDirectory(filePath) {
255
+ async function prepareFileDirectory(filePath) {
245
256
  const directory = dirname(filePath);
246
257
  await mkdir(directory, { recursive: true, mode: 448 });
247
258
  return directory;
@@ -301,7 +312,7 @@ var ConfigStore = class {
301
312
  * @param config Normalized credential set to write.
302
313
  */
303
314
  async save(config) {
304
- const directory = await ensureParentDirectory(this.filePath);
315
+ const directory = await prepareFileDirectory(this.filePath);
305
316
  await writeFile(this.filePath, `${JSON.stringify(config, null, 2)}
306
317
  `, {
307
318
  mode: 384
@@ -346,11 +357,16 @@ var StateStore = class {
346
357
  * @param state State payload to save.
347
358
  */
348
359
  async save(state) {
349
- await ensureParentDirectory(this.filePath);
360
+ const directory = await prepareFileDirectory(this.filePath);
350
361
  await writeFile(this.filePath, `${JSON.stringify(state, null, 2)}
351
362
  `, {
352
363
  mode: 384
353
364
  });
365
+ try {
366
+ await chmod(directory, 448);
367
+ await chmod(this.filePath, 384);
368
+ } catch {
369
+ }
354
370
  }
355
371
  };
356
372
 
@@ -383,20 +399,126 @@ async function resolveStoredConfig(env, store = new ConfigStore()) {
383
399
  };
384
400
  }
385
401
 
402
+ // src/lib/output.ts
403
+ function writeStdout(message = "") {
404
+ process.stdout.write(`${message}
405
+ `);
406
+ }
407
+ function writeStderr(message = "") {
408
+ process.stderr.write(`${message}
409
+ `);
410
+ }
411
+ function writeNoticeBox(title, lines, target = "stderr") {
412
+ const contentLines = lines.length > 0 ? lines : [""];
413
+ const width = Math.max(
414
+ title.length,
415
+ ...contentLines.map((line) => line.length)
416
+ );
417
+ const stream = target === "stdout" ? process.stdout : process.stderr;
418
+ const useTuiBox = stream.isTTY;
419
+ const border = useTuiBox ? {
420
+ topLeft: "\u250C",
421
+ topRight: "\u2510",
422
+ bottomLeft: "\u2514",
423
+ bottomRight: "\u2518",
424
+ horizontal: "\u2500",
425
+ vertical: "\u2502",
426
+ separatorLeft: "\u251C",
427
+ separatorRight: "\u2524"
428
+ } : {
429
+ topLeft: "+",
430
+ topRight: "+",
431
+ bottomLeft: "+",
432
+ bottomRight: "+",
433
+ horizontal: "-",
434
+ vertical: "|",
435
+ separatorLeft: "+",
436
+ separatorRight: "+"
437
+ };
438
+ const topBorder = `${border.topLeft}${border.horizontal.repeat(width + 2)}${border.topRight}`;
439
+ const separator = `${border.separatorLeft}${border.horizontal.repeat(width + 2)}${border.separatorRight}`;
440
+ const bottomBorder = `${border.bottomLeft}${border.horizontal.repeat(width + 2)}${border.bottomRight}`;
441
+ const writeLine = target === "stdout" ? writeStdout : writeStderr;
442
+ writeLine(topBorder);
443
+ writeLine(`${border.vertical} ${title.padEnd(width)} ${border.vertical}`);
444
+ writeLine(separator);
445
+ for (const line of contentLines) {
446
+ writeLine(
447
+ `${border.vertical} ${line.padEnd(width)} ${border.vertical}`
448
+ );
449
+ }
450
+ writeLine(bottomBorder);
451
+ }
452
+ function formatTable(headers, rows, target = "stdout") {
453
+ const stream = target === "stdout" ? process.stdout : process.stderr;
454
+ const useTuiBox = stream.isTTY;
455
+ const border = useTuiBox ? {
456
+ topLeft: "\u250C",
457
+ topRight: "\u2510",
458
+ bottomLeft: "\u2514",
459
+ bottomRight: "\u2518",
460
+ horizontal: "\u2500",
461
+ vertical: "\u2502",
462
+ separatorLeft: "\u251C",
463
+ separatorRight: "\u2524",
464
+ topJunction: "\u252C",
465
+ middleJunction: "\u253C",
466
+ bottomJunction: "\u2534"
467
+ } : {
468
+ topLeft: "+",
469
+ topRight: "+",
470
+ bottomLeft: "+",
471
+ bottomRight: "+",
472
+ horizontal: "-",
473
+ vertical: "|",
474
+ separatorLeft: "+",
475
+ separatorRight: "+",
476
+ topJunction: "+",
477
+ middleJunction: "+",
478
+ bottomJunction: "+"
479
+ };
480
+ const widths = headers.map(
481
+ (header, index) => Math.max(
482
+ header.length,
483
+ ...rows.map((row) => (row[index] ?? "").length)
484
+ )
485
+ );
486
+ const formatTableBorder = (left, join2, right) => `${left}${widths.map((width) => border.horizontal.repeat(width + 2)).join(join2)}${right}`;
487
+ const formatTableRow = (cells) => `${border.vertical}${cells.map((cell, index) => ` ${(cell ?? "").padEnd(widths[index])} `).join(border.vertical)}${border.vertical}`;
488
+ return [
489
+ formatTableBorder(border.topLeft, border.topJunction, border.topRight),
490
+ formatTableRow(headers),
491
+ formatTableBorder(
492
+ border.separatorLeft,
493
+ border.middleJunction,
494
+ border.separatorRight
495
+ ),
496
+ ...rows.map(formatTableRow),
497
+ formatTableBorder(
498
+ border.bottomLeft,
499
+ border.bottomJunction,
500
+ border.bottomRight
501
+ )
502
+ ].join("\n");
503
+ }
504
+ function writeJson(value) {
505
+ writeStdout(JSON.stringify(value, null, 2));
506
+ }
507
+
386
508
  // src/lib/links.ts
387
509
  var LIST_KEYS = ["urls", "items", "results"];
388
- function asRecord(value) {
510
+ function objectValue(value) {
389
511
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
390
512
  }
391
- function readString(value) {
513
+ function stringValue(value) {
392
514
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
393
515
  }
394
- function readNumber(value) {
516
+ function numberValue(value) {
395
517
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
396
518
  }
397
- function firstString(link, keys) {
519
+ function pickString(link, keys) {
398
520
  for (const key of keys) {
399
- const value = readString(link[key]);
521
+ const value = stringValue(link[key]);
400
522
  if (value) {
401
523
  return value;
402
524
  }
@@ -407,31 +529,31 @@ function truncate(value, maxLength) {
407
529
  return value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
408
530
  }
409
531
  function extractListMeta(data) {
410
- const record = asRecord(data);
532
+ const record = objectValue(data);
411
533
  if (!record) {
412
534
  return null;
413
535
  }
414
- const meta = asRecord(record.meta);
536
+ const meta = objectValue(record.meta);
415
537
  if (meta) {
416
538
  return {
417
- page: readNumber(meta.page),
418
- limit: readNumber(meta.limit),
419
- totalItems: readNumber(meta.totalItems),
420
- totalPages: readNumber(meta.totalPages)
539
+ page: numberValue(meta.page),
540
+ limit: numberValue(meta.limit),
541
+ totalItems: numberValue(meta.totalItems),
542
+ totalPages: numberValue(meta.totalPages)
421
543
  };
422
544
  }
423
545
  return {
424
- page: readNumber(record.page),
425
- limit: readNumber(record.limit),
426
- totalItems: readNumber(record.total),
427
- totalPages: readNumber(record.totalPages)
546
+ page: numberValue(record.page),
547
+ limit: numberValue(record.limit),
548
+ totalItems: numberValue(record.total),
549
+ totalPages: numberValue(record.totalPages)
428
550
  };
429
551
  }
430
552
  function extractLinks(data) {
431
553
  if (Array.isArray(data)) {
432
554
  return data;
433
555
  }
434
- const record = asRecord(data);
556
+ const record = objectValue(data);
435
557
  if (record) {
436
558
  for (const key of LIST_KEYS) {
437
559
  const value = record[key];
@@ -443,16 +565,16 @@ function extractLinks(data) {
443
565
  return [];
444
566
  }
445
567
  function getLinkId(link) {
446
- return firstString(link, ["id", "_id", "urlId"]) || (link.id !== void 0 ? String(link.id) : void 0);
568
+ return pickString(link, ["id", "_id", "urlId"]) || (link.id !== void 0 ? String(link.id) : void 0);
447
569
  }
448
570
  function getLinkAlias(link) {
449
- return firstString(link, ["alias", "shortCode", "slug", "code"]);
571
+ return pickString(link, ["alias", "shortCode", "slug", "code"]);
450
572
  }
451
573
  function getLinkShortUrl(link) {
452
- return firstString(link, ["shortUrl", "shortLink", "shortURL", "url"]);
574
+ return pickString(link, ["shortUrl", "shortLink", "shortURL", "url"]);
453
575
  }
454
576
  function getLinkDestination(link) {
455
- return firstString(link, [
577
+ return pickString(link, [
456
578
  "destinationUrl",
457
579
  "originalUrl",
458
580
  "targetUrl",
@@ -468,14 +590,14 @@ function formatLinkDetails(link) {
468
590
  ["Alias", getLinkAlias(link)],
469
591
  ["Short URL", getLinkShortUrl(link)],
470
592
  ["Destination", getLinkDestination(link)],
471
- ["Title", readString(link.title)],
472
- ["Status", readString(link.status)],
593
+ ["Title", stringValue(link.title)],
594
+ ["Status", stringValue(link.status)],
473
595
  [
474
596
  "Clicks",
475
- readNumber(link.clicks) === void 0 ? void 0 : String(link.clicks)
597
+ numberValue(link.clicks) === void 0 ? void 0 : String(link.clicks)
476
598
  ],
477
- ["Created", readString(link.createdAt)],
478
- ["Updated", readString(link.updatedAt)]
599
+ ["Created", stringValue(link.createdAt)],
600
+ ["Updated", stringValue(link.updatedAt)]
479
601
  ].filter((entry) => Boolean(entry[1]));
480
602
  return lines.map(([label, value]) => `${label}: ${value}`).join("\n");
481
603
  }
@@ -483,23 +605,15 @@ function formatLinksTable(links) {
483
605
  if (links.length === 0) {
484
606
  return "No links found.";
485
607
  }
486
- const headers = ["ID", "ALIAS", "SHORT URL", "DESTINATION", "STATUS"];
608
+ const headers = ["ID", "Alias", "Short URL", "Destination", "Status"];
487
609
  const rows = links.map((link) => [
488
610
  truncate(getLinkId(link) || "-", 18),
489
- truncate(getLinkAlias(link) || "-", 18),
611
+ truncate(getLinkAlias(link) || "-", 12),
490
612
  truncate(getLinkShortUrl(link) || "-", 36),
491
613
  truncate(getLinkDestination(link) || "-", 52),
492
- truncate(readString(link.status) || "-", 12)
614
+ truncate(stringValue(link.status) || "-", 12)
493
615
  ]);
494
- const widths = headers.map(
495
- (header, index) => Math.max(header.length, ...rows.map((row) => row[index].length))
496
- );
497
- const renderRow = (row) => row.map((cell, index) => cell.padEnd(widths[index])).join(" ");
498
- return [
499
- renderRow(headers),
500
- renderRow(widths.map((width) => "-".repeat(width))),
501
- ...rows.map(renderRow)
502
- ].join("\n");
616
+ return formatTable(headers, rows);
503
617
  }
504
618
  function formatListSummary(data, count) {
505
619
  const meta = extractListMeta(data);
@@ -515,60 +629,6 @@ function formatListSummary(data, count) {
515
629
  return `${count} link${count === 1 ? "" : "s"} returned.`;
516
630
  }
517
631
 
518
- // src/lib/output.ts
519
- function writeStdout(message = "") {
520
- process.stdout.write(`${message}
521
- `);
522
- }
523
- function writeStderr(message = "") {
524
- process.stderr.write(`${message}
525
- `);
526
- }
527
- function writeNoticeBox(title, lines, target = "stderr") {
528
- const contentLines = lines.length > 0 ? lines : [""];
529
- const width = Math.max(
530
- title.length,
531
- ...contentLines.map((line) => line.length)
532
- );
533
- const stream = target === "stdout" ? process.stdout : process.stderr;
534
- const useTuiBox = stream.isTTY;
535
- const border = useTuiBox ? {
536
- topLeft: "\u250C",
537
- topRight: "\u2510",
538
- bottomLeft: "\u2514",
539
- bottomRight: "\u2518",
540
- horizontal: "\u2500",
541
- vertical: "\u2502",
542
- separatorLeft: "\u251C",
543
- separatorRight: "\u2524"
544
- } : {
545
- topLeft: "+",
546
- topRight: "+",
547
- bottomLeft: "+",
548
- bottomRight: "+",
549
- horizontal: "-",
550
- vertical: "|",
551
- separatorLeft: "+",
552
- separatorRight: "+"
553
- };
554
- const topBorder = `${border.topLeft}${border.horizontal.repeat(width + 2)}${border.topRight}`;
555
- const separator = `${border.separatorLeft}${border.horizontal.repeat(width + 2)}${border.separatorRight}`;
556
- const bottomBorder = `${border.bottomLeft}${border.horizontal.repeat(width + 2)}${border.bottomRight}`;
557
- const writeLine = target === "stdout" ? writeStdout : writeStderr;
558
- writeLine(topBorder);
559
- writeLine(`${border.vertical} ${title.padEnd(width)} ${border.vertical}`);
560
- writeLine(separator);
561
- for (const line of contentLines) {
562
- writeLine(
563
- `${border.vertical} ${line.padEnd(width)} ${border.vertical}`
564
- );
565
- }
566
- writeLine(bottomBorder);
567
- }
568
- function writeJson(value) {
569
- writeStdout(JSON.stringify(value, null, 2));
570
- }
571
-
572
632
  // src/commands/create.ts
573
633
  function normalizeExpiresAt(value) {
574
634
  if (!value) {
@@ -674,22 +734,22 @@ async function listCommand(options) {
674
734
  }
675
735
 
676
736
  // src/lib/users.ts
677
- function readString2(value) {
737
+ function stringValue2(value) {
678
738
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
679
739
  }
680
740
  function getUserLabel(user) {
681
- const fullName = [readString2(user.firstName), readString2(user.lastName)].filter(Boolean).join(" ");
682
- return fullName || readString2(user.username) || readString2(user.email) || String(user.id ?? "unknown");
741
+ const fullName = [stringValue2(user.firstName), stringValue2(user.lastName)].filter(Boolean).join(" ");
742
+ return fullName || stringValue2(user.username) || stringValue2(user.email) || String(user.id ?? "unknown");
683
743
  }
684
744
  function getQuietUserValue(user) {
685
- return readString2(user.username) || readString2(user.email) || String(user.id ?? "");
745
+ return stringValue2(user.username) || stringValue2(user.email) || String(user.id ?? "");
686
746
  }
687
747
  function formatUserDetails(user) {
688
748
  const lines = [
689
749
  ["Name", getUserLabel(user)],
690
- ["Username", readString2(user.username)],
691
- ["Email", readString2(user.email)],
692
- ["Role", readString2(user.role)],
750
+ ["Username", stringValue2(user.username)],
751
+ ["Email", stringValue2(user.email)],
752
+ ["Role", stringValue2(user.role)],
693
753
  ["ID", user.id === void 0 ? void 0 : String(user.id)]
694
754
  ].filter((entry) => Boolean(entry[1]));
695
755
  return lines.map(([label, value]) => `${label}: ${value}`).join("\n");
@@ -701,7 +761,7 @@ async function loginCommand(options) {
701
761
  const client = new PeakUrlApiClient(credentials);
702
762
  const response = await client.whoami();
703
763
  await new ConfigStore().save(credentials);
704
- const payload = {
764
+ const responseBody = {
705
765
  success: true,
706
766
  message: `Saved credentials for ${credentials.baseUrl}.`,
707
767
  data: {
@@ -711,7 +771,7 @@ async function loginCommand(options) {
711
771
  timestamp: response.timestamp
712
772
  };
713
773
  if (options.json) {
714
- writeJson(payload);
774
+ writeJson(responseBody);
715
775
  return;
716
776
  }
717
777
  if (options.quiet) {
@@ -727,15 +787,27 @@ var PACKAGE_NAME = "peakurl";
727
787
  var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org";
728
788
  var CACHE_TTL_MS = 1e3 * 60 * 60 * 12;
729
789
  var NOTICE_TTL_MS = 1e3 * 60 * 60 * 24;
730
- function readTimestamp(value) {
790
+ function parseTime(value) {
731
791
  if (!value) {
732
792
  return null;
733
793
  }
734
794
  const parsed = Date.parse(value);
735
795
  return Number.isNaN(parsed) ? null : parsed;
736
796
  }
737
- function getRegistryUrl(env) {
738
- return env.PEAKURL_NPM_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL;
797
+ function getRegistryBaseUrl(env) {
798
+ const candidate = env.PEAKURL_NPM_REGISTRY_URL?.trim();
799
+ if (!candidate) {
800
+ return DEFAULT_REGISTRY_URL;
801
+ }
802
+ try {
803
+ const parsed = new URL(candidate);
804
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password) {
805
+ return DEFAULT_REGISTRY_URL;
806
+ }
807
+ return candidate;
808
+ } catch {
809
+ return DEFAULT_REGISTRY_URL;
810
+ }
739
811
  }
740
812
  function normalizeRegistryUrl(registryUrl) {
741
813
  return registryUrl.replace(/\/+$/, "");
@@ -811,11 +883,11 @@ function compareSemver(left, right) {
811
883
  }
812
884
  return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
813
885
  }
814
- function getInstallCommand() {
886
+ function getUpdateInstallCommand() {
815
887
  return `npm install -g ${PACKAGE_NAME}@latest`;
816
888
  }
817
- async function fetchLatestVersion(env) {
818
- const registryUrl = normalizeRegistryUrl(getRegistryUrl(env));
889
+ async function fetchLatestPackageVersion(env) {
890
+ const registryUrl = normalizeRegistryUrl(getRegistryBaseUrl(env));
819
891
  const url = `${registryUrl}/${PACKAGE_NAME}/latest`;
820
892
  const controller = new AbortController();
821
893
  const timeout = setTimeout(() => controller.abort(), 2e3);
@@ -837,7 +909,7 @@ async function fetchLatestVersion(env) {
837
909
  clearTimeout(timeout);
838
910
  }
839
911
  }
840
- async function loadLatestVersionFromState(store) {
912
+ async function getCachedUpdateState(store) {
841
913
  const state = await store.load();
842
914
  return state.update ?? {};
843
915
  }
@@ -848,15 +920,15 @@ async function saveUpdateState(store, update) {
848
920
  update
849
921
  });
850
922
  }
851
- async function resolveLatestVersion(env, options) {
923
+ async function getLatestPackageVersion(env, options) {
852
924
  const store = options?.store ?? new StateStore();
853
- const updateState = await loadLatestVersionFromState(store);
854
- const lastCheckedAt = readTimestamp(updateState.lastCheckedAt);
925
+ const updateState = await getCachedUpdateState(store);
926
+ const lastCheckedAt = parseTime(updateState.lastCheckedAt);
855
927
  const now = Date.now();
856
928
  if (!options?.forceRefresh && updateState.latestVersion && lastCheckedAt !== null && now - lastCheckedAt < CACHE_TTL_MS) {
857
929
  return updateState.latestVersion;
858
930
  }
859
- const latestVersion = await fetchLatestVersion(env);
931
+ const latestVersion = await fetchLatestPackageVersion(env);
860
932
  if (!latestVersion) {
861
933
  return updateState.latestVersion ?? null;
862
934
  }
@@ -868,7 +940,7 @@ async function resolveLatestVersion(env, options) {
868
940
  return latestVersion;
869
941
  }
870
942
  async function getUpdateStatus(currentVersion, env, options) {
871
- const resolvedLatestVersion = await resolveLatestVersion(env, {
943
+ const resolvedLatestVersion = await getLatestPackageVersion(env, {
872
944
  forceRefresh: options?.forceRefresh,
873
945
  store: options?.store
874
946
  });
@@ -882,7 +954,7 @@ async function getUpdateStatus(currentVersion, env, options) {
882
954
  currentVersion,
883
955
  latestVersion,
884
956
  isOutdated: compareSemver(currentVersion, latestVersion) < 0,
885
- installCommand: getInstallCommand()
957
+ installCommand: getUpdateInstallCommand()
886
958
  };
887
959
  }
888
960
  function writeUpdateNotice(status) {
@@ -899,14 +971,14 @@ async function maybeShowUpdateNotice(options) {
899
971
  return;
900
972
  }
901
973
  const store = new StateStore();
902
- const updateState = await loadLatestVersionFromState(store);
974
+ const updateState = await getCachedUpdateState(store);
903
975
  const status = await getUpdateStatus(options.currentVersion, options.env, {
904
976
  store
905
977
  });
906
978
  if (!status.isOutdated) {
907
979
  return;
908
980
  }
909
- const lastNotifiedAt = readTimestamp(updateState.lastNotifiedAt);
981
+ const lastNotifiedAt = parseTime(updateState.lastNotifiedAt);
910
982
  const alreadyNotifiedForVersion = updateState.lastNotifiedVersion === status.latestVersion;
911
983
  if (alreadyNotifiedForVersion && lastNotifiedAt !== null && Date.now() - lastNotifiedAt < NOTICE_TTL_MS) {
912
984
  return;
@@ -924,7 +996,7 @@ async function updateCommand(options, currentVersion) {
924
996
  const status = await getUpdateStatus(currentVersion, process.env, {
925
997
  forceRefresh: true
926
998
  });
927
- const payload = {
999
+ const responseBody = {
928
1000
  success: true,
929
1001
  message: status.isOutdated ? `A newer PeakURL CLI version is available (${status.latestVersion}).` : `PeakURL CLI ${status.currentVersion} is up to date.`,
930
1002
  data: {
@@ -937,7 +1009,7 @@ async function updateCommand(options, currentVersion) {
937
1009
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
938
1010
  };
939
1011
  if (options.json) {
940
- writeJson(payload);
1012
+ writeJson(responseBody);
941
1013
  return;
942
1014
  }
943
1015
  if (!status.isOutdated) {
@@ -988,7 +1060,7 @@ function parsePositiveInteger(label) {
988
1060
  return parsed;
989
1061
  };
990
1062
  }
991
- async function readVersion() {
1063
+ async function getCliVersion() {
992
1064
  const packageJson = new URL("../package.json", import.meta.url);
993
1065
  const content = await readFile2(packageJson, "utf8");
994
1066
  const parsed = JSON.parse(content);
@@ -996,7 +1068,7 @@ async function readVersion() {
996
1068
  }
997
1069
  async function main() {
998
1070
  const program = new Command();
999
- const version = await readVersion();
1071
+ const version = await getCliVersion();
1000
1072
  program.name("peakurl").description("PeakURL command-line interface").version(version).showHelpAfterError().showSuggestionAfterError().addHelpText(
1001
1073
  "after",
1002
1074
  `
@@ -1044,7 +1116,7 @@ Examples:
1044
1116
  if (error instanceof CommanderError) {
1045
1117
  process.exit(error.exitCode);
1046
1118
  }
1047
- const cliError = toCliError(error);
1119
+ const cliError = wrapCliError(error);
1048
1120
  writeStderr(cliError.message);
1049
1121
  process.exit(cliError.exitCode);
1050
1122
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peakurl",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Official CLI for creating, listing, and managing PeakURL short links from the terminal",
5
5
  "homepage": "https://peakurl.org",
6
6
  "bugs": {