@freecodecamp/universe-cli 0.7.1 → 0.7.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/dist/index.cjs CHANGED
@@ -24621,8 +24621,162 @@ async function update(options, deps = {}) {
24621
24621
  }
24622
24622
  }
24623
24623
 
24624
+ // src/lib/update-notifier.ts
24625
+ var import_node_fs3 = require("fs");
24626
+ var import_promises8 = require("fs/promises");
24627
+ var import_node_os2 = require("os");
24628
+ var import_node_path9 = require("path");
24629
+ var APP_DIR2 = "universe-cli";
24630
+ var CACHE_FILE = "update-check.json";
24631
+ var PKG_NAME = "@freecodecamp/universe-cli";
24632
+ var NPM_LATEST_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
24633
+ var TTL_MS = 24 * 60 * 60 * 1e3;
24634
+ var FETCH_TIMEOUT_MS = 3e3;
24635
+ function configBase2() {
24636
+ const xdg = process.env["XDG_CONFIG_HOME"];
24637
+ if (xdg && xdg.length > 0) return xdg;
24638
+ return (0, import_node_path9.join)((0, import_node_os2.homedir)(), ".config");
24639
+ }
24640
+ function cachePath() {
24641
+ return (0, import_node_path9.join)(configBase2(), APP_DIR2, CACHE_FILE);
24642
+ }
24643
+ function isDisabled() {
24644
+ const v = process.env["UNIVERSE_NO_UPDATE_CHECK"];
24645
+ return v === "1" || v === "true";
24646
+ }
24647
+ function parseCache(raw) {
24648
+ let parsed;
24649
+ try {
24650
+ parsed = JSON.parse(raw);
24651
+ } catch {
24652
+ return null;
24653
+ }
24654
+ if (typeof parsed !== "object" || parsed === null || !("latest" in parsed) || !("lastCheck" in parsed)) {
24655
+ return null;
24656
+ }
24657
+ const { latest, lastCheck } = parsed;
24658
+ if (typeof latest !== "string" || typeof lastCheck !== "number") {
24659
+ return null;
24660
+ }
24661
+ return { latest, lastCheck };
24662
+ }
24663
+ async function readCache() {
24664
+ try {
24665
+ const raw = await (0, import_promises8.readFile)(cachePath(), "utf-8");
24666
+ return parseCache(raw);
24667
+ } catch {
24668
+ return null;
24669
+ }
24670
+ }
24671
+ function readCacheSync() {
24672
+ try {
24673
+ const raw = (0, import_node_fs3.readFileSync)(cachePath(), "utf-8");
24674
+ return parseCache(raw);
24675
+ } catch {
24676
+ return null;
24677
+ }
24678
+ }
24679
+ async function writeCache(c2) {
24680
+ const path = cachePath();
24681
+ await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(path), { recursive: true, mode: 448 });
24682
+ await (0, import_promises8.writeFile)(path, JSON.stringify(c2), { mode: 420 });
24683
+ }
24684
+ async function fetchLatest() {
24685
+ const ctl = new AbortController();
24686
+ const timer = setTimeout(() => ctl.abort(), FETCH_TIMEOUT_MS);
24687
+ try {
24688
+ const res = await fetch(NPM_LATEST_URL, {
24689
+ signal: ctl.signal,
24690
+ headers: { accept: "application/json" }
24691
+ });
24692
+ if (!res.ok) return null;
24693
+ const body = await res.json();
24694
+ return typeof body.version === "string" ? body.version : null;
24695
+ } catch {
24696
+ return null;
24697
+ } finally {
24698
+ clearTimeout(timer);
24699
+ }
24700
+ }
24701
+ function compareVersions(a, b) {
24702
+ const pa = parseVersion(a);
24703
+ const pb = parseVersion(b);
24704
+ if (pa === null || pb === null) return 0;
24705
+ for (let i = 0; i < 3; i += 1) {
24706
+ const ai = pa[i] ?? 0;
24707
+ const bi = pb[i] ?? 0;
24708
+ if (ai < bi) return -1;
24709
+ if (ai > bi) return 1;
24710
+ }
24711
+ return 0;
24712
+ }
24713
+ function parseVersion(s) {
24714
+ const core = s.split("-")[0] ?? "";
24715
+ const parts = core.split(".");
24716
+ if (parts.length !== 3) return null;
24717
+ const nums = parts.map((p2) => Number.parseInt(p2, 10));
24718
+ if (nums.some((n) => Number.isNaN(n))) return null;
24719
+ return [nums[0], nums[1], nums[2]];
24720
+ }
24721
+ async function refreshIfStale(now = Date.now()) {
24722
+ if (isDisabled()) return;
24723
+ const cache = await readCache();
24724
+ if (cache !== null && now - cache.lastCheck < TTL_MS) return;
24725
+ const latest = await fetchLatest();
24726
+ if (latest === null) return;
24727
+ try {
24728
+ await writeCache({ latest, lastCheck: now });
24729
+ } catch {
24730
+ }
24731
+ }
24732
+ function getNoticeSync(current) {
24733
+ if (isDisabled()) return null;
24734
+ const cache = readCacheSync();
24735
+ if (cache === null) return null;
24736
+ if (compareVersions(current, cache.latest) >= 0) return null;
24737
+ return { current, latest: cache.latest };
24738
+ }
24739
+ function useColor() {
24740
+ if (process.env["NO_COLOR"] && process.env["NO_COLOR"].length > 0) {
24741
+ return false;
24742
+ }
24743
+ return process.stderr.isTTY === true;
24744
+ }
24745
+ function paint(s, code, color) {
24746
+ if (!color) return s;
24747
+ return `\x1B[${code}m${s}\x1B[0m`;
24748
+ }
24749
+ function formatNotice(n, color = useColor()) {
24750
+ const dim = (s) => paint(s, "2", color);
24751
+ const yellow = (s) => paint(s, "33", color);
24752
+ const cyan = (s) => paint(s, "36", color);
24753
+ const bar = dim("\u2502");
24754
+ const lines = [
24755
+ "",
24756
+ bar,
24757
+ `${yellow("\u25B2")} Update available: ${dim(n.current)} \u2192 ${cyan(n.latest)}`,
24758
+ `${bar} Run ${cyan(`npm i -g ${PKG_NAME}`)} to upgrade`,
24759
+ dim("\u2514"),
24760
+ ""
24761
+ ];
24762
+ return lines.join("\n");
24763
+ }
24764
+ function installExitNotice(current) {
24765
+ if (isDisabled()) return;
24766
+ let printed = false;
24767
+ const emit = () => {
24768
+ if (printed) return;
24769
+ printed = true;
24770
+ const n = getNoticeSync(current);
24771
+ if (n === null) return;
24772
+ process.stderr.write(formatNotice(n));
24773
+ };
24774
+ process.on("beforeExit", emit);
24775
+ process.on("exit", emit);
24776
+ }
24777
+
24624
24778
  // src/cli.ts
