@songsid/agend 2.0.8-beta.16 → 2.0.8-beta.17

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.
@@ -3679,14 +3679,21 @@ When users create specialized instances, suggest these configurations:
3679
3679
  const latest = execSync("npm view @songsid/agend version", { stdio: "pipe", timeout: 15_000 }).toString().trim();
3680
3680
  let target = latest;
3681
3681
  if (currentVersion.includes("-beta")) {
3682
+ // Beta users track the @beta channel (never fall back to @latest, which is
3683
+ // older), but should also hear when a newer STABLE ships — pick whichever
3684
+ // of beta/latest is the newest.
3685
+ let beta = "";
3682
3686
  try {
3683
- const beta = execSync("npm view @songsid/agend@beta version", { stdio: "pipe", timeout: 15_000 }).toString().trim();
3684
- if (beta && beta !== currentVersion)
3685
- target = beta;
3687
+ beta = execSync("npm view @songsid/agend@beta version", { stdio: "pipe", timeout: 15_000 }).toString().trim();
3686
3688
  }
3687
3689
  catch { /* no beta tag */ }
3690
+ target = beta || latest;
3691
+ if (latest && this.semverGt(latest, target))
3692
+ target = latest;
3688
3693
  }
3689
- if (target && target !== currentVersion) {
3694
+ // Only notify when target is genuinely newer (semver), so a beta user on
3695
+ // 2.0.8-beta.16 is never told that stable 2.0.7 is "available".
3696
+ if (target && this.semverGt(target, currentVersion)) {
3690
3697
  const generalId = this.findGeneralInstance();
3691
3698
  if (generalId) {
3692
3699
  this.notifyInstanceTopic(generalId, `🆕 AgEnD v${target} available (current: v${currentVersion}). Use \`/update\` to upgrade.`);
@@ -3695,6 +3702,49 @@ When users create specialized instances, suggest these configurations:
3695
3702
  }
3696
3703
  catch { /* silent — network issues */ }
3697
3704
  }
3705
+ /**
3706
+ * Semver "a > b". Compares major.minor.patch numerically; a version without a
3707
+ * prerelease outranks the same core with one (2.0.8 > 2.0.8-beta.16); two
3708
+ * prereleases compare identifier-by-identifier (numeric < alphanumeric, numeric
3709
+ * fields compared as numbers). Sufficient for our X.Y.Z[-beta.N] scheme.
3710
+ */
3711
+ semverGt(a, b) {
3712
+ const parse = (v) => {
3713
+ const [core, pre] = v.replace(/^v/, "").split("-");
3714
+ const nums = core.split(".").map(n => parseInt(n, 10) || 0);
3715
+ return { nums: [nums[0] ?? 0, nums[1] ?? 0, nums[2] ?? 0], pre: pre ? pre.split(".") : [] };
3716
+ };
3717
+ const pa = parse(a), pb = parse(b);
3718
+ for (let i = 0; i < 3; i++) {
3719
+ if (pa.nums[i] !== pb.nums[i])
3720
+ return pa.nums[i] > pb.nums[i];
3721
+ }
3722
+ if (pa.pre.length === 0 && pb.pre.length === 0)
3723
+ return false;
3724
+ if (pa.pre.length === 0)
3725
+ return true; // a stable, b prerelease → a > b
3726
+ if (pb.pre.length === 0)
3727
+ return false; // a prerelease, b stable → a < b
3728
+ const len = Math.max(pa.pre.length, pb.pre.length);
3729
+ for (let i = 0; i < len; i++) {
3730
+ const x = pa.pre[i], y = pb.pre[i];
3731
+ if (x === undefined)
3732
+ return false; // a has fewer identifiers → a < b
3733
+ if (y === undefined)
3734
+ return true; // a has more identifiers → a > b
3735
+ const xn = /^\d+$/.test(x), yn = /^\d+$/.test(y);
3736
+ if (xn && yn) {
3737
+ const dx = parseInt(x, 10), dy = parseInt(y, 10);
3738
+ if (dx !== dy)
3739
+ return dx > dy;
3740
+ }
3741
+ else if (xn !== yn)
3742
+ return yn; // numeric has lower precedence than alphanumeric
3743
+ else if (x !== y)
3744
+ return x > y; // both alphanumeric
3745
+ }
3746
+ return false; // identical
3747
+ }
3698
3748
  // ── Health HTTP endpoint ─────────────────────────────────────────────
3699
3749
  startHealthServer(port) {
3700
3750
  this.startedAt = Date.now();