@youtyan/code-viewer 0.2.8 → 0.2.10
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 +201 -129
- package/dist/code-viewer.js +904 -694
- package/package.json +2 -1
- package/web/app.js +2775 -433
- package/web/index.html +37 -10
- package/web/style.css +869 -121
package/dist/code-viewer.js
CHANGED
|
@@ -1514,6 +1514,7 @@ var init_git = __esm(() => {
|
|
|
1514
1514
|
init_runtime();
|
|
1515
1515
|
DEFAULT_WORKTREE_OMIT_DIR_NAMES = [
|
|
1516
1516
|
"node_modules",
|
|
1517
|
+
"bower_components",
|
|
1517
1518
|
".venv",
|
|
1518
1519
|
"venv",
|
|
1519
1520
|
".next",
|
|
@@ -1521,6 +1522,11 @@ var init_git = __esm(() => {
|
|
|
1521
1522
|
".svelte-kit",
|
|
1522
1523
|
".astro",
|
|
1523
1524
|
".vercel",
|
|
1525
|
+
".angular",
|
|
1526
|
+
".docusaurus",
|
|
1527
|
+
".expo",
|
|
1528
|
+
".dart_tool",
|
|
1529
|
+
".serverless",
|
|
1524
1530
|
"dist",
|
|
1525
1531
|
"build",
|
|
1526
1532
|
"out",
|
|
@@ -1528,6 +1534,9 @@ var init_git = __esm(() => {
|
|
|
1528
1534
|
".gradle",
|
|
1529
1535
|
".pnpm-store",
|
|
1530
1536
|
".turbo",
|
|
1537
|
+
".parcel-cache",
|
|
1538
|
+
".vite",
|
|
1539
|
+
".webpack",
|
|
1531
1540
|
"__pycache__",
|
|
1532
1541
|
".pytest_cache",
|
|
1533
1542
|
".tox",
|
|
@@ -1537,6 +1546,7 @@ var init_git = __esm(() => {
|
|
|
1537
1546
|
"vendor",
|
|
1538
1547
|
".cache",
|
|
1539
1548
|
"coverage",
|
|
1549
|
+
".nyc_output",
|
|
1540
1550
|
"tmp",
|
|
1541
1551
|
"log",
|
|
1542
1552
|
"storage",
|
|
@@ -3726,509 +3736,283 @@ var init_search = __esm(() => {
|
|
|
3726
3736
|
DEFAULT_EXCLUDE_NAMES = [".DS_Store"];
|
|
3727
3737
|
});
|
|
3728
3738
|
|
|
3729
|
-
// web-src/server/
|
|
3730
|
-
import {
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
watch as nodeWatch
|
|
3734
|
-
} from "node:fs";
|
|
3735
|
-
import { join as join7, relative } from "node:path";
|
|
3736
|
-
function normalizeRelativePath(path) {
|
|
3737
|
-
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
3739
|
+
// web-src/server/state-store.ts
|
|
3740
|
+
import { join as join7 } from "node:path";
|
|
3741
|
+
function codeViewerPath(root, fileName) {
|
|
3742
|
+
return join7(root, CODE_VIEWER_DIR2, fileName);
|
|
3738
3743
|
}
|
|
3739
|
-
function
|
|
3740
|
-
|
|
3741
|
-
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
3744
|
+
function isRecord(value) {
|
|
3745
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3742
3746
|
}
|
|
3743
|
-
function
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3747
|
+
function optionalString(value, maxLen) {
|
|
3748
|
+
if (typeof value !== "string")
|
|
3749
|
+
return;
|
|
3750
|
+
if (value.length === 0 || value.length > maxLen)
|
|
3751
|
+
return;
|
|
3752
|
+
if (value.includes("\x00"))
|
|
3753
|
+
return;
|
|
3754
|
+
return value;
|
|
3755
|
+
}
|
|
3756
|
+
function optionalBoolean(value) {
|
|
3757
|
+
return typeof value === "boolean" ? value : undefined;
|
|
3758
|
+
}
|
|
3759
|
+
function optionalNumber(value, min, max) {
|
|
3760
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
3761
|
+
return;
|
|
3762
|
+
return Math.max(min, Math.min(max, Math.round(value)));
|
|
3763
|
+
}
|
|
3764
|
+
function optionalFloat(value, min, max) {
|
|
3765
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
3766
|
+
return;
|
|
3767
|
+
return Math.max(min, Math.min(max, value));
|
|
3768
|
+
}
|
|
3769
|
+
function optionalFontSize(value) {
|
|
3770
|
+
return value === "compact" || value === "regular" || value === "large" || value === "xlarge" ? value : undefined;
|
|
3771
|
+
}
|
|
3772
|
+
function normalizeStringList(value, options) {
|
|
3773
|
+
if (!Array.isArray(value))
|
|
3774
|
+
return;
|
|
3775
|
+
const out = [];
|
|
3776
|
+
const seen = new Set;
|
|
3777
|
+
const items = options.keepLast ? [...value].reverse() : value;
|
|
3778
|
+
for (const item of items) {
|
|
3779
|
+
if (out.length >= options.maxItems)
|
|
3780
|
+
break;
|
|
3781
|
+
if (typeof item !== "string")
|
|
3782
|
+
continue;
|
|
3783
|
+
const name = item.trim();
|
|
3784
|
+
if (!name || name.length > options.maxLen || name.includes("\x00"))
|
|
3785
|
+
continue;
|
|
3786
|
+
if (options.pathSafe && (name.includes("/") || name.includes("\\") || name === "." || name === ".." || name === ".git")) {
|
|
3787
|
+
continue;
|
|
3751
3788
|
}
|
|
3789
|
+
if (seen.has(name))
|
|
3790
|
+
continue;
|
|
3791
|
+
seen.add(name);
|
|
3792
|
+
out.push(name);
|
|
3793
|
+
}
|
|
3794
|
+
if (options.keepLast)
|
|
3795
|
+
out.reverse();
|
|
3796
|
+
return options.sort === false ? out : out.sort((a, b) => a.localeCompare(b));
|
|
3797
|
+
}
|
|
3798
|
+
function emptySettings() {
|
|
3799
|
+
return { version: 1 };
|
|
3800
|
+
}
|
|
3801
|
+
function emptyViewState() {
|
|
3802
|
+
return {
|
|
3803
|
+
version: 1,
|
|
3804
|
+
collapsedDirs: [],
|
|
3805
|
+
lazyExpandedDirs: [],
|
|
3806
|
+
viewedFiles: []
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3809
|
+
function emptyDbUiState() {
|
|
3810
|
+
return { version: 1, columnWidths: {} };
|
|
3811
|
+
}
|
|
3812
|
+
function sanitizeSettings(raw) {
|
|
3813
|
+
if (!isRecord(raw))
|
|
3814
|
+
return emptySettings();
|
|
3815
|
+
const out = { version: 1 };
|
|
3816
|
+
if (raw.layout === "side-by-side" || raw.layout === "line-by-line")
|
|
3817
|
+
out.layout = raw.layout;
|
|
3818
|
+
if (raw.theme === "light" || raw.theme === "dark")
|
|
3819
|
+
out.theme = raw.theme;
|
|
3820
|
+
if (raw.language === "en" || raw.language === "ja")
|
|
3821
|
+
out.language = raw.language;
|
|
3822
|
+
if (raw.sidebarView === "tree" || raw.sidebarView === "flat")
|
|
3823
|
+
out.sidebarView = raw.sidebarView;
|
|
3824
|
+
const sidebarWidth = optionalNumber(raw.sidebarWidth, 180, 900);
|
|
3825
|
+
if (sidebarWidth !== undefined)
|
|
3826
|
+
out.sidebarWidth = sidebarWidth;
|
|
3827
|
+
const historyWidth = optionalNumber(raw.historyWidth, 220, 640);
|
|
3828
|
+
if (historyWidth !== undefined)
|
|
3829
|
+
out.historyWidth = historyWidth;
|
|
3830
|
+
const sidebarHidden = optionalBoolean(raw.sidebarHidden);
|
|
3831
|
+
if (sidebarHidden !== undefined)
|
|
3832
|
+
out.sidebarHidden = sidebarHidden;
|
|
3833
|
+
const sidebarFontSize = optionalFontSize(raw.sidebarFontSize);
|
|
3834
|
+
if (sidebarFontSize)
|
|
3835
|
+
out.sidebarFontSize = sidebarFontSize;
|
|
3836
|
+
const codeFontSize = optionalFontSize(raw.codeFontSize);
|
|
3837
|
+
if (codeFontSize)
|
|
3838
|
+
out.codeFontSize = codeFontSize;
|
|
3839
|
+
const syntaxHighlight = optionalBoolean(raw.syntaxHighlight);
|
|
3840
|
+
if (syntaxHighlight !== undefined)
|
|
3841
|
+
out.syntaxHighlight = syntaxHighlight;
|
|
3842
|
+
const autoUpdate = optionalBoolean(raw.autoUpdate);
|
|
3843
|
+
if (autoUpdate !== undefined)
|
|
3844
|
+
out.autoUpdate = autoUpdate;
|
|
3845
|
+
const queryHistoryPanelWidth = optionalNumber(raw.queryHistoryPanelWidth, 280, 800);
|
|
3846
|
+
if (queryHistoryPanelWidth !== undefined)
|
|
3847
|
+
out.queryHistoryPanelWidth = queryHistoryPanelWidth;
|
|
3848
|
+
const annotationPanelOpen = optionalBoolean(raw.annotationPanelOpen);
|
|
3849
|
+
if (annotationPanelOpen !== undefined)
|
|
3850
|
+
out.annotationPanelOpen = annotationPanelOpen;
|
|
3851
|
+
const annotationFollow = optionalBoolean(raw.annotationFollow);
|
|
3852
|
+
if (annotationFollow !== undefined)
|
|
3853
|
+
out.annotationFollow = annotationFollow;
|
|
3854
|
+
const annotationMuted = optionalBoolean(raw.annotationMuted);
|
|
3855
|
+
if (annotationMuted !== undefined)
|
|
3856
|
+
out.annotationMuted = annotationMuted;
|
|
3857
|
+
const annotationRate = optionalFloat(raw.annotationRate, 0.5, 2);
|
|
3858
|
+
if (annotationRate !== undefined)
|
|
3859
|
+
out.annotationRate = annotationRate;
|
|
3860
|
+
const ignoreWhitespace = optionalBoolean(raw.ignoreWhitespace);
|
|
3861
|
+
if (ignoreWhitespace !== undefined)
|
|
3862
|
+
out.ignoreWhitespace = ignoreWhitespace;
|
|
3863
|
+
const hideTests = optionalBoolean(raw.hideTests);
|
|
3864
|
+
if (hideTests !== undefined)
|
|
3865
|
+
out.hideTests = hideTests;
|
|
3866
|
+
const scopeOmitDirs = normalizeStringList(raw.scopeOmitDirs, {
|
|
3867
|
+
maxItems: 100,
|
|
3868
|
+
maxLen: 64,
|
|
3869
|
+
pathSafe: true
|
|
3752
3870
|
});
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
} catch {
|
|
3760
|
-
return null;
|
|
3761
|
-
}
|
|
3871
|
+
if (scopeOmitDirs)
|
|
3872
|
+
out.scopeOmitDirs = scopeOmitDirs;
|
|
3873
|
+
const scopeExcludeNames = normalizeStringList(raw.scopeExcludeNames, {
|
|
3874
|
+
maxItems: 200,
|
|
3875
|
+
maxLen: 128,
|
|
3876
|
+
pathSafe: true
|
|
3762
3877
|
});
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
const
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
const
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3878
|
+
if (scopeExcludeNames)
|
|
3879
|
+
out.scopeExcludeNames = scopeExcludeNames;
|
|
3880
|
+
const uploadEnabled = optionalBoolean(raw.uploadEnabled);
|
|
3881
|
+
if (uploadEnabled !== undefined)
|
|
3882
|
+
out.uploadEnabled = uploadEnabled;
|
|
3883
|
+
if (isRecord(raw.range)) {
|
|
3884
|
+
const from = optionalString(raw.range.from, MAX_REF_LEN);
|
|
3885
|
+
const to = optionalString(raw.range.to, MAX_REF_LEN);
|
|
3886
|
+
if (from && to)
|
|
3887
|
+
out.range = { from, to };
|
|
3888
|
+
}
|
|
3889
|
+
return out;
|
|
3890
|
+
}
|
|
3891
|
+
function mergeSettings(current, patch) {
|
|
3892
|
+
if (!isRecord(patch))
|
|
3893
|
+
return current;
|
|
3894
|
+
const raw = { ...current };
|
|
3895
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
3896
|
+
if (key === "version")
|
|
3897
|
+
continue;
|
|
3898
|
+
if (value === null)
|
|
3899
|
+
delete raw[key];
|
|
3900
|
+
else
|
|
3901
|
+
raw[key] = value;
|
|
3902
|
+
}
|
|
3903
|
+
return sanitizeSettings({ ...raw, version: 1 });
|
|
3904
|
+
}
|
|
3905
|
+
function sanitizeViewState(raw) {
|
|
3906
|
+
if (!isRecord(raw))
|
|
3907
|
+
return emptyViewState();
|
|
3908
|
+
return {
|
|
3909
|
+
version: 1,
|
|
3910
|
+
collapsedDirs: normalizeStringList(raw.collapsedDirs, {
|
|
3911
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3912
|
+
maxLen: MAX_KEY_LEN,
|
|
3913
|
+
keepLast: true,
|
|
3914
|
+
sort: false
|
|
3915
|
+
}) ?? [],
|
|
3916
|
+
lazyExpandedDirs: normalizeStringList(raw.lazyExpandedDirs, {
|
|
3917
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3918
|
+
maxLen: MAX_KEY_LEN,
|
|
3919
|
+
keepLast: true,
|
|
3920
|
+
sort: false
|
|
3921
|
+
}) ?? [],
|
|
3922
|
+
viewedFiles: normalizeStringList(raw.viewedFiles, {
|
|
3923
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3924
|
+
maxLen: MAX_KEY_LEN,
|
|
3925
|
+
keepLast: true,
|
|
3926
|
+
sort: false
|
|
3927
|
+
}) ?? []
|
|
3811
3928
|
};
|
|
3812
|
-
const closeAll = () => {
|
|
3813
|
-
if (initialScanTimer) {
|
|
3814
|
-
clearTimer(initialScanTimer);
|
|
3815
|
-
initialScanTimer = null;
|
|
3816
|
-
}
|
|
3817
|
-
if (pathInspectionTimer) {
|
|
3818
|
-
clearTimer(pathInspectionTimer);
|
|
3819
|
-
pathInspectionTimer = null;
|
|
3820
|
-
}
|
|
3821
|
-
initialScanQueue.length = 0;
|
|
3822
|
-
pendingPathInspections.clear();
|
|
3823
|
-
for (const watcher of [...watchers.values()]) {
|
|
3824
|
-
try {
|
|
3825
|
-
watcher.close?.();
|
|
3826
|
-
} catch {}
|
|
3827
|
-
}
|
|
3828
|
-
watchers.clear();
|
|
3829
|
-
signatures.clear();
|
|
3830
|
-
};
|
|
3831
|
-
const readChildDirectories = (dir) => {
|
|
3832
|
-
let entries;
|
|
3833
|
-
try {
|
|
3834
|
-
entries = readDirs(dir);
|
|
3835
|
-
} catch (error) {
|
|
3836
|
-
options.onError?.(error);
|
|
3837
|
-
return [];
|
|
3838
|
-
}
|
|
3839
|
-
const children = [];
|
|
3840
|
-
for (const entry of entries) {
|
|
3841
|
-
if (!entry.isDirectory())
|
|
3842
|
-
continue;
|
|
3843
|
-
const child = join7(dir, entry.name);
|
|
3844
|
-
if (ignoredDirectory(child))
|
|
3845
|
-
continue;
|
|
3846
|
-
children.push(child);
|
|
3847
|
-
}
|
|
3848
|
-
return children;
|
|
3849
|
-
};
|
|
3850
|
-
const processInitialScanQueue = () => {
|
|
3851
|
-
initialScanTimer = null;
|
|
3852
|
-
if (watchers.size >= maxWatchedDirectories) {
|
|
3853
|
-
reportWatchLimit();
|
|
3854
|
-
initialScanQueue.length = 0;
|
|
3855
|
-
return;
|
|
3856
|
-
}
|
|
3857
|
-
const next = initialScanQueue.shift();
|
|
3858
|
-
if (next)
|
|
3859
|
-
watchDirectory(next, true);
|
|
3860
|
-
if (watchers.size >= maxWatchedDirectories) {
|
|
3861
|
-
reportWatchLimit();
|
|
3862
|
-
initialScanQueue.length = 0;
|
|
3863
|
-
}
|
|
3864
|
-
if (initialScanQueue.length)
|
|
3865
|
-
initialScanTimer = setTimer(processInitialScanQueue, 50);
|
|
3866
|
-
};
|
|
3867
|
-
const queueInitialChildren = (dir) => {
|
|
3868
|
-
const remaining = maxWatchedDirectories - watchers.size;
|
|
3869
|
-
if (remaining <= 0) {
|
|
3870
|
-
reportWatchLimit();
|
|
3871
|
-
return;
|
|
3872
|
-
}
|
|
3873
|
-
const children = readChildDirectories(dir);
|
|
3874
|
-
if (children.length > remaining)
|
|
3875
|
-
reportWatchLimit();
|
|
3876
|
-
initialScanQueue.push(...children.slice(0, remaining));
|
|
3877
|
-
if (!initialScanTimer)
|
|
3878
|
-
initialScanTimer = setTimer(processInitialScanQueue, 5000);
|
|
3879
|
-
};
|
|
3880
|
-
const processChangedPath = (changed, fullChangedPath) => {
|
|
3881
|
-
const known = watchers.has(fullChangedPath);
|
|
3882
|
-
if (isDirectory(fullChangedPath)) {
|
|
3883
|
-
if (known) {
|
|
3884
|
-
const signature = directorySignature(fullChangedPath);
|
|
3885
|
-
if (signature && signature !== signatures.get(fullChangedPath)) {
|
|
3886
|
-
closeSubtree(fullChangedPath);
|
|
3887
|
-
watchDirectory(fullChangedPath, initialScanAsync);
|
|
3888
|
-
}
|
|
3889
|
-
scheduleUpdate(changed);
|
|
3890
|
-
return;
|
|
3891
|
-
}
|
|
3892
|
-
watchDirectory(fullChangedPath, initialScanAsync);
|
|
3893
|
-
} else if (known) {
|
|
3894
|
-
closeSubtree(fullChangedPath);
|
|
3895
|
-
}
|
|
3896
|
-
scheduleUpdate(changed);
|
|
3897
|
-
};
|
|
3898
|
-
const processPathInspections = () => {
|
|
3899
|
-
pathInspectionTimer = null;
|
|
3900
|
-
const entries = [...pendingPathInspections];
|
|
3901
|
-
pendingPathInspections.clear();
|
|
3902
|
-
for (const [changed, fullChangedPath] of entries) {
|
|
3903
|
-
processChangedPath(changed, fullChangedPath);
|
|
3904
|
-
}
|
|
3905
|
-
};
|
|
3906
|
-
const queuePathInspection = (changed, fullChangedPath) => {
|
|
3907
|
-
pendingPathInspections.set(changed, fullChangedPath);
|
|
3908
|
-
if (!pathInspectionTimer)
|
|
3909
|
-
pathInspectionTimer = setTimer(processPathInspections, 25);
|
|
3910
|
-
};
|
|
3911
|
-
const watchDirectory = (dir, initialScan = false) => {
|
|
3912
|
-
if (watchers.has(dir))
|
|
3913
|
-
return;
|
|
3914
|
-
if (watchers.size >= maxWatchedDirectories) {
|
|
3915
|
-
reportWatchLimit();
|
|
3916
|
-
return;
|
|
3917
|
-
}
|
|
3918
|
-
const rel = directoryRelativePath(dir);
|
|
3919
|
-
if (rel && ignored(rel))
|
|
3920
|
-
return;
|
|
3921
|
-
try {
|
|
3922
|
-
const watcher = watch(dir, { persistent: false }, (_event, filename) => {
|
|
3923
|
-
if (!filename) {
|
|
3924
|
-
scheduleUpdate();
|
|
3925
|
-
return;
|
|
3926
|
-
}
|
|
3927
|
-
const changed = normalizeRelativePath(join7(rel, filename.toString()));
|
|
3928
|
-
if (ignored(changed))
|
|
3929
|
-
return;
|
|
3930
|
-
const fullChangedPath = join7(options.root, changed);
|
|
3931
|
-
if (!isInsideRoot(options.root, fullChangedPath))
|
|
3932
|
-
return;
|
|
3933
|
-
if (initialScanAsync) {
|
|
3934
|
-
queuePathInspection(changed, fullChangedPath);
|
|
3935
|
-
return;
|
|
3936
|
-
}
|
|
3937
|
-
processChangedPath(changed, fullChangedPath);
|
|
3938
|
-
}) || {};
|
|
3939
|
-
watchers.set(dir, watcher);
|
|
3940
|
-
const signature = directorySignature(dir);
|
|
3941
|
-
if (signature)
|
|
3942
|
-
signatures.set(dir, signature);
|
|
3943
|
-
watcher.on?.("error", () => {
|
|
3944
|
-
if (watchers.get(dir) === watcher) {
|
|
3945
|
-
watchers.delete(dir);
|
|
3946
|
-
signatures.delete(dir);
|
|
3947
|
-
}
|
|
3948
|
-
});
|
|
3949
|
-
watcher.on?.("close", () => {
|
|
3950
|
-
if (watchers.get(dir) === watcher) {
|
|
3951
|
-
watchers.delete(dir);
|
|
3952
|
-
signatures.delete(dir);
|
|
3953
|
-
}
|
|
3954
|
-
});
|
|
3955
|
-
} catch (error) {
|
|
3956
|
-
options.onError?.(error);
|
|
3957
|
-
return;
|
|
3958
|
-
}
|
|
3959
|
-
if (initialScanAsync && initialScan) {
|
|
3960
|
-
queueInitialChildren(dir);
|
|
3961
|
-
return;
|
|
3962
|
-
}
|
|
3963
|
-
if (watchers.size >= maxWatchedDirectories) {
|
|
3964
|
-
reportWatchLimit();
|
|
3965
|
-
return;
|
|
3966
|
-
}
|
|
3967
|
-
for (const child of readChildDirectories(dir))
|
|
3968
|
-
watchDirectory(child);
|
|
3969
|
-
};
|
|
3970
|
-
watchDirectory(options.root, true);
|
|
3971
|
-
return { started: watchers.size > 0, close: closeAll };
|
|
3972
|
-
}
|
|
3973
|
-
var DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT = 256;
|
|
3974
|
-
var init_worktree_watcher = __esm(() => {
|
|
3975
|
-
init_search();
|
|
3976
|
-
});
|
|
3977
|
-
|
|
3978
|
-
// web-src/core/control-chars.ts
|
|
3979
|
-
function hasControlCharacter(value) {
|
|
3980
|
-
for (const ch of value) {
|
|
3981
|
-
const code = ch.charCodeAt(0);
|
|
3982
|
-
if (code < 32 || code === 127)
|
|
3983
|
-
return true;
|
|
3984
|
-
}
|
|
3985
|
-
return false;
|
|
3986
|
-
}
|
|
3987
|
-
|
|
3988
|
-
// web-src/core/id.ts
|
|
3989
|
-
function bytesToHex(bytes) {
|
|
3990
|
-
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3991
3929
|
}
|
|
3992
|
-
function
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3930
|
+
function mergeViewState(current, patch) {
|
|
3931
|
+
if (!isRecord(patch))
|
|
3932
|
+
return current;
|
|
3933
|
+
const base = sanitizeViewState({ ...current, version: 1 });
|
|
3934
|
+
const collapsedDirs = new Set(base.collapsedDirs);
|
|
3935
|
+
const lazyExpandedDirs = new Set(base.lazyExpandedDirs);
|
|
3936
|
+
const viewedFiles = new Set(base.viewedFiles);
|
|
3937
|
+
const addedCollapsedDirs = normalizeStringList(patch.addedCollapsedDirs, {
|
|
3938
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3939
|
+
maxLen: MAX_KEY_LEN,
|
|
3940
|
+
keepLast: true,
|
|
3941
|
+
sort: false
|
|
3942
|
+
});
|
|
3943
|
+
for (const path of addedCollapsedDirs || []) {
|
|
3944
|
+
collapsedDirs.add(path);
|
|
3945
|
+
lazyExpandedDirs.delete(path);
|
|
3996
3946
|
}
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
3947
|
+
const removedCollapsedDirs = normalizeStringList(patch.removedCollapsedDirs, {
|
|
3948
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3949
|
+
maxLen: MAX_KEY_LEN,
|
|
3950
|
+
sort: false
|
|
3951
|
+
});
|
|
3952
|
+
for (const path of removedCollapsedDirs || [])
|
|
3953
|
+
collapsedDirs.delete(path);
|
|
3954
|
+
const addedLazyExpandedDirs = normalizeStringList(patch.addedLazyExpandedDirs, {
|
|
3955
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3956
|
+
maxLen: MAX_KEY_LEN,
|
|
3957
|
+
keepLast: true,
|
|
3958
|
+
sort: false
|
|
3959
|
+
});
|
|
3960
|
+
for (const path of addedLazyExpandedDirs || []) {
|
|
3961
|
+
if (!collapsedDirs.has(path))
|
|
3962
|
+
lazyExpandedDirs.add(path);
|
|
4001
3963
|
}
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
3964
|
+
const removedLazyExpandedDirs = normalizeStringList(patch.removedLazyExpandedDirs, {
|
|
3965
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3966
|
+
maxLen: MAX_KEY_LEN,
|
|
3967
|
+
sort: false
|
|
3968
|
+
});
|
|
3969
|
+
for (const path of removedLazyExpandedDirs || [])
|
|
3970
|
+
lazyExpandedDirs.delete(path);
|
|
3971
|
+
const addedViewedFiles = normalizeStringList(patch.addedViewedFiles, {
|
|
3972
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3973
|
+
maxLen: MAX_KEY_LEN,
|
|
3974
|
+
keepLast: true,
|
|
3975
|
+
sort: false
|
|
3976
|
+
});
|
|
3977
|
+
for (const path of addedViewedFiles || [])
|
|
3978
|
+
viewedFiles.add(path);
|
|
3979
|
+
const removedViewedFiles = normalizeStringList(patch.removedViewedFiles, {
|
|
3980
|
+
maxItems: MAX_VIEW_ITEMS,
|
|
3981
|
+
maxLen: MAX_KEY_LEN,
|
|
3982
|
+
sort: false
|
|
3983
|
+
});
|
|
3984
|
+
for (const path of removedViewedFiles || [])
|
|
3985
|
+
viewedFiles.delete(path);
|
|
3986
|
+
return sanitizeViewState({
|
|
3987
|
+
version: 1,
|
|
3988
|
+
collapsedDirs: [...collapsedDirs],
|
|
3989
|
+
lazyExpandedDirs: [...lazyExpandedDirs],
|
|
3990
|
+
viewedFiles: [...viewedFiles]
|
|
3991
|
+
});
|
|
4012
3992
|
}
|
|
4013
|
-
function
|
|
4014
|
-
if (
|
|
4015
|
-
return;
|
|
4016
|
-
if (value.length === 0 || value.length > maxLen)
|
|
4017
|
-
return;
|
|
4018
|
-
if (value.includes("\x00"))
|
|
4019
|
-
return;
|
|
3993
|
+
function safeObjectKey(value) {
|
|
3994
|
+
if (!value || value.length > MAX_KEY_LEN || value.includes("\x00"))
|
|
3995
|
+
return null;
|
|
4020
3996
|
return value;
|
|
4021
3997
|
}
|
|
4022
|
-
function
|
|
4023
|
-
|
|
4024
|
-
}
|
|
4025
|
-
function optionalNumber(value, min, max) {
|
|
4026
|
-
if (typeof value !== "number" || !Number.isFinite(value))
|
|
3998
|
+
function sanitizeDbUiPrefs(raw) {
|
|
3999
|
+
if (!isRecord(raw))
|
|
4027
4000
|
return;
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
function optionalFontSize(value) {
|
|
4036
|
-
return value === "compact" || value === "regular" || value === "large" || value === "xlarge" ? value : undefined;
|
|
4037
|
-
}
|
|
4038
|
-
function normalizeStringList(value, options) {
|
|
4039
|
-
if (!Array.isArray(value))
|
|
4040
|
-
return;
|
|
4041
|
-
const out = [];
|
|
4042
|
-
const seen = new Set;
|
|
4043
|
-
const items = options.keepLast ? [...value].reverse() : value;
|
|
4044
|
-
for (const item of items) {
|
|
4045
|
-
if (out.length >= options.maxItems)
|
|
4046
|
-
break;
|
|
4047
|
-
if (typeof item !== "string")
|
|
4048
|
-
continue;
|
|
4049
|
-
const name = item.trim();
|
|
4050
|
-
if (!name || name.length > options.maxLen || name.includes("\x00"))
|
|
4051
|
-
continue;
|
|
4052
|
-
if (options.pathSafe && (name.includes("/") || name.includes("\\") || name === "." || name === ".." || name === ".git")) {
|
|
4053
|
-
continue;
|
|
4054
|
-
}
|
|
4055
|
-
if (seen.has(name))
|
|
4056
|
-
continue;
|
|
4057
|
-
seen.add(name);
|
|
4058
|
-
out.push(name);
|
|
4059
|
-
}
|
|
4060
|
-
if (options.keepLast)
|
|
4061
|
-
out.reverse();
|
|
4062
|
-
return options.sort === false ? out : out.sort((a, b) => a.localeCompare(b));
|
|
4063
|
-
}
|
|
4064
|
-
function emptySettings() {
|
|
4065
|
-
return { version: 1 };
|
|
4066
|
-
}
|
|
4067
|
-
function emptyViewState() {
|
|
4068
|
-
return { version: 1, collapsedDirs: [], viewedFiles: [] };
|
|
4069
|
-
}
|
|
4070
|
-
function emptyDbUiState() {
|
|
4071
|
-
return { version: 1, columnWidths: {} };
|
|
4072
|
-
}
|
|
4073
|
-
function sanitizeSettings(raw) {
|
|
4074
|
-
if (!isRecord(raw))
|
|
4075
|
-
return emptySettings();
|
|
4076
|
-
const out = { version: 1 };
|
|
4077
|
-
if (raw.layout === "side-by-side" || raw.layout === "line-by-line")
|
|
4078
|
-
out.layout = raw.layout;
|
|
4079
|
-
if (raw.theme === "light" || raw.theme === "dark")
|
|
4080
|
-
out.theme = raw.theme;
|
|
4081
|
-
if (raw.language === "en" || raw.language === "ja")
|
|
4082
|
-
out.language = raw.language;
|
|
4083
|
-
if (raw.sidebarView === "tree" || raw.sidebarView === "flat")
|
|
4084
|
-
out.sidebarView = raw.sidebarView;
|
|
4085
|
-
const sidebarWidth = optionalNumber(raw.sidebarWidth, 180, 900);
|
|
4086
|
-
if (sidebarWidth !== undefined)
|
|
4087
|
-
out.sidebarWidth = sidebarWidth;
|
|
4088
|
-
const historyWidth = optionalNumber(raw.historyWidth, 220, 640);
|
|
4089
|
-
if (historyWidth !== undefined)
|
|
4090
|
-
out.historyWidth = historyWidth;
|
|
4091
|
-
const sidebarHidden = optionalBoolean(raw.sidebarHidden);
|
|
4092
|
-
if (sidebarHidden !== undefined)
|
|
4093
|
-
out.sidebarHidden = sidebarHidden;
|
|
4094
|
-
const sidebarFontSize = optionalFontSize(raw.sidebarFontSize);
|
|
4095
|
-
if (sidebarFontSize)
|
|
4096
|
-
out.sidebarFontSize = sidebarFontSize;
|
|
4097
|
-
const codeFontSize = optionalFontSize(raw.codeFontSize);
|
|
4098
|
-
if (codeFontSize)
|
|
4099
|
-
out.codeFontSize = codeFontSize;
|
|
4100
|
-
const syntaxHighlight = optionalBoolean(raw.syntaxHighlight);
|
|
4101
|
-
if (syntaxHighlight !== undefined)
|
|
4102
|
-
out.syntaxHighlight = syntaxHighlight;
|
|
4103
|
-
const autoUpdate = optionalBoolean(raw.autoUpdate);
|
|
4104
|
-
if (autoUpdate !== undefined)
|
|
4105
|
-
out.autoUpdate = autoUpdate;
|
|
4106
|
-
const queryHistoryPanelWidth = optionalNumber(raw.queryHistoryPanelWidth, 280, 800);
|
|
4107
|
-
if (queryHistoryPanelWidth !== undefined)
|
|
4108
|
-
out.queryHistoryPanelWidth = queryHistoryPanelWidth;
|
|
4109
|
-
const annotationPanelOpen = optionalBoolean(raw.annotationPanelOpen);
|
|
4110
|
-
if (annotationPanelOpen !== undefined)
|
|
4111
|
-
out.annotationPanelOpen = annotationPanelOpen;
|
|
4112
|
-
const annotationFollow = optionalBoolean(raw.annotationFollow);
|
|
4113
|
-
if (annotationFollow !== undefined)
|
|
4114
|
-
out.annotationFollow = annotationFollow;
|
|
4115
|
-
const annotationMuted = optionalBoolean(raw.annotationMuted);
|
|
4116
|
-
if (annotationMuted !== undefined)
|
|
4117
|
-
out.annotationMuted = annotationMuted;
|
|
4118
|
-
const annotationRate = optionalFloat(raw.annotationRate, 0.5, 2);
|
|
4119
|
-
if (annotationRate !== undefined)
|
|
4120
|
-
out.annotationRate = annotationRate;
|
|
4121
|
-
const ignoreWhitespace = optionalBoolean(raw.ignoreWhitespace);
|
|
4122
|
-
if (ignoreWhitespace !== undefined)
|
|
4123
|
-
out.ignoreWhitespace = ignoreWhitespace;
|
|
4124
|
-
const hideTests = optionalBoolean(raw.hideTests);
|
|
4125
|
-
if (hideTests !== undefined)
|
|
4126
|
-
out.hideTests = hideTests;
|
|
4127
|
-
const scopeOmitDirs = normalizeStringList(raw.scopeOmitDirs, {
|
|
4128
|
-
maxItems: 100,
|
|
4129
|
-
maxLen: 64,
|
|
4130
|
-
pathSafe: true
|
|
4131
|
-
});
|
|
4132
|
-
if (scopeOmitDirs)
|
|
4133
|
-
out.scopeOmitDirs = scopeOmitDirs;
|
|
4134
|
-
const scopeExcludeNames = normalizeStringList(raw.scopeExcludeNames, {
|
|
4135
|
-
maxItems: 200,
|
|
4136
|
-
maxLen: 128,
|
|
4137
|
-
pathSafe: true
|
|
4138
|
-
});
|
|
4139
|
-
if (scopeExcludeNames)
|
|
4140
|
-
out.scopeExcludeNames = scopeExcludeNames;
|
|
4141
|
-
if (isRecord(raw.range)) {
|
|
4142
|
-
const from = optionalString(raw.range.from, MAX_REF_LEN);
|
|
4143
|
-
const to = optionalString(raw.range.to, MAX_REF_LEN);
|
|
4144
|
-
if (from && to)
|
|
4145
|
-
out.range = { from, to };
|
|
4146
|
-
}
|
|
4147
|
-
return out;
|
|
4148
|
-
}
|
|
4149
|
-
function mergeSettings(current, patch) {
|
|
4150
|
-
if (!isRecord(patch))
|
|
4151
|
-
return current;
|
|
4152
|
-
const raw = { ...current };
|
|
4153
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
4154
|
-
if (key === "version")
|
|
4155
|
-
continue;
|
|
4156
|
-
if (value === null)
|
|
4157
|
-
delete raw[key];
|
|
4158
|
-
else
|
|
4159
|
-
raw[key] = value;
|
|
4160
|
-
}
|
|
4161
|
-
return sanitizeSettings({ ...raw, version: 1 });
|
|
4162
|
-
}
|
|
4163
|
-
function sanitizeViewState(raw) {
|
|
4164
|
-
if (!isRecord(raw))
|
|
4165
|
-
return emptyViewState();
|
|
4166
|
-
return {
|
|
4167
|
-
version: 1,
|
|
4168
|
-
collapsedDirs: normalizeStringList(raw.collapsedDirs, {
|
|
4169
|
-
maxItems: MAX_VIEW_ITEMS,
|
|
4170
|
-
maxLen: MAX_KEY_LEN,
|
|
4171
|
-
keepLast: true,
|
|
4172
|
-
sort: false
|
|
4173
|
-
}) ?? [],
|
|
4174
|
-
viewedFiles: normalizeStringList(raw.viewedFiles, {
|
|
4175
|
-
maxItems: MAX_VIEW_ITEMS,
|
|
4176
|
-
maxLen: MAX_KEY_LEN,
|
|
4177
|
-
keepLast: true,
|
|
4178
|
-
sort: false
|
|
4179
|
-
}) ?? []
|
|
4180
|
-
};
|
|
4181
|
-
}
|
|
4182
|
-
function mergeViewState(current, patch) {
|
|
4183
|
-
if (!isRecord(patch))
|
|
4184
|
-
return current;
|
|
4185
|
-
const base = sanitizeViewState({ ...current, version: 1 });
|
|
4186
|
-
const collapsedDirs = new Set(base.collapsedDirs);
|
|
4187
|
-
const viewedFiles = new Set(base.viewedFiles);
|
|
4188
|
-
const addedCollapsedDirs = normalizeStringList(patch.addedCollapsedDirs, {
|
|
4189
|
-
maxItems: MAX_VIEW_ITEMS,
|
|
4190
|
-
maxLen: MAX_KEY_LEN,
|
|
4191
|
-
keepLast: true,
|
|
4192
|
-
sort: false
|
|
4193
|
-
});
|
|
4194
|
-
for (const path of addedCollapsedDirs || [])
|
|
4195
|
-
collapsedDirs.add(path);
|
|
4196
|
-
const removedCollapsedDirs = normalizeStringList(patch.removedCollapsedDirs, {
|
|
4197
|
-
maxItems: MAX_VIEW_ITEMS,
|
|
4198
|
-
maxLen: MAX_KEY_LEN,
|
|
4199
|
-
sort: false
|
|
4200
|
-
});
|
|
4201
|
-
for (const path of removedCollapsedDirs || [])
|
|
4202
|
-
collapsedDirs.delete(path);
|
|
4203
|
-
const addedViewedFiles = normalizeStringList(patch.addedViewedFiles, {
|
|
4204
|
-
maxItems: MAX_VIEW_ITEMS,
|
|
4205
|
-
maxLen: MAX_KEY_LEN,
|
|
4206
|
-
keepLast: true,
|
|
4207
|
-
sort: false
|
|
4208
|
-
});
|
|
4209
|
-
for (const path of addedViewedFiles || [])
|
|
4210
|
-
viewedFiles.add(path);
|
|
4211
|
-
const removedViewedFiles = normalizeStringList(patch.removedViewedFiles, {
|
|
4212
|
-
maxItems: MAX_VIEW_ITEMS,
|
|
4213
|
-
maxLen: MAX_KEY_LEN,
|
|
4214
|
-
sort: false
|
|
4215
|
-
});
|
|
4216
|
-
for (const path of removedViewedFiles || [])
|
|
4217
|
-
viewedFiles.delete(path);
|
|
4218
|
-
return sanitizeViewState({
|
|
4219
|
-
version: 1,
|
|
4220
|
-
collapsedDirs: [...collapsedDirs],
|
|
4221
|
-
viewedFiles: [...viewedFiles]
|
|
4222
|
-
});
|
|
4223
|
-
}
|
|
4224
|
-
function safeObjectKey(value) {
|
|
4225
|
-
if (!value || value.length > MAX_KEY_LEN || value.includes("\x00"))
|
|
4226
|
-
return null;
|
|
4227
|
-
return value;
|
|
4001
|
+
const out = {};
|
|
4002
|
+
for (const key of DB_UI_BOOL_PREF_KEYS) {
|
|
4003
|
+
const v = raw[key];
|
|
4004
|
+
if (v === true || v === false)
|
|
4005
|
+
out[key] = v;
|
|
4006
|
+
}
|
|
4007
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
4228
4008
|
}
|
|
4229
4009
|
function sanitizeDbUiState(raw) {
|
|
4230
|
-
if (!isRecord(raw)
|
|
4010
|
+
if (!isRecord(raw))
|
|
4231
4011
|
return emptyDbUiState();
|
|
4012
|
+
const prefs = sanitizeDbUiPrefs(raw.prefs);
|
|
4013
|
+
if (!isRecord(raw.columnWidths)) {
|
|
4014
|
+
return prefs ? { ...emptyDbUiState(), prefs } : emptyDbUiState();
|
|
4015
|
+
}
|
|
4232
4016
|
const columnWidths = {};
|
|
4233
4017
|
let dbCount = 0;
|
|
4234
4018
|
for (const [dbIdRaw, tablesRaw] of Object.entries(raw.columnWidths)) {
|
|
@@ -4262,105 +4046,411 @@ function sanitizeDbUiState(raw) {
|
|
|
4262
4046
|
tables[table] = columns;
|
|
4263
4047
|
tableCount++;
|
|
4264
4048
|
}
|
|
4265
|
-
if (Object.keys(tables).length === 0)
|
|
4266
|
-
continue;
|
|
4267
|
-
columnWidths[dbId] = tables;
|
|
4268
|
-
dbCount++;
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
const
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4049
|
+
if (Object.keys(tables).length === 0)
|
|
4050
|
+
continue;
|
|
4051
|
+
columnWidths[dbId] = tables;
|
|
4052
|
+
dbCount++;
|
|
4053
|
+
}
|
|
4054
|
+
const out = { version: 1, columnWidths };
|
|
4055
|
+
if (prefs)
|
|
4056
|
+
out.prefs = prefs;
|
|
4057
|
+
return out;
|
|
4058
|
+
}
|
|
4059
|
+
function mergeDbUiPrefs(current, patch) {
|
|
4060
|
+
if (!isRecord(patch))
|
|
4061
|
+
return current;
|
|
4062
|
+
const next = { ...current ?? {} };
|
|
4063
|
+
for (const key of DB_UI_BOOL_PREF_KEYS) {
|
|
4064
|
+
const v = patch[key];
|
|
4065
|
+
if (v === null)
|
|
4066
|
+
delete next[key];
|
|
4067
|
+
else if (v === true || v === false)
|
|
4068
|
+
next[key] = v;
|
|
4069
|
+
}
|
|
4070
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
4071
|
+
}
|
|
4072
|
+
function mergeDbUiState(current, patch) {
|
|
4073
|
+
if (!isRecord(patch))
|
|
4074
|
+
return current;
|
|
4075
|
+
const mergedPrefs = "prefs" in patch ? mergeDbUiPrefs(current.prefs, patch.prefs) : current.prefs;
|
|
4076
|
+
if (!isRecord(patch.columnWidths)) {
|
|
4077
|
+
const merged = { ...current, version: 1 };
|
|
4078
|
+
if (mergedPrefs)
|
|
4079
|
+
merged.prefs = mergedPrefs;
|
|
4080
|
+
else
|
|
4081
|
+
delete merged.prefs;
|
|
4082
|
+
return sanitizeDbUiState(merged);
|
|
4083
|
+
}
|
|
4084
|
+
const columnWidths = {
|
|
4085
|
+
...current.columnWidths
|
|
4086
|
+
};
|
|
4087
|
+
for (const [dbId, tablesRaw] of Object.entries(patch.columnWidths)) {
|
|
4088
|
+
if (tablesRaw === null) {
|
|
4089
|
+
delete columnWidths[dbId];
|
|
4090
|
+
continue;
|
|
4091
|
+
}
|
|
4092
|
+
if (!isRecord(tablesRaw))
|
|
4093
|
+
continue;
|
|
4094
|
+
const tables = { ...columnWidths[dbId] || {} };
|
|
4095
|
+
for (const [table, columnsRaw] of Object.entries(tablesRaw)) {
|
|
4096
|
+
if (columnsRaw === null) {
|
|
4097
|
+
delete tables[table];
|
|
4098
|
+
continue;
|
|
4099
|
+
}
|
|
4100
|
+
if (!isRecord(columnsRaw))
|
|
4101
|
+
continue;
|
|
4102
|
+
const columns = { ...tables[table] || {} };
|
|
4103
|
+
for (const [column, widthRaw] of Object.entries(columnsRaw)) {
|
|
4104
|
+
if (widthRaw === null)
|
|
4105
|
+
delete columns[column];
|
|
4106
|
+
else
|
|
4107
|
+
columns[column] = widthRaw;
|
|
4108
|
+
}
|
|
4109
|
+
tables[table] = columns;
|
|
4110
|
+
}
|
|
4111
|
+
columnWidths[dbId] = tables;
|
|
4112
|
+
}
|
|
4113
|
+
return sanitizeDbUiState({
|
|
4114
|
+
...current,
|
|
4115
|
+
...patch,
|
|
4116
|
+
columnWidths,
|
|
4117
|
+
prefs: mergedPrefs,
|
|
4118
|
+
version: 1
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
async function loadAppSettingsState(root) {
|
|
4122
|
+
return settingsStore.load(root);
|
|
4123
|
+
}
|
|
4124
|
+
async function patchAppSettingsState(root, patch) {
|
|
4125
|
+
return settingsStore.update(root, (state) => {
|
|
4126
|
+
const next = mergeSettings(state, patch);
|
|
4127
|
+
return { state: next, result: next };
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
async function loadViewState(root) {
|
|
4131
|
+
return viewStateStore.load(root);
|
|
4132
|
+
}
|
|
4133
|
+
async function patchViewState(root, patch) {
|
|
4134
|
+
return viewStateStore.update(root, (state) => {
|
|
4135
|
+
const next = mergeViewState(state, patch);
|
|
4136
|
+
return { state: next, result: next };
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
async function loadDbUiState(root) {
|
|
4140
|
+
return dbUiStore.load(root);
|
|
4141
|
+
}
|
|
4142
|
+
async function patchDbUiState(root, patch) {
|
|
4143
|
+
return dbUiStore.update(root, (state) => {
|
|
4144
|
+
const next = mergeDbUiState(state, patch);
|
|
4145
|
+
return { state: next, result: next };
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
var CODE_VIEWER_DIR2 = ".code-viewer", SETTINGS_FILE_NAME = "settings.json", VIEW_STATE_FILE_NAME = "view-state.json", DB_UI_FILE_NAME = "db-ui.json", MAX_SETTINGS_BYTES = 200000, MAX_VIEW_STATE_BYTES = 1e6, MAX_DB_UI_BYTES = 1e6, MAX_REF_LEN = 1024, MAX_KEY_LEN = 2048, MAX_VIEW_ITEMS = 20000, MAX_DB_UI_DBS = 200, MAX_DB_UI_TABLES = 500, MAX_DB_UI_COLUMNS = 1000, DB_UI_BOOL_PREF_KEYS, settingsStore, viewStateStore, dbUiStore;
|
|
4149
|
+
var init_state_store = __esm(() => {
|
|
4150
|
+
init_json_store();
|
|
4151
|
+
DB_UI_BOOL_PREF_KEYS = ["s3TooltipEnabled", "inferFkRails"];
|
|
4152
|
+
settingsStore = createJsonFileStore({
|
|
4153
|
+
filePath: (root) => codeViewerPath(root, SETTINGS_FILE_NAME),
|
|
4154
|
+
empty: emptySettings,
|
|
4155
|
+
sanitize: sanitizeSettings,
|
|
4156
|
+
maxBytes: MAX_SETTINGS_BYTES,
|
|
4157
|
+
backupSuffix: "corrupt",
|
|
4158
|
+
sizeErrorMessage: "settings state too large"
|
|
4159
|
+
});
|
|
4160
|
+
viewStateStore = createJsonFileStore({
|
|
4161
|
+
filePath: (root) => codeViewerPath(root, VIEW_STATE_FILE_NAME),
|
|
4162
|
+
empty: emptyViewState,
|
|
4163
|
+
sanitize: sanitizeViewState,
|
|
4164
|
+
maxBytes: MAX_VIEW_STATE_BYTES,
|
|
4165
|
+
backupSuffix: "corrupt",
|
|
4166
|
+
sizeErrorMessage: "view state too large"
|
|
4167
|
+
});
|
|
4168
|
+
dbUiStore = createJsonFileStore({
|
|
4169
|
+
filePath: (root) => codeViewerPath(root, DB_UI_FILE_NAME),
|
|
4170
|
+
empty: emptyDbUiState,
|
|
4171
|
+
sanitize: sanitizeDbUiState,
|
|
4172
|
+
maxBytes: MAX_DB_UI_BYTES,
|
|
4173
|
+
backupSuffix: "corrupt",
|
|
4174
|
+
sizeErrorMessage: "db UI state too large"
|
|
4175
|
+
});
|
|
4176
|
+
});
|
|
4177
|
+
|
|
4178
|
+
// web-src/server/worktree-watcher.ts
|
|
4179
|
+
import {
|
|
4180
|
+
lstatSync as lstatSync3,
|
|
4181
|
+
readdirSync as nodeReaddirSync,
|
|
4182
|
+
watch as nodeWatch
|
|
4183
|
+
} from "node:fs";
|
|
4184
|
+
import { join as join8, relative } from "node:path";
|
|
4185
|
+
function normalizeRelativePath(path) {
|
|
4186
|
+
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
4187
|
+
}
|
|
4188
|
+
function isInsideRoot(root, path) {
|
|
4189
|
+
const rel = relative(root, path).replace(/\\/g, "/");
|
|
4190
|
+
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
4191
|
+
}
|
|
4192
|
+
function startWorktreeUpdateWatch(options) {
|
|
4193
|
+
const watch = options.watch || nodeWatch;
|
|
4194
|
+
const readDirs = options.readdirSync || ((path) => nodeReaddirSync(path, { withFileTypes: true }));
|
|
4195
|
+
const isDirectory = options.isDirectory || ((path) => {
|
|
4196
|
+
try {
|
|
4197
|
+
return lstatSync3(path).isDirectory();
|
|
4198
|
+
} catch {
|
|
4199
|
+
return false;
|
|
4200
|
+
}
|
|
4201
|
+
});
|
|
4202
|
+
const directorySignature = options.directorySignature || ((path) => {
|
|
4203
|
+
try {
|
|
4204
|
+
const stats = lstatSync3(path);
|
|
4205
|
+
if (!stats.isDirectory())
|
|
4206
|
+
return null;
|
|
4207
|
+
return `${stats.dev}:${stats.ino}`;
|
|
4208
|
+
} catch {
|
|
4209
|
+
return null;
|
|
4210
|
+
}
|
|
4211
|
+
});
|
|
4212
|
+
const setTimer = options.setTimeoutFn || setTimeout;
|
|
4213
|
+
const clearTimer = options.clearTimeoutFn || clearTimeout;
|
|
4214
|
+
const debounceMs = options.debounceMs ?? 250;
|
|
4215
|
+
const maxWatchedDirectories = Math.max(1, Math.floor(options.maxWatchedDirectories ?? DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT));
|
|
4216
|
+
const watchers = new Map;
|
|
4217
|
+
const signatures = new Map;
|
|
4218
|
+
const initialScanAsync = options.initialScanMode === "async" || (!options.watch || options.watch === nodeWatch) && !options.readdirSync;
|
|
4219
|
+
const initialScanQueue = [];
|
|
4220
|
+
let initialScanTimer = null;
|
|
4221
|
+
const pendingPathInspections = new Map;
|
|
4222
|
+
let pathInspectionTimer = null;
|
|
4223
|
+
let timer = null;
|
|
4224
|
+
const pendingChangedPaths = new Set;
|
|
4225
|
+
let watchLimitReported = false;
|
|
4226
|
+
const ignored = (path) => isSkippableSearchPath(normalizeRelativePath(path), options.omitDirNames, options.excludeNames);
|
|
4227
|
+
const directoryRelativePath = (dir) => normalizeRelativePath(relative(options.root, dir));
|
|
4228
|
+
const ignoredDirectory = (dir) => {
|
|
4229
|
+
const rel = directoryRelativePath(dir);
|
|
4230
|
+
return Boolean(rel && ignored(rel));
|
|
4231
|
+
};
|
|
4232
|
+
const scheduleUpdate = (changedPath) => {
|
|
4233
|
+
if (changedPath)
|
|
4234
|
+
pendingChangedPaths.add(changedPath);
|
|
4235
|
+
if (timer)
|
|
4236
|
+
clearTimer(timer);
|
|
4237
|
+
timer = setTimer(() => {
|
|
4238
|
+
timer = null;
|
|
4239
|
+
const paths = pendingChangedPaths.size ? [...pendingChangedPaths] : undefined;
|
|
4240
|
+
pendingChangedPaths.clear();
|
|
4241
|
+
options.onUpdate(paths);
|
|
4242
|
+
}, debounceMs);
|
|
4243
|
+
};
|
|
4244
|
+
const reportWatchLimit = () => {
|
|
4245
|
+
if (watchLimitReported)
|
|
4246
|
+
return;
|
|
4247
|
+
watchLimitReported = true;
|
|
4248
|
+
options.onWatchLimit?.(maxWatchedDirectories);
|
|
4249
|
+
options.onError?.(new Error(`worktree watcher cap reached (${maxWatchedDirectories}); subsequent changes may be missed`));
|
|
4250
|
+
};
|
|
4251
|
+
const closeSubtree = (dir) => {
|
|
4252
|
+
for (const [watchedDir, watcher] of [...watchers]) {
|
|
4253
|
+
if (watchedDir !== dir && !watchedDir.startsWith(`${dir}/`))
|
|
4254
|
+
continue;
|
|
4255
|
+
try {
|
|
4256
|
+
watcher.close?.();
|
|
4257
|
+
} catch {}
|
|
4258
|
+
watchers.delete(watchedDir);
|
|
4259
|
+
signatures.delete(watchedDir);
|
|
4260
|
+
}
|
|
4261
|
+
};
|
|
4262
|
+
const closeAll = () => {
|
|
4263
|
+
if (initialScanTimer) {
|
|
4264
|
+
clearTimer(initialScanTimer);
|
|
4265
|
+
initialScanTimer = null;
|
|
4266
|
+
}
|
|
4267
|
+
if (pathInspectionTimer) {
|
|
4268
|
+
clearTimer(pathInspectionTimer);
|
|
4269
|
+
pathInspectionTimer = null;
|
|
4270
|
+
}
|
|
4271
|
+
initialScanQueue.length = 0;
|
|
4272
|
+
pendingPathInspections.clear();
|
|
4273
|
+
for (const watcher of [...watchers.values()]) {
|
|
4274
|
+
try {
|
|
4275
|
+
watcher.close?.();
|
|
4276
|
+
} catch {}
|
|
4277
|
+
}
|
|
4278
|
+
watchers.clear();
|
|
4279
|
+
signatures.clear();
|
|
4280
|
+
};
|
|
4281
|
+
const readChildDirectories = (dir) => {
|
|
4282
|
+
let entries;
|
|
4283
|
+
try {
|
|
4284
|
+
entries = readDirs(dir);
|
|
4285
|
+
} catch (error) {
|
|
4286
|
+
options.onError?.(error);
|
|
4287
|
+
return [];
|
|
4288
|
+
}
|
|
4289
|
+
const children = [];
|
|
4290
|
+
for (const entry of entries) {
|
|
4291
|
+
if (!entry.isDirectory())
|
|
4292
|
+
continue;
|
|
4293
|
+
const child = join8(dir, entry.name);
|
|
4294
|
+
if (ignoredDirectory(child))
|
|
4295
|
+
continue;
|
|
4296
|
+
children.push(child);
|
|
4297
|
+
}
|
|
4298
|
+
return children;
|
|
4299
|
+
};
|
|
4300
|
+
const processInitialScanQueue = () => {
|
|
4301
|
+
initialScanTimer = null;
|
|
4302
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
4303
|
+
reportWatchLimit();
|
|
4304
|
+
initialScanQueue.length = 0;
|
|
4305
|
+
return;
|
|
4306
|
+
}
|
|
4307
|
+
const next = initialScanQueue.shift();
|
|
4308
|
+
if (next)
|
|
4309
|
+
watchDirectory(next, true);
|
|
4310
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
4311
|
+
reportWatchLimit();
|
|
4312
|
+
initialScanQueue.length = 0;
|
|
4313
|
+
}
|
|
4314
|
+
if (initialScanQueue.length)
|
|
4315
|
+
initialScanTimer = setTimer(processInitialScanQueue, 50);
|
|
4316
|
+
};
|
|
4317
|
+
const queueInitialChildren = (dir) => {
|
|
4318
|
+
const remaining = maxWatchedDirectories - watchers.size;
|
|
4319
|
+
if (remaining <= 0) {
|
|
4320
|
+
reportWatchLimit();
|
|
4321
|
+
return;
|
|
4322
|
+
}
|
|
4323
|
+
const children = readChildDirectories(dir);
|
|
4324
|
+
if (children.length > remaining)
|
|
4325
|
+
reportWatchLimit();
|
|
4326
|
+
initialScanQueue.push(...children.slice(0, remaining));
|
|
4327
|
+
if (!initialScanTimer)
|
|
4328
|
+
initialScanTimer = setTimer(processInitialScanQueue, 5000);
|
|
4329
|
+
};
|
|
4330
|
+
const processChangedPath = (changed, fullChangedPath) => {
|
|
4331
|
+
const known = watchers.has(fullChangedPath);
|
|
4332
|
+
if (isDirectory(fullChangedPath)) {
|
|
4333
|
+
if (known) {
|
|
4334
|
+
const signature = directorySignature(fullChangedPath);
|
|
4335
|
+
if (signature && signature !== signatures.get(fullChangedPath)) {
|
|
4336
|
+
closeSubtree(fullChangedPath);
|
|
4337
|
+
watchDirectory(fullChangedPath, initialScanAsync);
|
|
4338
|
+
}
|
|
4339
|
+
scheduleUpdate(changed);
|
|
4340
|
+
return;
|
|
4341
|
+
}
|
|
4342
|
+
watchDirectory(fullChangedPath, initialScanAsync);
|
|
4343
|
+
} else if (known) {
|
|
4344
|
+
closeSubtree(fullChangedPath);
|
|
4345
|
+
}
|
|
4346
|
+
scheduleUpdate(changed);
|
|
4347
|
+
};
|
|
4348
|
+
const processPathInspections = () => {
|
|
4349
|
+
pathInspectionTimer = null;
|
|
4350
|
+
const entries = [...pendingPathInspections];
|
|
4351
|
+
pendingPathInspections.clear();
|
|
4352
|
+
for (const [changed, fullChangedPath] of entries) {
|
|
4353
|
+
processChangedPath(changed, fullChangedPath);
|
|
4354
|
+
}
|
|
4355
|
+
};
|
|
4356
|
+
const queuePathInspection = (changed, fullChangedPath) => {
|
|
4357
|
+
pendingPathInspections.set(changed, fullChangedPath);
|
|
4358
|
+
if (!pathInspectionTimer)
|
|
4359
|
+
pathInspectionTimer = setTimer(processPathInspections, 25);
|
|
4360
|
+
};
|
|
4361
|
+
const watchDirectory = (dir, initialScan = false) => {
|
|
4362
|
+
if (watchers.has(dir))
|
|
4363
|
+
return;
|
|
4364
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
4365
|
+
reportWatchLimit();
|
|
4366
|
+
return;
|
|
4367
|
+
}
|
|
4368
|
+
const rel = directoryRelativePath(dir);
|
|
4369
|
+
if (rel && ignored(rel))
|
|
4370
|
+
return;
|
|
4371
|
+
try {
|
|
4372
|
+
const watcher = watch(dir, { persistent: false }, (_event, filename) => {
|
|
4373
|
+
if (!filename) {
|
|
4374
|
+
scheduleUpdate();
|
|
4375
|
+
return;
|
|
4376
|
+
}
|
|
4377
|
+
const changed = normalizeRelativePath(join8(rel, filename.toString()));
|
|
4378
|
+
if (ignored(changed))
|
|
4379
|
+
return;
|
|
4380
|
+
const fullChangedPath = join8(options.root, changed);
|
|
4381
|
+
if (!isInsideRoot(options.root, fullChangedPath))
|
|
4382
|
+
return;
|
|
4383
|
+
if (initialScanAsync) {
|
|
4384
|
+
queuePathInspection(changed, fullChangedPath);
|
|
4385
|
+
return;
|
|
4386
|
+
}
|
|
4387
|
+
processChangedPath(changed, fullChangedPath);
|
|
4388
|
+
}) || {};
|
|
4389
|
+
watchers.set(dir, watcher);
|
|
4390
|
+
const signature = directorySignature(dir);
|
|
4391
|
+
if (signature)
|
|
4392
|
+
signatures.set(dir, signature);
|
|
4393
|
+
watcher.on?.("error", () => {
|
|
4394
|
+
if (watchers.get(dir) === watcher) {
|
|
4395
|
+
watchers.delete(dir);
|
|
4396
|
+
signatures.delete(dir);
|
|
4397
|
+
}
|
|
4398
|
+
});
|
|
4399
|
+
watcher.on?.("close", () => {
|
|
4400
|
+
if (watchers.get(dir) === watcher) {
|
|
4401
|
+
watchers.delete(dir);
|
|
4402
|
+
signatures.delete(dir);
|
|
4403
|
+
}
|
|
4404
|
+
});
|
|
4405
|
+
} catch (error) {
|
|
4406
|
+
options.onError?.(error);
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
if (initialScanAsync && initialScan) {
|
|
4410
|
+
queueInitialChildren(dir);
|
|
4411
|
+
return;
|
|
4285
4412
|
}
|
|
4286
|
-
if (
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
for (const [table, columnsRaw] of Object.entries(tablesRaw)) {
|
|
4290
|
-
if (columnsRaw === null) {
|
|
4291
|
-
delete tables[table];
|
|
4292
|
-
continue;
|
|
4293
|
-
}
|
|
4294
|
-
if (!isRecord(columnsRaw))
|
|
4295
|
-
continue;
|
|
4296
|
-
const columns = { ...tables[table] || {} };
|
|
4297
|
-
for (const [column, widthRaw] of Object.entries(columnsRaw)) {
|
|
4298
|
-
if (widthRaw === null)
|
|
4299
|
-
delete columns[column];
|
|
4300
|
-
else
|
|
4301
|
-
columns[column] = widthRaw;
|
|
4302
|
-
}
|
|
4303
|
-
tables[table] = columns;
|
|
4413
|
+
if (watchers.size >= maxWatchedDirectories) {
|
|
4414
|
+
reportWatchLimit();
|
|
4415
|
+
return;
|
|
4304
4416
|
}
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
return settingsStore.load(root);
|
|
4311
|
-
}
|
|
4312
|
-
async function patchAppSettingsState(root, patch) {
|
|
4313
|
-
return settingsStore.update(root, (state) => {
|
|
4314
|
-
const next = mergeSettings(state, patch);
|
|
4315
|
-
return { state: next, result: next };
|
|
4316
|
-
});
|
|
4317
|
-
}
|
|
4318
|
-
async function loadViewState(root) {
|
|
4319
|
-
return viewStateStore.load(root);
|
|
4417
|
+
for (const child of readChildDirectories(dir))
|
|
4418
|
+
watchDirectory(child);
|
|
4419
|
+
};
|
|
4420
|
+
watchDirectory(options.root, true);
|
|
4421
|
+
return { started: watchers.size > 0, close: closeAll };
|
|
4320
4422
|
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4423
|
+
var DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT = 1024, MIN_WORKTREE_WATCH_DIRECTORY_LIMIT = 1, MAX_WORKTREE_WATCH_DIRECTORY_LIMIT = 65536;
|
|
4424
|
+
var init_worktree_watcher = __esm(() => {
|
|
4425
|
+
init_search();
|
|
4426
|
+
});
|
|
4427
|
+
|
|
4428
|
+
// web-src/core/control-chars.ts
|
|
4429
|
+
function hasControlCharacter(value) {
|
|
4430
|
+
for (const ch of value) {
|
|
4431
|
+
const code = ch.charCodeAt(0);
|
|
4432
|
+
if (code < 32 || code === 127)
|
|
4433
|
+
return true;
|
|
4434
|
+
}
|
|
4435
|
+
return false;
|
|
4326
4436
|
}
|
|
4327
|
-
|
|
4328
|
-
|
|
4437
|
+
|
|
4438
|
+
// web-src/core/id.ts
|
|
4439
|
+
function bytesToHex(bytes) {
|
|
4440
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4329
4441
|
}
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
return {
|
|
4334
|
-
}
|
|
4442
|
+
function makeId(prefix) {
|
|
4443
|
+
const cryptoApi = globalThis.crypto;
|
|
4444
|
+
if (typeof cryptoApi?.randomUUID === "function") {
|
|
4445
|
+
return `${prefix}-${cryptoApi.randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
4446
|
+
}
|
|
4447
|
+
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
4448
|
+
const bytes = new Uint8Array(8);
|
|
4449
|
+
cryptoApi.getRandomValues(bytes);
|
|
4450
|
+
return `${prefix}-${bytesToHex(bytes)}`;
|
|
4451
|
+
}
|
|
4452
|
+
return `${prefix}-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
4335
4453
|
}
|
|
4336
|
-
var CODE_VIEWER_DIR2 = ".code-viewer", SETTINGS_FILE_NAME = "settings.json", VIEW_STATE_FILE_NAME = "view-state.json", DB_UI_FILE_NAME = "db-ui.json", MAX_SETTINGS_BYTES = 200000, MAX_VIEW_STATE_BYTES = 1e6, MAX_DB_UI_BYTES = 1e6, MAX_REF_LEN = 1024, MAX_KEY_LEN = 2048, MAX_VIEW_ITEMS = 20000, MAX_DB_UI_DBS = 200, MAX_DB_UI_TABLES = 500, MAX_DB_UI_COLUMNS = 1000, settingsStore, viewStateStore, dbUiStore;
|
|
4337
|
-
var init_state_store = __esm(() => {
|
|
4338
|
-
init_json_store();
|
|
4339
|
-
settingsStore = createJsonFileStore({
|
|
4340
|
-
filePath: (root) => codeViewerPath(root, SETTINGS_FILE_NAME),
|
|
4341
|
-
empty: emptySettings,
|
|
4342
|
-
sanitize: sanitizeSettings,
|
|
4343
|
-
maxBytes: MAX_SETTINGS_BYTES,
|
|
4344
|
-
backupSuffix: "corrupt",
|
|
4345
|
-
sizeErrorMessage: "settings state too large"
|
|
4346
|
-
});
|
|
4347
|
-
viewStateStore = createJsonFileStore({
|
|
4348
|
-
filePath: (root) => codeViewerPath(root, VIEW_STATE_FILE_NAME),
|
|
4349
|
-
empty: emptyViewState,
|
|
4350
|
-
sanitize: sanitizeViewState,
|
|
4351
|
-
maxBytes: MAX_VIEW_STATE_BYTES,
|
|
4352
|
-
backupSuffix: "corrupt",
|
|
4353
|
-
sizeErrorMessage: "view state too large"
|
|
4354
|
-
});
|
|
4355
|
-
dbUiStore = createJsonFileStore({
|
|
4356
|
-
filePath: (root) => codeViewerPath(root, DB_UI_FILE_NAME),
|
|
4357
|
-
empty: emptyDbUiState,
|
|
4358
|
-
sanitize: sanitizeDbUiState,
|
|
4359
|
-
maxBytes: MAX_DB_UI_BYTES,
|
|
4360
|
-
backupSuffix: "corrupt",
|
|
4361
|
-
sizeErrorMessage: "db UI state too large"
|
|
4362
|
-
});
|
|
4363
|
-
});
|
|
4364
4454
|
|
|
4365
4455
|
// web-src/server/database/adapters/abort.ts
|
|
4366
4456
|
function abortError(message = "operation aborted") {
|
|
@@ -4528,25 +4618,23 @@ function sanitizeIdentifier(name, kind = "sqlite") {
|
|
|
4528
4618
|
return `\`${name.replace(/`/g, "``")}\``;
|
|
4529
4619
|
return `"${name.replace(/"/g, '""')}"`;
|
|
4530
4620
|
}
|
|
4531
|
-
function escapeSqlString(value) {
|
|
4532
|
-
|
|
4621
|
+
function escapeSqlString(value, kind) {
|
|
4622
|
+
const escaped = kind === "mysql" ? value.replace(/\\/g, "\\\\").replace(/'/g, "''") : value.replace(/'/g, "''");
|
|
4623
|
+
return `'${escaped}'`;
|
|
4533
4624
|
}
|
|
4534
|
-
function buildFilterWhere(grouped, kind) {
|
|
4625
|
+
function buildFilterWhere(grouped, kind, exact) {
|
|
4535
4626
|
const whereParts = [];
|
|
4536
4627
|
const params = [];
|
|
4537
4628
|
const useParams = kind === "sqlite";
|
|
4629
|
+
const castOf = (column) => kind === "mysql" ? `CAST(${sanitizeIdentifier(column, kind)} AS CHAR)` : `CAST(${sanitizeIdentifier(column, kind)} AS TEXT)`;
|
|
4538
4630
|
for (const [value, cols] of grouped) {
|
|
4539
|
-
const likeVal = useParams ? "?" : escapeSqlString(`%${value}
|
|
4631
|
+
const likeVal = useParams ? "?" : escapeSqlString(`%${value}%`, kind);
|
|
4540
4632
|
if (cols.length === 1) {
|
|
4541
|
-
|
|
4542
|
-
whereParts.push(`${cast} LIKE ${likeVal}`);
|
|
4633
|
+
whereParts.push(`${castOf(cols[0])} LIKE ${likeVal}`);
|
|
4543
4634
|
if (useParams)
|
|
4544
4635
|
params.push(`%${value}%`);
|
|
4545
4636
|
} else {
|
|
4546
|
-
const orParts = cols.map((column) => {
|
|
4547
|
-
const cast = kind === "mysql" ? `CAST(${sanitizeIdentifier(column, kind)} AS CHAR)` : `CAST(${sanitizeIdentifier(column, kind)} AS TEXT)`;
|
|
4548
|
-
return `${cast} LIKE ${likeVal}`;
|
|
4549
|
-
});
|
|
4637
|
+
const orParts = cols.map((column) => `${castOf(column)} LIKE ${likeVal}`);
|
|
4550
4638
|
whereParts.push(`(${orParts.join(" OR ")})`);
|
|
4551
4639
|
if (useParams) {
|
|
4552
4640
|
for (let i = 0;i < cols.length; i++)
|
|
@@ -4554,6 +4642,12 @@ function buildFilterWhere(grouped, kind) {
|
|
|
4554
4642
|
}
|
|
4555
4643
|
}
|
|
4556
4644
|
}
|
|
4645
|
+
for (const cond of exact ?? []) {
|
|
4646
|
+
const rhs = useParams ? "?" : escapeSqlString(cond.value, kind);
|
|
4647
|
+
whereParts.push(`${castOf(cond.column)} = ${rhs}`);
|
|
4648
|
+
if (useParams)
|
|
4649
|
+
params.push(cond.value);
|
|
4650
|
+
}
|
|
4557
4651
|
return { where: whereParts.join(" AND "), params, useParams };
|
|
4558
4652
|
}
|
|
4559
4653
|
function filterGroupedColumns(grouped, columnNames) {
|
|
@@ -4566,6 +4660,12 @@ function filterGroupedColumns(grouped, columnNames) {
|
|
|
4566
4660
|
}
|
|
4567
4661
|
return filtered;
|
|
4568
4662
|
}
|
|
4663
|
+
function filterExactColumns(exact, columnNames) {
|
|
4664
|
+
if (!exact || exact.length === 0)
|
|
4665
|
+
return [];
|
|
4666
|
+
const validColumns = new Set(columnNames);
|
|
4667
|
+
return exact.filter((cond) => validColumns.has(cond.column));
|
|
4668
|
+
}
|
|
4569
4669
|
function filterOrderByColumns(orderBy, columnNames) {
|
|
4570
4670
|
if (!orderBy)
|
|
4571
4671
|
return;
|
|
@@ -4869,6 +4969,8 @@ function buildExecArgs(config, sql) {
|
|
|
4869
4969
|
"-A",
|
|
4870
4970
|
"-F",
|
|
4871
4971
|
"\t",
|
|
4972
|
+
"-R",
|
|
4973
|
+
PG_RECORD_SEPARATOR,
|
|
4872
4974
|
"-v",
|
|
4873
4975
|
"ON_ERROR_STOP=1",
|
|
4874
4976
|
"-c",
|
|
@@ -5044,11 +5146,14 @@ function splitTsvLine(line, decodeFields) {
|
|
|
5044
5146
|
const fields = line.split("\t");
|
|
5045
5147
|
return decodeFields ? fields.map(decodeMysqlBatchField) : fields;
|
|
5046
5148
|
}
|
|
5047
|
-
function
|
|
5048
|
-
|
|
5149
|
+
function stripFinalRecordSeparator(text, recordSeparator) {
|
|
5150
|
+
return text.endsWith(recordSeparator) ? text.slice(0, -recordSeparator.length) : text;
|
|
5151
|
+
}
|
|
5152
|
+
function parseTsvOutput(stdout, hasHeader, recordSeparator) {
|
|
5153
|
+
const text = recordSeparator ? stripFinalRecordSeparator(stdout, recordSeparator) : stripFinalLineBreak(stdout);
|
|
5049
5154
|
if (text.length === 0)
|
|
5050
5155
|
return { columns: [], rows: [] };
|
|
5051
|
-
const lines = text.split(/\r?\n/);
|
|
5156
|
+
const lines = recordSeparator ? text.split(recordSeparator) : text.split(/\r?\n/);
|
|
5052
5157
|
if (lines.length === 0)
|
|
5053
5158
|
return { columns: [], rows: [] };
|
|
5054
5159
|
if (hasHeader) {
|
|
@@ -5125,7 +5230,7 @@ function createDockerAdapter(config) {
|
|
|
5125
5230
|
if (result.code !== 0) {
|
|
5126
5231
|
throw new Error(result.stderr.trim() || "query failed");
|
|
5127
5232
|
}
|
|
5128
|
-
return parseTsvOutput(result.stdout, config.kind === "mysql");
|
|
5233
|
+
return parseTsvOutput(result.stdout, config.kind === "mysql", config.kind === "postgresql" ? PG_RECORD_SEPARATOR : undefined);
|
|
5129
5234
|
}
|
|
5130
5235
|
function toDbValue(val) {
|
|
5131
5236
|
if (val === "NULL" || val === "\\N")
|
|
@@ -5218,25 +5323,18 @@ function createDockerAdapter(config) {
|
|
|
5218
5323
|
},
|
|
5219
5324
|
async getIndexesAsync(signal) {
|
|
5220
5325
|
let sql;
|
|
5326
|
+
const INDEX_COL_SEP = "\x1F";
|
|
5221
5327
|
if (config.kind === "postgresql") {
|
|
5222
|
-
sql = `SELECT
|
|
5328
|
+
sql = `SELECT i.relname, t.relname, CASE WHEN ix.indisunique THEN '1' ELSE '0' END, COALESCE(string_agg(a.attname, E'\\x1f' ORDER BY k.ord), '') FROM pg_index ix JOIN pg_class i ON i.oid = ix.indexrelid JOIN pg_class t ON t.oid = ix.indrelid JOIN pg_namespace n ON n.oid = t.relnamespace LEFT JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, ord) ON true LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum WHERE n.nspname = ${postgresSchemaLiteral()} AND i.relname NOT LIKE 'pg_%' GROUP BY i.relname, t.relname, ix.indisunique ORDER BY t.relname, i.relname`;
|
|
5223
5329
|
} else {
|
|
5224
|
-
sql = `SELECT
|
|
5330
|
+
sql = `SELECT index_name, table_name, IF(MAX(non_unique) = 0, '1', '0'), GROUP_CONCAT(column_name ORDER BY seq_in_index SEPARATOR '\x1F') FROM information_schema.statistics WHERE table_schema = DATABASE() GROUP BY index_name, table_name ORDER BY table_name, index_name`;
|
|
5225
5331
|
}
|
|
5226
5332
|
const result = await execAsync(sql, signal);
|
|
5227
|
-
if (config.kind === "postgresql") {
|
|
5228
|
-
return result.rows.map((row) => ({
|
|
5229
|
-
name: row[0],
|
|
5230
|
-
table: row[1],
|
|
5231
|
-
columns: [],
|
|
5232
|
-
unique: false
|
|
5233
|
-
}));
|
|
5234
|
-
}
|
|
5235
5333
|
return result.rows.map((row) => ({
|
|
5236
5334
|
name: row[0],
|
|
5237
5335
|
table: row[1],
|
|
5238
|
-
|
|
5239
|
-
|
|
5336
|
+
unique: row[2] === "1",
|
|
5337
|
+
columns: row[3] ? row[3].split(INDEX_COL_SEP).filter((s) => s.length > 0) : []
|
|
5240
5338
|
}));
|
|
5241
5339
|
},
|
|
5242
5340
|
async getForeignKeysAsync(signal) {
|
|
@@ -5426,7 +5524,7 @@ function createDockerAdapter(config) {
|
|
|
5426
5524
|
}
|
|
5427
5525
|
const columnNames = columns.map((column) => column.name);
|
|
5428
5526
|
const order = buildOrderClause(filterOrderByColumns(options.orderBy, columnNames), config.kind);
|
|
5429
|
-
const where = buildFilterWhere(filterGroupedColumns(options.grouped, columnNames), config.kind).where;
|
|
5527
|
+
const where = buildFilterWhere(filterGroupedColumns(options.grouped, columnNames), config.kind, filterExactColumns(options.exact, columnNames)).where;
|
|
5430
5528
|
const whereClause = where ? ` WHERE ${where}` : "";
|
|
5431
5529
|
const countSql = `SELECT COUNT(*) AS cnt FROM ${id}${whereClause}`;
|
|
5432
5530
|
const countResultPromise = execAsync(countSql, signal);
|
|
@@ -5606,7 +5704,7 @@ async function listDockerDatabasesAsync(serviceName, kind, env, cwd, signal) {
|
|
|
5606
5704
|
return fallback;
|
|
5607
5705
|
return setDockerDatabasesCache(cacheKey, [], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
5608
5706
|
}
|
|
5609
|
-
const parsed = parseTsvOutput(result.stdout, kind === "mysql");
|
|
5707
|
+
const parsed = parseTsvOutput(result.stdout, kind === "mysql", kind === "postgresql" ? PG_RECORD_SEPARATOR : undefined);
|
|
5610
5708
|
const dbs = parsed.rows.map((r) => r[0]).filter(Boolean);
|
|
5611
5709
|
const value = dbs.length > 0 ? dbs : fallbackDockerDatabases(defaultDb);
|
|
5612
5710
|
return setDockerDatabasesCache(cacheKey, value, value.length > 0 ? DOCKER_DATABASES_POSITIVE_TTL_MS : DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
@@ -5647,7 +5745,7 @@ async function listDockerSchemasAsync(serviceName, kind, env, cwd, overrideDatab
|
|
|
5647
5745
|
if (result.code !== 0) {
|
|
5648
5746
|
return setDockerSchemasCache(cacheKey, ["public"], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
5649
5747
|
}
|
|
5650
|
-
const parsed = parseTsvOutput(result.stdout, false);
|
|
5748
|
+
const parsed = parseTsvOutput(result.stdout, false, PG_RECORD_SEPARATOR);
|
|
5651
5749
|
const schemas = parsed.rows.map((r) => r[0]).filter(Boolean);
|
|
5652
5750
|
const value = schemas.length > 0 ? schemas : ["public"];
|
|
5653
5751
|
return setDockerSchemasCache(cacheKey, value, DOCKER_DATABASES_POSITIVE_TTL_MS, now);
|
|
@@ -5671,7 +5769,7 @@ async function openDockerAdapterAsync(serviceName, kind, env, cwd, overrideDatab
|
|
|
5671
5769
|
...kind === "postgresql" && schema ? { schema } : {}
|
|
5672
5770
|
});
|
|
5673
5771
|
}
|
|
5674
|
-
var COLUMNS_TTL_MS = 30000, ROWCOUNT_TTL_MS = 15000, DOCKER_DATABASES_POSITIVE_TTL_MS = 15000, DOCKER_DATABASES_NEGATIVE_TTL_MS = 3000, dockerDatabasesCache, dockerSchemasCache, spawnSyncImpl2, MYSQL_SPATIAL_TYPES;
|
|
5772
|
+
var COLUMNS_TTL_MS = 30000, ROWCOUNT_TTL_MS = 15000, DOCKER_DATABASES_POSITIVE_TTL_MS = 15000, DOCKER_DATABASES_NEGATIVE_TTL_MS = 3000, dockerDatabasesCache, dockerSchemasCache, spawnSyncImpl2, PG_RECORD_SEPARATOR = "\x1E", MYSQL_SPATIAL_TYPES;
|
|
5675
5773
|
var init_docker = __esm(() => {
|
|
5676
5774
|
init_sql_snapshot();
|
|
5677
5775
|
init_docker_utils();
|
|
@@ -5870,7 +5968,7 @@ function createSqliteAdapter(db) {
|
|
|
5870
5968
|
async getFilteredTablePageWithMeta(table, options) {
|
|
5871
5969
|
const columns = queryColumns(db, table);
|
|
5872
5970
|
const columnNames = columns.map((column) => column.name);
|
|
5873
|
-
const filter = buildFilterWhere(filterGroupedColumns(options.grouped, columnNames), "sqlite");
|
|
5971
|
+
const filter = buildFilterWhere(filterGroupedColumns(options.grouped, columnNames), "sqlite", filterExactColumns(options.exact, columnNames));
|
|
5874
5972
|
const order = buildOrderClause2(filterOrderByColumns(options.orderBy, columnNames));
|
|
5875
5973
|
const tableId = sanitizeIdentifier(table);
|
|
5876
5974
|
const whereClause = filter.where ? ` WHERE ${filter.where}` : "";
|
|
@@ -5881,7 +5979,7 @@ function createSqliteAdapter(db) {
|
|
|
5881
5979
|
columns,
|
|
5882
5980
|
rows: result.rows,
|
|
5883
5981
|
rowCount: result.rowCount,
|
|
5884
|
-
totalRows: countRow?.cnt ?? 0
|
|
5982
|
+
totalRows: Number(countRow?.cnt ?? 0)
|
|
5885
5983
|
};
|
|
5886
5984
|
},
|
|
5887
5985
|
executeReadonlyQuery(sql, params, maxRows = 1000) {
|
|
@@ -8570,9 +8668,16 @@ function parseBuckets(xml) {
|
|
|
8570
8668
|
};
|
|
8571
8669
|
}).filter((bucket) => bucket.name).sort((a, b) => a.name.localeCompare(b.name));
|
|
8572
8670
|
}
|
|
8671
|
+
function decodeS3UrlEncoded(value) {
|
|
8672
|
+
try {
|
|
8673
|
+
return decodeURIComponent(value);
|
|
8674
|
+
} catch {
|
|
8675
|
+
return value;
|
|
8676
|
+
}
|
|
8677
|
+
}
|
|
8573
8678
|
function parseObjects(xml) {
|
|
8574
8679
|
const objects = xmlBlocks(xml, "Contents").map((block) => {
|
|
8575
|
-
const key = xmlText(block, "Key") || "";
|
|
8680
|
+
const key = decodeS3UrlEncoded(xmlText(block, "Key") || "");
|
|
8576
8681
|
const updatedAt = toIsoDate(xmlText(block, "LastModified"));
|
|
8577
8682
|
return {
|
|
8578
8683
|
key,
|
|
@@ -8582,8 +8687,10 @@ function parseObjects(xml) {
|
|
|
8582
8687
|
...xmlText(block, "StorageClass") ? { storageClass: xmlText(block, "StorageClass") } : {}
|
|
8583
8688
|
};
|
|
8584
8689
|
}).filter((object) => object.key);
|
|
8690
|
+
const commonPrefixes = xmlBlocks(xml, "CommonPrefixes").map((block) => decodeS3UrlEncoded(xmlText(block, "Prefix") || "")).filter(Boolean);
|
|
8585
8691
|
return {
|
|
8586
8692
|
objects,
|
|
8693
|
+
commonPrefixes,
|
|
8587
8694
|
nextToken: xmlText(xml, "NextContinuationToken"),
|
|
8588
8695
|
truncated: xmlText(xml, "IsTruncated") === "true"
|
|
8589
8696
|
};
|
|
@@ -8665,8 +8772,10 @@ function createS3Adapter(config) {
|
|
|
8665
8772
|
bucket: opts.bucket,
|
|
8666
8773
|
query: {
|
|
8667
8774
|
"list-type": "2",
|
|
8775
|
+
"encoding-type": "url",
|
|
8668
8776
|
"max-keys": String(Math.min(1000, Math.max(1, opts.maxKeys ?? 200))),
|
|
8669
8777
|
...opts.prefix ? { prefix: opts.prefix } : {},
|
|
8778
|
+
...opts.delimiter ? { delimiter: opts.delimiter } : {},
|
|
8670
8779
|
...opts.continuationToken ? { "continuation-token": opts.continuationToken } : {}
|
|
8671
8780
|
},
|
|
8672
8781
|
signal: opts.signal
|
|
@@ -9024,6 +9133,42 @@ ${s3ObjectName(object.key)}`.toLowerCase();
|
|
|
9024
9133
|
return s3ErrorResponse(err, "list s3 objects");
|
|
9025
9134
|
}
|
|
9026
9135
|
}
|
|
9136
|
+
async function handleFolder(cwd, req, url, omitDirNames) {
|
|
9137
|
+
const r = await resolveS3(cwd, url.searchParams.get("db"), req.signal, omitDirNames);
|
|
9138
|
+
if (r instanceof Response)
|
|
9139
|
+
return r;
|
|
9140
|
+
const bucket = validateBucket(url.searchParams.get("bucket"));
|
|
9141
|
+
if (bucket instanceof Response)
|
|
9142
|
+
return bucket;
|
|
9143
|
+
const prefix = validateOptionalText(url.searchParams.get("prefix"), "prefix", 2048);
|
|
9144
|
+
if (prefix instanceof Response)
|
|
9145
|
+
return prefix;
|
|
9146
|
+
const token = validateOptionalText(url.searchParams.get("token"), "token", 4096);
|
|
9147
|
+
if (token instanceof Response)
|
|
9148
|
+
return token;
|
|
9149
|
+
try {
|
|
9150
|
+
const page = await r.explorer.listObjects({
|
|
9151
|
+
bucket,
|
|
9152
|
+
prefix,
|
|
9153
|
+
delimiter: "/",
|
|
9154
|
+
continuationToken: token || undefined,
|
|
9155
|
+
maxKeys: MAX_OBJECT_LIMIT,
|
|
9156
|
+
signal: req.signal
|
|
9157
|
+
});
|
|
9158
|
+
const objects = page.objects.filter((object) => object.key !== prefix);
|
|
9159
|
+
const body = {
|
|
9160
|
+
dbId: r.dbId,
|
|
9161
|
+
bucket,
|
|
9162
|
+
prefix,
|
|
9163
|
+
folders: page.commonPrefixes ?? [],
|
|
9164
|
+
objects,
|
|
9165
|
+
...page.nextToken ? { nextToken: page.nextToken } : {}
|
|
9166
|
+
};
|
|
9167
|
+
return json(body);
|
|
9168
|
+
} catch (err) {
|
|
9169
|
+
return s3ErrorResponse(err, "list s3 folder");
|
|
9170
|
+
}
|
|
9171
|
+
}
|
|
9027
9172
|
async function handleHead(cwd, req, url, omitDirNames) {
|
|
9028
9173
|
const r = await resolveS3(cwd, url.searchParams.get("db"), req.signal, omitDirNames);
|
|
9029
9174
|
if (r instanceof Response)
|
|
@@ -9109,6 +9254,10 @@ async function handleS3Route(req, url, cwd, sideEffectAllowed, omitDirNames) {
|
|
|
9109
9254
|
methods: ["GET"],
|
|
9110
9255
|
handler: () => handleObjects(cwd, req, url, omitDirNames)
|
|
9111
9256
|
},
|
|
9257
|
+
"/_db/s3/folder": {
|
|
9258
|
+
methods: ["GET"],
|
|
9259
|
+
handler: () => handleFolder(cwd, req, url, omitDirNames)
|
|
9260
|
+
},
|
|
9112
9261
|
"/_db/s3/head": {
|
|
9113
9262
|
methods: ["GET"],
|
|
9114
9263
|
handler: () => handleHead(cwd, req, url, omitDirNames)
|
|
@@ -9803,6 +9952,9 @@ function sanitize(input) {
|
|
|
9803
9952
|
const sidebarWidth = sanitizeCssSize(tab.sidebarWidth);
|
|
9804
9953
|
if (sidebarWidth !== undefined)
|
|
9805
9954
|
out.sidebarWidth = sidebarWidth;
|
|
9955
|
+
const relatedPanelHeight = sanitizeCssSize(tab.relatedPanelHeight);
|
|
9956
|
+
if (relatedPanelHeight !== undefined)
|
|
9957
|
+
out.relatedPanelHeight = relatedPanelHeight;
|
|
9806
9958
|
const redis = sanitizeRedis(tab.redis);
|
|
9807
9959
|
if (redis !== undefined)
|
|
9808
9960
|
out.redis = redis;
|
|
@@ -10043,19 +10195,25 @@ async function handleSchema(cwd, url, omitDirNames, signal) {
|
|
|
10043
10195
|
linkedAbort.cleanup();
|
|
10044
10196
|
}
|
|
10045
10197
|
}
|
|
10046
|
-
function
|
|
10047
|
-
const raw = url.searchParams.get(
|
|
10198
|
+
function parseColumnValuePairs(url, param) {
|
|
10199
|
+
const raw = url.searchParams.get(param);
|
|
10048
10200
|
if (!raw)
|
|
10049
10201
|
return [];
|
|
10050
10202
|
try {
|
|
10051
10203
|
const parsed = JSON.parse(raw);
|
|
10052
10204
|
if (!Array.isArray(parsed))
|
|
10053
10205
|
return [];
|
|
10054
|
-
return parsed.filter((f) => !!f && typeof f === "object" && typeof f.column === "string" && typeof f.value === "string");
|
|
10206
|
+
return parsed.filter((f) => !!f && typeof f === "object" && typeof f.column === "string" && typeof f.value === "string").filter((f) => f.column.length <= MAX_FILTER_COLUMN_LEN && f.value.length <= MAX_FILTER_VALUE_LEN).slice(0, MAX_COLUMN_VALUE_PAIRS);
|
|
10055
10207
|
} catch {
|
|
10056
10208
|
return [];
|
|
10057
10209
|
}
|
|
10058
10210
|
}
|
|
10211
|
+
function parseFilters(url) {
|
|
10212
|
+
return parseColumnValuePairs(url, "filters");
|
|
10213
|
+
}
|
|
10214
|
+
function parseExactConditions(url) {
|
|
10215
|
+
return parseColumnValuePairs(url, "eq");
|
|
10216
|
+
}
|
|
10059
10217
|
function groupFiltersByValue(filters) {
|
|
10060
10218
|
const grouped = new Map;
|
|
10061
10219
|
for (const filter of filters) {
|
|
@@ -10086,14 +10244,16 @@ async function handleTable(cwd, url, omitDirNames, signal) {
|
|
|
10086
10244
|
];
|
|
10087
10245
|
}
|
|
10088
10246
|
const filters = parseFilters(url);
|
|
10247
|
+
const exact = parseExactConditions(url);
|
|
10089
10248
|
try {
|
|
10090
10249
|
const adapter = await getAdapter(r, cwd, signal);
|
|
10091
|
-
if (filters.length > 0) {
|
|
10250
|
+
if (filters.length > 0 || exact.length > 0) {
|
|
10092
10251
|
const meta2 = await adapter.getFilteredTablePageWithMeta(table, {
|
|
10093
10252
|
offset,
|
|
10094
10253
|
limit,
|
|
10095
10254
|
orderBy,
|
|
10096
|
-
grouped: groupFiltersByValue(filters)
|
|
10255
|
+
grouped: groupFiltersByValue(filters),
|
|
10256
|
+
...exact.length > 0 ? { exact } : {}
|
|
10097
10257
|
}, signal);
|
|
10098
10258
|
const colNames2 = new Set(meta2.columns.map((c) => c.name));
|
|
10099
10259
|
if (sortCol && !colNames2.has(sortCol)) {
|
|
@@ -10367,6 +10527,7 @@ async function handleExport(cwd, url, omitDirNames, signal) {
|
|
|
10367
10527
|
];
|
|
10368
10528
|
}
|
|
10369
10529
|
const filters = parseFilters(url);
|
|
10530
|
+
const exact = parseExactConditions(url);
|
|
10370
10531
|
try {
|
|
10371
10532
|
const adapter = await getAdapter(r, cwd, signal);
|
|
10372
10533
|
const db = asAsync(adapter);
|
|
@@ -10377,12 +10538,13 @@ async function handleExport(cwd, url, omitDirNames, signal) {
|
|
|
10377
10538
|
return textError(`invalid sort column: ${sortCol}`, 400);
|
|
10378
10539
|
}
|
|
10379
10540
|
let rawRows;
|
|
10380
|
-
if (filters.length > 0) {
|
|
10541
|
+
if (filters.length > 0 || exact.length > 0) {
|
|
10381
10542
|
const meta = await adapter.getFilteredTablePageWithMeta(table, {
|
|
10382
10543
|
offset: 0,
|
|
10383
10544
|
limit: EXPORT_MAX_ROWS,
|
|
10384
10545
|
orderBy,
|
|
10385
|
-
grouped: groupFiltersByValue(filters)
|
|
10546
|
+
grouped: groupFiltersByValue(filters),
|
|
10547
|
+
...exact.length > 0 ? { exact } : {}
|
|
10386
10548
|
}, signal);
|
|
10387
10549
|
rawRows = meta.rows;
|
|
10388
10550
|
} else {
|
|
@@ -11019,7 +11181,7 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
11019
11181
|
}
|
|
11020
11182
|
}, sideEffectAllowed, wrapResponse, (err) => handleError("database", "handle database request", err));
|
|
11021
11183
|
}
|
|
11022
|
-
var initialized = false, dockerAdapterCache, MAX_SCHEMA_NAME_LEN2 = 1024, EXPORT_MAX_ROWS = 1e5, MAX_TABS_BODY_BYTES = 1e6, MAX_DB_UI_BODY_BYTES = 1e6, MAX_SNAPSHOT_TABLES = 512, MAX_SNAPSHOT_TABLE_NAME_LEN = 1024, searchJobs, snapshotJobs, DOCKER_CLOSE_REGISTRY, SNAPSHOT_DOCKER_SOURCE_REGISTRY;
|
|
11184
|
+
var initialized = false, dockerAdapterCache, MAX_SCHEMA_NAME_LEN2 = 1024, MAX_COLUMN_VALUE_PAIRS = 64, MAX_FILTER_COLUMN_LEN = 128, MAX_FILTER_VALUE_LEN = 4096, EXPORT_MAX_ROWS = 1e5, MAX_TABS_BODY_BYTES = 1e6, MAX_DB_UI_BODY_BYTES = 1e6, MAX_SNAPSHOT_TABLES = 512, MAX_SNAPSHOT_TABLE_NAME_LEN = 1024, searchJobs, snapshotJobs, DOCKER_CLOSE_REGISTRY, SNAPSHOT_DOCKER_SOURCE_REGISTRY;
|
|
11023
11185
|
var init_handle = __esm(() => {
|
|
11024
11186
|
init_state_store();
|
|
11025
11187
|
init_docker();
|
|
@@ -11086,12 +11248,20 @@ async function parseJsonBody(req) {
|
|
|
11086
11248
|
async function handleSettingsGet(cwd) {
|
|
11087
11249
|
return jsonLoadResponse(() => loadAppSettingsState(cwd), "state", "failed to load settings state");
|
|
11088
11250
|
}
|
|
11089
|
-
async function handleSettingsPatch(cwd, req) {
|
|
11251
|
+
async function handleSettingsPatch(cwd, req, onChange) {
|
|
11090
11252
|
const body = await parseJsonBody(req);
|
|
11091
11253
|
if (body instanceof Response)
|
|
11092
11254
|
return body;
|
|
11093
11255
|
try {
|
|
11094
|
-
|
|
11256
|
+
const next = await patchAppSettingsState(cwd, body);
|
|
11257
|
+
if (onChange) {
|
|
11258
|
+
try {
|
|
11259
|
+
onChange(next);
|
|
11260
|
+
} catch (notifyErr) {
|
|
11261
|
+
console.warn("[code-viewer] settings change notify failed:", notifyErr);
|
|
11262
|
+
}
|
|
11263
|
+
}
|
|
11264
|
+
return json(next);
|
|
11095
11265
|
} catch (err) {
|
|
11096
11266
|
const message = err instanceof Error ? err.message : String(err);
|
|
11097
11267
|
if (message === "settings state too large")
|
|
@@ -11117,12 +11287,12 @@ async function handleViewPatch(cwd, req) {
|
|
|
11117
11287
|
return textError("failed to save view state", 500);
|
|
11118
11288
|
}
|
|
11119
11289
|
}
|
|
11120
|
-
async function handleStateRoute(req, url, cwd, sideEffectAllowed) {
|
|
11290
|
+
async function handleStateRoute(req, url, cwd, sideEffectAllowed, options = {}) {
|
|
11121
11291
|
return dispatchRoutes(req, url, {
|
|
11122
11292
|
"/_state/settings": {
|
|
11123
11293
|
methods: ["GET", "PATCH"],
|
|
11124
11294
|
sideEffect: (method) => method !== "GET",
|
|
11125
|
-
handler: () => req.method === "GET" ? handleSettingsGet(cwd) : handleSettingsPatch(cwd, req)
|
|
11295
|
+
handler: () => req.method === "GET" ? handleSettingsGet(cwd) : handleSettingsPatch(cwd, req, options.onSettingsChange)
|
|
11126
11296
|
},
|
|
11127
11297
|
"/_state/view": {
|
|
11128
11298
|
methods: ["GET", "PATCH"],
|
|
@@ -11218,16 +11388,53 @@ Examples:
|
|
|
11218
11388
|
}
|
|
11219
11389
|
if (rest.length)
|
|
11220
11390
|
cliArgs = rest;
|
|
11221
|
-
|
|
11222
|
-
const configScopeExcludeNames = loadProjectConfigScopeExcludeNames();
|
|
11223
|
-
uploadDisabledByConfig = loadProjectConfigUploadDisabled();
|
|
11391
|
+
warnIfLegacyConfigPresent();
|
|
11224
11392
|
if (scopeOmitDirCliOverride) {
|
|
11225
11393
|
scopeOmitDirNames = scopeOmitDirCliOverride;
|
|
11226
|
-
} else if (configScopeOmitDirs) {
|
|
11227
|
-
scopeOmitDirNames = configScopeOmitDirs;
|
|
11228
11394
|
}
|
|
11229
|
-
|
|
11230
|
-
|
|
11395
|
+
scopeWatchLimit = worktreeWatchDirectoryLimitFromEnv();
|
|
11396
|
+
}
|
|
11397
|
+
function warnIfLegacyConfigPresent() {
|
|
11398
|
+
try {
|
|
11399
|
+
if (existsSync6(join13(cwd, ".code-viewer.json"))) {
|
|
11400
|
+
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.");
|
|
11401
|
+
}
|
|
11402
|
+
} catch {}
|
|
11403
|
+
}
|
|
11404
|
+
function applyPersistedSettings(state) {
|
|
11405
|
+
const prevOmit = scopeOmitDirNames;
|
|
11406
|
+
const prevExclude = scopeExcludeNames;
|
|
11407
|
+
const prevWatchLimit = scopeWatchLimit;
|
|
11408
|
+
if (!scopeOmitDirCliOverride && Array.isArray(state.scopeOmitDirs) && state.scopeOmitDirs.length > 0) {
|
|
11409
|
+
scopeOmitDirNames = state.scopeOmitDirs;
|
|
11410
|
+
} else if (!scopeOmitDirCliOverride) {
|
|
11411
|
+
scopeOmitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES;
|
|
11412
|
+
}
|
|
11413
|
+
if (Array.isArray(state.scopeExcludeNames) && state.scopeExcludeNames.length > 0) {
|
|
11414
|
+
scopeExcludeNames = state.scopeExcludeNames;
|
|
11415
|
+
} else {
|
|
11416
|
+
scopeExcludeNames = DEFAULT_EXCLUDE_NAMES;
|
|
11417
|
+
}
|
|
11418
|
+
if (state.scopeWatchLimit != null) {
|
|
11419
|
+
scopeWatchLimit = normalizeScopeWatchLimit(state.scopeWatchLimit);
|
|
11420
|
+
}
|
|
11421
|
+
uploadEnabled = state.uploadEnabled !== false;
|
|
11422
|
+
if (prevOmit !== scopeOmitDirNames || prevExclude !== scopeExcludeNames || prevWatchLimit !== scopeWatchLimit) {
|
|
11423
|
+
restartWorktreeWatch();
|
|
11424
|
+
}
|
|
11425
|
+
}
|
|
11426
|
+
function normalizeScopeWatchLimit(value) {
|
|
11427
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
11428
|
+
return DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11429
|
+
}
|
|
11430
|
+
const floored = Math.floor(value);
|
|
11431
|
+
if (floored < MIN_WORKTREE_WATCH_DIRECTORY_LIMIT) {
|
|
11432
|
+
return MIN_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11433
|
+
}
|
|
11434
|
+
if (floored > MAX_WORKTREE_WATCH_DIRECTORY_LIMIT) {
|
|
11435
|
+
return MAX_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11436
|
+
}
|
|
11437
|
+
return floored;
|
|
11231
11438
|
}
|
|
11232
11439
|
function json2(data, init = {}) {
|
|
11233
11440
|
return new Response(JSON.stringify(data), {
|
|
@@ -11529,45 +11736,6 @@ function parseScopeExcludeNamesQuery(value) {
|
|
|
11529
11736
|
}
|
|
11530
11737
|
return normalizeScopeExcludeNames(names);
|
|
11531
11738
|
}
|
|
11532
|
-
function loadProjectConfig() {
|
|
11533
|
-
const full = join13(cwd, ".code-viewer.json");
|
|
11534
|
-
if (!existsSync6(full))
|
|
11535
|
-
return null;
|
|
11536
|
-
let realCwd;
|
|
11537
|
-
let realConfig;
|
|
11538
|
-
try {
|
|
11539
|
-
realCwd = realpathSync4(cwd);
|
|
11540
|
-
realConfig = realpathSync4(full);
|
|
11541
|
-
} catch {
|
|
11542
|
-
return null;
|
|
11543
|
-
}
|
|
11544
|
-
if (dirname3(realConfig) !== realCwd || basename3(realConfig) !== ".code-viewer.json")
|
|
11545
|
-
return null;
|
|
11546
|
-
try {
|
|
11547
|
-
const parsed = JSON.parse(readFileSync4(realConfig, "utf8"));
|
|
11548
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "version" in parsed && parsed.version !== 1)
|
|
11549
|
-
return null;
|
|
11550
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
11551
|
-
} catch {
|
|
11552
|
-
return null;
|
|
11553
|
-
}
|
|
11554
|
-
}
|
|
11555
|
-
function loadProjectConfigUploadDisabled() {
|
|
11556
|
-
const config = loadProjectConfig();
|
|
11557
|
-
return config?.upload?.enabled === false;
|
|
11558
|
-
}
|
|
11559
|
-
function loadProjectConfigScopeOmitDirs() {
|
|
11560
|
-
const config = loadProjectConfig();
|
|
11561
|
-
if (!config?.scope || !Array.isArray(config.scope.omitDirs))
|
|
11562
|
-
return null;
|
|
11563
|
-
return normalizeScopeOmitDirNames(config.scope.omitDirs);
|
|
11564
|
-
}
|
|
11565
|
-
function loadProjectConfigScopeExcludeNames() {
|
|
11566
|
-
const config = loadProjectConfig();
|
|
11567
|
-
if (!config?.scope || !Array.isArray(config.scope.excludeNames))
|
|
11568
|
-
return null;
|
|
11569
|
-
return normalizeScopeExcludeNames(config.scope.excludeNames);
|
|
11570
|
-
}
|
|
11571
11739
|
function scopeOmitDirNamesFromQuery(url) {
|
|
11572
11740
|
if (!url.searchParams.has("omit_dirs"))
|
|
11573
11741
|
return scopeOmitDirNames;
|
|
@@ -11735,7 +11903,7 @@ function handleTree(url) {
|
|
|
11735
11903
|
branch: currentBranch(cwd) || undefined,
|
|
11736
11904
|
entries: recursive ? entries : entries.map((entry) => attachTreeEntryMetadata(target, entry)),
|
|
11737
11905
|
readme: readReadme(target, path),
|
|
11738
|
-
upload_enabled:
|
|
11906
|
+
upload_enabled: uploadEnabled && (target === "worktree" || target === "")
|
|
11739
11907
|
});
|
|
11740
11908
|
}
|
|
11741
11909
|
function handleSettings() {
|
|
@@ -11748,7 +11916,11 @@ function handleSettings() {
|
|
|
11748
11916
|
omit_dirs_built_in: DEFAULT_WORKTREE_OMIT_DIR_NAMES,
|
|
11749
11917
|
exclude_names_effective: scopeExcludeNames,
|
|
11750
11918
|
exclude_names_built_in: DEFAULT_EXCLUDE_NAMES,
|
|
11751
|
-
max_entries: WORKTREE_RECURSIVE_ENTRY_LIMIT
|
|
11919
|
+
max_entries: WORKTREE_RECURSIVE_ENTRY_LIMIT,
|
|
11920
|
+
watch_limit_effective: scopeWatchLimit,
|
|
11921
|
+
watch_limit_default: DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT,
|
|
11922
|
+
watch_limit_min: MIN_WORKTREE_WATCH_DIRECTORY_LIMIT,
|
|
11923
|
+
watch_limit_max: MAX_WORKTREE_WATCH_DIRECTORY_LIMIT
|
|
11752
11924
|
}
|
|
11753
11925
|
});
|
|
11754
11926
|
}
|
|
@@ -11757,7 +11929,14 @@ function worktreeWatchDirectoryLimitFromEnv() {
|
|
|
11757
11929
|
if (!raw)
|
|
11758
11930
|
return DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11759
11931
|
const parsed = Number(raw);
|
|
11760
|
-
|
|
11932
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
11933
|
+
return DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11934
|
+
const floored = Math.floor(parsed);
|
|
11935
|
+
if (floored < MIN_WORKTREE_WATCH_DIRECTORY_LIMIT)
|
|
11936
|
+
return MIN_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11937
|
+
if (floored > MAX_WORKTREE_WATCH_DIRECTORY_LIMIT)
|
|
11938
|
+
return MAX_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
11939
|
+
return floored;
|
|
11761
11940
|
}
|
|
11762
11941
|
function handleFiles2(url) {
|
|
11763
11942
|
const target = url.searchParams.get("ref") || url.searchParams.get("target") || "worktree";
|
|
@@ -12297,8 +12476,8 @@ function uploadOpenFlags() {
|
|
|
12297
12476
|
return constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW || 0);
|
|
12298
12477
|
}
|
|
12299
12478
|
async function handleUploadFiles(req) {
|
|
12300
|
-
if (
|
|
12301
|
-
return text("upload disabled by
|
|
12479
|
+
if (!uploadEnabled)
|
|
12480
|
+
return text("upload disabled by viewer settings", 403);
|
|
12302
12481
|
if (req.method !== "POST")
|
|
12303
12482
|
return text("method not allowed", 405);
|
|
12304
12483
|
if (!sideEffectRequestAllowed(req))
|
|
@@ -12851,9 +13030,6 @@ async function handleAnnotations(req) {
|
|
|
12851
13030
|
}
|
|
12852
13031
|
return text("invalid action", 400);
|
|
12853
13032
|
}
|
|
12854
|
-
function isCodeViewerInternalPath(path) {
|
|
12855
|
-
return path.split(/[\\/]+/).some((part) => part.toLowerCase() === ".code-viewer");
|
|
12856
|
-
}
|
|
12857
13033
|
function sendSse(event, data = "tick") {
|
|
12858
13034
|
const payload = enc.encode(`event: ${event}
|
|
12859
13035
|
data: ${data}
|
|
@@ -12901,7 +13077,43 @@ async function shutdown(exitCode = 0) {
|
|
|
12901
13077
|
}
|
|
12902
13078
|
process.exit(exitCode);
|
|
12903
13079
|
}
|
|
12904
|
-
|
|
13080
|
+
function startScopedWorktreeWatch() {
|
|
13081
|
+
watchLimitReached = null;
|
|
13082
|
+
return startWorktreeUpdateWatch({
|
|
13083
|
+
root: cwd,
|
|
13084
|
+
omitDirNames: scopeOmitDirNames,
|
|
13085
|
+
excludeNames: scopeExcludeNames,
|
|
13086
|
+
watch,
|
|
13087
|
+
initialScanMode: "async",
|
|
13088
|
+
maxWatchedDirectories: scopeWatchLimit,
|
|
13089
|
+
onUpdate: triggerUpdate,
|
|
13090
|
+
onWatchLimit: (limit) => {
|
|
13091
|
+
watchLimitReached = limit;
|
|
13092
|
+
sendSse("watch-limit", String(limit));
|
|
13093
|
+
},
|
|
13094
|
+
onError: (error) => {
|
|
13095
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13096
|
+
console.warn(`code-viewer worktree watch skipped: ${message}`);
|
|
13097
|
+
}
|
|
13098
|
+
});
|
|
13099
|
+
}
|
|
13100
|
+
function restartWorktreeWatch() {
|
|
13101
|
+
try {
|
|
13102
|
+
if (shuttingDown)
|
|
13103
|
+
return;
|
|
13104
|
+
if (!worktreeWatch)
|
|
13105
|
+
return;
|
|
13106
|
+
} catch {
|
|
13107
|
+
return;
|
|
13108
|
+
}
|
|
13109
|
+
try {
|
|
13110
|
+
worktreeWatch.close();
|
|
13111
|
+
} catch (error) {
|
|
13112
|
+
console.warn(`code-viewer worktree watch restart close skipped: ${String(error)}`);
|
|
13113
|
+
}
|
|
13114
|
+
worktreeWatch = startScopedWorktreeWatch();
|
|
13115
|
+
}
|
|
13116
|
+
var WEB_ROOT, VERSION, DEFAULT_ARGS, PREVIEW_HUNKS_DEFAULT = 3, PREVIEW_LINES_DEFAULT = 1200, WATCHED_ASSET_FILES, SIZE_SMALL = 2000, SIZE_MEDIUM = 8000, SIZE_LARGE = 20000, LINE_INDEX_MIN_START = 1e4, LINE_INDEX_MAX_FILE_BYTES, BLOB_LINE_CACHE_MAX_BYTES, MAX_UPLOAD_FILE_BYTES, MAX_UPLOAD_TOTAL_BYTES, MAX_UPLOAD_BODY_BYTES, MAX_UPLOAD_FILES = 50, SAFE_UPLOAD_EXTENSIONS, generation = 1, cwd, cliArgs, listenPort = 0, openAfterStart = false, scopeOmitDirNames, scopeOmitDirCliOverride = null, scopeExcludeNames, scopeWatchLimit, uploadEnabled = true, rgAvailableCache = null, enc, sseClients, sseKeepalives, fileCache, metaCache, fileListCache, lineIndexCache, blobLineIndexCache, blobBytesCache, blobLineCacheBytes = 0, isCodeViewerInternalPath, watchLimitReached = null, server, worktreeWatch = null, shuttingDown = false;
|
|
12905
13117
|
var init_preview = __esm(async () => {
|
|
12906
13118
|
init_routes();
|
|
12907
13119
|
init_annotations();
|
|
@@ -12913,6 +13125,7 @@ var init_preview = __esm(async () => {
|
|
|
12913
13125
|
init_runtime();
|
|
12914
13126
|
init_search();
|
|
12915
13127
|
init_server_registry();
|
|
13128
|
+
init_state_store();
|
|
12916
13129
|
init_worktree_watcher();
|
|
12917
13130
|
WEB_ROOT = join13(ROOT, "web");
|
|
12918
13131
|
VERSION = JSON.parse(readFileSync4(join13(ROOT, "package.json"), "utf8")).version;
|
|
@@ -12963,6 +13176,7 @@ var init_preview = __esm(async () => {
|
|
|
12963
13176
|
cliArgs = DEFAULT_ARGS;
|
|
12964
13177
|
scopeOmitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES;
|
|
12965
13178
|
scopeExcludeNames = DEFAULT_EXCLUDE_NAMES;
|
|
13179
|
+
scopeWatchLimit = DEFAULT_WORKTREE_WATCH_DIRECTORY_LIMIT;
|
|
12966
13180
|
enc = new TextEncoder;
|
|
12967
13181
|
sseClients = new Set;
|
|
12968
13182
|
sseKeepalives = new Map;
|
|
@@ -12972,7 +13186,9 @@ var init_preview = __esm(async () => {
|
|
|
12972
13186
|
lineIndexCache = new Map;
|
|
12973
13187
|
blobLineIndexCache = new Map;
|
|
12974
13188
|
blobBytesCache = new Map;
|
|
13189
|
+
isCodeViewerInternalPath = isToolInternalPath;
|
|
12975
13190
|
parseCli();
|
|
13191
|
+
applyPersistedSettings(await loadAppSettingsState(cwd));
|
|
12976
13192
|
server = await startServer({
|
|
12977
13193
|
hostname: "127.0.0.1",
|
|
12978
13194
|
port: listenPort,
|
|
@@ -13021,7 +13237,7 @@ var init_preview = __esm(async () => {
|
|
|
13021
13237
|
}
|
|
13022
13238
|
if (url.pathname.startsWith("/_state/")) {
|
|
13023
13239
|
const { handleStateRoute: handleStateRoute2 } = await Promise.resolve().then(() => (init_state_route(), exports_state_route));
|
|
13024
|
-
const stateResponse = await handleStateRoute2(req, url, cwd, sideEffectRequestAllowed);
|
|
13240
|
+
const stateResponse = await handleStateRoute2(req, url, cwd, sideEffectRequestAllowed, { onSettingsChange: applyPersistedSettings });
|
|
13025
13241
|
if (stateResponse)
|
|
13026
13242
|
return stateResponse;
|
|
13027
13243
|
}
|
|
@@ -13046,6 +13262,12 @@ var init_preview = __esm(async () => {
|
|
|
13046
13262
|
data: ok
|
|
13047
13263
|
|
|
13048
13264
|
`));
|
|
13265
|
+
if (watchLimitReached !== null) {
|
|
13266
|
+
controller.enqueue(enc.encode(`event: watch-limit
|
|
13267
|
+
data: ${watchLimitReached}
|
|
13268
|
+
|
|
13269
|
+
`));
|
|
13270
|
+
}
|
|
13049
13271
|
keepalive = setInterval(() => {
|
|
13050
13272
|
try {
|
|
13051
13273
|
controller.enqueue(enc.encode(`: ping
|
|
@@ -13107,19 +13329,7 @@ data: ok
|
|
|
13107
13329
|
watch,
|
|
13108
13330
|
sendReload: () => sendSse("reload")
|
|
13109
13331
|
});
|
|
13110
|
-
worktreeWatch =
|
|
13111
|
-
root: cwd,
|
|
13112
|
-
omitDirNames: scopeOmitDirNames,
|
|
13113
|
-
excludeNames: scopeExcludeNames,
|
|
13114
|
-
watch,
|
|
13115
|
-
initialScanMode: "async",
|
|
13116
|
-
maxWatchedDirectories: worktreeWatchDirectoryLimitFromEnv(),
|
|
13117
|
-
onUpdate: triggerUpdate,
|
|
13118
|
-
onError: (error) => {
|
|
13119
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
13120
|
-
console.warn(`code-viewer worktree watch skipped: ${message}`);
|
|
13121
|
-
}
|
|
13122
|
-
});
|
|
13332
|
+
worktreeWatch = startScopedWorktreeWatch();
|
|
13123
13333
|
console.log(`GDP_LISTEN_URL=http://127.0.0.1:${server.port}/`);
|
|
13124
13334
|
console.log(`git-diff-preview serving ${cwd}`);
|
|
13125
13335
|
});
|