stone-lang 0.1.0
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/README.md +52 -0
- package/StoneEngine.js +879 -0
- package/StoneEngineService.js +1727 -0
- package/adapters/FileSystemAdapter.js +230 -0
- package/adapters/OutputAdapter.js +208 -0
- package/adapters/index.js +6 -0
- package/cli/CLIOutputAdapter.js +196 -0
- package/cli/DaemonClient.js +349 -0
- package/cli/JSONOutputAdapter.js +135 -0
- package/cli/ReplSession.js +567 -0
- package/cli/ViewerServer.js +590 -0
- package/cli/commands/check.js +84 -0
- package/cli/commands/daemon.js +189 -0
- package/cli/commands/kill.js +66 -0
- package/cli/commands/package.js +713 -0
- package/cli/commands/ps.js +65 -0
- package/cli/commands/run.js +537 -0
- package/cli/entry.js +169 -0
- package/cli/index.js +14 -0
- package/cli/stonec.js +358 -0
- package/cli/test-compiler.js +181 -0
- package/cli/viewer/index.html +495 -0
- package/daemon/IPCServer.js +455 -0
- package/daemon/ProcessManager.js +327 -0
- package/daemon/ProcessRunner.js +307 -0
- package/daemon/daemon.js +398 -0
- package/daemon/index.js +16 -0
- package/frontend/analysis/index.js +5 -0
- package/frontend/analysis/livenessAnalyzer.js +568 -0
- package/frontend/analysis/treeShaker.js +265 -0
- package/frontend/index.js +20 -0
- package/frontend/parsing/astBuilder.js +2196 -0
- package/frontend/parsing/index.js +7 -0
- package/frontend/parsing/sonParser.js +592 -0
- package/frontend/parsing/stoneAstTypes.js +703 -0
- package/frontend/parsing/terminal-registry.js +435 -0
- package/frontend/parsing/tokenizer.js +692 -0
- package/frontend/type-checker/OverloadedFunctionType.js +43 -0
- package/frontend/type-checker/TypeEnvironment.js +165 -0
- package/frontend/type-checker/bidirectionalInference.js +149 -0
- package/frontend/type-checker/index.js +10 -0
- package/frontend/type-checker/moduleAnalysis.js +248 -0
- package/frontend/type-checker/operatorMappings.js +35 -0
- package/frontend/type-checker/overloadResolution.js +605 -0
- package/frontend/type-checker/typeChecker.js +452 -0
- package/frontend/type-checker/typeCompatibility.js +389 -0
- package/frontend/type-checker/visitors/controlFlow.js +483 -0
- package/frontend/type-checker/visitors/functions.js +604 -0
- package/frontend/type-checker/visitors/index.js +38 -0
- package/frontend/type-checker/visitors/literals.js +341 -0
- package/frontend/type-checker/visitors/modules.js +159 -0
- package/frontend/type-checker/visitors/operators.js +109 -0
- package/frontend/type-checker/visitors/statements.js +768 -0
- package/frontend/types/index.js +5 -0
- package/frontend/types/operatorMap.js +134 -0
- package/frontend/types/types.js +2046 -0
- package/frontend/utils/errorCollector.js +244 -0
- package/frontend/utils/index.js +5 -0
- package/frontend/utils/moduleResolver.js +479 -0
- package/package.json +50 -0
- package/packages/browserCache.js +359 -0
- package/packages/fetcher.js +236 -0
- package/packages/index.js +130 -0
- package/packages/lockfile.js +271 -0
- package/packages/manifest.js +291 -0
- package/packages/packageResolver.js +356 -0
- package/packages/resolver.js +310 -0
- package/packages/semver.js +635 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stone Semver Library
|
|
3
|
+
*
|
|
4
|
+
* Implements semantic versioning (semver) parsing and comparison.
|
|
5
|
+
* Supports version ranges like ^, ~, >=, <, etc.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parsed semantic version
|
|
10
|
+
*/
|
|
11
|
+
export class SemVer {
|
|
12
|
+
constructor(major, minor, patch, prerelease = [], build = []) {
|
|
13
|
+
this.major = major;
|
|
14
|
+
this.minor = minor;
|
|
15
|
+
this.patch = patch;
|
|
16
|
+
this.prerelease = prerelease;
|
|
17
|
+
this.build = build;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toString() {
|
|
21
|
+
let version = `${this.major}.${this.minor}.${this.patch}`;
|
|
22
|
+
if (this.prerelease.length > 0) {
|
|
23
|
+
version += '-' + this.prerelease.join('.');
|
|
24
|
+
}
|
|
25
|
+
if (this.build.length > 0) {
|
|
26
|
+
version += '+' + this.build.join('.');
|
|
27
|
+
}
|
|
28
|
+
return version;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Compare this version to another
|
|
33
|
+
* Returns: -1 if this < other, 0 if equal, 1 if this > other
|
|
34
|
+
*/
|
|
35
|
+
compare(other) {
|
|
36
|
+
// Compare major.minor.patch
|
|
37
|
+
if (this.major !== other.major) {
|
|
38
|
+
return this.major > other.major ? 1 : -1;
|
|
39
|
+
}
|
|
40
|
+
if (this.minor !== other.minor) {
|
|
41
|
+
return this.minor > other.minor ? 1 : -1;
|
|
42
|
+
}
|
|
43
|
+
if (this.patch !== other.patch) {
|
|
44
|
+
return this.patch > other.patch ? 1 : -1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Prerelease versions have lower precedence than normal versions
|
|
48
|
+
if (this.prerelease.length === 0 && other.prerelease.length > 0) {
|
|
49
|
+
return 1; // this is release, other is prerelease
|
|
50
|
+
}
|
|
51
|
+
if (this.prerelease.length > 0 && other.prerelease.length === 0) {
|
|
52
|
+
return -1; // this is prerelease, other is release
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Compare prerelease identifiers
|
|
56
|
+
const minLen = Math.min(this.prerelease.length, other.prerelease.length);
|
|
57
|
+
for (let i = 0; i < minLen; i++) {
|
|
58
|
+
const a = this.prerelease[i];
|
|
59
|
+
const b = other.prerelease[i];
|
|
60
|
+
|
|
61
|
+
if (a === b) continue;
|
|
62
|
+
|
|
63
|
+
const aIsNum = typeof a === 'number';
|
|
64
|
+
const bIsNum = typeof b === 'number';
|
|
65
|
+
|
|
66
|
+
if (aIsNum && bIsNum) {
|
|
67
|
+
return a > b ? 1 : -1;
|
|
68
|
+
}
|
|
69
|
+
if (aIsNum) {
|
|
70
|
+
return -1; // numbers have lower precedence than strings
|
|
71
|
+
}
|
|
72
|
+
if (bIsNum) {
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
// Both are strings
|
|
76
|
+
return a > b ? 1 : -1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Longer prerelease has higher precedence
|
|
80
|
+
if (this.prerelease.length !== other.prerelease.length) {
|
|
81
|
+
return this.prerelease.length > other.prerelease.length ? 1 : -1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
eq(other) { return this.compare(other) === 0; }
|
|
88
|
+
lt(other) { return this.compare(other) < 0; }
|
|
89
|
+
lte(other) { return this.compare(other) <= 0; }
|
|
90
|
+
gt(other) { return this.compare(other) > 0; }
|
|
91
|
+
gte(other) { return this.compare(other) >= 0; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Parse a version string into a SemVer object
|
|
96
|
+
* @param {string} version - Version string (e.g., "1.2.3", "1.0.0-alpha.1+build.123")
|
|
97
|
+
* @returns {SemVer} Parsed version
|
|
98
|
+
*/
|
|
99
|
+
export function parseVersion(version) {
|
|
100
|
+
if (version instanceof SemVer) return version;
|
|
101
|
+
|
|
102
|
+
const str = String(version).trim();
|
|
103
|
+
if (!str) {
|
|
104
|
+
throw new SemVerError(`Invalid version: empty string`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Split off build metadata
|
|
108
|
+
let [versionPart, ...buildParts] = str.split('+');
|
|
109
|
+
const build = buildParts.length > 0 ? buildParts.join('+').split('.') : [];
|
|
110
|
+
|
|
111
|
+
// Split off prerelease
|
|
112
|
+
let [corePart, ...prereleaseParts] = versionPart.split('-');
|
|
113
|
+
const prereleaseStr = prereleaseParts.join('-');
|
|
114
|
+
const prerelease = prereleaseStr ? parsePrerelease(prereleaseStr) : [];
|
|
115
|
+
|
|
116
|
+
// Parse major.minor.patch
|
|
117
|
+
const parts = corePart.split('.');
|
|
118
|
+
if (parts.length < 1 || parts.length > 3) {
|
|
119
|
+
throw new SemVerError(`Invalid version format: ${version}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const major = parseVersionNumber(parts[0], 'major', version);
|
|
123
|
+
const minor = parts.length > 1 ? parseVersionNumber(parts[1], 'minor', version) : 0;
|
|
124
|
+
const patch = parts.length > 2 ? parseVersionNumber(parts[2], 'patch', version) : 0;
|
|
125
|
+
|
|
126
|
+
return new SemVer(major, minor, patch, prerelease, build);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function parseVersionNumber(str, name, fullVersion) {
|
|
130
|
+
if (!/^\d+$/.test(str)) {
|
|
131
|
+
throw new SemVerError(`Invalid ${name} version in: ${fullVersion}`);
|
|
132
|
+
}
|
|
133
|
+
const num = parseInt(str, 10);
|
|
134
|
+
if (num < 0) {
|
|
135
|
+
throw new SemVerError(`Negative ${name} version in: ${fullVersion}`);
|
|
136
|
+
}
|
|
137
|
+
return num;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function parsePrerelease(str) {
|
|
141
|
+
return str.split('.').map(part => {
|
|
142
|
+
if (/^\d+$/.test(part)) {
|
|
143
|
+
return parseInt(part, 10);
|
|
144
|
+
}
|
|
145
|
+
return part;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Semver Error
|
|
151
|
+
*/
|
|
152
|
+
export class SemVerError extends Error {
|
|
153
|
+
constructor(message) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.name = 'SemVerError';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// =============================================================================
|
|
160
|
+
// Version Ranges
|
|
161
|
+
// =============================================================================
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Range comparator types
|
|
165
|
+
*/
|
|
166
|
+
const ComparatorType = {
|
|
167
|
+
EQ: 'EQ', // =1.2.3 or 1.2.3
|
|
168
|
+
GT: 'GT', // >1.2.3
|
|
169
|
+
GTE: 'GTE', // >=1.2.3
|
|
170
|
+
LT: 'LT', // <1.2.3
|
|
171
|
+
LTE: 'LTE', // <=1.2.3
|
|
172
|
+
ANY: 'ANY', // * or x
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A single comparator (e.g., >=1.2.3)
|
|
177
|
+
*/
|
|
178
|
+
class Comparator {
|
|
179
|
+
constructor(type, version = null) {
|
|
180
|
+
this.type = type;
|
|
181
|
+
this.version = version;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if a version satisfies this comparator
|
|
186
|
+
*/
|
|
187
|
+
test(version) {
|
|
188
|
+
if (this.type === ComparatorType.ANY) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const v = parseVersion(version);
|
|
193
|
+
|
|
194
|
+
switch (this.type) {
|
|
195
|
+
case ComparatorType.EQ:
|
|
196
|
+
return v.eq(this.version);
|
|
197
|
+
case ComparatorType.GT:
|
|
198
|
+
return v.gt(this.version);
|
|
199
|
+
case ComparatorType.GTE:
|
|
200
|
+
return v.gte(this.version);
|
|
201
|
+
case ComparatorType.LT:
|
|
202
|
+
return v.lt(this.version);
|
|
203
|
+
case ComparatorType.LTE:
|
|
204
|
+
return v.lte(this.version);
|
|
205
|
+
default:
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
toString() {
|
|
211
|
+
switch (this.type) {
|
|
212
|
+
case ComparatorType.ANY: return '*';
|
|
213
|
+
case ComparatorType.EQ: return this.version.toString();
|
|
214
|
+
case ComparatorType.GT: return '>' + this.version.toString();
|
|
215
|
+
case ComparatorType.GTE: return '>=' + this.version.toString();
|
|
216
|
+
case ComparatorType.LT: return '<' + this.version.toString();
|
|
217
|
+
case ComparatorType.LTE: return '<=' + this.version.toString();
|
|
218
|
+
default: return '';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* A version range (set of comparators combined with AND, with multiple sets combined with OR)
|
|
225
|
+
*/
|
|
226
|
+
export class VersionRange {
|
|
227
|
+
constructor(comparatorSets = []) {
|
|
228
|
+
// Each set is an array of comparators (ANDed together)
|
|
229
|
+
// Multiple sets are ORed together
|
|
230
|
+
this.comparatorSets = comparatorSets;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Check if a version satisfies this range
|
|
235
|
+
*/
|
|
236
|
+
test(version) {
|
|
237
|
+
if (this.comparatorSets.length === 0) {
|
|
238
|
+
return true; // Empty range matches anything
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// OR between sets
|
|
242
|
+
return this.comparatorSets.some(set => {
|
|
243
|
+
// AND within each set
|
|
244
|
+
return set.every(comparator => comparator.test(version));
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
toString() {
|
|
249
|
+
return this.comparatorSets
|
|
250
|
+
.map(set => set.map(c => c.toString()).join(' '))
|
|
251
|
+
.join(' || ');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Parse a version range string
|
|
257
|
+
* Supports:
|
|
258
|
+
* - Exact: 1.2.3
|
|
259
|
+
* - Comparators: >1.0.0, >=1.0.0, <2.0.0, <=2.0.0
|
|
260
|
+
* - Caret: ^1.2.3 (>=1.2.3 <2.0.0)
|
|
261
|
+
* - Tilde: ~1.2.3 (>=1.2.3 <1.3.0)
|
|
262
|
+
* - Wildcards: 1.x, 1.2.x, 1.*, *
|
|
263
|
+
* - Ranges: >=1.0.0 <2.0.0
|
|
264
|
+
* - OR: 1.2.3 || 2.0.0
|
|
265
|
+
*
|
|
266
|
+
* @param {string} range - Range string
|
|
267
|
+
* @returns {VersionRange} Parsed range
|
|
268
|
+
*/
|
|
269
|
+
export function parseRange(range) {
|
|
270
|
+
if (range instanceof VersionRange) return range;
|
|
271
|
+
|
|
272
|
+
const str = String(range).trim();
|
|
273
|
+
if (!str || str === '*' || str === 'x' || str === 'X') {
|
|
274
|
+
return new VersionRange([[new Comparator(ComparatorType.ANY)]]);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Split by || for OR groups
|
|
278
|
+
const orParts = str.split('||').map(s => s.trim()).filter(s => s);
|
|
279
|
+
const comparatorSets = orParts.map(parseAndGroup);
|
|
280
|
+
|
|
281
|
+
return new VersionRange(comparatorSets);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Parse a single AND group (e.g., ">=1.0.0 <2.0.0")
|
|
286
|
+
*/
|
|
287
|
+
function parseAndGroup(str) {
|
|
288
|
+
const comparators = [];
|
|
289
|
+
const tokens = tokenizeRange(str);
|
|
290
|
+
|
|
291
|
+
let i = 0;
|
|
292
|
+
while (i < tokens.length) {
|
|
293
|
+
const token = tokens[i];
|
|
294
|
+
|
|
295
|
+
// Caret range: ^1.2.3
|
|
296
|
+
if (token === '^') {
|
|
297
|
+
i++;
|
|
298
|
+
const version = parseVersion(tokens[i]);
|
|
299
|
+
comparators.push(...expandCaret(version));
|
|
300
|
+
i++;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Tilde range: ~1.2.3
|
|
305
|
+
if (token === '~') {
|
|
306
|
+
i++;
|
|
307
|
+
const version = parseVersion(tokens[i]);
|
|
308
|
+
comparators.push(...expandTilde(version));
|
|
309
|
+
i++;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Comparators: >=, >, <=, <, =
|
|
314
|
+
if (token === '>=' || token === '>' || token === '<=' || token === '<' || token === '=') {
|
|
315
|
+
const type = {
|
|
316
|
+
'>=': ComparatorType.GTE,
|
|
317
|
+
'>': ComparatorType.GT,
|
|
318
|
+
'<=': ComparatorType.LTE,
|
|
319
|
+
'<': ComparatorType.LT,
|
|
320
|
+
'=': ComparatorType.EQ,
|
|
321
|
+
}[token];
|
|
322
|
+
|
|
323
|
+
i++;
|
|
324
|
+
const version = parseVersion(tokens[i]);
|
|
325
|
+
comparators.push(new Comparator(type, version));
|
|
326
|
+
i++;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Wildcard: 1.x, 1.2.x, 1.*, *
|
|
331
|
+
if (isWildcard(token)) {
|
|
332
|
+
comparators.push(...expandWildcard(token));
|
|
333
|
+
i++;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Exact version: 1.2.3
|
|
338
|
+
const version = parseVersion(token);
|
|
339
|
+
comparators.push(new Comparator(ComparatorType.EQ, version));
|
|
340
|
+
i++;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return comparators;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Tokenize a range string
|
|
348
|
+
*/
|
|
349
|
+
function tokenizeRange(str) {
|
|
350
|
+
const tokens = [];
|
|
351
|
+
let current = '';
|
|
352
|
+
|
|
353
|
+
for (let i = 0; i < str.length; i++) {
|
|
354
|
+
const char = str[i];
|
|
355
|
+
|
|
356
|
+
if (char === ' ' || char === '\t') {
|
|
357
|
+
if (current) {
|
|
358
|
+
tokens.push(current);
|
|
359
|
+
current = '';
|
|
360
|
+
}
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (char === '^' || char === '~') {
|
|
365
|
+
if (current) {
|
|
366
|
+
tokens.push(current);
|
|
367
|
+
current = '';
|
|
368
|
+
}
|
|
369
|
+
tokens.push(char);
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (char === '>' || char === '<' || char === '=') {
|
|
374
|
+
if (current) {
|
|
375
|
+
tokens.push(current);
|
|
376
|
+
current = '';
|
|
377
|
+
}
|
|
378
|
+
// Check for >= or <=
|
|
379
|
+
if ((char === '>' || char === '<') && str[i + 1] === '=') {
|
|
380
|
+
tokens.push(char + '=');
|
|
381
|
+
i++;
|
|
382
|
+
} else {
|
|
383
|
+
tokens.push(char);
|
|
384
|
+
}
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
current += char;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (current) {
|
|
392
|
+
tokens.push(current);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return tokens;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Check if a token is a wildcard (contains x, X, or *)
|
|
400
|
+
*/
|
|
401
|
+
function isWildcard(token) {
|
|
402
|
+
return /[xX*]/.test(token);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Expand a caret range (^1.2.3 -> >=1.2.3 <2.0.0)
|
|
407
|
+
* For 0.x versions: ^0.2.3 -> >=0.2.3 <0.3.0
|
|
408
|
+
*/
|
|
409
|
+
function expandCaret(version) {
|
|
410
|
+
const comparators = [];
|
|
411
|
+
comparators.push(new Comparator(ComparatorType.GTE, version));
|
|
412
|
+
|
|
413
|
+
let upper;
|
|
414
|
+
if (version.major === 0) {
|
|
415
|
+
if (version.minor === 0) {
|
|
416
|
+
// ^0.0.x -> >=0.0.x <0.0.(x+1)
|
|
417
|
+
upper = new SemVer(0, 0, version.patch + 1);
|
|
418
|
+
} else {
|
|
419
|
+
// ^0.x.y -> >=0.x.y <0.(x+1).0
|
|
420
|
+
upper = new SemVer(0, version.minor + 1, 0);
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
// ^x.y.z -> >=x.y.z <(x+1).0.0
|
|
424
|
+
upper = new SemVer(version.major + 1, 0, 0);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
comparators.push(new Comparator(ComparatorType.LT, upper));
|
|
428
|
+
return comparators;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Expand a tilde range (~1.2.3 -> >=1.2.3 <1.3.0)
|
|
433
|
+
*/
|
|
434
|
+
function expandTilde(version) {
|
|
435
|
+
const comparators = [];
|
|
436
|
+
comparators.push(new Comparator(ComparatorType.GTE, version));
|
|
437
|
+
|
|
438
|
+
const upper = new SemVer(version.major, version.minor + 1, 0);
|
|
439
|
+
comparators.push(new Comparator(ComparatorType.LT, upper));
|
|
440
|
+
|
|
441
|
+
return comparators;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Expand a wildcard (1.x -> >=1.0.0 <2.0.0)
|
|
446
|
+
*/
|
|
447
|
+
function expandWildcard(token) {
|
|
448
|
+
const parts = token.split('.');
|
|
449
|
+
|
|
450
|
+
// * or x -> any
|
|
451
|
+
if (parts.length === 1 && (parts[0] === '*' || parts[0].toLowerCase() === 'x')) {
|
|
452
|
+
return [new Comparator(ComparatorType.ANY)];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const major = parts[0] === '*' || parts[0].toLowerCase() === 'x' ? null : parseInt(parts[0], 10);
|
|
456
|
+
|
|
457
|
+
// 1.x or 1.*
|
|
458
|
+
if (parts.length === 2 || (parts.length === 3 && (parts[2] === '*' || parts[2].toLowerCase() === 'x'))) {
|
|
459
|
+
if (major === null) {
|
|
460
|
+
return [new Comparator(ComparatorType.ANY)];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const minor = parts.length > 1 && parts[1] !== '*' && parts[1].toLowerCase() !== 'x'
|
|
464
|
+
? parseInt(parts[1], 10)
|
|
465
|
+
: null;
|
|
466
|
+
|
|
467
|
+
if (minor === null) {
|
|
468
|
+
// 1.x -> >=1.0.0 <2.0.0
|
|
469
|
+
return [
|
|
470
|
+
new Comparator(ComparatorType.GTE, new SemVer(major, 0, 0)),
|
|
471
|
+
new Comparator(ComparatorType.LT, new SemVer(major + 1, 0, 0)),
|
|
472
|
+
];
|
|
473
|
+
} else {
|
|
474
|
+
// 1.2.x -> >=1.2.0 <1.3.0
|
|
475
|
+
return [
|
|
476
|
+
new Comparator(ComparatorType.GTE, new SemVer(major, minor, 0)),
|
|
477
|
+
new Comparator(ComparatorType.LT, new SemVer(major, minor + 1, 0)),
|
|
478
|
+
];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Fallback: treat as exact
|
|
483
|
+
return [new Comparator(ComparatorType.EQ, parseVersion(token.replace(/[xX*]/g, '0')))];
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// =============================================================================
|
|
487
|
+
// Utility Functions
|
|
488
|
+
// =============================================================================
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Check if a version satisfies a range
|
|
492
|
+
* @param {string|SemVer} version - Version to check
|
|
493
|
+
* @param {string|VersionRange} range - Range to check against
|
|
494
|
+
* @returns {boolean} True if version satisfies range
|
|
495
|
+
*/
|
|
496
|
+
export function satisfies(version, range) {
|
|
497
|
+
const v = parseVersion(version);
|
|
498
|
+
const r = parseRange(range);
|
|
499
|
+
return r.test(v);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Compare two versions
|
|
504
|
+
* @returns {number} -1, 0, or 1
|
|
505
|
+
*/
|
|
506
|
+
export function compare(a, b) {
|
|
507
|
+
return parseVersion(a).compare(parseVersion(b));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Check if a > b
|
|
512
|
+
*/
|
|
513
|
+
export function gt(a, b) {
|
|
514
|
+
return compare(a, b) > 0;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Check if a >= b
|
|
519
|
+
*/
|
|
520
|
+
export function gte(a, b) {
|
|
521
|
+
return compare(a, b) >= 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Check if a < b
|
|
526
|
+
*/
|
|
527
|
+
export function lt(a, b) {
|
|
528
|
+
return compare(a, b) < 0;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Check if a <= b
|
|
533
|
+
*/
|
|
534
|
+
export function lte(a, b) {
|
|
535
|
+
return compare(a, b) <= 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Check if a == b
|
|
540
|
+
*/
|
|
541
|
+
export function eq(a, b) {
|
|
542
|
+
return compare(a, b) === 0;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Sort versions in ascending order
|
|
547
|
+
*/
|
|
548
|
+
export function sort(versions) {
|
|
549
|
+
return [...versions].sort(compare);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Sort versions in descending order
|
|
554
|
+
*/
|
|
555
|
+
export function rsort(versions) {
|
|
556
|
+
return [...versions].sort((a, b) => compare(b, a));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Find the highest version that satisfies a range
|
|
561
|
+
* @param {string[]} versions - Available versions
|
|
562
|
+
* @param {string} range - Version range
|
|
563
|
+
* @returns {string|null} Highest matching version or null
|
|
564
|
+
*/
|
|
565
|
+
export function maxSatisfying(versions, range) {
|
|
566
|
+
const r = parseRange(range);
|
|
567
|
+
const matching = versions.filter(v => r.test(parseVersion(v)));
|
|
568
|
+
if (matching.length === 0) return null;
|
|
569
|
+
return rsort(matching)[0];
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Find the lowest version that satisfies a range
|
|
574
|
+
* @param {string[]} versions - Available versions
|
|
575
|
+
* @param {string} range - Version range
|
|
576
|
+
* @returns {string|null} Lowest matching version or null
|
|
577
|
+
*/
|
|
578
|
+
export function minSatisfying(versions, range) {
|
|
579
|
+
const r = parseRange(range);
|
|
580
|
+
const matching = versions.filter(v => r.test(parseVersion(v)));
|
|
581
|
+
if (matching.length === 0) return null;
|
|
582
|
+
return sort(matching)[0];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Check if a version is valid semver
|
|
587
|
+
*/
|
|
588
|
+
export function valid(version) {
|
|
589
|
+
try {
|
|
590
|
+
parseVersion(version);
|
|
591
|
+
return true;
|
|
592
|
+
} catch {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Coerce a string into a valid semver if possible
|
|
599
|
+
*/
|
|
600
|
+
export function coerce(version) {
|
|
601
|
+
try {
|
|
602
|
+
return parseVersion(version).toString();
|
|
603
|
+
} catch {
|
|
604
|
+
// Try to extract version-like pattern
|
|
605
|
+
const match = String(version).match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
|
606
|
+
if (match) {
|
|
607
|
+
const major = parseInt(match[1], 10);
|
|
608
|
+
const minor = match[2] ? parseInt(match[2], 10) : 0;
|
|
609
|
+
const patch = match[3] ? parseInt(match[3], 10) : 0;
|
|
610
|
+
return new SemVer(major, minor, patch).toString();
|
|
611
|
+
}
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export default {
|
|
617
|
+
SemVer,
|
|
618
|
+
SemVerError,
|
|
619
|
+
VersionRange,
|
|
620
|
+
parseVersion,
|
|
621
|
+
parseRange,
|
|
622
|
+
satisfies,
|
|
623
|
+
compare,
|
|
624
|
+
gt,
|
|
625
|
+
gte,
|
|
626
|
+
lt,
|
|
627
|
+
lte,
|
|
628
|
+
eq,
|
|
629
|
+
sort,
|
|
630
|
+
rsort,
|
|
631
|
+
maxSatisfying,
|
|
632
|
+
minSatisfying,
|
|
633
|
+
valid,
|
|
634
|
+
coerce,
|
|
635
|
+
};
|