@youtyan/code-viewer 0.9.0 → 0.9.2
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 +18 -2
- package/dist/code-viewer.js +1098 -681
- package/package.json +1 -1
- package/web/app.js +94 -69
- package/web/index.html +1 -1
- package/web/style.css +18 -0
package/dist/code-viewer.js
CHANGED
|
@@ -1167,8 +1167,12 @@ function runBytesAsync(args, cwd, options = {}) {
|
|
|
1167
1167
|
return new Promise((resolve) => {
|
|
1168
1168
|
const proc = spawn(args[0], args.slice(1), {
|
|
1169
1169
|
cwd,
|
|
1170
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1170
|
+
stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"]
|
|
1171
1171
|
});
|
|
1172
|
+
if (options.stdin !== undefined) {
|
|
1173
|
+
proc.stdin?.on("error", () => {});
|
|
1174
|
+
proc.stdin?.end(options.stdin);
|
|
1175
|
+
}
|
|
1172
1176
|
const stdoutChunks = [];
|
|
1173
1177
|
const stderrChunks = [];
|
|
1174
1178
|
let stdoutBytes = 0;
|
|
@@ -1448,9 +1452,10 @@ function run(args, cwd) {
|
|
|
1448
1452
|
timeout: GIT_COMMAND_TIMEOUT_MS
|
|
1449
1453
|
});
|
|
1450
1454
|
}
|
|
1451
|
-
function runGitAsync(args, cwd) {
|
|
1455
|
+
function runGitAsync(args, cwd, options = {}) {
|
|
1452
1456
|
return runAsync(resolveGitArgs(args), cwd, {
|
|
1453
|
-
timeout: GIT_COMMAND_TIMEOUT_MS
|
|
1457
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
1458
|
+
...options
|
|
1454
1459
|
});
|
|
1455
1460
|
}
|
|
1456
1461
|
function resolveGitArgs(args) {
|
|
@@ -1530,7 +1535,7 @@ async function repoStatusMapAsync(cwd, now = Date.now()) {
|
|
|
1530
1535
|
"status",
|
|
1531
1536
|
"--porcelain=v1",
|
|
1532
1537
|
"-z",
|
|
1533
|
-
"--untracked-files=
|
|
1538
|
+
"--untracked-files=normal"
|
|
1534
1539
|
], cwd);
|
|
1535
1540
|
if (res.code !== 0)
|
|
1536
1541
|
return map;
|
|
@@ -1542,7 +1547,7 @@ async function repoStatusMapAsync(cwd, now = Date.now()) {
|
|
|
1542
1547
|
if (!path)
|
|
1543
1548
|
continue;
|
|
1544
1549
|
if (xy === "??") {
|
|
1545
|
-
map.set(path, "
|
|
1550
|
+
map.set(path, "U");
|
|
1546
1551
|
continue;
|
|
1547
1552
|
}
|
|
1548
1553
|
if (xy[0] === "R" || xy[0] === "C" || xy[1] === "R" || xy[1] === "C") {
|
|
@@ -1557,6 +1562,27 @@ async function repoStatusMapAsync(cwd, now = Date.now()) {
|
|
|
1557
1562
|
setTimedCacheEntry(repoStatusMapCache, cwd, { map }, now);
|
|
1558
1563
|
return map;
|
|
1559
1564
|
}
|
|
1565
|
+
function repoStatusForPath(map, path) {
|
|
1566
|
+
const own = map.get(path) ?? map.get(`${path}/`);
|
|
1567
|
+
if (own)
|
|
1568
|
+
return { code: own, inherited: false };
|
|
1569
|
+
for (let slash = path.lastIndexOf("/");slash > 0; slash = path.lastIndexOf("/", slash - 1)) {
|
|
1570
|
+
const ancestor = map.get(`${path.slice(0, slash)}/`);
|
|
1571
|
+
if (ancestor)
|
|
1572
|
+
return { code: ancestor, inherited: true };
|
|
1573
|
+
}
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
async function ignoredPathsAsync(paths, cwd) {
|
|
1577
|
+
if (!paths.length)
|
|
1578
|
+
return new Set;
|
|
1579
|
+
const res = await runGitAsync(["git", "check-ignore", "-z", "--stdin"], cwd, {
|
|
1580
|
+
stdin: paths.join("\x00")
|
|
1581
|
+
});
|
|
1582
|
+
if (res.code !== 0 && res.code !== 1)
|
|
1583
|
+
return new Set;
|
|
1584
|
+
return new Set(res.stdout.split("\x00").filter(Boolean));
|
|
1585
|
+
}
|
|
1560
1586
|
function showAsync(ref, path, cwd) {
|
|
1561
1587
|
return runGitAsync(["git", "show", `${ref}:${path}`], cwd);
|
|
1562
1588
|
}
|
|
@@ -9737,245 +9763,716 @@ var init_agent_help = __esm(() => {
|
|
|
9737
9763
|
];
|
|
9738
9764
|
});
|
|
9739
9765
|
|
|
9740
|
-
// web-src/server/
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
}
|
|
9750
|
-
if (typeof value === "object") {
|
|
9751
|
-
try {
|
|
9752
|
-
return JSON.stringify(value);
|
|
9753
|
-
} catch {
|
|
9754
|
-
return String(value);
|
|
9755
|
-
}
|
|
9756
|
-
}
|
|
9757
|
-
return value;
|
|
9766
|
+
// web-src/server/worktree-watcher.ts
|
|
9767
|
+
import {
|
|
9768
|
+
lstatSync as lstatSync3,
|
|
9769
|
+
readdirSync as nodeReaddirSync,
|
|
9770
|
+
watch as nodeWatch
|
|
9771
|
+
} from "node:fs";
|
|
9772
|
+
import { join as join9, relative as relative4 } from "node:path";
|
|
9773
|
+
function supportsNativeRecursiveWatch(platform) {
|
|
9774
|
+
return platform === "darwin" || platform === "win32";
|
|
9758
9775
|
}
|
|
9759
|
-
function
|
|
9760
|
-
return
|
|
9776
|
+
function normalizeRelativePath(path) {
|
|
9777
|
+
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
9761
9778
|
}
|
|
9762
|
-
function
|
|
9763
|
-
|
|
9779
|
+
function isInsideRoot(root, path) {
|
|
9780
|
+
const rel = relative4(root, path).replace(/\\/g, "/");
|
|
9781
|
+
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
9764
9782
|
}
|
|
9765
|
-
function
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
const
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
return null;
|
|
9773
|
-
if (v === "true" || v === "t" || v === "1")
|
|
9774
|
-
return true;
|
|
9775
|
-
if (v === "false" || v === "f" || v === "0")
|
|
9783
|
+
function startWorktreeUpdateWatch(options) {
|
|
9784
|
+
const watch = options.watch || nodeWatch;
|
|
9785
|
+
const readDirs = options.readdirSync || ((path) => nodeReaddirSync(path, { withFileTypes: true }));
|
|
9786
|
+
const isDirectory = options.isDirectory || ((path) => {
|
|
9787
|
+
try {
|
|
9788
|
+
return lstatSync3(path).isDirectory();
|
|
9789
|
+
} catch {
|
|
9776
9790
|
return false;
|
|
9777
|
-
|
|
9778
|
-
}
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9791
|
+
}
|
|
9792
|
+
});
|
|
9793
|
+
const directorySignature = options.directorySignature || ((path) => {
|
|
9794
|
+
try {
|
|
9795
|
+
const stats = lstatSync3(path);
|
|
9796
|
+
if (!stats.isDirectory())
|
|
9797
|
+
return null;
|
|
9798
|
+
return `${stats.dev}:${stats.ino}`;
|
|
9799
|
+
} catch {
|
|
9782
9800
|
return null;
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
const
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
const
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
if (
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9801
|
+
}
|
|
9802
|
+
});
|
|
9803
|
+
const setTimer = options.setTimeoutFn || setTimeout;
|
|
9804
|
+
const clearTimer = options.clearTimeoutFn || clearTimeout;
|
|
9805
|
+
const debounceMs = options.debounceMs ?? 250;
|
|
9806
|
+
const recursive = options.recursive === true;
|
|
9807
|
+
const maxWatchedDirectories = Math.max(1, Math.floor(options.maxWatchedDirectories ?? DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT));
|
|
9808
|
+
const watchers = new Map;
|
|
9809
|
+
const signatures = new Map;
|
|
9810
|
+
const initialScanAsync = options.initialScanMode === "async" || (!options.watch || options.watch === nodeWatch) && !options.readdirSync;
|
|
9811
|
+
const initialScanQueue = [];
|
|
9812
|
+
let initialScanTimer = null;
|
|
9813
|
+
let processingInitialScan = false;
|
|
9814
|
+
const pendingPathInspections = new Map;
|
|
9815
|
+
let pathInspectionTimer = null;
|
|
9816
|
+
let timer = null;
|
|
9817
|
+
const pendingChangedPaths = new Set;
|
|
9818
|
+
let fullUpdatePending = false;
|
|
9819
|
+
let watchLimitReported = false;
|
|
9820
|
+
const ignored = (path) => isSkippableSearchPath(normalizeRelativePath(path), options.omitDirNames, options.excludeNames);
|
|
9821
|
+
const directoryRelativePath = (dir) => normalizeRelativePath(relative4(options.root, dir));
|
|
9822
|
+
const ignoredDirectory = (dir) => {
|
|
9823
|
+
const rel = directoryRelativePath(dir);
|
|
9824
|
+
return Boolean(rel && ignored(rel));
|
|
9825
|
+
};
|
|
9826
|
+
const scheduleUpdate = (changedPath) => {
|
|
9827
|
+
if (changedPath)
|
|
9828
|
+
pendingChangedPaths.add(changedPath);
|
|
9829
|
+
else
|
|
9830
|
+
fullUpdatePending = true;
|
|
9831
|
+
if (timer)
|
|
9832
|
+
clearTimer(timer);
|
|
9833
|
+
timer = setTimer(() => {
|
|
9834
|
+
timer = null;
|
|
9835
|
+
const paths = !fullUpdatePending && pendingChangedPaths.size ? [...pendingChangedPaths] : undefined;
|
|
9836
|
+
pendingChangedPaths.clear();
|
|
9837
|
+
fullUpdatePending = false;
|
|
9838
|
+
options.onUpdate(paths);
|
|
9839
|
+
}, debounceMs);
|
|
9840
|
+
};
|
|
9841
|
+
const reportWatchLimit = () => {
|
|
9842
|
+
if (watchLimitReported)
|
|
9843
|
+
return;
|
|
9844
|
+
watchLimitReported = true;
|
|
9845
|
+
options.onWatchLimit?.(maxWatchedDirectories);
|
|
9846
|
+
options.onError?.(new Error(`worktree watcher cap reached (${maxWatchedDirectories}); subsequent changes may be missed`));
|
|
9847
|
+
};
|
|
9848
|
+
const closeSubtree = (dir) => {
|
|
9849
|
+
for (const [watchedDir, watcher] of [...watchers]) {
|
|
9850
|
+
if (watchedDir !== dir && !watchedDir.startsWith(`${dir}/`))
|
|
9851
|
+
continue;
|
|
9852
|
+
try {
|
|
9853
|
+
watcher.close?.();
|
|
9854
|
+
} catch {}
|
|
9855
|
+
watchers.delete(watchedDir);
|
|
9856
|
+
signatures.delete(watchedDir);
|
|
9857
|
+
}
|
|
9858
|
+
};
|
|
9859
|
+
const closeAll = () => {
|
|
9860
|
+
if (initialScanTimer) {
|
|
9861
|
+
clearTimer(initialScanTimer);
|
|
9862
|
+
initialScanTimer = null;
|
|
9863
|
+
}
|
|
9864
|
+
if (pathInspectionTimer) {
|
|
9865
|
+
clearTimer(pathInspectionTimer);
|
|
9866
|
+
pathInspectionTimer = null;
|
|
9867
|
+
}
|
|
9868
|
+
initialScanQueue.length = 0;
|
|
9869
|
+
pendingPathInspections.clear();
|
|
9870
|
+
for (const watcher of [...watchers.values()]) {
|
|
9871
|
+
try {
|
|
9872
|
+
watcher.close?.();
|
|
9873
|
+
} catch {}
|
|
9874
|
+
}
|
|
9875
|
+
watchers.clear();
|
|
9876
|
+
signatures.clear();
|
|
9877
|
+
};
|
|
9878
|
+
const readChildDirectories = (dir) => {
|
|
9879
|
+
let entries;
|
|
9880
|
+
try {
|
|
9881
|
+
entries = readDirs(dir);
|
|
9882
|
+
} catch (error) {
|
|
9883
|
+
options.onError?.(error);
|
|
9884
|
+
return [];
|
|
9885
|
+
}
|
|
9886
|
+
const children = [];
|
|
9887
|
+
for (const entry of entries) {
|
|
9888
|
+
if (!entry.isDirectory())
|
|
9889
|
+
continue;
|
|
9890
|
+
const child = join9(dir, entry.name);
|
|
9891
|
+
if (ignoredDirectory(child))
|
|
9892
|
+
continue;
|
|
9893
|
+
children.push(child);
|
|
9894
|
+
}
|
|
9895
|
+
return children;
|
|
9896
|
+
};
|
|
9897
|
+
const processInitialScanQueue = () => {
|
|
9898
|
+
initialScanTimer = null;
|
|
9899
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
9900
|
+
reportWatchLimit();
|
|
9901
|
+
initialScanQueue.length = 0;
|
|
9902
|
+
return;
|
|
9903
|
+
}
|
|
9904
|
+
const next = initialScanQueue.shift();
|
|
9905
|
+
if (next) {
|
|
9906
|
+
processingInitialScan = true;
|
|
9907
|
+
try {
|
|
9908
|
+
watchDirectory(next, true);
|
|
9909
|
+
} finally {
|
|
9910
|
+
processingInitialScan = false;
|
|
9823
9911
|
}
|
|
9824
9912
|
}
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
if (
|
|
9830
|
-
|
|
9831
|
-
}
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
const
|
|
9839
|
-
if (
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
}
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
}
|
|
9863
|
-
|
|
9864
|
-
|
|
9913
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
9914
|
+
reportWatchLimit();
|
|
9915
|
+
initialScanQueue.length = 0;
|
|
9916
|
+
}
|
|
9917
|
+
if (initialScanQueue.length)
|
|
9918
|
+
initialScanTimer = setTimer(processInitialScanQueue, 50);
|
|
9919
|
+
};
|
|
9920
|
+
const queueInitialChildren = (dir) => {
|
|
9921
|
+
const remaining = maxWatchedDirectories - watchers.size;
|
|
9922
|
+
if (remaining <= 0) {
|
|
9923
|
+
reportWatchLimit();
|
|
9924
|
+
return;
|
|
9925
|
+
}
|
|
9926
|
+
const children = readChildDirectories(dir);
|
|
9927
|
+
if (children.length > remaining)
|
|
9928
|
+
reportWatchLimit();
|
|
9929
|
+
initialScanQueue.push(...children.slice(0, remaining));
|
|
9930
|
+
if (!initialScanTimer && !processingInitialScan)
|
|
9931
|
+
initialScanTimer = setTimer(processInitialScanQueue, 5000);
|
|
9932
|
+
};
|
|
9933
|
+
const processChangedPath = (changed, fullChangedPath) => {
|
|
9934
|
+
const known = watchers.has(fullChangedPath);
|
|
9935
|
+
if (isDirectory(fullChangedPath)) {
|
|
9936
|
+
if (known) {
|
|
9937
|
+
const signature = directorySignature(fullChangedPath);
|
|
9938
|
+
if (signature && signature !== signatures.get(fullChangedPath)) {
|
|
9939
|
+
closeSubtree(fullChangedPath);
|
|
9940
|
+
watchDirectory(fullChangedPath, initialScanAsync);
|
|
9941
|
+
}
|
|
9942
|
+
scheduleUpdate(changed);
|
|
9943
|
+
return;
|
|
9944
|
+
}
|
|
9945
|
+
watchDirectory(fullChangedPath, initialScanAsync);
|
|
9946
|
+
} else if (known) {
|
|
9947
|
+
closeSubtree(fullChangedPath);
|
|
9948
|
+
}
|
|
9949
|
+
scheduleUpdate(changed);
|
|
9950
|
+
};
|
|
9951
|
+
const processPathInspections = () => {
|
|
9952
|
+
pathInspectionTimer = null;
|
|
9953
|
+
const entries = [...pendingPathInspections];
|
|
9954
|
+
pendingPathInspections.clear();
|
|
9955
|
+
for (const [changed, fullChangedPath] of entries) {
|
|
9956
|
+
processChangedPath(changed, fullChangedPath);
|
|
9957
|
+
}
|
|
9958
|
+
};
|
|
9959
|
+
const queuePathInspection = (changed, fullChangedPath) => {
|
|
9960
|
+
pendingPathInspections.set(changed, fullChangedPath);
|
|
9961
|
+
if (!pathInspectionTimer)
|
|
9962
|
+
pathInspectionTimer = setTimer(processPathInspections, 25);
|
|
9963
|
+
};
|
|
9964
|
+
const watchDirectory = (dir, initialScan = false) => {
|
|
9965
|
+
if (watchers.has(dir))
|
|
9966
|
+
return;
|
|
9967
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
9968
|
+
reportWatchLimit();
|
|
9969
|
+
return;
|
|
9970
|
+
}
|
|
9971
|
+
const rel = directoryRelativePath(dir);
|
|
9972
|
+
if (rel && ignored(rel))
|
|
9973
|
+
return;
|
|
9974
|
+
try {
|
|
9975
|
+
const watcher = watch(dir, { persistent: false, recursive }, (_event, filename) => {
|
|
9976
|
+
if (!filename) {
|
|
9977
|
+
scheduleUpdate();
|
|
9978
|
+
return;
|
|
9979
|
+
}
|
|
9980
|
+
const changed = normalizeRelativePath(join9(rel, filename.toString()));
|
|
9981
|
+
if (ignored(changed))
|
|
9982
|
+
return;
|
|
9983
|
+
const fullChangedPath = join9(options.root, changed);
|
|
9984
|
+
if (!isInsideRoot(options.root, fullChangedPath))
|
|
9985
|
+
return;
|
|
9986
|
+
if (recursive) {
|
|
9987
|
+
scheduleUpdate(changed);
|
|
9988
|
+
return;
|
|
9989
|
+
}
|
|
9990
|
+
if (initialScanAsync) {
|
|
9991
|
+
queuePathInspection(changed, fullChangedPath);
|
|
9992
|
+
return;
|
|
9993
|
+
}
|
|
9994
|
+
processChangedPath(changed, fullChangedPath);
|
|
9995
|
+
}) || {};
|
|
9996
|
+
watchers.set(dir, watcher);
|
|
9997
|
+
const signature = directorySignature(dir);
|
|
9998
|
+
if (signature)
|
|
9999
|
+
signatures.set(dir, signature);
|
|
10000
|
+
watcher.on?.("error", () => {
|
|
10001
|
+
if (watchers.get(dir) === watcher) {
|
|
10002
|
+
watchers.delete(dir);
|
|
10003
|
+
signatures.delete(dir);
|
|
10004
|
+
}
|
|
10005
|
+
});
|
|
10006
|
+
watcher.on?.("close", () => {
|
|
10007
|
+
if (watchers.get(dir) === watcher) {
|
|
10008
|
+
watchers.delete(dir);
|
|
10009
|
+
signatures.delete(dir);
|
|
10010
|
+
}
|
|
10011
|
+
});
|
|
10012
|
+
} catch (error) {
|
|
10013
|
+
options.onError?.(error);
|
|
10014
|
+
return;
|
|
10015
|
+
}
|
|
10016
|
+
if (recursive)
|
|
10017
|
+
return;
|
|
10018
|
+
if (initialScanAsync && initialScan) {
|
|
10019
|
+
queueInitialChildren(dir);
|
|
10020
|
+
return;
|
|
10021
|
+
}
|
|
10022
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
10023
|
+
reportWatchLimit();
|
|
10024
|
+
return;
|
|
10025
|
+
}
|
|
10026
|
+
for (const child of readChildDirectories(dir))
|
|
10027
|
+
watchDirectory(child);
|
|
10028
|
+
};
|
|
10029
|
+
watchDirectory(options.root, true);
|
|
10030
|
+
return { started: watchers.size > 0, close: closeAll };
|
|
9865
10031
|
}
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
10032
|
+
var DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT = 1024, MIN_WORKTREE_WATCH_DIRECTORY_LIMIT = 1, MAX_WORKTREE_WATCH_DIRECTORY_LIMIT = 65536;
|
|
10033
|
+
var init_worktree_watcher = __esm(() => {
|
|
10034
|
+
init_search();
|
|
10035
|
+
});
|
|
10036
|
+
|
|
10037
|
+
// web-src/server/watch-child.ts
|
|
10038
|
+
var exports_watch_child = {};
|
|
10039
|
+
__export(exports_watch_child, {
|
|
10040
|
+
runWatchChild: () => runWatchChild,
|
|
10041
|
+
parsePorcelainV2: () => parsePorcelainV2
|
|
10042
|
+
});
|
|
10043
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
10044
|
+
import { createReadStream as createReadStream2, lstatSync as lstatSync4 } from "node:fs";
|
|
10045
|
+
import { join as join10 } from "node:path";
|
|
10046
|
+
function send(message) {
|
|
10047
|
+
process.stdout.write(`${JSON.stringify(message)}
|
|
10048
|
+
`);
|
|
9879
10049
|
}
|
|
9880
|
-
function
|
|
9881
|
-
return
|
|
10050
|
+
function readConfigLine() {
|
|
10051
|
+
return new Promise((resolve2, reject) => {
|
|
10052
|
+
let buffer = "";
|
|
10053
|
+
const stdin = process.stdin;
|
|
10054
|
+
stdin.setEncoding("utf8");
|
|
10055
|
+
const onData = (chunk) => {
|
|
10056
|
+
buffer += chunk;
|
|
10057
|
+
const newline = buffer.indexOf(`
|
|
10058
|
+
`);
|
|
10059
|
+
if (newline === -1)
|
|
10060
|
+
return;
|
|
10061
|
+
stdin.off("data", onData);
|
|
10062
|
+
stdin.off("end", onEnd);
|
|
10063
|
+
resolve2(buffer.slice(0, newline));
|
|
10064
|
+
};
|
|
10065
|
+
const onEnd = () => {
|
|
10066
|
+
stdin.off("data", onData);
|
|
10067
|
+
reject(new Error("watch child: stdin closed before config arrived"));
|
|
10068
|
+
};
|
|
10069
|
+
stdin.on("data", onData);
|
|
10070
|
+
stdin.on("end", onEnd);
|
|
10071
|
+
});
|
|
9882
10072
|
}
|
|
9883
|
-
function
|
|
9884
|
-
return
|
|
10073
|
+
function hashFile(path) {
|
|
10074
|
+
return new Promise((resolve2) => {
|
|
10075
|
+
const hash = createHash2("sha256");
|
|
10076
|
+
const stream = createReadStream2(path);
|
|
10077
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
10078
|
+
stream.on("end", () => resolve2(hash.digest("hex")));
|
|
10079
|
+
stream.on("error", () => resolve2("unreadable"));
|
|
10080
|
+
});
|
|
9885
10081
|
}
|
|
9886
|
-
function
|
|
9887
|
-
|
|
9888
|
-
|
|
10082
|
+
function parsePorcelainV2(raw) {
|
|
10083
|
+
const records = raw.split("\x00");
|
|
10084
|
+
let head = "";
|
|
10085
|
+
const entries = [];
|
|
10086
|
+
for (let i = 0;i < records.length; i++) {
|
|
10087
|
+
const record = records[i];
|
|
10088
|
+
if (!record)
|
|
10089
|
+
continue;
|
|
10090
|
+
if (record.startsWith("# branch.oid ")) {
|
|
10091
|
+
head = record.slice("# branch.oid ".length);
|
|
10092
|
+
continue;
|
|
10093
|
+
}
|
|
10094
|
+
if (record.startsWith("# "))
|
|
10095
|
+
continue;
|
|
10096
|
+
const kind = record[0];
|
|
10097
|
+
if (kind === "1" || kind === "2" || kind === "u") {
|
|
10098
|
+
const metaCount = kind === "1" ? 8 : kind === "2" ? 9 : 10;
|
|
10099
|
+
const status = record.split(" ").slice(0, metaCount).join(" ");
|
|
10100
|
+
entries.push({ path: record.slice(status.length + 1), status });
|
|
10101
|
+
if (kind === "2")
|
|
10102
|
+
i++;
|
|
10103
|
+
continue;
|
|
10104
|
+
}
|
|
10105
|
+
if (kind === "?" || kind === "!") {
|
|
10106
|
+
entries.push({ path: record.slice(2), status: kind });
|
|
10107
|
+
}
|
|
9889
10108
|
}
|
|
9890
|
-
|
|
9891
|
-
const params = [];
|
|
9892
|
-
const cols = cells.map((c) => sanitizeIdentifier(c.column, kind));
|
|
9893
|
-
const placeholders = cells.map((c) => placeValue(coerceCell(c, columnTypes.get(c.column) ?? "TEXT"), kind, useParams, params));
|
|
9894
|
-
const sql = `INSERT INTO ${sanitizeIdentifier(table, kind)} (${cols.join(", ")}) VALUES (${placeholders.join(", ")})`;
|
|
9895
|
-
return { sql, params };
|
|
10109
|
+
return { head, entries };
|
|
9896
10110
|
}
|
|
9897
|
-
function
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
10111
|
+
async function readWorktreeSnapshot(config) {
|
|
10112
|
+
const result = await runAsync([
|
|
10113
|
+
"git",
|
|
10114
|
+
"--no-optional-locks",
|
|
10115
|
+
"-c",
|
|
10116
|
+
"core.fsmonitor=false",
|
|
10117
|
+
"status",
|
|
10118
|
+
"--porcelain=v2",
|
|
10119
|
+
"-z",
|
|
10120
|
+
"--branch",
|
|
10121
|
+
"--untracked-files=all"
|
|
10122
|
+
], config.root, { timeout: 60000 });
|
|
10123
|
+
if (result.code !== 0)
|
|
10124
|
+
return null;
|
|
10125
|
+
const parsed = parsePorcelainV2(result.stdout);
|
|
10126
|
+
const entries = new Map;
|
|
10127
|
+
for (const entry of parsed.entries) {
|
|
10128
|
+
if (!entry.path)
|
|
10129
|
+
continue;
|
|
10130
|
+
if (isSkippableSearchPath(entry.path, config.omitDirNames, config.excludeNames))
|
|
10131
|
+
continue;
|
|
10132
|
+
entries.set(entry.path, await pathSignature(config.root, entry));
|
|
9903
10133
|
}
|
|
9904
|
-
|
|
9905
|
-
const params = [];
|
|
9906
|
-
const setSql = formatWriteComparisons(set, columnTypes, kind, useParams, params, ", ");
|
|
9907
|
-
const whereSql = formatWriteComparisons(pk, columnTypes, kind, useParams, params, " AND ");
|
|
9908
|
-
const sql = `UPDATE ${sanitizeIdentifier(table, kind)} SET ${setSql} WHERE ${whereSql}`;
|
|
9909
|
-
return { sql, params };
|
|
10134
|
+
return { head: parsed.head, entries };
|
|
9910
10135
|
}
|
|
9911
|
-
function
|
|
9912
|
-
|
|
9913
|
-
|
|
10136
|
+
async function pathSignature(root, entry) {
|
|
10137
|
+
const full = join10(root, entry.path);
|
|
10138
|
+
let stats;
|
|
10139
|
+
try {
|
|
10140
|
+
stats = lstatSync4(full);
|
|
10141
|
+
} catch {
|
|
10142
|
+
return `${entry.status}:absent`;
|
|
9914
10143
|
}
|
|
9915
|
-
const
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
return
|
|
10144
|
+
const base = `${entry.status}:${stats.size}:${stats.mtimeMs}:${stats.mode}`;
|
|
10145
|
+
if (stats.isFile() && stats.size <= MAX_HASHED_FILE_BYTES) {
|
|
10146
|
+
return `${base}:${await hashFile(full)}`;
|
|
10147
|
+
}
|
|
10148
|
+
return base;
|
|
9920
10149
|
}
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
}
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
throw new Error(`${label} must be an array`);
|
|
10150
|
+
function snapshotDiff(previous, next) {
|
|
10151
|
+
if (previous.head !== next.head)
|
|
10152
|
+
return { full: true, paths: [] };
|
|
10153
|
+
const paths = [];
|
|
10154
|
+
for (const [path, signature] of next.entries) {
|
|
10155
|
+
if (previous.entries.get(path) !== signature)
|
|
10156
|
+
paths.push(path);
|
|
9929
10157
|
}
|
|
9930
|
-
for (const
|
|
9931
|
-
if (!
|
|
9932
|
-
|
|
9933
|
-
}
|
|
10158
|
+
for (const path of previous.entries.keys()) {
|
|
10159
|
+
if (!next.entries.has(path))
|
|
10160
|
+
paths.push(path);
|
|
9934
10161
|
}
|
|
9935
|
-
return
|
|
10162
|
+
return { full: false, paths };
|
|
9936
10163
|
}
|
|
9937
|
-
function
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
10164
|
+
async function runWatchChild() {
|
|
10165
|
+
const config = JSON.parse(await readConfigLine());
|
|
10166
|
+
process.stdin.on("end", () => process.exit(0));
|
|
10167
|
+
process.stdin.on("close", () => process.exit(0));
|
|
10168
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
10169
|
+
process.on(signal, () => process.exit(0));
|
|
10170
|
+
}
|
|
10171
|
+
let watching = false;
|
|
10172
|
+
if (!config.pollOnly) {
|
|
10173
|
+
const recursive = supportsNativeRecursiveWatch(process.platform);
|
|
10174
|
+
const watch = startWorktreeUpdateWatch({
|
|
10175
|
+
root: config.root,
|
|
10176
|
+
omitDirNames: config.omitDirNames,
|
|
10177
|
+
excludeNames: config.excludeNames,
|
|
10178
|
+
recursive,
|
|
10179
|
+
initialScanMode: recursive ? "sync" : "async",
|
|
10180
|
+
maxWatchedDirectories: config.maxWatchedDirectories,
|
|
10181
|
+
debounceMs: config.debounceMs,
|
|
10182
|
+
onUpdate: (paths) => send({ type: "update", paths }),
|
|
10183
|
+
onWatchLimit: (limit) => send({ type: "watch-limit", limit }),
|
|
10184
|
+
onError: (error) => send({
|
|
10185
|
+
type: "warn",
|
|
10186
|
+
message: error instanceof Error ? error.message : String(error)
|
|
10187
|
+
})
|
|
10188
|
+
});
|
|
10189
|
+
watching = watch.started;
|
|
9943
10190
|
}
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
throw new Error(`unknown column: ${cell.column}`);
|
|
9952
|
-
}
|
|
9953
|
-
}
|
|
9954
|
-
};
|
|
9955
|
-
const requirePrimaryKey = (pk) => {
|
|
9956
|
-
if (pkColumns.length === 0) {
|
|
9957
|
-
throw new Error("table has no primary key; row update/delete is not supported");
|
|
10191
|
+
send({ type: "ready", watching, pollOnly: config.pollOnly });
|
|
10192
|
+
setInterval(() => send({ type: "heartbeat" }), config.heartbeatIntervalMs).unref?.();
|
|
10193
|
+
setInterval(() => {
|
|
10194
|
+
try {
|
|
10195
|
+
process.kill(config.parentPid, 0);
|
|
10196
|
+
} catch {
|
|
10197
|
+
process.exit(0);
|
|
9958
10198
|
}
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
10199
|
+
}, config.heartbeatIntervalMs);
|
|
10200
|
+
let snapshot = null;
|
|
10201
|
+
let baselineEstablished = false;
|
|
10202
|
+
const poll = async () => {
|
|
10203
|
+
const next = await readWorktreeSnapshot(config);
|
|
10204
|
+
if (!next)
|
|
10205
|
+
return;
|
|
10206
|
+
if (!snapshot) {
|
|
10207
|
+
snapshot = next;
|
|
10208
|
+
if (baselineEstablished)
|
|
10209
|
+
send({ type: "update" });
|
|
10210
|
+
baselineEstablished = true;
|
|
10211
|
+
return;
|
|
9964
10212
|
}
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
}
|
|
9969
|
-
|
|
9970
|
-
throw new Error(`primary key column cannot be null: ${cell.column}`);
|
|
9971
|
-
}
|
|
10213
|
+
const diff = snapshotDiff(snapshot, next);
|
|
10214
|
+
snapshot = next;
|
|
10215
|
+
if (diff.full) {
|
|
10216
|
+
send({ type: "update" });
|
|
10217
|
+
return;
|
|
9972
10218
|
}
|
|
10219
|
+
if (diff.paths.length)
|
|
10220
|
+
send({ type: "update", paths: diff.paths });
|
|
9973
10221
|
};
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
}
|
|
10222
|
+
setInterval(() => {
|
|
10223
|
+
poll().catch((error) => send({
|
|
10224
|
+
type: "warn",
|
|
10225
|
+
message: error instanceof Error ? error.message : String(error)
|
|
10226
|
+
}));
|
|
10227
|
+
}, config.pollIntervalMs);
|
|
10228
|
+
}
|
|
10229
|
+
var MAX_HASHED_FILE_BYTES;
|
|
10230
|
+
var init_watch_child = __esm(() => {
|
|
10231
|
+
init_runtime();
|
|
10232
|
+
init_search();
|
|
10233
|
+
init_worktree_watcher();
|
|
10234
|
+
MAX_HASHED_FILE_BYTES = 4 * 1024 * 1024;
|
|
10235
|
+
});
|
|
10236
|
+
|
|
10237
|
+
// web-src/server/database/serialize.ts
|
|
10238
|
+
function serializeDbValue(value) {
|
|
10239
|
+
if (value === null || value === undefined)
|
|
10240
|
+
return null;
|
|
10241
|
+
if (typeof value === "bigint") {
|
|
10242
|
+
return value >= MIN_SAFE && value <= MAX_SAFE ? Number(value) : value.toString();
|
|
10243
|
+
}
|
|
10244
|
+
if (value instanceof Uint8Array) {
|
|
10245
|
+
return `<blob ${value.byteLength} bytes>`;
|
|
10246
|
+
}
|
|
10247
|
+
if (typeof value === "object") {
|
|
10248
|
+
try {
|
|
10249
|
+
return JSON.stringify(value);
|
|
10250
|
+
} catch {
|
|
10251
|
+
return String(value);
|
|
10252
|
+
}
|
|
10253
|
+
}
|
|
10254
|
+
return value;
|
|
10255
|
+
}
|
|
10256
|
+
function serializeDbRow(row) {
|
|
10257
|
+
return row.map(serializeDbValue);
|
|
10258
|
+
}
|
|
10259
|
+
function serializeDbRows(rows) {
|
|
10260
|
+
return rows.map(serializeDbRow);
|
|
10261
|
+
}
|
|
10262
|
+
function coerceDbValue(value, columnType) {
|
|
10263
|
+
if (value === null)
|
|
10264
|
+
return null;
|
|
10265
|
+
const t = (columnType || "").toLowerCase();
|
|
10266
|
+
if (/bool/.test(t)) {
|
|
10267
|
+
const v = value.trim().toLowerCase();
|
|
10268
|
+
if (v === "")
|
|
10269
|
+
return null;
|
|
10270
|
+
if (v === "true" || v === "t" || v === "1")
|
|
10271
|
+
return true;
|
|
10272
|
+
if (v === "false" || v === "f" || v === "0")
|
|
10273
|
+
return false;
|
|
10274
|
+
return value;
|
|
10275
|
+
}
|
|
10276
|
+
if (/int|serial|real|floa|doub|numeric|decimal|number/.test(t)) {
|
|
10277
|
+
const trimmed = value.trim();
|
|
10278
|
+
if (trimmed === "")
|
|
10279
|
+
return null;
|
|
10280
|
+
const n = Number(trimmed);
|
|
10281
|
+
if (Number.isFinite(n) && String(n) === trimmed)
|
|
10282
|
+
return n;
|
|
10283
|
+
return value;
|
|
10284
|
+
}
|
|
10285
|
+
return value;
|
|
10286
|
+
}
|
|
10287
|
+
var MIN_SAFE, MAX_SAFE;
|
|
10288
|
+
var init_serialize = __esm(() => {
|
|
10289
|
+
MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
|
|
10290
|
+
MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
|
|
10291
|
+
});
|
|
10292
|
+
|
|
10293
|
+
// web-src/server/database/sql-utils.ts
|
|
10294
|
+
function sanitizeIdentifier(name, kind = "sqlite") {
|
|
10295
|
+
if (kind === "mysql")
|
|
10296
|
+
return `\`${name.replace(/`/g, "``")}\``;
|
|
10297
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
10298
|
+
}
|
|
10299
|
+
function escapeSqlString(value, kind) {
|
|
10300
|
+
const escaped = kind === "mysql" ? value.replace(/\\/g, "\\\\").replace(/'/g, "''") : value.replace(/'/g, "''");
|
|
10301
|
+
return `'${escaped}'`;
|
|
10302
|
+
}
|
|
10303
|
+
function buildFilterWhere(grouped, kind, exact) {
|
|
10304
|
+
const whereParts = [];
|
|
10305
|
+
const params = [];
|
|
10306
|
+
const useParams = kind === "sqlite";
|
|
10307
|
+
const castOf = (column) => kind === "mysql" ? `CAST(${sanitizeIdentifier(column, kind)} AS CHAR)` : `CAST(${sanitizeIdentifier(column, kind)} AS TEXT)`;
|
|
10308
|
+
for (const [value, cols] of grouped) {
|
|
10309
|
+
const likeVal = useParams ? "?" : escapeSqlString(`%${value}%`, kind);
|
|
10310
|
+
if (cols.length === 1) {
|
|
10311
|
+
whereParts.push(`${castOf(cols[0])} LIKE ${likeVal}`);
|
|
10312
|
+
if (useParams)
|
|
10313
|
+
params.push(`%${value}%`);
|
|
10314
|
+
} else {
|
|
10315
|
+
const orParts = cols.map((column) => `${castOf(column)} LIKE ${likeVal}`);
|
|
10316
|
+
whereParts.push(`(${orParts.join(" OR ")})`);
|
|
10317
|
+
if (useParams) {
|
|
10318
|
+
for (let i = 0;i < cols.length; i++)
|
|
10319
|
+
params.push(`%${value}%`);
|
|
10320
|
+
}
|
|
10321
|
+
}
|
|
10322
|
+
}
|
|
10323
|
+
for (const cond of exact ?? []) {
|
|
10324
|
+
const rhs = useParams ? "?" : escapeSqlString(cond.value, kind);
|
|
10325
|
+
whereParts.push(`${castOf(cond.column)} = ${rhs}`);
|
|
10326
|
+
if (useParams)
|
|
10327
|
+
params.push(cond.value);
|
|
10328
|
+
}
|
|
10329
|
+
return { where: whereParts.join(" AND "), params, useParams };
|
|
10330
|
+
}
|
|
10331
|
+
function filterGroupedColumns(grouped, columnNames) {
|
|
10332
|
+
const validColumns = new Set(columnNames);
|
|
10333
|
+
const filtered = new Map;
|
|
10334
|
+
for (const [value, columns] of grouped) {
|
|
10335
|
+
const valid = columns.filter((column) => validColumns.has(column));
|
|
10336
|
+
if (valid.length > 0)
|
|
10337
|
+
filtered.set(value, valid);
|
|
10338
|
+
}
|
|
10339
|
+
return filtered;
|
|
10340
|
+
}
|
|
10341
|
+
function filterExactColumns(exact, columnNames) {
|
|
10342
|
+
if (!exact || exact.length === 0)
|
|
10343
|
+
return [];
|
|
10344
|
+
const validColumns = new Set(columnNames);
|
|
10345
|
+
return exact.filter((cond) => validColumns.has(cond.column));
|
|
10346
|
+
}
|
|
10347
|
+
function filterOrderByColumns(orderBy, columnNames) {
|
|
10348
|
+
if (!orderBy)
|
|
10349
|
+
return;
|
|
10350
|
+
const validColumns = new Set(columnNames);
|
|
10351
|
+
const filtered = orderBy.filter((order) => validColumns.has(order.column));
|
|
10352
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
10353
|
+
}
|
|
10354
|
+
function buildOrderClause(orderBy, kind = "sqlite") {
|
|
10355
|
+
if (!orderBy?.length)
|
|
10356
|
+
return "";
|
|
10357
|
+
const parts = orderBy.map((o) => `${sanitizeIdentifier(o.column, kind)} ${o.direction === "desc" ? "DESC" : "ASC"}`);
|
|
10358
|
+
return ` ORDER BY ${parts.join(", ")}`;
|
|
10359
|
+
}
|
|
10360
|
+
function useParamsFor(kind) {
|
|
10361
|
+
return kind === "sqlite";
|
|
10362
|
+
}
|
|
10363
|
+
function placeValue(coerced, kind, useParams, params) {
|
|
10364
|
+
if (useParams) {
|
|
10365
|
+
params.push(typeof coerced === "boolean" ? coerced ? 1 : 0 : coerced);
|
|
10366
|
+
return "?";
|
|
10367
|
+
}
|
|
10368
|
+
if (coerced === null)
|
|
10369
|
+
return "NULL";
|
|
10370
|
+
if (typeof coerced === "number")
|
|
10371
|
+
return String(coerced);
|
|
10372
|
+
if (typeof coerced === "boolean")
|
|
10373
|
+
return coerced ? "TRUE" : "FALSE";
|
|
10374
|
+
const text = coerced instanceof Uint8Array ? new TextDecoder().decode(coerced) : String(coerced);
|
|
10375
|
+
return escapeSqlString(text, kind);
|
|
10376
|
+
}
|
|
10377
|
+
function coerceCell(cell, columnType) {
|
|
10378
|
+
return coerceDbValue(cell.value, columnType);
|
|
10379
|
+
}
|
|
10380
|
+
function formatWriteComparisons(cells, columnTypes, kind, useParams, params, separator) {
|
|
10381
|
+
return cells.map((cell) => `${sanitizeIdentifier(cell.column, kind)} = ${placeValue(coerceCell(cell, columnTypes.get(cell.column) ?? "TEXT"), kind, useParams, params)}`).join(separator);
|
|
10382
|
+
}
|
|
10383
|
+
function buildInsertSql(table, cells, columnTypes, kind) {
|
|
10384
|
+
if (cells.length === 0) {
|
|
10385
|
+
throw new Error("insert requires at least one column value");
|
|
10386
|
+
}
|
|
10387
|
+
const useParams = useParamsFor(kind);
|
|
10388
|
+
const params = [];
|
|
10389
|
+
const cols = cells.map((c) => sanitizeIdentifier(c.column, kind));
|
|
10390
|
+
const placeholders = cells.map((c) => placeValue(coerceCell(c, columnTypes.get(c.column) ?? "TEXT"), kind, useParams, params));
|
|
10391
|
+
const sql = `INSERT INTO ${sanitizeIdentifier(table, kind)} (${cols.join(", ")}) VALUES (${placeholders.join(", ")})`;
|
|
10392
|
+
return { sql, params };
|
|
10393
|
+
}
|
|
10394
|
+
function buildUpdateSql(table, set, pk, columnTypes, kind) {
|
|
10395
|
+
if (set.length === 0) {
|
|
10396
|
+
throw new Error("update requires at least one column to set");
|
|
10397
|
+
}
|
|
10398
|
+
if (pk.length === 0) {
|
|
10399
|
+
throw new Error("update requires a primary key condition");
|
|
10400
|
+
}
|
|
10401
|
+
const useParams = useParamsFor(kind);
|
|
10402
|
+
const params = [];
|
|
10403
|
+
const setSql = formatWriteComparisons(set, columnTypes, kind, useParams, params, ", ");
|
|
10404
|
+
const whereSql = formatWriteComparisons(pk, columnTypes, kind, useParams, params, " AND ");
|
|
10405
|
+
const sql = `UPDATE ${sanitizeIdentifier(table, kind)} SET ${setSql} WHERE ${whereSql}`;
|
|
10406
|
+
return { sql, params };
|
|
10407
|
+
}
|
|
10408
|
+
function buildDeleteSql(table, pk, columnTypes, kind) {
|
|
10409
|
+
if (pk.length === 0) {
|
|
10410
|
+
throw new Error("delete requires a primary key condition");
|
|
10411
|
+
}
|
|
10412
|
+
const useParams = useParamsFor(kind);
|
|
10413
|
+
const params = [];
|
|
10414
|
+
const whereSql = formatWriteComparisons(pk, columnTypes, kind, useParams, params, " AND ");
|
|
10415
|
+
const sql = `DELETE FROM ${sanitizeIdentifier(table, kind)} WHERE ${whereSql}`;
|
|
10416
|
+
return { sql, params };
|
|
10417
|
+
}
|
|
10418
|
+
var init_sql_utils = __esm(() => {
|
|
10419
|
+
init_serialize();
|
|
10420
|
+
});
|
|
10421
|
+
|
|
10422
|
+
// web-src/server/database/mutate.ts
|
|
10423
|
+
function assertCells(cells, label) {
|
|
10424
|
+
if (!Array.isArray(cells)) {
|
|
10425
|
+
throw new Error(`${label} must be an array`);
|
|
10426
|
+
}
|
|
10427
|
+
for (const cell of cells) {
|
|
10428
|
+
if (!cell || typeof cell !== "object" || typeof cell.column !== "string" || cell.value !== null && typeof cell.value !== "string") {
|
|
10429
|
+
throw new Error(`${label} contains an invalid cell`);
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
return cells;
|
|
10433
|
+
}
|
|
10434
|
+
function buildMutationStatements(table, mutations, columns, kind) {
|
|
10435
|
+
if (!Array.isArray(mutations) || mutations.length === 0) {
|
|
10436
|
+
throw new Error("no mutations provided");
|
|
10437
|
+
}
|
|
10438
|
+
if (mutations.length > MAX_MUTATIONS) {
|
|
10439
|
+
throw new Error(`too many mutations (max ${MAX_MUTATIONS})`);
|
|
10440
|
+
}
|
|
10441
|
+
const columnTypes = new Map(columns.map((c) => [c.name, c.type]));
|
|
10442
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
10443
|
+
const pkColumns = columns.filter((c) => c.primaryKey).map((c) => c.name);
|
|
10444
|
+
const pkNames = new Set(pkColumns);
|
|
10445
|
+
const requireKnownColumns = (cells, label) => {
|
|
10446
|
+
for (const cell of cells) {
|
|
10447
|
+
if (!columnNames.has(cell.column)) {
|
|
10448
|
+
throw new Error(`unknown column: ${cell.column}`);
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
};
|
|
10452
|
+
const requirePrimaryKey = (pk) => {
|
|
10453
|
+
if (pkColumns.length === 0) {
|
|
10454
|
+
throw new Error("table has no primary key; row update/delete is not supported");
|
|
10455
|
+
}
|
|
10456
|
+
const provided = new Set(pk.map((c) => c.column));
|
|
10457
|
+
for (const name of pkColumns) {
|
|
10458
|
+
if (!provided.has(name)) {
|
|
10459
|
+
throw new Error(`missing primary key column: ${name}`);
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
10462
|
+
for (const cell of pk) {
|
|
10463
|
+
if (!pkNames.has(cell.column)) {
|
|
10464
|
+
throw new Error(`not a primary key column: ${cell.column}`);
|
|
10465
|
+
}
|
|
10466
|
+
if (cell.value === null) {
|
|
10467
|
+
throw new Error(`primary key column cannot be null: ${cell.column}`);
|
|
10468
|
+
}
|
|
10469
|
+
}
|
|
10470
|
+
};
|
|
10471
|
+
const statements = [];
|
|
10472
|
+
for (const mutation of mutations) {
|
|
10473
|
+
if (!mutation || typeof mutation !== "object") {
|
|
10474
|
+
throw new Error("invalid mutation");
|
|
10475
|
+
}
|
|
9979
10476
|
if (mutation.kind === "insert") {
|
|
9980
10477
|
const values = assertCells(mutation.values, "insert values");
|
|
9981
10478
|
requireKnownColumns(values, "insert values");
|
|
@@ -10002,7 +10499,7 @@ var init_mutate = __esm(() => {
|
|
|
10002
10499
|
});
|
|
10003
10500
|
|
|
10004
10501
|
// web-src/server/database/sources/sql-snapshot.ts
|
|
10005
|
-
import { createHash as
|
|
10502
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
10006
10503
|
function normalizeRawValue(v) {
|
|
10007
10504
|
if (v === null)
|
|
10008
10505
|
return "\\N";
|
|
@@ -10022,7 +10519,7 @@ function rowToPayloadJson(columns, row) {
|
|
|
10022
10519
|
}
|
|
10023
10520
|
function computeRowHash(columns, row) {
|
|
10024
10521
|
const parts = columns.map((_, i) => normalizeRawValue(row[i]));
|
|
10025
|
-
return
|
|
10522
|
+
return createHash3("sha256").update(parts.join("\t")).digest("hex");
|
|
10026
10523
|
}
|
|
10027
10524
|
function buildRowKeyJson(pkColumns, allColumns, row, rowIndex) {
|
|
10028
10525
|
if (pkColumns.length === 0) {
|
|
@@ -12093,7 +12590,7 @@ __export(exports_redis, {
|
|
|
12093
12590
|
canonicalizeRedisSnapshotContainer: () => canonicalizeRedisSnapshotContainer,
|
|
12094
12591
|
__setRedisClientFactoryForTest: () => __setRedisClientFactoryForTest
|
|
12095
12592
|
});
|
|
12096
|
-
import { createHash as
|
|
12593
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
12097
12594
|
import { createClient } from "@redis/client";
|
|
12098
12595
|
function __setRedisClientFactoryForTest(factory) {
|
|
12099
12596
|
createRedisClientImpl = factory ?? ((options) => createClient(options));
|
|
@@ -12470,7 +12967,7 @@ function createRedisAdapter(config) {
|
|
|
12470
12967
|
if (type === "none") {
|
|
12471
12968
|
return {
|
|
12472
12969
|
payload: { type: "none" },
|
|
12473
|
-
fullHash:
|
|
12970
|
+
fullHash: createHash4("sha256").update("").digest("hex")
|
|
12474
12971
|
};
|
|
12475
12972
|
}
|
|
12476
12973
|
if (type === "string")
|
|
@@ -12487,7 +12984,7 @@ function createRedisAdapter(config) {
|
|
|
12487
12984
|
return snapshotFetchStream(db, hexKey, signal);
|
|
12488
12985
|
return {
|
|
12489
12986
|
payload: { type: "none" },
|
|
12490
|
-
fullHash:
|
|
12987
|
+
fullHash: createHash4("sha256").update("").digest("hex")
|
|
12491
12988
|
};
|
|
12492
12989
|
}
|
|
12493
12990
|
async function evalHex(db, luaBody, extraArgv, label, signal) {
|
|
@@ -12499,7 +12996,7 @@ function createRedisAdapter(config) {
|
|
|
12499
12996
|
}
|
|
12500
12997
|
async function snapshotFetchString(db, hexKey, signal) {
|
|
12501
12998
|
const fullSize = Number(await evalHex(db, `${LUA_HEX_KEY_PRELUDE} return redis.call('STRLEN', fromhex(ARGV[1]))`, [hexKey], "STRLEN", signal)) || 0;
|
|
12502
|
-
const hasher =
|
|
12999
|
+
const hasher = createHash4("sha256");
|
|
12503
13000
|
let previewBytes = Buffer.alloc(0);
|
|
12504
13001
|
for (let offset = 0;offset < fullSize; offset += REDIS_STRING_BYTE_LIMIT) {
|
|
12505
13002
|
if (signal?.aborted)
|
|
@@ -12532,7 +13029,7 @@ function createRedisAdapter(config) {
|
|
|
12532
13029
|
}
|
|
12533
13030
|
async function snapshotFetchList(db, hexKey, signal) {
|
|
12534
13031
|
const total = Number(await evalHex(db, `${LUA_HEX_KEY_PRELUDE} return redis.call('LLEN', fromhex(ARGV[1]))`, [hexKey], "LLEN", signal)) || 0;
|
|
12535
|
-
const hasher =
|
|
13032
|
+
const hasher = createHash4("sha256");
|
|
12536
13033
|
let previewItems = [];
|
|
12537
13034
|
for (let offset = 0;offset < total; offset += REDIS_COLLECTION_LIMIT) {
|
|
12538
13035
|
if (signal?.aborted)
|
|
@@ -12574,7 +13071,7 @@ function createRedisAdapter(config) {
|
|
|
12574
13071
|
uniquePairs.push(p);
|
|
12575
13072
|
}
|
|
12576
13073
|
uniquePairs.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
12577
|
-
const hasher =
|
|
13074
|
+
const hasher = createHash4("sha256");
|
|
12578
13075
|
for (const [f, v] of uniquePairs) {
|
|
12579
13076
|
hashHexItem(hasher, f);
|
|
12580
13077
|
hashHexItem(hasher, v);
|
|
@@ -12602,7 +13099,7 @@ function createRedisAdapter(config) {
|
|
|
12602
13099
|
} while (cursor !== "0");
|
|
12603
13100
|
const dedup = Array.from(new Set(allMembers));
|
|
12604
13101
|
dedup.sort();
|
|
12605
|
-
const hasher =
|
|
13102
|
+
const hasher = createHash4("sha256");
|
|
12606
13103
|
for (const m of dedup)
|
|
12607
13104
|
hashHexItem(hasher, m);
|
|
12608
13105
|
const previewMembers = dedup.slice(0, REDIS_COLLECTION_LIMIT).map(decodeHexItem);
|
|
@@ -12616,7 +13113,7 @@ function createRedisAdapter(config) {
|
|
|
12616
13113
|
}
|
|
12617
13114
|
async function snapshotFetchZset(db, hexKey, signal) {
|
|
12618
13115
|
const total = Number(await evalHex(db, `${LUA_HEX_KEY_PRELUDE} return redis.call('ZCARD', fromhex(ARGV[1]))`, [hexKey], "ZCARD", signal)) || 0;
|
|
12619
|
-
const hasher =
|
|
13116
|
+
const hasher = createHash4("sha256");
|
|
12620
13117
|
let previewMembers = [];
|
|
12621
13118
|
for (let offset = 0;offset < total; offset += REDIS_COLLECTION_LIMIT) {
|
|
12622
13119
|
if (signal?.aborted)
|
|
@@ -12646,7 +13143,7 @@ function createRedisAdapter(config) {
|
|
|
12646
13143
|
}
|
|
12647
13144
|
async function snapshotFetchStream(db, hexKey, signal) {
|
|
12648
13145
|
const total = Number(await evalHex(db, `${LUA_HEX_KEY_PRELUDE} return redis.call('XLEN', fromhex(ARGV[1]))`, [hexKey], "XLEN", signal)) || 0;
|
|
12649
|
-
const hasher =
|
|
13146
|
+
const hasher = createHash4("sha256");
|
|
12650
13147
|
let previewEntries = [];
|
|
12651
13148
|
let startId = "-";
|
|
12652
13149
|
const seenIds = new Set;
|
|
@@ -13238,7 +13735,7 @@ var init_raw_file_headers = __esm(() => {
|
|
|
13238
13735
|
|
|
13239
13736
|
// web-src/server/database/adapters/s3.ts
|
|
13240
13737
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
13241
|
-
import { createHash as
|
|
13738
|
+
import { createHash as createHash5, createHmac } from "node:crypto";
|
|
13242
13739
|
function createS3RequestDeadline() {
|
|
13243
13740
|
const timeoutMs = s3RequestTimeoutMs;
|
|
13244
13741
|
return {
|
|
@@ -13268,10 +13765,10 @@ function hmac(key, value) {
|
|
|
13268
13765
|
return createHmac("sha256", key).update(value, "utf8").digest();
|
|
13269
13766
|
}
|
|
13270
13767
|
function sha256(value) {
|
|
13271
|
-
return
|
|
13768
|
+
return createHash5("sha256").update(value, "utf8").digest("hex");
|
|
13272
13769
|
}
|
|
13273
13770
|
function sha256Bytes(value) {
|
|
13274
|
-
return
|
|
13771
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
13275
13772
|
}
|
|
13276
13773
|
function encodeRfc3986(value) {
|
|
13277
13774
|
return encodeURIComponent(value).replace(/[!'()*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
@@ -14278,7 +14775,7 @@ import {
|
|
|
14278
14775
|
statSync as statSync5
|
|
14279
14776
|
} from "node:fs";
|
|
14280
14777
|
import { lstat, open as open2, readdir, readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
14281
|
-
import { basename, join as
|
|
14778
|
+
import { basename, join as join11, relative as relative5 } from "node:path";
|
|
14282
14779
|
function isSqliteFile(fullPath) {
|
|
14283
14780
|
try {
|
|
14284
14781
|
const stat3 = statSync5(fullPath);
|
|
@@ -14349,7 +14846,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
14349
14846
|
return;
|
|
14350
14847
|
if (omitSet.has(entry.toLowerCase()))
|
|
14351
14848
|
continue;
|
|
14352
|
-
const full =
|
|
14849
|
+
const full = join11(dir, entry);
|
|
14353
14850
|
let entryStat;
|
|
14354
14851
|
try {
|
|
14355
14852
|
entryStat = await lstat(full);
|
|
@@ -14366,7 +14863,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
14366
14863
|
continue;
|
|
14367
14864
|
if (!await isSqliteFileAsync(full))
|
|
14368
14865
|
continue;
|
|
14369
|
-
const rel =
|
|
14866
|
+
const rel = relative5(cwd, full);
|
|
14370
14867
|
if (rel.startsWith("..") || rel.startsWith("/"))
|
|
14371
14868
|
continue;
|
|
14372
14869
|
results.push({
|
|
@@ -14393,7 +14890,7 @@ function validateDbPath(cwd, dbPath) {
|
|
|
14393
14890
|
const parts = dbPath.split(/[\\/]+/);
|
|
14394
14891
|
if (parts.some((p) => p === ".." || p.toLowerCase() === ".git" || p.toLowerCase() === ".code-viewer"))
|
|
14395
14892
|
return null;
|
|
14396
|
-
const full =
|
|
14893
|
+
const full = join11(cwd, dbPath);
|
|
14397
14894
|
if (!existsSync6(full))
|
|
14398
14895
|
return null;
|
|
14399
14896
|
let realCwd;
|
|
@@ -14404,7 +14901,7 @@ function validateDbPath(cwd, dbPath) {
|
|
|
14404
14901
|
} catch {
|
|
14405
14902
|
return null;
|
|
14406
14903
|
}
|
|
14407
|
-
const rel =
|
|
14904
|
+
const rel = relative5(realCwd, realFull);
|
|
14408
14905
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/"))
|
|
14409
14906
|
return null;
|
|
14410
14907
|
if (!isSqliteFile(realFull))
|
|
@@ -14565,7 +15062,7 @@ function resolveEnvValue(raw, composeDirEnv = {}) {
|
|
|
14565
15062
|
}
|
|
14566
15063
|
async function readDotenvAsync(composeDir) {
|
|
14567
15064
|
try {
|
|
14568
|
-
const content = await readFile2(
|
|
15065
|
+
const content = await readFile2(join11(composeDir, ".env"), "utf-8");
|
|
14569
15066
|
return parseDotenvContent(content);
|
|
14570
15067
|
} catch {
|
|
14571
15068
|
return {};
|
|
@@ -14687,7 +15184,7 @@ function parseComposeContent(content, filepath, composeDir, cwd, composeDirEnv,
|
|
|
14687
15184
|
for (let match = serviceRegex.exec(servicesBlock);match !== null; match = serviceRegex.exec(servicesBlock)) {
|
|
14688
15185
|
servicePositions.push({ name: match[1], start: match.index });
|
|
14689
15186
|
}
|
|
14690
|
-
const relDir =
|
|
15187
|
+
const relDir = relative5(cwd, composeDir);
|
|
14691
15188
|
const isRoot = relDir === "" || relDir === ".";
|
|
14692
15189
|
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
14693
15190
|
const filename = basename(filepath);
|
|
@@ -14792,7 +15289,7 @@ async function walkForMarkerFileAsync(dir, depth, omitSet, hasCapacity, visitDir
|
|
|
14792
15289
|
return;
|
|
14793
15290
|
if (omitSet.has(entry.toLowerCase()))
|
|
14794
15291
|
continue;
|
|
14795
|
-
const full =
|
|
15292
|
+
const full = join11(dir, entry);
|
|
14796
15293
|
let entryStat;
|
|
14797
15294
|
try {
|
|
14798
15295
|
entryStat = await lstat(full);
|
|
@@ -14819,7 +15316,7 @@ async function discoverDockerDatabasesAsync(cwd, omitDirNames = [], signal) {
|
|
|
14819
15316
|
omitSet.add("node_modules");
|
|
14820
15317
|
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_DOCKER_SERVICES, async (dir) => {
|
|
14821
15318
|
for (const filename of COMPOSE_FILENAMES) {
|
|
14822
|
-
const filepath =
|
|
15319
|
+
const filepath = join11(dir, filename);
|
|
14823
15320
|
if (await pathExistsAsync(filepath)) {
|
|
14824
15321
|
await parseComposeFileAsync(filepath, dir, cwd, results);
|
|
14825
15322
|
break;
|
|
@@ -14930,410 +15427,153 @@ function isSafeDockerRelDir(value) {
|
|
|
14930
15427
|
}
|
|
14931
15428
|
function isSafeDockerDatabaseName(value) {
|
|
14932
15429
|
if (value === undefined)
|
|
14933
|
-
return true;
|
|
14934
|
-
if (value === "")
|
|
14935
|
-
return false;
|
|
14936
|
-
if (hasControlCharacter(value))
|
|
14937
|
-
return false;
|
|
14938
|
-
return /^[A-Za-z0-9_$.-]+$/.test(value);
|
|
14939
|
-
}
|
|
14940
|
-
function cloneSupabaseDiscoveryResult(result) {
|
|
14941
|
-
return result.map((entry) => ({ ...entry }));
|
|
14942
|
-
}
|
|
14943
|
-
function parseSupabaseConfigToml(content) {
|
|
14944
|
-
let section = null;
|
|
14945
|
-
let projectId = null;
|
|
14946
|
-
let dbPort = null;
|
|
14947
|
-
for (const rawLine of content.split(`
|
|
14948
|
-
`)) {
|
|
14949
|
-
const line = rawLine.trim();
|
|
14950
|
-
if (!line || line.startsWith("#"))
|
|
14951
|
-
continue;
|
|
14952
|
-
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
14953
|
-
if (sectionMatch) {
|
|
14954
|
-
section = sectionMatch[1];
|
|
14955
|
-
continue;
|
|
14956
|
-
}
|
|
14957
|
-
const kvMatch = line.match(/^([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.+)$/);
|
|
14958
|
-
if (!kvMatch)
|
|
14959
|
-
continue;
|
|
14960
|
-
const value = stripScalarSyntax(kvMatch[2]);
|
|
14961
|
-
if (section === null && kvMatch[1] === "project_id") {
|
|
14962
|
-
projectId = value;
|
|
14963
|
-
} else if (section === "db" && kvMatch[1] === "port") {
|
|
14964
|
-
dbPort = value;
|
|
14965
|
-
}
|
|
14966
|
-
}
|
|
14967
|
-
if (!projectId || !isSafeDockerServiceName(projectId))
|
|
14968
|
-
return null;
|
|
14969
|
-
return {
|
|
14970
|
-
projectId,
|
|
14971
|
-
dbPort: dbPort && /^\d+$/.test(dbPort) ? dbPort : DEFAULT_SUPABASE_DB_PORT
|
|
14972
|
-
};
|
|
14973
|
-
}
|
|
14974
|
-
async function discoverSupabaseCliProjectsAsync(cwd, omitDirNames = [], signal) {
|
|
14975
|
-
const cacheKey = discoveryCacheKey(cwd, omitDirNames);
|
|
14976
|
-
const now = Date.now();
|
|
14977
|
-
const cached = supabaseDiscoveryCache.get(cacheKey);
|
|
14978
|
-
if (cached && cached.expiresAt > now) {
|
|
14979
|
-
return cloneSupabaseDiscoveryResult(cached.result);
|
|
14980
|
-
}
|
|
14981
|
-
const omitSet = new Set(omitDirNames.map((d) => d.toLowerCase()));
|
|
14982
|
-
omitSet.add(".git");
|
|
14983
|
-
omitSet.add("node_modules");
|
|
14984
|
-
const results = [];
|
|
14985
|
-
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_SUPABASE_PROJECTS, async (dir) => {
|
|
14986
|
-
const configPath = join9(dir, "supabase", "config.toml");
|
|
14987
|
-
if (!await pathExistsAsync(configPath))
|
|
14988
|
-
return;
|
|
14989
|
-
try {
|
|
14990
|
-
const content = await readFile2(configPath, "utf-8");
|
|
14991
|
-
const parsed = parseSupabaseConfigToml(content);
|
|
14992
|
-
if (!parsed)
|
|
14993
|
-
return;
|
|
14994
|
-
const relDir = relative4(cwd, dir);
|
|
14995
|
-
const isRoot = relDir === "" || relDir === ".";
|
|
14996
|
-
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
14997
|
-
const id = isRoot ? `supabase:${parsed.projectId}` : `supabase:${parsed.projectId}@${encodeURIComponent(relDirSlash)}`;
|
|
14998
|
-
const labelPath = isRoot ? "" : ` — ${relDirSlash}`;
|
|
14999
|
-
results.push({
|
|
15000
|
-
id,
|
|
15001
|
-
path: isRoot ? "supabase/config.toml" : `${relDirSlash}/supabase/config.toml`,
|
|
15002
|
-
name: `${parsed.projectId} (Supabase CLI, postgres@127.0.0.1:${parsed.dbPort}/postgres${labelPath})`,
|
|
15003
|
-
sizeBytes: 0,
|
|
15004
|
-
kind: "postgresql",
|
|
15005
|
-
projectId: parsed.projectId,
|
|
15006
|
-
relDirSlash,
|
|
15007
|
-
dbPort: parsed.dbPort
|
|
15008
|
-
});
|
|
15009
|
-
} catch {}
|
|
15010
|
-
}, signal);
|
|
15011
|
-
if (signal?.aborted)
|
|
15012
|
-
return cloneSupabaseDiscoveryResult(results);
|
|
15013
|
-
supabaseDiscoveryCache.set(cacheKey, {
|
|
15014
|
-
expiresAt: now + SUPABASE_DISCOVERY_TTL_MS,
|
|
15015
|
-
result: cloneSupabaseDiscoveryResult(results)
|
|
15016
|
-
});
|
|
15017
|
-
return cloneSupabaseDiscoveryResult(results);
|
|
15018
|
-
}
|
|
15019
|
-
function parseSupabaseDbId(dbId) {
|
|
15020
|
-
if (!dbId.startsWith("supabase:"))
|
|
15021
|
-
return null;
|
|
15022
|
-
const rest = dbId.slice("supabase:".length);
|
|
15023
|
-
if (!rest)
|
|
15024
|
-
return null;
|
|
15025
|
-
const atIdx = rest.indexOf("@");
|
|
15026
|
-
let projectId;
|
|
15027
|
-
let relDir = "";
|
|
15028
|
-
if (atIdx >= 0) {
|
|
15029
|
-
if (rest.indexOf("@", atIdx + 1) >= 0)
|
|
15030
|
-
return null;
|
|
15031
|
-
projectId = rest.slice(0, atIdx);
|
|
15032
|
-
try {
|
|
15033
|
-
relDir = decodeURIComponent(rest.slice(atIdx + 1));
|
|
15034
|
-
} catch {
|
|
15035
|
-
return null;
|
|
15036
|
-
}
|
|
15037
|
-
if (!isSafeDockerRelDir(relDir))
|
|
15038
|
-
return null;
|
|
15039
|
-
} else {
|
|
15040
|
-
projectId = rest;
|
|
15041
|
-
}
|
|
15042
|
-
if (!isSafeDockerServiceName(projectId))
|
|
15043
|
-
return null;
|
|
15044
|
-
return { projectId, relDir };
|
|
15045
|
-
}
|
|
15046
|
-
async function findSupabaseCliProjectByDbIdAsync(cwd, dbId, omitDirNames, signal) {
|
|
15047
|
-
const parsed = parseSupabaseDbId(dbId);
|
|
15048
|
-
if (!parsed)
|
|
15049
|
-
return null;
|
|
15050
|
-
const projects = await discoverSupabaseCliProjectsAsync(cwd, omitDirNames, signal);
|
|
15051
|
-
return projects.find((p) => p.projectId === parsed.projectId && p.relDirSlash === parsed.relDir) || null;
|
|
15052
|
-
}
|
|
15053
|
-
var SQLITE_EXTENSIONS, SQLITE_MAGIC = "SQLite format 3\x00", MAX_SCAN_DEPTH = 3, MAX_ENTRIES = 50, DOCKER_DISCOVERY_TTL_MS = 5000, SQLITE_DISCOVERY_TTL_MS = 5000, sqliteDiscoveryCache, COMPOSE_FILENAMES, MAX_DOCKER_SERVICES = 30, dockerDiscoveryCache, DB_KIND_VALUES, MAX_SUPABASE_PROJECTS = 30, SUPABASE_DISCOVERY_TTL_MS = 5000, DEFAULT_SUPABASE_DB_PORT = "54322", supabaseDiscoveryCache;
|
|
15054
|
-
var init_discovery = __esm(() => {
|
|
15055
|
-
SQLITE_EXTENSIONS = new Set([".db", ".sqlite", ".sqlite3", ".s3db"]);
|
|
15056
|
-
sqliteDiscoveryCache = new Map;
|
|
15057
|
-
COMPOSE_FILENAMES = [
|
|
15058
|
-
"docker-compose.yml",
|
|
15059
|
-
"docker-compose.yaml",
|
|
15060
|
-
"compose.yml",
|
|
15061
|
-
"compose.yaml"
|
|
15062
|
-
];
|
|
15063
|
-
dockerDiscoveryCache = new Map;
|
|
15064
|
-
DB_KIND_VALUES = new Set([
|
|
15065
|
-
"sqlite",
|
|
15066
|
-
"postgresql",
|
|
15067
|
-
"mysql",
|
|
15068
|
-
"redis",
|
|
15069
|
-
"elasticsearch",
|
|
15070
|
-
"s3",
|
|
15071
|
-
"dynamodb"
|
|
15072
|
-
]);
|
|
15073
|
-
supabaseDiscoveryCache = new Map;
|
|
15074
|
-
});
|
|
15075
|
-
|
|
15076
|
-
// web-src/server/worktree-watcher.ts
|
|
15077
|
-
import {
|
|
15078
|
-
lstatSync as lstatSync3,
|
|
15079
|
-
readdirSync as nodeReaddirSync,
|
|
15080
|
-
watch as nodeWatch
|
|
15081
|
-
} from "node:fs";
|
|
15082
|
-
import { join as join10, relative as relative5 } from "node:path";
|
|
15083
|
-
function normalizeRelativePath(path) {
|
|
15084
|
-
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
15085
|
-
}
|
|
15086
|
-
function isInsideRoot(root, path) {
|
|
15087
|
-
const rel = relative5(root, path).replace(/\\/g, "/");
|
|
15088
|
-
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
15089
|
-
}
|
|
15090
|
-
function startWorktreeUpdateWatch(options) {
|
|
15091
|
-
const watch = options.watch || nodeWatch;
|
|
15092
|
-
const readDirs = options.readdirSync || ((path) => nodeReaddirSync(path, { withFileTypes: true }));
|
|
15093
|
-
const isDirectory = options.isDirectory || ((path) => {
|
|
15094
|
-
try {
|
|
15095
|
-
return lstatSync3(path).isDirectory();
|
|
15096
|
-
} catch {
|
|
15097
|
-
return false;
|
|
15098
|
-
}
|
|
15099
|
-
});
|
|
15100
|
-
const directorySignature = options.directorySignature || ((path) => {
|
|
15101
|
-
try {
|
|
15102
|
-
const stats = lstatSync3(path);
|
|
15103
|
-
if (!stats.isDirectory())
|
|
15104
|
-
return null;
|
|
15105
|
-
return `${stats.dev}:${stats.ino}`;
|
|
15106
|
-
} catch {
|
|
15107
|
-
return null;
|
|
15108
|
-
}
|
|
15109
|
-
});
|
|
15110
|
-
const setTimer = options.setTimeoutFn || setTimeout;
|
|
15111
|
-
const clearTimer = options.clearTimeoutFn || clearTimeout;
|
|
15112
|
-
const debounceMs = options.debounceMs ?? 250;
|
|
15113
|
-
const maxWatchedDirectories = Math.max(1, Math.floor(options.maxWatchedDirectories ?? DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT));
|
|
15114
|
-
const watchers = new Map;
|
|
15115
|
-
const signatures = new Map;
|
|
15116
|
-
const initialScanAsync = options.initialScanMode === "async" || (!options.watch || options.watch === nodeWatch) && !options.readdirSync;
|
|
15117
|
-
const initialScanQueue = [];
|
|
15118
|
-
let initialScanTimer = null;
|
|
15119
|
-
let processingInitialScan = false;
|
|
15120
|
-
const pendingPathInspections = new Map;
|
|
15121
|
-
let pathInspectionTimer = null;
|
|
15122
|
-
let timer = null;
|
|
15123
|
-
const pendingChangedPaths = new Set;
|
|
15124
|
-
let watchLimitReported = false;
|
|
15125
|
-
const ignored = (path) => isSkippableSearchPath(normalizeRelativePath(path), options.omitDirNames, options.excludeNames);
|
|
15126
|
-
const directoryRelativePath = (dir) => normalizeRelativePath(relative5(options.root, dir));
|
|
15127
|
-
const ignoredDirectory = (dir) => {
|
|
15128
|
-
const rel = directoryRelativePath(dir);
|
|
15129
|
-
return Boolean(rel && ignored(rel));
|
|
15130
|
-
};
|
|
15131
|
-
const scheduleUpdate = (changedPath) => {
|
|
15132
|
-
if (changedPath)
|
|
15133
|
-
pendingChangedPaths.add(changedPath);
|
|
15134
|
-
if (timer)
|
|
15135
|
-
clearTimer(timer);
|
|
15136
|
-
timer = setTimer(() => {
|
|
15137
|
-
timer = null;
|
|
15138
|
-
const paths = pendingChangedPaths.size ? [...pendingChangedPaths] : undefined;
|
|
15139
|
-
pendingChangedPaths.clear();
|
|
15140
|
-
options.onUpdate(paths);
|
|
15141
|
-
}, debounceMs);
|
|
15142
|
-
};
|
|
15143
|
-
const reportWatchLimit = () => {
|
|
15144
|
-
if (watchLimitReported)
|
|
15145
|
-
return;
|
|
15146
|
-
watchLimitReported = true;
|
|
15147
|
-
options.onWatchLimit?.(maxWatchedDirectories);
|
|
15148
|
-
options.onError?.(new Error(`worktree watcher cap reached (${maxWatchedDirectories}); subsequent changes may be missed`));
|
|
15149
|
-
};
|
|
15150
|
-
const closeSubtree = (dir) => {
|
|
15151
|
-
for (const [watchedDir, watcher] of [...watchers]) {
|
|
15152
|
-
if (watchedDir !== dir && !watchedDir.startsWith(`${dir}/`))
|
|
15153
|
-
continue;
|
|
15154
|
-
try {
|
|
15155
|
-
watcher.close?.();
|
|
15156
|
-
} catch {}
|
|
15157
|
-
watchers.delete(watchedDir);
|
|
15158
|
-
signatures.delete(watchedDir);
|
|
15159
|
-
}
|
|
15160
|
-
};
|
|
15161
|
-
const closeAll = () => {
|
|
15162
|
-
if (initialScanTimer) {
|
|
15163
|
-
clearTimer(initialScanTimer);
|
|
15164
|
-
initialScanTimer = null;
|
|
15165
|
-
}
|
|
15166
|
-
if (pathInspectionTimer) {
|
|
15167
|
-
clearTimer(pathInspectionTimer);
|
|
15168
|
-
pathInspectionTimer = null;
|
|
15169
|
-
}
|
|
15170
|
-
initialScanQueue.length = 0;
|
|
15171
|
-
pendingPathInspections.clear();
|
|
15172
|
-
for (const watcher of [...watchers.values()]) {
|
|
15173
|
-
try {
|
|
15174
|
-
watcher.close?.();
|
|
15175
|
-
} catch {}
|
|
15176
|
-
}
|
|
15177
|
-
watchers.clear();
|
|
15178
|
-
signatures.clear();
|
|
15179
|
-
};
|
|
15180
|
-
const readChildDirectories = (dir) => {
|
|
15181
|
-
let entries;
|
|
15182
|
-
try {
|
|
15183
|
-
entries = readDirs(dir);
|
|
15184
|
-
} catch (error) {
|
|
15185
|
-
options.onError?.(error);
|
|
15186
|
-
return [];
|
|
15187
|
-
}
|
|
15188
|
-
const children = [];
|
|
15189
|
-
for (const entry of entries) {
|
|
15190
|
-
if (!entry.isDirectory())
|
|
15191
|
-
continue;
|
|
15192
|
-
const child = join10(dir, entry.name);
|
|
15193
|
-
if (ignoredDirectory(child))
|
|
15194
|
-
continue;
|
|
15195
|
-
children.push(child);
|
|
15196
|
-
}
|
|
15197
|
-
return children;
|
|
15198
|
-
};
|
|
15199
|
-
const processInitialScanQueue = () => {
|
|
15200
|
-
initialScanTimer = null;
|
|
15201
|
-
if (watchers.size >= maxWatchedDirectories) {
|
|
15202
|
-
reportWatchLimit();
|
|
15203
|
-
initialScanQueue.length = 0;
|
|
15204
|
-
return;
|
|
15205
|
-
}
|
|
15206
|
-
const next = initialScanQueue.shift();
|
|
15207
|
-
if (next) {
|
|
15208
|
-
processingInitialScan = true;
|
|
15209
|
-
try {
|
|
15210
|
-
watchDirectory(next, true);
|
|
15211
|
-
} finally {
|
|
15212
|
-
processingInitialScan = false;
|
|
15213
|
-
}
|
|
15214
|
-
}
|
|
15215
|
-
if (watchers.size >= maxWatchedDirectories) {
|
|
15216
|
-
reportWatchLimit();
|
|
15217
|
-
initialScanQueue.length = 0;
|
|
15218
|
-
}
|
|
15219
|
-
if (initialScanQueue.length)
|
|
15220
|
-
initialScanTimer = setTimer(processInitialScanQueue, 50);
|
|
15221
|
-
};
|
|
15222
|
-
const queueInitialChildren = (dir) => {
|
|
15223
|
-
const remaining = maxWatchedDirectories - watchers.size;
|
|
15224
|
-
if (remaining <= 0) {
|
|
15225
|
-
reportWatchLimit();
|
|
15226
|
-
return;
|
|
15227
|
-
}
|
|
15228
|
-
const children = readChildDirectories(dir);
|
|
15229
|
-
if (children.length > remaining)
|
|
15230
|
-
reportWatchLimit();
|
|
15231
|
-
initialScanQueue.push(...children.slice(0, remaining));
|
|
15232
|
-
if (!initialScanTimer && !processingInitialScan)
|
|
15233
|
-
initialScanTimer = setTimer(processInitialScanQueue, 5000);
|
|
15234
|
-
};
|
|
15235
|
-
const processChangedPath = (changed, fullChangedPath) => {
|
|
15236
|
-
const known = watchers.has(fullChangedPath);
|
|
15237
|
-
if (isDirectory(fullChangedPath)) {
|
|
15238
|
-
if (known) {
|
|
15239
|
-
const signature = directorySignature(fullChangedPath);
|
|
15240
|
-
if (signature && signature !== signatures.get(fullChangedPath)) {
|
|
15241
|
-
closeSubtree(fullChangedPath);
|
|
15242
|
-
watchDirectory(fullChangedPath, initialScanAsync);
|
|
15243
|
-
}
|
|
15244
|
-
scheduleUpdate(changed);
|
|
15245
|
-
return;
|
|
15246
|
-
}
|
|
15247
|
-
watchDirectory(fullChangedPath, initialScanAsync);
|
|
15248
|
-
} else if (known) {
|
|
15249
|
-
closeSubtree(fullChangedPath);
|
|
15430
|
+
return true;
|
|
15431
|
+
if (value === "")
|
|
15432
|
+
return false;
|
|
15433
|
+
if (hasControlCharacter(value))
|
|
15434
|
+
return false;
|
|
15435
|
+
return /^[A-Za-z0-9_$.-]+$/.test(value);
|
|
15436
|
+
}
|
|
15437
|
+
function cloneSupabaseDiscoveryResult(result) {
|
|
15438
|
+
return result.map((entry) => ({ ...entry }));
|
|
15439
|
+
}
|
|
15440
|
+
function parseSupabaseConfigToml(content) {
|
|
15441
|
+
let section = null;
|
|
15442
|
+
let projectId = null;
|
|
15443
|
+
let dbPort = null;
|
|
15444
|
+
for (const rawLine of content.split(`
|
|
15445
|
+
`)) {
|
|
15446
|
+
const line = rawLine.trim();
|
|
15447
|
+
if (!line || line.startsWith("#"))
|
|
15448
|
+
continue;
|
|
15449
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
15450
|
+
if (sectionMatch) {
|
|
15451
|
+
section = sectionMatch[1];
|
|
15452
|
+
continue;
|
|
15250
15453
|
}
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15454
|
+
const kvMatch = line.match(/^([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.+)$/);
|
|
15455
|
+
if (!kvMatch)
|
|
15456
|
+
continue;
|
|
15457
|
+
const value = stripScalarSyntax(kvMatch[2]);
|
|
15458
|
+
if (section === null && kvMatch[1] === "project_id") {
|
|
15459
|
+
projectId = value;
|
|
15460
|
+
} else if (section === "db" && kvMatch[1] === "port") {
|
|
15461
|
+
dbPort = value;
|
|
15259
15462
|
}
|
|
15463
|
+
}
|
|
15464
|
+
if (!projectId || !isSafeDockerServiceName(projectId))
|
|
15465
|
+
return null;
|
|
15466
|
+
return {
|
|
15467
|
+
projectId,
|
|
15468
|
+
dbPort: dbPort && /^\d+$/.test(dbPort) ? dbPort : DEFAULT_SUPABASE_DB_PORT
|
|
15260
15469
|
};
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15274
|
-
|
|
15470
|
+
}
|
|
15471
|
+
async function discoverSupabaseCliProjectsAsync(cwd, omitDirNames = [], signal) {
|
|
15472
|
+
const cacheKey = discoveryCacheKey(cwd, omitDirNames);
|
|
15473
|
+
const now = Date.now();
|
|
15474
|
+
const cached = supabaseDiscoveryCache.get(cacheKey);
|
|
15475
|
+
if (cached && cached.expiresAt > now) {
|
|
15476
|
+
return cloneSupabaseDiscoveryResult(cached.result);
|
|
15477
|
+
}
|
|
15478
|
+
const omitSet = new Set(omitDirNames.map((d) => d.toLowerCase()));
|
|
15479
|
+
omitSet.add(".git");
|
|
15480
|
+
omitSet.add("node_modules");
|
|
15481
|
+
const results = [];
|
|
15482
|
+
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_SUPABASE_PROJECTS, async (dir) => {
|
|
15483
|
+
const configPath = join11(dir, "supabase", "config.toml");
|
|
15484
|
+
if (!await pathExistsAsync(configPath))
|
|
15275
15485
|
return;
|
|
15276
15486
|
try {
|
|
15277
|
-
const
|
|
15278
|
-
|
|
15279
|
-
|
|
15280
|
-
|
|
15281
|
-
|
|
15282
|
-
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
const signature = directorySignature(dir);
|
|
15296
|
-
if (signature)
|
|
15297
|
-
signatures.set(dir, signature);
|
|
15298
|
-
watcher.on?.("error", () => {
|
|
15299
|
-
if (watchers.get(dir) === watcher) {
|
|
15300
|
-
watchers.delete(dir);
|
|
15301
|
-
signatures.delete(dir);
|
|
15302
|
-
}
|
|
15303
|
-
});
|
|
15304
|
-
watcher.on?.("close", () => {
|
|
15305
|
-
if (watchers.get(dir) === watcher) {
|
|
15306
|
-
watchers.delete(dir);
|
|
15307
|
-
signatures.delete(dir);
|
|
15308
|
-
}
|
|
15487
|
+
const content = await readFile2(configPath, "utf-8");
|
|
15488
|
+
const parsed = parseSupabaseConfigToml(content);
|
|
15489
|
+
if (!parsed)
|
|
15490
|
+
return;
|
|
15491
|
+
const relDir = relative5(cwd, dir);
|
|
15492
|
+
const isRoot = relDir === "" || relDir === ".";
|
|
15493
|
+
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
15494
|
+
const id = isRoot ? `supabase:${parsed.projectId}` : `supabase:${parsed.projectId}@${encodeURIComponent(relDirSlash)}`;
|
|
15495
|
+
const labelPath = isRoot ? "" : ` — ${relDirSlash}`;
|
|
15496
|
+
results.push({
|
|
15497
|
+
id,
|
|
15498
|
+
path: isRoot ? "supabase/config.toml" : `${relDirSlash}/supabase/config.toml`,
|
|
15499
|
+
name: `${parsed.projectId} (Supabase CLI, postgres@127.0.0.1:${parsed.dbPort}/postgres${labelPath})`,
|
|
15500
|
+
sizeBytes: 0,
|
|
15501
|
+
kind: "postgresql",
|
|
15502
|
+
projectId: parsed.projectId,
|
|
15503
|
+
relDirSlash,
|
|
15504
|
+
dbPort: parsed.dbPort
|
|
15309
15505
|
});
|
|
15310
|
-
} catch
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15506
|
+
} catch {}
|
|
15507
|
+
}, signal);
|
|
15508
|
+
if (signal?.aborted)
|
|
15509
|
+
return cloneSupabaseDiscoveryResult(results);
|
|
15510
|
+
supabaseDiscoveryCache.set(cacheKey, {
|
|
15511
|
+
expiresAt: now + SUPABASE_DISCOVERY_TTL_MS,
|
|
15512
|
+
result: cloneSupabaseDiscoveryResult(results)
|
|
15513
|
+
});
|
|
15514
|
+
return cloneSupabaseDiscoveryResult(results);
|
|
15515
|
+
}
|
|
15516
|
+
function parseSupabaseDbId(dbId) {
|
|
15517
|
+
if (!dbId.startsWith("supabase:"))
|
|
15518
|
+
return null;
|
|
15519
|
+
const rest = dbId.slice("supabase:".length);
|
|
15520
|
+
if (!rest)
|
|
15521
|
+
return null;
|
|
15522
|
+
const atIdx = rest.indexOf("@");
|
|
15523
|
+
let projectId;
|
|
15524
|
+
let relDir = "";
|
|
15525
|
+
if (atIdx >= 0) {
|
|
15526
|
+
if (rest.indexOf("@", atIdx + 1) >= 0)
|
|
15527
|
+
return null;
|
|
15528
|
+
projectId = rest.slice(0, atIdx);
|
|
15529
|
+
try {
|
|
15530
|
+
relDir = decodeURIComponent(rest.slice(atIdx + 1));
|
|
15531
|
+
} catch {
|
|
15532
|
+
return null;
|
|
15321
15533
|
}
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
}
|
|
15325
|
-
|
|
15326
|
-
|
|
15534
|
+
if (!isSafeDockerRelDir(relDir))
|
|
15535
|
+
return null;
|
|
15536
|
+
} else {
|
|
15537
|
+
projectId = rest;
|
|
15538
|
+
}
|
|
15539
|
+
if (!isSafeDockerServiceName(projectId))
|
|
15540
|
+
return null;
|
|
15541
|
+
return { projectId, relDir };
|
|
15327
15542
|
}
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15543
|
+
async function findSupabaseCliProjectByDbIdAsync(cwd, dbId, omitDirNames, signal) {
|
|
15544
|
+
const parsed = parseSupabaseDbId(dbId);
|
|
15545
|
+
if (!parsed)
|
|
15546
|
+
return null;
|
|
15547
|
+
const projects = await discoverSupabaseCliProjectsAsync(cwd, omitDirNames, signal);
|
|
15548
|
+
return projects.find((p) => p.projectId === parsed.projectId && p.relDirSlash === parsed.relDir) || null;
|
|
15549
|
+
}
|
|
15550
|
+
var SQLITE_EXTENSIONS, SQLITE_MAGIC = "SQLite format 3\x00", MAX_SCAN_DEPTH = 3, MAX_ENTRIES = 50, DOCKER_DISCOVERY_TTL_MS = 5000, SQLITE_DISCOVERY_TTL_MS = 5000, sqliteDiscoveryCache, COMPOSE_FILENAMES, MAX_DOCKER_SERVICES = 30, dockerDiscoveryCache, DB_KIND_VALUES, MAX_SUPABASE_PROJECTS = 30, SUPABASE_DISCOVERY_TTL_MS = 5000, DEFAULT_SUPABASE_DB_PORT = "54322", supabaseDiscoveryCache;
|
|
15551
|
+
var init_discovery = __esm(() => {
|
|
15552
|
+
SQLITE_EXTENSIONS = new Set([".db", ".sqlite", ".sqlite3", ".s3db"]);
|
|
15553
|
+
sqliteDiscoveryCache = new Map;
|
|
15554
|
+
COMPOSE_FILENAMES = [
|
|
15555
|
+
"docker-compose.yml",
|
|
15556
|
+
"docker-compose.yaml",
|
|
15557
|
+
"compose.yml",
|
|
15558
|
+
"compose.yaml"
|
|
15559
|
+
];
|
|
15560
|
+
dockerDiscoveryCache = new Map;
|
|
15561
|
+
DB_KIND_VALUES = new Set([
|
|
15562
|
+
"sqlite",
|
|
15563
|
+
"postgresql",
|
|
15564
|
+
"mysql",
|
|
15565
|
+
"redis",
|
|
15566
|
+
"elasticsearch",
|
|
15567
|
+
"s3",
|
|
15568
|
+
"dynamodb"
|
|
15569
|
+
]);
|
|
15570
|
+
supabaseDiscoveryCache = new Map;
|
|
15331
15571
|
});
|
|
15332
15572
|
|
|
15333
15573
|
// web-src/server/state-store.ts
|
|
15334
|
-
import { join as
|
|
15574
|
+
import { join as join12 } from "node:path";
|
|
15335
15575
|
function codeViewerPath(root, fileName) {
|
|
15336
|
-
return
|
|
15576
|
+
return join12(root, CODE_VIEWER_DIR2, fileName);
|
|
15337
15577
|
}
|
|
15338
15578
|
function isRecord(value) {
|
|
15339
15579
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -16213,7 +16453,7 @@ var init_d1 = __esm(() => {
|
|
|
16213
16453
|
|
|
16214
16454
|
// web-src/server/database/adapters/dynamodb.ts
|
|
16215
16455
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
16216
|
-
import { createHash as
|
|
16456
|
+
import { createHash as createHash6, createHmac as createHmac2 } from "node:crypto";
|
|
16217
16457
|
function createDynamoDbRequestDeadline() {
|
|
16218
16458
|
const timeoutMs = dynamoDbRequestTimeoutMs;
|
|
16219
16459
|
return { expiresAt: Date.now() + timeoutMs, timeoutMs };
|
|
@@ -16237,7 +16477,7 @@ function hmac2(key, value) {
|
|
|
16237
16477
|
return createHmac2("sha256", key).update(value, "utf8").digest();
|
|
16238
16478
|
}
|
|
16239
16479
|
function sha2562(value) {
|
|
16240
|
-
return
|
|
16480
|
+
return createHash6("sha256").update(value, "utf8").digest("hex");
|
|
16241
16481
|
}
|
|
16242
16482
|
function amzDate2(date = new Date) {
|
|
16243
16483
|
const iso = date.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
@@ -16882,7 +17122,7 @@ var init_credential_store = __esm(() => {
|
|
|
16882
17122
|
// web-src/server/database/connections-store.ts
|
|
16883
17123
|
import { randomUUID } from "node:crypto";
|
|
16884
17124
|
import { chmod } from "node:fs/promises";
|
|
16885
|
-
import { join as
|
|
17125
|
+
import { join as join13 } from "node:path";
|
|
16886
17126
|
function secretKey(cwd, id) {
|
|
16887
17127
|
return `${cwd}\x00${id}`;
|
|
16888
17128
|
}
|
|
@@ -16933,7 +17173,7 @@ function withRuntimeSecrets(cwd, connection) {
|
|
|
16933
17173
|
};
|
|
16934
17174
|
}
|
|
16935
17175
|
function connectionsFilePath(root) {
|
|
16936
|
-
return
|
|
17176
|
+
return join13(root, ".code-viewer", CONNECTIONS_FILE_NAME);
|
|
16937
17177
|
}
|
|
16938
17178
|
function emptyState() {
|
|
16939
17179
|
return { version: 1, connections: [] };
|
|
@@ -18745,9 +18985,9 @@ var init_handle_s3 = __esm(() => {
|
|
|
18745
18985
|
});
|
|
18746
18986
|
|
|
18747
18987
|
// web-src/server/database/query-history.ts
|
|
18748
|
-
import { join as
|
|
18988
|
+
import { join as join14 } from "node:path";
|
|
18749
18989
|
function historyFilePath(root) {
|
|
18750
|
-
return
|
|
18990
|
+
return join14(root, CODE_VIEWER_DIR3, HISTORY_FILE_NAME);
|
|
18751
18991
|
}
|
|
18752
18992
|
function emptyState2() {
|
|
18753
18993
|
return { version: 1, entries: [] };
|
|
@@ -18895,11 +19135,11 @@ var init_query_history = __esm(() => {
|
|
|
18895
19135
|
});
|
|
18896
19136
|
|
|
18897
19137
|
// web-src/server/database/snapshot-store.ts
|
|
18898
|
-
import { createHash as
|
|
19138
|
+
import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
|
|
18899
19139
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
18900
|
-
import { join as
|
|
19140
|
+
import { join as join15 } from "node:path";
|
|
18901
19141
|
async function getStoreDb(cwd) {
|
|
18902
|
-
const dbPath =
|
|
19142
|
+
const dbPath = join15(cwd, CODE_VIEWER_DIR4, SNAPSHOT_DB_NAME);
|
|
18903
19143
|
if (storeDb && storeDbPath === dbPath)
|
|
18904
19144
|
return storeDb;
|
|
18905
19145
|
if (storeDb) {
|
|
@@ -18907,7 +19147,7 @@ async function getStoreDb(cwd) {
|
|
|
18907
19147
|
storeDb.close();
|
|
18908
19148
|
} catch {}
|
|
18909
19149
|
}
|
|
18910
|
-
mkdirSync3(
|
|
19150
|
+
mkdirSync3(join15(cwd, CODE_VIEWER_DIR4), { recursive: true });
|
|
18911
19151
|
const DbClass = await loadSqliteClass();
|
|
18912
19152
|
storeDb = new DbClass(dbPath);
|
|
18913
19153
|
storeDbPath = dbPath;
|
|
@@ -18926,7 +19166,7 @@ function makeId2(prefix) {
|
|
|
18926
19166
|
return `${prefix}-${randomBytes2(8).toString("hex")}`;
|
|
18927
19167
|
}
|
|
18928
19168
|
function hashPayload(payloadJson) {
|
|
18929
|
-
return
|
|
19169
|
+
return createHash7("sha256").update(payloadJson).digest("hex");
|
|
18930
19170
|
}
|
|
18931
19171
|
function hashLengthPrefixed(hasher, value) {
|
|
18932
19172
|
hasher.update(`${Buffer.byteLength(value, "utf8")}:`);
|
|
@@ -19001,7 +19241,7 @@ async function addSnapshotTableRows(cwd, revisionId, rows) {
|
|
|
19001
19241
|
db.exec("BEGIN");
|
|
19002
19242
|
try {
|
|
19003
19243
|
for (const row of rows) {
|
|
19004
|
-
const rowKeyHash =
|
|
19244
|
+
const rowKeyHash = createHash7("sha256").update(row.rowKeyJson).digest("hex");
|
|
19005
19245
|
const payloadHash = hashPayload(row.payloadJson);
|
|
19006
19246
|
insertRow.run(revisionId, rowKeyHash, row.rowKeyJson, row.rowHash, payloadHash);
|
|
19007
19247
|
insertPayload.run(payloadHash, row.payloadJson);
|
|
@@ -19015,7 +19255,7 @@ async function addSnapshotTableRows(cwd, revisionId, rows) {
|
|
|
19015
19255
|
}
|
|
19016
19256
|
}
|
|
19017
19257
|
function computeRevisionTableHash(db, revisionId) {
|
|
19018
|
-
const hasher =
|
|
19258
|
+
const hasher = createHash7("sha256");
|
|
19019
19259
|
hashLengthPrefixed(hasher, `snapshot-table-v${SNAPSHOT_TABLE_HASH_VERSION}`);
|
|
19020
19260
|
let rowCount = 0;
|
|
19021
19261
|
let last;
|
|
@@ -19513,9 +19753,9 @@ var init_snapshot_runner = __esm(() => {
|
|
|
19513
19753
|
});
|
|
19514
19754
|
|
|
19515
19755
|
// web-src/server/database/tabs-store.ts
|
|
19516
|
-
import { join as
|
|
19756
|
+
import { join as join16 } from "node:path";
|
|
19517
19757
|
function tabsFilePath(root) {
|
|
19518
|
-
return
|
|
19758
|
+
return join16(root, CODE_VIEWER_DIR5, TABS_FILE_NAME);
|
|
19519
19759
|
}
|
|
19520
19760
|
function emptyState3() {
|
|
19521
19761
|
return { version: 1, tabs: [], activeTabId: null };
|
|
@@ -21469,7 +21709,7 @@ var init_handle = __esm(() => {
|
|
|
21469
21709
|
|
|
21470
21710
|
// web-src/server/doctor.ts
|
|
21471
21711
|
import { accessSync as accessSync2, constants as constants2, readFileSync as readFileSync6, statSync as statSync6 } from "node:fs";
|
|
21472
|
-
import { dirname as dirname5, join as
|
|
21712
|
+
import { dirname as dirname5, join as join17, relative as relative6 } from "node:path";
|
|
21473
21713
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
21474
21714
|
function statusWorse(a, b) {
|
|
21475
21715
|
const rank = { ok: 0, warn: 1, error: 2 };
|
|
@@ -21563,7 +21803,7 @@ function findCodeViewerPackageJson() {
|
|
|
21563
21803
|
cursor = dirname5(process.argv[1] || ".");
|
|
21564
21804
|
}
|
|
21565
21805
|
for (let depth = 0;depth < 8; depth += 1) {
|
|
21566
|
-
const candidate =
|
|
21806
|
+
const candidate = join17(cursor, "package.json");
|
|
21567
21807
|
try {
|
|
21568
21808
|
const raw = readFileSync6(candidate, "utf8");
|
|
21569
21809
|
const pkg = JSON.parse(raw);
|
|
@@ -21659,7 +21899,7 @@ async function checkSqlite(cwd) {
|
|
|
21659
21899
|
return { id: "sqlite", title: "SQLite driver", rows };
|
|
21660
21900
|
}
|
|
21661
21901
|
async function trySnapshotDbOpen(cwd) {
|
|
21662
|
-
const dbPath =
|
|
21902
|
+
const dbPath = join17(cwd, SNAPSHOT_DB_REL);
|
|
21663
21903
|
try {
|
|
21664
21904
|
statSync6(dbPath);
|
|
21665
21905
|
} catch {
|
|
@@ -21680,7 +21920,7 @@ async function trySnapshotDbOpen(cwd) {
|
|
|
21680
21920
|
}
|
|
21681
21921
|
}
|
|
21682
21922
|
function checkSnapshotStore(cwd) {
|
|
21683
|
-
const dbPath =
|
|
21923
|
+
const dbPath = join17(cwd, SNAPSHOT_DB_REL);
|
|
21684
21924
|
const dir = dirname5(dbPath);
|
|
21685
21925
|
let dirStatus = "ok";
|
|
21686
21926
|
let dirDetail = dir;
|
|
@@ -22699,12 +22939,12 @@ function startDevAssetReload(options) {
|
|
|
22699
22939
|
var init_dev_assets = () => {};
|
|
22700
22940
|
|
|
22701
22941
|
// web-src/server/journal.ts
|
|
22702
|
-
import { join as
|
|
22942
|
+
import { join as join18 } from "node:path";
|
|
22703
22943
|
function dailyJournalFilePath(root) {
|
|
22704
|
-
return
|
|
22944
|
+
return join18(root, CODE_VIEWER_DIR, DAILY_JOURNAL_FILE_NAME);
|
|
22705
22945
|
}
|
|
22706
22946
|
function journalTasksFilePath(root) {
|
|
22707
|
-
return
|
|
22947
|
+
return join18(root, CODE_VIEWER_DIR, JOURNAL_TASKS_FILE_NAME);
|
|
22708
22948
|
}
|
|
22709
22949
|
function emptyDailyJournalState() {
|
|
22710
22950
|
return { version: 1, entries: [] };
|
|
@@ -23317,7 +23557,7 @@ var init_journal2 = __esm(() => {
|
|
|
23317
23557
|
// web-src/server/search-service.ts
|
|
23318
23558
|
import { existsSync as existsSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
23319
23559
|
import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
|
|
23320
|
-
import { join as
|
|
23560
|
+
import { join as join19, relative as relative7 } from "node:path";
|
|
23321
23561
|
async function rgAvailableAsync(cwd) {
|
|
23322
23562
|
if (rgAvailableCache !== null)
|
|
23323
23563
|
return rgAvailableCache;
|
|
@@ -23347,7 +23587,7 @@ function safeWorktreePath(env, path) {
|
|
|
23347
23587
|
return null;
|
|
23348
23588
|
if (isGitInternalPath(path))
|
|
23349
23589
|
return null;
|
|
23350
|
-
const full =
|
|
23590
|
+
const full = join19(env.cwd, path);
|
|
23351
23591
|
if (!existsSync7(full))
|
|
23352
23592
|
return null;
|
|
23353
23593
|
let realCwd;
|
|
@@ -23540,7 +23780,7 @@ var init_search_service = __esm(() => {
|
|
|
23540
23780
|
|
|
23541
23781
|
// web-src/server/mcp.ts
|
|
23542
23782
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
23543
|
-
import { join as
|
|
23783
|
+
import { join as join20 } from "node:path";
|
|
23544
23784
|
function defaultMcpTools(options = {}) {
|
|
23545
23785
|
return [
|
|
23546
23786
|
{
|
|
@@ -25014,7 +25254,7 @@ var init_mcp = __esm(() => {
|
|
|
25014
25254
|
init_search_cli();
|
|
25015
25255
|
init_search_service();
|
|
25016
25256
|
init_status_cli();
|
|
25017
|
-
PACKAGE_VERSION = JSON.parse(readFileSync7(
|
|
25257
|
+
PACKAGE_VERSION = JSON.parse(readFileSync7(join20(ROOT, "package.json"), "utf8")).version;
|
|
25018
25258
|
MCP_SERVER_INFO = {
|
|
25019
25259
|
name: "code-viewer",
|
|
25020
25260
|
title: "code-viewer",
|
|
@@ -25022,6 +25262,172 @@ var init_mcp = __esm(() => {
|
|
|
25022
25262
|
};
|
|
25023
25263
|
});
|
|
25024
25264
|
|
|
25265
|
+
// web-src/server/watch-supervisor.ts
|
|
25266
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
25267
|
+
import { join as join21 } from "node:path";
|
|
25268
|
+
function watchChildCommand() {
|
|
25269
|
+
const entry = process.argv[1] ?? "";
|
|
25270
|
+
const script = entry.endsWith(".ts") ? join21(import.meta.dir, "cli.ts") : entry;
|
|
25271
|
+
return [process.argv[0], script, "watch-child"];
|
|
25272
|
+
}
|
|
25273
|
+
function startWatchSupervisor(options) {
|
|
25274
|
+
const spawnChild = options.spawnFn || spawn3;
|
|
25275
|
+
const now = options.nowFn || Date.now;
|
|
25276
|
+
const setTimer = options.setTimeoutFn || setTimeout;
|
|
25277
|
+
const setRepeating = options.setIntervalFn || setInterval;
|
|
25278
|
+
const clearRepeating = options.clearIntervalFn || clearInterval;
|
|
25279
|
+
const heartbeatMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS;
|
|
25280
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
25281
|
+
const command = options.command ?? watchChildCommand();
|
|
25282
|
+
let child = null;
|
|
25283
|
+
let closed = false;
|
|
25284
|
+
let watchFailures = 0;
|
|
25285
|
+
let pollOnly = false;
|
|
25286
|
+
let sawReady = false;
|
|
25287
|
+
let lastSignalAt = now();
|
|
25288
|
+
let watchdog = null;
|
|
25289
|
+
const report = (error) => options.onError?.(error);
|
|
25290
|
+
const abandon = (victim) => {
|
|
25291
|
+
victim.removeAllListeners?.();
|
|
25292
|
+
victim.stdout?.removeAllListeners?.();
|
|
25293
|
+
victim.stderr?.removeAllListeners?.();
|
|
25294
|
+
try {
|
|
25295
|
+
victim.kill("SIGTERM");
|
|
25296
|
+
} catch {}
|
|
25297
|
+
const grace = setTimer(() => {
|
|
25298
|
+
try {
|
|
25299
|
+
victim.kill("SIGKILL");
|
|
25300
|
+
} catch {}
|
|
25301
|
+
}, KILL_GRACE_MS);
|
|
25302
|
+
grace.unref?.();
|
|
25303
|
+
};
|
|
25304
|
+
const handleMessage = (message) => {
|
|
25305
|
+
lastSignalAt = now();
|
|
25306
|
+
if (message.type === "heartbeat")
|
|
25307
|
+
return;
|
|
25308
|
+
if (message.type === "ready") {
|
|
25309
|
+
if (message.pollOnly || message.watching) {
|
|
25310
|
+
sawReady = true;
|
|
25311
|
+
watchFailures = 0;
|
|
25312
|
+
} else {
|
|
25313
|
+
watchFailures++;
|
|
25314
|
+
}
|
|
25315
|
+
return;
|
|
25316
|
+
}
|
|
25317
|
+
if (message.type === "update") {
|
|
25318
|
+
options.onUpdate(message.paths);
|
|
25319
|
+
return;
|
|
25320
|
+
}
|
|
25321
|
+
if (message.type === "watch-limit") {
|
|
25322
|
+
options.onWatchLimit?.(message.limit);
|
|
25323
|
+
return;
|
|
25324
|
+
}
|
|
25325
|
+
if (message.type === "warn")
|
|
25326
|
+
report(new Error(message.message));
|
|
25327
|
+
};
|
|
25328
|
+
const start = () => {
|
|
25329
|
+
if (closed)
|
|
25330
|
+
return;
|
|
25331
|
+
if (!pollOnly && watchFailures >= WATCH_FAILURES_BEFORE_POLL_ONLY) {
|
|
25332
|
+
pollOnly = true;
|
|
25333
|
+
options.onPollOnly?.();
|
|
25334
|
+
}
|
|
25335
|
+
const config = {
|
|
25336
|
+
root: options.root,
|
|
25337
|
+
omitDirNames: options.omitDirNames,
|
|
25338
|
+
excludeNames: options.excludeNames,
|
|
25339
|
+
maxWatchedDirectories: options.maxWatchedDirectories,
|
|
25340
|
+
debounceMs: options.debounceMs ?? 250,
|
|
25341
|
+
pollIntervalMs,
|
|
25342
|
+
heartbeatIntervalMs: heartbeatMs,
|
|
25343
|
+
parentPid: process.pid,
|
|
25344
|
+
pollOnly
|
|
25345
|
+
};
|
|
25346
|
+
let spawned;
|
|
25347
|
+
try {
|
|
25348
|
+
spawned = spawnChild(command[0], command.slice(1), {
|
|
25349
|
+
cwd: options.root,
|
|
25350
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
25351
|
+
});
|
|
25352
|
+
} catch (error) {
|
|
25353
|
+
report(error);
|
|
25354
|
+
return;
|
|
25355
|
+
}
|
|
25356
|
+
child = spawned;
|
|
25357
|
+
sawReady = false;
|
|
25358
|
+
lastSignalAt = now();
|
|
25359
|
+
try {
|
|
25360
|
+
spawned.stdin?.write(`${JSON.stringify(config)}
|
|
25361
|
+
`);
|
|
25362
|
+
} catch (error) {
|
|
25363
|
+
report(error);
|
|
25364
|
+
}
|
|
25365
|
+
let buffer = "";
|
|
25366
|
+
spawned.stdout?.setEncoding?.("utf8");
|
|
25367
|
+
spawned.stdout?.on("data", (chunk) => {
|
|
25368
|
+
buffer += chunk;
|
|
25369
|
+
let newline = buffer.indexOf(`
|
|
25370
|
+
`);
|
|
25371
|
+
while (newline !== -1) {
|
|
25372
|
+
const line = buffer.slice(0, newline).trim();
|
|
25373
|
+
buffer = buffer.slice(newline + 1);
|
|
25374
|
+
if (line) {
|
|
25375
|
+
try {
|
|
25376
|
+
handleMessage(JSON.parse(line));
|
|
25377
|
+
} catch {}
|
|
25378
|
+
}
|
|
25379
|
+
newline = buffer.indexOf(`
|
|
25380
|
+
`);
|
|
25381
|
+
}
|
|
25382
|
+
});
|
|
25383
|
+
spawned.stderr?.setEncoding?.("utf8");
|
|
25384
|
+
spawned.stderr?.on("data", (chunk) => {
|
|
25385
|
+
const text = String(chunk).trim();
|
|
25386
|
+
if (text)
|
|
25387
|
+
console.warn(`[code-viewer] watch child: ${text}`);
|
|
25388
|
+
});
|
|
25389
|
+
spawned.on("error", (error) => report(error));
|
|
25390
|
+
spawned.on("exit", () => {
|
|
25391
|
+
if (closed || child !== spawned)
|
|
25392
|
+
return;
|
|
25393
|
+
if (!sawReady)
|
|
25394
|
+
watchFailures++;
|
|
25395
|
+
child = null;
|
|
25396
|
+
const retry = setTimer(start, heartbeatMs);
|
|
25397
|
+
retry.unref?.();
|
|
25398
|
+
});
|
|
25399
|
+
};
|
|
25400
|
+
start();
|
|
25401
|
+
watchdog = setRepeating(() => {
|
|
25402
|
+
if (closed || !child)
|
|
25403
|
+
return;
|
|
25404
|
+
if (now() - lastSignalAt < heartbeatMs * HEARTBEAT_MISS_LIMIT)
|
|
25405
|
+
return;
|
|
25406
|
+
const victim = child;
|
|
25407
|
+
child = null;
|
|
25408
|
+
watchFailures++;
|
|
25409
|
+
report(new Error("watch child stopped reporting; restarting"));
|
|
25410
|
+
abandon(victim);
|
|
25411
|
+
start();
|
|
25412
|
+
}, heartbeatMs);
|
|
25413
|
+
watchdog.unref?.();
|
|
25414
|
+
return {
|
|
25415
|
+
close: () => {
|
|
25416
|
+
closed = true;
|
|
25417
|
+
if (watchdog)
|
|
25418
|
+
clearRepeating(watchdog);
|
|
25419
|
+
watchdog = null;
|
|
25420
|
+
const victim = child;
|
|
25421
|
+
child = null;
|
|
25422
|
+
if (victim)
|
|
25423
|
+
abandon(victim);
|
|
25424
|
+
},
|
|
25425
|
+
pollOnly: () => pollOnly
|
|
25426
|
+
};
|
|
25427
|
+
}
|
|
25428
|
+
var DEFAULT_HEARTBEAT_MS = 5000, DEFAULT_POLL_MS = 15000, HEARTBEAT_MISS_LIMIT = 3, KILL_GRACE_MS = 2000, WATCH_FAILURES_BEFORE_POLL_ONLY = 2;
|
|
25429
|
+
var init_watch_supervisor = () => {};
|
|
25430
|
+
|
|
25025
25431
|
// web-src/server/state-route.ts
|
|
25026
25432
|
var exports_state_route = {};
|
|
25027
25433
|
__export(exports_state_route, {
|
|
@@ -25098,7 +25504,7 @@ import {
|
|
|
25098
25504
|
closeSync as closeSync2,
|
|
25099
25505
|
constants as constants3,
|
|
25100
25506
|
existsSync as existsSync8,
|
|
25101
|
-
lstatSync as
|
|
25507
|
+
lstatSync as lstatSync5,
|
|
25102
25508
|
mkdirSync as mkdirSync4,
|
|
25103
25509
|
openSync as openSync2,
|
|
25104
25510
|
readFileSync as readFileSync8,
|
|
@@ -25110,7 +25516,7 @@ import {
|
|
|
25110
25516
|
writeFileSync as writeFileSync2
|
|
25111
25517
|
} from "node:fs";
|
|
25112
25518
|
import { homedir as homedir3 } from "node:os";
|
|
25113
|
-
import { basename as basename3, dirname as dirname6, extname as extname2, join as
|
|
25519
|
+
import { basename as basename3, dirname as dirname6, extname as extname2, join as join22, relative as relative8 } from "node:path";
|
|
25114
25520
|
function parseCli() {
|
|
25115
25521
|
const rest = [];
|
|
25116
25522
|
for (let i = 2;i < process.argv.length; i++) {
|
|
@@ -25232,7 +25638,7 @@ Examples:
|
|
|
25232
25638
|
}
|
|
25233
25639
|
function warnIfLegacyConfigPresent() {
|
|
25234
25640
|
try {
|
|
25235
|
-
if (existsSync8(
|
|
25641
|
+
if (existsSync8(join22(cwd, ".code-viewer.json"))) {
|
|
25236
25642
|
console.warn("[code-viewer] .code-viewer.json is no longer used; configure scope and upload from Viewer Settings instead. The file can be safely removed.");
|
|
25237
25643
|
}
|
|
25238
25644
|
} catch {}
|
|
@@ -25339,7 +25745,7 @@ function staticFile(pathname) {
|
|
|
25339
25745
|
const spec = map[pathname];
|
|
25340
25746
|
if (!spec)
|
|
25341
25747
|
return null;
|
|
25342
|
-
const full =
|
|
25748
|
+
const full = join22(WEB_ROOT, spec[0]);
|
|
25343
25749
|
if (!existsSync8(full))
|
|
25344
25750
|
return text("not found", 404);
|
|
25345
25751
|
return new Response(readFileSync8(full), {
|
|
@@ -25597,7 +26003,7 @@ function safeWorktreePath2(path) {
|
|
|
25597
26003
|
return safeWorktreePath(currentSearchEnv(), path);
|
|
25598
26004
|
}
|
|
25599
26005
|
function worktreePath(path) {
|
|
25600
|
-
return
|
|
26006
|
+
return join22(cwd, path);
|
|
25601
26007
|
}
|
|
25602
26008
|
function safeOpenWorktreePath(path) {
|
|
25603
26009
|
if (path === "") {
|
|
@@ -25750,9 +26156,14 @@ async function handleTree(url) {
|
|
|
25750
26156
|
if (tree.error)
|
|
25751
26157
|
return text(tree.error, tree.status ?? 500);
|
|
25752
26158
|
const entries = tree.entries.filter((entry) => !isExcludedScopePath(entry.path, excludeNames));
|
|
25753
|
-
const
|
|
26159
|
+
const worktreeTarget = target === "worktree" || target === "";
|
|
26160
|
+
const [statusMap, ignoredPaths] = worktreeTarget ? await Promise.all([
|
|
26161
|
+
repoStatusMapAsync(cwd),
|
|
26162
|
+
ignoredPathsAsync(entries.map((entry) => entry.path), cwd)
|
|
26163
|
+
]) : [null, null];
|
|
25754
26164
|
const withStatus = (entry) => {
|
|
25755
|
-
const
|
|
26165
|
+
const found = statusMap && repoStatusForPath(statusMap, entry.path);
|
|
26166
|
+
const status = ignoredPaths?.has(entry.path) && (!found || found.inherited) ? "I" : found?.code;
|
|
25756
26167
|
return status ? { ...entry, status } : entry;
|
|
25757
26168
|
};
|
|
25758
26169
|
const deletedEntries = !recursive && statusMap ? deletedTreeEntriesForPath(statusMap, path) : [];
|
|
@@ -25766,7 +26177,7 @@ async function handleTree(url) {
|
|
|
25766
26177
|
...deletedEntries
|
|
25767
26178
|
],
|
|
25768
26179
|
readme: await readReadme(target, path),
|
|
25769
|
-
upload_enabled: uploadEnabled &&
|
|
26180
|
+
upload_enabled: uploadEnabled && worktreeTarget
|
|
25770
26181
|
});
|
|
25771
26182
|
}
|
|
25772
26183
|
async function handleSettings() {
|
|
@@ -25783,7 +26194,8 @@ async function handleSettings() {
|
|
|
25783
26194
|
watch_limit_effective: scopeWatchLimit,
|
|
25784
26195
|
watch_limit_default: DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT,
|
|
25785
26196
|
watch_limit_min: MIN_WORKTREE_WATCH_DIRECTORY_LIMIT,
|
|
25786
|
-
watch_limit_max: MAX_WORKTREE_WATCH_DIRECTORY_LIMIT
|
|
26197
|
+
watch_limit_max: MAX_WORKTREE_WATCH_DIRECTORY_LIMIT,
|
|
26198
|
+
watch_recursive: supportsNativeRecursiveWatch(process.platform)
|
|
25787
26199
|
}
|
|
25788
26200
|
});
|
|
25789
26201
|
}
|
|
@@ -25926,7 +26338,7 @@ async function handleLog(url) {
|
|
|
25926
26338
|
}
|
|
25927
26339
|
function blamePathKey(p) {
|
|
25928
26340
|
try {
|
|
25929
|
-
const st = statSync7(
|
|
26341
|
+
const st = statSync7(join22(cwd, p));
|
|
25930
26342
|
return `${st.mtimeMs}:${st.size}`;
|
|
25931
26343
|
} catch {
|
|
25932
26344
|
return "missing";
|
|
@@ -26465,7 +26877,7 @@ async function handleUploadFiles(req) {
|
|
|
26465
26877
|
total += file.size;
|
|
26466
26878
|
if (total > MAX_UPLOAD_TOTAL_BYTES)
|
|
26467
26879
|
return text("upload too large", 413);
|
|
26468
|
-
const target =
|
|
26880
|
+
const target = join22(realDir, safeName);
|
|
26469
26881
|
if (relative8(realDir, dirname6(target)) !== "")
|
|
26470
26882
|
return text("invalid filename", 400);
|
|
26471
26883
|
if (existsSync8(target))
|
|
@@ -26587,9 +26999,9 @@ function triggerUpdate(changedPaths) {
|
|
|
26587
26999
|
sendSse("update", data);
|
|
26588
27000
|
}
|
|
26589
27001
|
function moveMacPathIntoTrash(path) {
|
|
26590
|
-
const trashDir =
|
|
27002
|
+
const trashDir = join22(homedir3(), ".Trash");
|
|
26591
27003
|
const base = basename3(path) || "code-viewer-trash-item";
|
|
26592
|
-
const target =
|
|
27004
|
+
const target = join22(trashDir, `${base}-${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`);
|
|
26593
27005
|
try {
|
|
26594
27006
|
mkdirSync4(trashDir, { recursive: true });
|
|
26595
27007
|
renameSync(path, target);
|
|
@@ -26599,7 +27011,7 @@ function moveMacPathIntoTrash(path) {
|
|
|
26599
27011
|
}
|
|
26600
27012
|
}
|
|
26601
27013
|
async function movePathToTrash(path) {
|
|
26602
|
-
|
|
27014
|
+
lstatSync5(path);
|
|
26603
27015
|
if (process.platform === "darwin") {
|
|
26604
27016
|
return moveMacPathIntoTrash(path);
|
|
26605
27017
|
}
|
|
@@ -26631,7 +27043,7 @@ async function restoreTrashPath(originalPath, trashPath) {
|
|
|
26631
27043
|
if (!existsSync8(trashPath))
|
|
26632
27044
|
return { ok: false, error: "trash item not found" };
|
|
26633
27045
|
try {
|
|
26634
|
-
const trashRoot =
|
|
27046
|
+
const trashRoot = join22(homedir3(), ".Trash");
|
|
26635
27047
|
const trashRelative = relative8(trashRoot, trashPath);
|
|
26636
27048
|
if (trashRelative === "" || trashRelative.startsWith("..") || trashRelative.startsWith("/") || trashRelative.startsWith("\\"))
|
|
26637
27049
|
return { ok: false, error: "invalid trash handle" };
|
|
@@ -26787,7 +27199,7 @@ async function handleCreateDirectory(req) {
|
|
|
26787
27199
|
const targetPath = dir ? `${dir}/${name}` : name;
|
|
26788
27200
|
if (!safeRepoPath(targetPath) || isGitInternalPath(targetPath))
|
|
26789
27201
|
return text("invalid target", 400);
|
|
26790
|
-
const target =
|
|
27202
|
+
const target = join22(parent, name);
|
|
26791
27203
|
if (existsSync8(target))
|
|
26792
27204
|
return text("already exists", 409);
|
|
26793
27205
|
try {
|
|
@@ -27336,18 +27748,19 @@ async function shutdown(exitCode = 0) {
|
|
|
27336
27748
|
}
|
|
27337
27749
|
function startScopedWorktreeWatch() {
|
|
27338
27750
|
watchLimitReached = null;
|
|
27339
|
-
return
|
|
27751
|
+
return startWatchSupervisor({
|
|
27340
27752
|
root: cwd,
|
|
27341
27753
|
omitDirNames: scopeOmitDirNames,
|
|
27342
27754
|
excludeNames: scopeExcludeNames,
|
|
27343
|
-
watch,
|
|
27344
|
-
initialScanMode: "async",
|
|
27345
27755
|
maxWatchedDirectories: scopeWatchLimit,
|
|
27346
27756
|
onUpdate: triggerUpdate,
|
|
27347
27757
|
onWatchLimit: (limit) => {
|
|
27348
27758
|
watchLimitReached = limit;
|
|
27349
27759
|
sendSse("watch-limit", String(limit));
|
|
27350
27760
|
},
|
|
27761
|
+
onPollOnly: () => {
|
|
27762
|
+
console.warn("[code-viewer] file watching is unavailable; updates now come from periodic polling");
|
|
27763
|
+
},
|
|
27351
27764
|
onError: (error) => {
|
|
27352
27765
|
const message = error instanceof Error ? error.message : String(error);
|
|
27353
27766
|
console.warn(`code-viewer worktree watch skipped: ${message}`);
|
|
@@ -27390,9 +27803,10 @@ var init_preview = __esm(async () => {
|
|
|
27390
27803
|
init_search_service();
|
|
27391
27804
|
init_server_registry();
|
|
27392
27805
|
init_state_store();
|
|
27806
|
+
init_watch_supervisor();
|
|
27393
27807
|
init_worktree_watcher();
|
|
27394
|
-
WEB_ROOT =
|
|
27395
|
-
VERSION = JSON.parse(readFileSync8(
|
|
27808
|
+
WEB_ROOT = join22(ROOT, "web");
|
|
27809
|
+
VERSION = JSON.parse(readFileSync8(join22(ROOT, "package.json"), "utf8")).version;
|
|
27396
27810
|
DEFAULT_ARGS = ["HEAD"];
|
|
27397
27811
|
WATCHED_ASSET_FILES = ["index.html", "style.css", "app.js"];
|
|
27398
27812
|
LINE_INDEX_MAX_FILE_BYTES = 256 * 1024 * 1024;
|
|
@@ -27688,6 +28102,9 @@ if (process.argv[2] === "agent-help") {
|
|
|
27688
28102
|
} else if (process.argv[2] === "skill") {
|
|
27689
28103
|
const { runSkillCli: runSkillCli2 } = await Promise.resolve().then(() => (init_skill_cli(), exports_skill_cli));
|
|
27690
28104
|
runSkillCli2(process.argv.slice(3));
|
|
28105
|
+
} else if (process.argv[2] === "watch-child") {
|
|
28106
|
+
const { runWatchChild: runWatchChild2 } = await Promise.resolve().then(() => (init_watch_child(), exports_watch_child));
|
|
28107
|
+
await runWatchChild2();
|
|
27691
28108
|
} else if (process.argv[2] === "doctor") {
|
|
27692
28109
|
const { runDoctorCli: runDoctorCli2 } = await Promise.resolve().then(() => (init_doctor_cli(), exports_doctor_cli));
|
|
27693
28110
|
await runDoctorCli2(process.argv.slice(3));
|