inup 1.5.5 → 1.5.6
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 +24 -10
- package/dist/cli.js +20 -22
- package/dist/config/constants.js +4 -2
- package/dist/config/package-meta.js +10 -0
- package/dist/config/project-config.js +6 -1
- package/dist/core/package-detector.js +8 -4
- package/dist/core/upgrade-runner.js +7 -10
- package/dist/core/upgrader.js +6 -8
- package/dist/features/changelog/clients/github-client.js +5 -6
- package/dist/features/changelog/services/changelog-service.js +2 -4
- package/dist/features/changelog/services/package-metadata-service.js +6 -11
- package/dist/features/changelog/services/release-notes-service.js +4 -2
- package/dist/features/debug/services/performance-tracker.js +6 -8
- package/dist/index.js +1 -2
- package/dist/interactive-ui.js +13 -606
- package/dist/services/background-audit.js +3 -5
- package/dist/services/cache-manager.js +5 -7
- package/dist/services/http/inflight.js +22 -0
- package/dist/services/http/retry.js +30 -0
- package/dist/services/jsdelivr/client.js +191 -0
- package/dist/services/jsdelivr/manifest.js +136 -0
- package/dist/services/jsdelivr-registry.js +6 -392
- package/dist/services/npm-registry.js +12 -81
- package/dist/services/persistent-cache.js +8 -13
- package/dist/types/domain.js +3 -0
- package/dist/types/streaming.js +3 -0
- package/dist/types/ui.js +3 -0
- package/dist/types.js +17 -0
- package/dist/ui/controllers/package-info-modal-controller.js +22 -31
- package/dist/ui/controllers/vulnerability-audit-controller.js +17 -8
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +58 -75
- package/dist/ui/keymap.js +208 -0
- package/dist/ui/modal/package-info-sections/release-notes.js +161 -0
- package/dist/ui/modal/package-info-sections/sections.js +150 -0
- package/dist/ui/modal/package-info-sections/text.js +75 -0
- package/dist/ui/modal/package-info-sections.js +15 -369
- package/dist/ui/modal/package-info.js +1 -1
- package/dist/ui/renderer/help-modal.js +75 -0
- package/dist/ui/renderer/index.js +2 -5
- package/dist/ui/renderer/package-list/interface.js +184 -0
- package/dist/ui/renderer/package-list/rows.js +172 -0
- package/dist/ui/renderer/package-list.js +15 -455
- package/dist/ui/session/action-dispatcher.js +233 -0
- package/dist/ui/session/index.js +13 -0
- package/dist/ui/session/interactive-session.js +280 -0
- package/dist/ui/session/selection-state-builder.js +144 -0
- package/dist/ui/state/filter-manager.js +17 -6
- package/dist/ui/state/modal-manager.js +35 -0
- package/dist/ui/state/navigation-manager.js +33 -4
- package/dist/ui/state/state-manager.js +68 -3
- package/dist/ui/state/theme-manager.js +1 -0
- package/dist/ui/themes-colors.js +4 -1
- package/dist/ui/themes.js +11 -19
- package/dist/ui/utils/cursor.js +3 -4
- package/dist/ui/utils/index.js +6 -1
- package/dist/ui/utils/text.js +3 -2
- package/dist/ui/utils/version.js +60 -86
- package/dist/utils/config.js +13 -1
- package/dist/utils/filesystem/io.js +87 -0
- package/dist/utils/filesystem/paths.js +19 -0
- package/dist/utils/filesystem/scan.js +205 -0
- package/dist/utils/filesystem.js +17 -335
- package/dist/utils/version.js +31 -1
- package/package.json +6 -5
|
@@ -1,395 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.fetchExactPackageManifest =
|
|
37
|
-
|
|
38
|
-
exports.
|
|
39
|
-
exports.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const config_1 = require("../config");
|
|
43
|
-
const utils_1 = require("../utils");
|
|
44
|
-
const DEFAULT_JSDELIVR_RETRY_TIMEOUT_MS = 2000;
|
|
45
|
-
const DEFAULT_JSDELIVR_POOL_TIMEOUT_MS = 60000;
|
|
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.-]+)?$/;
|
|
48
|
-
const toPositiveInteger = (value) => {
|
|
49
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
const normalized = Math.floor(value);
|
|
53
|
-
return normalized > 0 ? normalized : null;
|
|
54
|
-
};
|
|
55
|
-
const RETRY_TIMEOUTS = (() => {
|
|
56
|
-
const configured = Array.from(new Set(config_1.JSDELIVR_RETRY_TIMEOUTS.map(toPositiveInteger).filter((value) => value !== null))).sort((a, b) => a - b);
|
|
57
|
-
return configured.length > 0 ? configured : [DEFAULT_JSDELIVR_RETRY_TIMEOUT_MS];
|
|
58
|
-
})();
|
|
59
|
-
const RETRY_DELAYS = config_1.JSDELIVR_RETRY_DELAYS.map(toPositiveInteger).filter((value) => value !== null);
|
|
60
|
-
const MAX_RETRY_AFTER_DELAY_MS = RETRY_TIMEOUTS[RETRY_TIMEOUTS.length - 1];
|
|
61
|
-
const RETRY_AFTER_HEADER = 'retry-after';
|
|
62
|
-
const parseRetryAfterMs = (value) => {
|
|
63
|
-
const trimmed = value.trim();
|
|
64
|
-
if (!trimmed) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
const seconds = Number(trimmed);
|
|
68
|
-
if (Number.isFinite(seconds)) {
|
|
69
|
-
if (seconds <= 0) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const delayMs = Math.floor(seconds * 1000);
|
|
73
|
-
return delayMs > 0 ? delayMs : null;
|
|
74
|
-
}
|
|
75
|
-
const dateMs = Date.parse(trimmed);
|
|
76
|
-
if (Number.isNaN(dateMs)) {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
const delayMs = dateMs - Date.now();
|
|
80
|
-
return delayMs > 0 ? delayMs : null;
|
|
81
|
-
};
|
|
82
|
-
const getHeaderValue = (headers, name) => {
|
|
83
|
-
if (!headers) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
const direct = headers[name];
|
|
87
|
-
if (typeof direct === 'string') {
|
|
88
|
-
return direct;
|
|
89
|
-
}
|
|
90
|
-
if (Array.isArray(direct)) {
|
|
91
|
-
return direct.find((value) => typeof value === 'string') ?? null;
|
|
92
|
-
}
|
|
93
|
-
const headerEntry = Object.entries(headers).find(([headerName]) => headerName.toLowerCase() === name);
|
|
94
|
-
if (!headerEntry) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const [, rawValue] = headerEntry;
|
|
98
|
-
if (typeof rawValue === 'string') {
|
|
99
|
-
return rawValue;
|
|
100
|
-
}
|
|
101
|
-
if (Array.isArray(rawValue)) {
|
|
102
|
-
return rawValue.find((value) => typeof value === 'string') ?? null;
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
};
|
|
106
|
-
const getRetryAfterDelay = (headers) => {
|
|
107
|
-
const retryAfterValue = getHeaderValue(headers, RETRY_AFTER_HEADER);
|
|
108
|
-
if (!retryAfterValue) {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
const parsedDelay = parseRetryAfterMs(retryAfterValue);
|
|
112
|
-
if (parsedDelay === null) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
return Math.min(parsedDelay, MAX_RETRY_AFTER_DELAY_MS);
|
|
116
|
-
};
|
|
117
|
-
const getRetryDelay = (attempt, headers) => {
|
|
118
|
-
const configuredDelay = RETRY_DELAYS.length === 0 ? 0 : RETRY_DELAYS[Math.min(attempt, RETRY_DELAYS.length - 1)];
|
|
119
|
-
const retryAfterDelay = getRetryAfterDelay(headers);
|
|
120
|
-
return retryAfterDelay === null ? configuredDelay : Math.max(configuredDelay, retryAfterDelay);
|
|
121
|
-
};
|
|
122
|
-
// Keep connection setup bounded by retry budget so fallback stays responsive.
|
|
123
|
-
const JSDELIVR_CONNECT_TIMEOUT_MS = Math.max(RETRY_TIMEOUTS[0], MIN_JSDELIVR_CONNECT_TIMEOUT_MS);
|
|
124
|
-
const JSDELIVR_POOL_TIMEOUT_MS = toPositiveInteger(config_1.JSDELIVR_POOL_TIMEOUT) ?? DEFAULT_JSDELIVR_POOL_TIMEOUT_MS;
|
|
125
|
-
const JSDELIVR_CONNECTIONS = toPositiveInteger(config_1.MAX_CONCURRENT_REQUESTS) ?? 1;
|
|
126
|
-
// Create a persistent connection pool for jsDelivr CDN with optimal settings
|
|
127
|
-
// This enables connection reuse and HTTP/1.1 keep-alive for blazing fast requests
|
|
128
|
-
const jsdelivrPool = new undici_1.Pool('https://cdn.jsdelivr.net', {
|
|
129
|
-
connections: JSDELIVR_CONNECTIONS,
|
|
130
|
-
pipelining: 10,
|
|
131
|
-
keepAliveTimeout: JSDELIVR_POOL_TIMEOUT_MS,
|
|
132
|
-
keepAliveMaxTimeout: JSDELIVR_POOL_TIMEOUT_MS,
|
|
133
|
-
connectTimeout: JSDELIVR_CONNECT_TIMEOUT_MS,
|
|
134
|
-
});
|
|
135
|
-
const isTimeoutError = (error) => {
|
|
136
|
-
if (!(error instanceof Error)) {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
const maybeCode = error.code;
|
|
140
|
-
const message = error.message.toLowerCase();
|
|
141
|
-
return (maybeCode === 'UND_ERR_HEADERS_TIMEOUT' ||
|
|
142
|
-
maybeCode === 'UND_ERR_BODY_TIMEOUT' ||
|
|
143
|
-
maybeCode === 'UND_ERR_CONNECT_TIMEOUT' ||
|
|
144
|
-
error.name === 'HeadersTimeoutError' ||
|
|
145
|
-
error.name === 'BodyTimeoutError' ||
|
|
146
|
-
error.name === 'ConnectTimeoutError' ||
|
|
147
|
-
message.includes('timeout'));
|
|
148
|
-
};
|
|
149
|
-
const isTransientNetworkError = (error) => {
|
|
150
|
-
if (!(error instanceof Error)) {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
const maybeCode = error.code;
|
|
154
|
-
return (maybeCode === 'UND_ERR_SOCKET' ||
|
|
155
|
-
maybeCode === 'ENOTFOUND' ||
|
|
156
|
-
maybeCode === 'EAI_AGAIN' ||
|
|
157
|
-
maybeCode === 'ECONNRESET' ||
|
|
158
|
-
maybeCode === 'ECONNREFUSED' ||
|
|
159
|
-
maybeCode === 'ETIMEDOUT' ||
|
|
160
|
-
maybeCode === 'EPIPE');
|
|
161
|
-
};
|
|
162
|
-
const isRetryableStatus = (statusCode) => statusCode === 408 || statusCode === 429 || statusCode >= 500;
|
|
163
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
164
|
-
const consumeBodySafely = async (body) => {
|
|
165
|
-
try {
|
|
166
|
-
await body.text();
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
// Ignore body read errors on non-200 responses because request will be retried/fallback.
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
const extractMajorVersion = (version) => {
|
|
173
|
-
if (!version) {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
const coerced = semver.coerce(version);
|
|
177
|
-
if (!coerced) {
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
return semver.major(coerced).toString();
|
|
181
|
-
};
|
|
182
|
-
const toComparableVersion = (version) => {
|
|
183
|
-
const validVersion = semver.valid(version);
|
|
184
|
-
if (validVersion) {
|
|
185
|
-
return validVersion;
|
|
186
|
-
}
|
|
187
|
-
const coerced = semver.coerce(version);
|
|
188
|
-
return coerced ? coerced.version : null;
|
|
189
|
-
};
|
|
190
|
-
const versionIdentity = (version) => {
|
|
191
|
-
const comparable = toComparableVersion(version);
|
|
192
|
-
return comparable ?? `raw:${version}`;
|
|
193
|
-
};
|
|
194
|
-
const sortVersionsDescending = (versions) => {
|
|
195
|
-
const uniqueVersions = [];
|
|
196
|
-
const seenVersions = new Set();
|
|
197
|
-
for (const version of versions) {
|
|
198
|
-
const identity = versionIdentity(version);
|
|
199
|
-
if (!seenVersions.has(identity)) {
|
|
200
|
-
seenVersions.add(identity);
|
|
201
|
-
uniqueVersions.push(version);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return uniqueVersions.sort((a, b) => {
|
|
205
|
-
const comparableA = toComparableVersion(a);
|
|
206
|
-
const comparableB = toComparableVersion(b);
|
|
207
|
-
if (comparableA && comparableB) {
|
|
208
|
-
return semver.rcompare(comparableA, comparableB);
|
|
209
|
-
}
|
|
210
|
-
if (comparableA) {
|
|
211
|
-
return -1;
|
|
212
|
-
}
|
|
213
|
-
if (comparableB) {
|
|
214
|
-
return 1;
|
|
215
|
-
}
|
|
216
|
-
return b.localeCompare(a);
|
|
217
|
-
});
|
|
218
|
-
};
|
|
219
|
-
const isExpectedTransientError = (error) => isTimeoutError(error) || isTransientNetworkError(error);
|
|
220
|
-
/**
|
|
221
|
-
* Fetches a package.json manifest from jsDelivr for a version tag.
|
|
222
|
-
*/
|
|
223
|
-
async function fetchPackageManifestFromJsdelivr(packageName, versionTag) {
|
|
224
|
-
const url = `${config_1.JSDELIVR_CDN_URL}/${encodeURIComponent(packageName)}@${versionTag}/package.json`;
|
|
225
|
-
for (let attempt = 0; attempt < RETRY_TIMEOUTS.length; attempt++) {
|
|
226
|
-
const timeout = RETRY_TIMEOUTS[attempt];
|
|
227
|
-
const tReq = Date.now();
|
|
228
|
-
try {
|
|
229
|
-
const { statusCode, headers, body } = await (0, undici_1.request)(url, {
|
|
230
|
-
dispatcher: jsdelivrPool,
|
|
231
|
-
method: 'GET',
|
|
232
|
-
headers: {
|
|
233
|
-
accept: 'application/json',
|
|
234
|
-
},
|
|
235
|
-
headersTimeout: timeout,
|
|
236
|
-
bodyTimeout: timeout,
|
|
237
|
-
});
|
|
238
|
-
if (statusCode !== 200) {
|
|
239
|
-
// Consume body to prevent memory leaks
|
|
240
|
-
await consumeBodySafely(body);
|
|
241
|
-
if (isRetryableStatus(statusCode) && attempt < RETRY_TIMEOUTS.length - 1) {
|
|
242
|
-
const delay = getRetryDelay(attempt, headers);
|
|
243
|
-
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} HTTP ${statusCode}, retry ${attempt + 1} in ${delay}ms`);
|
|
244
|
-
if (delay > 0) {
|
|
245
|
-
await sleep(delay);
|
|
246
|
-
}
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} HTTP ${statusCode}, no more retries`);
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
const text = await body.text();
|
|
253
|
-
const data = JSON.parse(text);
|
|
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;
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
if ((isTimeoutError(error) || isTransientNetworkError(error)) &&
|
|
262
|
-
attempt < RETRY_TIMEOUTS.length - 1) {
|
|
263
|
-
const delay = getRetryDelay(attempt);
|
|
264
|
-
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} transient error on attempt ${attempt + 1}, retry in ${delay}ms`, error);
|
|
265
|
-
if (delay > 0) {
|
|
266
|
-
await sleep(delay);
|
|
267
|
-
}
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
if (!isExpectedTransientError(error)) {
|
|
271
|
-
// Unexpected errors are logged for observability.
|
|
272
|
-
console.error(`jsDelivr fetch failed for ${packageName}@${versionTag} on attempt ${attempt + 1}/${RETRY_TIMEOUTS.length}`, error);
|
|
273
|
-
utils_1.debugLog.error('jsdelivr', `unexpected error for ${packageName}@${versionTag} attempt ${attempt + 1}`, error);
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
utils_1.debugLog.warn('jsdelivr', `${packageName}@${versionTag} exhausted retries`, error);
|
|
277
|
-
}
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
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) {
|
|
332
|
-
const packageData = new Map();
|
|
333
|
-
if (packageNames.length === 0) {
|
|
334
|
-
return packageData;
|
|
335
|
-
}
|
|
336
|
-
const total = packageNames.length;
|
|
337
|
-
let completedCount = 0;
|
|
338
|
-
const inFlightLookups = new Map();
|
|
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) {
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
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
|
-
};
|
|
360
|
-
};
|
|
361
|
-
const getPackageData = async (packageName, currentVersion) => {
|
|
362
|
-
const inFlight = inFlightLookups.get(packageName);
|
|
363
|
-
if (inFlight) {
|
|
364
|
-
return await inFlight;
|
|
365
|
-
}
|
|
366
|
-
const lookupPromise = fetchPackageData(packageName, currentVersion).finally(() => {
|
|
367
|
-
inFlightLookups.delete(packageName);
|
|
368
|
-
});
|
|
369
|
-
inFlightLookups.set(packageName, lookupPromise);
|
|
370
|
-
return await lookupPromise;
|
|
371
|
-
};
|
|
372
|
-
await Promise.all(packageNames.map(async (packageName) => {
|
|
373
|
-
try {
|
|
374
|
-
const result = await getPackageData(packageName, currentVersions?.get(packageName));
|
|
375
|
-
if (result) {
|
|
376
|
-
packageData.set(packageName, result);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
finally {
|
|
380
|
-
completedCount++;
|
|
381
|
-
onProgress?.(packageName, completedCount, total);
|
|
382
|
-
}
|
|
383
|
-
}));
|
|
384
|
-
return packageData;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Close the jsDelivr connection pool (useful for graceful shutdown)
|
|
388
|
-
*/
|
|
389
|
-
async function closeJsdelivrPool() {
|
|
390
|
-
await jsdelivrPool.close();
|
|
391
|
-
}
|
|
392
|
-
function clearExactManifestCache() {
|
|
393
|
-
inFlightManifests.clear();
|
|
394
|
-
}
|
|
3
|
+
exports.clearExactManifestCache = exports.closeJsdelivrPool = exports.getAllPackageDataFromJsdelivr = exports.fetchExactPackageManifest = void 0;
|
|
4
|
+
var manifest_1 = require("./jsdelivr/manifest");
|
|
5
|
+
Object.defineProperty(exports, "fetchExactPackageManifest", { enumerable: true, get: function () { return manifest_1.fetchExactPackageManifest; } });
|
|
6
|
+
Object.defineProperty(exports, "getAllPackageDataFromJsdelivr", { enumerable: true, get: function () { return manifest_1.getAllPackageDataFromJsdelivr; } });
|
|
7
|
+
Object.defineProperty(exports, "closeJsdelivrPool", { enumerable: true, get: function () { return manifest_1.closeJsdelivrPool; } });
|
|
8
|
+
Object.defineProperty(exports, "clearExactManifestCache", { enumerable: true, get: function () { return manifest_1.clearExactManifestCache; } });
|
|
395
9
|
//# sourceMappingURL=jsdelivr-registry.js.map
|
|
@@ -1,42 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.fetchPackageVersions = fetchPackageVersions;
|
|
37
4
|
exports.clearPackageCache = clearPackageCache;
|
|
38
5
|
exports.closeRegistryPool = closeRegistryPool;
|
|
39
|
-
const semver = __importStar(require("semver"));
|
|
40
6
|
const undici_1 = require("undici");
|
|
41
7
|
const node_zlib_1 = require("node:zlib");
|
|
42
8
|
const node_util_1 = require("node:util");
|
|
@@ -45,7 +11,10 @@ const inflateAsync = (0, node_util_1.promisify)(node_zlib_1.inflate);
|
|
|
45
11
|
const brotliDecompressAsync = (0, node_util_1.promisify)(node_zlib_1.brotliDecompress);
|
|
46
12
|
const config_1 = require("../config");
|
|
47
13
|
const jsdelivr_registry_1 = require("./jsdelivr-registry");
|
|
48
|
-
const
|
|
14
|
+
const version_1 = require("../utils/version");
|
|
15
|
+
const retry_1 = require("./http/retry");
|
|
16
|
+
const inflight_1 = require("./http/inflight");
|
|
17
|
+
const inFlightLookups = new inflight_1.InflightMap();
|
|
49
18
|
const registryOrigin = new URL(config_1.NPM_REGISTRY_URL).origin;
|
|
50
19
|
const registryPathPrefix = new URL(config_1.NPM_REGISTRY_URL).pathname.replace(/\/$/, '');
|
|
51
20
|
// Few connections + many requests per connection = maximum keep-alive reuse.
|
|
@@ -54,60 +23,22 @@ const registryPathPrefix = new URL(config_1.NPM_REGISTRY_URL).pathname.replace(/
|
|
|
54
23
|
const registryPool = new undici_1.Pool(registryOrigin, {
|
|
55
24
|
connections: 6,
|
|
56
25
|
pipelining: 1,
|
|
57
|
-
keepAliveTimeout:
|
|
58
|
-
keepAliveMaxTimeout:
|
|
26
|
+
keepAliveTimeout: 30_000,
|
|
27
|
+
keepAliveMaxTimeout: 600_000,
|
|
59
28
|
headersTimeout: 0,
|
|
60
29
|
bodyTimeout: 0,
|
|
61
|
-
connectTimeout:
|
|
30
|
+
connectTimeout: 15_000,
|
|
62
31
|
allowH2: false,
|
|
63
32
|
});
|
|
64
33
|
const MAX_REGISTRY_ATTEMPTS = 3;
|
|
65
34
|
const RETRY_BACKOFF_MS = [500, 1500, 3000];
|
|
66
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
67
|
-
const isRetryableStatus = (statusCode) => statusCode === 408 || statusCode === 429 || statusCode >= 500;
|
|
68
|
-
const isTransientNetworkError = (error) => {
|
|
69
|
-
if (!(error instanceof Error)) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
const maybeCode = error.code;
|
|
73
|
-
return (error.name === 'AbortError' ||
|
|
74
|
-
error.name === 'HeadersTimeoutError' ||
|
|
75
|
-
error.name === 'BodyTimeoutError' ||
|
|
76
|
-
error.name === 'ConnectTimeoutError' ||
|
|
77
|
-
error.name === 'SocketError' ||
|
|
78
|
-
maybeCode === 'UND_ERR_HEADERS_TIMEOUT' ||
|
|
79
|
-
maybeCode === 'UND_ERR_BODY_TIMEOUT' ||
|
|
80
|
-
maybeCode === 'UND_ERR_CONNECT_TIMEOUT' ||
|
|
81
|
-
maybeCode === 'UND_ERR_SOCKET' ||
|
|
82
|
-
maybeCode === 'ENOTFOUND' ||
|
|
83
|
-
maybeCode === 'EAI_AGAIN' ||
|
|
84
|
-
maybeCode === 'ECONNRESET' ||
|
|
85
|
-
maybeCode === 'ECONNREFUSED' ||
|
|
86
|
-
maybeCode === 'ETIMEDOUT' ||
|
|
87
|
-
maybeCode === 'EPIPE');
|
|
88
|
-
};
|
|
89
35
|
const fetchFromJsdelivrFallback = async (packageName, currentVersion) => {
|
|
90
36
|
const jsdelivrData = await (0, jsdelivr_registry_1.getAllPackageDataFromJsdelivr)([packageName], currentVersion ? new Map([[packageName, currentVersion]]) : undefined);
|
|
91
37
|
return jsdelivrData.get(packageName) ?? { latestVersion: 'unknown', allVersions: [] };
|
|
92
38
|
};
|
|
93
39
|
async function getFreshPackageData(packageName, currentVersion) {
|
|
94
40
|
const cacheKey = `${packageName}@${currentVersion ?? ''}`;
|
|
95
|
-
|
|
96
|
-
if (inFlight) {
|
|
97
|
-
return await inFlight;
|
|
98
|
-
}
|
|
99
|
-
const lookupPromise = fetchPackageFromRegistryWithFallback(packageName, currentVersion).finally(() => {
|
|
100
|
-
inFlightLookups.delete(cacheKey);
|
|
101
|
-
});
|
|
102
|
-
inFlightLookups.set(cacheKey, lookupPromise);
|
|
103
|
-
return await lookupPromise;
|
|
104
|
-
}
|
|
105
|
-
function parseVersions(raw) {
|
|
106
|
-
const data = JSON.parse(raw);
|
|
107
|
-
const allVersions = Object.keys(data.versions || {}).filter((v) => /^[0-9]+\.[0-9]+\.[0-9]+$/.test(v));
|
|
108
|
-
const sortedVersions = allVersions.sort(semver.rcompare);
|
|
109
|
-
const latestVersion = sortedVersions.length > 0 ? sortedVersions[0] : 'unknown';
|
|
110
|
-
return { latestVersion, allVersions };
|
|
41
|
+
return inFlightLookups.dedupe(cacheKey, () => fetchPackageFromRegistryWithFallback(packageName, currentVersion));
|
|
111
42
|
}
|
|
112
43
|
const encodeRegistryPath = (packageName) => {
|
|
113
44
|
const encodedName = packageName.startsWith('@')
|
|
@@ -130,7 +61,7 @@ async function attemptRegistryFetch(path) {
|
|
|
130
61
|
});
|
|
131
62
|
if (statusCode < 200 || statusCode >= 300) {
|
|
132
63
|
await body.dump().catch(() => undefined);
|
|
133
|
-
if (isRetryableStatus(statusCode)) {
|
|
64
|
+
if ((0, retry_1.isRetryableStatus)(statusCode)) {
|
|
134
65
|
return { kind: 'retryable' };
|
|
135
66
|
}
|
|
136
67
|
return { kind: 'not-found' };
|
|
@@ -153,10 +84,10 @@ async function attemptRegistryFetch(path) {
|
|
|
153
84
|
else {
|
|
154
85
|
decoded = raw;
|
|
155
86
|
}
|
|
156
|
-
return { kind: 'success', data: parseVersions(decoded.toString('utf8')) };
|
|
87
|
+
return { kind: 'success', data: (0, version_1.parseVersions)(decoded.toString('utf8')) };
|
|
157
88
|
}
|
|
158
89
|
catch (error) {
|
|
159
|
-
if (isTransientNetworkError(error)) {
|
|
90
|
+
if ((0, retry_1.isTransientNetworkError)(error)) {
|
|
160
91
|
return { kind: 'transient' };
|
|
161
92
|
}
|
|
162
93
|
// Unknown error: treat as transient so we try the fallback rather than
|
|
@@ -174,7 +105,7 @@ async function fetchFromRegistryWithRetries(path) {
|
|
|
174
105
|
lastOutcome = outcome;
|
|
175
106
|
if (attempt < MAX_REGISTRY_ATTEMPTS - 1) {
|
|
176
107
|
const backoff = RETRY_BACKOFF_MS[Math.min(attempt, RETRY_BACKOFF_MS.length - 1)];
|
|
177
|
-
await sleep(backoff);
|
|
108
|
+
await (0, retry_1.sleep)(backoff);
|
|
178
109
|
}
|
|
179
110
|
}
|
|
180
111
|
return lastOutcome;
|
|
@@ -7,21 +7,22 @@ exports.persistentCache = void 0;
|
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const path_1 = require("path");
|
|
9
9
|
const env_paths_1 = __importDefault(require("env-paths"));
|
|
10
|
-
|
|
11
|
-
const DISK_CACHE_TTL = 24 * 60 * 60 * 1000;
|
|
10
|
+
const config_1 = require("../config");
|
|
12
11
|
// Maximum cache size (number of packages)
|
|
13
12
|
const MAX_CACHE_ENTRIES = 5000;
|
|
14
13
|
// Cache file format version (increment when structure changes)
|
|
15
|
-
const CACHE_VERSION =
|
|
14
|
+
const CACHE_VERSION = 2;
|
|
16
15
|
/**
|
|
17
16
|
* Persistent cache manager for package registry data.
|
|
18
17
|
* Stores cache on disk for fast repeated runs across CLI invocations.
|
|
19
18
|
*/
|
|
20
19
|
class PersistentCacheManager {
|
|
20
|
+
cacheDir;
|
|
21
|
+
indexPath;
|
|
22
|
+
index = null;
|
|
23
|
+
dirty = false;
|
|
21
24
|
constructor() {
|
|
22
|
-
|
|
23
|
-
this.dirty = false;
|
|
24
|
-
const paths = (0, env_paths_1.default)('inup');
|
|
25
|
+
const paths = (0, env_paths_1.default)(config_1.PACKAGE_NAME);
|
|
25
26
|
this.cacheDir = (0, path_1.join)(paths.cache, 'registry');
|
|
26
27
|
this.indexPath = (0, path_1.join)(this.cacheDir, 'index.json');
|
|
27
28
|
}
|
|
@@ -93,13 +94,6 @@ class PersistentCacheManager {
|
|
|
93
94
|
if (!entry) {
|
|
94
95
|
return null;
|
|
95
96
|
}
|
|
96
|
-
// Check TTL
|
|
97
|
-
if (Date.now() - entry.timestamp > DISK_CACHE_TTL) {
|
|
98
|
-
// Expired, remove from index
|
|
99
|
-
delete index.entries[packageName];
|
|
100
|
-
this.dirty = true;
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
97
|
// Read the actual cache file
|
|
104
98
|
try {
|
|
105
99
|
const filePath = (0, path_1.join)(this.cacheDir, entry.file);
|
|
@@ -113,6 +107,7 @@ class PersistentCacheManager {
|
|
|
113
107
|
return {
|
|
114
108
|
latestVersion: cached.latestVersion,
|
|
115
109
|
allVersions: cached.allVersions,
|
|
110
|
+
timestamp: entry.timestamp,
|
|
116
111
|
};
|
|
117
112
|
}
|
|
118
113
|
catch {
|
package/dist/types/ui.js
ADDED
package/dist/types.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types/domain"), exports);
|
|
18
|
+
__exportStar(require("./types/ui"), exports);
|
|
19
|
+
__exportStar(require("./types/streaming"), exports);
|
|
3
20
|
//# sourceMappingURL=types.js.map
|