@vltpkg/semver 1.0.2 → 1.0.3

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/index.js +48 -3
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -15,6 +15,20 @@ export const parse = (version) => {
15
15
  return undefined;
16
16
  }
17
17
  };
18
+ /**
19
+ * Bounded cache for {@link parseRange}, keyed on `(includePrerelease, raw)`.
20
+ * `Range` is effectively immutable after construction, so parsed results
21
+ * (including `undefined` for invalid input, tracked via `has()`) are safe
22
+ * to share across callers. Cleared wholesale once it hits the cap rather
23
+ * than tracking LRU order, since real workloads only see a few hundred to
24
+ * a few thousand distinct range strings per install.
25
+ *
26
+ * Sharing instances also means textually identical ranges are identical
27
+ * objects, which is what makes the identity-keyed `intersectsCache` below
28
+ * effective.
29
+ */
30
+ const rangeCache = new Map();
31
+ const MAX_RANGE_CACHE = 4096;
18
32
  /** Return the parsed version range, or `undefined` if invalid */
19
33
  export const parseRange = (range, includePrerelease = false) => {
20
34
  if (typeof range === 'object') {
@@ -22,12 +36,23 @@ export const parseRange = (range, includePrerelease = false) => {
22
36
  return range;
23
37
  range = range.raw;
24
38
  }
39
+ const key = `${+includePrerelease}\0${range}`;
40
+ // single map lookup on the common hit path; the extra has() check only
41
+ // runs for ranges cached as invalid (undefined) and for misses
42
+ const cached = rangeCache.get(key);
43
+ if (cached !== undefined || rangeCache.has(key))
44
+ return cached;
45
+ let parsed;
25
46
  try {
26
- return new Range(range, includePrerelease);
47
+ parsed = new Range(range, includePrerelease);
27
48
  }
28
49
  catch {
29
- return undefined;
50
+ parsed = undefined;
30
51
  }
52
+ if (rangeCache.size >= MAX_RANGE_CACHE)
53
+ rangeCache.clear();
54
+ rangeCache.set(key, parsed);
55
+ return parsed;
31
56
  };
32
57
  /**
33
58
  * return true if the version is valid
@@ -348,6 +373,16 @@ export const stable = (versions) => versions.filter(v => {
348
373
  return false;
349
374
  return !p.prerelease?.length;
350
375
  });
376
+ /**
377
+ * Identity-keyed cache for {@link intersects}. Keying on the `Range`
378
+ * instances themselves (rather than a string of their raw sources) keeps
379
+ * the hit path free of string building, and needs no size cap or clear
380
+ * policy: entries are dropped by GC together with their ranges. It relies
381
+ * on `rangeCache` deduping instances, so string inputs and `spec.range`
382
+ * objects for the same source share entries; callers constructing a fresh
383
+ * `Range` per call simply recompute, as they did before memoization.
384
+ */
385
+ const intersectsCache = new WeakMap();
351
386
  /**
352
387
  * Return true if the range r1 intersects any of the ranges r2
353
388
  * r1 and r2 are either Range objects or range strings.
@@ -361,8 +396,18 @@ export const intersects = (r1, r2, includePrerelease) => {
361
396
  // If either range is 'any', they intersect
362
397
  if (range1.isAny || range2.isAny)
363
398
  return true;
399
+ let inner = intersectsCache.get(range1);
400
+ const cached = inner?.get(range2);
401
+ if (cached !== undefined)
402
+ return cached;
364
403
  // Check if any set from range1 intersects with any set from range2
365
- return range1.set.some(set1 => range2.set.some(set2 => intersectComparators(set1, set2)));
404
+ const result = range1.set.some(set1 => range2.set.some(set2 => intersectComparators(set1, set2)));
405
+ if (!inner) {
406
+ inner = new WeakMap();
407
+ intersectsCache.set(range1, inner);
408
+ }
409
+ inner.set(range2, result);
410
+ return result;
366
411
  };
367
412
  /**
368
413
  * Check if range r1 is a subset of range r2.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/semver",
3
3
  "description": "The semantic version parser used by vlt",
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",
@@ -13,9 +13,9 @@
13
13
  "url": "http://vlt.sh"
14
14
  },
15
15
  "dependencies": {
16
- "@vltpkg/error-cause": "1.0.2",
17
- "@vltpkg/fast-split": "1.0.2",
18
- "@vltpkg/types": "1.0.2"
16
+ "@vltpkg/error-cause": "1.0.3",
17
+ "@vltpkg/fast-split": "1.0.3",
18
+ "@vltpkg/types": "1.0.3"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@eslint/js": "^9.39.1",