24625
- var version2 = true ? "0.7.1" : "0.0.0";
24779
+ var version2 = true ? "0.7.2" : "0.0.0";
24626
24780
  function handleActionError(command, json2, err) {
24627
24781
  const ctx = { json: json2, command };
24628
24782
  const message = err instanceof Error ? err.message : "unknown error";
@@ -24638,6 +24792,8 @@ function findFirstPositional(args) {
24638
24792
  return -1;
24639
24793
  }
24640
24794
  function run(argv = process.argv) {
24795
+ installExitNotice(version2);
24796
+ void refreshIfStale();
24641
24797
  const args = argv.slice(2);
24642
24798
  const firstPosIdx = findFirstPositional(args);
24643
24799
  const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
package/dist/index.js CHANGED
@@ -2130,8 +2130,162 @@ async function update(options, deps = {}) {
2130
2130
  }
2131
2131
  }
2132
2132
 
2133
+ // src/lib/update-notifier.ts
2134
+ import { readFileSync } from "fs";
2135
+ import { mkdir as mkdir2, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
2136
+ import { homedir as homedir2 } from "os";
2137
+ import { dirname as dirname2, join as join3 } from "path";
2138
+ var APP_DIR2 = "universe-cli";
2139
+ var CACHE_FILE = "update-check.json";
2140
+ var PKG_NAME = "@freecodecamp/universe-cli";
2141
+ var NPM_LATEST_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
2142
+ var TTL_MS = 24 * 60 * 60 * 1e3;
2143
+ var FETCH_TIMEOUT_MS = 3e3;
2144
+ function configBase2() {
2145
+ const xdg = process.env["XDG_CONFIG_HOME"];
2146
+ if (xdg && xdg.length > 0) return xdg;
2147
+ return join3(homedir2(), ".config");
2148
+ }
2149
+ function cachePath() {
2150
+ return join3(configBase2(), APP_DIR2, CACHE_FILE);
2151
+ }
2152
+ function isDisabled() {
2153
+ const v = process.env["UNIVERSE_NO_UPDATE_CHECK"];
2154
+ return v === "1" || v === "true";
2155
+ }
2156
+ function parseCache(raw) {
2157
+ let parsed;
2158
+ try {
2159
+ parsed = JSON.parse(raw);
2160
+ } catch {
2161
+ return null;
2162
+ }
2163
+ if (typeof parsed !== "object" || parsed === null || !("latest" in parsed) || !("lastCheck" in parsed)) {
2164
+ return null;
2165
+ }
2166
+ const { latest, lastCheck } = parsed;
2167
+ if (typeof latest !== "string" || typeof lastCheck !== "number") {
2168
+ return null;
2169
+ }
2170
+ return { latest, lastCheck };
2171
+ }
2172
+ async function readCache() {
2173
+ try {
2174
+ const raw = await readFile6(cachePath(), "utf-8");
2175
+ return parseCache(raw);
2176
+ } catch {
2177
+ return null;
2178
+ }
2179
+ }
2180
+ function readCacheSync() {
2181
+ try {
2182
+ const raw = readFileSync(cachePath(), "utf-8");
2183
+ return parseCache(raw);
2184
+ } catch {
2185
+ return null;
2186
+ }
2187
+ }
2188
+ async function writeCache(c) {
2189
+ const path = cachePath();
2190
+ await mkdir2(dirname2(path), { recursive: true, mode: 448 });
2191
+ await writeFile2(path, JSON.stringify(c), { mode: 420 });
2192
+ }
2193
+ async function fetchLatest() {
2194
+ const ctl = new AbortController();
2195
+ const timer = setTimeout(() => ctl.abort(), FETCH_TIMEOUT_MS);
2196
+ try {
2197
+ const res = await fetch(NPM_LATEST_URL, {
2198
+ signal: ctl.signal,
2199
+ headers: { accept: "application/json" }
2200
+ });
2201
+ if (!res.ok) return null;
2202
+ const body = await res.json();
2203
+ return typeof body.version === "string" ? body.version : null;
2204
+ } catch {
2205
+ return null;
2206
+ } finally {
2207
+ clearTimeout(timer);
2208
+ }
2209
+ }
2210
+ function compareVersions(a, b) {
2211
+ const pa = parseVersion(a);
2212
+ const pb = parseVersion(b);
2213
+ if (pa === null || pb === null) return 0;
2214
+ for (let i = 0; i < 3; i += 1) {
2215
+ const ai = pa[i] ?? 0;
2216
+ const bi = pb[i] ?? 0;
2217
+ if (ai < bi) return -1;
2218
+ if (ai > bi) return 1;
2219
+ }
2220
+ return 0;
2221
+ }
2222
+ function parseVersion(s) {
2223
+ const core = s.split("-")[0] ?? "";
2224
+ const parts = core.split(".");
2225
+ if (parts.length !== 3) return null;
2226
+ const nums = parts.map((p) => Number.parseInt(p, 10));
2227
+ if (nums.some((n) => Number.isNaN(n))) return null;
2228
+ return [nums[0], nums[1], nums[2]];
2229
+ }
2230
+ async function refreshIfStale(now = Date.now()) {
2231
+ if (isDisabled()) return;
2232
+ const cache = await readCache();
2233
+ if (cache !== null && now - cache.lastCheck < TTL_MS) return;
2234
+ const latest = await fetchLatest();
2235
+ if (latest === null) return;
2236
+ try {
2237
+ await writeCache({ latest, lastCheck: now });
2238
+ } catch {
2239
+ }
2240
+ }
2241
+ function getNoticeSync(current) {
2242
+ if (isDisabled()) return null;
2243
+ const cache = readCacheSync();
2244
+ if (cache === null) return null;
2245
+ if (compareVersions(current, cache.latest) >= 0) return null;
2246
+ return { current, latest: cache.latest };
2247
+ }
2248
+ function useColor() {
2249
+ if (process.env["NO_COLOR"] && process.env["NO_COLOR"].length > 0) {
2250
+ return false;
2251
+ }
2252
+ return process.stderr.isTTY === true;
2253
+ }
2254
+ function paint(s, code, color) {
2255
+ if (!color) return s;
2256
+ return `\x1B[${code}m${s}\x1B[0m`;
2257
+ }
2258
+ function formatNotice(n, color = useColor()) {
2259
+ const dim = (s) => paint(s, "2", color);
2260
+ const yellow = (s) => paint(s, "33", color);
2261
+ const cyan = (s) => paint(s, "36", color);
2262
+ const bar = dim("\u2502");
2263
+ const lines = [
2264
+ "",
2265
+ bar,
2266
+ `${yellow("\u25B2")} Update available: ${dim(n.current)} \u2192 ${cyan(n.latest)}`,
2267
+ `${bar} Run ${cyan(`npm i -g ${PKG_NAME}`)} to upgrade`,
2268
+ dim("\u2514"),
2269
+ ""
2270
+ ];
2271
+ return lines.join("\n");
2272
+ }
2273
+ function installExitNotice(current) {
2274
+ if (isDisabled()) return;
2275
+ let printed = false;
2276
+ const emit = () => {
2277
+ if (printed) return;
2278
+ printed = true;
2279
+ const n = getNoticeSync(current);
2280
+ if (n === null) return;
2281
+ process.stderr.write(formatNotice(n));
2282
+ };
2283
+ process.on("beforeExit", emit);
2284
+ process.on("exit", emit);
2285
+ }
2286
+
2133
2287
  // src/cli.ts
2134
- var version = true ? "0.7.1" : "0.0.0";
2288
+ var version = true ? "0.7.2" : "0.0.0";
2135
2289
  function handleActionError(command, json, err) {
2136
2290
  const ctx = { json, command };
2137
2291
  const message = err instanceof Error ? err.message : "unknown error";
@@ -2147,6 +2301,8 @@ function findFirstPositional(args) {
2147
2301
  return -1;
2148
2302
  }
2149
2303
  function run(argv = process.argv) {
2304
+ installExitNotice(version);
2305
+ void refreshIfStale();
2150
2306
  const args = argv.slice(2);
2151
2307
  const firstPosIdx = findFirstPositional(args);
2152
2308
  const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freecodecamp/universe-cli",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "test": "vitest run",