inup 1.4.4 → 1.4.5
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/dist/cli.js +18 -2
- package/dist/config/index.js +1 -0
- package/dist/config/project-config.js +87 -0
- package/dist/core/package-detector.js +14 -4
- package/dist/services/index.js +1 -0
- package/dist/services/jsdelivr-registry.js +33 -6
- package/dist/services/npm-registry.js +20 -5
- package/dist/services/persistent-cache.js +242 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ const fs_1 = require("fs");
|
|
|
10
10
|
const path_1 = require("path");
|
|
11
11
|
const index_1 = require("./index");
|
|
12
12
|
const services_1 = require("./services");
|
|
13
|
+
const config_1 = require("./config");
|
|
13
14
|
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf-8'));
|
|
14
15
|
const program = new commander_1.Command();
|
|
15
16
|
program
|
|
@@ -18,17 +19,31 @@ program
|
|
|
18
19
|
.version(packageJson.version)
|
|
19
20
|
.option('-d, --dir <directory>', 'specify directory to run in', process.cwd())
|
|
20
21
|
.option('-e, --exclude <patterns>', 'exclude paths matching regex patterns (comma-separated)', '')
|
|
22
|
+
.option('-i, --ignore <packages>', 'ignore packages (comma-separated, supports glob patterns like @babel/*)')
|
|
21
23
|
.option('--package-manager <name>', 'manually specify package manager (npm, yarn, pnpm, bun)')
|
|
22
24
|
.action(async (options) => {
|
|
23
25
|
console.log(chalk_1.default.bold.blue(`🚀 `) + chalk_1.default.bold.red(`i`) + chalk_1.default.bold.yellow(`n`) + chalk_1.default.bold.blue(`u`) + chalk_1.default.bold.magenta(`p`) + `\n`);
|
|
24
26
|
// Check for updates in the background (non-blocking)
|
|
25
27
|
const updateCheckPromise = (0, services_1.checkForUpdateAsync)('inup', packageJson.version);
|
|
26
|
-
const
|
|
28
|
+
const cwd = (0, path_1.resolve)(options.dir);
|
|
29
|
+
// Load project config from .inuprc
|
|
30
|
+
const projectConfig = (0, config_1.loadProjectConfig)(cwd);
|
|
31
|
+
// Merge CLI exclude patterns with config
|
|
32
|
+
const cliExcludePatterns = options.exclude
|
|
27
33
|
? options.exclude
|
|
28
34
|
.split(',')
|
|
29
35
|
.map((p) => p.trim())
|
|
30
36
|
.filter(Boolean)
|
|
31
37
|
: [];
|
|
38
|
+
const excludePatterns = [...cliExcludePatterns, ...(projectConfig.exclude || [])];
|
|
39
|
+
// Merge CLI ignore patterns with config (CLI takes precedence / adds to config)
|
|
40
|
+
const cliIgnorePatterns = options.ignore
|
|
41
|
+
? options.ignore
|
|
42
|
+
.split(',')
|
|
43
|
+
.map((p) => p.trim())
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
: [];
|
|
46
|
+
const ignorePackages = [...new Set([...cliIgnorePatterns, ...(projectConfig.ignore || [])])];
|
|
32
47
|
// Validate package manager if provided
|
|
33
48
|
let packageManager;
|
|
34
49
|
if (options.packageManager) {
|
|
@@ -41,8 +56,9 @@ program
|
|
|
41
56
|
packageManager = options.packageManager;
|
|
42
57
|
}
|
|
43
58
|
const upgrader = new index_1.UpgradeRunner({
|
|
44
|
-
cwd
|
|
59
|
+
cwd,
|
|
45
60
|
excludePatterns,
|
|
61
|
+
ignorePackages,
|
|
46
62
|
packageManager,
|
|
47
63
|
});
|
|
48
64
|
await upgrader.run();
|
package/dist/config/index.js
CHANGED
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./constants"), exports);
|
|
18
|
+
__exportStar(require("./project-config"), exports);
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadProjectConfig = loadProjectConfig;
|
|
4
|
+
exports.isPackageIgnored = isPackageIgnored;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const CONFIG_FILES = ['.inuprc', '.inuprc.json', 'inup.config.json'];
|
|
8
|
+
/**
|
|
9
|
+
* Load project configuration from .inuprc, .inuprc.json, or inup.config.json
|
|
10
|
+
* Searches in the specified directory and parent directories up to root
|
|
11
|
+
*/
|
|
12
|
+
function loadProjectConfig(cwd) {
|
|
13
|
+
let currentDir = cwd;
|
|
14
|
+
while (currentDir !== '/') {
|
|
15
|
+
for (const configFile of CONFIG_FILES) {
|
|
16
|
+
const configPath = (0, path_1.join)(currentDir, configFile);
|
|
17
|
+
if ((0, fs_1.existsSync)(configPath)) {
|
|
18
|
+
try {
|
|
19
|
+
const content = (0, fs_1.readFileSync)(configPath, 'utf-8');
|
|
20
|
+
const config = JSON.parse(content);
|
|
21
|
+
return normalizeConfig(config);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
// Invalid JSON or read error - continue searching
|
|
25
|
+
console.warn(`Warning: Failed to parse ${configPath}: ${error}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Move to parent directory
|
|
30
|
+
const parentDir = (0, path_1.join)(currentDir, '..');
|
|
31
|
+
if (parentDir === currentDir)
|
|
32
|
+
break;
|
|
33
|
+
currentDir = parentDir;
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Normalize and validate the config
|
|
39
|
+
*/
|
|
40
|
+
function normalizeConfig(config) {
|
|
41
|
+
const normalized = {};
|
|
42
|
+
if (config.ignore) {
|
|
43
|
+
if (Array.isArray(config.ignore)) {
|
|
44
|
+
normalized.ignore = config.ignore.filter((item) => typeof item === 'string');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (config.exclude) {
|
|
48
|
+
if (Array.isArray(config.exclude)) {
|
|
49
|
+
normalized.exclude = config.exclude.filter((item) => typeof item === 'string');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a package name matches any of the ignore patterns
|
|
56
|
+
* Supports exact matches and glob patterns (* and ?)
|
|
57
|
+
*/
|
|
58
|
+
function isPackageIgnored(packageName, ignorePatterns) {
|
|
59
|
+
for (const pattern of ignorePatterns) {
|
|
60
|
+
if (matchesPattern(packageName, pattern)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Match a package name against a pattern
|
|
68
|
+
* Supports:
|
|
69
|
+
* - Exact match: "lodash"
|
|
70
|
+
* - Wildcard: "*" matches any sequence of characters
|
|
71
|
+
* - Single char wildcard: "?" matches single character
|
|
72
|
+
* - Scoped packages: "@babel/*" matches all @babel packages
|
|
73
|
+
*/
|
|
74
|
+
function matchesPattern(name, pattern) {
|
|
75
|
+
// Exact match
|
|
76
|
+
if (pattern === name) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
// Convert glob pattern to regex
|
|
80
|
+
const regexPattern = pattern
|
|
81
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except * and ?
|
|
82
|
+
.replace(/\*/g, '.*') // * matches any sequence
|
|
83
|
+
.replace(/\?/g, '.'); // ? matches single char
|
|
84
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
85
|
+
return regex.test(name);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=project-config.js.map
|
|
@@ -44,6 +44,7 @@ class PackageDetector {
|
|
|
44
44
|
this.packageJson = null;
|
|
45
45
|
this.cwd = options?.cwd || process.cwd();
|
|
46
46
|
this.excludePatterns = options?.excludePatterns || [];
|
|
47
|
+
this.ignorePackages = options?.ignorePackages || [];
|
|
47
48
|
this.packageJsonPath = (0, utils_1.findPackageJson)(this.cwd);
|
|
48
49
|
if (this.packageJsonPath) {
|
|
49
50
|
this.packageJson = (0, utils_1.readPackageJson)(this.packageJsonPath);
|
|
@@ -67,15 +68,24 @@ class PackageDetector {
|
|
|
67
68
|
includePeerDeps: true,
|
|
68
69
|
includeOptionalDeps: true,
|
|
69
70
|
});
|
|
70
|
-
// Step 3: Get unique package names while filtering out workspace references
|
|
71
|
+
// Step 3: Get unique package names while filtering out workspace references and ignored packages
|
|
71
72
|
this.showProgress('🔍 Identifying unique packages...');
|
|
72
73
|
const uniquePackageNames = new Set();
|
|
73
74
|
const allDeps = [];
|
|
75
|
+
let ignoredCount = 0;
|
|
74
76
|
for (const dep of allDepsRaw) {
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
uniquePackageNames.add(dep.name);
|
|
77
|
+
if (this.isWorkspaceReference(dep.version)) {
|
|
78
|
+
continue;
|
|
78
79
|
}
|
|
80
|
+
if (this.ignorePackages.length > 0 && (0, config_1.isPackageIgnored)(dep.name, this.ignorePackages)) {
|
|
81
|
+
ignoredCount++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
allDeps.push(dep);
|
|
85
|
+
uniquePackageNames.add(dep.name);
|
|
86
|
+
}
|
|
87
|
+
if (ignoredCount > 0) {
|
|
88
|
+
this.showProgress(`🔍 Skipped ${ignoredCount} ignored package(s)`);
|
|
79
89
|
}
|
|
80
90
|
const packageNames = Array.from(uniquePackageNames);
|
|
81
91
|
// Step 4: Fetch all package data in one call per package
|
package/dist/services/index.js
CHANGED
|
@@ -21,4 +21,5 @@ __exportStar(require("./npm-registry"), exports);
|
|
|
21
21
|
__exportStar(require("./jsdelivr-registry"), exports);
|
|
22
22
|
__exportStar(require("./changelog-fetcher"), exports);
|
|
23
23
|
__exportStar(require("./version-checker"), exports);
|
|
24
|
+
__exportStar(require("./persistent-cache"), exports);
|
|
24
25
|
//# sourceMappingURL=index.js.map
|
|
@@ -40,6 +40,7 @@ const undici_1 = require("undici");
|
|
|
40
40
|
const semver = __importStar(require("semver"));
|
|
41
41
|
const config_1 = require("../config");
|
|
42
42
|
const npm_registry_1 = require("./npm-registry");
|
|
43
|
+
const persistent_cache_1 = require("./persistent-cache");
|
|
43
44
|
// Create a persistent connection pool for jsDelivr CDN with optimal settings
|
|
44
45
|
// This enables connection reuse and HTTP/1.1 keep-alive for blazing fast requests
|
|
45
46
|
const jsdelivrPool = new undici_1.Pool('https://cdn.jsdelivr.net', {
|
|
@@ -135,15 +136,31 @@ async function getAllPackageDataFromJsdelivr(packageNames, currentVersions, onPr
|
|
|
135
136
|
// Process individual package fetch with immediate npm fallback on failure
|
|
136
137
|
const fetchPackageWithFallback = async (packageName) => {
|
|
137
138
|
const currentVersion = currentVersions?.get(packageName);
|
|
138
|
-
// Try to get from cache first
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
141
|
-
packageData.set(packageName,
|
|
139
|
+
// Try to get from in-memory cache first (fastest)
|
|
140
|
+
const memoryCached = packageCache.get(packageName);
|
|
141
|
+
if (memoryCached && Date.now() - memoryCached.timestamp < config_1.CACHE_TTL) {
|
|
142
|
+
packageData.set(packageName, memoryCached.data);
|
|
142
143
|
completedCount++;
|
|
143
144
|
if (onProgress) {
|
|
144
145
|
onProgress(packageName, completedCount, total);
|
|
145
146
|
}
|
|
146
|
-
addToBatch(packageName,
|
|
147
|
+
addToBatch(packageName, memoryCached.data);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Try persistent disk cache (fast, survives restarts)
|
|
151
|
+
const diskCached = persistent_cache_1.persistentCache.get(packageName);
|
|
152
|
+
if (diskCached) {
|
|
153
|
+
// Also populate in-memory cache for subsequent accesses
|
|
154
|
+
packageCache.set(packageName, {
|
|
155
|
+
data: diskCached,
|
|
156
|
+
timestamp: Date.now(),
|
|
157
|
+
});
|
|
158
|
+
packageData.set(packageName, diskCached);
|
|
159
|
+
completedCount++;
|
|
160
|
+
if (onProgress) {
|
|
161
|
+
onProgress(packageName, completedCount, total);
|
|
162
|
+
}
|
|
163
|
+
addToBatch(packageName, diskCached);
|
|
147
164
|
return;
|
|
148
165
|
}
|
|
149
166
|
try {
|
|
@@ -168,10 +185,13 @@ async function getAllPackageDataFromJsdelivr(packageNames, currentVersions, onPr
|
|
|
168
185
|
const result = npmData.get(packageName);
|
|
169
186
|
if (result) {
|
|
170
187
|
packageData.set(packageName, result);
|
|
188
|
+
// Cache in memory
|
|
171
189
|
packageCache.set(packageName, {
|
|
172
190
|
data: result,
|
|
173
191
|
timestamp: Date.now(),
|
|
174
192
|
});
|
|
193
|
+
// Cache to disk for persistence
|
|
194
|
+
persistent_cache_1.persistentCache.set(packageName, result);
|
|
175
195
|
addToBatch(packageName, result);
|
|
176
196
|
}
|
|
177
197
|
completedCount++;
|
|
@@ -190,11 +210,13 @@ async function getAllPackageDataFromJsdelivr(packageNames, currentVersions, onPr
|
|
|
190
210
|
latestVersion,
|
|
191
211
|
allVersions: allVersions.sort(semver.rcompare),
|
|
192
212
|
};
|
|
193
|
-
// Cache the result
|
|
213
|
+
// Cache the result in memory
|
|
194
214
|
packageCache.set(packageName, {
|
|
195
215
|
data: result,
|
|
196
216
|
timestamp: Date.now(),
|
|
197
217
|
});
|
|
218
|
+
// Cache to disk for persistence
|
|
219
|
+
persistent_cache_1.persistentCache.set(packageName, result);
|
|
198
220
|
packageData.set(packageName, result);
|
|
199
221
|
completedCount++;
|
|
200
222
|
if (onProgress) {
|
|
@@ -209,10 +231,13 @@ async function getAllPackageDataFromJsdelivr(packageNames, currentVersions, onPr
|
|
|
209
231
|
const result = npmData.get(packageName);
|
|
210
232
|
if (result) {
|
|
211
233
|
packageData.set(packageName, result);
|
|
234
|
+
// Cache in memory
|
|
212
235
|
packageCache.set(packageName, {
|
|
213
236
|
data: result,
|
|
214
237
|
timestamp: Date.now(),
|
|
215
238
|
});
|
|
239
|
+
// Cache to disk for persistence
|
|
240
|
+
persistent_cache_1.persistentCache.set(packageName, result);
|
|
216
241
|
addToBatch(packageName, result);
|
|
217
242
|
}
|
|
218
243
|
}
|
|
@@ -229,6 +254,8 @@ async function getAllPackageDataFromJsdelivr(packageNames, currentVersions, onPr
|
|
|
229
254
|
await Promise.all(packageNames.map(fetchPackageWithFallback));
|
|
230
255
|
// Flush any remaining batch items
|
|
231
256
|
flushBatch();
|
|
257
|
+
// Flush persistent cache to disk
|
|
258
|
+
persistent_cache_1.persistentCache.flush();
|
|
232
259
|
// Clear the progress line and show completion time if no custom progress handler
|
|
233
260
|
if (!onProgress) {
|
|
234
261
|
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
@@ -37,16 +37,27 @@ exports.getAllPackageData = getAllPackageData;
|
|
|
37
37
|
exports.clearPackageCache = clearPackageCache;
|
|
38
38
|
const semver = __importStar(require("semver"));
|
|
39
39
|
const config_1 = require("../config");
|
|
40
|
+
const persistent_cache_1 = require("./persistent-cache");
|
|
40
41
|
const packageCache = new Map();
|
|
41
42
|
/**
|
|
42
43
|
* Fetches package data from npm registry with caching using native fetch.
|
|
43
44
|
* Includes timeout support for slow connections.
|
|
44
45
|
*/
|
|
45
46
|
async function fetchPackageFromRegistry(packageName) {
|
|
46
|
-
// Check cache first
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
47
|
+
// Check in-memory cache first (fastest)
|
|
48
|
+
const memoryCached = packageCache.get(packageName);
|
|
49
|
+
if (memoryCached && Date.now() - memoryCached.timestamp < config_1.CACHE_TTL) {
|
|
50
|
+
return memoryCached.data;
|
|
51
|
+
}
|
|
52
|
+
// Check persistent disk cache (fast, survives restarts)
|
|
53
|
+
const diskCached = persistent_cache_1.persistentCache.get(packageName);
|
|
54
|
+
if (diskCached) {
|
|
55
|
+
// Also populate in-memory cache for subsequent accesses
|
|
56
|
+
packageCache.set(packageName, {
|
|
57
|
+
data: diskCached,
|
|
58
|
+
timestamp: Date.now(),
|
|
59
|
+
});
|
|
60
|
+
return diskCached;
|
|
50
61
|
}
|
|
51
62
|
try {
|
|
52
63
|
const url = `${config_1.NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}`;
|
|
@@ -78,11 +89,13 @@ async function fetchPackageFromRegistry(packageName) {
|
|
|
78
89
|
latestVersion,
|
|
79
90
|
allVersions,
|
|
80
91
|
};
|
|
81
|
-
// Cache the result
|
|
92
|
+
// Cache the result in memory
|
|
82
93
|
packageCache.set(packageName, {
|
|
83
94
|
data: result,
|
|
84
95
|
timestamp: Date.now(),
|
|
85
96
|
});
|
|
97
|
+
// Cache to disk for persistence
|
|
98
|
+
persistent_cache_1.persistentCache.set(packageName, result);
|
|
86
99
|
return result;
|
|
87
100
|
}
|
|
88
101
|
finally {
|
|
@@ -118,6 +131,8 @@ async function getAllPackageData(packageNames, onProgress) {
|
|
|
118
131
|
});
|
|
119
132
|
// Wait for all requests to complete
|
|
120
133
|
await Promise.all(allPromises);
|
|
134
|
+
// Flush persistent cache to disk
|
|
135
|
+
persistent_cache_1.persistentCache.flush();
|
|
121
136
|
// Clear the progress line and show completion time if no custom progress handler
|
|
122
137
|
if (!onProgress) {
|
|
123
138
|
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.persistentCache = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const env_paths_1 = __importDefault(require("env-paths"));
|
|
10
|
+
// Cache TTL: 24 hours for disk cache (much longer than in-memory 5 minutes)
|
|
11
|
+
const DISK_CACHE_TTL = 24 * 60 * 60 * 1000;
|
|
12
|
+
// Maximum cache size (number of packages)
|
|
13
|
+
const MAX_CACHE_ENTRIES = 5000;
|
|
14
|
+
// Cache file format version (increment when structure changes)
|
|
15
|
+
const CACHE_VERSION = 1;
|
|
16
|
+
/**
|
|
17
|
+
* Persistent cache manager for package registry data.
|
|
18
|
+
* Stores cache on disk for fast repeated runs across CLI invocations.
|
|
19
|
+
*/
|
|
20
|
+
class PersistentCacheManager {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.index = null;
|
|
23
|
+
this.dirty = false;
|
|
24
|
+
const paths = (0, env_paths_1.default)('inup');
|
|
25
|
+
this.cacheDir = (0, path_1.join)(paths.cache, 'registry');
|
|
26
|
+
this.indexPath = (0, path_1.join)(this.cacheDir, 'index.json');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Ensure cache directory exists
|
|
30
|
+
*/
|
|
31
|
+
ensureCacheDir() {
|
|
32
|
+
if (!(0, fs_1.existsSync)(this.cacheDir)) {
|
|
33
|
+
(0, fs_1.mkdirSync)(this.cacheDir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load cache index from disk
|
|
38
|
+
*/
|
|
39
|
+
loadIndex() {
|
|
40
|
+
if (this.index) {
|
|
41
|
+
return this.index;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
if ((0, fs_1.existsSync)(this.indexPath)) {
|
|
45
|
+
const content = (0, fs_1.readFileSync)(this.indexPath, 'utf-8');
|
|
46
|
+
const parsed = JSON.parse(content);
|
|
47
|
+
// Check cache version - invalidate if outdated
|
|
48
|
+
if (parsed.version !== CACHE_VERSION) {
|
|
49
|
+
this.clearCache();
|
|
50
|
+
this.index = { version: CACHE_VERSION, entries: {} };
|
|
51
|
+
return this.index;
|
|
52
|
+
}
|
|
53
|
+
this.index = parsed;
|
|
54
|
+
return this.index;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Corrupted index, start fresh
|
|
59
|
+
}
|
|
60
|
+
this.index = { version: CACHE_VERSION, entries: {} };
|
|
61
|
+
return this.index;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Save cache index to disk
|
|
65
|
+
*/
|
|
66
|
+
saveIndex() {
|
|
67
|
+
if (!this.dirty || !this.index) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
this.ensureCacheDir();
|
|
72
|
+
(0, fs_1.writeFileSync)(this.indexPath, JSON.stringify(this.index), 'utf-8');
|
|
73
|
+
this.dirty = false;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Silently fail - cache is not critical
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Generate a safe filename for a package name
|
|
81
|
+
*/
|
|
82
|
+
getFilename(packageName) {
|
|
83
|
+
// Handle scoped packages: @scope/name -> scope__name
|
|
84
|
+
const safeName = packageName.replace(/^@/, '').replace(/\//g, '__');
|
|
85
|
+
return `${safeName}.json`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get cached data for a package
|
|
89
|
+
*/
|
|
90
|
+
get(packageName) {
|
|
91
|
+
const index = this.loadIndex();
|
|
92
|
+
const entry = index.entries[packageName];
|
|
93
|
+
if (!entry) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
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
|
+
// Read the actual cache file
|
|
104
|
+
try {
|
|
105
|
+
const filePath = (0, path_1.join)(this.cacheDir, entry.file);
|
|
106
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
107
|
+
delete index.entries[packageName];
|
|
108
|
+
this.dirty = true;
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
112
|
+
const cached = JSON.parse(content);
|
|
113
|
+
return {
|
|
114
|
+
latestVersion: cached.latestVersion,
|
|
115
|
+
allVersions: cached.allVersions,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Corrupted cache file, remove from index
|
|
120
|
+
delete index.entries[packageName];
|
|
121
|
+
this.dirty = true;
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Store data for a package
|
|
127
|
+
*/
|
|
128
|
+
set(packageName, data) {
|
|
129
|
+
const index = this.loadIndex();
|
|
130
|
+
// Evict old entries if cache is too large
|
|
131
|
+
const entryCount = Object.keys(index.entries).length;
|
|
132
|
+
if (entryCount >= MAX_CACHE_ENTRIES) {
|
|
133
|
+
this.evictOldest(Math.floor(MAX_CACHE_ENTRIES * 0.1)); // Evict 10%
|
|
134
|
+
}
|
|
135
|
+
const filename = this.getFilename(packageName);
|
|
136
|
+
const entry = {
|
|
137
|
+
...data,
|
|
138
|
+
timestamp: Date.now(),
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
this.ensureCacheDir();
|
|
142
|
+
const filePath = (0, path_1.join)(this.cacheDir, filename);
|
|
143
|
+
(0, fs_1.writeFileSync)(filePath, JSON.stringify(entry), 'utf-8');
|
|
144
|
+
index.entries[packageName] = {
|
|
145
|
+
file: filename,
|
|
146
|
+
timestamp: Date.now(),
|
|
147
|
+
};
|
|
148
|
+
this.dirty = true;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Silently fail - cache is not critical
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Batch get multiple packages (returns map of found entries)
|
|
156
|
+
*/
|
|
157
|
+
getMany(packageNames) {
|
|
158
|
+
const results = new Map();
|
|
159
|
+
for (const name of packageNames) {
|
|
160
|
+
const cached = this.get(name);
|
|
161
|
+
if (cached) {
|
|
162
|
+
results.set(name, cached);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return results;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Batch set multiple packages
|
|
169
|
+
*/
|
|
170
|
+
setMany(entries) {
|
|
171
|
+
for (const [name, data] of entries) {
|
|
172
|
+
this.set(name, data);
|
|
173
|
+
}
|
|
174
|
+
this.flush();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Evict oldest cache entries
|
|
178
|
+
*/
|
|
179
|
+
evictOldest(count) {
|
|
180
|
+
const index = this.loadIndex();
|
|
181
|
+
const entries = Object.entries(index.entries);
|
|
182
|
+
// Sort by timestamp (oldest first)
|
|
183
|
+
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
184
|
+
// Remove oldest entries
|
|
185
|
+
const toRemove = entries.slice(0, count);
|
|
186
|
+
for (const [packageName, entry] of toRemove) {
|
|
187
|
+
try {
|
|
188
|
+
const filePath = (0, path_1.join)(this.cacheDir, entry.file);
|
|
189
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
190
|
+
(0, fs_1.unlinkSync)(filePath);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Ignore deletion errors
|
|
195
|
+
}
|
|
196
|
+
delete index.entries[packageName];
|
|
197
|
+
}
|
|
198
|
+
this.dirty = true;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Clear all cache
|
|
202
|
+
*/
|
|
203
|
+
clearCache() {
|
|
204
|
+
try {
|
|
205
|
+
if ((0, fs_1.existsSync)(this.cacheDir)) {
|
|
206
|
+
const files = (0, fs_1.readdirSync)(this.cacheDir);
|
|
207
|
+
for (const file of files) {
|
|
208
|
+
try {
|
|
209
|
+
(0, fs_1.unlinkSync)((0, path_1.join)(this.cacheDir, file));
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Ignore
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// Ignore
|
|
219
|
+
}
|
|
220
|
+
this.index = { version: CACHE_VERSION, entries: {} };
|
|
221
|
+
this.dirty = true;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Flush pending changes to disk
|
|
225
|
+
*/
|
|
226
|
+
flush() {
|
|
227
|
+
this.saveIndex();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get cache statistics
|
|
231
|
+
*/
|
|
232
|
+
getStats() {
|
|
233
|
+
const index = this.loadIndex();
|
|
234
|
+
return {
|
|
235
|
+
entries: Object.keys(index.entries).length,
|
|
236
|
+
cacheDir: this.cacheDir,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Export singleton instance
|
|
241
|
+
exports.persistentCache = new PersistentCacheManager();
|
|
242
|
+
//# sourceMappingURL=persistent-cache.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inup",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.5",
|
|
4
4
|
"description": "Interactive CLI tool for upgrading dependencies with ease. Auto-detects and works with npm, yarn, pnpm, and bun. Inspired by yarn upgrade-interactive. Supports monorepos, workspaces, and batch upgrades.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|