comze 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +179 -62
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -7395,7 +7395,7 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
7395
7395
  import { resolve } from "path";
7396
7396
 
7397
7397
  // src/fetcher.ts
7398
- var import_semver2 = __toESM(require_semver2(), 1);
7398
+ var import_semver3 = __toESM(require_semver2(), 1);
7399
7399
 
7400
7400
  // src/types.ts
7401
7401
  var STABILITY_ORDER = {
@@ -7555,7 +7555,6 @@ function formatNewVersion(originalConstraint, newVersion) {
7555
7555
  import fs from "node:fs/promises";
7556
7556
  import path from "node:path";
7557
7557
  import os from "node:os";
7558
- var CACHE_TTL = 30 * 60 * 1000;
7559
7558
  function getCacheDir() {
7560
7559
  const home = os.homedir();
7561
7560
  const platform = os.platform();
@@ -7571,84 +7570,160 @@ function getCacheDir() {
7571
7570
  }
7572
7571
  return path.join(process.env.XDG_CACHE_HOME || path.join(home, ".cache"), "comze");
7573
7572
  }
7574
- async function getCache(key) {
7573
+ async function getCacheEntry(key, expectedVersion) {
7575
7574
  try {
7576
7575
  const cacheDir = getCacheDir();
7577
7576
  const filePath = path.join(cacheDir, `${key}.json`);
7578
7577
  const content = await fs.readFile(filePath, "utf-8");
7579
7578
  const data = JSON.parse(content);
7580
- if (Date.now() - data.timestamp > CACHE_TTL) {
7581
- fs.unlink(filePath).catch(() => {});
7579
+ if (data.version !== expectedVersion) {
7582
7580
  return null;
7583
7581
  }
7584
- return data.value;
7582
+ return data;
7585
7583
  } catch {
7586
7584
  return null;
7587
7585
  }
7588
7586
  }
7589
- async function setCache(key, value) {
7587
+ async function setCache(key, value, version, meta = {}) {
7590
7588
  try {
7591
7589
  const cacheDir = getCacheDir();
7592
7590
  await fs.mkdir(cacheDir, { recursive: true });
7593
7591
  const filePath = path.join(cacheDir, `${key}.json`);
7594
7592
  const data = {
7593
+ version,
7595
7594
  timestamp: Date.now(),
7596
- value
7595
+ value,
7596
+ lastModified: meta.lastModified,
7597
+ etag: meta.etag
7597
7598
  };
7598
7599
  await fs.writeFile(filePath, JSON.stringify(data), "utf-8");
7599
7600
  } catch {}
7600
7601
  }
7602
+ async function touchCache(key, version) {
7603
+ try {
7604
+ const entry = await getCacheEntry(key, version);
7605
+ if (entry) {
7606
+ await setCache(key, entry.value, version, {
7607
+ lastModified: entry.lastModified,
7608
+ etag: entry.etag
7609
+ });
7610
+ }
7611
+ } catch {}
7612
+ }
7601
7613
 
7602
- // src/fetcher.ts
7603
- var PACKAGIST_API = "https://repo.packagist.org/p2";
7604
- var PACKAGIST_PACKAGE_API = "https://packagist.org/packages";
7605
- async function fetchDeprecatedInfo(packageName, noCache = false) {
7606
- const cacheKey = `${packageName.replace("/", "_")}_deprecated`;
7607
- if (!noCache) {
7608
- const cached = await getCache(cacheKey);
7609
- if (cached)
7610
- return cached;
7614
+ // src/utils/php.ts
7615
+ var import_semver2 = __toESM(require_semver2(), 1);
7616
+ function normalizeComposerConstraint(constraint) {
7617
+ if (!constraint)
7618
+ return "*";
7619
+ let normalized = constraint.trim();
7620
+ normalized = normalized.replace(/\|\|/g, "<<<OR>>>");
7621
+ normalized = normalized.replace(/\|/g, "<<<OR>>>");
7622
+ normalized = normalized.replace(/<<<OR>>>/g, " || ");
7623
+ normalized = normalized.replace(/\s*\|\|\s*/g, " || ");
7624
+ normalized = normalized.replace(/,\s*/g, " ");
7625
+ normalized = normalized.replace(/@(dev|alpha|beta|rc|stable)/gi, "");
7626
+ if (normalized === "*" || normalized.startsWith("ext-")) {
7627
+ return "*";
7628
+ }
7629
+ return normalized;
7630
+ }
7631
+ function extractMinVersion(constraint) {
7632
+ const normalized = normalizeComposerConstraint(constraint);
7633
+ if (normalized === "*")
7634
+ return null;
7635
+ const orParts = normalized.split(/\s*\|\|\s*/);
7636
+ let minVersion = null;
7637
+ for (const part of orParts) {
7638
+ const coerced = import_semver2.default.coerce(part.trim());
7639
+ if (coerced) {
7640
+ if (!minVersion || import_semver2.default.lt(coerced.version, minVersion)) {
7641
+ minVersion = coerced.version;
7642
+ }
7643
+ }
7611
7644
  }
7612
- try {
7613
- const url = `${PACKAGIST_PACKAGE_API}/${packageName}.json`;
7614
- const response = await fetch(url);
7615
- if (!response.ok)
7616
- return {};
7617
- const data = await response.json();
7618
- const abandoned = data.package?.abandoned;
7619
- if (!abandoned) {
7620
- const result2 = {};
7621
- if (!noCache)
7622
- await setCache(cacheKey, result2);
7623
- return result2;
7624
- }
7625
- if (typeof abandoned === "string") {
7626
- const result2 = { deprecated: true, replacement: abandoned };
7627
- if (!noCache)
7628
- await setCache(cacheKey, result2);
7629
- return result2;
7630
- }
7631
- const result = { deprecated: true };
7632
- if (!noCache)
7633
- await setCache(cacheKey, result);
7634
- return result;
7635
- } catch {
7636
- return {};
7645
+ return minVersion;
7646
+ }
7647
+ function extractMaxVersion(constraint) {
7648
+ const normalized = normalizeComposerConstraint(constraint);
7649
+ if (normalized === "*")
7650
+ return null;
7651
+ const upperBoundMatch = normalized.match(/<(=?)\s*(\d+(?:\.\d+)*)/);
7652
+ if (upperBoundMatch) {
7653
+ const coerced = import_semver2.default.coerce(upperBoundMatch[2]);
7654
+ return coerced ? coerced.version : null;
7655
+ }
7656
+ const caretMatch = normalized.match(/\^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
7657
+ if (caretMatch) {
7658
+ const major = parseInt(caretMatch[1], 10);
7659
+ return `${major + 1}.0.0`;
7660
+ }
7661
+ return null;
7662
+ }
7663
+ function checkPhpCompatibility(projectPhp, packagePhp) {
7664
+ if (!packagePhp || packagePhp === "*")
7665
+ return { satisfied: true };
7666
+ if (!projectPhp)
7667
+ return { satisfied: true };
7668
+ const projectMin = extractMinVersion(projectPhp);
7669
+ if (!projectMin)
7670
+ return { satisfied: true };
7671
+ const packageNorm = normalizeComposerConstraint(packagePhp);
7672
+ const packageParts = packageNorm.split(/\s*\|\|\s*/).filter((p) => p.trim());
7673
+ for (const part of packageParts) {
7674
+ const partMax = extractMaxVersion(part);
7675
+ if (partMax && import_semver2.default.gte(projectMin, partMax))
7676
+ continue;
7677
+ try {
7678
+ const range = import_semver2.default.validRange(part.trim());
7679
+ if (range && import_semver2.default.satisfies(projectMin, range))
7680
+ return { satisfied: true };
7681
+ } catch {}
7682
+ const partMin = extractMinVersion(part);
7683
+ if (partMin) {
7684
+ const partMajor = import_semver2.default.major(partMin);
7685
+ const projectMajor = import_semver2.default.major(projectMin);
7686
+ if (part.trim().startsWith("^")) {
7687
+ if (projectMajor === partMajor && import_semver2.default.gte(projectMin, partMin)) {
7688
+ return { satisfied: true };
7689
+ }
7690
+ } else if (import_semver2.default.gte(projectMin, partMin) && !partMax) {
7691
+ return { satisfied: true };
7692
+ }
7693
+ }
7637
7694
  }
7695
+ return { satisfied: false, reason: `requires php ${packagePhp}` };
7638
7696
  }
7639
- async function fetchPackage(packageName, minStability = "stable", preferStable = true, currentVersion, allowMajor = true, noCache = false) {
7697
+
7698
+ // src/fetcher.ts
7699
+ var PACKAGIST_API = "https://repo.packagist.org/p2";
7700
+ var CACHE_VERSION = 1;
7701
+ async function fetchPackage(packageName, minStability = "stable", preferStable = true, currentVersion, allowMajor = true, noCache = false, projectPhp) {
7640
7702
  const cacheKey = packageName.replace("/", "_");
7703
+ let cachedEntry = null;
7704
+ const headers = {};
7641
7705
  if (!noCache) {
7642
- const cached = await getCache(cacheKey);
7643
- if (cached)
7644
- return cached;
7706
+ cachedEntry = await getCacheEntry(cacheKey, CACHE_VERSION);
7707
+ if (cachedEntry?.lastModified) {
7708
+ headers["If-Modified-Since"] = cachedEntry.lastModified;
7709
+ }
7645
7710
  }
7711
+ let data;
7646
7712
  try {
7647
7713
  const url = `${PACKAGIST_API}/${packageName}.json`;
7648
- const response = await fetch(url);
7649
- if (!response.ok)
7714
+ const response = await fetch(url, { headers });
7715
+ if (response.status === 304 && cachedEntry) {
7716
+ await touchCache(cacheKey, CACHE_VERSION);
7717
+ data = cachedEntry.value;
7718
+ } else if (response.ok) {
7719
+ data = await response.json();
7720
+ if (!noCache) {
7721
+ const lastModified = response.headers.get("Last-Modified") || undefined;
7722
+ await setCache(cacheKey, data, CACHE_VERSION, { lastModified });
7723
+ }
7724
+ } else {
7650
7725
  return null;
7651
- const data = await response.json();
7726
+ }
7652
7727
  const versions = data.packages?.[packageName] ?? [];
7653
7728
  if (versions.length === 0)
7654
7729
  return null;
@@ -7676,21 +7751,39 @@ async function fetchPackage(packageName, minStability = "stable", preferStable =
7676
7751
  }
7677
7752
  if (!selectedVersion)
7678
7753
  return null;
7754
+ let phpIncompatible = false;
7755
+ let skippedVersion;
7756
+ if (projectPhp && selectedVersion.require?.php) {
7757
+ const phpCheck = checkPhpCompatibility(projectPhp, selectedVersion.require.php);
7758
+ if (!phpCheck.satisfied) {
7759
+ phpIncompatible = true;
7760
+ skippedVersion = selectedVersion.version;
7761
+ const versionsToCheck = preferStable ? eligibleVersions.filter((v) => getVersionStability(v.version) === "stable") : eligibleVersions;
7762
+ const compatibleVersion = versionsToCheck.find((v) => {
7763
+ if (!v.require?.php)
7764
+ return true;
7765
+ return checkPhpCompatibility(projectPhp, v.require.php).satisfied;
7766
+ });
7767
+ if (compatibleVersion && compatibleVersion !== selectedVersion) {
7768
+ selectedVersion = compatibleVersion;
7769
+ }
7770
+ }
7771
+ }
7679
7772
  const phpRequirement = selectedVersion.require?.php;
7680
7773
  let majorDetected;
7681
7774
  if (currentVersion) {
7682
7775
  const currentNorm = normalizeVersion(currentVersion);
7683
7776
  const selectedNorm = normalizeVersion(selectedVersion.version);
7684
7777
  if (currentNorm && selectedNorm) {
7685
- const currentMajor = import_semver2.default.major(currentNorm);
7686
- const selectedMajor = import_semver2.default.major(selectedNorm);
7778
+ const currentMajor = import_semver3.default.major(currentNorm);
7779
+ const selectedMajor = import_semver3.default.major(selectedNorm);
7687
7780
  if (selectedMajor > currentMajor) {
7688
7781
  majorDetected = selectedVersion.version;
7689
7782
  if (!allowMajor) {
7690
7783
  const versionsToCheck = preferStable ? eligibleVersions.filter((v) => getVersionStability(v.version) === "stable") : eligibleVersions;
7691
7784
  const sameMajorVersion = versionsToCheck.find((v) => {
7692
7785
  const norm = normalizeVersion(v.version);
7693
- return norm && import_semver2.default.major(norm) === currentMajor;
7786
+ return norm && import_semver3.default.major(norm) === currentMajor;
7694
7787
  });
7695
7788
  if (sameMajorVersion) {
7696
7789
  selectedVersion = sameMajorVersion;
@@ -7699,31 +7792,37 @@ async function fetchPackage(packageName, minStability = "stable", preferStable =
7699
7792
  }
7700
7793
  }
7701
7794
  }
7702
- const deprecatedInfo = await fetchDeprecatedInfo(packageName, noCache);
7795
+ const deprecatedInfo = {};
7796
+ if (typeof selectedVersion.abandoned === "string") {
7797
+ deprecatedInfo.deprecated = true;
7798
+ deprecatedInfo.replacement = selectedVersion.abandoned;
7799
+ } else if (selectedVersion.abandoned) {
7800
+ deprecatedInfo.deprecated = true;
7801
+ }
7703
7802
  const result = {
7704
7803
  latestVersion: selectedVersion.version,
7705
7804
  releaseTime: selectedVersion.time,
7706
7805
  phpRequirement: selectedVersion.require?.php ?? phpRequirement,
7707
7806
  majorVersion: majorDetected,
7708
7807
  deprecated: deprecatedInfo.deprecated,
7709
- replacement: deprecatedInfo.replacement
7808
+ replacement: deprecatedInfo.replacement,
7809
+ phpIncompatible: phpIncompatible || undefined,
7810
+ skippedVersion,
7811
+ require: selectedVersion.require
7710
7812
  };
7711
- if (!noCache) {
7712
- await setCache(cacheKey, result);
7713
- }
7714
7813
  return result;
7715
7814
  } catch {
7716
7815
  return null;
7717
7816
  }
7718
7817
  }
7719
- async function fetchAllPackages(packages, minStability = "stable", preferStable = true, allowMajor = true, noCache = false) {
7818
+ async function fetchAllPackages(packages, minStability = "stable", preferStable = true, allowMajor = true, noCache = false, projectPhp) {
7720
7819
  const results = new Map;
7721
7820
  const entries = Object.entries(packages);
7722
7821
  const CONCURRENCY = 5;
7723
7822
  for (let i = 0;i < entries.length; i += CONCURRENCY) {
7724
7823
  const batch = entries.slice(i, i + CONCURRENCY);
7725
7824
  const promises = batch.map(async ([name, version]) => {
7726
- const result = await fetchPackage(name, minStability, preferStable, version, allowMajor, noCache);
7825
+ const result = await fetchPackage(name, minStability, preferStable, version, allowMajor, noCache, projectPhp);
7727
7826
  if (result)
7728
7827
  results.set(name, result);
7729
7828
  });
@@ -7980,6 +8079,9 @@ function renderTable(packages) {
7980
8079
  if (pkg.phpRequirement) {
7981
8080
  extra += import_picocolors.default.gray(` php ${pkg.phpRequirement}`);
7982
8081
  }
8082
+ if (pkg.phpIncompatible && pkg.skippedVersion) {
8083
+ extra += import_picocolors.default.yellow(` ${pkg.skippedVersion} skipped (php)`);
8084
+ }
7983
8085
  if (pkg.deprecated) {
7984
8086
  extra += import_picocolors.default.red(" deprecated");
7985
8087
  if (pkg.replacement) {
@@ -7988,6 +8090,15 @@ function renderTable(packages) {
7988
8090
  }
7989
8091
  console.log(` ${name} ${oldVer} ${arrow} ${coloredNewVer} ${diffLabel} ${age}${extra}`);
7990
8092
  }
8093
+ const skippedPackages = packages.filter((p) => p.phpIncompatible && p.skippedVersion);
8094
+ if (skippedPackages.length > 0) {
8095
+ console.log(import_picocolors.default.yellow(`
8096
+ Some versions skipped due to PHP constraints:`));
8097
+ for (const pkg of skippedPackages) {
8098
+ console.log(` ${import_picocolors.default.bold(pkg.name)} ${pkg.skippedVersion} requires higher PHP version`);
8099
+ }
8100
+ console.log(import_picocolors.default.gray(" Update the php constraint in composer.json to use these versions."));
8101
+ }
7991
8102
  console.log("");
7992
8103
  }
7993
8104
  function renderHeader(version) {
@@ -8028,6 +8139,9 @@ function formatPackageChoice(pkg) {
8028
8139
  if (pkg.phpRequirement) {
8029
8140
  extra += import_picocolors.default.gray(` php ${pkg.phpRequirement}`);
8030
8141
  }
8142
+ if (pkg.phpIncompatible && pkg.skippedVersion) {
8143
+ extra += import_picocolors.default.yellow(` ${pkg.skippedVersion} skipped`);
8144
+ }
8031
8145
  if (pkg.deprecated) {
8032
8146
  extra += import_picocolors.default.red(" deprecated");
8033
8147
  if (pkg.replacement) {
@@ -8073,7 +8187,7 @@ async function selectPackages(packages) {
8073
8187
  // package.json
8074
8188
  var package_default = {
8075
8189
  name: "comze",
8076
- version: "0.3.0",
8190
+ version: "0.3.2",
8077
8191
  description: "A taze-like CLI for updating composer.json dependencies",
8078
8192
  author: "qoqn",
8079
8193
  license: "MIT",
@@ -8149,7 +8263,8 @@ async function run(options) {
8149
8263
  console.log(import_picocolors2.default.gray(` Checking ${Object.keys(filteredPackages).length} packages...`));
8150
8264
  console.log(import_picocolors2.default.gray(` Stability: ${minStability}${preferStable ? " (prefer-stable)" : ""}
8151
8265
  `));
8152
- const results = await fetchAllPackages(filteredPackages, minStability, preferStable, options.major, options.noCache);
8266
+ const projectPhp = allPackages["php"];
8267
+ const results = await fetchAllPackages(filteredPackages, minStability, preferStable, options.major, options.noCache, projectPhp);
8153
8268
  const updates = [];
8154
8269
  const deprecatedPackages = [];
8155
8270
  for (const [name, currentVersion] of Object.entries(filteredPackages)) {
@@ -8184,7 +8299,9 @@ async function run(options) {
8184
8299
  majorAvailable,
8185
8300
  phpRequirement: result.phpRequirement,
8186
8301
  deprecated: result.deprecated,
8187
- replacement: result.replacement
8302
+ replacement: result.replacement,
8303
+ phpIncompatible: result.phpIncompatible,
8304
+ skippedVersion: result.skippedVersion
8188
8305
  });
8189
8306
  }
8190
8307
  const order = { major: 0, minor: 1, patch: 2 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comze",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "A taze-like CLI for updating composer.json dependencies",
5
5
  "author": "qoqn",
6
6
  "license": "MIT",