pacman-debian 7.3.36 → 7.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 +12 -0
- package/README_zh-CN.md +11 -0
- package/dist/cli/archlinux-java.js +36 -12
- package/dist/cli/archlinux-java.js.map +1 -1
- package/dist/cli/paclink.js +84 -0
- package/dist/cli/paclink.js.map +1 -1
- package/dist/cli/pacman.d.ts.map +1 -1
- package/dist/cli/pacman.js +66 -11
- package/dist/cli/pacman.js.map +1 -1
- package/dist/cli/pactree.d.ts +3 -0
- package/dist/cli/pactree.d.ts.map +1 -0
- package/dist/cli/pactree.js +216 -0
- package/dist/cli/pactree.js.map +1 -0
- package/dist/core/deps.d.ts +7 -0
- package/dist/core/deps.d.ts.map +1 -1
- package/dist/core/deps.js +104 -24
- package/dist/core/deps.js.map +1 -1
- package/dist/core/logger.d.ts +7 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +93 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/options.d.ts +2 -0
- package/dist/core/options.d.ts.map +1 -1
- package/dist/core/paclinks.d.ts +9 -0
- package/dist/core/paclinks.d.ts.map +1 -0
- package/dist/core/paclinks.js +69 -0
- package/dist/core/paclinks.js.map +1 -0
- package/dist/core/pkgfile.d.ts +2 -0
- package/dist/core/pkgfile.d.ts.map +1 -1
- package/dist/core/pkgfile.js +6 -0
- package/dist/core/pkgfile.js.map +1 -1
- package/dist/core/tar.d.ts +3 -1
- package/dist/core/tar.d.ts.map +1 -1
- package/dist/core/tar.js +87 -11
- package/dist/core/tar.js.map +1 -1
- package/dist/core/types.d.ts +17 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/db/dpkg-compat.d.ts +5 -2
- package/dist/db/dpkg-compat.d.ts.map +1 -1
- package/dist/db/dpkg-compat.js +130 -57
- package/dist/db/dpkg-compat.js.map +1 -1
- package/dist/db/localdb.d.ts.map +1 -1
- package/dist/db/localdb.js +21 -2
- package/dist/db/localdb.js.map +1 -1
- package/dist/debug-upgrade.d.ts +2 -0
- package/dist/debug-upgrade.d.ts.map +1 -0
- package/dist/debug-upgrade.js +140 -0
- package/dist/debug-upgrade.js.map +1 -0
- package/dist/i18n/en.json +2 -0
- package/dist/i18n/paclink/en.d.ts.map +1 -1
- package/dist/i18n/paclink/en.js +7 -0
- package/dist/i18n/paclink/en.js.map +1 -1
- package/dist/i18n/paclink/zh-CN.d.ts.map +1 -1
- package/dist/i18n/paclink/zh-CN.js +7 -0
- package/dist/i18n/paclink/zh-CN.js.map +1 -1
- package/dist/i18n/zh-CN.json +2 -0
- package/dist/lock/dpkg-helper +0 -0
- package/dist/lock/dpkg-lock.d.ts +5 -0
- package/dist/lock/dpkg-lock.d.ts.map +1 -0
- package/dist/lock/dpkg-lock.js +113 -0
- package/dist/lock/dpkg-lock.js.map +1 -0
- package/dist/makepkg/build.d.ts.map +1 -1
- package/dist/makepkg/build.js +3 -5
- package/dist/makepkg/build.js.map +1 -1
- package/dist/makepkg/pkgbuild.d.ts.map +1 -1
- package/dist/makepkg/pkgbuild.js +109 -53
- package/dist/makepkg/pkgbuild.js.map +1 -1
- package/dist/ops/install.d.ts +2 -1
- package/dist/ops/install.d.ts.map +1 -1
- package/dist/ops/install.js +290 -157
- package/dist/ops/install.js.map +1 -1
- package/dist/ops/query.d.ts.map +1 -1
- package/dist/ops/query.js +62 -35
- package/dist/ops/query.js.map +1 -1
- package/dist/ops/remove.d.ts.map +1 -1
- package/dist/ops/remove.js +106 -60
- package/dist/ops/remove.js.map +1 -1
- package/dist/ops/upgrade.d.ts.map +1 -1
- package/dist/ops/upgrade.js +71 -13
- package/dist/ops/upgrade.js.map +1 -1
- package/dist/repo/config.d.ts.map +1 -1
- package/dist/repo/config.js +60 -6
- package/dist/repo/config.js.map +1 -1
- package/dist/repo/repository.d.ts +6 -0
- package/dist/repo/repository.d.ts.map +1 -1
- package/dist/repo/repository.js +348 -49
- package/dist/repo/repository.js.map +1 -1
- package/dist/scripts/setup.js +27 -77
- package/dist/scripts/setup.js.map +1 -1
- package/docs/en/architecture.md +109 -45
- package/docs/en/pactree.md +42 -0
- package/docs/en/yay-aur.md +10 -6
- package/docs/zh-CN/pactree.md +41 -0
- package/lib/pac4deb/include/alpm.h +2 -1
- package/lib/pac4deb/src/libalpm.c +116 -54
- package/lib/pac4deb/src/stubs_manual.c +11 -1
- package/package.json +4 -3
- package/resources/paclinks.conf +64 -0
- package/resources/pacman.conf.template +16 -2
package/dist/repo/repository.js
CHANGED
|
@@ -38,16 +38,21 @@ exports.syncRepos = syncRepos;
|
|
|
38
38
|
exports.readPkgAt = readPkgAt;
|
|
39
39
|
exports.batchFindInRepo = batchFindInRepo;
|
|
40
40
|
exports.findProvides = findProvides;
|
|
41
|
+
exports.findProvidesScoped = findProvidesScoped;
|
|
41
42
|
exports.getRepoCache = getRepoCache;
|
|
42
43
|
exports.invalidateCache = invalidateCache;
|
|
43
44
|
exports.searchRepo = searchRepo;
|
|
44
45
|
exports.findInRepo = findInRepo;
|
|
46
|
+
exports.findInRepoScoped = findInRepoScoped;
|
|
47
|
+
exports.findInRepoVersioned = findInRepoVersioned;
|
|
45
48
|
exports.getPkgUrl = getPkgUrl;
|
|
46
49
|
exports.downloadPkg = downloadPkg;
|
|
47
50
|
const fs = __importStar(require("node:fs"));
|
|
48
51
|
const path = __importStar(require("node:path"));
|
|
49
52
|
const https = __importStar(require("node:https"));
|
|
50
53
|
const http = __importStar(require("node:http"));
|
|
54
|
+
const crypto = __importStar(require("node:crypto"));
|
|
55
|
+
const node_child_process_1 = require("node:child_process");
|
|
51
56
|
const config_1 = require("./config");
|
|
52
57
|
const control_1 = require("../core/control");
|
|
53
58
|
const compress_1 = require("../core/compress");
|
|
@@ -56,15 +61,48 @@ const pkgfile_1 = require("../core/pkgfile");
|
|
|
56
61
|
const colors_1 = require("../ui/colors");
|
|
57
62
|
const i18n_1 = require("../i18n");
|
|
58
63
|
const progress_1 = require("../ui/progress");
|
|
64
|
+
const logger_1 = require("../core/logger");
|
|
59
65
|
const CACHE_DIR = '/var/cache/pacman-debian';
|
|
60
66
|
const PKG_CACHE = path.join(CACHE_DIR, 'packages');
|
|
61
67
|
const DEB_CACHE = path.join(CACHE_DIR, 'pkg');
|
|
68
|
+
const activePartials = new Set();
|
|
69
|
+
let partialCleanupInstalled = false;
|
|
70
|
+
function installPartialCleanup() {
|
|
71
|
+
if (partialCleanupInstalled)
|
|
72
|
+
return;
|
|
73
|
+
partialCleanupInstalled = true;
|
|
74
|
+
process.once('SIGINT', () => {
|
|
75
|
+
for (const file of activePartials) {
|
|
76
|
+
try {
|
|
77
|
+
fs.unlinkSync(file);
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
80
|
+
}
|
|
81
|
+
process.exit(130);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function atomicWrite(filePath, content) {
|
|
85
|
+
const tmp = `${filePath}.tmp-${process.pid}`;
|
|
86
|
+
try {
|
|
87
|
+
await fs.promises.writeFile(tmp, content, 'utf8');
|
|
88
|
+
await fs.promises.rename(tmp, filePath);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
try {
|
|
92
|
+
await fs.promises.unlink(tmp);
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
62
98
|
/* ---- Shared keep-alive HTTP agents ---- */
|
|
63
99
|
const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 8 });
|
|
64
100
|
const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 8 });
|
|
65
101
|
let _idxCache = new Map();
|
|
102
|
+
let _pkgCache = new Map(); // LRU: key = chunkFile:offset
|
|
66
103
|
function invalidateIdxCache() {
|
|
67
104
|
_idxCache.clear();
|
|
105
|
+
_pkgCache.clear();
|
|
68
106
|
}
|
|
69
107
|
function parseIdxLine(line) {
|
|
70
108
|
const lastTab = line.lastIndexOf('\t');
|
|
@@ -81,20 +119,24 @@ function parseIdxLine(line) {
|
|
|
81
119
|
if (!chunkFile)
|
|
82
120
|
return null;
|
|
83
121
|
const rest = beforeOff.slice(0, secondLastTab);
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
122
|
+
const thirdLastTab = rest.lastIndexOf('\t');
|
|
123
|
+
const provides = thirdLastTab >= 0 ? rest.slice(thirdLastTab + 1) : '';
|
|
124
|
+
const beforeProv = thirdLastTab >= 0 ? rest.slice(0, thirdLastTab) : rest;
|
|
125
|
+
const firstSpace = beforeProv.indexOf(' ');
|
|
126
|
+
const pkgName = beforeProv.slice(0, firstSpace);
|
|
127
|
+
const versionText = firstSpace >= 0 ? beforeProv.slice(firstSpace + 1) : '';
|
|
128
|
+
const firstTab = versionText.indexOf('\t');
|
|
129
|
+
const version = (firstTab >= 0 ? versionText.slice(0, firstTab) : versionText).trim();
|
|
130
|
+
return { pkgName, version, provides, chunkFile, offset };
|
|
88
131
|
}
|
|
89
132
|
function getIdx(repoName) {
|
|
133
|
+
const cached = _idxCache.get(repoName);
|
|
134
|
+
if (cached)
|
|
135
|
+
return cached;
|
|
90
136
|
const pkgDir = path.join(PKG_CACHE, repoName);
|
|
91
137
|
const idxPath = path.join(pkgDir, 'packages.idx');
|
|
92
138
|
if (!fs.existsSync(idxPath))
|
|
93
139
|
return null;
|
|
94
|
-
const st = fs.statSync(idxPath);
|
|
95
|
-
const cached = _idxCache.get(repoName);
|
|
96
|
-
if (cached && cached.mtime === st.mtimeMs)
|
|
97
|
-
return cached;
|
|
98
140
|
const lines = fs.readFileSync(idxPath, 'utf8').split('\n').filter(l => l.length > 0);
|
|
99
141
|
const providesIndex = new Map();
|
|
100
142
|
for (const line of lines) {
|
|
@@ -110,11 +152,28 @@ function getIdx(repoName) {
|
|
|
110
152
|
}
|
|
111
153
|
}
|
|
112
154
|
}
|
|
113
|
-
const entry = { lines,
|
|
155
|
+
const entry = { lines, providesIndex };
|
|
114
156
|
_idxCache.set(repoName, entry);
|
|
115
157
|
return entry;
|
|
116
158
|
}
|
|
117
|
-
async function downloadFile(url, onProgress, ifModifiedSince) {
|
|
159
|
+
async function downloadFile(url, onProgress, ifModifiedSince, dest) {
|
|
160
|
+
const cfg = (0, config_1.loadConfig)();
|
|
161
|
+
// XferCommand support
|
|
162
|
+
if (cfg.xferCommand) {
|
|
163
|
+
const tmpDest = dest || path.join('/tmp', `download-${Date.now()}`);
|
|
164
|
+
const cmd = cfg.xferCommand.replace(/%u/g, url).replace(/%o/g, tmpDest);
|
|
165
|
+
const result = (0, node_child_process_1.spawnSync)('/bin/sh', ['-c', cmd], { stdio: 'pipe', timeout: 300000 });
|
|
166
|
+
if (result.status !== 0) {
|
|
167
|
+
const out = result.stdout?.toString() || '';
|
|
168
|
+
const err = result.stderr?.toString() || '';
|
|
169
|
+
(0, logger_1.logError)(`XferCommand exit ${result.status} for ${url}: ${err || out}`);
|
|
170
|
+
throw new Error(`XferCommand failed (exit ${result.status}): ${err || out}`);
|
|
171
|
+
}
|
|
172
|
+
if (dest)
|
|
173
|
+
return null;
|
|
174
|
+
(0, logger_1.log)(`download: ${url} -> ${tmpDest}`);
|
|
175
|
+
return fs.readFileSync(tmpDest);
|
|
176
|
+
}
|
|
118
177
|
const maxRedirects = 5;
|
|
119
178
|
const doRequest = (u, redirects) => {
|
|
120
179
|
return new Promise((resolve, reject) => {
|
|
@@ -129,7 +188,6 @@ async function downloadFile(url, onProgress, ifModifiedSince) {
|
|
|
129
188
|
resolve(null);
|
|
130
189
|
return;
|
|
131
190
|
}
|
|
132
|
-
// Handle redirects (301, 302, 303, 307, 308)
|
|
133
191
|
if (res.statusCode && (res.statusCode >= 300 && res.statusCode < 400)) {
|
|
134
192
|
const loc = res.headers['location'];
|
|
135
193
|
res.destroy();
|
|
@@ -148,14 +206,29 @@ async function downloadFile(url, onProgress, ifModifiedSince) {
|
|
|
148
206
|
}
|
|
149
207
|
const total = parseInt(res.headers['content-length'] || '0', 10);
|
|
150
208
|
let received = 0;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
onProgress
|
|
157
|
-
|
|
158
|
-
|
|
209
|
+
if (dest) {
|
|
210
|
+
// Stream directly to file (for package downloads)
|
|
211
|
+
const ws = fs.createWriteStream(dest);
|
|
212
|
+
res.on('data', (c) => {
|
|
213
|
+
received += c.length;
|
|
214
|
+
if (onProgress)
|
|
215
|
+
onProgress(received, total);
|
|
216
|
+
});
|
|
217
|
+
res.pipe(ws);
|
|
218
|
+
ws.on('finish', () => resolve(null));
|
|
219
|
+
ws.on('error', reject);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// Buffer in memory (for metadata)
|
|
223
|
+
const chunks = [];
|
|
224
|
+
res.on('data', (c) => {
|
|
225
|
+
chunks.push(c);
|
|
226
|
+
received += c.length;
|
|
227
|
+
if (onProgress)
|
|
228
|
+
onProgress(received, total);
|
|
229
|
+
});
|
|
230
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
231
|
+
}
|
|
159
232
|
});
|
|
160
233
|
req.on('error', reject);
|
|
161
234
|
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
@@ -182,36 +255,124 @@ function parseDebianPackages(content, repo) {
|
|
|
182
255
|
}
|
|
183
256
|
return pkgs;
|
|
184
257
|
}
|
|
258
|
+
function parseReleaseSha256(text) {
|
|
259
|
+
const map = new Map();
|
|
260
|
+
let inSha256 = false;
|
|
261
|
+
for (const line of text.split('\n')) {
|
|
262
|
+
const t = line.trim();
|
|
263
|
+
if (t === 'SHA256:') {
|
|
264
|
+
inSha256 = true;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (inSha256) {
|
|
268
|
+
if (!t || t === '') {
|
|
269
|
+
inSha256 = false;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const m = t.match(/^([a-f0-9]{64})\s+\d+\s+(.+)$/);
|
|
273
|
+
if (m)
|
|
274
|
+
map.set(m[2], m[1]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return map;
|
|
278
|
+
}
|
|
279
|
+
function debArch(arch) {
|
|
280
|
+
const map = { aarch64: 'arm64', x86_64: 'amd64', x64: 'amd64', i686: 'i386' };
|
|
281
|
+
return map[arch] || arch;
|
|
282
|
+
}
|
|
185
283
|
async function syncDebian(repo, arch, ifModifiedSince, onProgress) {
|
|
284
|
+
arch = debArch(arch);
|
|
186
285
|
const comps = repo.components || ['main'];
|
|
286
|
+
const pkgDir = path.join(PKG_CACHE, repo.name);
|
|
287
|
+
let info = {};
|
|
288
|
+
const infoFile = path.join(pkgDir, '.info');
|
|
289
|
+
if (fs.existsSync(infoFile)) {
|
|
290
|
+
try {
|
|
291
|
+
info = JSON.parse(fs.readFileSync(infoFile, 'utf8'));
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
info = {};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const oldSha256 = info.sha256 || {};
|
|
298
|
+
// Download Release file first (like apt does)
|
|
299
|
+
const releaseBase = `${repo.server}/dists/${repo.dist}`;
|
|
300
|
+
let releaseText = null;
|
|
301
|
+
let releaseSize = 0;
|
|
302
|
+
for (const rfile of ['InRelease', 'Release']) {
|
|
303
|
+
try {
|
|
304
|
+
const releaseBuf = await downloadFile(`${releaseBase}/${rfile}`, (rec, tot) => {
|
|
305
|
+
if (onProgress)
|
|
306
|
+
onProgress(rec, tot);
|
|
307
|
+
}, ifModifiedSince);
|
|
308
|
+
if (releaseBuf === null)
|
|
309
|
+
return { pkgs: [], size: 0, notModified: true };
|
|
310
|
+
releaseText = releaseBuf.toString('utf8');
|
|
311
|
+
releaseSize = releaseBuf.length;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
catch { }
|
|
315
|
+
}
|
|
316
|
+
if (!releaseText)
|
|
317
|
+
return { pkgs: [], size: 0, notModified: true };
|
|
318
|
+
const sha256Map = parseReleaseSha256(releaseText);
|
|
187
319
|
const all = [];
|
|
188
|
-
let totalSize =
|
|
320
|
+
let totalSize = releaseSize;
|
|
321
|
+
let anyData = false;
|
|
322
|
+
const newSha256 = {};
|
|
189
323
|
for (const comp of comps) {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
324
|
+
const prefix = `${comp}/binary-${arch}/Packages`;
|
|
325
|
+
let downloaded = false;
|
|
326
|
+
for (const ext of ['xz', 'gz']) {
|
|
327
|
+
const key = `${prefix}.${ext}`;
|
|
328
|
+
const expectedHash = sha256Map.get(key);
|
|
329
|
+
if (!expectedHash)
|
|
330
|
+
continue;
|
|
331
|
+
newSha256[key] = expectedHash;
|
|
332
|
+
if (oldSha256[key] === expectedHash)
|
|
333
|
+
continue; // SHA256 matches, skip download
|
|
193
334
|
try {
|
|
194
|
-
const url = `${
|
|
335
|
+
const url = `${releaseBase}/${key}`;
|
|
195
336
|
const buf = await downloadFile(url, (rec, tot) => {
|
|
196
337
|
if (onProgress)
|
|
197
338
|
onProgress(totalSize + rec, totalSize + (tot || 0));
|
|
198
|
-
},
|
|
199
|
-
if (buf
|
|
200
|
-
|
|
201
|
-
|
|
339
|
+
}, undefined);
|
|
340
|
+
if (!buf)
|
|
341
|
+
continue;
|
|
342
|
+
downloaded = true;
|
|
343
|
+
anyData = true;
|
|
202
344
|
totalSize += buf.length;
|
|
203
|
-
const text = (await (0, compress_1.decompressAsync)(buf,
|
|
345
|
+
const text = (await (0, compress_1.decompressAsync)(buf, key)).toString('utf8');
|
|
204
346
|
all.push(...parseDebianPackages(text, repo.name));
|
|
205
347
|
break;
|
|
206
348
|
}
|
|
207
|
-
catch
|
|
208
|
-
if (gotData)
|
|
209
|
-
throw e;
|
|
210
|
-
}
|
|
349
|
+
catch { }
|
|
211
350
|
}
|
|
212
351
|
ifModifiedSince = undefined;
|
|
213
352
|
}
|
|
214
|
-
|
|
353
|
+
// Save SHA256 info only if anything was downloaded
|
|
354
|
+
if (anyData) {
|
|
355
|
+
info.sha256 = newSha256;
|
|
356
|
+
info.total = all.length;
|
|
357
|
+
try {
|
|
358
|
+
fs.mkdirSync(pkgDir, { recursive: true });
|
|
359
|
+
fs.writeFileSync(infoFile, JSON.stringify(info));
|
|
360
|
+
}
|
|
361
|
+
catch { }
|
|
362
|
+
}
|
|
363
|
+
// If idx is empty but Release was fresh → force re-download next time
|
|
364
|
+
if (!anyData && all.length === 0) {
|
|
365
|
+
const idxPath = path.join(pkgDir, 'packages.idx');
|
|
366
|
+
if (!fs.existsSync(idxPath) || fs.statSync(idxPath).size < 10) {
|
|
367
|
+
// Clear cached SHA256 so next sync forces Packages download
|
|
368
|
+
delete info.sha256;
|
|
369
|
+
try {
|
|
370
|
+
fs.writeFileSync(infoFile, JSON.stringify(info));
|
|
371
|
+
}
|
|
372
|
+
catch { }
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return { pkgs: all, size: totalSize, notModified: !anyData };
|
|
215
376
|
}
|
|
216
377
|
function parseArchDb(dbTar, repo) {
|
|
217
378
|
const pkgs = [];
|
|
@@ -449,6 +610,11 @@ async function syncRepos(force = false) {
|
|
|
449
610
|
progress.setRow(idx, `\x1b[K ${pname}${' '.repeat(fixedNameWidth - repo.name.length)}${colors_1.color.ok((0, i18n_1.t)('repo_already_uptodate'))}`);
|
|
450
611
|
return;
|
|
451
612
|
}
|
|
613
|
+
// Show "up to date" when 0 bytes downloaded (304 or empty sync)
|
|
614
|
+
if (totalDownloaded === 0 && totalExpected === 0 && ifModifiedSince) {
|
|
615
|
+
progress.setRow(idx, `\x1b[K ${pname}${' '.repeat(fixedNameWidth - repo.name.length)}${colors_1.color.ok((0, i18n_1.t)('repo_already_uptodate'))}`);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
452
618
|
// Write JSON Lines chunks (parallel per chunk)
|
|
453
619
|
const pkgDir = path.join(PKG_CACHE, repo.name);
|
|
454
620
|
if (!fs.existsSync(pkgDir))
|
|
@@ -461,7 +627,7 @@ async function syncRepos(force = false) {
|
|
|
461
627
|
const chunk = pkgs.slice(c * CHUNK, (c + 1) * CHUNK);
|
|
462
628
|
const lines = chunk.map(p => JSON.stringify(p)).join('\n');
|
|
463
629
|
const fname = `${String(c).padStart(5, '0')}.jsonl`;
|
|
464
|
-
writeTasks.push(
|
|
630
|
+
writeTasks.push(atomicWrite(path.join(pkgDir, fname), lines + '\n'));
|
|
465
631
|
// Build index with byte offsets (computed from previous chunk lengths)
|
|
466
632
|
let offset = 0;
|
|
467
633
|
for (let pi = 0; pi < chunk.length; pi++) {
|
|
@@ -469,19 +635,20 @@ async function syncRepos(force = false) {
|
|
|
469
635
|
const json = JSON.stringify(p);
|
|
470
636
|
const desc = (p.description || '').replace(/\\/g, '\\\\').replace(/\t/g, '\\t').replace(/\n/g, '\\n');
|
|
471
637
|
const provides = (p.provides || '').replace(/\\/g, '\\\\').replace(/\t/g, '\\t').replace(/\n/g, '\\n');
|
|
472
|
-
|
|
638
|
+
const version = (p.version || '').replace(/\\/g, '\\\\').replace(/\t/g, '\\t').replace(/\n/g, '\\n');
|
|
639
|
+
idxLines.push(`${p.package} ${version}\t${desc}\t${provides}\t${fname}\t${offset}`);
|
|
473
640
|
offset += Buffer.byteLength(json, 'utf8') + 1;
|
|
474
641
|
}
|
|
475
642
|
}
|
|
476
643
|
await Promise.all(writeTasks);
|
|
477
|
-
await
|
|
644
|
+
await atomicWrite(path.join(pkgDir, '.info'), JSON.stringify({ total: pkgs.length, chunks, chunkSize: CHUNK }));
|
|
478
645
|
// Remove legacy all.json
|
|
479
646
|
try {
|
|
480
647
|
fs.unlinkSync(path.join(pkgDir, 'all.json'));
|
|
481
648
|
}
|
|
482
649
|
catch { }
|
|
483
650
|
idxLines.sort(); // global sort so binary search works
|
|
484
|
-
await
|
|
651
|
+
await atomicWrite(path.join(pkgDir, 'packages.idx'), idxLines.join('\n') + '\n');
|
|
485
652
|
// Final line
|
|
486
653
|
const elapsed = (Date.now() - startTime) / 1000;
|
|
487
654
|
const totalSec = Math.round(elapsed);
|
|
@@ -636,6 +803,26 @@ function findProvides(name) {
|
|
|
636
803
|
}
|
|
637
804
|
return undefined;
|
|
638
805
|
}
|
|
806
|
+
/** Look up a virtual provider in one repository only. */
|
|
807
|
+
function findProvidesScoped(repoName, name) {
|
|
808
|
+
const cfg = (0, config_1.loadConfig)();
|
|
809
|
+
const repo = cfg.repos.find(r => r.name === repoName);
|
|
810
|
+
if (!repo)
|
|
811
|
+
return undefined;
|
|
812
|
+
const idxEntry = getIdx(repo.name);
|
|
813
|
+
if (!idxEntry)
|
|
814
|
+
return undefined;
|
|
815
|
+
const entries = idxEntry.providesIndex.get(name);
|
|
816
|
+
if (!entries)
|
|
817
|
+
return undefined;
|
|
818
|
+
const pkgDir = path.join(PKG_CACHE, repo.name);
|
|
819
|
+
for (const e of entries) {
|
|
820
|
+
const p = readPkgAt(pkgDir, e.chunkFile, e.offset);
|
|
821
|
+
if (p)
|
|
822
|
+
return p;
|
|
823
|
+
}
|
|
824
|
+
return undefined;
|
|
825
|
+
}
|
|
639
826
|
// ---- Cache ----
|
|
640
827
|
let _cache = null;
|
|
641
828
|
function getRepoCache() {
|
|
@@ -676,7 +863,8 @@ function invalidateCache() {
|
|
|
676
863
|
invalidateIdxCache();
|
|
677
864
|
}
|
|
678
865
|
function searchRepo(query) {
|
|
679
|
-
const
|
|
866
|
+
const sl = query.indexOf('/');
|
|
867
|
+
const lq = (sl > 0 ? query.slice(sl + 1) : query).toLowerCase();
|
|
680
868
|
const results = [];
|
|
681
869
|
const cfg = (0, config_1.loadConfig)();
|
|
682
870
|
const seen = new Set();
|
|
@@ -693,14 +881,24 @@ function searchRepo(query) {
|
|
|
693
881
|
if (!info || seen.has(info.pkgName))
|
|
694
882
|
continue;
|
|
695
883
|
seen.add(info.pkgName);
|
|
696
|
-
|
|
697
|
-
|
|
884
|
+
// Read from cache first, fallback to JSONL
|
|
885
|
+
const cacheKey = `${info.chunkFile}:${info.offset}`;
|
|
886
|
+
let p = _pkgCache.get(cacheKey);
|
|
887
|
+
if (!p) {
|
|
888
|
+
p = readPkgAt(pkgDir, info.chunkFile, info.offset);
|
|
889
|
+
if (p)
|
|
890
|
+
_pkgCache.set(cacheKey, p);
|
|
891
|
+
}
|
|
892
|
+
if (p && results.length < 50)
|
|
698
893
|
results.push(p);
|
|
699
894
|
}
|
|
700
895
|
}
|
|
701
896
|
return results;
|
|
702
897
|
}
|
|
703
898
|
function findInRepo(pkgName) {
|
|
899
|
+
const sl = pkgName.indexOf('/');
|
|
900
|
+
if (sl > 0)
|
|
901
|
+
return findInRepoScoped(pkgName.slice(0, sl), pkgName.slice(sl + 1));
|
|
704
902
|
const cfg = (0, config_1.loadConfig)();
|
|
705
903
|
for (const repo of cfg.repos) {
|
|
706
904
|
const idxEntry = getIdx(repo.name);
|
|
@@ -719,10 +917,79 @@ function findInRepo(pkgName) {
|
|
|
719
917
|
lo = mid + 1;
|
|
720
918
|
else {
|
|
721
919
|
const info = parseIdxLine(line);
|
|
722
|
-
if (info)
|
|
723
|
-
return
|
|
920
|
+
if (!info)
|
|
921
|
+
return undefined;
|
|
922
|
+
const cacheKey = `${info.chunkFile}:${info.offset}`;
|
|
923
|
+
let p = _pkgCache.get(cacheKey);
|
|
924
|
+
if (!p) {
|
|
925
|
+
p = readPkgAt(pkgDir, info.chunkFile, info.offset);
|
|
926
|
+
if (p)
|
|
927
|
+
_pkgCache.set(cacheKey, p);
|
|
928
|
+
}
|
|
929
|
+
return p;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return undefined;
|
|
934
|
+
}
|
|
935
|
+
/** Search only in a specific repo by name (e.g. "extra", "trixie"). */
|
|
936
|
+
function findInRepoScoped(repoName, pkgName) {
|
|
937
|
+
const cfg = (0, config_1.loadConfig)();
|
|
938
|
+
const repo = cfg.repos.find(r => r.name === repoName);
|
|
939
|
+
if (!repo)
|
|
940
|
+
return undefined;
|
|
941
|
+
const idxEntry = getIdx(repo.name);
|
|
942
|
+
if (!idxEntry)
|
|
943
|
+
return undefined;
|
|
944
|
+
const { lines } = idxEntry;
|
|
945
|
+
const pkgDir = path.join(PKG_CACHE, repo.name);
|
|
946
|
+
let lo = 0, hi = lines.length - 1;
|
|
947
|
+
while (lo <= hi) {
|
|
948
|
+
const mid = (lo + hi) >>> 1;
|
|
949
|
+
const line = lines[mid];
|
|
950
|
+
const pname = line.slice(0, line.indexOf(' '));
|
|
951
|
+
if (pkgName < pname)
|
|
952
|
+
hi = mid - 1;
|
|
953
|
+
else if (pkgName > pname)
|
|
954
|
+
lo = mid + 1;
|
|
955
|
+
else {
|
|
956
|
+
const info = parseIdxLine(line);
|
|
957
|
+
if (!info)
|
|
724
958
|
return undefined;
|
|
959
|
+
const cacheKey = `${info.chunkFile}:${info.offset}`;
|
|
960
|
+
let p = _pkgCache.get(cacheKey);
|
|
961
|
+
if (!p) {
|
|
962
|
+
p = readPkgAt(pkgDir, info.chunkFile, info.offset);
|
|
963
|
+
if (p)
|
|
964
|
+
_pkgCache.set(cacheKey, p);
|
|
725
965
|
}
|
|
966
|
+
return p;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return undefined;
|
|
970
|
+
}
|
|
971
|
+
/** Find an exact package version, respecting repository order or a scope. */
|
|
972
|
+
function findInRepoVersioned(pkgName, version, repoName) {
|
|
973
|
+
const cfg = (0, config_1.loadConfig)();
|
|
974
|
+
const repos = repoName ? cfg.repos.filter(r => r.name === repoName) : cfg.repos;
|
|
975
|
+
for (const repo of repos) {
|
|
976
|
+
const idxEntry = getIdx(repo.name);
|
|
977
|
+
if (!idxEntry)
|
|
978
|
+
continue;
|
|
979
|
+
const pkgDir = path.join(PKG_CACHE, repo.name);
|
|
980
|
+
for (const line of idxEntry.lines) {
|
|
981
|
+
const info = parseIdxLine(line);
|
|
982
|
+
if (!info || info.pkgName !== pkgName)
|
|
983
|
+
continue;
|
|
984
|
+
const cacheKey = `${info.chunkFile}:${info.offset}`;
|
|
985
|
+
let p = _pkgCache.get(cacheKey);
|
|
986
|
+
if (!p) {
|
|
987
|
+
p = readPkgAt(pkgDir, info.chunkFile, info.offset);
|
|
988
|
+
if (p)
|
|
989
|
+
_pkgCache.set(cacheKey, p);
|
|
990
|
+
}
|
|
991
|
+
if (p && p.package === pkgName && p.version === version)
|
|
992
|
+
return p;
|
|
726
993
|
}
|
|
727
994
|
}
|
|
728
995
|
return undefined;
|
|
@@ -747,13 +1014,45 @@ async function downloadPkg(rp, dest, onProgress) {
|
|
|
747
1014
|
fs.mkdirSync(DEB_CACHE, { recursive: true });
|
|
748
1015
|
const fn = path.basename(rp.filename);
|
|
749
1016
|
const local = path.join(dest || DEB_CACHE, fn);
|
|
750
|
-
if (fs.existsSync(local))
|
|
751
|
-
|
|
1017
|
+
if (fs.existsSync(local)) {
|
|
1018
|
+
if (!rp.sha256)
|
|
1019
|
+
return local;
|
|
1020
|
+
try {
|
|
1021
|
+
const hash = crypto.createHash('sha256').update(fs.readFileSync(local)).digest('hex');
|
|
1022
|
+
if (hash === rp.sha256)
|
|
1023
|
+
return local;
|
|
1024
|
+
fs.unlinkSync(local);
|
|
1025
|
+
}
|
|
1026
|
+
catch { }
|
|
1027
|
+
}
|
|
752
1028
|
const url = getPkgUrl(rp);
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1029
|
+
const partial = `${local}.part`;
|
|
1030
|
+
try {
|
|
1031
|
+
fs.unlinkSync(partial);
|
|
1032
|
+
}
|
|
1033
|
+
catch { }
|
|
1034
|
+
installPartialCleanup();
|
|
1035
|
+
activePartials.add(partial);
|
|
1036
|
+
// Stream directly to file instead of buffering in memory
|
|
1037
|
+
try {
|
|
1038
|
+
const data = await downloadFile(url, onProgress, undefined, partial);
|
|
1039
|
+
if (data === null)
|
|
1040
|
+
fs.renameSync(partial, local);
|
|
1041
|
+
else if (data)
|
|
1042
|
+
fs.writeFileSync(partial, data);
|
|
1043
|
+
if (data)
|
|
1044
|
+
fs.renameSync(partial, local);
|
|
1045
|
+
}
|
|
1046
|
+
catch (error) {
|
|
1047
|
+
try {
|
|
1048
|
+
fs.unlinkSync(partial);
|
|
1049
|
+
}
|
|
1050
|
+
catch { }
|
|
1051
|
+
throw error;
|
|
1052
|
+
}
|
|
1053
|
+
finally {
|
|
1054
|
+
activePartials.delete(partial);
|
|
1055
|
+
}
|
|
757
1056
|
return local;
|
|
758
1057
|
}
|
|
759
1058
|
//# sourceMappingURL=repository.js.map
|