@youtyan/code-viewer 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/code-viewer.js +819 -251
- package/package.json +1 -1
- package/web/app.js +460 -225
package/dist/code-viewer.js
CHANGED
|
@@ -17,14 +17,97 @@ var __export = (target, all) => {
|
|
|
17
17
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
|
+
// web-src/server/json-store.ts
|
|
21
|
+
import { randomBytes } from "node:crypto";
|
|
22
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
23
|
+
import { dirname } from "node:path";
|
|
24
|
+
function isEnoent(err) {
|
|
25
|
+
return err?.code === "ENOENT";
|
|
26
|
+
}
|
|
27
|
+
function tmpPath(file) {
|
|
28
|
+
return `${file}.tmp-${process.pid}-${Date.now()}-${randomBytes(4).toString("hex")}`;
|
|
29
|
+
}
|
|
30
|
+
async function backupInvalidFile(file, suffix) {
|
|
31
|
+
try {
|
|
32
|
+
await rename(file, `${file}.${suffix}-${Date.now()}`);
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
function createJsonFileStore(options) {
|
|
36
|
+
const queues = new Map;
|
|
37
|
+
const backupSuffix = options.backupSuffix ?? "corrupt";
|
|
38
|
+
const serialize = options.serialize ?? ((state) => `${JSON.stringify(state, null, 2)}
|
|
39
|
+
`);
|
|
40
|
+
async function loadUnqueued(root) {
|
|
41
|
+
const file = options.filePath(root);
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
raw = await readFile(file, "utf8");
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (isEnoent(err))
|
|
47
|
+
return options.empty();
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return options.sanitize(JSON.parse(raw));
|
|
52
|
+
} catch {
|
|
53
|
+
await backupInvalidFile(file, backupSuffix);
|
|
54
|
+
return options.empty();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function saveUnqueued(root, state) {
|
|
58
|
+
const file = options.filePath(root);
|
|
59
|
+
const normalized = options.sanitize(state);
|
|
60
|
+
const content = serialize(normalized);
|
|
61
|
+
if (options.maxBytes !== undefined && Buffer.byteLength(content, "utf8") > options.maxBytes) {
|
|
62
|
+
throw new Error(options.sizeErrorMessage ?? "JSON state too large");
|
|
63
|
+
}
|
|
64
|
+
await mkdir(dirname(file), { recursive: true });
|
|
65
|
+
const tmp = tmpPath(file);
|
|
66
|
+
await writeFile(tmp, content, "utf8");
|
|
67
|
+
await rename(tmp, file);
|
|
68
|
+
}
|
|
69
|
+
async function load(root) {
|
|
70
|
+
const pendingWrite = queues.get(options.filePath(root));
|
|
71
|
+
if (pendingWrite)
|
|
72
|
+
await pendingWrite.catch(() => {});
|
|
73
|
+
return loadUnqueued(root);
|
|
74
|
+
}
|
|
75
|
+
async function save(root, state) {
|
|
76
|
+
const file = options.filePath(root);
|
|
77
|
+
const previous = queues.get(file) ?? Promise.resolve();
|
|
78
|
+
const run = previous.catch(() => {}).then(() => saveUnqueued(root, state));
|
|
79
|
+
const queued = run.then(() => {}, () => {});
|
|
80
|
+
queues.set(file, queued);
|
|
81
|
+
try {
|
|
82
|
+
await run;
|
|
83
|
+
} finally {
|
|
84
|
+
if (queues.get(file) === queued)
|
|
85
|
+
queues.delete(file);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function update(root, updater) {
|
|
89
|
+
const file = options.filePath(root);
|
|
90
|
+
const previous = queues.get(file) ?? Promise.resolve();
|
|
91
|
+
const run = previous.catch(() => {}).then(async () => {
|
|
92
|
+
const current = await loadUnqueued(root);
|
|
93
|
+
const updated = await updater(current);
|
|
94
|
+
await saveUnqueued(root, updated.state);
|
|
95
|
+
return updated.result;
|
|
96
|
+
});
|
|
97
|
+
const queued = run.then(() => {}, () => {});
|
|
98
|
+
queues.set(file, queued);
|
|
99
|
+
try {
|
|
100
|
+
return await run;
|
|
101
|
+
} finally {
|
|
102
|
+
if (queues.get(file) === queued)
|
|
103
|
+
queues.delete(file);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { load, save, update };
|
|
107
|
+
}
|
|
108
|
+
var init_json_store = () => {};
|
|
109
|
+
|
|
20
110
|
// web-src/server/annotations.ts
|
|
21
|
-
import {
|
|
22
|
-
existsSync,
|
|
23
|
-
mkdirSync,
|
|
24
|
-
readFileSync,
|
|
25
|
-
renameSync,
|
|
26
|
-
writeFileSync
|
|
27
|
-
} from "node:fs";
|
|
28
111
|
import { join } from "node:path";
|
|
29
112
|
function annotationsFilePath(root) {
|
|
30
113
|
return join(root, CODE_VIEWER_DIR, ANNOTATIONS_FILE_NAME);
|
|
@@ -242,24 +325,11 @@ function normalizeAnnotationsState(raw) {
|
|
|
242
325
|
sessions: sessions.map(normalizeSession).filter((session) => session !== null)
|
|
243
326
|
};
|
|
244
327
|
}
|
|
245
|
-
function loadAnnotationsState(root) {
|
|
246
|
-
|
|
247
|
-
if (!existsSync(file))
|
|
248
|
-
return emptyAnnotationsState();
|
|
249
|
-
try {
|
|
250
|
-
return normalizeAnnotationsState(JSON.parse(readFileSync(file, "utf8")));
|
|
251
|
-
} catch {
|
|
252
|
-
return emptyAnnotationsState();
|
|
253
|
-
}
|
|
328
|
+
async function loadAnnotationsState(root) {
|
|
329
|
+
return annotationsStore.load(root);
|
|
254
330
|
}
|
|
255
|
-
function saveAnnotationsState(root, state) {
|
|
256
|
-
|
|
257
|
-
mkdirSync(dir, { recursive: true });
|
|
258
|
-
const file = annotationsFilePath(root);
|
|
259
|
-
const tmp = `${file}.tmp-${process.pid}`;
|
|
260
|
-
writeFileSync(tmp, `${JSON.stringify(state, null, 2)}
|
|
261
|
-
`, "utf8");
|
|
262
|
-
renameSync(tmp, file);
|
|
331
|
+
async function saveAnnotationsState(root, state) {
|
|
332
|
+
return annotationsStore.save(root, state);
|
|
263
333
|
}
|
|
264
334
|
function startAnnotationSession(state, title, now, id = makeAnnotationId("s")) {
|
|
265
335
|
const session = {
|
|
@@ -484,9 +554,18 @@ function deleteAnnotationById(state, id) {
|
|
|
484
554
|
}
|
|
485
555
|
return { state, removed: null };
|
|
486
556
|
}
|
|
487
|
-
var CODE_VIEWER_DIR = ".code-viewer", ANNOTATIONS_FILE_NAME = "annotations.json", ANNOTATION_BODY_MAX_BYTES, ANNOTATION_TITLE_MAX_CHARS = 300;
|
|
557
|
+
var CODE_VIEWER_DIR = ".code-viewer", ANNOTATIONS_FILE_NAME = "annotations.json", ANNOTATION_BODY_MAX_BYTES, ANNOTATION_TITLE_MAX_CHARS = 300, MAX_ANNOTATIONS_JSON_BYTES = 5000000, annotationsStore;
|
|
488
558
|
var init_annotations = __esm(() => {
|
|
559
|
+
init_json_store();
|
|
489
560
|
ANNOTATION_BODY_MAX_BYTES = 64 * 1024;
|
|
561
|
+
annotationsStore = createJsonFileStore({
|
|
562
|
+
filePath: annotationsFilePath,
|
|
563
|
+
empty: emptyAnnotationsState,
|
|
564
|
+
sanitize: normalizeAnnotationsState,
|
|
565
|
+
maxBytes: MAX_ANNOTATIONS_JSON_BYTES,
|
|
566
|
+
backupSuffix: "corrupt",
|
|
567
|
+
sizeErrorMessage: "annotations state too large"
|
|
568
|
+
});
|
|
490
569
|
});
|
|
491
570
|
|
|
492
571
|
// web-src/server/runtime.ts
|
|
@@ -673,10 +752,10 @@ var init_runtime = () => {};
|
|
|
673
752
|
|
|
674
753
|
// web-src/server/git.ts
|
|
675
754
|
import {
|
|
676
|
-
existsSync
|
|
755
|
+
existsSync,
|
|
677
756
|
lstatSync,
|
|
678
757
|
readdirSync,
|
|
679
|
-
readFileSync
|
|
758
|
+
readFileSync,
|
|
680
759
|
statSync
|
|
681
760
|
} from "node:fs";
|
|
682
761
|
import { join as join2 } from "node:path";
|
|
@@ -1120,7 +1199,7 @@ function omittedWorktreeDirectoryReason(name, omitDirNames) {
|
|
|
1120
1199
|
return omitDirNames.has(name) ? "heavy" : undefined;
|
|
1121
1200
|
}
|
|
1122
1201
|
function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, excludeNames) {
|
|
1123
|
-
if (excludeNames.has(name.toLowerCase())
|
|
1202
|
+
if (excludeNames.has(name.toLowerCase()))
|
|
1124
1203
|
return {
|
|
1125
1204
|
name,
|
|
1126
1205
|
path: "",
|
|
@@ -1182,7 +1261,7 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
1182
1261
|
return;
|
|
1183
1262
|
}
|
|
1184
1263
|
for (const entry of entries) {
|
|
1185
|
-
if (excludeNameSet.has(entry.name.toLowerCase())
|
|
1264
|
+
if (excludeNameSet.has(entry.name.toLowerCase()))
|
|
1186
1265
|
continue;
|
|
1187
1266
|
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1188
1267
|
const full = join2(dir, entry.name);
|
|
@@ -1287,12 +1366,12 @@ function untrackedMeta(cwd) {
|
|
|
1287
1366
|
let lines = 0;
|
|
1288
1367
|
let fileExists = false;
|
|
1289
1368
|
try {
|
|
1290
|
-
fileExists =
|
|
1369
|
+
fileExists = existsSync(full) && statSync(full).isFile();
|
|
1291
1370
|
} catch {
|
|
1292
1371
|
fileExists = false;
|
|
1293
1372
|
}
|
|
1294
1373
|
if (fileExists) {
|
|
1295
|
-
const data =
|
|
1374
|
+
const data = readFileSync(full);
|
|
1296
1375
|
const probe = data.subarray(0, 8192);
|
|
1297
1376
|
binary = probe.includes(0);
|
|
1298
1377
|
if (!binary)
|
|
@@ -1471,11 +1550,11 @@ var init_git = __esm(() => {
|
|
|
1471
1550
|
// web-src/server/server-registry.ts
|
|
1472
1551
|
import { createHash } from "node:crypto";
|
|
1473
1552
|
import {
|
|
1474
|
-
existsSync as
|
|
1475
|
-
mkdirSync
|
|
1476
|
-
readFileSync as
|
|
1553
|
+
existsSync as existsSync2,
|
|
1554
|
+
mkdirSync,
|
|
1555
|
+
readFileSync as readFileSync2,
|
|
1477
1556
|
unlinkSync,
|
|
1478
|
-
writeFileSync
|
|
1557
|
+
writeFileSync
|
|
1479
1558
|
} from "node:fs";
|
|
1480
1559
|
import { homedir } from "node:os";
|
|
1481
1560
|
import { join as join3 } from "node:path";
|
|
@@ -1488,17 +1567,17 @@ function serverRegistryFilePath(root) {
|
|
|
1488
1567
|
}
|
|
1489
1568
|
function writeServerRegistry(entry) {
|
|
1490
1569
|
try {
|
|
1491
|
-
|
|
1492
|
-
|
|
1570
|
+
mkdirSync(registryDir(), { recursive: true });
|
|
1571
|
+
writeFileSync(serverRegistryFilePath(entry.root), `${JSON.stringify(entry, null, 2)}
|
|
1493
1572
|
`, "utf8");
|
|
1494
1573
|
} catch {}
|
|
1495
1574
|
}
|
|
1496
1575
|
function readServerRegistry(root) {
|
|
1497
1576
|
const file = serverRegistryFilePath(root);
|
|
1498
|
-
if (!
|
|
1577
|
+
if (!existsSync2(file))
|
|
1499
1578
|
return null;
|
|
1500
1579
|
try {
|
|
1501
|
-
const raw = JSON.parse(
|
|
1580
|
+
const raw = JSON.parse(readFileSync2(file, "utf8"));
|
|
1502
1581
|
if (!raw || typeof raw !== "object")
|
|
1503
1582
|
return null;
|
|
1504
1583
|
const entry = raw;
|
|
@@ -1532,7 +1611,7 @@ __export(exports_annotate_cli, {
|
|
|
1532
1611
|
ANNOTATE_HELP: () => ANNOTATE_HELP,
|
|
1533
1612
|
ANNOTATE_AGENT_HELP: () => ANNOTATE_AGENT_HELP
|
|
1534
1613
|
});
|
|
1535
|
-
import { readFileSync as
|
|
1614
|
+
import { readFileSync as readFileSync3, realpathSync } from "node:fs";
|
|
1536
1615
|
function takeValue(argv, index, flag) {
|
|
1537
1616
|
const value = argv[index + 1];
|
|
1538
1617
|
if (value === undefined)
|
|
@@ -1961,7 +2040,7 @@ async function annotationBodyFromCommand(command) {
|
|
|
1961
2040
|
let body = command.body;
|
|
1962
2041
|
if (body === undefined && command.bodyFile !== undefined) {
|
|
1963
2042
|
try {
|
|
1964
|
-
body =
|
|
2043
|
+
body = readFileSync3(command.bodyFile, "utf8");
|
|
1965
2044
|
} catch {
|
|
1966
2045
|
console.error(`could not read --body-file: ${command.bodyFile}`);
|
|
1967
2046
|
process.exit(1);
|
|
@@ -2029,7 +2108,7 @@ async function runAnnotateCli(argv) {
|
|
|
2029
2108
|
let sql = command.sql;
|
|
2030
2109
|
if (sql === undefined && command.sqlFile !== undefined) {
|
|
2031
2110
|
try {
|
|
2032
|
-
sql =
|
|
2111
|
+
sql = readFileSync3(command.sqlFile, "utf8");
|
|
2033
2112
|
} catch {
|
|
2034
2113
|
console.error(`could not read --sql-file: ${command.sqlFile}`);
|
|
2035
2114
|
process.exit(1);
|
|
@@ -2098,7 +2177,7 @@ async function runAnnotateCli(argv) {
|
|
|
2098
2177
|
if (command.kind === "edit") {
|
|
2099
2178
|
let bodyText = command.body;
|
|
2100
2179
|
if (command.bodyFile !== undefined)
|
|
2101
|
-
bodyText =
|
|
2180
|
+
bodyText = readFileSync3(command.bodyFile, "utf8");
|
|
2102
2181
|
if (bodyText === undefined) {
|
|
2103
2182
|
const stdin = await readStdin();
|
|
2104
2183
|
if (stdin.trim())
|
|
@@ -2656,16 +2735,16 @@ var init_query_cli = __esm(() => {
|
|
|
2656
2735
|
});
|
|
2657
2736
|
|
|
2658
2737
|
// web-src/server/root.ts
|
|
2659
|
-
import { existsSync as
|
|
2660
|
-
import { dirname, join as join4, normalize } from "node:path";
|
|
2738
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2739
|
+
import { dirname as dirname2, join as join4, normalize } from "node:path";
|
|
2661
2740
|
import { fileURLToPath } from "node:url";
|
|
2662
2741
|
function findRoot(start) {
|
|
2663
2742
|
let current = start;
|
|
2664
2743
|
for (let i = 0;i < 5; i++) {
|
|
2665
|
-
if (
|
|
2744
|
+
if (existsSync3(join4(current, "package.json")) && existsSync3(join4(current, "web"))) {
|
|
2666
2745
|
return normalize(current);
|
|
2667
2746
|
}
|
|
2668
|
-
const parent =
|
|
2747
|
+
const parent = dirname2(current);
|
|
2669
2748
|
if (parent === current)
|
|
2670
2749
|
break;
|
|
2671
2750
|
current = parent;
|
|
@@ -2674,7 +2753,7 @@ function findRoot(start) {
|
|
|
2674
2753
|
}
|
|
2675
2754
|
var ROOT;
|
|
2676
2755
|
var init_root = __esm(() => {
|
|
2677
|
-
ROOT = findRoot(
|
|
2756
|
+
ROOT = findRoot(dirname2(fileURLToPath(import.meta.url)));
|
|
2678
2757
|
});
|
|
2679
2758
|
|
|
2680
2759
|
// web-src/server/skill-cli.ts
|
|
@@ -2686,7 +2765,7 @@ __export(exports_skill_cli, {
|
|
|
2686
2765
|
SKILL_HELP: () => SKILL_HELP,
|
|
2687
2766
|
AGENT_SKILL_DIRS: () => AGENT_SKILL_DIRS
|
|
2688
2767
|
});
|
|
2689
|
-
import { cpSync, existsSync as
|
|
2768
|
+
import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "node:fs";
|
|
2690
2769
|
import { homedir as homedir2 } from "node:os";
|
|
2691
2770
|
import { join as join5, resolve } from "node:path";
|
|
2692
2771
|
function parseAgentList(value) {
|
|
@@ -2743,7 +2822,7 @@ function parseSkillArgs(argv) {
|
|
|
2743
2822
|
return { ok: true, args: { kind: "install", agents, global, cwd } };
|
|
2744
2823
|
}
|
|
2745
2824
|
function installSkill(args, deps) {
|
|
2746
|
-
if (!
|
|
2825
|
+
if (!existsSync4(join5(deps.sourceDir, "SKILL.md"))) {
|
|
2747
2826
|
return {
|
|
2748
2827
|
ok: false,
|
|
2749
2828
|
error: `bundled skill not found at ${deps.sourceDir}`
|
|
@@ -2753,9 +2832,9 @@ function installSkill(args, deps) {
|
|
|
2753
2832
|
const results = [];
|
|
2754
2833
|
for (const agent of args.agents) {
|
|
2755
2834
|
const target = join5(base, AGENT_SKILL_DIRS[agent], "skills", SKILL_NAME);
|
|
2756
|
-
const action =
|
|
2835
|
+
const action = existsSync4(target) ? "updated" : "installed";
|
|
2757
2836
|
try {
|
|
2758
|
-
|
|
2837
|
+
mkdirSync2(target, { recursive: true });
|
|
2759
2838
|
cpSync(deps.sourceDir, target, { recursive: true });
|
|
2760
2839
|
} catch (error) {
|
|
2761
2840
|
return { ok: false, error: String(error) };
|
|
@@ -3923,6 +4002,366 @@ function makeId(prefix) {
|
|
|
3923
4002
|
return `${prefix}-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
3924
4003
|
}
|
|
3925
4004
|
|
|
4005
|
+
// web-src/server/state-store.ts
|
|
4006
|
+
import { join as join8 } from "node:path";
|
|
4007
|
+
function codeViewerPath(root, fileName) {
|
|
4008
|
+
return join8(root, CODE_VIEWER_DIR2, fileName);
|
|
4009
|
+
}
|
|
4010
|
+
function isRecord(value) {
|
|
4011
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
4012
|
+
}
|
|
4013
|
+
function optionalString(value, maxLen) {
|
|
4014
|
+
if (typeof value !== "string")
|
|
4015
|
+
return;
|
|
4016
|
+
if (value.length === 0 || value.length > maxLen)
|
|
4017
|
+
return;
|
|
4018
|
+
if (value.includes("\x00"))
|
|
4019
|
+
return;
|
|
4020
|
+
return value;
|
|
4021
|
+
}
|
|
4022
|
+
function optionalBoolean(value) {
|
|
4023
|
+
return typeof value === "boolean" ? value : undefined;
|
|
4024
|
+
}
|
|
4025
|
+
function optionalNumber(value, min, max) {
|
|
4026
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
4027
|
+
return;
|
|
4028
|
+
return Math.max(min, Math.min(max, Math.round(value)));
|
|
4029
|
+
}
|
|
4030
|
+
function optionalFloat(value, min, max) {
|
|
4031
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
4032
|
+
return;
|
|
4033
|
+
return Math.max(min, Math.min(max, value));
|
|
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;
|
|
4228
|
+
}
|
|
4229
|
+
function sanitizeDbUiState(raw) {
|
|
4230
|
+
if (!isRecord(raw) || !isRecord(raw.columnWidths))
|
|
4231
|
+
return emptyDbUiState();
|
|
4232
|
+
const columnWidths = {};
|
|
4233
|
+
let dbCount = 0;
|
|
4234
|
+
for (const [dbIdRaw, tablesRaw] of Object.entries(raw.columnWidths)) {
|
|
4235
|
+
if (dbCount >= MAX_DB_UI_DBS)
|
|
4236
|
+
break;
|
|
4237
|
+
const dbId = safeObjectKey(dbIdRaw);
|
|
4238
|
+
if (!dbId || !isRecord(tablesRaw))
|
|
4239
|
+
continue;
|
|
4240
|
+
const tables = {};
|
|
4241
|
+
let tableCount = 0;
|
|
4242
|
+
for (const [tableRaw, columnsRaw] of Object.entries(tablesRaw)) {
|
|
4243
|
+
if (tableCount >= MAX_DB_UI_TABLES)
|
|
4244
|
+
break;
|
|
4245
|
+
const table = safeObjectKey(tableRaw);
|
|
4246
|
+
if (!table || !isRecord(columnsRaw))
|
|
4247
|
+
continue;
|
|
4248
|
+
const columns = {};
|
|
4249
|
+
let columnCount = 0;
|
|
4250
|
+
for (const [columnRaw, widthRaw] of Object.entries(columnsRaw)) {
|
|
4251
|
+
if (columnCount >= MAX_DB_UI_COLUMNS)
|
|
4252
|
+
break;
|
|
4253
|
+
const column = safeObjectKey(columnRaw);
|
|
4254
|
+
const width = optionalNumber(widthRaw, 60, 1200);
|
|
4255
|
+
if (!column || width === undefined)
|
|
4256
|
+
continue;
|
|
4257
|
+
columns[column] = width;
|
|
4258
|
+
columnCount++;
|
|
4259
|
+
}
|
|
4260
|
+
if (Object.keys(columns).length === 0)
|
|
4261
|
+
continue;
|
|
4262
|
+
tables[table] = columns;
|
|
4263
|
+
tableCount++;
|
|
4264
|
+
}
|
|
4265
|
+
if (Object.keys(tables).length === 0)
|
|
4266
|
+
continue;
|
|
4267
|
+
columnWidths[dbId] = tables;
|
|
4268
|
+
dbCount++;
|
|
4269
|
+
}
|
|
4270
|
+
return { version: 1, columnWidths };
|
|
4271
|
+
}
|
|
4272
|
+
function mergeDbUiState(current, patch) {
|
|
4273
|
+
if (!isRecord(patch))
|
|
4274
|
+
return current;
|
|
4275
|
+
if (!isRecord(patch.columnWidths)) {
|
|
4276
|
+
return sanitizeDbUiState({ ...current, ...patch, version: 1 });
|
|
4277
|
+
}
|
|
4278
|
+
const columnWidths = {
|
|
4279
|
+
...current.columnWidths
|
|
4280
|
+
};
|
|
4281
|
+
for (const [dbId, tablesRaw] of Object.entries(patch.columnWidths)) {
|
|
4282
|
+
if (tablesRaw === null) {
|
|
4283
|
+
delete columnWidths[dbId];
|
|
4284
|
+
continue;
|
|
4285
|
+
}
|
|
4286
|
+
if (!isRecord(tablesRaw))
|
|
4287
|
+
continue;
|
|
4288
|
+
const tables = { ...columnWidths[dbId] || {} };
|
|
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;
|
|
4304
|
+
}
|
|
4305
|
+
columnWidths[dbId] = tables;
|
|
4306
|
+
}
|
|
4307
|
+
return sanitizeDbUiState({ ...current, ...patch, columnWidths, version: 1 });
|
|
4308
|
+
}
|
|
4309
|
+
async function loadAppSettingsState(root) {
|
|
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);
|
|
4320
|
+
}
|
|
4321
|
+
async function patchViewState(root, patch) {
|
|
4322
|
+
return viewStateStore.update(root, (state) => {
|
|
4323
|
+
const next = mergeViewState(state, patch);
|
|
4324
|
+
return { state: next, result: next };
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
async function loadDbUiState(root) {
|
|
4328
|
+
return dbUiStore.load(root);
|
|
4329
|
+
}
|
|
4330
|
+
async function patchDbUiState(root, patch) {
|
|
4331
|
+
return dbUiStore.update(root, (state) => {
|
|
4332
|
+
const next = mergeDbUiState(state, patch);
|
|
4333
|
+
return { state: next, result: next };
|
|
4334
|
+
});
|
|
4335
|
+
}
|
|
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
|
+
|
|
3926
4365
|
// web-src/server/database/adapters/abort.ts
|
|
3927
4366
|
function abortError(message = "operation aborted") {
|
|
3928
4367
|
const err = new Error(message);
|
|
@@ -4676,6 +5115,10 @@ function createTableMetaCache(now = () => Date.now()) {
|
|
|
4676
5115
|
}
|
|
4677
5116
|
};
|
|
4678
5117
|
}
|
|
5118
|
+
function observeBackgroundRejection(promise) {
|
|
5119
|
+
promise.catch(() => {});
|
|
5120
|
+
return promise;
|
|
5121
|
+
}
|
|
4679
5122
|
function createDockerAdapter(config) {
|
|
4680
5123
|
async function execAsync(sql, signal) {
|
|
4681
5124
|
const result = await execInContainerAsync(config, sql, 1e4, signal);
|
|
@@ -4947,7 +5390,7 @@ function createDockerAdapter(config) {
|
|
|
4947
5390
|
const id = tableIdentifier(table);
|
|
4948
5391
|
const countSql = `SELECT COUNT(*) AS cnt FROM ${id}`;
|
|
4949
5392
|
const columnsPromise = tableMetaCache.getColumns(table, () => fetchColumnsAsyncUncached(table, signal));
|
|
4950
|
-
const totalRowsPromise = tableMetaCache.getRowCount(table, async () => rowCountFromResult(await execAsync(countSql, signal)));
|
|
5393
|
+
const totalRowsPromise = observeBackgroundRejection(tableMetaCache.getRowCount(table, async () => rowCountFromResult(await execAsync(countSql, signal))));
|
|
4951
5394
|
let columns;
|
|
4952
5395
|
try {
|
|
4953
5396
|
columns = await columnsPromise;
|
|
@@ -5626,14 +6069,14 @@ var init_connection_pool = __esm(() => {
|
|
|
5626
6069
|
// web-src/server/database/discovery.ts
|
|
5627
6070
|
import {
|
|
5628
6071
|
closeSync,
|
|
5629
|
-
existsSync as
|
|
6072
|
+
existsSync as existsSync5,
|
|
5630
6073
|
openSync,
|
|
5631
6074
|
readSync,
|
|
5632
6075
|
realpathSync as realpathSync3,
|
|
5633
6076
|
statSync as statSync2
|
|
5634
6077
|
} from "node:fs";
|
|
5635
|
-
import { lstat, open, readdir, readFile, stat } from "node:fs/promises";
|
|
5636
|
-
import { basename as basename2, join as
|
|
6078
|
+
import { lstat, open, readdir, readFile as readFile2, stat } from "node:fs/promises";
|
|
6079
|
+
import { basename as basename2, join as join9, relative as relative2 } from "node:path";
|
|
5637
6080
|
function isSqliteFile(fullPath) {
|
|
5638
6081
|
try {
|
|
5639
6082
|
const stat2 = statSync2(fullPath);
|
|
@@ -5704,7 +6147,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
5704
6147
|
return;
|
|
5705
6148
|
if (omitSet.has(entry.toLowerCase()))
|
|
5706
6149
|
continue;
|
|
5707
|
-
const full =
|
|
6150
|
+
const full = join9(dir, entry);
|
|
5708
6151
|
let entryStat;
|
|
5709
6152
|
try {
|
|
5710
6153
|
entryStat = await lstat(full);
|
|
@@ -5748,8 +6191,8 @@ function validateDbPath(cwd, dbPath) {
|
|
|
5748
6191
|
const parts = dbPath.split(/[\\/]+/);
|
|
5749
6192
|
if (parts.some((p) => p === ".." || p.toLowerCase() === ".git" || p.toLowerCase() === ".code-viewer"))
|
|
5750
6193
|
return null;
|
|
5751
|
-
const full =
|
|
5752
|
-
if (!
|
|
6194
|
+
const full = join9(cwd, dbPath);
|
|
6195
|
+
if (!existsSync5(full))
|
|
5753
6196
|
return null;
|
|
5754
6197
|
let realCwd;
|
|
5755
6198
|
let realFull;
|
|
@@ -5885,7 +6328,7 @@ function resolveEnvValue(raw, composeDirEnv = {}) {
|
|
|
5885
6328
|
}
|
|
5886
6329
|
async function readDotenvAsync(composeDir) {
|
|
5887
6330
|
try {
|
|
5888
|
-
const content = await
|
|
6331
|
+
const content = await readFile2(join9(composeDir, ".env"), "utf-8");
|
|
5889
6332
|
return parseDotenvContent(content);
|
|
5890
6333
|
} catch {
|
|
5891
6334
|
return {};
|
|
@@ -6059,7 +6502,7 @@ function parseComposeContent(content, filepath, composeDir, cwd, composeDirEnv,
|
|
|
6059
6502
|
async function parseComposeFileAsync(filepath, composeDir, cwd, results) {
|
|
6060
6503
|
let content;
|
|
6061
6504
|
try {
|
|
6062
|
-
content = await
|
|
6505
|
+
content = await readFile2(filepath, "utf-8");
|
|
6063
6506
|
} catch {
|
|
6064
6507
|
return;
|
|
6065
6508
|
}
|
|
@@ -6105,7 +6548,7 @@ async function discoverDockerDatabasesAsync(cwd, omitDirNames = [], signal) {
|
|
|
6105
6548
|
if (depth > MAX_SCAN_DEPTH)
|
|
6106
6549
|
return;
|
|
6107
6550
|
for (const filename of COMPOSE_FILENAMES) {
|
|
6108
|
-
const filepath =
|
|
6551
|
+
const filepath = join9(dir, filename);
|
|
6109
6552
|
if (await pathExistsAsync(filepath)) {
|
|
6110
6553
|
await parseComposeFileAsync(filepath, dir, cwd, results);
|
|
6111
6554
|
break;
|
|
@@ -6128,7 +6571,7 @@ async function discoverDockerDatabasesAsync(cwd, omitDirNames = [], signal) {
|
|
|
6128
6571
|
return;
|
|
6129
6572
|
if (omitSet.has(entry.toLowerCase()))
|
|
6130
6573
|
continue;
|
|
6131
|
-
const full =
|
|
6574
|
+
const full = join9(dir, entry);
|
|
6132
6575
|
let entryStat;
|
|
6133
6576
|
try {
|
|
6134
6577
|
entryStat = await lstat(full);
|
|
@@ -6763,6 +7206,32 @@ function textError(message, status) {
|
|
|
6763
7206
|
}
|
|
6764
7207
|
});
|
|
6765
7208
|
}
|
|
7209
|
+
async function jsonLoadResponse(load, logPrefix, errorMessage) {
|
|
7210
|
+
try {
|
|
7211
|
+
return json(await load());
|
|
7212
|
+
} catch (err) {
|
|
7213
|
+
console.error(`[code-viewer] ${logPrefix} error:`, err);
|
|
7214
|
+
return textError(errorMessage, 500);
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
async function parseBoundedJsonBody(req, maxBytes, tooLargeMessage) {
|
|
7218
|
+
const contentType = req.headers.get("content-type") || "";
|
|
7219
|
+
if (!contentType.toLowerCase().startsWith("application/json")) {
|
|
7220
|
+
return textError("unsupported media type", 415);
|
|
7221
|
+
}
|
|
7222
|
+
const contentLength = Number(req.headers.get("content-length") || "0");
|
|
7223
|
+
if (contentLength > maxBytes)
|
|
7224
|
+
return textError(tooLargeMessage, 413);
|
|
7225
|
+
try {
|
|
7226
|
+
const raw = await req.text();
|
|
7227
|
+
if (Buffer.byteLength(raw, "utf8") > maxBytes) {
|
|
7228
|
+
return textError(tooLargeMessage, 413);
|
|
7229
|
+
}
|
|
7230
|
+
return JSON.parse(raw);
|
|
7231
|
+
} catch {
|
|
7232
|
+
return textError("invalid JSON body", 400);
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
6766
7235
|
function waitForCallerAbort(promise, signal, message) {
|
|
6767
7236
|
if (!signal)
|
|
6768
7237
|
return promise;
|
|
@@ -6794,6 +7263,10 @@ function waitForCallerAbort(promise, signal, message) {
|
|
|
6794
7263
|
});
|
|
6795
7264
|
});
|
|
6796
7265
|
}
|
|
7266
|
+
function isFilesystemAccessError(err) {
|
|
7267
|
+
const code = err?.code;
|
|
7268
|
+
return code === "EACCES" || code === "EBUSY" || code === "EIO" || code === "EISDIR" || code === "ENOSPC" || code === "ENOTDIR" || code === "EPERM" || code === "EROFS";
|
|
7269
|
+
}
|
|
6797
7270
|
async function resolveDockerExplorerAsync(cwd, dbParam, kind, cache, openFn, omitDirNames, signal) {
|
|
6798
7271
|
if (!dbParam)
|
|
6799
7272
|
return textError("missing db parameter", 400);
|
|
@@ -6860,6 +7333,9 @@ function handleError(prefix, action, err) {
|
|
|
6860
7333
|
if (isDockerComposeServiceUnavailableError(err)) {
|
|
6861
7334
|
return textError(message, err.status);
|
|
6862
7335
|
}
|
|
7336
|
+
if (isFilesystemAccessError(err)) {
|
|
7337
|
+
return textError(`failed to ${action}`, 500);
|
|
7338
|
+
}
|
|
6863
7339
|
return textError(`failed to ${action}: ${message}`, 500);
|
|
6864
7340
|
}
|
|
6865
7341
|
var DEFAULT_MAX_DOCKER_ADAPTER_CACHE = 8, DEFAULT_DOCKER_ADAPTER_IDLE_MS;
|
|
@@ -8657,13 +9133,9 @@ var init_handle_s3 = __esm(() => {
|
|
|
8657
9133
|
});
|
|
8658
9134
|
|
|
8659
9135
|
// web-src/server/database/query-history.ts
|
|
8660
|
-
import {
|
|
8661
|
-
import { join as join9 } from "node:path";
|
|
9136
|
+
import { join as join10 } from "node:path";
|
|
8662
9137
|
function historyFilePath(root) {
|
|
8663
|
-
return
|
|
8664
|
-
}
|
|
8665
|
-
function isEnoent(err) {
|
|
8666
|
-
return err?.code === "ENOENT";
|
|
9138
|
+
return join10(root, CODE_VIEWER_DIR3, HISTORY_FILE_NAME);
|
|
8667
9139
|
}
|
|
8668
9140
|
function emptyState() {
|
|
8669
9141
|
return { version: 1, entries: [] };
|
|
@@ -8682,69 +9154,86 @@ function serializeHistoryState(state) {
|
|
|
8682
9154
|
}
|
|
8683
9155
|
return content;
|
|
8684
9156
|
}
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
9157
|
+
function optionalString2(value, maxLen) {
|
|
9158
|
+
if (typeof value !== "string")
|
|
9159
|
+
return;
|
|
9160
|
+
if (!value || value.length > maxLen || value.includes("\x00"))
|
|
9161
|
+
return;
|
|
9162
|
+
return value;
|
|
8689
9163
|
}
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
9164
|
+
function finiteNumber(value, min = 0) {
|
|
9165
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
9166
|
+
return;
|
|
9167
|
+
return Math.max(min, Math.round(value));
|
|
9168
|
+
}
|
|
9169
|
+
function sanitizeDbValue(value) {
|
|
9170
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
9171
|
+
return value;
|
|
8694
9172
|
}
|
|
8695
|
-
return
|
|
9173
|
+
return null;
|
|
8696
9174
|
}
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
9175
|
+
function sanitizeRows(raw) {
|
|
9176
|
+
if (!Array.isArray(raw))
|
|
9177
|
+
return [];
|
|
9178
|
+
return raw.slice(0, MAX_PREVIEW_ROWS).map((row) => {
|
|
9179
|
+
if (!Array.isArray(row))
|
|
9180
|
+
return [];
|
|
9181
|
+
return row.map(sanitizeDbValue);
|
|
9182
|
+
});
|
|
9183
|
+
}
|
|
9184
|
+
function sanitizeEntry(raw) {
|
|
9185
|
+
if (!raw || typeof raw !== "object")
|
|
9186
|
+
return null;
|
|
9187
|
+
const entry = raw;
|
|
9188
|
+
const id = optionalString2(entry.id, MAX_ID_LEN);
|
|
9189
|
+
const dbId = optionalString2(entry.dbId, MAX_DB_ID_LEN);
|
|
9190
|
+
const sql = optionalString2(entry.sql, MAX_SQL_LEN);
|
|
9191
|
+
if (!id || !dbId || !sql)
|
|
9192
|
+
return null;
|
|
9193
|
+
const columns = Array.isArray(entry.columns) ? entry.columns.filter((col) => typeof col === "string" && !!col).map((col) => col.slice(0, MAX_COLUMN_LEN)).slice(0, MAX_COLUMNS) : [];
|
|
9194
|
+
const rowsPreview = sanitizeRows(entry.rowsPreview);
|
|
9195
|
+
const schema = optionalString2(entry.schema, MAX_SCHEMA_LEN);
|
|
9196
|
+
const title = optionalString2(entry.title, MAX_TEXT_LEN);
|
|
9197
|
+
const body = optionalString2(entry.body, MAX_TEXT_LEN);
|
|
9198
|
+
return {
|
|
9199
|
+
id,
|
|
9200
|
+
dbId,
|
|
9201
|
+
...schema ? { schema } : {},
|
|
9202
|
+
sql,
|
|
9203
|
+
...title ? { title } : {},
|
|
9204
|
+
...body ? { body } : {},
|
|
9205
|
+
columns,
|
|
9206
|
+
rowsPreview,
|
|
9207
|
+
rowCount: finiteNumber(entry.rowCount) ?? rowsPreview.length,
|
|
9208
|
+
savedRows: finiteNumber(entry.savedRows) ?? rowsPreview.length,
|
|
9209
|
+
truncated: typeof entry.truncated === "boolean" ? entry.truncated : false,
|
|
9210
|
+
elapsedMs: finiteNumber(entry.elapsedMs) ?? 0,
|
|
9211
|
+
executedAt: optionalString2(entry.executedAt, 64) ?? new Date(0).toISOString(),
|
|
9212
|
+
executedBy: entry.executedBy === "ai" ? "ai" : "user",
|
|
9213
|
+
source: entry.source === "cli" ? "cli" : "browser"
|
|
9214
|
+
};
|
|
9215
|
+
}
|
|
9216
|
+
function sanitizeHistoryState(raw) {
|
|
9217
|
+
if (!raw || typeof raw !== "object")
|
|
8706
9218
|
return emptyState();
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
const parsed = JSON.parse(raw);
|
|
8710
|
-
if (!parsed || typeof parsed !== "object" || parsed.version !== 1 || !Array.isArray(parsed.entries)) {
|
|
8711
|
-
await backupCorruptHistoryFileAsync(file);
|
|
8712
|
-
return emptyState();
|
|
8713
|
-
}
|
|
8714
|
-
return parsed;
|
|
8715
|
-
} catch (err) {
|
|
8716
|
-
if (isEnoent(err))
|
|
8717
|
-
return emptyState();
|
|
8718
|
-
await backupCorruptHistoryFileAsync(file);
|
|
9219
|
+
const entriesRaw = raw.entries;
|
|
9220
|
+
if (!Array.isArray(entriesRaw))
|
|
8719
9221
|
return emptyState();
|
|
9222
|
+
const entries = [];
|
|
9223
|
+
for (const entry of entriesRaw) {
|
|
9224
|
+
if (entries.length >= MAX_ENTRIES2)
|
|
9225
|
+
break;
|
|
9226
|
+
const normalized = sanitizeEntry(entry);
|
|
9227
|
+
if (normalized)
|
|
9228
|
+
entries.push(normalized);
|
|
8720
9229
|
}
|
|
9230
|
+
return { version: 1, entries };
|
|
8721
9231
|
}
|
|
8722
|
-
async function
|
|
8723
|
-
|
|
8724
|
-
await mkdir(dir, { recursive: true });
|
|
8725
|
-
const file = historyFilePath(cwd);
|
|
8726
|
-
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
8727
|
-
const content = serializeHistoryState(state);
|
|
8728
|
-
await writeFile(tmp, content, "utf8");
|
|
8729
|
-
await rename(tmp, file);
|
|
9232
|
+
async function loadQueryHistoryAsync(cwd) {
|
|
9233
|
+
return historyStore.load(cwd);
|
|
8730
9234
|
}
|
|
8731
9235
|
async function updateQueryHistoryAsync(cwd, updater) {
|
|
8732
|
-
|
|
8733
|
-
const run2 = previous.catch(() => {}).then(async () => {
|
|
8734
|
-
const current = await loadQueryHistoryAsyncUnqueued(cwd);
|
|
8735
|
-
const updated = await updater(current);
|
|
8736
|
-
await saveQueryHistoryAsync(cwd, updated.state);
|
|
8737
|
-
return updated.result;
|
|
8738
|
-
});
|
|
8739
|
-
const queued = run2.then(() => {}, () => {});
|
|
8740
|
-
historyWriteQueues.set(cwd, queued);
|
|
8741
|
-
try {
|
|
8742
|
-
return await run2;
|
|
8743
|
-
} finally {
|
|
8744
|
-
if (historyWriteQueues.get(cwd) === queued) {
|
|
8745
|
-
historyWriteQueues.delete(cwd);
|
|
8746
|
-
}
|
|
8747
|
-
}
|
|
9236
|
+
return historyStore.update(cwd, updater);
|
|
8748
9237
|
}
|
|
8749
9238
|
function clampPreviewRows(rows) {
|
|
8750
9239
|
return rows.slice(0, MAX_PREVIEW_ROWS);
|
|
@@ -8780,15 +9269,23 @@ function clearQueryHistory(state, dbId, schema) {
|
|
|
8780
9269
|
})
|
|
8781
9270
|
};
|
|
8782
9271
|
}
|
|
8783
|
-
var
|
|
9272
|
+
var CODE_VIEWER_DIR3 = ".code-viewer", HISTORY_FILE_NAME = "query-history.json", MAX_ENTRIES2 = 200, MAX_PREVIEW_ROWS = 100, MAX_JSON_BYTES = 1e6, MAX_ID_LEN = 128, MAX_DB_ID_LEN = 2048, MAX_SCHEMA_LEN = 512, MAX_SQL_LEN = 64000, MAX_TEXT_LEN = 64000, MAX_COLUMN_LEN = 512, MAX_COLUMNS = 500, historyStore;
|
|
8784
9273
|
var init_query_history = __esm(() => {
|
|
8785
|
-
|
|
9274
|
+
init_json_store();
|
|
9275
|
+
historyStore = createJsonFileStore({
|
|
9276
|
+
filePath: historyFilePath,
|
|
9277
|
+
empty: emptyState,
|
|
9278
|
+
sanitize: sanitizeHistoryState,
|
|
9279
|
+
maxBytes: MAX_JSON_BYTES,
|
|
9280
|
+
backupSuffix: "corrupt",
|
|
9281
|
+
serialize: serializeHistoryState
|
|
9282
|
+
});
|
|
8786
9283
|
});
|
|
8787
9284
|
|
|
8788
9285
|
// web-src/server/database/snapshot-store.ts
|
|
8789
|
-
import { createHash as createHash5, randomBytes } from "node:crypto";
|
|
8790
|
-
import { mkdirSync as
|
|
8791
|
-
import { join as
|
|
9286
|
+
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
9287
|
+
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
9288
|
+
import { join as join11 } from "node:path";
|
|
8792
9289
|
async function getSqliteClass2() {
|
|
8793
9290
|
if (cachedDbClass2)
|
|
8794
9291
|
return cachedDbClass2;
|
|
@@ -8805,7 +9302,7 @@ async function getSqliteClass2() {
|
|
|
8805
9302
|
throw new Error("No SQLite driver available. Install better-sqlite3 or use the bun runtime.");
|
|
8806
9303
|
}
|
|
8807
9304
|
async function getStoreDb(cwd) {
|
|
8808
|
-
const dbPath =
|
|
9305
|
+
const dbPath = join11(cwd, CODE_VIEWER_DIR4, SNAPSHOT_DB_NAME);
|
|
8809
9306
|
if (storeDb && storeDbPath === dbPath)
|
|
8810
9307
|
return storeDb;
|
|
8811
9308
|
if (storeDb) {
|
|
@@ -8813,7 +9310,7 @@ async function getStoreDb(cwd) {
|
|
|
8813
9310
|
storeDb.close();
|
|
8814
9311
|
} catch {}
|
|
8815
9312
|
}
|
|
8816
|
-
|
|
9313
|
+
mkdirSync3(join11(cwd, CODE_VIEWER_DIR4), { recursive: true });
|
|
8817
9314
|
const DbClass = await getSqliteClass2();
|
|
8818
9315
|
storeDb = new DbClass(dbPath);
|
|
8819
9316
|
storeDbPath = dbPath;
|
|
@@ -8826,7 +9323,7 @@ async function getStoreDb(cwd) {
|
|
|
8826
9323
|
return storeDb;
|
|
8827
9324
|
}
|
|
8828
9325
|
function makeId2(prefix) {
|
|
8829
|
-
return `${prefix}-${
|
|
9326
|
+
return `${prefix}-${randomBytes2(8).toString("hex")}`;
|
|
8830
9327
|
}
|
|
8831
9328
|
function hashPayload(payloadJson) {
|
|
8832
9329
|
return createHash5("sha256").update(payloadJson).digest("hex");
|
|
@@ -9073,7 +9570,7 @@ async function computeDiffRows(cwd, beforeId, afterId, table, offset = 0, limit
|
|
|
9073
9570
|
});
|
|
9074
9571
|
return { rows, total };
|
|
9075
9572
|
}
|
|
9076
|
-
var
|
|
9573
|
+
var CODE_VIEWER_DIR4 = ".code-viewer", SNAPSHOT_DB_NAME = "db-snapshots.sqlite", cachedDbClass2 = null, SCHEMA_SQL = `
|
|
9077
9574
|
CREATE TABLE IF NOT EXISTS snapshots (
|
|
9078
9575
|
id TEXT PRIMARY KEY,
|
|
9079
9576
|
db_id TEXT NOT NULL,
|
|
@@ -9170,13 +9667,9 @@ var init_snapshot_runner = __esm(() => {
|
|
|
9170
9667
|
});
|
|
9171
9668
|
|
|
9172
9669
|
// web-src/server/database/tabs-store.ts
|
|
9173
|
-
import {
|
|
9174
|
-
import { join as join11 } from "node:path";
|
|
9670
|
+
import { join as join12 } from "node:path";
|
|
9175
9671
|
function tabsFilePath(root) {
|
|
9176
|
-
return
|
|
9177
|
-
}
|
|
9178
|
-
function isEnoent2(err) {
|
|
9179
|
-
return err?.code === "ENOENT";
|
|
9672
|
+
return join12(root, CODE_VIEWER_DIR5, TABS_FILE_NAME);
|
|
9180
9673
|
}
|
|
9181
9674
|
function emptyState2() {
|
|
9182
9675
|
return { version: 1, tabs: [], activeTabId: null };
|
|
@@ -9244,6 +9737,31 @@ function sanitizeEs(v) {
|
|
|
9244
9737
|
return;
|
|
9245
9738
|
return out;
|
|
9246
9739
|
}
|
|
9740
|
+
function sanitizeS3(v) {
|
|
9741
|
+
if (!v || typeof v !== "object")
|
|
9742
|
+
return;
|
|
9743
|
+
const r = v;
|
|
9744
|
+
const out = {};
|
|
9745
|
+
const bucket = sanitizeOptionalString(r.bucket, MAX_S3_BUCKET_LEN);
|
|
9746
|
+
if (bucket !== undefined)
|
|
9747
|
+
out.bucket = bucket;
|
|
9748
|
+
const prefix = sanitizeOptionalString(r.prefix, MAX_S3_KEY_LEN);
|
|
9749
|
+
if (prefix !== undefined)
|
|
9750
|
+
out.prefix = prefix;
|
|
9751
|
+
const query = sanitizeOptionalString(r.query, MAX_S3_QUERY_LEN);
|
|
9752
|
+
if (query !== undefined)
|
|
9753
|
+
out.query = query;
|
|
9754
|
+
if (r.mode === "prefix" || r.mode === "contains")
|
|
9755
|
+
out.mode = r.mode;
|
|
9756
|
+
if (r.sort === "key-asc" || r.sort === "updated-desc")
|
|
9757
|
+
out.sort = r.sort;
|
|
9758
|
+
const key = sanitizeOptionalString(r.key, MAX_S3_KEY_LEN);
|
|
9759
|
+
if (key !== undefined)
|
|
9760
|
+
out.key = key;
|
|
9761
|
+
if (out.bucket === undefined && out.prefix === undefined && out.query === undefined && out.mode === undefined && out.sort === undefined && out.key === undefined)
|
|
9762
|
+
return;
|
|
9763
|
+
return out;
|
|
9764
|
+
}
|
|
9247
9765
|
function sanitize(input) {
|
|
9248
9766
|
if (!input || typeof input !== "object")
|
|
9249
9767
|
return emptyState2();
|
|
@@ -9265,12 +9783,15 @@ function sanitize(input) {
|
|
|
9265
9783
|
if (seenIds.has(id))
|
|
9266
9784
|
continue;
|
|
9267
9785
|
seenIds.add(id);
|
|
9268
|
-
const dbId = sanitizeOptionalString(tab.dbId,
|
|
9786
|
+
const dbId = sanitizeOptionalString(tab.dbId, MAX_DB_ID_LEN2) ?? null;
|
|
9269
9787
|
if (isToolInternalDbId(dbId))
|
|
9270
9788
|
continue;
|
|
9789
|
+
const schema = sanitizeOptionalString(tab.schema, MAX_SCHEMA_NAME_LEN);
|
|
9271
9790
|
const table = sanitizeOptionalString(tab.table, MAX_TABLE_NAME_LEN) ?? null;
|
|
9272
9791
|
const view = typeof tab.view === "string" && VALID_VIEWS.has(tab.view) ? tab.view : "data";
|
|
9273
9792
|
const out = { id, dbId, table, view };
|
|
9793
|
+
if (schema !== undefined)
|
|
9794
|
+
out.schema = schema;
|
|
9274
9795
|
const sqlDraft = sanitizeOptionalString(tab.sqlDraft, MAX_SQL_DRAFT_LEN);
|
|
9275
9796
|
if (sqlDraft !== undefined)
|
|
9276
9797
|
out.sqlDraft = sqlDraft;
|
|
@@ -9288,6 +9809,9 @@ function sanitize(input) {
|
|
|
9288
9809
|
const es = sanitizeEs(tab.es);
|
|
9289
9810
|
if (es !== undefined)
|
|
9290
9811
|
out.es = es;
|
|
9812
|
+
const s3 = sanitizeS3(tab.s3);
|
|
9813
|
+
if (s3 !== undefined)
|
|
9814
|
+
out.s3 = s3;
|
|
9291
9815
|
tabs.push(out);
|
|
9292
9816
|
}
|
|
9293
9817
|
let activeTabId = sanitizeOptionalString(obj.activeTabId, MAX_TAB_ID_LEN) ?? null;
|
|
@@ -9297,69 +9821,14 @@ function sanitize(input) {
|
|
|
9297
9821
|
return { version: 1, tabs, activeTabId };
|
|
9298
9822
|
}
|
|
9299
9823
|
async function loadTabsAsync(cwd) {
|
|
9300
|
-
|
|
9301
|
-
if (pendingWrite) {
|
|
9302
|
-
await pendingWrite.catch(() => {});
|
|
9303
|
-
}
|
|
9304
|
-
const file = tabsFilePath(cwd);
|
|
9305
|
-
let raw;
|
|
9306
|
-
try {
|
|
9307
|
-
raw = await readFile3(file, "utf8");
|
|
9308
|
-
} catch (err) {
|
|
9309
|
-
if (isEnoent2(err))
|
|
9310
|
-
return emptyState2();
|
|
9311
|
-
await backupInvalidTabsFileAsync(file);
|
|
9312
|
-
return emptyState2();
|
|
9313
|
-
}
|
|
9314
|
-
try {
|
|
9315
|
-
const parsed = JSON.parse(raw);
|
|
9316
|
-
if (!parsed || typeof parsed !== "object" || parsed.version !== 1) {
|
|
9317
|
-
await backupInvalidTabsFileAsync(file);
|
|
9318
|
-
return emptyState2();
|
|
9319
|
-
}
|
|
9320
|
-
return sanitize(parsed);
|
|
9321
|
-
} catch (err) {
|
|
9322
|
-
if (isEnoent2(err))
|
|
9323
|
-
return emptyState2();
|
|
9324
|
-
await backupInvalidTabsFileAsync(file);
|
|
9325
|
-
return emptyState2();
|
|
9326
|
-
}
|
|
9327
|
-
}
|
|
9328
|
-
async function backupInvalidTabsFileAsync(file) {
|
|
9329
|
-
try {
|
|
9330
|
-
await rename2(file, `${file}.bak-${Date.now()}`);
|
|
9331
|
-
} catch {}
|
|
9332
|
-
}
|
|
9333
|
-
async function saveTabsAsyncUnqueued(cwd, state) {
|
|
9334
|
-
const normalized = sanitize(state);
|
|
9335
|
-
const dir = join11(cwd, CODE_VIEWER_DIR4);
|
|
9336
|
-
await mkdir2(dir, { recursive: true });
|
|
9337
|
-
const file = tabsFilePath(cwd);
|
|
9338
|
-
const tmp = `${file}.${makeId(`tmp-${process.pid}`)}`;
|
|
9339
|
-
const content = `${JSON.stringify(normalized, null, 2)}
|
|
9340
|
-
`;
|
|
9341
|
-
if (Buffer.byteLength(content, "utf8") > MAX_JSON_BYTES2) {
|
|
9342
|
-
throw new Error("tabs state too large");
|
|
9343
|
-
}
|
|
9344
|
-
await writeFile2(tmp, content, "utf8");
|
|
9345
|
-
await rename2(tmp, file);
|
|
9824
|
+
return tabsStore.load(cwd);
|
|
9346
9825
|
}
|
|
9347
9826
|
async function saveTabsAsync(cwd, state) {
|
|
9348
|
-
|
|
9349
|
-
const run2 = previous.catch(() => {}).then(() => saveTabsAsyncUnqueued(cwd, state));
|
|
9350
|
-
const queued = run2.then(() => {}, () => {});
|
|
9351
|
-
tabsWriteQueues.set(cwd, queued);
|
|
9352
|
-
try {
|
|
9353
|
-
await run2;
|
|
9354
|
-
} finally {
|
|
9355
|
-
if (tabsWriteQueues.get(cwd) === queued) {
|
|
9356
|
-
tabsWriteQueues.delete(cwd);
|
|
9357
|
-
}
|
|
9358
|
-
}
|
|
9827
|
+
return tabsStore.save(cwd, state);
|
|
9359
9828
|
}
|
|
9360
|
-
var
|
|
9829
|
+
var CODE_VIEWER_DIR5 = ".code-viewer", TABS_FILE_NAME = "tabs.json", MAX_TABS = 64, MAX_JSON_BYTES2 = 1e6, MAX_SQL_DRAFT_LEN = 16000, MAX_ES_QUERY_LEN = 16000, MAX_TAB_ID_LEN = 128, MAX_DB_ID_LEN2 = 2048, MAX_SCHEMA_NAME_LEN = 512, MAX_TABLE_NAME_LEN = 512, MAX_REDIS_KEY_LEN = 1024, MAX_REDIS_KEY_FILTER_LEN = 512, MAX_INDEX_NAME_LEN = 256, MAX_S3_BUCKET_LEN = 256, MAX_S3_KEY_LEN = 2048, MAX_S3_QUERY_LEN = 2048, MAX_CSS_SIZE_LEN = 16, VALID_VIEWS, tabsStore;
|
|
9361
9830
|
var init_tabs_store = __esm(() => {
|
|
9362
|
-
|
|
9831
|
+
init_json_store();
|
|
9363
9832
|
VALID_VIEWS = new Set([
|
|
9364
9833
|
"data",
|
|
9365
9834
|
"query",
|
|
@@ -9368,6 +9837,14 @@ var init_tabs_store = __esm(() => {
|
|
|
9368
9837
|
"search",
|
|
9369
9838
|
"snapshot"
|
|
9370
9839
|
]);
|
|
9840
|
+
tabsStore = createJsonFileStore({
|
|
9841
|
+
filePath: tabsFilePath,
|
|
9842
|
+
empty: emptyState2,
|
|
9843
|
+
sanitize,
|
|
9844
|
+
maxBytes: MAX_JSON_BYTES2,
|
|
9845
|
+
backupSuffix: "bak",
|
|
9846
|
+
sizeErrorMessage: "tabs state too large"
|
|
9847
|
+
});
|
|
9371
9848
|
});
|
|
9372
9849
|
|
|
9373
9850
|
// web-src/server/database/handle.ts
|
|
@@ -9396,7 +9873,7 @@ function sanitizeFilename(name) {
|
|
|
9396
9873
|
function normalizeSchemaParam(value) {
|
|
9397
9874
|
if (value === undefined || value === null || value === "")
|
|
9398
9875
|
return;
|
|
9399
|
-
if (value.length >
|
|
9876
|
+
if (value.length > MAX_SCHEMA_NAME_LEN2) {
|
|
9400
9877
|
return textError("invalid schema parameter", 400);
|
|
9401
9878
|
}
|
|
9402
9879
|
if (hasControlCharacter(value)) {
|
|
@@ -10355,6 +10832,23 @@ async function handleTabsPut(cwd, req) {
|
|
|
10355
10832
|
return textError(`failed to save tabs: ${message}`, 500);
|
|
10356
10833
|
}
|
|
10357
10834
|
}
|
|
10835
|
+
async function handleDbUiGet(cwd) {
|
|
10836
|
+
return jsonLoadResponse(() => loadDbUiState(cwd), "db UI", "failed to load db UI state");
|
|
10837
|
+
}
|
|
10838
|
+
async function handleDbUiPatch(cwd, req) {
|
|
10839
|
+
const body = await parseBoundedJsonBody(req, MAX_DB_UI_BODY_BYTES, "db UI body too large");
|
|
10840
|
+
if (body instanceof Response)
|
|
10841
|
+
return body;
|
|
10842
|
+
try {
|
|
10843
|
+
return json(await patchDbUiState(cwd, body));
|
|
10844
|
+
} catch (err) {
|
|
10845
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10846
|
+
if (message === "db UI state too large")
|
|
10847
|
+
return textError(message, 413);
|
|
10848
|
+
console.error("[code-viewer] db UI error:", err);
|
|
10849
|
+
return textError("failed to save db UI state", 500);
|
|
10850
|
+
}
|
|
10851
|
+
}
|
|
10358
10852
|
async function handleClose(cwd, req, omitDirNames) {
|
|
10359
10853
|
const body = await parsePostJsonBody(req);
|
|
10360
10854
|
if (body instanceof Response)
|
|
@@ -10517,11 +11011,17 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
10517
11011
|
methods: ["GET", "PUT", "POST"],
|
|
10518
11012
|
sideEffect: (m) => m !== "GET",
|
|
10519
11013
|
handler: () => method === "GET" ? handleTabsGet(cwd) : handleTabsPut(cwd, req)
|
|
11014
|
+
},
|
|
11015
|
+
"/_db/ui": {
|
|
11016
|
+
methods: ["GET", "PATCH"],
|
|
11017
|
+
sideEffect: (m) => m !== "GET",
|
|
11018
|
+
handler: () => method === "GET" ? handleDbUiGet(cwd) : handleDbUiPatch(cwd, req)
|
|
10520
11019
|
}
|
|
10521
11020
|
}, sideEffectAllowed, wrapResponse, (err) => handleError("database", "handle database request", err));
|
|
10522
11021
|
}
|
|
10523
|
-
var initialized = false, dockerAdapterCache,
|
|
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;
|
|
10524
11023
|
var init_handle = __esm(() => {
|
|
11024
|
+
init_state_store();
|
|
10525
11025
|
init_docker();
|
|
10526
11026
|
init_docker_utils();
|
|
10527
11027
|
init_sqlite();
|
|
@@ -10575,25 +11075,87 @@ var init_handle = __esm(() => {
|
|
|
10575
11075
|
};
|
|
10576
11076
|
});
|
|
10577
11077
|
|
|
11078
|
+
// web-src/server/state-route.ts
|
|
11079
|
+
var exports_state_route = {};
|
|
11080
|
+
__export(exports_state_route, {
|
|
11081
|
+
handleStateRoute: () => handleStateRoute
|
|
11082
|
+
});
|
|
11083
|
+
async function parseJsonBody(req) {
|
|
11084
|
+
return parseBoundedJsonBody(req, MAX_STATE_PATCH_BODY_BYTES, "state body too large");
|
|
11085
|
+
}
|
|
11086
|
+
async function handleSettingsGet(cwd) {
|
|
11087
|
+
return jsonLoadResponse(() => loadAppSettingsState(cwd), "state", "failed to load settings state");
|
|
11088
|
+
}
|
|
11089
|
+
async function handleSettingsPatch(cwd, req) {
|
|
11090
|
+
const body = await parseJsonBody(req);
|
|
11091
|
+
if (body instanceof Response)
|
|
11092
|
+
return body;
|
|
11093
|
+
try {
|
|
11094
|
+
return json(await patchAppSettingsState(cwd, body));
|
|
11095
|
+
} catch (err) {
|
|
11096
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11097
|
+
if (message === "settings state too large")
|
|
11098
|
+
return textError(message, 413);
|
|
11099
|
+
console.error("[code-viewer] state error:", err);
|
|
11100
|
+
return textError("failed to save settings state", 500);
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
11103
|
+
async function handleViewGet(cwd) {
|
|
11104
|
+
return jsonLoadResponse(() => loadViewState(cwd), "state", "failed to load view state");
|
|
11105
|
+
}
|
|
11106
|
+
async function handleViewPatch(cwd, req) {
|
|
11107
|
+
const body = await parseJsonBody(req);
|
|
11108
|
+
if (body instanceof Response)
|
|
11109
|
+
return body;
|
|
11110
|
+
try {
|
|
11111
|
+
return json(await patchViewState(cwd, body));
|
|
11112
|
+
} catch (err) {
|
|
11113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11114
|
+
if (message === "view state too large")
|
|
11115
|
+
return textError(message, 413);
|
|
11116
|
+
console.error("[code-viewer] state error:", err);
|
|
11117
|
+
return textError("failed to save view state", 500);
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
async function handleStateRoute(req, url, cwd, sideEffectAllowed) {
|
|
11121
|
+
return dispatchRoutes(req, url, {
|
|
11122
|
+
"/_state/settings": {
|
|
11123
|
+
methods: ["GET", "PATCH"],
|
|
11124
|
+
sideEffect: (method) => method !== "GET",
|
|
11125
|
+
handler: () => req.method === "GET" ? handleSettingsGet(cwd) : handleSettingsPatch(cwd, req)
|
|
11126
|
+
},
|
|
11127
|
+
"/_state/view": {
|
|
11128
|
+
methods: ["GET", "PATCH"],
|
|
11129
|
+
sideEffect: (method) => method !== "GET",
|
|
11130
|
+
handler: () => req.method === "GET" ? handleViewGet(cwd) : handleViewPatch(cwd, req)
|
|
11131
|
+
}
|
|
11132
|
+
}, sideEffectAllowed, (res) => res, (err) => handleError("state", "handle state request", err));
|
|
11133
|
+
}
|
|
11134
|
+
var MAX_STATE_PATCH_BODY_BYTES = 1e6;
|
|
11135
|
+
var init_state_route = __esm(() => {
|
|
11136
|
+
init_handle_shared();
|
|
11137
|
+
init_state_store();
|
|
11138
|
+
});
|
|
11139
|
+
|
|
10578
11140
|
// web-src/server/preview.ts
|
|
10579
11141
|
var exports_preview = {};
|
|
10580
11142
|
import {
|
|
10581
11143
|
closeSync as closeSync2,
|
|
10582
11144
|
constants,
|
|
10583
|
-
existsSync as
|
|
11145
|
+
existsSync as existsSync6,
|
|
10584
11146
|
lstatSync as lstatSync4,
|
|
10585
|
-
mkdirSync as
|
|
11147
|
+
mkdirSync as mkdirSync4,
|
|
10586
11148
|
openSync as openSync2,
|
|
10587
|
-
readFileSync as
|
|
11149
|
+
readFileSync as readFileSync4,
|
|
10588
11150
|
realpathSync as realpathSync4,
|
|
10589
|
-
renameSync
|
|
11151
|
+
renameSync,
|
|
10590
11152
|
statSync as statSync3,
|
|
10591
11153
|
unlinkSync as unlinkSync2,
|
|
10592
11154
|
watch,
|
|
10593
|
-
writeFileSync as
|
|
11155
|
+
writeFileSync as writeFileSync2
|
|
10594
11156
|
} from "node:fs";
|
|
10595
11157
|
import { homedir as homedir3 } from "node:os";
|
|
10596
|
-
import { basename as basename3, dirname as
|
|
11158
|
+
import { basename as basename3, dirname as dirname3, extname as extname2, join as join13, relative as relative3 } from "node:path";
|
|
10597
11159
|
function parseCli() {
|
|
10598
11160
|
const rest = [];
|
|
10599
11161
|
for (let i = 2;i < process.argv.length; i++) {
|
|
@@ -10734,10 +11296,10 @@ function staticFile(pathname) {
|
|
|
10734
11296
|
const spec = map[pathname];
|
|
10735
11297
|
if (!spec)
|
|
10736
11298
|
return null;
|
|
10737
|
-
const full =
|
|
10738
|
-
if (!
|
|
11299
|
+
const full = join13(WEB_ROOT, spec[0]);
|
|
11300
|
+
if (!existsSync6(full))
|
|
10739
11301
|
return text("not found", 404);
|
|
10740
|
-
return new Response(
|
|
11302
|
+
return new Response(readFileSync4(full), {
|
|
10741
11303
|
headers: { "Content-Type": spec[1], "Cache-Control": "no-store" }
|
|
10742
11304
|
});
|
|
10743
11305
|
}
|
|
@@ -10968,8 +11530,8 @@ function parseScopeExcludeNamesQuery(value) {
|
|
|
10968
11530
|
return normalizeScopeExcludeNames(names);
|
|
10969
11531
|
}
|
|
10970
11532
|
function loadProjectConfig() {
|
|
10971
|
-
const full =
|
|
10972
|
-
if (!
|
|
11533
|
+
const full = join13(cwd, ".code-viewer.json");
|
|
11534
|
+
if (!existsSync6(full))
|
|
10973
11535
|
return null;
|
|
10974
11536
|
let realCwd;
|
|
10975
11537
|
let realConfig;
|
|
@@ -10979,10 +11541,10 @@ function loadProjectConfig() {
|
|
|
10979
11541
|
} catch {
|
|
10980
11542
|
return null;
|
|
10981
11543
|
}
|
|
10982
|
-
if (
|
|
11544
|
+
if (dirname3(realConfig) !== realCwd || basename3(realConfig) !== ".code-viewer.json")
|
|
10983
11545
|
return null;
|
|
10984
11546
|
try {
|
|
10985
|
-
const parsed = JSON.parse(
|
|
11547
|
+
const parsed = JSON.parse(readFileSync4(realConfig, "utf8"));
|
|
10986
11548
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "version" in parsed && parsed.version !== 1)
|
|
10987
11549
|
return null;
|
|
10988
11550
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
@@ -11033,8 +11595,8 @@ function safeWorktreePath(path) {
|
|
|
11033
11595
|
return null;
|
|
11034
11596
|
if (isGitInternalPath(path))
|
|
11035
11597
|
return null;
|
|
11036
|
-
const full =
|
|
11037
|
-
if (!
|
|
11598
|
+
const full = join13(cwd, path);
|
|
11599
|
+
if (!existsSync6(full))
|
|
11038
11600
|
return null;
|
|
11039
11601
|
let realCwd;
|
|
11040
11602
|
let realFull;
|
|
@@ -11052,7 +11614,7 @@ function safeWorktreePath(path) {
|
|
|
11052
11614
|
return realFull;
|
|
11053
11615
|
}
|
|
11054
11616
|
function worktreePath(path) {
|
|
11055
|
-
return
|
|
11617
|
+
return join13(cwd, path);
|
|
11056
11618
|
}
|
|
11057
11619
|
function safeOpenWorktreePath(path) {
|
|
11058
11620
|
if (path === "") {
|
|
@@ -11068,7 +11630,7 @@ function safeOpenWorktreePath(path) {
|
|
|
11068
11630
|
return safeWorktreePath(path);
|
|
11069
11631
|
}
|
|
11070
11632
|
function parentRepoPath(path) {
|
|
11071
|
-
const parent =
|
|
11633
|
+
const parent = dirname3(path);
|
|
11072
11634
|
return parent === "." ? "" : parent;
|
|
11073
11635
|
}
|
|
11074
11636
|
function isoDate(ms) {
|
|
@@ -11135,7 +11697,7 @@ function readReadme(target, dirPath) {
|
|
|
11135
11697
|
if (!full)
|
|
11136
11698
|
continue;
|
|
11137
11699
|
try {
|
|
11138
|
-
return { path, text:
|
|
11700
|
+
return { path, text: readFileSync4(full, "utf8") };
|
|
11139
11701
|
} catch {
|
|
11140
11702
|
continue;
|
|
11141
11703
|
}
|
|
@@ -11256,7 +11818,7 @@ function grepWorktreeFallback(query, max, paths, omitDirNames, excludeNames) {
|
|
|
11256
11818
|
continue;
|
|
11257
11819
|
let data;
|
|
11258
11820
|
try {
|
|
11259
|
-
data =
|
|
11821
|
+
data = readFileSync4(full);
|
|
11260
11822
|
} catch {
|
|
11261
11823
|
continue;
|
|
11262
11824
|
}
|
|
@@ -11792,10 +12354,10 @@ async function handleUploadFiles(req) {
|
|
|
11792
12354
|
total += file.size;
|
|
11793
12355
|
if (total > MAX_UPLOAD_TOTAL_BYTES)
|
|
11794
12356
|
return text("upload too large", 413);
|
|
11795
|
-
const target =
|
|
11796
|
-
if (relative3(realDir,
|
|
12357
|
+
const target = join13(realDir, safeName);
|
|
12358
|
+
if (relative3(realDir, dirname3(target)) !== "")
|
|
11797
12359
|
return text("invalid filename", 400);
|
|
11798
|
-
if (
|
|
12360
|
+
if (existsSync6(target))
|
|
11799
12361
|
return text("file exists", 409);
|
|
11800
12362
|
uploads.push({ file, name: safeName, target });
|
|
11801
12363
|
}
|
|
@@ -11804,7 +12366,7 @@ async function handleUploadFiles(req) {
|
|
|
11804
12366
|
for (const upload of uploads) {
|
|
11805
12367
|
const fd = openSync2(upload.target, uploadOpenFlags(), 420);
|
|
11806
12368
|
try {
|
|
11807
|
-
|
|
12369
|
+
writeFileSync2(fd, new Uint8Array(await upload.file.arrayBuffer()));
|
|
11808
12370
|
} finally {
|
|
11809
12371
|
closeSync2(fd);
|
|
11810
12372
|
}
|
|
@@ -11914,12 +12476,12 @@ function triggerUpdate(changedPaths) {
|
|
|
11914
12476
|
sendSse("update", data);
|
|
11915
12477
|
}
|
|
11916
12478
|
function moveMacPathIntoTrash(path) {
|
|
11917
|
-
const trashDir =
|
|
12479
|
+
const trashDir = join13(homedir3(), ".Trash");
|
|
11918
12480
|
const base = basename3(path) || "code-viewer-trash-item";
|
|
11919
|
-
const target =
|
|
12481
|
+
const target = join13(trashDir, `${base}-${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`);
|
|
11920
12482
|
try {
|
|
11921
|
-
|
|
11922
|
-
|
|
12483
|
+
mkdirSync4(trashDir, { recursive: true });
|
|
12484
|
+
renameSync(path, target);
|
|
11923
12485
|
return { ok: true, trashPath: target };
|
|
11924
12486
|
} catch (error) {
|
|
11925
12487
|
return { ok: false, error: String(error) };
|
|
@@ -11950,20 +12512,20 @@ function restoreTrashPath(originalPath, trashPath) {
|
|
|
11950
12512
|
if (!parentFullPath)
|
|
11951
12513
|
return { ok: false, error: "invalid restore target" };
|
|
11952
12514
|
const original = worktreePath(originalPath);
|
|
11953
|
-
if (
|
|
12515
|
+
if (existsSync6(original))
|
|
11954
12516
|
return { ok: false, error: "restore target exists" };
|
|
11955
12517
|
if (trashPath) {
|
|
11956
12518
|
if (process.platform !== "darwin")
|
|
11957
12519
|
return { ok: false, error: "invalid trash handle" };
|
|
11958
|
-
if (!
|
|
12520
|
+
if (!existsSync6(trashPath))
|
|
11959
12521
|
return { ok: false, error: "trash item not found" };
|
|
11960
12522
|
try {
|
|
11961
|
-
const trashRoot =
|
|
12523
|
+
const trashRoot = join13(homedir3(), ".Trash");
|
|
11962
12524
|
const trashRelative = relative3(trashRoot, trashPath);
|
|
11963
12525
|
if (trashRelative === "" || trashRelative.startsWith("..") || trashRelative.startsWith("/") || trashRelative.startsWith("\\"))
|
|
11964
12526
|
return { ok: false, error: "invalid trash handle" };
|
|
11965
|
-
|
|
11966
|
-
|
|
12527
|
+
mkdirSync4(dirname3(original), { recursive: true });
|
|
12528
|
+
renameSync(trashPath, original);
|
|
11967
12529
|
return { ok: true };
|
|
11968
12530
|
} catch (error) {
|
|
11969
12531
|
return { ok: false, error: String(error) };
|
|
@@ -12108,11 +12670,11 @@ async function handleCreateDirectory(req) {
|
|
|
12108
12670
|
const targetPath = dir ? `${dir}/${name}` : name;
|
|
12109
12671
|
if (!safeRepoPath(targetPath) || isGitInternalPath(targetPath))
|
|
12110
12672
|
return text("invalid target", 400);
|
|
12111
|
-
const target =
|
|
12112
|
-
if (
|
|
12673
|
+
const target = join13(parent, name);
|
|
12674
|
+
if (existsSync6(target))
|
|
12113
12675
|
return text("already exists", 409);
|
|
12114
12676
|
try {
|
|
12115
|
-
|
|
12677
|
+
mkdirSync4(target, { recursive: false });
|
|
12116
12678
|
} catch (error) {
|
|
12117
12679
|
if (error.code === "EEXIST")
|
|
12118
12680
|
return text("already exists", 409);
|
|
@@ -12158,7 +12720,7 @@ function annotationSse(kind, sessionId, entryId) {
|
|
|
12158
12720
|
}
|
|
12159
12721
|
async function handleAnnotations(req) {
|
|
12160
12722
|
if (req.method === "GET")
|
|
12161
|
-
return json2(loadAnnotationsState(cwd));
|
|
12723
|
+
return json2(await loadAnnotationsState(cwd));
|
|
12162
12724
|
if (req.method !== "POST")
|
|
12163
12725
|
return text("method not allowed", 405);
|
|
12164
12726
|
if (!sideEffectRequestAllowed(req))
|
|
@@ -12182,8 +12744,8 @@ async function handleAnnotations(req) {
|
|
|
12182
12744
|
const action = body.action;
|
|
12183
12745
|
if (action === "start") {
|
|
12184
12746
|
const title = typeof body.title === "string" ? body.title : "";
|
|
12185
|
-
const started = startAnnotationSession(loadAnnotationsState(cwd), title, new Date().toISOString());
|
|
12186
|
-
saveAnnotationsState(cwd, started.state);
|
|
12747
|
+
const started = startAnnotationSession(await loadAnnotationsState(cwd), title, new Date().toISOString());
|
|
12748
|
+
await saveAnnotationsState(cwd, started.state);
|
|
12187
12749
|
annotationSse("start", started.session.id);
|
|
12188
12750
|
return json2({ ok: true, session: started.session });
|
|
12189
12751
|
}
|
|
@@ -12201,7 +12763,7 @@ async function handleAnnotations(req) {
|
|
|
12201
12763
|
if (isGitInternalPath(path) || isCodeViewerInternalPath(path))
|
|
12202
12764
|
return text("forbidden", 403);
|
|
12203
12765
|
}
|
|
12204
|
-
const result = addAnnotationEntry(loadAnnotationsState(cwd), {
|
|
12766
|
+
const result = addAnnotationEntry(await loadAnnotationsState(cwd), {
|
|
12205
12767
|
session_id: typeof body.session_id === "string" ? body.session_id : undefined,
|
|
12206
12768
|
session_title: typeof body.session_title === "string" ? body.session_title : undefined,
|
|
12207
12769
|
path,
|
|
@@ -12216,7 +12778,7 @@ async function handleAnnotations(req) {
|
|
|
12216
12778
|
}, new Date().toISOString());
|
|
12217
12779
|
if (result.ok === false)
|
|
12218
12780
|
return text(result.error, 400);
|
|
12219
|
-
saveAnnotationsState(cwd, result.state);
|
|
12781
|
+
await saveAnnotationsState(cwd, result.state);
|
|
12220
12782
|
annotationSse("add", result.session.id, result.entry.id);
|
|
12221
12783
|
return json2({
|
|
12222
12784
|
ok: true,
|
|
@@ -12230,14 +12792,14 @@ async function handleAnnotations(req) {
|
|
|
12230
12792
|
const id = typeof body.id === "string" ? body.id : "";
|
|
12231
12793
|
if (!id)
|
|
12232
12794
|
return text("invalid id", 400);
|
|
12233
|
-
const result = moveAnnotationEntry(loadAnnotationsState(cwd), id, {
|
|
12795
|
+
const result = moveAnnotationEntry(await loadAnnotationsState(cwd), id, {
|
|
12234
12796
|
before_id: typeof body.before_id === "string" ? body.before_id : undefined,
|
|
12235
12797
|
after_id: typeof body.after_id === "string" ? body.after_id : undefined,
|
|
12236
12798
|
position: typeof body.position === "number" ? body.position : undefined
|
|
12237
12799
|
});
|
|
12238
12800
|
if (result.ok === false)
|
|
12239
12801
|
return text(result.error, 400);
|
|
12240
|
-
saveAnnotationsState(cwd, result.state);
|
|
12802
|
+
await saveAnnotationsState(cwd, result.state);
|
|
12241
12803
|
annotationSse("update", result.session.id, result.entry.id);
|
|
12242
12804
|
return json2({
|
|
12243
12805
|
ok: true,
|
|
@@ -12249,9 +12811,9 @@ async function handleAnnotations(req) {
|
|
|
12249
12811
|
const id = typeof body.id === "string" ? body.id : "";
|
|
12250
12812
|
if (!id)
|
|
12251
12813
|
return text("invalid id", 400);
|
|
12252
|
-
const result = deleteAnnotationById(loadAnnotationsState(cwd), id);
|
|
12814
|
+
const result = deleteAnnotationById(await loadAnnotationsState(cwd), id);
|
|
12253
12815
|
if (result.removed) {
|
|
12254
|
-
saveAnnotationsState(cwd, result.state);
|
|
12816
|
+
await saveAnnotationsState(cwd, result.state);
|
|
12255
12817
|
annotationSse("delete");
|
|
12256
12818
|
}
|
|
12257
12819
|
return json2({ ok: true, removed: result.removed });
|
|
@@ -12261,10 +12823,10 @@ async function handleAnnotations(req) {
|
|
|
12261
12823
|
const title = typeof body.title === "string" ? body.title : "";
|
|
12262
12824
|
if (!id)
|
|
12263
12825
|
return text("invalid id", 400);
|
|
12264
|
-
const result = renameAnnotationSession(loadAnnotationsState(cwd), id, title);
|
|
12826
|
+
const result = renameAnnotationSession(await loadAnnotationsState(cwd), id, title);
|
|
12265
12827
|
if (!result.renamed)
|
|
12266
12828
|
return text("session not found", 404);
|
|
12267
|
-
saveAnnotationsState(cwd, result.state);
|
|
12829
|
+
await saveAnnotationsState(cwd, result.state);
|
|
12268
12830
|
annotationSse("update", id);
|
|
12269
12831
|
return json2({ ok: true });
|
|
12270
12832
|
}
|
|
@@ -12272,18 +12834,18 @@ async function handleAnnotations(req) {
|
|
|
12272
12834
|
const id = typeof body.id === "string" ? body.id : "";
|
|
12273
12835
|
if (!id)
|
|
12274
12836
|
return text("invalid id", 400);
|
|
12275
|
-
const result = updateAnnotationEntry(loadAnnotationsState(cwd), id, {
|
|
12837
|
+
const result = updateAnnotationEntry(await loadAnnotationsState(cwd), id, {
|
|
12276
12838
|
title: typeof body.title === "string" ? body.title : undefined,
|
|
12277
12839
|
body: typeof body.body === "string" ? body.body : undefined
|
|
12278
12840
|
});
|
|
12279
12841
|
if (result.ok === false)
|
|
12280
12842
|
return text(result.error, 400);
|
|
12281
|
-
saveAnnotationsState(cwd, result.state);
|
|
12843
|
+
await saveAnnotationsState(cwd, result.state);
|
|
12282
12844
|
annotationSse("update", undefined, id);
|
|
12283
12845
|
return json2({ ok: true, entry: result.entry });
|
|
12284
12846
|
}
|
|
12285
12847
|
if (action === "clear") {
|
|
12286
|
-
saveAnnotationsState(cwd, emptyAnnotationsState());
|
|
12848
|
+
await saveAnnotationsState(cwd, emptyAnnotationsState());
|
|
12287
12849
|
annotationSse("clear");
|
|
12288
12850
|
return json2({ ok: true });
|
|
12289
12851
|
}
|
|
@@ -12352,8 +12914,8 @@ var init_preview = __esm(async () => {
|
|
|
12352
12914
|
init_search();
|
|
12353
12915
|
init_server_registry();
|
|
12354
12916
|
init_worktree_watcher();
|
|
12355
|
-
WEB_ROOT =
|
|
12356
|
-
VERSION = JSON.parse(
|
|
12917
|
+
WEB_ROOT = join13(ROOT, "web");
|
|
12918
|
+
VERSION = JSON.parse(readFileSync4(join13(ROOT, "package.json"), "utf8")).version;
|
|
12357
12919
|
DEFAULT_ARGS = ["HEAD"];
|
|
12358
12920
|
WATCHED_ASSET_FILES = ["index.html", "style.css", "app.js"];
|
|
12359
12921
|
LINE_INDEX_MAX_FILE_BYTES = 256 * 1024 * 1024;
|
|
@@ -12457,6 +13019,12 @@ var init_preview = __esm(async () => {
|
|
|
12457
13019
|
if (dbResponse)
|
|
12458
13020
|
return dbResponse;
|
|
12459
13021
|
}
|
|
13022
|
+
if (url.pathname.startsWith("/_state/")) {
|
|
13023
|
+
const { handleStateRoute: handleStateRoute2 } = await Promise.resolve().then(() => (init_state_route(), exports_state_route));
|
|
13024
|
+
const stateResponse = await handleStateRoute2(req, url, cwd, sideEffectRequestAllowed);
|
|
13025
|
+
if (stateResponse)
|
|
13026
|
+
return stateResponse;
|
|
13027
|
+
}
|
|
12460
13028
|
if (url.pathname === "/_annotations")
|
|
12461
13029
|
return handleAnnotations(req);
|
|
12462
13030
|
if (url.pathname === "/_refs")
|