@soulbatical/tetra-dev-toolkit 1.18.0 → 1.18.1

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.
@@ -38,50 +38,89 @@ function readJson(path) {
38
38
  }
39
39
 
40
40
  function satisfiesRange(installed, range) {
41
- // Simple semver range check without external deps
42
- // Handles: exact "2.93.3", caret "^2.93.3", tilde "~2.93.3", star "*", >= ">= 8.0.0"
41
+ // Check if a consumer's dependency range can satisfy a peer dep range.
42
+ // Both `installed` and `range` can be semver ranges (^2.48.0, ^2.93.3, etc.)
43
+ // We check if the ranges CAN overlap — i.e., there exists a version that satisfies both.
43
44
  if (!installed || !range) return false
44
- if (range === '*') return true
45
+ if (range === '*' || installed === '*') return true
45
46
 
46
- // Clean versions: remove leading ^ ~ >= <= > < =
47
47
  const cleanVersion = (v) => v.replace(/^[\^~>=<\s]+/, '').trim()
48
48
  const parseVersion = (v) => {
49
49
  const cleaned = cleanVersion(v)
50
50
  const parts = cleaned.split('.').map(Number)
51
51
  return { major: parts[0] || 0, minor: parts[1] || 0, patch: parts[2] || 0 }
52
52
  }
53
+ const versionGte = (a, b) => {
54
+ if (a.major !== b.major) return a.major > b.major
55
+ if (a.minor !== b.minor) return a.minor > b.minor
56
+ return a.patch >= b.patch
57
+ }
53
58
 
54
- const inst = parseVersion(installed)
55
- const req = parseVersion(range)
59
+ // For || ranges, check each part independently
60
+ const rangeParts = range.split('||').map(p => p.trim())
61
+ const installedParts = installed.split('||').map(p => p.trim())
56
62
 
57
- if (range.startsWith('>=')) {
58
- // >= check
59
- if (inst.major > req.major) return true
60
- if (inst.major === req.major && inst.minor > req.minor) return true
61
- if (inst.major === req.major && inst.minor === req.minor && inst.patch >= req.patch) return true
63
+ return rangeParts.some(rPart => {
64
+ return installedParts.some(iPart => {
65
+ return rangesOverlap(iPart, rPart, parseVersion, versionGte)
66
+ })
67
+ })
68
+ }
69
+
70
+ function rangesOverlap(installedRange, requiredRange, parseVersion, versionGte) {
71
+ const isCaret = (r) => r.startsWith('^')
72
+ const isTilde = (r) => r.startsWith('~')
73
+ const isGte = (r) => r.startsWith('>=')
74
+
75
+ const inst = parseVersion(installedRange)
76
+ const req = parseVersion(requiredRange)
77
+
78
+ // Both caret ranges with same major: they overlap if their ranges intersect
79
+ // ^2.48.0 allows 2.48.0 - 2.x.x, ^2.93.3 allows 2.93.3 - 2.x.x
80
+ // They overlap because ^2.48.0 includes 2.93.3
81
+ if ((isCaret(installedRange) || !installedRange.match(/^[\^~>=]/)) &&
82
+ (isCaret(requiredRange) || !requiredRange.match(/^[\^~>=]/))) {
83
+ // Same major = ranges can overlap
84
+ if (inst.major === req.major) {
85
+ // The higher minimum must be reachable from the lower range
86
+ // ^2.48.0 (allows up to <3.0.0) can reach 2.93.3 ✓
87
+ // ^3.0.0 cannot reach 2.93.3 ✗
88
+ return true // Same major with caret = always overlapping
89
+ }
90
+ // Different major with exact versions: only if equal
91
+ if (!isCaret(installedRange) && !isCaret(requiredRange)) {
92
+ return inst.major === req.major && inst.minor === req.minor && inst.patch === req.patch
93
+ }
62
94
  return false
63
95
  }
64
96
 
65
- if (range.startsWith('^') || range.includes('||')) {
66
- // Caret: same major, >= minor.patch
67
- // For ranges with || (e.g. "^18.0.0 || ^19.0.0"), check each part
68
- const parts = range.split('||').map(p => p.trim())
69
- return parts.some(part => {
70
- const r = parseVersion(part)
71
- if (inst.major !== r.major) return false
72
- if (inst.minor > r.minor) return true
73
- if (inst.minor === r.minor && inst.patch >= r.patch) return true
74
- return false
75
- })
97
+ // >= range checks
98
+ if (isGte(requiredRange)) {
99
+ // Required >= X.Y.Z: consumer's range must be able to produce a version >= X.Y.Z
100
+ // ^9.0.0 can produce 9.0.0+ which is >= 8.0.0 ✓
101
+ // ^5.3.3 can produce 5.3.3+ which is >= 5.0.0 ✓
102
+ // The max version of consumer's caret range is <(major+1).0.0
103
+ // So: consumer max >= required min
104
+ const consumerMax = isCaret(installedRange) ? { major: inst.major + 1, minor: 0, patch: 0 } : inst
105
+ return versionGte(consumerMax, req)
106
+ }
107
+ if (isGte(installedRange)) {
108
+ // Consumer has >= X, required has ^Y.Z.W — any version >= X could satisfy ^Y if X <= Y
109
+ return true // >= is open-ended, always overlaps with bounded ranges
76
110
  }
77
111
 
78
- if (range.startsWith('~')) {
79
- // Tilde: same major.minor, >= patch
80
- if (inst.major !== req.major || inst.minor !== req.minor) return false
81
- return inst.patch >= req.patch
112
+ // Tilde: ~X.Y.Z allows X.Y.Z - X.Y+1.0
113
+ if (isTilde(installedRange) || isTilde(requiredRange)) {
114
+ if (inst.major !== req.major) return false
115
+ // Tilde ranges on same major.minor overlap
116
+ if (isTilde(installedRange) && isTilde(requiredRange)) {
117
+ return inst.minor === req.minor
118
+ }
119
+ // Tilde + caret: tilde range must include or be included in caret range
120
+ return inst.minor === req.minor || (isCaret(requiredRange) && inst.minor >= req.minor)
82
121
  }
83
122
 
84
- // Exact version match
123
+ // Fallback: exact match
85
124
  return inst.major === req.major && inst.minor === req.minor && inst.patch === req.patch
86
125
  }
87
126
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-dev-toolkit",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },