@youtyan/code-viewer 0.8.2 → 0.8.3
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 +6 -0
- package/dist/code-viewer.js +313 -168
- package/package.json +1 -1
- package/web/app.js +107 -15
- package/web/style.css +46 -0
package/dist/code-viewer.js
CHANGED
|
@@ -666,9 +666,47 @@ var init_annotations = __esm(() => {
|
|
|
666
666
|
});
|
|
667
667
|
});
|
|
668
668
|
|
|
669
|
+
// web-src/server/cache.ts
|
|
670
|
+
import { lstatSync } from "node:fs";
|
|
671
|
+
import { join as join2 } from "node:path";
|
|
672
|
+
function cacheFresh(cached, now = Date.now(), ttlMs = CACHE_TTL_MS) {
|
|
673
|
+
return !!cached && now - cached.storedAt <= ttlMs;
|
|
674
|
+
}
|
|
675
|
+
function setTimedCacheEntry(cache, key, value, now = Date.now(), maxEntries = MAX_TIMED_CACHE_ENTRIES) {
|
|
676
|
+
cache.set(key, { ...value, storedAt: now });
|
|
677
|
+
while (cache.size > maxEntries) {
|
|
678
|
+
const oldest = cache.keys().next().value;
|
|
679
|
+
if (oldest === undefined)
|
|
680
|
+
break;
|
|
681
|
+
cache.delete(oldest);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function worktreeFileSignature(path, cwd) {
|
|
685
|
+
try {
|
|
686
|
+
const stats = lstatSync(join2(cwd, path));
|
|
687
|
+
const inode = "ino" in stats ? stats.ino : 0;
|
|
688
|
+
return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
|
|
689
|
+
} catch {
|
|
690
|
+
return "state:missing";
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function fileDiffCacheKey(options) {
|
|
694
|
+
const worktreeTarget = options.range.from === "worktree" || !options.range.to || options.range.to === "worktree";
|
|
695
|
+
if (options.isUntracked && !worktreeTarget) {
|
|
696
|
+
throw new Error("untracked file diffs require a worktree range");
|
|
697
|
+
}
|
|
698
|
+
const signature = worktreeTarget ? `\x00${worktreeFileSignature(options.path, options.cwd)}` : "";
|
|
699
|
+
if (options.isUntracked) {
|
|
700
|
+
return `u\x00${options.path}${signature}\x00${options.extras.join("\x00")}`;
|
|
701
|
+
}
|
|
702
|
+
return `t\x00${options.path}\x00${options.oldPath || ""}${signature}\x00${[...options.extras, ...options.args].join("\x00")}`;
|
|
703
|
+
}
|
|
704
|
+
var CACHE_TTL_MS = 1500, MAX_TIMED_CACHE_ENTRIES = 200;
|
|
705
|
+
var init_cache = () => {};
|
|
706
|
+
|
|
669
707
|
// web-src/server/command-resolver.ts
|
|
670
708
|
import { accessSync, constants, realpathSync, statSync } from "node:fs";
|
|
671
|
-
import { dirname as dirname2, isAbsolute, join as
|
|
709
|
+
import { dirname as dirname2, isAbsolute, join as join3, relative } from "node:path";
|
|
672
710
|
function isExternalCommandName(value) {
|
|
673
711
|
return commandNameSet.has(value);
|
|
674
712
|
}
|
|
@@ -803,7 +841,7 @@ function findGitRootByWalking(start) {
|
|
|
803
841
|
let current = start;
|
|
804
842
|
for (;; ) {
|
|
805
843
|
try {
|
|
806
|
-
statSync(
|
|
844
|
+
statSync(join3(current, ".git"));
|
|
807
845
|
return realpathSync(current);
|
|
808
846
|
} catch {}
|
|
809
847
|
const parent = dirname2(current);
|
|
@@ -1217,14 +1255,16 @@ var init_runtime = () => {};
|
|
|
1217
1255
|
import {
|
|
1218
1256
|
closeSync,
|
|
1219
1257
|
existsSync,
|
|
1220
|
-
lstatSync,
|
|
1258
|
+
lstatSync as lstatSync2,
|
|
1221
1259
|
openSync,
|
|
1222
1260
|
readdirSync,
|
|
1223
1261
|
readFileSync,
|
|
1262
|
+
readlinkSync,
|
|
1224
1263
|
readSync,
|
|
1264
|
+
realpathSync as realpathSync2,
|
|
1225
1265
|
statSync as statSync2
|
|
1226
1266
|
} from "node:fs";
|
|
1227
|
-
import { join as
|
|
1267
|
+
import { dirname as dirname3, join as join4, posix, relative as relative2 } from "node:path";
|
|
1228
1268
|
function normalizeBlameRef(ref, base) {
|
|
1229
1269
|
const rawRef = ref || "worktree";
|
|
1230
1270
|
if (base === "worktree" && rawRef !== "worktree") {
|
|
@@ -1322,12 +1362,76 @@ async function statusPorcelainForPathAsync(path, cwd) {
|
|
|
1322
1362
|
error: gitFailureMessage(res, "git status failed")
|
|
1323
1363
|
};
|
|
1324
1364
|
}
|
|
1365
|
+
async function repoStatusMapAsync(cwd, now = Date.now()) {
|
|
1366
|
+
const cached = repoStatusMapCache.get(cwd);
|
|
1367
|
+
if (cacheFresh(cached, now))
|
|
1368
|
+
return cached.map;
|
|
1369
|
+
const map = new Map;
|
|
1370
|
+
const res = await runGitAsync([
|
|
1371
|
+
"git",
|
|
1372
|
+
"-c",
|
|
1373
|
+
"core.quotepath=false",
|
|
1374
|
+
"status",
|
|
1375
|
+
"--porcelain=v1",
|
|
1376
|
+
"-z",
|
|
1377
|
+
"--untracked-files=all"
|
|
1378
|
+
], cwd);
|
|
1379
|
+
if (res.code !== 0)
|
|
1380
|
+
return map;
|
|
1381
|
+
const records = res.stdout.split("\x00").filter(Boolean);
|
|
1382
|
+
for (let i = 0;i < records.length; i++) {
|
|
1383
|
+
const record = records[i];
|
|
1384
|
+
const xy = record.slice(0, 2);
|
|
1385
|
+
const path = record.slice(3);
|
|
1386
|
+
if (!path)
|
|
1387
|
+
continue;
|
|
1388
|
+
if (xy === "??") {
|
|
1389
|
+
map.set(path, "A");
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (xy[0] === "R" || xy[0] === "C" || xy[1] === "R" || xy[1] === "C") {
|
|
1393
|
+
i++;
|
|
1394
|
+
map.set(path, "R");
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
const code = xy[0] !== " " ? xy[0] : xy[1];
|
|
1398
|
+
if (code && code !== " ")
|
|
1399
|
+
map.set(path, code);
|
|
1400
|
+
}
|
|
1401
|
+
setTimedCacheEntry(repoStatusMapCache, cwd, { map }, now);
|
|
1402
|
+
return map;
|
|
1403
|
+
}
|
|
1325
1404
|
function show(ref, path, cwd) {
|
|
1326
1405
|
return run(["git", "show", `${ref}:${path}`], cwd);
|
|
1327
1406
|
}
|
|
1328
1407
|
function showAsync(ref, path, cwd) {
|
|
1329
1408
|
return runGitAsync(["git", "show", `${ref}:${path}`], cwd);
|
|
1330
1409
|
}
|
|
1410
|
+
function resolveSymlinkPath(linkPath, target) {
|
|
1411
|
+
if (!target || target.startsWith("/") || target.includes("\x00"))
|
|
1412
|
+
return null;
|
|
1413
|
+
const baseDir = dirname3(linkPath);
|
|
1414
|
+
const combined = baseDir === "." ? target : `${baseDir}/${target}`;
|
|
1415
|
+
const normalized = posix.normalize(combined);
|
|
1416
|
+
if (normalized === "." || normalized === "")
|
|
1417
|
+
return "";
|
|
1418
|
+
if (normalized === ".." || normalized.startsWith("../"))
|
|
1419
|
+
return null;
|
|
1420
|
+
return normalized;
|
|
1421
|
+
}
|
|
1422
|
+
async function gitSymlinkTargetMetadataAsync(ref, path, cwd) {
|
|
1423
|
+
const res = await showAsync(ref, path, cwd);
|
|
1424
|
+
if (res.code !== 0)
|
|
1425
|
+
return { symlink_target_type: "missing" };
|
|
1426
|
+
const target = res.stdout;
|
|
1427
|
+
const resolved = resolveSymlinkPath(path, target);
|
|
1428
|
+
if (resolved === null)
|
|
1429
|
+
return { symlink_target: target, symlink_target_type: "missing" };
|
|
1430
|
+
const type = await runGitAsync(["git", "cat-file", "-t", `${ref}:${resolved}`], cwd);
|
|
1431
|
+
const kind = type.stdout.trim();
|
|
1432
|
+
const symlink_target_type = kind === "tree" ? "tree" : kind === "blob" ? "blob" : "missing";
|
|
1433
|
+
return symlink_target_type === "missing" ? { symlink_target: target, symlink_target_type } : { symlink_target: target, symlink_target_type, resolved_path: resolved };
|
|
1434
|
+
}
|
|
1331
1435
|
function catFileBlobStream(oid, cwd) {
|
|
1332
1436
|
return spawnStream(resolveGitArgs(["git", "cat-file", "blob", oid]), cwd);
|
|
1333
1437
|
}
|
|
@@ -1958,7 +2062,7 @@ function isGitInternalPath(path) {
|
|
|
1958
2062
|
return pathHasSegment(path, ".git");
|
|
1959
2063
|
}
|
|
1960
2064
|
function syntheticUncommittedBlameFromWorktree(cwd, path) {
|
|
1961
|
-
const filePath =
|
|
2065
|
+
const filePath = join4(cwd, path);
|
|
1962
2066
|
try {
|
|
1963
2067
|
const stat = statSync2(filePath);
|
|
1964
2068
|
if (!stat.isFile())
|
|
@@ -2217,7 +2321,7 @@ function omittedWorktreeDirectoryReason(name, omitDirNames) {
|
|
|
2217
2321
|
return omitDirNames.matches(name) ? "heavy" : undefined;
|
|
2218
2322
|
}
|
|
2219
2323
|
function worktreeSubmodulePaths(cwd) {
|
|
2220
|
-
if (!existsSync(
|
|
2324
|
+
if (!existsSync(join4(cwd, ".gitmodules")))
|
|
2221
2325
|
return new Set;
|
|
2222
2326
|
const res = run(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
2223
2327
|
if (res.code !== 0)
|
|
@@ -2229,7 +2333,7 @@ function worktreeSubmodulePaths(cwd) {
|
|
|
2229
2333
|
}).filter(Boolean));
|
|
2230
2334
|
}
|
|
2231
2335
|
async function worktreeSubmodulePathsAsync(cwd) {
|
|
2232
|
-
if (!existsSync(
|
|
2336
|
+
if (!existsSync(join4(cwd, ".gitmodules")))
|
|
2233
2337
|
return new Set;
|
|
2234
2338
|
const res = await runGitAsync(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
2235
2339
|
if (res.code !== 0)
|
|
@@ -2240,7 +2344,48 @@ async function worktreeSubmodulePathsAsync(cwd) {
|
|
|
2240
2344
|
return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
|
|
2241
2345
|
}).filter(Boolean));
|
|
2242
2346
|
}
|
|
2243
|
-
function
|
|
2347
|
+
function realpathWithinRepo(cwd, full, allowRoot) {
|
|
2348
|
+
try {
|
|
2349
|
+
const realCwd = realpathSync2(cwd);
|
|
2350
|
+
const realFull = realpathSync2(full);
|
|
2351
|
+
const rel = relative2(realCwd, realFull);
|
|
2352
|
+
if (rel.startsWith("..") || rel.startsWith("/") || rel.startsWith("\\"))
|
|
2353
|
+
return null;
|
|
2354
|
+
if (rel === "" && !allowRoot)
|
|
2355
|
+
return null;
|
|
2356
|
+
return realFull;
|
|
2357
|
+
} catch {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
function resolveWorktreeSymlinkTarget(cwd, full) {
|
|
2362
|
+
let symlink_target;
|
|
2363
|
+
try {
|
|
2364
|
+
symlink_target = readlinkSync(full);
|
|
2365
|
+
} catch {
|
|
2366
|
+
symlink_target = undefined;
|
|
2367
|
+
}
|
|
2368
|
+
let symlink_target_type = "missing";
|
|
2369
|
+
if (realpathWithinRepo(cwd, full, false) !== null) {
|
|
2370
|
+
try {
|
|
2371
|
+
const stat = statSync2(full);
|
|
2372
|
+
symlink_target_type = stat.isDirectory() ? "tree" : stat.isFile() ? "blob" : "missing";
|
|
2373
|
+
} catch {
|
|
2374
|
+
symlink_target_type = "missing";
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
return symlink_target === undefined ? { symlink_target_type } : { symlink_target, symlink_target_type };
|
|
2378
|
+
}
|
|
2379
|
+
function recursiveWorktreeFileEntry(cwd, full, name, path, isSymlink) {
|
|
2380
|
+
const symlinkInfo = isSymlink ? resolveWorktreeSymlinkTarget(cwd, full) : null;
|
|
2381
|
+
return {
|
|
2382
|
+
name,
|
|
2383
|
+
path,
|
|
2384
|
+
type: "blob",
|
|
2385
|
+
...symlinkInfo ? { is_symlink: true, ...symlinkInfo } : {}
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
function worktreeEntryFromDirent(cwd, base, dir, name, isDirectory, isSymlink, omitDirNames, excludeNames, submodulePaths) {
|
|
2244
2389
|
if (excludeNames.matches(name))
|
|
2245
2390
|
return {
|
|
2246
2391
|
name,
|
|
@@ -2248,10 +2393,18 @@ function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, exc
|
|
|
2248
2393
|
type: isDirectory ? "tree" : "blob"
|
|
2249
2394
|
};
|
|
2250
2395
|
const entryPath = base ? `${base}/${name}` : name;
|
|
2251
|
-
const
|
|
2396
|
+
const symlinkInfo = isSymlink ? resolveWorktreeSymlinkTarget(cwd, join4(dir, name)) : null;
|
|
2397
|
+
const resolvedIsDirectory = symlinkInfo ? symlinkInfo.symlink_target_type === "tree" : isDirectory;
|
|
2398
|
+
const type = symlinkInfo && symlinkInfo.symlink_target_type === "missing" ? "blob" : resolvedIsDirectory ? hasDotGitEntry(join4(dir, name)) ? "commit" : "tree" : "blob";
|
|
2252
2399
|
const omittedReason = type === "tree" ? omittedWorktreeDirectoryReason(name, omitDirNames) : undefined;
|
|
2253
2400
|
const submodule = type === "commit" && submodulePaths.has(entryPath) ? true : undefined;
|
|
2254
|
-
const baseEntry =
|
|
2401
|
+
const baseEntry = {
|
|
2402
|
+
name,
|
|
2403
|
+
path: entryPath,
|
|
2404
|
+
type,
|
|
2405
|
+
...submodule ? { submodule } : {},
|
|
2406
|
+
...symlinkInfo ? { is_symlink: true, ...symlinkInfo } : {}
|
|
2407
|
+
};
|
|
2255
2408
|
return omittedReason ? {
|
|
2256
2409
|
...baseEntry,
|
|
2257
2410
|
children_omitted: true,
|
|
@@ -2260,14 +2413,16 @@ function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, exc
|
|
|
2260
2413
|
}
|
|
2261
2414
|
function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2262
2415
|
const base = normalizeTreePath(path);
|
|
2263
|
-
const root =
|
|
2416
|
+
const root = join4(cwd, base);
|
|
2417
|
+
if (realpathWithinRepo(cwd, root, true) === null)
|
|
2418
|
+
return [];
|
|
2264
2419
|
const omitDirNameSet = compileNamePatterns(omitDirNames);
|
|
2265
2420
|
const excludeNameSet = compileNamePatterns(excludeNames);
|
|
2266
2421
|
const submodulePaths = worktreeSubmodulePaths(cwd);
|
|
2267
2422
|
let directEntries;
|
|
2268
2423
|
try {
|
|
2269
2424
|
const dirents = readdirSync(root, { withFileTypes: true });
|
|
2270
|
-
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2425
|
+
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(cwd, base, root, entry.name, entry.isDirectory(), entry.isSymbolicLink(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2271
2426
|
} catch {
|
|
2272
2427
|
return [];
|
|
2273
2428
|
}
|
|
@@ -2307,7 +2462,7 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
2307
2462
|
if (excludeNameSet.matches(entry.name))
|
|
2308
2463
|
continue;
|
|
2309
2464
|
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2310
|
-
const full =
|
|
2465
|
+
const full = join4(dir, entry.name);
|
|
2311
2466
|
if (entry.isDirectory()) {
|
|
2312
2467
|
const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
|
|
2313
2468
|
if (omittedReason) {
|
|
@@ -2325,11 +2480,7 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
2325
2480
|
continue;
|
|
2326
2481
|
walk(full, entryPath, depth + 1);
|
|
2327
2482
|
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
2328
|
-
if (!pushRecursiveEntry(
|
|
2329
|
-
name: entry.name,
|
|
2330
|
-
path: entryPath,
|
|
2331
|
-
type: "blob"
|
|
2332
|
-
}))
|
|
2483
|
+
if (!pushRecursiveEntry(recursiveWorktreeFileEntry(cwd, full, entry.name, entryPath, entry.isSymbolicLink())))
|
|
2333
2484
|
return;
|
|
2334
2485
|
}
|
|
2335
2486
|
}
|
|
@@ -2339,14 +2490,16 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
2339
2490
|
}
|
|
2340
2491
|
async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2341
2492
|
const base = normalizeTreePath(path);
|
|
2342
|
-
const root =
|
|
2493
|
+
const root = join4(cwd, base);
|
|
2494
|
+
if (realpathWithinRepo(cwd, root, true) === null)
|
|
2495
|
+
return [];
|
|
2343
2496
|
const omitDirNameSet = compileNamePatterns(omitDirNames);
|
|
2344
2497
|
const excludeNameSet = compileNamePatterns(excludeNames);
|
|
2345
2498
|
const submodulePaths = await worktreeSubmodulePathsAsync(cwd);
|
|
2346
2499
|
let directEntries;
|
|
2347
2500
|
try {
|
|
2348
2501
|
const dirents = readdirSync(root, { withFileTypes: true });
|
|
2349
|
-
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2502
|
+
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(cwd, base, root, entry.name, entry.isDirectory(), entry.isSymbolicLink(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2350
2503
|
} catch {
|
|
2351
2504
|
return [];
|
|
2352
2505
|
}
|
|
@@ -2394,7 +2547,7 @@ async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames
|
|
|
2394
2547
|
if (excludeNameSet.matches(entry.name))
|
|
2395
2548
|
continue;
|
|
2396
2549
|
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2397
|
-
const full =
|
|
2550
|
+
const full = join4(dir, entry.name);
|
|
2398
2551
|
if (entry.isDirectory()) {
|
|
2399
2552
|
const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
|
|
2400
2553
|
if (omittedReason) {
|
|
@@ -2412,11 +2565,7 @@ async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames
|
|
|
2412
2565
|
continue;
|
|
2413
2566
|
await walk(full, entryPath, depth + 1);
|
|
2414
2567
|
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
2415
|
-
if (!pushRecursiveEntry(
|
|
2416
|
-
name: entry.name,
|
|
2417
|
-
path: entryPath,
|
|
2418
|
-
type: "blob"
|
|
2419
|
-
}))
|
|
2568
|
+
if (!pushRecursiveEntry(recursiveWorktreeFileEntry(cwd, full, entry.name, entryPath, entry.isSymbolicLink())))
|
|
2420
2569
|
return;
|
|
2421
2570
|
}
|
|
2422
2571
|
}
|
|
@@ -2426,12 +2575,24 @@ async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames
|
|
|
2426
2575
|
}
|
|
2427
2576
|
function hasDotGitEntry(dir) {
|
|
2428
2577
|
try {
|
|
2429
|
-
|
|
2578
|
+
lstatSync2(join4(dir, ".git"));
|
|
2430
2579
|
return true;
|
|
2431
2580
|
} catch (err) {
|
|
2432
2581
|
return !!err && typeof err === "object" && "code" in err && err.code !== "ENOENT";
|
|
2433
2582
|
}
|
|
2434
2583
|
}
|
|
2584
|
+
function parseLsTreeRecord(rec, allowedTypes) {
|
|
2585
|
+
const match = rec.match(new RegExp(`^(\\d+)\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2586
|
+
if (!match)
|
|
2587
|
+
return null;
|
|
2588
|
+
const [, mode, type, entryPath] = match;
|
|
2589
|
+
return {
|
|
2590
|
+
name: entryPath.split("/").pop() || entryPath,
|
|
2591
|
+
path: entryPath,
|
|
2592
|
+
type,
|
|
2593
|
+
...mode === LS_TREE_SYMLINK_MODE ? { is_symlink: true } : {}
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2435
2596
|
function gitTreeEntries(ref, path, cwd, recursive) {
|
|
2436
2597
|
const base = normalizeTreePath(path);
|
|
2437
2598
|
const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
|
|
@@ -2444,17 +2605,7 @@ function gitTreeEntries(ref, path, cwd, recursive) {
|
|
|
2444
2605
|
if (res.code !== 0)
|
|
2445
2606
|
return { code: res.code, entries: [], stderr: res.stderr };
|
|
2446
2607
|
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
2447
|
-
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) =>
|
|
2448
|
-
const match = rec.match(new RegExp(`^\\d+\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2449
|
-
if (!match)
|
|
2450
|
-
return null;
|
|
2451
|
-
const entryPath = match[2];
|
|
2452
|
-
return {
|
|
2453
|
-
name: entryPath.split("/").pop() || entryPath,
|
|
2454
|
-
path: entryPath,
|
|
2455
|
-
type: match[1]
|
|
2456
|
-
};
|
|
2457
|
-
}).filter((entry) => !!entry);
|
|
2608
|
+
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
|
|
2458
2609
|
if (recursive)
|
|
2459
2610
|
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
2460
2611
|
else
|
|
@@ -2473,17 +2624,7 @@ async function gitTreeEntriesAsync(ref, path, cwd, recursive) {
|
|
|
2473
2624
|
if (res.code !== 0)
|
|
2474
2625
|
return { code: res.code, entries: [], stderr: res.stderr };
|
|
2475
2626
|
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
2476
|
-
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) =>
|
|
2477
|
-
const match = rec.match(new RegExp(`^\\d+\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2478
|
-
if (!match)
|
|
2479
|
-
return null;
|
|
2480
|
-
const entryPath = match[2];
|
|
2481
|
-
return {
|
|
2482
|
-
name: entryPath.split("/").pop() || entryPath,
|
|
2483
|
-
path: entryPath,
|
|
2484
|
-
type: match[1]
|
|
2485
|
-
};
|
|
2486
|
-
}).filter((entry) => !!entry);
|
|
2627
|
+
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
|
|
2487
2628
|
if (recursive)
|
|
2488
2629
|
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
2489
2630
|
else
|
|
@@ -2547,7 +2688,7 @@ async function listTreeResultAsync(ref, path, cwd, options = {}) {
|
|
|
2547
2688
|
}
|
|
2548
2689
|
function untrackedMeta(cwd) {
|
|
2549
2690
|
return untracked(cwd).flatMap((path) => {
|
|
2550
|
-
const full =
|
|
2691
|
+
const full = join4(cwd, path);
|
|
2551
2692
|
let fileExists = false;
|
|
2552
2693
|
try {
|
|
2553
2694
|
fileExists = existsSync(full) && statSync2(full).isFile();
|
|
@@ -2579,7 +2720,7 @@ function untrackedMeta(cwd) {
|
|
|
2579
2720
|
async function untrackedMetaAsync(cwd) {
|
|
2580
2721
|
const paths = await untrackedAsync(cwd);
|
|
2581
2722
|
return paths.flatMap((path) => {
|
|
2582
|
-
const full =
|
|
2723
|
+
const full = join4(cwd, path);
|
|
2583
2724
|
let fileExists = false;
|
|
2584
2725
|
try {
|
|
2585
2726
|
fileExists = existsSync(full) && statSync2(full).isFile();
|
|
@@ -2827,8 +2968,9 @@ function truncateToNHunks(diffText, n, maxLines = Number.POSITIVE_INFINITY) {
|
|
|
2827
2968
|
lineTruncated
|
|
2828
2969
|
};
|
|
2829
2970
|
}
|
|
2830
|
-
var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200;
|
|
2971
|
+
var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, repoStatusMapCache, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200, LS_TREE_SYMLINK_MODE = "120000";
|
|
2831
2972
|
var init_git = __esm(() => {
|
|
2973
|
+
init_cache();
|
|
2832
2974
|
init_command_resolver();
|
|
2833
2975
|
init_name_pattern();
|
|
2834
2976
|
init_runtime();
|
|
@@ -2877,6 +3019,7 @@ var init_git = __esm(() => {
|
|
|
2877
3019
|
"bin",
|
|
2878
3020
|
"obj"
|
|
2879
3021
|
];
|
|
3022
|
+
repoStatusMapCache = new Map;
|
|
2880
3023
|
});
|
|
2881
3024
|
|
|
2882
3025
|
// web-src/server/server-registry.ts
|
|
@@ -2889,16 +3032,16 @@ import {
|
|
|
2889
3032
|
writeFileSync
|
|
2890
3033
|
} from "node:fs";
|
|
2891
3034
|
import { homedir } from "node:os";
|
|
2892
|
-
import { join as
|
|
3035
|
+
import { join as join5 } from "node:path";
|
|
2893
3036
|
function registryDir() {
|
|
2894
3037
|
const override = process.env.CODE_VIEWER_TEST_SERVER_REGISTRY_DIR;
|
|
2895
3038
|
if (override)
|
|
2896
3039
|
return override;
|
|
2897
|
-
return
|
|
3040
|
+
return join5(homedir(), ".cache", "code-viewer", "servers");
|
|
2898
3041
|
}
|
|
2899
3042
|
function serverRegistryFilePath(root) {
|
|
2900
3043
|
const hash = createHash("sha256").update(root).digest("hex").slice(0, 16);
|
|
2901
|
-
return
|
|
3044
|
+
return join5(registryDir(), `${hash}.json`);
|
|
2902
3045
|
}
|
|
2903
3046
|
function writeServerRegistry(entry) {
|
|
2904
3047
|
try {
|
|
@@ -2939,7 +3082,7 @@ function removeServerRegistry(root, pid) {
|
|
|
2939
3082
|
var init_server_registry = () => {};
|
|
2940
3083
|
|
|
2941
3084
|
// web-src/server/cli-helpers.ts
|
|
2942
|
-
import { realpathSync as
|
|
3085
|
+
import { realpathSync as realpathSync3 } from "node:fs";
|
|
2943
3086
|
function takeValue(argv, index, flag) {
|
|
2944
3087
|
const value = argv[index + 1];
|
|
2945
3088
|
if (value === undefined)
|
|
@@ -2998,7 +3141,7 @@ function resolveRepoRootSafe(cwdOption) {
|
|
|
2998
3141
|
const base = cwdOption || process.cwd();
|
|
2999
3142
|
let baseReal;
|
|
3000
3143
|
try {
|
|
3001
|
-
baseReal =
|
|
3144
|
+
baseReal = realpathSync3(base);
|
|
3002
3145
|
} catch {
|
|
3003
3146
|
return {
|
|
3004
3147
|
ok: false,
|
|
@@ -4108,8 +4251,8 @@ __export(exports_file_cli, {
|
|
|
4108
4251
|
FILE_DEFAULT_HISTORY_LIMIT: () => FILE_DEFAULT_HISTORY_LIMIT,
|
|
4109
4252
|
FILE_AGENT_HELP: () => FILE_AGENT_HELP
|
|
4110
4253
|
});
|
|
4111
|
-
import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as
|
|
4112
|
-
import { join as
|
|
4254
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
|
|
4255
|
+
import { join as join6, relative as relative3 } from "node:path";
|
|
4113
4256
|
function validatePath(value) {
|
|
4114
4257
|
return validateRepoRelativePathValue(value, "--path");
|
|
4115
4258
|
}
|
|
@@ -4513,13 +4656,13 @@ function sliceLines(text, start, end) {
|
|
|
4513
4656
|
function safeWorktreePathFromRoot(root, path) {
|
|
4514
4657
|
if (validatePath(path))
|
|
4515
4658
|
return null;
|
|
4516
|
-
const full =
|
|
4659
|
+
const full = join6(root, path);
|
|
4517
4660
|
if (!existsSync3(full))
|
|
4518
4661
|
return null;
|
|
4519
4662
|
try {
|
|
4520
|
-
const realRoot =
|
|
4521
|
-
const realFull =
|
|
4522
|
-
const rel =
|
|
4663
|
+
const realRoot = realpathSync4(root);
|
|
4664
|
+
const realFull = realpathSync4(full);
|
|
4665
|
+
const rel = relative3(realRoot, realFull);
|
|
4523
4666
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/") || rel.startsWith("\\")) {
|
|
4524
4667
|
return null;
|
|
4525
4668
|
}
|
|
@@ -9423,24 +9566,24 @@ Parse failures and unreachable servers exit 1.
|
|
|
9423
9566
|
|
|
9424
9567
|
// web-src/server/root.ts
|
|
9425
9568
|
import { existsSync as existsSync4 } from "node:fs";
|
|
9426
|
-
import { dirname as
|
|
9569
|
+
import { dirname as dirname4, join as join7, normalize } from "node:path";
|
|
9427
9570
|
import { fileURLToPath } from "node:url";
|
|
9428
9571
|
function findRoot(start) {
|
|
9429
9572
|
let current = start;
|
|
9430
9573
|
for (let i = 0;i < 5; i++) {
|
|
9431
|
-
if (existsSync4(
|
|
9574
|
+
if (existsSync4(join7(current, "package.json")) && existsSync4(join7(current, "web"))) {
|
|
9432
9575
|
return normalize(current);
|
|
9433
9576
|
}
|
|
9434
|
-
const parent =
|
|
9577
|
+
const parent = dirname4(current);
|
|
9435
9578
|
if (parent === current)
|
|
9436
9579
|
break;
|
|
9437
9580
|
current = parent;
|
|
9438
9581
|
}
|
|
9439
|
-
return normalize(
|
|
9582
|
+
return normalize(join7(start, "..", ".."));
|
|
9440
9583
|
}
|
|
9441
9584
|
var ROOT;
|
|
9442
9585
|
var init_root = __esm(() => {
|
|
9443
|
-
ROOT = findRoot(
|
|
9586
|
+
ROOT = findRoot(dirname4(fileURLToPath(import.meta.url)));
|
|
9444
9587
|
});
|
|
9445
9588
|
|
|
9446
9589
|
// web-src/server/skill-cli.ts
|
|
@@ -9455,7 +9598,7 @@ __export(exports_skill_cli, {
|
|
|
9455
9598
|
});
|
|
9456
9599
|
import { cpSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
9457
9600
|
import { homedir as homedir2 } from "node:os";
|
|
9458
|
-
import { join as
|
|
9601
|
+
import { join as join8, resolve } from "node:path";
|
|
9459
9602
|
function parseAgentList(value) {
|
|
9460
9603
|
if (value === "all")
|
|
9461
9604
|
return [...AGENT_NAMES];
|
|
@@ -9515,7 +9658,7 @@ function parseSkillArgs(argv) {
|
|
|
9515
9658
|
function discoverBundledSkills(skillsRoot) {
|
|
9516
9659
|
if (!existsSync5(skillsRoot))
|
|
9517
9660
|
return [];
|
|
9518
|
-
return readdirSync2(skillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync5(
|
|
9661
|
+
return readdirSync2(skillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync5(join8(skillsRoot, entry.name, "SKILL.md"))).map((entry) => entry.name).sort();
|
|
9519
9662
|
}
|
|
9520
9663
|
function installSkill(args, deps) {
|
|
9521
9664
|
const skills = discoverBundledSkills(deps.skillsRoot);
|
|
@@ -9529,8 +9672,8 @@ function installSkill(args, deps) {
|
|
|
9529
9672
|
const results = [];
|
|
9530
9673
|
for (const agent of args.agents) {
|
|
9531
9674
|
for (const skill of skills) {
|
|
9532
|
-
const sourceDir =
|
|
9533
|
-
const target =
|
|
9675
|
+
const sourceDir = join8(deps.skillsRoot, skill);
|
|
9676
|
+
const target = join8(base, AGENT_SKILL_DIRS[agent], "skills", skill);
|
|
9534
9677
|
const action = existsSync5(target) ? "updated" : "installed";
|
|
9535
9678
|
try {
|
|
9536
9679
|
mkdirSync2(target, { recursive: true });
|
|
@@ -9559,7 +9702,7 @@ function runSkillCli(argv) {
|
|
|
9559
9702
|
return;
|
|
9560
9703
|
}
|
|
9561
9704
|
const result = installSkill(parsed.args, {
|
|
9562
|
-
skillsRoot:
|
|
9705
|
+
skillsRoot: join8(ROOT, "skills"),
|
|
9563
9706
|
homeDir: homedir2(),
|
|
9564
9707
|
projectDir: process.cwd()
|
|
9565
9708
|
});
|
|
@@ -14623,11 +14766,11 @@ import {
|
|
|
14623
14766
|
existsSync as existsSync6,
|
|
14624
14767
|
openSync as openSync2,
|
|
14625
14768
|
readSync as readSync2,
|
|
14626
|
-
realpathSync as
|
|
14769
|
+
realpathSync as realpathSync5,
|
|
14627
14770
|
statSync as statSync4
|
|
14628
14771
|
} from "node:fs";
|
|
14629
14772
|
import { lstat, open, readdir, readFile as readFile2, stat } from "node:fs/promises";
|
|
14630
|
-
import { basename, join as
|
|
14773
|
+
import { basename, join as join9, relative as relative4 } from "node:path";
|
|
14631
14774
|
function isSqliteFile(fullPath) {
|
|
14632
14775
|
try {
|
|
14633
14776
|
const stat2 = statSync4(fullPath);
|
|
@@ -14698,7 +14841,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
14698
14841
|
return;
|
|
14699
14842
|
if (omitSet.has(entry.toLowerCase()))
|
|
14700
14843
|
continue;
|
|
14701
|
-
const full =
|
|
14844
|
+
const full = join9(dir, entry);
|
|
14702
14845
|
let entryStat;
|
|
14703
14846
|
try {
|
|
14704
14847
|
entryStat = await lstat(full);
|
|
@@ -14715,7 +14858,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
14715
14858
|
continue;
|
|
14716
14859
|
if (!await isSqliteFileAsync(full))
|
|
14717
14860
|
continue;
|
|
14718
|
-
const rel =
|
|
14861
|
+
const rel = relative4(cwd, full);
|
|
14719
14862
|
if (rel.startsWith("..") || rel.startsWith("/"))
|
|
14720
14863
|
continue;
|
|
14721
14864
|
results.push({
|
|
@@ -14742,18 +14885,18 @@ function validateDbPath(cwd, dbPath) {
|
|
|
14742
14885
|
const parts = dbPath.split(/[\\/]+/);
|
|
14743
14886
|
if (parts.some((p) => p === ".." || p.toLowerCase() === ".git" || p.toLowerCase() === ".code-viewer"))
|
|
14744
14887
|
return null;
|
|
14745
|
-
const full =
|
|
14888
|
+
const full = join9(cwd, dbPath);
|
|
14746
14889
|
if (!existsSync6(full))
|
|
14747
14890
|
return null;
|
|
14748
14891
|
let realCwd;
|
|
14749
14892
|
let realFull;
|
|
14750
14893
|
try {
|
|
14751
|
-
realCwd =
|
|
14752
|
-
realFull =
|
|
14894
|
+
realCwd = realpathSync5(cwd);
|
|
14895
|
+
realFull = realpathSync5(full);
|
|
14753
14896
|
} catch {
|
|
14754
14897
|
return null;
|
|
14755
14898
|
}
|
|
14756
|
-
const rel =
|
|
14899
|
+
const rel = relative4(realCwd, realFull);
|
|
14757
14900
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/"))
|
|
14758
14901
|
return null;
|
|
14759
14902
|
if (!isSqliteFile(realFull))
|
|
@@ -14912,7 +15055,7 @@ function resolveEnvValue(raw, composeDirEnv = {}) {
|
|
|
14912
15055
|
}
|
|
14913
15056
|
async function readDotenvAsync(composeDir) {
|
|
14914
15057
|
try {
|
|
14915
|
-
const content = await readFile2(
|
|
15058
|
+
const content = await readFile2(join9(composeDir, ".env"), "utf-8");
|
|
14916
15059
|
return parseDotenvContent(content);
|
|
14917
15060
|
} catch {
|
|
14918
15061
|
return {};
|
|
@@ -15034,7 +15177,7 @@ function parseComposeContent(content, filepath, composeDir, cwd, composeDirEnv,
|
|
|
15034
15177
|
for (let match = serviceRegex.exec(servicesBlock);match !== null; match = serviceRegex.exec(servicesBlock)) {
|
|
15035
15178
|
servicePositions.push({ name: match[1], start: match.index });
|
|
15036
15179
|
}
|
|
15037
|
-
const relDir =
|
|
15180
|
+
const relDir = relative4(cwd, composeDir);
|
|
15038
15181
|
const isRoot = relDir === "" || relDir === ".";
|
|
15039
15182
|
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
15040
15183
|
const filename = basename(filepath);
|
|
@@ -15139,7 +15282,7 @@ async function walkForMarkerFileAsync(dir, depth, omitSet, hasCapacity, visitDir
|
|
|
15139
15282
|
return;
|
|
15140
15283
|
if (omitSet.has(entry.toLowerCase()))
|
|
15141
15284
|
continue;
|
|
15142
|
-
const full =
|
|
15285
|
+
const full = join9(dir, entry);
|
|
15143
15286
|
let entryStat;
|
|
15144
15287
|
try {
|
|
15145
15288
|
entryStat = await lstat(full);
|
|
@@ -15166,7 +15309,7 @@ async function discoverDockerDatabasesAsync(cwd, omitDirNames = [], signal) {
|
|
|
15166
15309
|
omitSet.add("node_modules");
|
|
15167
15310
|
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_DOCKER_SERVICES, async (dir) => {
|
|
15168
15311
|
for (const filename of COMPOSE_FILENAMES) {
|
|
15169
|
-
const filepath =
|
|
15312
|
+
const filepath = join9(dir, filename);
|
|
15170
15313
|
if (await pathExistsAsync(filepath)) {
|
|
15171
15314
|
await parseComposeFileAsync(filepath, dir, cwd, results);
|
|
15172
15315
|
break;
|
|
@@ -15330,7 +15473,7 @@ async function discoverSupabaseCliProjectsAsync(cwd, omitDirNames = [], signal)
|
|
|
15330
15473
|
omitSet.add("node_modules");
|
|
15331
15474
|
const results = [];
|
|
15332
15475
|
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_SUPABASE_PROJECTS, async (dir) => {
|
|
15333
|
-
const configPath =
|
|
15476
|
+
const configPath = join9(dir, "supabase", "config.toml");
|
|
15334
15477
|
if (!await pathExistsAsync(configPath))
|
|
15335
15478
|
return;
|
|
15336
15479
|
try {
|
|
@@ -15338,7 +15481,7 @@ async function discoverSupabaseCliProjectsAsync(cwd, omitDirNames = [], signal)
|
|
|
15338
15481
|
const parsed = parseSupabaseConfigToml(content);
|
|
15339
15482
|
if (!parsed)
|
|
15340
15483
|
return;
|
|
15341
|
-
const relDir =
|
|
15484
|
+
const relDir = relative4(cwd, dir);
|
|
15342
15485
|
const isRoot = relDir === "" || relDir === ".";
|
|
15343
15486
|
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
15344
15487
|
const id = isRoot ? `supabase:${parsed.projectId}` : `supabase:${parsed.projectId}@${encodeURIComponent(relDirSlash)}`;
|
|
@@ -15439,16 +15582,16 @@ function makeId(prefix) {
|
|
|
15439
15582
|
|
|
15440
15583
|
// web-src/server/worktree-watcher.ts
|
|
15441
15584
|
import {
|
|
15442
|
-
lstatSync as
|
|
15585
|
+
lstatSync as lstatSync3,
|
|
15443
15586
|
readdirSync as nodeReaddirSync,
|
|
15444
15587
|
watch as nodeWatch
|
|
15445
15588
|
} from "node:fs";
|
|
15446
|
-
import { join as
|
|
15589
|
+
import { join as join10, relative as relative5 } from "node:path";
|
|
15447
15590
|
function normalizeRelativePath(path) {
|
|
15448
15591
|
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
15449
15592
|
}
|
|
15450
15593
|
function isInsideRoot(root, path) {
|
|
15451
|
-
const rel =
|
|
15594
|
+
const rel = relative5(root, path).replace(/\\/g, "/");
|
|
15452
15595
|
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
15453
15596
|
}
|
|
15454
15597
|
function startWorktreeUpdateWatch(options) {
|
|
@@ -15456,14 +15599,14 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15456
15599
|
const readDirs = options.readdirSync || ((path) => nodeReaddirSync(path, { withFileTypes: true }));
|
|
15457
15600
|
const isDirectory = options.isDirectory || ((path) => {
|
|
15458
15601
|
try {
|
|
15459
|
-
return
|
|
15602
|
+
return lstatSync3(path).isDirectory();
|
|
15460
15603
|
} catch {
|
|
15461
15604
|
return false;
|
|
15462
15605
|
}
|
|
15463
15606
|
});
|
|
15464
15607
|
const directorySignature = options.directorySignature || ((path) => {
|
|
15465
15608
|
try {
|
|
15466
|
-
const stats =
|
|
15609
|
+
const stats = lstatSync3(path);
|
|
15467
15610
|
if (!stats.isDirectory())
|
|
15468
15611
|
return null;
|
|
15469
15612
|
return `${stats.dev}:${stats.ino}`;
|
|
@@ -15486,7 +15629,7 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15486
15629
|
const pendingChangedPaths = new Set;
|
|
15487
15630
|
let watchLimitReported = false;
|
|
15488
15631
|
const ignored = (path) => isSkippableSearchPath(normalizeRelativePath(path), options.omitDirNames, options.excludeNames);
|
|
15489
|
-
const directoryRelativePath = (dir) => normalizeRelativePath(
|
|
15632
|
+
const directoryRelativePath = (dir) => normalizeRelativePath(relative5(options.root, dir));
|
|
15490
15633
|
const ignoredDirectory = (dir) => {
|
|
15491
15634
|
const rel = directoryRelativePath(dir);
|
|
15492
15635
|
return Boolean(rel && ignored(rel));
|
|
@@ -15552,7 +15695,7 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15552
15695
|
for (const entry of entries) {
|
|
15553
15696
|
if (!entry.isDirectory())
|
|
15554
15697
|
continue;
|
|
15555
|
-
const child =
|
|
15698
|
+
const child = join10(dir, entry.name);
|
|
15556
15699
|
if (ignoredDirectory(child))
|
|
15557
15700
|
continue;
|
|
15558
15701
|
children.push(child);
|
|
@@ -15636,10 +15779,10 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15636
15779
|
scheduleUpdate();
|
|
15637
15780
|
return;
|
|
15638
15781
|
}
|
|
15639
|
-
const changed = normalizeRelativePath(
|
|
15782
|
+
const changed = normalizeRelativePath(join10(rel, filename.toString()));
|
|
15640
15783
|
if (ignored(changed))
|
|
15641
15784
|
return;
|
|
15642
|
-
const fullChangedPath =
|
|
15785
|
+
const fullChangedPath = join10(options.root, changed);
|
|
15643
15786
|
if (!isInsideRoot(options.root, fullChangedPath))
|
|
15644
15787
|
return;
|
|
15645
15788
|
if (initialScanAsync) {
|
|
@@ -15688,9 +15831,9 @@ var init_worktree_watcher = __esm(() => {
|
|
|
15688
15831
|
});
|
|
15689
15832
|
|
|
15690
15833
|
// web-src/server/state-store.ts
|
|
15691
|
-
import { join as
|
|
15834
|
+
import { join as join11 } from "node:path";
|
|
15692
15835
|
function codeViewerPath(root, fileName) {
|
|
15693
|
-
return
|
|
15836
|
+
return join11(root, CODE_VIEWER_DIR2, fileName);
|
|
15694
15837
|
}
|
|
15695
15838
|
function isRecord(value) {
|
|
15696
15839
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -16806,7 +16949,7 @@ var init_connection_pool = __esm(() => {
|
|
|
16806
16949
|
// web-src/server/database/connections-store.ts
|
|
16807
16950
|
import { randomUUID } from "node:crypto";
|
|
16808
16951
|
import { chmod } from "node:fs/promises";
|
|
16809
|
-
import { join as
|
|
16952
|
+
import { join as join12 } from "node:path";
|
|
16810
16953
|
function secretKey(cwd, id) {
|
|
16811
16954
|
return `${cwd}\x00${id}`;
|
|
16812
16955
|
}
|
|
@@ -16827,7 +16970,7 @@ function withRuntimeSecrets(cwd, connection) {
|
|
|
16827
16970
|
};
|
|
16828
16971
|
}
|
|
16829
16972
|
function connectionsFilePath(root) {
|
|
16830
|
-
return
|
|
16973
|
+
return join12(root, ".code-viewer", CONNECTIONS_FILE_NAME);
|
|
16831
16974
|
}
|
|
16832
16975
|
function emptyState() {
|
|
16833
16976
|
return { version: 1, connections: [] };
|
|
@@ -18609,9 +18752,9 @@ var init_handle_s3 = __esm(() => {
|
|
|
18609
18752
|
});
|
|
18610
18753
|
|
|
18611
18754
|
// web-src/server/database/query-history.ts
|
|
18612
|
-
import { join as
|
|
18755
|
+
import { join as join13 } from "node:path";
|
|
18613
18756
|
function historyFilePath(root) {
|
|
18614
|
-
return
|
|
18757
|
+
return join13(root, CODE_VIEWER_DIR3, HISTORY_FILE_NAME);
|
|
18615
18758
|
}
|
|
18616
18759
|
function emptyState2() {
|
|
18617
18760
|
return { version: 1, entries: [] };
|
|
@@ -18761,9 +18904,9 @@ var init_query_history = __esm(() => {
|
|
|
18761
18904
|
// web-src/server/database/snapshot-store.ts
|
|
18762
18905
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
18763
18906
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
18764
|
-
import { join as
|
|
18907
|
+
import { join as join14 } from "node:path";
|
|
18765
18908
|
async function getStoreDb(cwd) {
|
|
18766
|
-
const dbPath =
|
|
18909
|
+
const dbPath = join14(cwd, CODE_VIEWER_DIR4, SNAPSHOT_DB_NAME);
|
|
18767
18910
|
if (storeDb && storeDbPath === dbPath)
|
|
18768
18911
|
return storeDb;
|
|
18769
18912
|
if (storeDb) {
|
|
@@ -18771,7 +18914,7 @@ async function getStoreDb(cwd) {
|
|
|
18771
18914
|
storeDb.close();
|
|
18772
18915
|
} catch {}
|
|
18773
18916
|
}
|
|
18774
|
-
mkdirSync3(
|
|
18917
|
+
mkdirSync3(join14(cwd, CODE_VIEWER_DIR4), { recursive: true });
|
|
18775
18918
|
const DbClass = await loadSqliteClass();
|
|
18776
18919
|
storeDb = new DbClass(dbPath);
|
|
18777
18920
|
storeDbPath = dbPath;
|
|
@@ -19377,9 +19520,9 @@ var init_snapshot_runner = __esm(() => {
|
|
|
19377
19520
|
});
|
|
19378
19521
|
|
|
19379
19522
|
// web-src/server/database/tabs-store.ts
|
|
19380
|
-
import { join as
|
|
19523
|
+
import { join as join15 } from "node:path";
|
|
19381
19524
|
function tabsFilePath(root) {
|
|
19382
|
-
return
|
|
19525
|
+
return join15(root, CODE_VIEWER_DIR5, TABS_FILE_NAME);
|
|
19383
19526
|
}
|
|
19384
19527
|
function emptyState3() {
|
|
19385
19528
|
return { version: 1, tabs: [], activeTabId: null };
|
|
@@ -21316,7 +21459,7 @@ var init_handle = __esm(() => {
|
|
|
21316
21459
|
|
|
21317
21460
|
// web-src/server/doctor.ts
|
|
21318
21461
|
import { accessSync as accessSync2, constants as constants2, readFileSync as readFileSync6, statSync as statSync5 } from "node:fs";
|
|
21319
|
-
import { dirname as
|
|
21462
|
+
import { dirname as dirname5, join as join16, relative as relative6 } from "node:path";
|
|
21320
21463
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
21321
21464
|
function statusWorse(a, b) {
|
|
21322
21465
|
const rank = { ok: 0, warn: 1, error: 2 };
|
|
@@ -21405,12 +21548,12 @@ function findCodeViewerPackageJson() {
|
|
|
21405
21548
|
try {
|
|
21406
21549
|
let cursor;
|
|
21407
21550
|
try {
|
|
21408
|
-
cursor =
|
|
21551
|
+
cursor = dirname5(fileURLToPath2(import.meta.url));
|
|
21409
21552
|
} catch {
|
|
21410
|
-
cursor =
|
|
21553
|
+
cursor = dirname5(process.argv[1] || ".");
|
|
21411
21554
|
}
|
|
21412
21555
|
for (let depth = 0;depth < 8; depth += 1) {
|
|
21413
|
-
const candidate =
|
|
21556
|
+
const candidate = join16(cursor, "package.json");
|
|
21414
21557
|
try {
|
|
21415
21558
|
const raw = readFileSync6(candidate, "utf8");
|
|
21416
21559
|
const pkg = JSON.parse(raw);
|
|
@@ -21418,7 +21561,7 @@ function findCodeViewerPackageJson() {
|
|
|
21418
21561
|
return { version: pkg.version, path: candidate };
|
|
21419
21562
|
}
|
|
21420
21563
|
} catch {}
|
|
21421
|
-
const next =
|
|
21564
|
+
const next = dirname5(cursor);
|
|
21422
21565
|
if (next === cursor)
|
|
21423
21566
|
break;
|
|
21424
21567
|
cursor = next;
|
|
@@ -21506,7 +21649,7 @@ async function checkSqlite(cwd) {
|
|
|
21506
21649
|
return { id: "sqlite", title: "SQLite driver", rows };
|
|
21507
21650
|
}
|
|
21508
21651
|
async function trySnapshotDbOpen(cwd) {
|
|
21509
|
-
const dbPath =
|
|
21652
|
+
const dbPath = join16(cwd, SNAPSHOT_DB_REL);
|
|
21510
21653
|
try {
|
|
21511
21654
|
statSync5(dbPath);
|
|
21512
21655
|
} catch {
|
|
@@ -21527,8 +21670,8 @@ async function trySnapshotDbOpen(cwd) {
|
|
|
21527
21670
|
}
|
|
21528
21671
|
}
|
|
21529
21672
|
function checkSnapshotStore(cwd) {
|
|
21530
|
-
const dbPath =
|
|
21531
|
-
const dir =
|
|
21673
|
+
const dbPath = join16(cwd, SNAPSHOT_DB_REL);
|
|
21674
|
+
const dir = dirname5(dbPath);
|
|
21532
21675
|
let dirStatus = "ok";
|
|
21533
21676
|
let dirDetail = dir;
|
|
21534
21677
|
let dirHint;
|
|
@@ -21601,7 +21744,7 @@ async function checkGit(cwd, signal) {
|
|
|
21601
21744
|
if (repoCheck && repoCheck.code === 0 && /true/.test(repoCheck.stdout)) {
|
|
21602
21745
|
const topRes = await runCached(gitCache, TTL.gitRepo, commandForExternal("git"), ["rev-parse", "--show-toplevel"], TIMEOUT.git, signal, cwd);
|
|
21603
21746
|
const top = topRes?.stdout.trim() || cwd;
|
|
21604
|
-
const insideCwd =
|
|
21747
|
+
const insideCwd = relative6(top, cwd) || ".";
|
|
21605
21748
|
rows.push({
|
|
21606
21749
|
id: "git.repo",
|
|
21607
21750
|
title: "Working tree",
|
|
@@ -22529,44 +22672,6 @@ function normalizeNewDirectoryName(name) {
|
|
|
22529
22672
|
return trimmed;
|
|
22530
22673
|
}
|
|
22531
22674
|
|
|
22532
|
-
// web-src/server/cache.ts
|
|
22533
|
-
import { lstatSync as lstatSync3 } from "node:fs";
|
|
22534
|
-
import { join as join16 } from "node:path";
|
|
22535
|
-
function cacheFresh(cached, now = Date.now(), ttlMs = CACHE_TTL_MS) {
|
|
22536
|
-
return !!cached && now - cached.storedAt <= ttlMs;
|
|
22537
|
-
}
|
|
22538
|
-
function setTimedCacheEntry(cache, key, value, now = Date.now(), maxEntries = MAX_TIMED_CACHE_ENTRIES) {
|
|
22539
|
-
cache.set(key, { ...value, storedAt: now });
|
|
22540
|
-
while (cache.size > maxEntries) {
|
|
22541
|
-
const oldest = cache.keys().next().value;
|
|
22542
|
-
if (oldest === undefined)
|
|
22543
|
-
break;
|
|
22544
|
-
cache.delete(oldest);
|
|
22545
|
-
}
|
|
22546
|
-
}
|
|
22547
|
-
function worktreeFileSignature(path, cwd) {
|
|
22548
|
-
try {
|
|
22549
|
-
const stats = lstatSync3(join16(cwd, path));
|
|
22550
|
-
const inode = "ino" in stats ? stats.ino : 0;
|
|
22551
|
-
return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
|
|
22552
|
-
} catch {
|
|
22553
|
-
return "state:missing";
|
|
22554
|
-
}
|
|
22555
|
-
}
|
|
22556
|
-
function fileDiffCacheKey(options) {
|
|
22557
|
-
const worktreeTarget = options.range.from === "worktree" || !options.range.to || options.range.to === "worktree";
|
|
22558
|
-
if (options.isUntracked && !worktreeTarget) {
|
|
22559
|
-
throw new Error("untracked file diffs require a worktree range");
|
|
22560
|
-
}
|
|
22561
|
-
const signature = worktreeTarget ? `\x00${worktreeFileSignature(options.path, options.cwd)}` : "";
|
|
22562
|
-
if (options.isUntracked) {
|
|
22563
|
-
return `u\x00${options.path}${signature}\x00${options.extras.join("\x00")}`;
|
|
22564
|
-
}
|
|
22565
|
-
return `t\x00${options.path}\x00${options.oldPath || ""}${signature}\x00${[...options.extras, ...options.args].join("\x00")}`;
|
|
22566
|
-
}
|
|
22567
|
-
var CACHE_TTL_MS = 1500, MAX_TIMED_CACHE_ENTRIES = 200;
|
|
22568
|
-
var init_cache = () => {};
|
|
22569
|
-
|
|
22570
22675
|
// web-src/server/dev-assets.ts
|
|
22571
22676
|
import { basename as basename2 } from "node:path";
|
|
22572
22677
|
function startDevAssetReload(options) {
|
|
@@ -23229,8 +23334,8 @@ var init_journal2 = __esm(() => {
|
|
|
23229
23334
|
});
|
|
23230
23335
|
|
|
23231
23336
|
// web-src/server/search-service.ts
|
|
23232
|
-
import { existsSync as existsSync7, lstatSync as lstatSync4, readFileSync as readFileSync7, realpathSync as
|
|
23233
|
-
import { join as join18, relative as
|
|
23337
|
+
import { existsSync as existsSync7, lstatSync as lstatSync4, readFileSync as readFileSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
23338
|
+
import { join as join18, relative as relative7 } from "node:path";
|
|
23234
23339
|
async function rgAvailableAsync(cwd) {
|
|
23235
23340
|
if (rgAvailableCache !== null)
|
|
23236
23341
|
return rgAvailableCache;
|
|
@@ -23266,12 +23371,12 @@ function safeWorktreePath(env, path) {
|
|
|
23266
23371
|
let realCwd;
|
|
23267
23372
|
let realFull;
|
|
23268
23373
|
try {
|
|
23269
|
-
realCwd =
|
|
23270
|
-
realFull =
|
|
23374
|
+
realCwd = realpathSync6(env.cwd);
|
|
23375
|
+
realFull = realpathSync6(full);
|
|
23271
23376
|
} catch {
|
|
23272
23377
|
return null;
|
|
23273
23378
|
}
|
|
23274
|
-
const rel =
|
|
23379
|
+
const rel = relative7(realCwd, realFull);
|
|
23275
23380
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/") || rel.startsWith("\\"))
|
|
23276
23381
|
return null;
|
|
23277
23382
|
if (isGitInternalPath(rel))
|
|
@@ -25015,7 +25120,7 @@ import {
|
|
|
25015
25120
|
mkdirSync as mkdirSync4,
|
|
25016
25121
|
openSync as openSync3,
|
|
25017
25122
|
readFileSync as readFileSync9,
|
|
25018
|
-
realpathSync as
|
|
25123
|
+
realpathSync as realpathSync7,
|
|
25019
25124
|
renameSync,
|
|
25020
25125
|
statSync as statSync6,
|
|
25021
25126
|
unlinkSync as unlinkSync2,
|
|
@@ -25023,7 +25128,7 @@ import {
|
|
|
25023
25128
|
writeFileSync as writeFileSync2
|
|
25024
25129
|
} from "node:fs";
|
|
25025
25130
|
import { homedir as homedir3 } from "node:os";
|
|
25026
|
-
import { basename as basename3, dirname as
|
|
25131
|
+
import { basename as basename3, dirname as dirname6, extname as extname2, join as join20, relative as relative8 } from "node:path";
|
|
25027
25132
|
function parseCli() {
|
|
25028
25133
|
const rest = [];
|
|
25029
25134
|
for (let i = 2;i < process.argv.length; i++) {
|
|
@@ -25077,7 +25182,7 @@ Examples:
|
|
|
25077
25182
|
process.exit(1);
|
|
25078
25183
|
}
|
|
25079
25184
|
try {
|
|
25080
|
-
cwd =
|
|
25185
|
+
cwd = realpathSync7(next);
|
|
25081
25186
|
cwdWasExplicit = true;
|
|
25082
25187
|
} catch {
|
|
25083
25188
|
console.error("--cwd must point to an existing directory");
|
|
@@ -25508,7 +25613,7 @@ function worktreePath(path) {
|
|
|
25508
25613
|
function safeOpenWorktreePath(path) {
|
|
25509
25614
|
if (path === "") {
|
|
25510
25615
|
try {
|
|
25511
|
-
const realCwd =
|
|
25616
|
+
const realCwd = realpathSync7(cwd);
|
|
25512
25617
|
if (isGitInternalPath(realCwd))
|
|
25513
25618
|
return null;
|
|
25514
25619
|
return realCwd;
|
|
@@ -25519,7 +25624,7 @@ function safeOpenWorktreePath(path) {
|
|
|
25519
25624
|
return safeWorktreePath2(path);
|
|
25520
25625
|
}
|
|
25521
25626
|
function parentRepoPath(path) {
|
|
25522
|
-
const parent =
|
|
25627
|
+
const parent = dirname6(path);
|
|
25523
25628
|
return parent === "." ? "" : parent;
|
|
25524
25629
|
}
|
|
25525
25630
|
function isoDate(ms) {
|
|
@@ -25577,6 +25682,21 @@ async function attachTreeEntryMetadata(target, entry) {
|
|
|
25577
25682
|
return { ...entry, ...await directoryMetadata(target, entry.path) };
|
|
25578
25683
|
if (entry.type !== "blob")
|
|
25579
25684
|
return entry;
|
|
25685
|
+
if (entry.is_symlink && target !== "worktree" && target !== "") {
|
|
25686
|
+
const symlinkMeta = await gitSymlinkTargetMetadataAsync(target, entry.path, cwd);
|
|
25687
|
+
if (symlinkMeta.symlink_target_type === "tree")
|
|
25688
|
+
return {
|
|
25689
|
+
...entry,
|
|
25690
|
+
...symlinkMeta,
|
|
25691
|
+
type: "tree",
|
|
25692
|
+
...await directoryMetadata(target, entry.path)
|
|
25693
|
+
};
|
|
25694
|
+
return {
|
|
25695
|
+
...entry,
|
|
25696
|
+
...symlinkMeta,
|
|
25697
|
+
...await fileMetadataForTarget(target, entry.path)
|
|
25698
|
+
};
|
|
25699
|
+
}
|
|
25580
25700
|
return { ...entry, ...await fileMetadataForTarget(target, entry.path) };
|
|
25581
25701
|
}
|
|
25582
25702
|
async function readReadme(target, dirPath) {
|
|
@@ -25599,6 +25719,22 @@ async function readReadme(target, dirPath) {
|
|
|
25599
25719
|
}
|
|
25600
25720
|
return null;
|
|
25601
25721
|
}
|
|
25722
|
+
function deletedTreeEntriesForPath(statusMap, basePath) {
|
|
25723
|
+
const entries = [];
|
|
25724
|
+
for (const [path, status] of statusMap) {
|
|
25725
|
+
if (status !== "D")
|
|
25726
|
+
continue;
|
|
25727
|
+
if (basePath) {
|
|
25728
|
+
if (!path.startsWith(`${basePath}/`))
|
|
25729
|
+
continue;
|
|
25730
|
+
}
|
|
25731
|
+
const rel = basePath ? path.slice(basePath.length + 1) : path;
|
|
25732
|
+
if (!rel || rel.includes("/"))
|
|
25733
|
+
continue;
|
|
25734
|
+
entries.push({ name: rel, path, type: "blob", status: "D" });
|
|
25735
|
+
}
|
|
25736
|
+
return entries;
|
|
25737
|
+
}
|
|
25602
25738
|
async function handleTree(url) {
|
|
25603
25739
|
const target = url.searchParams.get("ref") || url.searchParams.get("target") || "worktree";
|
|
25604
25740
|
const path = (url.searchParams.get("path") || "").replace(/^\/+|\/+$/g, "");
|
|
@@ -25625,12 +25761,21 @@ async function handleTree(url) {
|
|
|
25625
25761
|
if (tree.error)
|
|
25626
25762
|
return text(tree.error, tree.status ?? 500);
|
|
25627
25763
|
const entries = tree.entries.filter((entry) => !isExcludedScopePath(entry.path, excludeNames));
|
|
25764
|
+
const statusMap = target === "worktree" || target === "" ? await repoStatusMapAsync(cwd) : null;
|
|
25765
|
+
const withStatus = (entry) => {
|
|
25766
|
+
const status = statusMap?.get(entry.path);
|
|
25767
|
+
return status ? { ...entry, status } : entry;
|
|
25768
|
+
};
|
|
25769
|
+
const deletedEntries = !recursive && statusMap ? deletedTreeEntriesForPath(statusMap, path) : [];
|
|
25628
25770
|
return json2({
|
|
25629
25771
|
ref: target,
|
|
25630
25772
|
path,
|
|
25631
25773
|
project: basename3(cwd),
|
|
25632
25774
|
branch: await currentBranchMetadata(),
|
|
25633
|
-
entries: recursive ? entries
|
|
25775
|
+
entries: recursive ? entries.map(withStatus) : [
|
|
25776
|
+
...await Promise.all(entries.map((entry) => attachTreeEntryMetadata(target, entry).then(withStatus))),
|
|
25777
|
+
...deletedEntries
|
|
25778
|
+
],
|
|
25634
25779
|
readme: await readReadme(target, path),
|
|
25635
25780
|
upload_enabled: uploadEnabled && (target === "worktree" || target === "")
|
|
25636
25781
|
});
|
|
@@ -26326,7 +26471,7 @@ async function handleUploadFiles(req) {
|
|
|
26326
26471
|
if (total > MAX_UPLOAD_TOTAL_BYTES)
|
|
26327
26472
|
return text("upload too large", 413);
|
|
26328
26473
|
const target = join20(realDir, safeName);
|
|
26329
|
-
if (
|
|
26474
|
+
if (relative8(realDir, dirname6(target)) !== "")
|
|
26330
26475
|
return text("invalid filename", 400);
|
|
26331
26476
|
if (existsSync8(target))
|
|
26332
26477
|
return text("file exists", 409);
|
|
@@ -26492,10 +26637,10 @@ async function restoreTrashPath(originalPath, trashPath) {
|
|
|
26492
26637
|
return { ok: false, error: "trash item not found" };
|
|
26493
26638
|
try {
|
|
26494
26639
|
const trashRoot = join20(homedir3(), ".Trash");
|
|
26495
|
-
const trashRelative =
|
|
26640
|
+
const trashRelative = relative8(trashRoot, trashPath);
|
|
26496
26641
|
if (trashRelative === "" || trashRelative.startsWith("..") || trashRelative.startsWith("/") || trashRelative.startsWith("\\"))
|
|
26497
26642
|
return { ok: false, error: "invalid trash handle" };
|
|
26498
|
-
mkdirSync4(
|
|
26643
|
+
mkdirSync4(dirname6(original), { recursive: true });
|
|
26499
26644
|
renameSync(trashPath, original);
|
|
26500
26645
|
return { ok: true };
|
|
26501
26646
|
} catch (error) {
|