inup 1.4.10 → 1.5.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 +1 -7
- package/dist/cli.js +2 -1
- package/dist/config/constants.js +1 -2
- package/dist/config/project-config.js +6 -0
- package/dist/core/package-detector.js +163 -89
- package/dist/core/upgrade-runner.js +68 -16
- package/dist/features/changelog/clients/github-client.js +134 -0
- package/dist/features/changelog/clients/npm-registry-client.js +53 -0
- package/dist/features/changelog/index.js +19 -0
- package/dist/features/changelog/parsers/changelog-parser.js +68 -0
- package/dist/features/changelog/parsers/github-release-html-parser.js +61 -0
- package/dist/features/changelog/parsers/package-metadata.js +34 -0
- package/dist/features/changelog/parsers/repository-ref.js +26 -0
- package/dist/features/changelog/services/changelog-service.js +30 -0
- package/dist/features/changelog/services/package-metadata-service.js +108 -0
- package/dist/features/changelog/services/release-notes-service.js +180 -0
- package/dist/features/changelog/types/changelog.types.js +3 -0
- package/dist/interactive-ui.js +343 -161
- package/dist/services/background-audit.js +60 -0
- package/dist/services/index.js +3 -3
- package/dist/services/jsdelivr-registry.js +92 -176
- package/dist/services/npm-registry.js +97 -27
- package/dist/services/vulnerability-checker.js +133 -0
- package/dist/ui/controllers/index.js +8 -0
- package/dist/ui/controllers/package-info-modal-controller.js +237 -0
- package/dist/ui/controllers/vulnerability-audit-controller.js +82 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +41 -10
- package/dist/ui/modal/index.js +22 -0
- package/dist/ui/modal/layout.js +84 -0
- package/dist/ui/modal/package-info-sections.js +327 -0
- package/dist/ui/modal/package-info.js +147 -0
- package/dist/ui/modal/theme-selector.js +46 -0
- package/dist/ui/modal/types.js +3 -0
- package/dist/ui/presenters/index.js +11 -0
- package/dist/ui/presenters/vulnerability.js +76 -0
- package/dist/ui/renderer/index.js +9 -11
- package/dist/ui/renderer/package-list.js +166 -66
- package/dist/ui/state/filter-manager.js +17 -2
- package/dist/ui/state/modal-manager.js +48 -6
- package/dist/ui/state/state-manager.js +49 -12
- package/dist/ui/utils/cursor.js +18 -0
- package/dist/ui/utils/index.js +8 -1
- package/dist/ui/utils/terminal-input.js +82 -0
- package/dist/ui/utils/text.js +75 -0
- package/dist/ui/utils/version.js +3 -2
- package/package.json +7 -11
- package/dist/services/changelog-fetcher.js +0 -190
- package/dist/ui/renderer/modal.js +0 -190
- package/dist/ui/renderer/theme-selector.js +0 -83
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BackgroundAuditTracker = void 0;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
class BackgroundAuditTracker {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.pending = new Map();
|
|
8
|
+
this.inFlight = new Set();
|
|
9
|
+
this.completed = new Set();
|
|
10
|
+
}
|
|
11
|
+
enqueue(packages) {
|
|
12
|
+
let added = 0;
|
|
13
|
+
for (const pkg of packages) {
|
|
14
|
+
if (!pkg.name || !pkg.version)
|
|
15
|
+
continue;
|
|
16
|
+
if (this.pending.has(pkg.name) ||
|
|
17
|
+
this.inFlight.has(pkg.name) ||
|
|
18
|
+
this.completed.has(pkg.name)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
this.pending.set(pkg.name, pkg.version);
|
|
22
|
+
added++;
|
|
23
|
+
}
|
|
24
|
+
if (added > 0) {
|
|
25
|
+
utils_1.debugLog.info('background-audit', `queued ${added} package(s)`);
|
|
26
|
+
}
|
|
27
|
+
return added;
|
|
28
|
+
}
|
|
29
|
+
reserveNextBatch(limit = 20) {
|
|
30
|
+
const packages = new Map();
|
|
31
|
+
const packageNames = [];
|
|
32
|
+
for (const [name, version] of this.pending) {
|
|
33
|
+
packages.set(name, version);
|
|
34
|
+
packageNames.push(name);
|
|
35
|
+
this.pending.delete(name);
|
|
36
|
+
this.inFlight.add(name);
|
|
37
|
+
if (packageNames.length >= limit) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { packages, packageNames };
|
|
42
|
+
}
|
|
43
|
+
markCompleted(packageNames) {
|
|
44
|
+
for (const packageName of packageNames) {
|
|
45
|
+
this.inFlight.delete(packageName);
|
|
46
|
+
this.completed.add(packageName);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
getProgress() {
|
|
50
|
+
const total = this.pending.size + this.inFlight.size + this.completed.size;
|
|
51
|
+
return {
|
|
52
|
+
completed: this.completed.size,
|
|
53
|
+
total,
|
|
54
|
+
isRunning: this.pending.size > 0 || this.inFlight.size > 0,
|
|
55
|
+
hasData: this.completed.size > 0,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.BackgroundAuditTracker = BackgroundAuditTracker;
|
|
60
|
+
//# sourceMappingURL=background-audit.js.map
|
package/dist/services/index.js
CHANGED
|
@@ -19,8 +19,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
__exportStar(require("./npm-registry"), exports);
|
|
21
21
|
__exportStar(require("./jsdelivr-registry"), exports);
|
|
22
|
-
__exportStar(require("
|
|
22
|
+
__exportStar(require("../features/changelog"), exports);
|
|
23
23
|
__exportStar(require("./version-checker"), exports);
|
|
24
|
-
__exportStar(require("./
|
|
25
|
-
__exportStar(require("./
|
|
24
|
+
__exportStar(require("./vulnerability-checker"), exports);
|
|
25
|
+
__exportStar(require("./background-audit"), exports);
|
|
26
26
|
//# sourceMappingURL=index.js.map
|
|
@@ -33,22 +33,18 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.fetchExactPackageManifest = fetchExactPackageManifest;
|
|
36
37
|
exports.getAllPackageDataFromJsdelivr = getAllPackageDataFromJsdelivr;
|
|
37
|
-
exports.clearJsdelivrPackageCache = clearJsdelivrPackageCache;
|
|
38
38
|
exports.closeJsdelivrPool = closeJsdelivrPool;
|
|
39
|
+
exports.clearExactManifestCache = clearExactManifestCache;
|
|
39
40
|
const undici_1 = require("undici");
|
|
40
41
|
const semver = __importStar(require("semver"));
|
|
41
42
|
const config_1 = require("../config");
|
|
42
|
-
const
|
|
43
|
-
const cache_manager_1 = require("./cache-manager");
|
|
44
|
-
const utils_1 = require("../ui/utils");
|
|
45
|
-
const utils_2 = require("../utils");
|
|
46
|
-
// Batch configuration for progressive loading
|
|
47
|
-
const BATCH_SIZE = 5;
|
|
48
|
-
const BATCH_TIMEOUT_MS = 500;
|
|
43
|
+
const utils_1 = require("../utils");
|
|
49
44
|
const DEFAULT_JSDELIVR_RETRY_TIMEOUT_MS = 2000;
|
|
50
45
|
const DEFAULT_JSDELIVR_POOL_TIMEOUT_MS = 60000;
|
|
51
46
|
const MIN_JSDELIVR_CONNECT_TIMEOUT_MS = 500;
|
|
47
|
+
const EXACT_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
52
48
|
const toPositiveInteger = (value) => {
|
|
53
49
|
if (!Number.isFinite(value) || value <= 0) {
|
|
54
50
|
return null;
|
|
@@ -222,14 +218,9 @@ const sortVersionsDescending = (versions) => {
|
|
|
222
218
|
};
|
|
223
219
|
const isExpectedTransientError = (error) => isTimeoutError(error) || isTransientNetworkError(error);
|
|
224
220
|
/**
|
|
225
|
-
* Fetches package.json from
|
|
226
|
-
* Uses connection pooling and keep-alive for maximum performance.
|
|
227
|
-
* Retries on transient failures while keeping a short fallback budget.
|
|
228
|
-
* @param packageName - The npm package name
|
|
229
|
-
* @param versionTag - The version tag (e.g., '14', 'latest')
|
|
230
|
-
* @returns The package.json content or null if not found
|
|
221
|
+
* Fetches a package.json manifest from jsDelivr for a version tag.
|
|
231
222
|
*/
|
|
232
|
-
async function
|
|
223
|
+
async function fetchPackageManifestFromJsdelivr(packageName, versionTag) {
|
|
233
224
|
const url = `${config_1.JSDELIVR_CDN_URL}/${encodeURIComponent(packageName)}@${versionTag}/package.json`;
|
|
234
225
|
for (let attempt = 0; attempt < RETRY_TIMEOUTS.length; attempt++) {
|
|
235
226
|
const timeout = RETRY_TIMEOUTS[attempt];
|
|
@@ -249,26 +240,28 @@ async function fetchPackageJsonFromJsdelivr(packageName, versionTag) {
|
|
|
249
240
|
await consumeBodySafely(body);
|
|
250
241
|
if (isRetryableStatus(statusCode) && attempt < RETRY_TIMEOUTS.length - 1) {
|
|
251
242
|
const delay = getRetryDelay(attempt, headers);
|
|
252
|
-
|
|
243
|
+
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} HTTP ${statusCode}, retry ${attempt + 1} in ${delay}ms`);
|
|
253
244
|
if (delay > 0) {
|
|
254
245
|
await sleep(delay);
|
|
255
246
|
}
|
|
256
247
|
continue;
|
|
257
248
|
}
|
|
258
|
-
|
|
249
|
+
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} HTTP ${statusCode}, no more retries`);
|
|
259
250
|
return null;
|
|
260
251
|
}
|
|
261
252
|
const text = await body.text();
|
|
262
253
|
const data = JSON.parse(text);
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
254
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
utils_1.debugLog.perf('jsdelivr', `fetch manifest ${packageName}@${versionTag}`, tReq);
|
|
258
|
+
return data;
|
|
266
259
|
}
|
|
267
260
|
catch (error) {
|
|
268
261
|
if ((isTimeoutError(error) || isTransientNetworkError(error)) &&
|
|
269
262
|
attempt < RETRY_TIMEOUTS.length - 1) {
|
|
270
263
|
const delay = getRetryDelay(attempt);
|
|
271
|
-
|
|
264
|
+
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} transient error on attempt ${attempt + 1}, retry in ${delay}ms`, error);
|
|
272
265
|
if (delay > 0) {
|
|
273
266
|
await sleep(delay);
|
|
274
267
|
}
|
|
@@ -277,203 +270,126 @@ async function fetchPackageJsonFromJsdelivr(packageName, versionTag) {
|
|
|
277
270
|
if (!isExpectedTransientError(error)) {
|
|
278
271
|
// Unexpected errors are logged for observability.
|
|
279
272
|
console.error(`jsDelivr fetch failed for ${packageName}@${versionTag} on attempt ${attempt + 1}/${RETRY_TIMEOUTS.length}`, error);
|
|
280
|
-
|
|
273
|
+
utils_1.debugLog.error('jsdelivr', `unexpected error for ${packageName}@${versionTag} attempt ${attempt + 1}`, error);
|
|
281
274
|
}
|
|
282
275
|
else {
|
|
283
|
-
|
|
276
|
+
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} exhausted retries`, error);
|
|
284
277
|
}
|
|
285
278
|
return null;
|
|
286
279
|
}
|
|
287
280
|
}
|
|
288
281
|
return null;
|
|
289
282
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
283
|
+
async function fetchPackageManifestFromNpmRegistry(packageName, version) {
|
|
284
|
+
const controller = new AbortController();
|
|
285
|
+
const timeoutId = setTimeout(() => controller.abort(), config_1.REQUEST_TIMEOUT);
|
|
286
|
+
try {
|
|
287
|
+
const response = await fetch(`${config_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}`, {
|
|
288
|
+
method: 'GET',
|
|
289
|
+
headers: {
|
|
290
|
+
accept: 'application/json',
|
|
291
|
+
},
|
|
292
|
+
signal: controller.signal,
|
|
293
|
+
});
|
|
294
|
+
if (!response.ok) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return (await response.json());
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
utils_1.debugLog.warn('npm-registry', `exact manifest fallback failed for ${packageName}@${version}`, error);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
clearTimeout(timeoutId);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const inFlightManifests = new Map();
|
|
308
|
+
async function fetchExactPackageManifest(packageName, version) {
|
|
309
|
+
const normalizedVersion = version.trim();
|
|
310
|
+
if (!EXACT_VERSION_PATTERN.test(normalizedVersion) || !semver.valid(normalizedVersion)) {
|
|
311
|
+
utils_1.debugLog.warn('jsdelivr', `skipping non-exact version lookup for ${packageName}@${version}`);
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
const cacheKey = `${packageName}@${normalizedVersion}`;
|
|
315
|
+
const inFlight = inFlightManifests.get(cacheKey);
|
|
316
|
+
if (inFlight) {
|
|
317
|
+
return await inFlight;
|
|
318
|
+
}
|
|
319
|
+
const lookupPromise = (async () => {
|
|
320
|
+
const jsdelivrManifest = await fetchPackageManifestFromJsdelivr(packageName, normalizedVersion);
|
|
321
|
+
if (jsdelivrManifest) {
|
|
322
|
+
return jsdelivrManifest;
|
|
323
|
+
}
|
|
324
|
+
return await fetchPackageManifestFromNpmRegistry(packageName, normalizedVersion);
|
|
325
|
+
})().finally(() => {
|
|
326
|
+
inFlightManifests.delete(cacheKey);
|
|
327
|
+
});
|
|
328
|
+
inFlightManifests.set(cacheKey, lookupPromise);
|
|
329
|
+
return await lookupPromise;
|
|
330
|
+
}
|
|
331
|
+
async function getAllPackageDataFromJsdelivr(packageNames, currentVersions, onProgress) {
|
|
302
332
|
const packageData = new Map();
|
|
303
333
|
if (packageNames.length === 0) {
|
|
304
334
|
return packageData;
|
|
305
335
|
}
|
|
306
336
|
const total = packageNames.length;
|
|
307
337
|
let completedCount = 0;
|
|
308
|
-
let progressCallback = onProgress;
|
|
309
|
-
let batchReadyCallback = onBatchReady;
|
|
310
|
-
// Batch buffer for progressive updates
|
|
311
|
-
let batchBuffer = [];
|
|
312
|
-
let batchTimer = null;
|
|
313
|
-
const emitProgress = (packageName, completed, packageTotal) => {
|
|
314
|
-
if (!progressCallback) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
try {
|
|
318
|
-
progressCallback(packageName, completed, packageTotal);
|
|
319
|
-
}
|
|
320
|
-
catch (error) {
|
|
321
|
-
console.error('Progress callback failed, disabling progress updates for this run.', error);
|
|
322
|
-
progressCallback = undefined;
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
const emitBatch = (batch) => {
|
|
326
|
-
if (!batchReadyCallback) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
try {
|
|
330
|
-
batchReadyCallback(batch);
|
|
331
|
-
}
|
|
332
|
-
catch (error) {
|
|
333
|
-
console.error('Batch callback failed, disabling batch updates for this run.', error);
|
|
334
|
-
batchReadyCallback = undefined;
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
// Helper to flush the current batch
|
|
338
|
-
const flushBatch = () => {
|
|
339
|
-
if (batchBuffer.length > 0) {
|
|
340
|
-
const batch = [...batchBuffer];
|
|
341
|
-
batchBuffer = [];
|
|
342
|
-
emitBatch(batch);
|
|
343
|
-
}
|
|
344
|
-
if (batchTimer) {
|
|
345
|
-
clearTimeout(batchTimer);
|
|
346
|
-
batchTimer = null;
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
// Helper to add package to batch and flush if needed
|
|
350
|
-
const addToBatch = (packageName, data) => {
|
|
351
|
-
if (!batchReadyCallback) {
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
batchBuffer.push({ name: packageName, data });
|
|
355
|
-
// Flush if batch is full
|
|
356
|
-
if (batchBuffer.length >= BATCH_SIZE) {
|
|
357
|
-
flushBatch();
|
|
358
|
-
}
|
|
359
|
-
else if (!batchTimer) {
|
|
360
|
-
// Set timer to flush batch after timeout
|
|
361
|
-
batchTimer = setTimeout(flushBatch, BATCH_TIMEOUT_MS);
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
// Process individual package fetch with immediate npm fallback on failure
|
|
365
338
|
const inFlightLookups = new Map();
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const npmData = await (0, npm_registry_1.getAllPackageData)([packageName]);
|
|
371
|
-
const result = npmData.get(packageName) ?? null;
|
|
372
|
-
if (result) {
|
|
373
|
-
cache_manager_1.packageCache.set(packageName, result);
|
|
374
|
-
utils_2.debugLog.perf('jsdelivr', `npm fallback resolved ${packageName} → ${result.latestVersion}`, tFallback);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
utils_2.debugLog.warn('jsdelivr', `npm fallback returned no data for ${packageName}`);
|
|
378
|
-
}
|
|
379
|
-
return result;
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
utils_2.debugLog.error('jsdelivr', `npm fallback failed for ${packageName}`, error);
|
|
339
|
+
const fetchPackageData = async (packageName, currentVersion) => {
|
|
340
|
+
const latestManifest = await fetchPackageManifestFromJsdelivr(packageName, 'latest');
|
|
341
|
+
const latestVersion = typeof latestManifest?.version === 'string' ? latestManifest.version.trim() : '';
|
|
342
|
+
if (!latestVersion) {
|
|
383
343
|
return null;
|
|
384
344
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
if (majorResult && majorResult.version !== latestVersion) {
|
|
401
|
-
allVersions.push(majorResult.version);
|
|
402
|
-
}
|
|
403
|
-
const sortedVersions = sortVersionsDescending(allVersions);
|
|
404
|
-
const orderedVersions = sortedVersions[0] === latestVersion
|
|
405
|
-
? sortedVersions
|
|
406
|
-
: [latestVersion, ...sortedVersions.filter((version) => version !== latestVersion)];
|
|
407
|
-
const result = {
|
|
408
|
-
latestVersion,
|
|
409
|
-
allVersions: orderedVersions,
|
|
410
|
-
};
|
|
411
|
-
cache_manager_1.packageCache.set(packageName, result);
|
|
412
|
-
return result;
|
|
413
|
-
}
|
|
414
|
-
catch {
|
|
415
|
-
return await fetchFromNpmFallback(packageName);
|
|
416
|
-
}
|
|
345
|
+
const majorVersion = extractMajorVersion(currentVersion);
|
|
346
|
+
const latestMajorVersion = extractMajorVersion(latestVersion);
|
|
347
|
+
const shouldFetchMajorVersion = Boolean(majorVersion && (latestMajorVersion === null || latestMajorVersion !== majorVersion));
|
|
348
|
+
const majorManifest = shouldFetchMajorVersion
|
|
349
|
+
? await fetchPackageManifestFromJsdelivr(packageName, majorVersion)
|
|
350
|
+
: null;
|
|
351
|
+
const majorResolvedVersion = typeof majorManifest?.version === 'string' ? majorManifest.version.trim() : '';
|
|
352
|
+
const sortedVersions = sortVersionsDescending([latestVersion, majorResolvedVersion].filter(Boolean));
|
|
353
|
+
const allVersions = sortedVersions[0] === latestVersion
|
|
354
|
+
? sortedVersions
|
|
355
|
+
: [latestVersion, ...sortedVersions.filter((version) => version !== latestVersion)];
|
|
356
|
+
return {
|
|
357
|
+
latestVersion,
|
|
358
|
+
allVersions,
|
|
359
|
+
};
|
|
417
360
|
};
|
|
418
361
|
const getPackageData = async (packageName, currentVersion) => {
|
|
419
|
-
const cached = cache_manager_1.packageCache.get(packageName);
|
|
420
|
-
if (cached) {
|
|
421
|
-
utils_2.debugLog.info('jsdelivr', `cache hit: ${packageName} → ${cached.latestVersion}`);
|
|
422
|
-
return cached;
|
|
423
|
-
}
|
|
424
362
|
const inFlight = inFlightLookups.get(packageName);
|
|
425
363
|
if (inFlight) {
|
|
426
364
|
return await inFlight;
|
|
427
365
|
}
|
|
428
|
-
const lookupPromise =
|
|
366
|
+
const lookupPromise = fetchPackageData(packageName, currentVersion).finally(() => {
|
|
429
367
|
inFlightLookups.delete(packageName);
|
|
430
368
|
});
|
|
431
369
|
inFlightLookups.set(packageName, lookupPromise);
|
|
432
370
|
return await lookupPromise;
|
|
433
371
|
};
|
|
434
|
-
|
|
372
|
+
await Promise.all(packageNames.map(async (packageName) => {
|
|
435
373
|
try {
|
|
436
|
-
const
|
|
437
|
-
const result = await getPackageData(packageName, currentVersion);
|
|
374
|
+
const result = await getPackageData(packageName, currentVersions?.get(packageName));
|
|
438
375
|
if (result) {
|
|
439
376
|
packageData.set(packageName, result);
|
|
440
|
-
addToBatch(packageName, result);
|
|
441
377
|
}
|
|
442
378
|
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
console.error(`Failed to resolve package data for ${packageName}; continuing with others.`, error);
|
|
445
|
-
}
|
|
446
379
|
finally {
|
|
447
380
|
completedCount++;
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
try {
|
|
452
|
-
// Fire all requests simultaneously - each request internally handles retries/fallback.
|
|
453
|
-
await Promise.all(packageNames.map(fetchPackageWithFallback));
|
|
454
|
-
}
|
|
455
|
-
finally {
|
|
456
|
-
// Flush any remaining batch items
|
|
457
|
-
flushBatch();
|
|
458
|
-
// Flush persistent cache to disk
|
|
459
|
-
cache_manager_1.packageCache.flush();
|
|
460
|
-
// Clear the progress line if no custom progress handler
|
|
461
|
-
if (!onProgress) {
|
|
462
|
-
utils_1.ConsoleUtils.clearProgress();
|
|
381
|
+
onProgress?.(packageName, completedCount, total);
|
|
463
382
|
}
|
|
464
|
-
}
|
|
383
|
+
}));
|
|
465
384
|
return packageData;
|
|
466
385
|
}
|
|
467
|
-
/**
|
|
468
|
-
* Clear the package cache (useful for testing)
|
|
469
|
-
*/
|
|
470
|
-
function clearJsdelivrPackageCache() {
|
|
471
|
-
cache_manager_1.packageCache.clear();
|
|
472
|
-
}
|
|
473
386
|
/**
|
|
474
387
|
* Close the jsDelivr connection pool (useful for graceful shutdown)
|
|
475
388
|
*/
|
|
476
389
|
async function closeJsdelivrPool() {
|
|
477
390
|
await jsdelivrPool.close();
|
|
478
391
|
}
|
|
392
|
+
function clearExactManifestCache() {
|
|
393
|
+
inFlightManifests.clear();
|
|
394
|
+
}
|
|
479
395
|
//# sourceMappingURL=jsdelivr-registry.js.map
|
|
@@ -34,21 +34,48 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getAllPackageData = getAllPackageData;
|
|
37
|
+
exports.getAllPackageDataBatched = getAllPackageDataBatched;
|
|
37
38
|
exports.clearPackageCache = clearPackageCache;
|
|
38
39
|
const semver = __importStar(require("semver"));
|
|
39
40
|
const config_1 = require("../config");
|
|
40
|
-
const
|
|
41
|
+
const jsdelivr_registry_1 = require("./jsdelivr-registry");
|
|
41
42
|
const utils_1 = require("../ui/utils");
|
|
43
|
+
const inFlightLookups = new Map();
|
|
44
|
+
const isRetryableStatus = (statusCode) => statusCode === 408 || statusCode === 429 || statusCode >= 500;
|
|
45
|
+
const isTransientNetworkError = (error) => {
|
|
46
|
+
if (!(error instanceof Error)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const maybeCode = error.code;
|
|
50
|
+
return (error.name === 'AbortError' ||
|
|
51
|
+
maybeCode === 'ENOTFOUND' ||
|
|
52
|
+
maybeCode === 'EAI_AGAIN' ||
|
|
53
|
+
maybeCode === 'ECONNRESET' ||
|
|
54
|
+
maybeCode === 'ECONNREFUSED' ||
|
|
55
|
+
maybeCode === 'ETIMEDOUT' ||
|
|
56
|
+
maybeCode === 'EPIPE');
|
|
57
|
+
};
|
|
58
|
+
const fetchFromJsdelivrFallback = async (packageName, currentVersion) => {
|
|
59
|
+
const jsdelivrData = await (0, jsdelivr_registry_1.getAllPackageDataFromJsdelivr)([packageName], currentVersion ? new Map([[packageName, currentVersion]]) : undefined);
|
|
60
|
+
return jsdelivrData.get(packageName) ?? { latestVersion: 'unknown', allVersions: [] };
|
|
61
|
+
};
|
|
62
|
+
async function getFreshPackageData(packageName, currentVersion) {
|
|
63
|
+
const cacheKey = `${packageName}@${currentVersion ?? ''}`;
|
|
64
|
+
const inFlight = inFlightLookups.get(cacheKey);
|
|
65
|
+
if (inFlight) {
|
|
66
|
+
return await inFlight;
|
|
67
|
+
}
|
|
68
|
+
const lookupPromise = fetchPackageFromRegistryWithFallback(packageName, currentVersion).finally(() => {
|
|
69
|
+
inFlightLookups.delete(cacheKey);
|
|
70
|
+
});
|
|
71
|
+
inFlightLookups.set(cacheKey, lookupPromise);
|
|
72
|
+
return await lookupPromise;
|
|
73
|
+
}
|
|
42
74
|
/**
|
|
43
75
|
* Fetches package data from npm registry.
|
|
44
|
-
*
|
|
76
|
+
* Falls back to jsDelivr when npm is temporarily unavailable.
|
|
45
77
|
*/
|
|
46
|
-
async function
|
|
47
|
-
// Use CacheManager for unified caching (memory + disk)
|
|
48
|
-
const cached = cache_manager_1.packageCache.get(packageName);
|
|
49
|
-
if (cached) {
|
|
50
|
-
return cached;
|
|
51
|
-
}
|
|
78
|
+
async function fetchPackageFromRegistryWithFallback(packageName, currentVersion) {
|
|
52
79
|
try {
|
|
53
80
|
const url = `${config_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}`;
|
|
54
81
|
const controller = new AbortController();
|
|
@@ -63,32 +90,29 @@ async function fetchPackageFromRegistry(packageName) {
|
|
|
63
90
|
});
|
|
64
91
|
clearTimeout(timeoutId);
|
|
65
92
|
if (!response.ok) {
|
|
93
|
+
if (isRetryableStatus(response.status)) {
|
|
94
|
+
return await fetchFromJsdelivrFallback(packageName, currentVersion);
|
|
95
|
+
}
|
|
66
96
|
throw new Error(`HTTP ${response.status}`);
|
|
67
97
|
}
|
|
68
98
|
const text = await response.text();
|
|
69
99
|
const data = JSON.parse(text);
|
|
70
|
-
|
|
71
|
-
const allVersions = Object.keys(data.versions || {}).filter((version) => {
|
|
72
|
-
// Match only X.Y.Z format (no pre-release, no build metadata)
|
|
73
|
-
return /^[0-9]+\.[0-9]+\.[0-9]+$/.test(version);
|
|
74
|
-
});
|
|
75
|
-
// Sort versions to find the latest
|
|
100
|
+
const allVersions = Object.keys(data.versions || {}).filter((version) => /^[0-9]+\.[0-9]+\.[0-9]+$/.test(version));
|
|
76
101
|
const sortedVersions = allVersions.sort(semver.rcompare);
|
|
77
102
|
const latestVersion = sortedVersions.length > 0 ? sortedVersions[0] : 'unknown';
|
|
78
|
-
|
|
103
|
+
return {
|
|
79
104
|
latestVersion,
|
|
80
105
|
allVersions,
|
|
81
106
|
};
|
|
82
|
-
// Cache the result using CacheManager (handles both memory and disk)
|
|
83
|
-
cache_manager_1.packageCache.set(packageName, result);
|
|
84
|
-
return result;
|
|
85
107
|
}
|
|
86
108
|
finally {
|
|
87
109
|
clearTimeout(timeoutId);
|
|
88
110
|
}
|
|
89
111
|
}
|
|
90
112
|
catch (error) {
|
|
91
|
-
|
|
113
|
+
if (isTransientNetworkError(error)) {
|
|
114
|
+
return await fetchFromJsdelivrFallback(packageName, currentVersion);
|
|
115
|
+
}
|
|
92
116
|
return { latestVersion: 'unknown', allVersions: [] };
|
|
93
117
|
}
|
|
94
118
|
}
|
|
@@ -97,17 +121,15 @@ async function fetchPackageFromRegistry(packageName) {
|
|
|
97
121
|
* Uses native fetch with timeout support for reliable performance.
|
|
98
122
|
* Only returns valid semantic versions (X.Y.Z format, excluding pre-releases).
|
|
99
123
|
*/
|
|
100
|
-
async function getAllPackageData(packageNames, onProgress) {
|
|
124
|
+
async function getAllPackageData(packageNames, onProgress, currentVersions) {
|
|
101
125
|
const packageData = new Map();
|
|
102
126
|
if (packageNames.length === 0) {
|
|
103
127
|
return packageData;
|
|
104
128
|
}
|
|
105
129
|
const total = packageNames.length;
|
|
106
130
|
let completedCount = 0;
|
|
107
|
-
// Fire all requests simultaneously
|
|
108
|
-
// Concurrency is handled naturally by the event loop with fetch
|
|
109
131
|
const allPromises = packageNames.map(async (packageName) => {
|
|
110
|
-
const data = await
|
|
132
|
+
const data = await getFreshPackageData(packageName, currentVersions?.get(packageName));
|
|
111
133
|
packageData.set(packageName, data);
|
|
112
134
|
completedCount++;
|
|
113
135
|
if (onProgress) {
|
|
@@ -116,18 +138,66 @@ async function getAllPackageData(packageNames, onProgress) {
|
|
|
116
138
|
});
|
|
117
139
|
// Wait for all requests to complete
|
|
118
140
|
await Promise.all(allPromises);
|
|
119
|
-
// Flush persistent cache to disk
|
|
120
|
-
cache_manager_1.packageCache.flush();
|
|
121
141
|
// Clear the progress line if no custom progress handler
|
|
122
142
|
if (!onProgress) {
|
|
123
143
|
utils_1.ConsoleUtils.clearProgress();
|
|
124
144
|
}
|
|
125
145
|
return packageData;
|
|
126
146
|
}
|
|
147
|
+
async function runWithConcurrencyLimit(items, concurrency, worker) {
|
|
148
|
+
if (items.length === 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const limit = Math.max(1, Math.min(concurrency, items.length));
|
|
152
|
+
let nextIndex = 0;
|
|
153
|
+
const runWorker = async () => {
|
|
154
|
+
while (nextIndex < items.length) {
|
|
155
|
+
const currentIndex = nextIndex++;
|
|
156
|
+
await worker(items[currentIndex], currentIndex);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
await Promise.all(Array.from({ length: limit }, () => runWorker()));
|
|
160
|
+
}
|
|
161
|
+
async function getAllPackageDataBatched(packageNames, onBatchReady, currentVersions, options = {}) {
|
|
162
|
+
const packageData = new Map();
|
|
163
|
+
if (packageNames.length === 0) {
|
|
164
|
+
return packageData;
|
|
165
|
+
}
|
|
166
|
+
const batchSizes = options.batchSizes && options.batchSizes.length > 0
|
|
167
|
+
? options.batchSizes.map((size) => Math.max(1, size))
|
|
168
|
+
: [Math.max(1, options.batchSize ?? 20)];
|
|
169
|
+
const concurrency = Math.max(1, options.concurrency ?? 5);
|
|
170
|
+
const total = packageNames.length;
|
|
171
|
+
let completedCount = 0;
|
|
172
|
+
let batchStart = 0;
|
|
173
|
+
let batchIndex = 0;
|
|
174
|
+
while (batchStart < packageNames.length) {
|
|
175
|
+
const batchSize = batchSizes[Math.min(batchIndex, batchSizes.length - 1)];
|
|
176
|
+
const batchNames = packageNames.slice(batchStart, batchStart + batchSize);
|
|
177
|
+
const batchResults = new Array(batchNames.length);
|
|
178
|
+
await runWithConcurrencyLimit(batchNames, concurrency, async (packageName, itemIndex) => {
|
|
179
|
+
const data = await getFreshPackageData(packageName, currentVersions?.get(packageName));
|
|
180
|
+
packageData.set(packageName, data);
|
|
181
|
+
completedCount++;
|
|
182
|
+
batchResults[itemIndex] = {
|
|
183
|
+
packageName,
|
|
184
|
+
data,
|
|
185
|
+
completed: completedCount,
|
|
186
|
+
total,
|
|
187
|
+
batchIndex,
|
|
188
|
+
itemIndex,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
onBatchReady?.(batchResults.filter(Boolean));
|
|
192
|
+
batchStart += batchSize;
|
|
193
|
+
batchIndex++;
|
|
194
|
+
}
|
|
195
|
+
return packageData;
|
|
196
|
+
}
|
|
127
197
|
/**
|
|
128
|
-
*
|
|
198
|
+
* Retained for backward compatibility. Registry responses are fresh-by-default.
|
|
129
199
|
*/
|
|
130
200
|
function clearPackageCache() {
|
|
131
|
-
|
|
201
|
+
inFlightLookups.clear();
|
|
132
202
|
}
|
|
133
203
|
//# sourceMappingURL=npm-registry.js.map
|