@youtyan/code-viewer 0.6.10 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -13
- package/dist/code-viewer.js +1797 -342
- package/package.json +1 -1
- package/web/app.js +810 -18
- package/web/style.css +179 -0
package/dist/code-viewer.js
CHANGED
|
@@ -857,6 +857,88 @@ function runSync(args, cwd, options = {}) {
|
|
|
857
857
|
stderr: appendProcessError(new TextDecoder().decode(proc.stderr || new Uint8Array), proc.error)
|
|
858
858
|
};
|
|
859
859
|
}
|
|
860
|
+
function runAsync(args, cwd, options = {}) {
|
|
861
|
+
return runBytesAsync(args, cwd, options).then((proc) => ({
|
|
862
|
+
code: proc.code,
|
|
863
|
+
stdout: new TextDecoder().decode(proc.stdout),
|
|
864
|
+
stderr: proc.stderr
|
|
865
|
+
}));
|
|
866
|
+
}
|
|
867
|
+
function runBytesAsync(args, cwd, options = {}) {
|
|
868
|
+
const maxBuffer = options.maxBuffer ?? 64 * 1024 * 1024;
|
|
869
|
+
return new Promise((resolve) => {
|
|
870
|
+
const proc = spawn(args[0], args.slice(1), {
|
|
871
|
+
cwd,
|
|
872
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
873
|
+
});
|
|
874
|
+
const stdoutChunks = [];
|
|
875
|
+
const stderrChunks = [];
|
|
876
|
+
let stdoutBytes = 0;
|
|
877
|
+
let stderrBytes = 0;
|
|
878
|
+
let settled = false;
|
|
879
|
+
let timedOut = false;
|
|
880
|
+
let bufferExceeded = false;
|
|
881
|
+
let processError;
|
|
882
|
+
const killSignal = "SIGKILL";
|
|
883
|
+
const timer = options.timeout === undefined ? null : setTimeout(() => {
|
|
884
|
+
timedOut = true;
|
|
885
|
+
proc.kill(killSignal);
|
|
886
|
+
}, options.timeout);
|
|
887
|
+
const finish = (code) => {
|
|
888
|
+
if (settled)
|
|
889
|
+
return;
|
|
890
|
+
settled = true;
|
|
891
|
+
if (timer)
|
|
892
|
+
clearTimeout(timer);
|
|
893
|
+
let stderr = new TextDecoder().decode(concatBytes(stderrChunks));
|
|
894
|
+
if (timedOut) {
|
|
895
|
+
stderr = appendProcessError(stderr, new Error(`spawn ${args[0]} ETIMEDOUT`));
|
|
896
|
+
} else if (bufferExceeded) {
|
|
897
|
+
stderr = appendProcessError(stderr, new Error("stdout maxBuffer exceeded"));
|
|
898
|
+
} else {
|
|
899
|
+
stderr = appendProcessError(stderr, processError);
|
|
900
|
+
}
|
|
901
|
+
resolve({
|
|
902
|
+
code,
|
|
903
|
+
stdout: concatBytes(stdoutChunks),
|
|
904
|
+
stderr
|
|
905
|
+
});
|
|
906
|
+
};
|
|
907
|
+
const collect = (chunks, onBytes) => (chunk) => {
|
|
908
|
+
const bytes = new Uint8Array(chunk);
|
|
909
|
+
chunks.push(bytes);
|
|
910
|
+
onBytes(bytes.byteLength);
|
|
911
|
+
if (!bufferExceeded && (stdoutBytes > maxBuffer || stderrBytes > maxBuffer)) {
|
|
912
|
+
bufferExceeded = true;
|
|
913
|
+
proc.kill(killSignal);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
proc.stdout?.on("data", collect(stdoutChunks, (length) => {
|
|
917
|
+
stdoutBytes += length;
|
|
918
|
+
}));
|
|
919
|
+
proc.stderr?.on("data", collect(stderrChunks, (length) => {
|
|
920
|
+
stderrBytes += length;
|
|
921
|
+
}));
|
|
922
|
+
proc.on("error", (err) => {
|
|
923
|
+
processError = err;
|
|
924
|
+
});
|
|
925
|
+
proc.on("close", (code) => {
|
|
926
|
+
finish(timedOut || bufferExceeded ? 1 : code ?? (processError ? 1 : 0));
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
function concatBytes(chunks) {
|
|
931
|
+
if (chunks.length === 0)
|
|
932
|
+
return new Uint8Array;
|
|
933
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
934
|
+
const out = new Uint8Array(total);
|
|
935
|
+
let offset = 0;
|
|
936
|
+
for (const chunk of chunks) {
|
|
937
|
+
out.set(chunk, offset);
|
|
938
|
+
offset += chunk.byteLength;
|
|
939
|
+
}
|
|
940
|
+
return out;
|
|
941
|
+
}
|
|
860
942
|
function spawnDetached(args) {
|
|
861
943
|
const child = spawn(args[0], args.slice(1), {
|
|
862
944
|
detached: true,
|
|
@@ -1058,6 +1140,11 @@ function run(args, cwd) {
|
|
|
1058
1140
|
timeout: GIT_COMMAND_TIMEOUT_MS
|
|
1059
1141
|
});
|
|
1060
1142
|
}
|
|
1143
|
+
function runGitAsync(args, cwd) {
|
|
1144
|
+
return runAsync(resolveGitArgs(args), cwd, {
|
|
1145
|
+
timeout: GIT_COMMAND_TIMEOUT_MS
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1061
1148
|
function resolveGitArgs(args) {
|
|
1062
1149
|
if (args[0] !== "git")
|
|
1063
1150
|
return args;
|
|
@@ -1081,6 +1168,10 @@ function runGitRefLookup(args, cwd) {
|
|
|
1081
1168
|
const res = run(args, cwd);
|
|
1082
1169
|
return res.code === 0 ? res.stdout.trimEnd() : null;
|
|
1083
1170
|
}
|
|
1171
|
+
async function runGitRefLookupAsync(args, cwd) {
|
|
1172
|
+
const res = await runGitAsync(args, cwd);
|
|
1173
|
+
return res.code === 0 ? res.stdout.trimEnd() : null;
|
|
1174
|
+
}
|
|
1084
1175
|
function repoRoot(cwd) {
|
|
1085
1176
|
return runGitRefLookup(["git", "rev-parse", "--show-toplevel"], cwd);
|
|
1086
1177
|
}
|
|
@@ -1099,14 +1190,17 @@ function repoRootResult(cwd) {
|
|
|
1099
1190
|
function currentBranch(cwd) {
|
|
1100
1191
|
return runGitRefLookup(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
1101
1192
|
}
|
|
1102
|
-
function
|
|
1103
|
-
|
|
1193
|
+
function currentBranchAsync(cwd) {
|
|
1194
|
+
return runGitRefLookupAsync(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
1195
|
+
}
|
|
1196
|
+
async function verifyCommitAsync(ref, cwd) {
|
|
1197
|
+
const res = await runGitAsync(["git", "rev-parse", "--verify", `${ref}^{commit}`], cwd);
|
|
1104
1198
|
if (res.code === 0)
|
|
1105
1199
|
return { ok: true, sha: res.stdout.trim() };
|
|
1106
1200
|
return { ok: false, error: gitFailureMessage(res, "unknown ref") };
|
|
1107
1201
|
}
|
|
1108
|
-
function
|
|
1109
|
-
const res =
|
|
1202
|
+
async function statusPorcelainForPathAsync(path, cwd) {
|
|
1203
|
+
const res = await runGitAsync([
|
|
1110
1204
|
"git",
|
|
1111
1205
|
"-c",
|
|
1112
1206
|
"core.quotepath=false",
|
|
@@ -1127,63 +1221,63 @@ function statusPorcelainForPath(path, cwd) {
|
|
|
1127
1221
|
function show(ref, path, cwd) {
|
|
1128
1222
|
return run(["git", "show", `${ref}:${path}`], cwd);
|
|
1129
1223
|
}
|
|
1224
|
+
function showAsync(ref, path, cwd) {
|
|
1225
|
+
return runGitAsync(["git", "show", `${ref}:${path}`], cwd);
|
|
1226
|
+
}
|
|
1130
1227
|
function catFileBlobStream(oid, cwd) {
|
|
1131
1228
|
return spawnStream(resolveGitArgs(["git", "cat-file", "blob", oid]), cwd);
|
|
1132
1229
|
}
|
|
1133
|
-
function
|
|
1134
|
-
const res =
|
|
1230
|
+
async function objectSizeAsync(ref, path, cwd) {
|
|
1231
|
+
const res = await runGitAsync(["git", "cat-file", "-s", `${ref}:${path}`], cwd);
|
|
1135
1232
|
return {
|
|
1136
1233
|
code: res.code,
|
|
1137
1234
|
size: Number(res.stdout.trim()) || 0,
|
|
1138
1235
|
stderr: res.stderr
|
|
1139
1236
|
};
|
|
1140
1237
|
}
|
|
1141
|
-
function
|
|
1142
|
-
const res =
|
|
1238
|
+
async function objectByteSizeAsync(oid, cwd) {
|
|
1239
|
+
const res = await runGitAsync(["git", "cat-file", "-s", oid], cwd);
|
|
1143
1240
|
return {
|
|
1144
1241
|
code: res.code,
|
|
1145
1242
|
size: Number(res.stdout.trim()) || 0,
|
|
1146
1243
|
stderr: res.stderr
|
|
1147
1244
|
};
|
|
1148
1245
|
}
|
|
1149
|
-
function
|
|
1246
|
+
async function lastCommitDateForPathAsync(ref, path, cwd) {
|
|
1150
1247
|
const args = ["git", "log", "-1", "--format=%cI", ref, "--", path];
|
|
1151
|
-
const res =
|
|
1248
|
+
const res = await runGitAsync(args, cwd);
|
|
1152
1249
|
if (res.code !== 0)
|
|
1153
1250
|
return null;
|
|
1154
1251
|
return res.stdout.trim() || null;
|
|
1155
1252
|
}
|
|
1156
|
-
function
|
|
1157
|
-
const res =
|
|
1253
|
+
async function objectIdAsync(ref, path, cwd) {
|
|
1254
|
+
const res = await runGitAsync(["git", "rev-parse", "--verify", `${ref}:${path}`], cwd);
|
|
1158
1255
|
const oid = res.stdout.trim();
|
|
1159
1256
|
if (res.code !== 0 || !oid)
|
|
1160
1257
|
return { code: res.code || 1, oid: "", stderr: res.stderr };
|
|
1161
|
-
const type =
|
|
1258
|
+
const type = await runGitAsync(["git", "cat-file", "-t", oid], cwd);
|
|
1162
1259
|
if (type.code !== 0 || type.stdout.trim() !== "blob")
|
|
1163
1260
|
return { code: 1, oid: "", stderr: type.stderr };
|
|
1164
1261
|
return { code: 0, oid, stderr: "" };
|
|
1165
1262
|
}
|
|
1166
|
-
function
|
|
1167
|
-
return verifyTreeRefResult(ref, cwd).ok;
|
|
1168
|
-
}
|
|
1169
|
-
function verifyTreeRefResult(ref, cwd) {
|
|
1263
|
+
async function verifyTreeRefResultAsync(ref, cwd) {
|
|
1170
1264
|
if (!ref || ref === "worktree")
|
|
1171
1265
|
return { ok: false, error: "invalid target", status: 400 };
|
|
1172
1266
|
if (ref.startsWith("-"))
|
|
1173
1267
|
return { ok: false, error: "invalid target", status: 400 };
|
|
1174
|
-
const res =
|
|
1268
|
+
const res = await runGitAsync(["git", "rev-parse", "--verify", `${ref}^{tree}`], cwd);
|
|
1175
1269
|
if (res.code === 0)
|
|
1176
1270
|
return { ok: true };
|
|
1177
1271
|
return { ok: false, ...gitFailureResult(res, "invalid target") };
|
|
1178
1272
|
}
|
|
1179
|
-
function
|
|
1273
|
+
async function refsResultAsync(cwd) {
|
|
1180
1274
|
const out = {
|
|
1181
1275
|
branches: [],
|
|
1182
1276
|
tags: [],
|
|
1183
1277
|
commits: [],
|
|
1184
1278
|
current: ""
|
|
1185
1279
|
};
|
|
1186
|
-
const branches =
|
|
1280
|
+
const branches = await runGitAsync([
|
|
1187
1281
|
"git",
|
|
1188
1282
|
"for-each-ref",
|
|
1189
1283
|
"--sort=-committerdate",
|
|
@@ -1203,7 +1297,7 @@ function refsResult(cwd) {
|
|
|
1203
1297
|
out.branches.push({ name, when });
|
|
1204
1298
|
}
|
|
1205
1299
|
}
|
|
1206
|
-
const tags =
|
|
1300
|
+
const tags = await runGitAsync([
|
|
1207
1301
|
"git",
|
|
1208
1302
|
"for-each-ref",
|
|
1209
1303
|
"--sort=-creatordate",
|
|
@@ -1222,7 +1316,7 @@ function refsResult(cwd) {
|
|
|
1222
1316
|
out.tags.push({ name, when });
|
|
1223
1317
|
}
|
|
1224
1318
|
}
|
|
1225
|
-
const commits =
|
|
1319
|
+
const commits = await refCommitPageResultAsync(cwd, {
|
|
1226
1320
|
query: "",
|
|
1227
1321
|
max: DEFAULT_REF_COMMIT_LIMIT
|
|
1228
1322
|
});
|
|
@@ -1230,7 +1324,7 @@ function refsResult(cwd) {
|
|
|
1230
1324
|
return { refs: out, error: commits.error, status: commits.status };
|
|
1231
1325
|
}
|
|
1232
1326
|
out.commits = commits.commits;
|
|
1233
|
-
out.current =
|
|
1327
|
+
out.current = await currentBranchAsync(cwd) || "";
|
|
1234
1328
|
return { refs: out };
|
|
1235
1329
|
}
|
|
1236
1330
|
function clampCommitLimit(max) {
|
|
@@ -1284,22 +1378,22 @@ function mergeCommitResults(limit, ...groups) {
|
|
|
1284
1378
|
}
|
|
1285
1379
|
return merged;
|
|
1286
1380
|
}
|
|
1287
|
-
function
|
|
1288
|
-
const commits =
|
|
1381
|
+
async function runCommitLogResultAsync(cwd, args) {
|
|
1382
|
+
const commits = await runGitAsync(args, cwd);
|
|
1289
1383
|
if (commits.code === 0)
|
|
1290
1384
|
return { commits: parseCommitLog(commits.stdout) };
|
|
1291
1385
|
if (isCommandNotFoundResult("git", commits))
|
|
1292
1386
|
return { commits: [], ...gitFailureResult(commits, "git log failed") };
|
|
1293
1387
|
return { commits: [] };
|
|
1294
1388
|
}
|
|
1295
|
-
function
|
|
1389
|
+
async function refCommitPageResultAsync(cwd, options = {}) {
|
|
1296
1390
|
const limit = clampCommitLimit(options.max ?? DEFAULT_REF_COMMIT_LIMIT);
|
|
1297
1391
|
const skip = clampCommitSkip(options.skip ?? 0);
|
|
1298
1392
|
const fetchLimit = limit + 1;
|
|
1299
1393
|
const hashMatches = [];
|
|
1300
1394
|
const trimmed = (options.query || "").trim().slice(0, 200).replace(/\0/g, "");
|
|
1301
1395
|
if (skip === 0 && /^[0-9a-f]{4,40}$/i.test(trimmed)) {
|
|
1302
|
-
const verified =
|
|
1396
|
+
const verified = await runGitAsync(["git", "rev-parse", "--verify", `${trimmed}^{commit}`], cwd);
|
|
1303
1397
|
if (verified.code !== 0 && isCommandNotFoundResult("git", verified)) {
|
|
1304
1398
|
return {
|
|
1305
1399
|
commits: [],
|
|
@@ -1307,7 +1401,7 @@ function refCommitPageResult(cwd, options = {}) {
|
|
|
1307
1401
|
...gitFailureResult(verified, "unknown ref")
|
|
1308
1402
|
};
|
|
1309
1403
|
}
|
|
1310
|
-
const single =
|
|
1404
|
+
const single = await runGitAsync([
|
|
1311
1405
|
"git",
|
|
1312
1406
|
"log",
|
|
1313
1407
|
"-z",
|
|
@@ -1327,7 +1421,7 @@ function refCommitPageResult(cwd, options = {}) {
|
|
|
1327
1421
|
}
|
|
1328
1422
|
}
|
|
1329
1423
|
if (!trimmed) {
|
|
1330
|
-
const result =
|
|
1424
|
+
const result = await runCommitLogResultAsync(cwd, commitLogArgs(fetchLimit, skip));
|
|
1331
1425
|
if (result.error) {
|
|
1332
1426
|
return {
|
|
1333
1427
|
commits: [],
|
|
@@ -1342,11 +1436,19 @@ function refCommitPageResult(cwd, options = {}) {
|
|
|
1342
1436
|
hasMore: commits.length > limit
|
|
1343
1437
|
};
|
|
1344
1438
|
}
|
|
1345
|
-
const subjectMatches =
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1439
|
+
const [subjectMatches, authorMatches] = await Promise.all([
|
|
1440
|
+
runCommitLogResultAsync(cwd, [
|
|
1441
|
+
...commitLogArgs(fetchLimit, skip),
|
|
1442
|
+
"--regexp-ignore-case",
|
|
1443
|
+
"--fixed-strings",
|
|
1444
|
+
`--grep=${trimmed}`
|
|
1445
|
+
]),
|
|
1446
|
+
runCommitLogResultAsync(cwd, [
|
|
1447
|
+
...commitLogArgs(fetchLimit, skip),
|
|
1448
|
+
"--regexp-ignore-case",
|
|
1449
|
+
"--fixed-strings",
|
|
1450
|
+
`--author=${trimmed}`
|
|
1451
|
+
])
|
|
1350
1452
|
]);
|
|
1351
1453
|
if (subjectMatches.error) {
|
|
1352
1454
|
return {
|
|
@@ -1356,12 +1458,6 @@ function refCommitPageResult(cwd, options = {}) {
|
|
|
1356
1458
|
status: subjectMatches.status
|
|
1357
1459
|
};
|
|
1358
1460
|
}
|
|
1359
|
-
const authorMatches = runCommitLogResult(cwd, [
|
|
1360
|
-
...commitLogArgs(fetchLimit, skip),
|
|
1361
|
-
"--regexp-ignore-case",
|
|
1362
|
-
"--fixed-strings",
|
|
1363
|
-
`--author=${trimmed}`
|
|
1364
|
-
]);
|
|
1365
1461
|
if (authorMatches.error) {
|
|
1366
1462
|
return {
|
|
1367
1463
|
commits: [],
|
|
@@ -1397,6 +1493,12 @@ function remoteWebUrl(cwd) {
|
|
|
1397
1493
|
return null;
|
|
1398
1494
|
return parseRemoteWebUrl(res.stdout.trim());
|
|
1399
1495
|
}
|
|
1496
|
+
async function remoteWebUrlAsync(cwd) {
|
|
1497
|
+
const res = await runGitAsync(["git", "remote", "get-url", "origin"], cwd);
|
|
1498
|
+
if (res.code !== 0)
|
|
1499
|
+
return null;
|
|
1500
|
+
return parseRemoteWebUrl(res.stdout.trim());
|
|
1501
|
+
}
|
|
1400
1502
|
function parseHistoryLog(stdout) {
|
|
1401
1503
|
const parts = stdout.split("\x00");
|
|
1402
1504
|
const commits = [];
|
|
@@ -1513,6 +1615,60 @@ function commitHistory(cwd, options) {
|
|
|
1513
1615
|
const hasMore = parsed.length > limit;
|
|
1514
1616
|
return { commits: hasMore ? parsed.slice(0, limit) : parsed, hasMore };
|
|
1515
1617
|
}
|
|
1618
|
+
async function commitHistoryAsync(cwd, options) {
|
|
1619
|
+
const ref = (options.ref || "HEAD").trim();
|
|
1620
|
+
if (!ref || ref.startsWith("-") || ref.includes("\x00"))
|
|
1621
|
+
return { commits: [], hasMore: false, error: "invalid ref" };
|
|
1622
|
+
const verified = await runGitAsync(["git", "rev-parse", "--verify", `${ref}^{commit}`], cwd);
|
|
1623
|
+
if (verified.code !== 0)
|
|
1624
|
+
return {
|
|
1625
|
+
commits: [],
|
|
1626
|
+
hasMore: false,
|
|
1627
|
+
...gitFailureResult(verified, "unknown ref")
|
|
1628
|
+
};
|
|
1629
|
+
const skip = Math.max(0, Math.floor(options.skip) || 0);
|
|
1630
|
+
const limit = Math.max(1, Math.min(Math.floor(options.limit) || 1, MAX_HISTORY_LIMIT));
|
|
1631
|
+
const { filterArgs, pathspec, shaTerm } = historyQueryArgs(options.query || "");
|
|
1632
|
+
const pathFilter = (options.path || "").trim();
|
|
1633
|
+
const pathArgs = [];
|
|
1634
|
+
if (pathFilter && !pathFilter.includes("\x00") && !pathFilter.startsWith("-")) {
|
|
1635
|
+
if (!pathFilter.endsWith("/"))
|
|
1636
|
+
pathArgs.push("--follow");
|
|
1637
|
+
pathArgs.push("--", pathFilter);
|
|
1638
|
+
}
|
|
1639
|
+
const res = await runGitAsync([
|
|
1640
|
+
"git",
|
|
1641
|
+
"log",
|
|
1642
|
+
"-z",
|
|
1643
|
+
`--skip=${skip}`,
|
|
1644
|
+
`--max-count=${limit + 1}`,
|
|
1645
|
+
`--format=${HISTORY_FORMAT}`,
|
|
1646
|
+
...filterArgs,
|
|
1647
|
+
verified.stdout.trim(),
|
|
1648
|
+
...pathspec,
|
|
1649
|
+
...pathArgs
|
|
1650
|
+
], cwd);
|
|
1651
|
+
if (res.code !== 0)
|
|
1652
|
+
return {
|
|
1653
|
+
commits: [],
|
|
1654
|
+
hasMore: false,
|
|
1655
|
+
...gitFailureResult(res, "git log failed")
|
|
1656
|
+
};
|
|
1657
|
+
let parsed = parseHistoryLog(res.stdout);
|
|
1658
|
+
if (shaTerm && skip === 0) {
|
|
1659
|
+
const bySha = await runGitAsync(["git", "rev-parse", "--verify", `${shaTerm}^{commit}`], cwd);
|
|
1660
|
+
const sha = bySha.code === 0 ? bySha.stdout.trim() : "";
|
|
1661
|
+
if (sha) {
|
|
1662
|
+
const single = await runGitAsync(["git", "log", "-z", "-1", `--format=${HISTORY_FORMAT}`, sha], cwd);
|
|
1663
|
+
if (single.code === 0) {
|
|
1664
|
+
const hit = parseHistoryLog(single.stdout);
|
|
1665
|
+
parsed = [...hit, ...parsed.filter((c) => c.sha !== sha)];
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
const hasMore = parsed.length > limit;
|
|
1670
|
+
return { commits: hasMore ? parsed.slice(0, limit) : parsed, hasMore };
|
|
1671
|
+
}
|
|
1516
1672
|
function nameStatusResult(args, cwd) {
|
|
1517
1673
|
const res = run([
|
|
1518
1674
|
"git",
|
|
@@ -1557,6 +1713,50 @@ function nameStatusResult(args, cwd) {
|
|
|
1557
1713
|
}
|
|
1558
1714
|
return { files };
|
|
1559
1715
|
}
|
|
1716
|
+
async function nameStatusResultAsync(args, cwd) {
|
|
1717
|
+
const res = await runGitAsync([
|
|
1718
|
+
"git",
|
|
1719
|
+
"-c",
|
|
1720
|
+
"core.quotepath=false",
|
|
1721
|
+
"diff",
|
|
1722
|
+
"--no-color",
|
|
1723
|
+
"--no-ext-diff",
|
|
1724
|
+
"--find-renames",
|
|
1725
|
+
"--name-status",
|
|
1726
|
+
"-z",
|
|
1727
|
+
...args
|
|
1728
|
+
], cwd);
|
|
1729
|
+
if (res.code !== 0) {
|
|
1730
|
+
return {
|
|
1731
|
+
files: [],
|
|
1732
|
+
error: gitFailureMessage(res, "git diff --name-status failed")
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
const parts = res.stdout.split("\x00");
|
|
1736
|
+
const files = [];
|
|
1737
|
+
for (let i = 0;i < parts.length; ) {
|
|
1738
|
+
const status = parts[i++];
|
|
1739
|
+
if (!status)
|
|
1740
|
+
break;
|
|
1741
|
+
const kind = status[0];
|
|
1742
|
+
if (kind === "R" || kind === "C") {
|
|
1743
|
+
const oldPath = parts[i++] || "";
|
|
1744
|
+
const path = parts[i++] || "";
|
|
1745
|
+
if (path)
|
|
1746
|
+
files.push({
|
|
1747
|
+
status: kind,
|
|
1748
|
+
old_path: oldPath,
|
|
1749
|
+
path,
|
|
1750
|
+
similarity: Number(status.slice(1)) || undefined
|
|
1751
|
+
});
|
|
1752
|
+
} else {
|
|
1753
|
+
const path = parts[i++] || "";
|
|
1754
|
+
if (path)
|
|
1755
|
+
files.push({ status: kind, path });
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return { files };
|
|
1759
|
+
}
|
|
1560
1760
|
function numstatZResult(args, cwd) {
|
|
1561
1761
|
const res = run([
|
|
1562
1762
|
"git",
|
|
@@ -1600,6 +1800,49 @@ function numstatZResult(args, cwd) {
|
|
|
1600
1800
|
}
|
|
1601
1801
|
return { files };
|
|
1602
1802
|
}
|
|
1803
|
+
async function numstatZResultAsync(args, cwd) {
|
|
1804
|
+
const res = await runGitAsync([
|
|
1805
|
+
"git",
|
|
1806
|
+
"-c",
|
|
1807
|
+
"core.quotepath=false",
|
|
1808
|
+
"diff",
|
|
1809
|
+
"--no-color",
|
|
1810
|
+
"--no-ext-diff",
|
|
1811
|
+
"--find-renames",
|
|
1812
|
+
"--numstat",
|
|
1813
|
+
"-z",
|
|
1814
|
+
...args
|
|
1815
|
+
], cwd);
|
|
1816
|
+
if (res.code !== 0) {
|
|
1817
|
+
return {
|
|
1818
|
+
files: [],
|
|
1819
|
+
error: gitFailureMessage(res, "git diff --numstat failed")
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
const parts = res.stdout.split("\x00");
|
|
1823
|
+
const files = [];
|
|
1824
|
+
for (let i = 0;i < parts.length; ) {
|
|
1825
|
+
const rec = parts[i++];
|
|
1826
|
+
if (!rec)
|
|
1827
|
+
break;
|
|
1828
|
+
const match = rec.match(/^(\S+)\t(\S+)\t(.*)$/);
|
|
1829
|
+
if (!match)
|
|
1830
|
+
break;
|
|
1831
|
+
const [, add, del, rest] = match;
|
|
1832
|
+
const binary = add === "-" && del === "-";
|
|
1833
|
+
const additions = binary ? 0 : Number(add) || 0;
|
|
1834
|
+
const deletions = binary ? 0 : Number(del) || 0;
|
|
1835
|
+
if (rest === "") {
|
|
1836
|
+
const oldPath = parts[i++] || "";
|
|
1837
|
+
const path = parts[i++] || "";
|
|
1838
|
+
if (path)
|
|
1839
|
+
files.push({ old_path: oldPath, path, additions, deletions, binary });
|
|
1840
|
+
} else {
|
|
1841
|
+
files.push({ path: rest, additions, deletions, binary });
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
return { files };
|
|
1845
|
+
}
|
|
1603
1846
|
function pathHasSegment(path, segment) {
|
|
1604
1847
|
const target = segment.toLowerCase();
|
|
1605
1848
|
return path.split(/[\\/]+/).some((part) => part.toLowerCase() === target);
|
|
@@ -1742,6 +1985,98 @@ function blame(cwd, options) {
|
|
|
1742
1985
|
lines.sort((a, b) => a.lineNo - b.lineNo);
|
|
1743
1986
|
return { lines, commits };
|
|
1744
1987
|
}
|
|
1988
|
+
async function blameAsync(cwd, options) {
|
|
1989
|
+
const path = options.path;
|
|
1990
|
+
if (!path || path.includes("\x00") || path.startsWith("-")) {
|
|
1991
|
+
return { lines: [], commits: {}, error: "invalid path" };
|
|
1992
|
+
}
|
|
1993
|
+
const normalized = normalizeBlameRef(options.ref, options.base);
|
|
1994
|
+
const args = ["git", "blame", "--porcelain"];
|
|
1995
|
+
if (normalized.base === "HEAD") {
|
|
1996
|
+
if (normalized.ref.startsWith("-") || normalized.ref.includes("\x00"))
|
|
1997
|
+
return { lines: [], commits: {}, error: "invalid ref" };
|
|
1998
|
+
args.push(normalized.ref);
|
|
1999
|
+
}
|
|
2000
|
+
args.push("--", path);
|
|
2001
|
+
const res = await runGitAsync(args, cwd);
|
|
2002
|
+
if (res.code !== 0) {
|
|
2003
|
+
if (isCommandNotFoundResult("git", res)) {
|
|
2004
|
+
return {
|
|
2005
|
+
lines: [],
|
|
2006
|
+
commits: {},
|
|
2007
|
+
error: commandNotFoundDetail("git"),
|
|
2008
|
+
status: 503
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
if (normalized.base === "worktree") {
|
|
2012
|
+
return syntheticUncommittedBlameFromWorktree(cwd, path);
|
|
2013
|
+
}
|
|
2014
|
+
return {
|
|
2015
|
+
lines: [],
|
|
2016
|
+
commits: {},
|
|
2017
|
+
error: res.stderr.trim() || "blame failed"
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
const lines = [];
|
|
2021
|
+
const commits = {};
|
|
2022
|
+
const rawLines = res.stdout.split(`
|
|
2023
|
+
`);
|
|
2024
|
+
let i = 0;
|
|
2025
|
+
while (i < rawLines.length) {
|
|
2026
|
+
const headerLine = rawLines[i];
|
|
2027
|
+
if (!headerLine) {
|
|
2028
|
+
i++;
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
const headerMatch = /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(headerLine);
|
|
2032
|
+
if (!headerMatch) {
|
|
2033
|
+
i++;
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
const sha = headerMatch[1];
|
|
2037
|
+
const finalLine = Number(headerMatch[3]);
|
|
2038
|
+
i++;
|
|
2039
|
+
let commit = commits[sha];
|
|
2040
|
+
if (!commit) {
|
|
2041
|
+
commit = {
|
|
2042
|
+
sha,
|
|
2043
|
+
author: "",
|
|
2044
|
+
authorMail: "",
|
|
2045
|
+
authorTime: 0,
|
|
2046
|
+
summary: "",
|
|
2047
|
+
isUncommitted: sha === BLAME_ZERO_SHA
|
|
2048
|
+
};
|
|
2049
|
+
commits[sha] = commit;
|
|
2050
|
+
}
|
|
2051
|
+
while (i < rawLines.length && !rawLines[i].startsWith("\t")) {
|
|
2052
|
+
const metaLine = rawLines[i++];
|
|
2053
|
+
if (!metaLine)
|
|
2054
|
+
continue;
|
|
2055
|
+
const sp = metaLine.indexOf(" ");
|
|
2056
|
+
const key = sp >= 0 ? metaLine.slice(0, sp) : metaLine;
|
|
2057
|
+
const val = sp >= 0 ? metaLine.slice(sp + 1) : "";
|
|
2058
|
+
if (key === "author" && !commit.author)
|
|
2059
|
+
commit.author = val;
|
|
2060
|
+
else if (key === "author-mail" && !commit.authorMail)
|
|
2061
|
+
commit.authorMail = val.replace(/^</, "").replace(/>$/, "");
|
|
2062
|
+
else if (key === "author-time" && !commit.authorTime)
|
|
2063
|
+
commit.authorTime = Number(val) || 0;
|
|
2064
|
+
else if (key === "summary" && !commit.summary)
|
|
2065
|
+
commit.summary = val;
|
|
2066
|
+
}
|
|
2067
|
+
if (i < rawLines.length && rawLines[i].startsWith("\t"))
|
|
2068
|
+
i++;
|
|
2069
|
+
if (Number.isFinite(finalLine) && finalLine > 0) {
|
|
2070
|
+
lines.push({
|
|
2071
|
+
lineNo: finalLine,
|
|
2072
|
+
sha,
|
|
2073
|
+
isUncommitted: sha === BLAME_ZERO_SHA
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
lines.sort((a, b) => a.lineNo - b.lineNo);
|
|
2078
|
+
return { lines, commits };
|
|
2079
|
+
}
|
|
1745
2080
|
function untracked(cwd, path = "") {
|
|
1746
2081
|
const args = ["git", "ls-files", "--others", "--exclude-standard"];
|
|
1747
2082
|
if (path)
|
|
@@ -1752,6 +2087,16 @@ function untracked(cwd, path = "") {
|
|
|
1752
2087
|
return res.stdout.split(`
|
|
1753
2088
|
`).filter(Boolean).filter((entry) => !isToolInternalPath(entry));
|
|
1754
2089
|
}
|
|
2090
|
+
async function untrackedAsync(cwd, path = "") {
|
|
2091
|
+
const args = ["git", "ls-files", "--others", "--exclude-standard"];
|
|
2092
|
+
if (path)
|
|
2093
|
+
args.push("--", `${path}/`);
|
|
2094
|
+
const res = await runGitAsync(args, cwd);
|
|
2095
|
+
if (res.code !== 0)
|
|
2096
|
+
return [];
|
|
2097
|
+
return res.stdout.split(`
|
|
2098
|
+
`).filter(Boolean).filter((entry) => !isToolInternalPath(entry));
|
|
2099
|
+
}
|
|
1755
2100
|
function normalizeTreePath(path) {
|
|
1756
2101
|
return path.replace(/^\/+|\/+$/g, "");
|
|
1757
2102
|
}
|
|
@@ -1779,6 +2124,18 @@ function worktreeSubmodulePaths(cwd) {
|
|
|
1779
2124
|
return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
|
|
1780
2125
|
}).filter(Boolean));
|
|
1781
2126
|
}
|
|
2127
|
+
async function worktreeSubmodulePathsAsync(cwd) {
|
|
2128
|
+
if (!existsSync(join3(cwd, ".gitmodules")))
|
|
2129
|
+
return new Set;
|
|
2130
|
+
const res = await runGitAsync(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
2131
|
+
if (res.code !== 0)
|
|
2132
|
+
return new Set;
|
|
2133
|
+
return new Set(res.stdout.split(`
|
|
2134
|
+
`).map((line) => {
|
|
2135
|
+
const split = line.indexOf(" ");
|
|
2136
|
+
return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
|
|
2137
|
+
}).filter(Boolean));
|
|
2138
|
+
}
|
|
1782
2139
|
function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, excludeNames, submodulePaths) {
|
|
1783
2140
|
if (excludeNames.has(name.toLowerCase()))
|
|
1784
2141
|
return {
|
|
@@ -1876,6 +2233,93 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
1876
2233
|
walk(root, base, 0);
|
|
1877
2234
|
return combineDirectAndRecursiveFiles(directEntries, fileEntries.sort((a, b) => a.path.localeCompare(b.path)));
|
|
1878
2235
|
}
|
|
2236
|
+
async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2237
|
+
const base = normalizeTreePath(path);
|
|
2238
|
+
const root = join3(cwd, base);
|
|
2239
|
+
const omitDirNameSet = new Set(omitDirNames);
|
|
2240
|
+
const excludeNameSet = new Set(excludeNames.map((name) => name.toLowerCase()));
|
|
2241
|
+
const submodulePaths = await worktreeSubmodulePathsAsync(cwd);
|
|
2242
|
+
let directEntries;
|
|
2243
|
+
try {
|
|
2244
|
+
const dirents = readdirSync(root, { withFileTypes: true });
|
|
2245
|
+
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2246
|
+
} catch {
|
|
2247
|
+
return [];
|
|
2248
|
+
}
|
|
2249
|
+
if (!recursive)
|
|
2250
|
+
return directEntries;
|
|
2251
|
+
const fileEntries = [];
|
|
2252
|
+
let truncated = false;
|
|
2253
|
+
const pushRecursiveEntry = (entry) => {
|
|
2254
|
+
if (fileEntries.length >= WORKTREE_RECURSIVE_ENTRY_LIMIT) {
|
|
2255
|
+
if (!truncated) {
|
|
2256
|
+
fileEntries.push({
|
|
2257
|
+
name: "more...",
|
|
2258
|
+
path: "__code_viewer_truncated__",
|
|
2259
|
+
type: "tree",
|
|
2260
|
+
children_omitted: true,
|
|
2261
|
+
children_omitted_reason: "truncated"
|
|
2262
|
+
});
|
|
2263
|
+
truncated = true;
|
|
2264
|
+
}
|
|
2265
|
+
return false;
|
|
2266
|
+
}
|
|
2267
|
+
fileEntries.push(entry);
|
|
2268
|
+
return true;
|
|
2269
|
+
};
|
|
2270
|
+
let visitedDirs = 0;
|
|
2271
|
+
const yieldIfNeeded = async () => {
|
|
2272
|
+
visitedDirs++;
|
|
2273
|
+
if (visitedDirs % 25 === 0) {
|
|
2274
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
const walk = async (dir, prefix, depth) => {
|
|
2278
|
+
if (truncated)
|
|
2279
|
+
return;
|
|
2280
|
+
if (depth >= WORKTREE_RECURSIVE_DEPTH_LIMIT)
|
|
2281
|
+
return;
|
|
2282
|
+
await yieldIfNeeded();
|
|
2283
|
+
let entries;
|
|
2284
|
+
try {
|
|
2285
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
2286
|
+
} catch {
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
for (const entry of entries) {
|
|
2290
|
+
if (excludeNameSet.has(entry.name.toLowerCase()))
|
|
2291
|
+
continue;
|
|
2292
|
+
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2293
|
+
const full = join3(dir, entry.name);
|
|
2294
|
+
if (entry.isDirectory()) {
|
|
2295
|
+
const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
|
|
2296
|
+
if (omittedReason) {
|
|
2297
|
+
if (!pushRecursiveEntry({
|
|
2298
|
+
name: entry.name,
|
|
2299
|
+
path: entryPath,
|
|
2300
|
+
type: "tree",
|
|
2301
|
+
children_omitted: true,
|
|
2302
|
+
children_omitted_reason: omittedReason
|
|
2303
|
+
}))
|
|
2304
|
+
return;
|
|
2305
|
+
continue;
|
|
2306
|
+
}
|
|
2307
|
+
if (hasDotGitEntry(full))
|
|
2308
|
+
continue;
|
|
2309
|
+
await walk(full, entryPath, depth + 1);
|
|
2310
|
+
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
2311
|
+
if (!pushRecursiveEntry({
|
|
2312
|
+
name: entry.name,
|
|
2313
|
+
path: entryPath,
|
|
2314
|
+
type: "blob"
|
|
2315
|
+
}))
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
await walk(root, base, 0);
|
|
2321
|
+
return combineDirectAndRecursiveFiles(directEntries, fileEntries.sort((a, b) => a.path.localeCompare(b.path)));
|
|
2322
|
+
}
|
|
1879
2323
|
function hasDotGitEntry(dir) {
|
|
1880
2324
|
try {
|
|
1881
2325
|
lstatSync(join3(dir, ".git"));
|
|
@@ -1884,7 +2328,36 @@ function hasDotGitEntry(dir) {
|
|
|
1884
2328
|
return !!err && typeof err === "object" && "code" in err && err.code !== "ENOENT";
|
|
1885
2329
|
}
|
|
1886
2330
|
}
|
|
1887
|
-
function gitTreeEntries(ref, path, cwd, recursive) {
|
|
2331
|
+
function gitTreeEntries(ref, path, cwd, recursive) {
|
|
2332
|
+
const base = normalizeTreePath(path);
|
|
2333
|
+
const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
|
|
2334
|
+
if (recursive)
|
|
2335
|
+
args.push("-r");
|
|
2336
|
+
args.push("-z", "--full-tree", ref, "--");
|
|
2337
|
+
if (base)
|
|
2338
|
+
args.push(`${base}/`);
|
|
2339
|
+
const res = run(args, cwd);
|
|
2340
|
+
if (res.code !== 0)
|
|
2341
|
+
return { code: res.code, entries: [], stderr: res.stderr };
|
|
2342
|
+
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
2343
|
+
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => {
|
|
2344
|
+
const match = rec.match(new RegExp(`^\\d+\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2345
|
+
if (!match)
|
|
2346
|
+
return null;
|
|
2347
|
+
const entryPath = match[2];
|
|
2348
|
+
return {
|
|
2349
|
+
name: entryPath.split("/").pop() || entryPath,
|
|
2350
|
+
path: entryPath,
|
|
2351
|
+
type: match[1]
|
|
2352
|
+
};
|
|
2353
|
+
}).filter((entry) => !!entry);
|
|
2354
|
+
if (recursive)
|
|
2355
|
+
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
2356
|
+
else
|
|
2357
|
+
entries = sortTreeEntries(entries);
|
|
2358
|
+
return { code: 0, entries, stderr: "" };
|
|
2359
|
+
}
|
|
2360
|
+
async function gitTreeEntriesAsync(ref, path, cwd, recursive) {
|
|
1888
2361
|
const base = normalizeTreePath(path);
|
|
1889
2362
|
const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
|
|
1890
2363
|
if (recursive)
|
|
@@ -1892,7 +2365,7 @@ function gitTreeEntries(ref, path, cwd, recursive) {
|
|
|
1892
2365
|
args.push("-z", "--full-tree", ref, "--");
|
|
1893
2366
|
if (base)
|
|
1894
2367
|
args.push(`${base}/`);
|
|
1895
|
-
const res =
|
|
2368
|
+
const res = await runGitAsync(args, cwd);
|
|
1896
2369
|
if (res.code !== 0)
|
|
1897
2370
|
return { code: res.code, entries: [], stderr: res.stderr };
|
|
1898
2371
|
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
@@ -1941,8 +2414,29 @@ function listTree(ref, path, cwd, options = {}) {
|
|
|
1941
2414
|
stderr: ""
|
|
1942
2415
|
};
|
|
1943
2416
|
}
|
|
1944
|
-
function
|
|
1945
|
-
const
|
|
2417
|
+
async function listTreeAsync(ref, path, cwd, options = {}) {
|
|
2418
|
+
const base = normalizeTreePath(path);
|
|
2419
|
+
if (ref === "worktree") {
|
|
2420
|
+
return {
|
|
2421
|
+
code: 0,
|
|
2422
|
+
entries: await worktreeFilesystemEntriesAsync(cwd, base, !!options.recursive, options.omitDirNames, options.excludeNames),
|
|
2423
|
+
stderr: ""
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
const direct = await gitTreeEntriesAsync(ref, base, cwd, false);
|
|
2427
|
+
if (direct.code !== 0 || !options.recursive)
|
|
2428
|
+
return direct;
|
|
2429
|
+
const recursive = await gitTreeEntriesAsync(ref, base, cwd, true);
|
|
2430
|
+
if (recursive.code !== 0)
|
|
2431
|
+
return recursive;
|
|
2432
|
+
return {
|
|
2433
|
+
code: 0,
|
|
2434
|
+
entries: combineDirectAndRecursiveFiles(direct.entries, recursive.entries),
|
|
2435
|
+
stderr: ""
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
async function listTreeResultAsync(ref, path, cwd, options = {}) {
|
|
2439
|
+
const result = await listTreeAsync(ref, path, cwd, options);
|
|
1946
2440
|
if (result.code === 0)
|
|
1947
2441
|
return { entries: result.entries };
|
|
1948
2442
|
return { entries: [], ...gitFailureResult(result, "git ls-tree failed") };
|
|
@@ -1978,6 +2472,38 @@ function untrackedMeta(cwd) {
|
|
|
1978
2472
|
];
|
|
1979
2473
|
});
|
|
1980
2474
|
}
|
|
2475
|
+
async function untrackedMetaAsync(cwd) {
|
|
2476
|
+
const paths = await untrackedAsync(cwd);
|
|
2477
|
+
return paths.flatMap((path) => {
|
|
2478
|
+
const full = join3(cwd, path);
|
|
2479
|
+
let fileExists = false;
|
|
2480
|
+
try {
|
|
2481
|
+
fileExists = existsSync(full) && statSync2(full).isFile();
|
|
2482
|
+
} catch {
|
|
2483
|
+
fileExists = false;
|
|
2484
|
+
}
|
|
2485
|
+
let scan;
|
|
2486
|
+
if (fileExists) {
|
|
2487
|
+
try {
|
|
2488
|
+
scan = scanFileBinaryAndNewlines(full);
|
|
2489
|
+
} catch {
|
|
2490
|
+
return [];
|
|
2491
|
+
}
|
|
2492
|
+
} else {
|
|
2493
|
+
return [];
|
|
2494
|
+
}
|
|
2495
|
+
return [
|
|
2496
|
+
{
|
|
2497
|
+
path,
|
|
2498
|
+
status: "A",
|
|
2499
|
+
additions: scan.binary ? 0 : scan.newlines,
|
|
2500
|
+
deletions: 0,
|
|
2501
|
+
binary: scan.binary,
|
|
2502
|
+
untracked: true
|
|
2503
|
+
}
|
|
2504
|
+
];
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
1981
2507
|
function scanFileBinaryAndNewlines(full) {
|
|
1982
2508
|
const fd = openSync(full, "r");
|
|
1983
2509
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
@@ -2025,6 +2551,27 @@ function fileMetaResult(args, cwd, includeUntracked = false) {
|
|
|
2025
2551
|
files: includeUntracked ? files.concat(untrackedMeta(cwd)) : files
|
|
2026
2552
|
};
|
|
2027
2553
|
}
|
|
2554
|
+
async function fileMetaResultAsync(args, cwd, includeUntracked = false) {
|
|
2555
|
+
const ns = await nameStatusResultAsync(args, cwd);
|
|
2556
|
+
if (ns.error)
|
|
2557
|
+
return { files: [], error: ns.error };
|
|
2558
|
+
const nm = await numstatZResultAsync(args, cwd);
|
|
2559
|
+
if (nm.error)
|
|
2560
|
+
return { files: [], error: nm.error };
|
|
2561
|
+
const byPath = new Map(nm.files.map((file) => [file.path, file]));
|
|
2562
|
+
const files = ns.files.map((file) => {
|
|
2563
|
+
const stats = byPath.get(file.path);
|
|
2564
|
+
return {
|
|
2565
|
+
...file,
|
|
2566
|
+
additions: stats?.additions || 0,
|
|
2567
|
+
deletions: stats?.deletions || 0,
|
|
2568
|
+
binary: stats?.binary || false
|
|
2569
|
+
};
|
|
2570
|
+
});
|
|
2571
|
+
return {
|
|
2572
|
+
files: includeUntracked ? files.concat(await untrackedMetaAsync(cwd)) : files
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2028
2575
|
function fileDiffText(args, path, cwd) {
|
|
2029
2576
|
const paths = Array.isArray(path) ? path : [path];
|
|
2030
2577
|
const res = run([
|
|
@@ -2044,6 +2591,25 @@ function fileDiffText(args, path, cwd) {
|
|
|
2044
2591
|
}
|
|
2045
2592
|
return res;
|
|
2046
2593
|
}
|
|
2594
|
+
async function fileDiffTextAsync(args, path, cwd) {
|
|
2595
|
+
const paths = Array.isArray(path) ? path : [path];
|
|
2596
|
+
const res = await runGitAsync([
|
|
2597
|
+
"git",
|
|
2598
|
+
"-c",
|
|
2599
|
+
"core.quotepath=false",
|
|
2600
|
+
"diff",
|
|
2601
|
+
"--no-color",
|
|
2602
|
+
"--no-ext-diff",
|
|
2603
|
+
"--find-renames",
|
|
2604
|
+
...args,
|
|
2605
|
+
"--",
|
|
2606
|
+
...paths
|
|
2607
|
+
], cwd);
|
|
2608
|
+
if (isCommandNotFoundResult("git", res)) {
|
|
2609
|
+
return { ...res, stderr: commandNotFoundDetail("git"), status: 503 };
|
|
2610
|
+
}
|
|
2611
|
+
return res;
|
|
2612
|
+
}
|
|
2047
2613
|
function untrackedFileDiff(extras, path, cwd) {
|
|
2048
2614
|
const res = run([
|
|
2049
2615
|
"git",
|
|
@@ -2062,6 +2628,24 @@ function untrackedFileDiff(extras, path, cwd) {
|
|
|
2062
2628
|
}
|
|
2063
2629
|
return res;
|
|
2064
2630
|
}
|
|
2631
|
+
async function untrackedFileDiffAsync(extras, path, cwd) {
|
|
2632
|
+
const res = await runGitAsync([
|
|
2633
|
+
"git",
|
|
2634
|
+
"-c",
|
|
2635
|
+
"core.quotepath=false",
|
|
2636
|
+
"diff",
|
|
2637
|
+
"--no-color",
|
|
2638
|
+
"--no-ext-diff",
|
|
2639
|
+
"--no-index",
|
|
2640
|
+
...extras,
|
|
2641
|
+
"/dev/null",
|
|
2642
|
+
path
|
|
2643
|
+
], cwd);
|
|
2644
|
+
if (isCommandNotFoundResult("git", res)) {
|
|
2645
|
+
return { ...res, stderr: commandNotFoundDetail("git"), status: 503 };
|
|
2646
|
+
}
|
|
2647
|
+
return res;
|
|
2648
|
+
}
|
|
2065
2649
|
function splitHunks(diffText) {
|
|
2066
2650
|
if (!diffText)
|
|
2067
2651
|
return { header: "", hunks: [] };
|
|
@@ -3399,11 +3983,16 @@ __export(exports_file_cli, {
|
|
|
3399
3983
|
sliceLines: () => sliceLines,
|
|
3400
3984
|
safeWorktreePathFromRoot: () => safeWorktreePathFromRoot,
|
|
3401
3985
|
runFileCli: () => runFileCli,
|
|
3986
|
+
readShowTextAsync: () => readShowTextAsync,
|
|
3402
3987
|
readShowText: () => readShowText,
|
|
3403
3988
|
parseFileArgs: () => parseFileArgs,
|
|
3989
|
+
buildFileShowReportAsync: () => buildFileShowReportAsync,
|
|
3404
3990
|
buildFileShowReport: () => buildFileShowReport,
|
|
3991
|
+
buildFileHistoryReportAsync: () => buildFileHistoryReportAsync,
|
|
3405
3992
|
buildFileHistoryReport: () => buildFileHistoryReport,
|
|
3993
|
+
buildFileDiffReportAsync: () => buildFileDiffReportAsync,
|
|
3406
3994
|
buildFileDiffReport: () => buildFileDiffReport,
|
|
3995
|
+
buildFileBlameReportAsync: () => buildFileBlameReportAsync,
|
|
3407
3996
|
buildFileBlameReport: () => buildFileBlameReport,
|
|
3408
3997
|
FILE_HISTORY_HARD_CAP: () => FILE_HISTORY_HARD_CAP,
|
|
3409
3998
|
FILE_HELP: () => FILE_HELP,
|
|
@@ -3723,6 +4312,19 @@ function buildFileBlameReport(root, command) {
|
|
|
3723
4312
|
result
|
|
3724
4313
|
};
|
|
3725
4314
|
}
|
|
4315
|
+
async function buildFileBlameReportAsync(root, command) {
|
|
4316
|
+
const result = await blameAsync(root, {
|
|
4317
|
+
path: command.path,
|
|
4318
|
+
ref: command.ref,
|
|
4319
|
+
base: command.base
|
|
4320
|
+
});
|
|
4321
|
+
return {
|
|
4322
|
+
path: command.path,
|
|
4323
|
+
ref: command.ref,
|
|
4324
|
+
base: command.base,
|
|
4325
|
+
result
|
|
4326
|
+
};
|
|
4327
|
+
}
|
|
3726
4328
|
function buildFileHistoryReport(root, command) {
|
|
3727
4329
|
const result = commitHistory(root, {
|
|
3728
4330
|
ref: command.ref,
|
|
@@ -3740,6 +4342,23 @@ function buildFileHistoryReport(root, command) {
|
|
|
3740
4342
|
result
|
|
3741
4343
|
};
|
|
3742
4344
|
}
|
|
4345
|
+
async function buildFileHistoryReportAsync(root, command) {
|
|
4346
|
+
const result = await commitHistoryAsync(root, {
|
|
4347
|
+
ref: command.ref,
|
|
4348
|
+
skip: command.skip,
|
|
4349
|
+
limit: command.limit,
|
|
4350
|
+
query: command.query,
|
|
4351
|
+
path: command.path
|
|
4352
|
+
});
|
|
4353
|
+
return {
|
|
4354
|
+
path: command.path,
|
|
4355
|
+
ref: command.ref,
|
|
4356
|
+
limit: command.limit,
|
|
4357
|
+
skip: command.skip,
|
|
4358
|
+
...command.query !== undefined ? { query: command.query } : {},
|
|
4359
|
+
result
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
3743
4362
|
function runBlame(root, command) {
|
|
3744
4363
|
const report = buildFileBlameReport(root, command);
|
|
3745
4364
|
if (command.json) {
|
|
@@ -3828,6 +4447,28 @@ function readShowText(root, command) {
|
|
|
3828
4447
|
return { code: 1, stdout: "", stderr: "file not readable" };
|
|
3829
4448
|
}
|
|
3830
4449
|
}
|
|
4450
|
+
async function readShowTextAsync(root, command) {
|
|
4451
|
+
if (command.ref !== "worktree" && command.ref !== "") {
|
|
4452
|
+
return showAsync(command.ref, command.path, root);
|
|
4453
|
+
}
|
|
4454
|
+
const full = safeWorktreePathFromRoot(root, command.path);
|
|
4455
|
+
if (!full) {
|
|
4456
|
+
return {
|
|
4457
|
+
code: 1,
|
|
4458
|
+
stdout: "",
|
|
4459
|
+
stderr: "file not found or forbidden"
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
try {
|
|
4463
|
+
const stat = statSync3(full);
|
|
4464
|
+
if (!stat.isFile()) {
|
|
4465
|
+
return { code: 1, stdout: "", stderr: "not a file" };
|
|
4466
|
+
}
|
|
4467
|
+
return { code: 0, stdout: readFileSync4(full, "utf8"), stderr: "" };
|
|
4468
|
+
} catch {
|
|
4469
|
+
return { code: 1, stdout: "", stderr: "file not readable" };
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
3831
4472
|
function buildFileShowReport(root, command) {
|
|
3832
4473
|
const res = readShowText(root, command);
|
|
3833
4474
|
if (res.code !== 0) {
|
|
@@ -3855,6 +4496,33 @@ function buildFileShowReport(root, command) {
|
|
|
3855
4496
|
`)
|
|
3856
4497
|
};
|
|
3857
4498
|
}
|
|
4499
|
+
async function buildFileShowReportAsync(root, command) {
|
|
4500
|
+
const res = await readShowTextAsync(root, command);
|
|
4501
|
+
if (res.code !== 0) {
|
|
4502
|
+
const detail = res.stderr.trim() || `git show exited with code ${res.code}`;
|
|
4503
|
+
return {
|
|
4504
|
+
path: command.path,
|
|
4505
|
+
ref: command.ref,
|
|
4506
|
+
...command.start !== undefined ? { start: command.start } : {},
|
|
4507
|
+
...command.end !== undefined ? { end: command.end } : {},
|
|
4508
|
+
totalLines: 0,
|
|
4509
|
+
complete: false,
|
|
4510
|
+
text: "",
|
|
4511
|
+
error: detail
|
|
4512
|
+
};
|
|
4513
|
+
}
|
|
4514
|
+
const sliced = sliceLines(res.stdout, command.start, command.end);
|
|
4515
|
+
return {
|
|
4516
|
+
path: command.path,
|
|
4517
|
+
ref: command.ref,
|
|
4518
|
+
...command.start !== undefined ? { start: command.start } : {},
|
|
4519
|
+
...command.end !== undefined ? { end: command.end } : {},
|
|
4520
|
+
totalLines: sliced.total,
|
|
4521
|
+
complete: sliced.complete,
|
|
4522
|
+
text: sliced.lines.join(`
|
|
4523
|
+
`)
|
|
4524
|
+
};
|
|
4525
|
+
}
|
|
3858
4526
|
function runShow(root, command) {
|
|
3859
4527
|
const report = buildFileShowReport(root, command);
|
|
3860
4528
|
if (report.error !== undefined) {
|
|
@@ -3937,6 +4605,64 @@ function buildFileDiffReport(root, command) {
|
|
|
3937
4605
|
...errText ? { error: errText } : {}
|
|
3938
4606
|
};
|
|
3939
4607
|
}
|
|
4608
|
+
async function buildFileDiffReportAsync(root, command) {
|
|
4609
|
+
const base = {
|
|
4610
|
+
path: command.path,
|
|
4611
|
+
...command.oldPath !== undefined ? { old_path: command.oldPath } : {},
|
|
4612
|
+
from: command.untracked ? "/dev/null" : command.from,
|
|
4613
|
+
to: command.to,
|
|
4614
|
+
untracked: command.untracked,
|
|
4615
|
+
ignore_ws: command.ignoreWs,
|
|
4616
|
+
ignore_blank: command.ignoreBlank,
|
|
4617
|
+
mode: command.mode,
|
|
4618
|
+
max_hunks: command.mode === "preview" ? command.maxHunks : null,
|
|
4619
|
+
max_lines: command.mode === "preview" ? command.maxLines : null,
|
|
4620
|
+
diff: "",
|
|
4621
|
+
hunk_count: 0,
|
|
4622
|
+
rendered_hunk_count: 0,
|
|
4623
|
+
line_count: 0,
|
|
4624
|
+
truncated: false,
|
|
4625
|
+
binary: false
|
|
4626
|
+
};
|
|
4627
|
+
if (!command.untracked && isSameWorktreeRange({ from: command.from, to: command.to })) {
|
|
4628
|
+
return base;
|
|
4629
|
+
}
|
|
4630
|
+
const extras = [];
|
|
4631
|
+
if (command.ignoreWs)
|
|
4632
|
+
extras.push("-w");
|
|
4633
|
+
if (command.ignoreBlank)
|
|
4634
|
+
extras.push("--ignore-blank-lines");
|
|
4635
|
+
let diffText = "";
|
|
4636
|
+
let errText = "";
|
|
4637
|
+
if (command.untracked) {
|
|
4638
|
+
const res = await untrackedFileDiffAsync(extras, command.path, root);
|
|
4639
|
+
diffText = res.stdout || "";
|
|
4640
|
+
if (res.code !== 0)
|
|
4641
|
+
errText = res.stderr.trim();
|
|
4642
|
+
} else {
|
|
4643
|
+
const args = [
|
|
4644
|
+
...extras,
|
|
4645
|
+
...buildFileDiffRangeArgs(command.from, command.to)
|
|
4646
|
+
];
|
|
4647
|
+
const paths = command.oldPath !== undefined ? [command.oldPath, command.path] : command.path;
|
|
4648
|
+
const res = await fileDiffTextAsync(args, paths, root);
|
|
4649
|
+
diffText = res.stdout || "";
|
|
4650
|
+
if (res.code !== 0)
|
|
4651
|
+
errText = res.stderr.trim();
|
|
4652
|
+
}
|
|
4653
|
+
const truncated = command.mode === "preview" ? truncateToNHunks(diffText, command.maxHunks, command.maxLines) : truncateToNHunks(diffText, 1e9);
|
|
4654
|
+
const previewTruncated = command.mode === "preview" && (truncated.totalHunks > truncated.renderedHunks || truncated.lineTruncated);
|
|
4655
|
+
return {
|
|
4656
|
+
...base,
|
|
4657
|
+
diff: truncated.text,
|
|
4658
|
+
hunk_count: truncated.totalHunks,
|
|
4659
|
+
rendered_hunk_count: truncated.renderedHunks,
|
|
4660
|
+
line_count: truncated.lineCount,
|
|
4661
|
+
truncated: previewTruncated,
|
|
4662
|
+
binary: diffText.includes("Binary files"),
|
|
4663
|
+
...errText ? { error: errText } : {}
|
|
4664
|
+
};
|
|
4665
|
+
}
|
|
3940
4666
|
function runDiff(root, command) {
|
|
3941
4667
|
const report = buildFileDiffReport(root, command);
|
|
3942
4668
|
if (command.json) {
|
|
@@ -4423,8 +5149,8 @@ function buildGithubIssueViewArgs(options) {
|
|
|
4423
5149
|
args.push("--repo", repo);
|
|
4424
5150
|
return args;
|
|
4425
5151
|
}
|
|
4426
|
-
function
|
|
4427
|
-
const proc =
|
|
5152
|
+
async function readGithubIssueListAsync(options) {
|
|
5153
|
+
const proc = await runAsync(buildGithubIssueListArgs(options), options.cwd, {
|
|
4428
5154
|
timeout: 30000
|
|
4429
5155
|
});
|
|
4430
5156
|
if (proc.code !== 0) {
|
|
@@ -4437,8 +5163,8 @@ function readGithubIssueList(options) {
|
|
|
4437
5163
|
throw new GithubIssueListError("failed to parse gh issue list output");
|
|
4438
5164
|
}
|
|
4439
5165
|
}
|
|
4440
|
-
function
|
|
4441
|
-
const proc =
|
|
5166
|
+
async function readGithubIssueAsync(options) {
|
|
5167
|
+
const proc = await runAsync(buildGithubIssueViewArgs(options), options.cwd, {
|
|
4442
5168
|
timeout: 30000
|
|
4443
5169
|
});
|
|
4444
5170
|
if (proc.code !== 0) {
|
|
@@ -5092,7 +5818,7 @@ async function runJournalCli(argv) {
|
|
|
5092
5818
|
console.error(commandConfig.error);
|
|
5093
5819
|
process.exit(1);
|
|
5094
5820
|
}
|
|
5095
|
-
const issues =
|
|
5821
|
+
const issues = await readGithubIssueListAsync({
|
|
5096
5822
|
cwd: root,
|
|
5097
5823
|
repo: command.repo,
|
|
5098
5824
|
labels: command.ghLabels,
|
|
@@ -5116,7 +5842,7 @@ async function runJournalCli(argv) {
|
|
|
5116
5842
|
console.error(commandConfig.error);
|
|
5117
5843
|
process.exit(1);
|
|
5118
5844
|
}
|
|
5119
|
-
const issue =
|
|
5845
|
+
const issue = await readGithubIssueAsync({
|
|
5120
5846
|
cwd: root,
|
|
5121
5847
|
number: command.issueNumber,
|
|
5122
5848
|
repo: command.repo
|
|
@@ -13551,15 +14277,26 @@ function validateDbPath(cwd, dbPath) {
|
|
|
13551
14277
|
return null;
|
|
13552
14278
|
return realFull;
|
|
13553
14279
|
}
|
|
13554
|
-
function
|
|
14280
|
+
function serviceListIncludesAwsService(value, service) {
|
|
13555
14281
|
if (value === undefined || value.trim() === "")
|
|
13556
14282
|
return true;
|
|
13557
|
-
return value.split(/[,\s]+/).map((part) => part.trim().toLowerCase()).filter(Boolean).includes(
|
|
14283
|
+
return value.split(/[,\s]+/).map((part) => part.trim().toLowerCase()).filter(Boolean).includes(service);
|
|
14284
|
+
}
|
|
14285
|
+
function detectAwsKindsFromServices(services) {
|
|
14286
|
+
const kinds = [];
|
|
14287
|
+
if (serviceListIncludesAwsService(services, "s3"))
|
|
14288
|
+
kinds.push("s3");
|
|
14289
|
+
if (serviceListIncludesAwsService(services, "dynamodb"))
|
|
14290
|
+
kinds.push("dynamodb");
|
|
14291
|
+
return kinds;
|
|
14292
|
+
}
|
|
14293
|
+
function isLocalstackImage(image) {
|
|
14294
|
+
return !!image && image.toLowerCase().includes("localstack/localstack");
|
|
13558
14295
|
}
|
|
13559
14296
|
function imageLooksLikeMinio(image) {
|
|
13560
14297
|
return /(^|\/)minio(?::|\/|$)/.test(image.toLowerCase());
|
|
13561
14298
|
}
|
|
13562
|
-
function detectDbKind(image,
|
|
14299
|
+
function detectDbKind(image, _env = {}) {
|
|
13563
14300
|
const lower = image.toLowerCase();
|
|
13564
14301
|
if (lower.includes("postgres"))
|
|
13565
14302
|
return "postgresql";
|
|
@@ -13571,9 +14308,6 @@ function detectDbKind(image, env = {}) {
|
|
|
13571
14308
|
return "elasticsearch";
|
|
13572
14309
|
if (imageLooksLikeMinio(lower))
|
|
13573
14310
|
return "s3";
|
|
13574
|
-
if (lower.includes("localstack/localstack")) {
|
|
13575
|
-
return serviceListIncludesS3(env.SERVICES) ? "s3" : null;
|
|
13576
|
-
}
|
|
13577
14311
|
return null;
|
|
13578
14312
|
}
|
|
13579
14313
|
function detectDbKindFromEnv(env) {
|
|
@@ -13586,10 +14320,33 @@ function detectDbKindFromEnv(env) {
|
|
|
13586
14320
|
if (env.MINIO_ROOT_USER || env.MINIO_ROOT_PASSWORD || env.MINIO_ACCESS_KEY || env.MINIO_SECRET_KEY) {
|
|
13587
14321
|
return "s3";
|
|
13588
14322
|
}
|
|
13589
|
-
if (env.SERVICES && serviceListIncludesS3(env.SERVICES))
|
|
13590
|
-
return "s3";
|
|
13591
14323
|
return null;
|
|
13592
14324
|
}
|
|
14325
|
+
function detectDbKindsForService(image, env, containerPort, serviceName) {
|
|
14326
|
+
if (isLocalstackImage(image) || env.SERVICES !== undefined) {
|
|
14327
|
+
return detectAwsKindsFromServices(env.SERVICES);
|
|
14328
|
+
}
|
|
14329
|
+
const single = (image ? detectDbKind(image, env) : null) ?? detectDbKindFromEnv(env) ?? detectDbKindFromContainerPort(containerPort) ?? detectDbKindFromServiceName(serviceName);
|
|
14330
|
+
return single ? [single] : [];
|
|
14331
|
+
}
|
|
14332
|
+
function dbKindDisplayName(kind) {
|
|
14333
|
+
switch (kind) {
|
|
14334
|
+
case "s3":
|
|
14335
|
+
return "S3";
|
|
14336
|
+
case "dynamodb":
|
|
14337
|
+
return "DynamoDB";
|
|
14338
|
+
case "redis":
|
|
14339
|
+
return "Redis";
|
|
14340
|
+
case "elasticsearch":
|
|
14341
|
+
return "Elasticsearch";
|
|
14342
|
+
case "postgresql":
|
|
14343
|
+
return "PostgreSQL";
|
|
14344
|
+
case "mysql":
|
|
14345
|
+
return "MySQL";
|
|
14346
|
+
case "sqlite":
|
|
14347
|
+
return "SQLite";
|
|
14348
|
+
}
|
|
14349
|
+
}
|
|
13593
14350
|
function detectDbKindFromContainerPort(port) {
|
|
13594
14351
|
switch (port) {
|
|
13595
14352
|
case "3306":
|
|
@@ -13634,8 +14391,10 @@ function defaultPortFor(kind, image, env = {}) {
|
|
|
13634
14391
|
return "9200";
|
|
13635
14392
|
case "s3": {
|
|
13636
14393
|
const lower = image?.toLowerCase() || "";
|
|
13637
|
-
return lower.includes("localstack/localstack") || env.SERVICES !== undefined &&
|
|
14394
|
+
return lower.includes("localstack/localstack") || env.SERVICES !== undefined && serviceListIncludesAwsService(env.SERVICES, "s3") ? "4566" : "9000";
|
|
13638
14395
|
}
|
|
14396
|
+
case "dynamodb":
|
|
14397
|
+
return "4566";
|
|
13639
14398
|
default:
|
|
13640
14399
|
return "";
|
|
13641
14400
|
}
|
|
@@ -13722,7 +14481,7 @@ function parseComposePortMappings(serviceBlock, composeDirEnv = {}) {
|
|
|
13722
14481
|
const trimmed = line.trim();
|
|
13723
14482
|
if (!trimmed.startsWith("-"))
|
|
13724
14483
|
continue;
|
|
13725
|
-
const value = resolveEnvValue(trimmed.slice(1).trim()
|
|
14484
|
+
const value = resolveEnvValue(trimmed.slice(1).trim(), composeDirEnv).split("/")[0].trim();
|
|
13726
14485
|
if (!value || value.includes("target:"))
|
|
13727
14486
|
continue;
|
|
13728
14487
|
const parts = value.split(":");
|
|
@@ -13807,40 +14566,45 @@ function parseComposeContent(content, filepath, composeDir, cwd, composeDirEnv,
|
|
|
13807
14566
|
const env = parseComposeEnv(svcBlock, composeDirEnv);
|
|
13808
14567
|
const containerPort = parseComposeContainerPort(svcBlock);
|
|
13809
14568
|
const profiled = /^\s+profiles:/m.test(svcBlock);
|
|
13810
|
-
const
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
const
|
|
13823
|
-
label
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
14569
|
+
const kinds = detectDbKindsForService(image, env, containerPort, svc.name);
|
|
14570
|
+
const isMultiKind = kinds.length > 1;
|
|
14571
|
+
for (const kind of kinds) {
|
|
14572
|
+
if (results.length >= MAX_DOCKER_SERVICES)
|
|
14573
|
+
return;
|
|
14574
|
+
const defaultPort = defaultPortFor(kind, image, env);
|
|
14575
|
+
const serviceContainerPort = kind === "s3" || kind === "dynamodb" ? defaultPort : containerPort || defaultPort;
|
|
14576
|
+
const publishedHostPort = parseComposeHostPortForContainer(svcBlock, serviceContainerPort, composeDirEnv) || parseComposeHostPortForContainer(svcBlock, defaultPort, composeDirEnv) || parseComposePorts(svcBlock, composeDirEnv);
|
|
14577
|
+
const hostPort = kind === "s3" || kind === "dynamodb" ? publishedHostPort || undefined : publishedHostPort || defaultPort;
|
|
14578
|
+
const imageLabel = image ?? `build:${kind}`;
|
|
14579
|
+
const kindSuffix = isMultiKind ? `#${kind}` : "";
|
|
14580
|
+
const id = isRoot ? `docker:${svc.name}${kindSuffix}` : `docker:${svc.name}@${encodeURIComponent(relDirSlash)}${kindSuffix}`;
|
|
14581
|
+
const labelPath = isRoot ? "" : ` — ${relDirSlash}`;
|
|
14582
|
+
let label;
|
|
14583
|
+
if (kind === "redis" || kind === "elasticsearch" || kind === "s3" || kind === "dynamodb") {
|
|
14584
|
+
const endpointLabel = hostPort ? `localhost:${hostPort}` : `container:${serviceContainerPort}`;
|
|
14585
|
+
const svcLabel = isMultiKind ? `${svc.name} / ${dbKindDisplayName(kind)}` : svc.name;
|
|
14586
|
+
label = `${svcLabel} (${imageLabel}, ${endpointLabel}${labelPath})`;
|
|
14587
|
+
} else {
|
|
14588
|
+
const dbName = env.POSTGRES_DB || env.MYSQL_DATABASE || env.MARIADB_DATABASE || svc.name;
|
|
14589
|
+
const user = env.POSTGRES_USER || env.MYSQL_USER || env.MARIADB_USER || (kind === "postgresql" ? "postgres" : "root");
|
|
14590
|
+
label = `${svc.name} (${imageLabel}, ${user}@localhost:${hostPort}/${dbName}${labelPath})`;
|
|
14591
|
+
}
|
|
14592
|
+
results.push({
|
|
14593
|
+
id,
|
|
14594
|
+
path: isRoot ? filename : `${relDirSlash}/${filename}`,
|
|
14595
|
+
name: label,
|
|
14596
|
+
sizeBytes: 0,
|
|
14597
|
+
kind,
|
|
14598
|
+
serviceName: svc.name,
|
|
14599
|
+
...image ? { image } : {},
|
|
14600
|
+
env,
|
|
14601
|
+
composeDir,
|
|
14602
|
+
relDirSlash,
|
|
14603
|
+
...hostPort ? { hostPort } : {},
|
|
14604
|
+
containerPort: serviceContainerPort,
|
|
14605
|
+
...profiled ? { profiled: true } : {}
|
|
14606
|
+
});
|
|
13828
14607
|
}
|
|
13829
|
-
results.push({
|
|
13830
|
-
id,
|
|
13831
|
-
path: isRoot ? filename : `${relDirSlash}/${filename}`,
|
|
13832
|
-
name: label,
|
|
13833
|
-
sizeBytes: 0,
|
|
13834
|
-
kind,
|
|
13835
|
-
serviceName: svc.name,
|
|
13836
|
-
...image ? { image } : {},
|
|
13837
|
-
env,
|
|
13838
|
-
composeDir,
|
|
13839
|
-
relDirSlash,
|
|
13840
|
-
...hostPort ? { hostPort } : {},
|
|
13841
|
-
containerPort: serviceContainerPort,
|
|
13842
|
-
...profiled ? { profiled: true } : {}
|
|
13843
|
-
});
|
|
13844
14608
|
}
|
|
13845
14609
|
}
|
|
13846
14610
|
async function parseComposeFileAsync(filepath, composeDir, cwd, results) {
|
|
@@ -13938,12 +14702,24 @@ async function discoverDockerDatabasesAsync(cwd, omitDirNames = [], signal) {
|
|
|
13938
14702
|
});
|
|
13939
14703
|
return cloneDockerDiscoveryResult(results);
|
|
13940
14704
|
}
|
|
14705
|
+
function isDbKind(value) {
|
|
14706
|
+
return DB_KIND_VALUES.has(value);
|
|
14707
|
+
}
|
|
13941
14708
|
function parseDockerDbId(dbId) {
|
|
13942
14709
|
if (!dbId.startsWith("docker:"))
|
|
13943
14710
|
return null;
|
|
13944
14711
|
let rest = dbId.slice(7);
|
|
13945
14712
|
if (!rest)
|
|
13946
14713
|
return null;
|
|
14714
|
+
let kind;
|
|
14715
|
+
const hashIdx = rest.indexOf("#");
|
|
14716
|
+
if (hashIdx >= 0) {
|
|
14717
|
+
const kindPart = rest.slice(hashIdx + 1);
|
|
14718
|
+
if (!isDbKind(kindPart))
|
|
14719
|
+
return null;
|
|
14720
|
+
kind = kindPart;
|
|
14721
|
+
rest = rest.slice(0, hashIdx);
|
|
14722
|
+
}
|
|
13947
14723
|
let database;
|
|
13948
14724
|
const atIdx = rest.indexOf("@");
|
|
13949
14725
|
if (atIdx >= 0) {
|
|
@@ -13967,7 +14743,8 @@ function parseDockerDbId(dbId) {
|
|
|
13967
14743
|
return {
|
|
13968
14744
|
serviceName,
|
|
13969
14745
|
relDir,
|
|
13970
|
-
database
|
|
14746
|
+
database,
|
|
14747
|
+
kind
|
|
13971
14748
|
};
|
|
13972
14749
|
} catch {
|
|
13973
14750
|
return null;
|
|
@@ -13982,23 +14759,27 @@ function parseDockerDbId(dbId) {
|
|
|
13982
14759
|
return null;
|
|
13983
14760
|
if (!isSafeDockerDatabaseName(database))
|
|
13984
14761
|
return null;
|
|
13985
|
-
return { serviceName: rest, relDir: "", database };
|
|
14762
|
+
return { serviceName: rest, relDir: "", database, kind };
|
|
13986
14763
|
}
|
|
13987
14764
|
function canonicalizeDockerDbId(dbId) {
|
|
13988
14765
|
const parsed = parseDockerDbId(dbId);
|
|
13989
14766
|
if (!parsed)
|
|
13990
14767
|
return null;
|
|
13991
14768
|
const database = parsed.database ? `:${parsed.database}` : "";
|
|
14769
|
+
const kindSuffix = parsed.kind ? `#${parsed.kind}` : "";
|
|
13992
14770
|
if (!parsed.relDir)
|
|
13993
|
-
return `docker:${parsed.serviceName}${database}`;
|
|
13994
|
-
return `docker:${parsed.serviceName}@${encodeURIComponent(parsed.relDir)}${database}`;
|
|
14771
|
+
return `docker:${parsed.serviceName}${database}${kindSuffix}`;
|
|
14772
|
+
return `docker:${parsed.serviceName}@${encodeURIComponent(parsed.relDir)}${database}${kindSuffix}`;
|
|
13995
14773
|
}
|
|
13996
14774
|
async function findDockerServiceByDbIdAsync(cwd, dbId, kind, omitDirNames, signal) {
|
|
13997
14775
|
const parsed = parseDockerDbId(dbId);
|
|
13998
14776
|
if (!parsed)
|
|
13999
14777
|
return null;
|
|
14778
|
+
if (kind && parsed.kind && parsed.kind !== kind)
|
|
14779
|
+
return null;
|
|
14780
|
+
const effectiveKind = parsed.kind ?? kind;
|
|
14000
14781
|
const services = await discoverDockerDatabasesAsync(cwd, omitDirNames, signal);
|
|
14001
|
-
return services.find((d) => d.serviceName === parsed.serviceName && d.relDirSlash === parsed.relDir && (!
|
|
14782
|
+
return services.find((d) => d.serviceName === parsed.serviceName && d.relDirSlash === parsed.relDir && (!effectiveKind || d.kind === effectiveKind)) || null;
|
|
14002
14783
|
}
|
|
14003
14784
|
function isSafeDockerServiceName(value) {
|
|
14004
14785
|
return /^[A-Za-z0-9_-]+$/.test(value);
|
|
@@ -14133,7 +14914,7 @@ async function findSupabaseCliProjectByDbIdAsync(cwd, dbId, omitDirNames, signal
|
|
|
14133
14914
|
const projects = await discoverSupabaseCliProjectsAsync(cwd, omitDirNames, signal);
|
|
14134
14915
|
return projects.find((p) => p.projectId === parsed.projectId && p.relDirSlash === parsed.relDir) || null;
|
|
14135
14916
|
}
|
|
14136
|
-
var SQLITE_EXTENSIONS, SQLITE_MAGIC = "SQLite format 3\x00", MAX_SCAN_DEPTH = 3, MAX_ENTRIES = 50, DOCKER_DISCOVERY_TTL_MS = 5000, SQLITE_DISCOVERY_TTL_MS = 5000, sqliteDiscoveryCache, COMPOSE_FILENAMES, MAX_DOCKER_SERVICES = 30, dockerDiscoveryCache, MAX_SUPABASE_PROJECTS = 30, SUPABASE_DISCOVERY_TTL_MS = 5000, DEFAULT_SUPABASE_DB_PORT = "54322", supabaseDiscoveryCache;
|
|
14917
|
+
var SQLITE_EXTENSIONS, SQLITE_MAGIC = "SQLite format 3\x00", MAX_SCAN_DEPTH = 3, MAX_ENTRIES = 50, DOCKER_DISCOVERY_TTL_MS = 5000, SQLITE_DISCOVERY_TTL_MS = 5000, sqliteDiscoveryCache, COMPOSE_FILENAMES, MAX_DOCKER_SERVICES = 30, dockerDiscoveryCache, DB_KIND_VALUES, MAX_SUPABASE_PROJECTS = 30, SUPABASE_DISCOVERY_TTL_MS = 5000, DEFAULT_SUPABASE_DB_PORT = "54322", supabaseDiscoveryCache;
|
|
14137
14918
|
var init_discovery = __esm(() => {
|
|
14138
14919
|
SQLITE_EXTENSIONS = new Set([".db", ".sqlite", ".sqlite3", ".s3db"]);
|
|
14139
14920
|
sqliteDiscoveryCache = new Map;
|
|
@@ -14144,6 +14925,15 @@ var init_discovery = __esm(() => {
|
|
|
14144
14925
|
"compose.yaml"
|
|
14145
14926
|
];
|
|
14146
14927
|
dockerDiscoveryCache = new Map;
|
|
14928
|
+
DB_KIND_VALUES = new Set([
|
|
14929
|
+
"sqlite",
|
|
14930
|
+
"postgresql",
|
|
14931
|
+
"mysql",
|
|
14932
|
+
"redis",
|
|
14933
|
+
"elasticsearch",
|
|
14934
|
+
"s3",
|
|
14935
|
+
"dynamodb"
|
|
14936
|
+
]);
|
|
14147
14937
|
supabaseDiscoveryCache = new Map;
|
|
14148
14938
|
});
|
|
14149
14939
|
|
|
@@ -15131,14 +15921,476 @@ async function searchTableAsync(adapter, table, columns, term, maxHits, includeN
|
|
|
15131
15921
|
throw err;
|
|
15132
15922
|
}
|
|
15133
15923
|
}
|
|
15134
|
-
return hits;
|
|
15924
|
+
return hits;
|
|
15925
|
+
}
|
|
15926
|
+
function getPrimaryKeyColumnsFromColumns(columns) {
|
|
15927
|
+
return columns.filter((c) => c.primaryKey).map((c) => c.name);
|
|
15928
|
+
}
|
|
15929
|
+
var init_global_search = __esm(() => {
|
|
15930
|
+
init_serialize();
|
|
15931
|
+
init_sql_utils();
|
|
15932
|
+
});
|
|
15933
|
+
|
|
15934
|
+
// web-src/server/database/adapters/dynamodb.ts
|
|
15935
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
15936
|
+
import { createHash as createHash5, createHmac as createHmac2 } from "node:crypto";
|
|
15937
|
+
function createDynamoDbRequestDeadline() {
|
|
15938
|
+
const timeoutMs = dynamoDbRequestTimeoutMs;
|
|
15939
|
+
return { expiresAt: Date.now() + timeoutMs, timeoutMs };
|
|
15940
|
+
}
|
|
15941
|
+
function createDynamoDbDockerCurlDeadline() {
|
|
15942
|
+
const timeoutMs = dynamoDbDockerCurlTimeoutMs;
|
|
15943
|
+
return { expiresAt: Date.now() + timeoutMs, timeoutMs };
|
|
15944
|
+
}
|
|
15945
|
+
function createDynamoDbTransportDeadline(config) {
|
|
15946
|
+
return config.dockerContainerName ? createDynamoDbDockerCurlDeadline() : createDynamoDbRequestDeadline();
|
|
15947
|
+
}
|
|
15948
|
+
function dynamoDbTimeoutError(deadline) {
|
|
15949
|
+
return new DynamoDbHttpError(503, `DynamoDB request timed out after ${deadline?.timeoutMs ?? dynamoDbRequestTimeoutMs}ms`);
|
|
15950
|
+
}
|
|
15951
|
+
function remainingDynamoDbTimeoutMs(deadline) {
|
|
15952
|
+
if (!deadline)
|
|
15953
|
+
return dynamoDbRequestTimeoutMs;
|
|
15954
|
+
return Math.max(0, deadline.expiresAt - Date.now());
|
|
15955
|
+
}
|
|
15956
|
+
function hmac2(key, value) {
|
|
15957
|
+
return createHmac2("sha256", key).update(value, "utf8").digest();
|
|
15958
|
+
}
|
|
15959
|
+
function sha2562(value) {
|
|
15960
|
+
return createHash5("sha256").update(value, "utf8").digest("hex");
|
|
15961
|
+
}
|
|
15962
|
+
function amzDate2(date = new Date) {
|
|
15963
|
+
const iso = date.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
15964
|
+
return { dateStamp: iso.slice(0, 8), amzDate: iso };
|
|
15965
|
+
}
|
|
15966
|
+
function signingKey2(secret, dateStamp, region) {
|
|
15967
|
+
const kDate = hmac2(`AWS4${secret}`, dateStamp);
|
|
15968
|
+
const kRegion = hmac2(kDate, region);
|
|
15969
|
+
const kService = hmac2(kRegion, "dynamodb");
|
|
15970
|
+
return hmac2(kService, "aws4_request");
|
|
15971
|
+
}
|
|
15972
|
+
function compareCodeUnit2(a, b) {
|
|
15973
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
15974
|
+
}
|
|
15975
|
+
function signedHeadersString2(headers) {
|
|
15976
|
+
return Object.keys(headers).map((key) => key.toLowerCase()).sort(compareCodeUnit2).join(";");
|
|
15977
|
+
}
|
|
15978
|
+
function canonicalHeaders2(headers) {
|
|
15979
|
+
return Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value.trim().replace(/\s+/g, " ")]).sort(([a], [b]) => compareCodeUnit2(a, b)).map(([key, value]) => `${key}:${value}
|
|
15980
|
+
`).join("");
|
|
15981
|
+
}
|
|
15982
|
+
function splitCurlHeadersAndBody2(bytes) {
|
|
15983
|
+
const buffer = Buffer.from(bytes);
|
|
15984
|
+
let idx = buffer.indexOf(`\r
|
|
15985
|
+
\r
|
|
15986
|
+
`);
|
|
15987
|
+
let sepLen = 4;
|
|
15988
|
+
if (idx < 0) {
|
|
15989
|
+
idx = buffer.indexOf(`
|
|
15990
|
+
|
|
15991
|
+
`);
|
|
15992
|
+
sepLen = 2;
|
|
15993
|
+
}
|
|
15994
|
+
if (idx < 0)
|
|
15995
|
+
return { headerText: "", body: bytes };
|
|
15996
|
+
return {
|
|
15997
|
+
headerText: buffer.subarray(0, idx).toString("utf8"),
|
|
15998
|
+
body: new Uint8Array(buffer.subarray(idx + sepLen))
|
|
15999
|
+
};
|
|
16000
|
+
}
|
|
16001
|
+
function responseFromCurlOutput2(bytes) {
|
|
16002
|
+
const { headerText, body } = splitCurlHeadersAndBody2(bytes);
|
|
16003
|
+
const headerBlocks = headerText.split(/\r?\n\r?\n/).map((block2) => block2.trim()).filter(Boolean);
|
|
16004
|
+
const block = headerBlocks[headerBlocks.length - 1] || "";
|
|
16005
|
+
const lines = block.split(/\r?\n/).filter(Boolean);
|
|
16006
|
+
const statusMatch = lines[0]?.match(/^HTTP\/\S+\s+(\d+)/i);
|
|
16007
|
+
const status = statusMatch ? Number(statusMatch[1]) : 502;
|
|
16008
|
+
const headers = new Headers;
|
|
16009
|
+
for (const line of lines.slice(1)) {
|
|
16010
|
+
const idx = line.indexOf(":");
|
|
16011
|
+
if (idx <= 0)
|
|
16012
|
+
continue;
|
|
16013
|
+
headers.append(line.slice(0, idx).trim(), line.slice(idx + 1).trim());
|
|
16014
|
+
}
|
|
16015
|
+
const arrayBuffer = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
|
|
16016
|
+
return new Response(arrayBuffer, { status, headers });
|
|
16017
|
+
}
|
|
16018
|
+
function curlConfigQuote2(value) {
|
|
16019
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[\r\n]+/g, " ")}"`;
|
|
16020
|
+
}
|
|
16021
|
+
function curlHeaderConfig2(headers) {
|
|
16022
|
+
const lines = [];
|
|
16023
|
+
headers.forEach((value, key) => {
|
|
16024
|
+
lines.push(`header = ${curlConfigQuote2(`${key}: ${value}`)}`);
|
|
16025
|
+
});
|
|
16026
|
+
return `${lines.join(`
|
|
16027
|
+
`)}
|
|
16028
|
+
`;
|
|
16029
|
+
}
|
|
16030
|
+
function dockerCurlCommand2(opts) {
|
|
16031
|
+
const shellScript = [
|
|
16032
|
+
"set -eu",
|
|
16033
|
+
'headers_file="$(mktemp)"',
|
|
16034
|
+
'body_file="$(mktemp)"',
|
|
16035
|
+
`trap 'rm -f "$headers_file" "$body_file"' EXIT`,
|
|
16036
|
+
`while IFS= read -r line; do [ "$line" = "${DOCKER_CURL_BODY_MARKER}" ] && break; printf '%s\\n' "$line" >> "$headers_file"; done`,
|
|
16037
|
+
'cat > "$body_file"',
|
|
16038
|
+
`curl -sS -X POST -D - -o - -K "$headers_file" --data-binary "@$body_file" ${curlConfigQuote2(opts.url)}`
|
|
16039
|
+
].join(`
|
|
16040
|
+
`);
|
|
16041
|
+
return {
|
|
16042
|
+
args: ["exec", "-i", opts.containerName, "sh", "-c", shellScript],
|
|
16043
|
+
input: Buffer.from(`${curlHeaderConfig2(opts.headers)}${DOCKER_CURL_BODY_MARKER}
|
|
16044
|
+
${opts.body}`, "utf8")
|
|
16045
|
+
};
|
|
16046
|
+
}
|
|
16047
|
+
function guardedDynamoDbTransport(signal, operation, deadline) {
|
|
16048
|
+
if (signal?.aborted) {
|
|
16049
|
+
return Promise.reject(new DynamoDbHttpError(503, "DynamoDB HTTP transport aborted"));
|
|
16050
|
+
}
|
|
16051
|
+
const timeoutMs = remainingDynamoDbTimeoutMs(deadline);
|
|
16052
|
+
if (timeoutMs <= 0)
|
|
16053
|
+
return Promise.reject(dynamoDbTimeoutError(deadline));
|
|
16054
|
+
const controller = new AbortController;
|
|
16055
|
+
let timedOut = false;
|
|
16056
|
+
let settled = false;
|
|
16057
|
+
let timer;
|
|
16058
|
+
let cleanupParent = () => {
|
|
16059
|
+
return;
|
|
16060
|
+
};
|
|
16061
|
+
const abort = (err, reject) => {
|
|
16062
|
+
if (settled)
|
|
16063
|
+
return;
|
|
16064
|
+
settled = true;
|
|
16065
|
+
controller.abort(err);
|
|
16066
|
+
reject(err);
|
|
16067
|
+
};
|
|
16068
|
+
const guarded = new Promise((resolve2, reject) => {
|
|
16069
|
+
const onParentAbort = () => abort(new DynamoDbHttpError(503, "DynamoDB HTTP transport aborted"), reject);
|
|
16070
|
+
if (signal) {
|
|
16071
|
+
signal.addEventListener("abort", onParentAbort, { once: true });
|
|
16072
|
+
cleanupParent = () => signal.removeEventListener("abort", onParentAbort);
|
|
16073
|
+
}
|
|
16074
|
+
timer = setTimeout(() => {
|
|
16075
|
+
timedOut = true;
|
|
16076
|
+
abort(dynamoDbTimeoutError(deadline), reject);
|
|
16077
|
+
}, timeoutMs);
|
|
16078
|
+
operation(controller.signal).then((value) => {
|
|
16079
|
+
if (settled)
|
|
16080
|
+
return;
|
|
16081
|
+
settled = true;
|
|
16082
|
+
resolve2(value);
|
|
16083
|
+
}, (err) => {
|
|
16084
|
+
if (settled)
|
|
16085
|
+
return;
|
|
16086
|
+
settled = true;
|
|
16087
|
+
if (timedOut) {
|
|
16088
|
+
reject(dynamoDbTimeoutError(deadline));
|
|
16089
|
+
} else if (signal?.aborted) {
|
|
16090
|
+
reject(new DynamoDbHttpError(503, "DynamoDB HTTP transport aborted"));
|
|
16091
|
+
} else {
|
|
16092
|
+
reject(err);
|
|
16093
|
+
}
|
|
16094
|
+
});
|
|
16095
|
+
});
|
|
16096
|
+
return guarded.finally(() => {
|
|
16097
|
+
if (timer)
|
|
16098
|
+
clearTimeout(timer);
|
|
16099
|
+
cleanupParent();
|
|
16100
|
+
});
|
|
16101
|
+
}
|
|
16102
|
+
async function readStreamChunkWithTimeout2(reader, signal, deadline) {
|
|
16103
|
+
return guardedDynamoDbTransport(signal, (transportSignal) => {
|
|
16104
|
+
const cancelRead = () => {
|
|
16105
|
+
reader.cancel(transportSignal.reason).catch(() => {
|
|
16106
|
+
return;
|
|
16107
|
+
});
|
|
16108
|
+
};
|
|
16109
|
+
if (transportSignal.aborted) {
|
|
16110
|
+
cancelRead();
|
|
16111
|
+
} else {
|
|
16112
|
+
transportSignal.addEventListener("abort", cancelRead, { once: true });
|
|
16113
|
+
}
|
|
16114
|
+
return reader.read().finally(() => {
|
|
16115
|
+
transportSignal.removeEventListener("abort", cancelRead);
|
|
16116
|
+
});
|
|
16117
|
+
}, deadline);
|
|
16118
|
+
}
|
|
16119
|
+
async function readResponseBytesWithTimeout2(res, signal, deadline) {
|
|
16120
|
+
if (!res.body)
|
|
16121
|
+
return new Uint8Array;
|
|
16122
|
+
const reader = res.body.getReader();
|
|
16123
|
+
const chunks = [];
|
|
16124
|
+
let total = 0;
|
|
16125
|
+
try {
|
|
16126
|
+
for (;; ) {
|
|
16127
|
+
const { done, value } = await readStreamChunkWithTimeout2(reader, signal, deadline);
|
|
16128
|
+
if (done)
|
|
16129
|
+
break;
|
|
16130
|
+
if (!value?.byteLength)
|
|
16131
|
+
continue;
|
|
16132
|
+
chunks.push(value);
|
|
16133
|
+
total += value.byteLength;
|
|
16134
|
+
}
|
|
16135
|
+
} finally {
|
|
16136
|
+
try {
|
|
16137
|
+
reader.releaseLock();
|
|
16138
|
+
} catch {}
|
|
16139
|
+
}
|
|
16140
|
+
if (chunks.length === 1)
|
|
16141
|
+
return chunks[0];
|
|
16142
|
+
const bytes = new Uint8Array(total);
|
|
16143
|
+
let offset = 0;
|
|
16144
|
+
for (const chunk of chunks) {
|
|
16145
|
+
bytes.set(chunk, offset);
|
|
16146
|
+
offset += chunk.byteLength;
|
|
16147
|
+
}
|
|
16148
|
+
return bytes;
|
|
16149
|
+
}
|
|
16150
|
+
async function readResponseTextWithTimeout2(res, signal, deadline) {
|
|
16151
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(await readResponseBytesWithTimeout2(res, signal, deadline));
|
|
16152
|
+
}
|
|
16153
|
+
async function dockerCurlFetch2(opts) {
|
|
16154
|
+
const { args, input } = dockerCurlCommand2(opts);
|
|
16155
|
+
if (spawnSyncImplIsTestOverride2) {
|
|
16156
|
+
const proc2 = spawnSyncImpl4(dockerCommand(), args, {
|
|
16157
|
+
encoding: "buffer",
|
|
16158
|
+
input,
|
|
16159
|
+
timeout: dynamoDbDockerCurlTimeoutMs,
|
|
16160
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
16161
|
+
});
|
|
16162
|
+
if ((proc2.status ?? 1) !== 0) {
|
|
16163
|
+
const stderr = new TextDecoder().decode(proc2.stderr || new Uint8Array).concat(proc2.error ? `
|
|
16164
|
+
${proc2.error.message}` : "").replace(/\s+/g, " ").trim();
|
|
16165
|
+
throwIfDockerCommandUnavailableResult({
|
|
16166
|
+
code: proc2.status ?? 1,
|
|
16167
|
+
stderr
|
|
16168
|
+
});
|
|
16169
|
+
throw new DynamoDbHttpError(503, `DynamoDB HTTP transport failed via docker exec${stderr ? `: ${stderr.slice(0, 240)}` : ""}`);
|
|
16170
|
+
}
|
|
16171
|
+
return responseFromCurlOutput2(new Uint8Array(proc2.stdout || new Uint8Array));
|
|
16172
|
+
}
|
|
16173
|
+
if (opts.signal?.aborted) {
|
|
16174
|
+
throw new DynamoDbHttpError(503, "DynamoDB HTTP transport aborted");
|
|
16175
|
+
}
|
|
16176
|
+
const proc = await spawnCollectAsync({
|
|
16177
|
+
command: dockerCommand(),
|
|
16178
|
+
args,
|
|
16179
|
+
input,
|
|
16180
|
+
timeoutMs: dynamoDbDockerCurlTimeoutMs,
|
|
16181
|
+
signal: opts.signal,
|
|
16182
|
+
abortMessage: "DynamoDB HTTP transport aborted",
|
|
16183
|
+
timeoutMessage: `docker exec curl timed out after ${dynamoDbDockerCurlTimeoutMs}ms`,
|
|
16184
|
+
rejectOnError: false
|
|
16185
|
+
});
|
|
16186
|
+
if (proc.code !== 0) {
|
|
16187
|
+
const stderr = new TextDecoder().decode(proc.stderr).replace(/\s+/g, " ").trim();
|
|
16188
|
+
throwIfDockerCommandUnavailableResult({ code: proc.code, stderr });
|
|
16189
|
+
throw new DynamoDbHttpError(503, `DynamoDB HTTP transport failed via docker exec${stderr ? `: ${stderr.slice(0, 240)}` : ""}`);
|
|
16190
|
+
}
|
|
16191
|
+
return responseFromCurlOutput2(new Uint8Array(proc.stdout));
|
|
16192
|
+
}
|
|
16193
|
+
function sanitizeDynamoDbError(status, text) {
|
|
16194
|
+
let message = "";
|
|
16195
|
+
try {
|
|
16196
|
+
const parsed = JSON.parse(text);
|
|
16197
|
+
const type = typeof parsed.__type === "string" ? parsed.__type.split("#").pop() || parsed.__type : "";
|
|
16198
|
+
const bodyMessage = typeof parsed.message === "string" ? parsed.message : typeof parsed.Message === "string" ? parsed.Message : "";
|
|
16199
|
+
message = [type, bodyMessage].filter(Boolean).join(": ");
|
|
16200
|
+
} catch {
|
|
16201
|
+
message = text.replace(/\s+/g, " ").trim().slice(0, 240);
|
|
16202
|
+
}
|
|
16203
|
+
return new DynamoDbHttpError(status, `DynamoDB HTTP ${status}: ${message || "request failed"}`);
|
|
16204
|
+
}
|
|
16205
|
+
function assertTableName(tableName) {
|
|
16206
|
+
if (!/^[A-Za-z0-9_.-]{3,255}$/.test(tableName)) {
|
|
16207
|
+
throw new Error("invalid DynamoDB table name");
|
|
16208
|
+
}
|
|
16209
|
+
}
|
|
16210
|
+
function asObject(value) {
|
|
16211
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
16212
|
+
}
|
|
16213
|
+
function asStringArray(value) {
|
|
16214
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
16215
|
+
}
|
|
16216
|
+
function asNumber(value) {
|
|
16217
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
16218
|
+
}
|
|
16219
|
+
function createDynamoDbAdapter(config) {
|
|
16220
|
+
async function signedJsonRequest(action, body, signal, deadline = createDynamoDbTransportDeadline(config)) {
|
|
16221
|
+
const requestBody = JSON.stringify(body);
|
|
16222
|
+
const res = await guardedDynamoDbTransport(signal, (transportSignal) => {
|
|
16223
|
+
const endpoint = new URL(config.endpoint);
|
|
16224
|
+
const { dateStamp, amzDate: requestDate } = amzDate2();
|
|
16225
|
+
const payloadHash = requestBody ? sha2562(requestBody) : EMPTY_SHA2562;
|
|
16226
|
+
const headers = {
|
|
16227
|
+
"content-type": DYNAMODB_JSON_CONTENT_TYPE,
|
|
16228
|
+
host: endpoint.host,
|
|
16229
|
+
"x-amz-content-sha256": payloadHash,
|
|
16230
|
+
"x-amz-date": requestDate,
|
|
16231
|
+
"x-amz-target": `DynamoDB_20120810.${action}`,
|
|
16232
|
+
...config.sessionToken ? { "x-amz-security-token": config.sessionToken } : {}
|
|
16233
|
+
};
|
|
16234
|
+
const signedNames = signedHeadersString2(headers);
|
|
16235
|
+
const canonicalRequest = [
|
|
16236
|
+
"POST",
|
|
16237
|
+
"/",
|
|
16238
|
+
"",
|
|
16239
|
+
canonicalHeaders2(headers),
|
|
16240
|
+
signedNames,
|
|
16241
|
+
payloadHash
|
|
16242
|
+
].join(`
|
|
16243
|
+
`);
|
|
16244
|
+
const scope = `${dateStamp}/${config.region}/dynamodb/aws4_request`;
|
|
16245
|
+
const stringToSign = [
|
|
16246
|
+
"AWS4-HMAC-SHA256",
|
|
16247
|
+
requestDate,
|
|
16248
|
+
scope,
|
|
16249
|
+
sha2562(canonicalRequest)
|
|
16250
|
+
].join(`
|
|
16251
|
+
`);
|
|
16252
|
+
const signature = createHmac2("sha256", signingKey2(config.secretAccessKey, dateStamp, config.region)).update(stringToSign, "utf8").digest("hex");
|
|
16253
|
+
const requestHeaders = new Headers;
|
|
16254
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
16255
|
+
if (key !== "host")
|
|
16256
|
+
requestHeaders.set(key, value);
|
|
16257
|
+
}
|
|
16258
|
+
requestHeaders.set("Authorization", `AWS4-HMAC-SHA256 Credential=${config.accessKeyId}/${scope}, SignedHeaders=${signedNames}, Signature=${signature}`);
|
|
16259
|
+
const url = `${config.endpoint.replace(/\/$/, "")}/`;
|
|
16260
|
+
if (config.dockerContainerName) {
|
|
16261
|
+
return dockerCurlFetch2({
|
|
16262
|
+
containerName: config.dockerContainerName,
|
|
16263
|
+
url,
|
|
16264
|
+
headers: requestHeaders,
|
|
16265
|
+
body: requestBody,
|
|
16266
|
+
signal: transportSignal
|
|
16267
|
+
});
|
|
16268
|
+
}
|
|
16269
|
+
return fetch(url, {
|
|
16270
|
+
method: "POST",
|
|
16271
|
+
headers: requestHeaders,
|
|
16272
|
+
body: requestBody,
|
|
16273
|
+
signal: transportSignal
|
|
16274
|
+
});
|
|
16275
|
+
}, deadline);
|
|
16276
|
+
const text = await readResponseTextWithTimeout2(res, signal, deadline);
|
|
16277
|
+
if (!res.ok)
|
|
16278
|
+
throw sanitizeDynamoDbError(res.status, text);
|
|
16279
|
+
return text ? JSON.parse(text) : {};
|
|
16280
|
+
}
|
|
16281
|
+
async function listTablesAsync(opts) {
|
|
16282
|
+
const body = {
|
|
16283
|
+
...opts?.limit ? { Limit: Math.min(100, Math.max(1, opts.limit)) } : {},
|
|
16284
|
+
...opts?.exclusiveStartTableName ? { ExclusiveStartTableName: opts.exclusiveStartTableName } : {}
|
|
16285
|
+
};
|
|
16286
|
+
const raw = await signedJsonRequest("ListTables", body, opts?.signal);
|
|
16287
|
+
return {
|
|
16288
|
+
tableNames: asStringArray(raw.TableNames),
|
|
16289
|
+
...typeof raw.LastEvaluatedTableName === "string" ? { lastEvaluatedTableName: raw.LastEvaluatedTableName } : {}
|
|
16290
|
+
};
|
|
16291
|
+
}
|
|
16292
|
+
async function describeTableAsync(tableName, signal) {
|
|
16293
|
+
assertTableName(tableName);
|
|
16294
|
+
const raw = await signedJsonRequest("DescribeTable", { TableName: tableName }, signal);
|
|
16295
|
+
return asObject(raw.Table);
|
|
16296
|
+
}
|
|
16297
|
+
async function scanAsync(opts) {
|
|
16298
|
+
assertTableName(opts.tableName);
|
|
16299
|
+
const raw = await signedJsonRequest("Scan", {
|
|
16300
|
+
TableName: opts.tableName,
|
|
16301
|
+
...opts.limit ? { Limit: Math.min(1000, Math.max(1, opts.limit)) } : {},
|
|
16302
|
+
...opts.exclusiveStartKey ? { ExclusiveStartKey: opts.exclusiveStartKey } : {},
|
|
16303
|
+
...opts.indexName ? { IndexName: opts.indexName } : {},
|
|
16304
|
+
...opts.projectionExpression ? { ProjectionExpression: opts.projectionExpression } : {},
|
|
16305
|
+
...opts.filterExpression ? { FilterExpression: opts.filterExpression } : {},
|
|
16306
|
+
...opts.expressionAttributeNames ? { ExpressionAttributeNames: opts.expressionAttributeNames } : {},
|
|
16307
|
+
...opts.expressionAttributeValues ? { ExpressionAttributeValues: opts.expressionAttributeValues } : {}
|
|
16308
|
+
}, opts.signal);
|
|
16309
|
+
return {
|
|
16310
|
+
items: Array.isArray(raw.Items) ? raw.Items : [],
|
|
16311
|
+
count: asNumber(raw.Count),
|
|
16312
|
+
scannedCount: asNumber(raw.ScannedCount),
|
|
16313
|
+
...raw.LastEvaluatedKey ? { lastEvaluatedKey: asObject(raw.LastEvaluatedKey) } : {}
|
|
16314
|
+
};
|
|
16315
|
+
}
|
|
16316
|
+
async function queryAsync(opts) {
|
|
16317
|
+
assertTableName(opts.tableName);
|
|
16318
|
+
const raw = await signedJsonRequest("Query", {
|
|
16319
|
+
TableName: opts.tableName,
|
|
16320
|
+
KeyConditionExpression: opts.keyConditionExpression,
|
|
16321
|
+
...opts.limit ? { Limit: Math.min(1000, Math.max(1, opts.limit)) } : {},
|
|
16322
|
+
...opts.exclusiveStartKey ? { ExclusiveStartKey: opts.exclusiveStartKey } : {},
|
|
16323
|
+
...opts.indexName ? { IndexName: opts.indexName } : {},
|
|
16324
|
+
...opts.projectionExpression ? { ProjectionExpression: opts.projectionExpression } : {},
|
|
16325
|
+
...opts.filterExpression ? { FilterExpression: opts.filterExpression } : {},
|
|
16326
|
+
...opts.expressionAttributeNames ? { ExpressionAttributeNames: opts.expressionAttributeNames } : {},
|
|
16327
|
+
...opts.expressionAttributeValues ? { ExpressionAttributeValues: opts.expressionAttributeValues } : {},
|
|
16328
|
+
...opts.scanIndexForward !== undefined ? { ScanIndexForward: opts.scanIndexForward } : {}
|
|
16329
|
+
}, opts.signal);
|
|
16330
|
+
return {
|
|
16331
|
+
items: Array.isArray(raw.Items) ? raw.Items : [],
|
|
16332
|
+
count: asNumber(raw.Count),
|
|
16333
|
+
scannedCount: asNumber(raw.ScannedCount),
|
|
16334
|
+
...raw.LastEvaluatedKey ? { lastEvaluatedKey: asObject(raw.LastEvaluatedKey) } : {}
|
|
16335
|
+
};
|
|
16336
|
+
}
|
|
16337
|
+
async function getItemAsync(opts) {
|
|
16338
|
+
assertTableName(opts.tableName);
|
|
16339
|
+
const raw = await signedJsonRequest("GetItem", {
|
|
16340
|
+
TableName: opts.tableName,
|
|
16341
|
+
Key: opts.key,
|
|
16342
|
+
...opts.projectionExpression ? { ProjectionExpression: opts.projectionExpression } : {},
|
|
16343
|
+
...opts.expressionAttributeNames ? { ExpressionAttributeNames: opts.expressionAttributeNames } : {},
|
|
16344
|
+
...opts.consistentRead !== undefined ? { ConsistentRead: opts.consistentRead } : {}
|
|
16345
|
+
}, opts.signal);
|
|
16346
|
+
return {
|
|
16347
|
+
...raw.Item ? { item: asObject(raw.Item) } : {},
|
|
16348
|
+
...raw.ConsumedCapacity ? { consumedCapacity: raw.ConsumedCapacity } : {}
|
|
16349
|
+
};
|
|
16350
|
+
}
|
|
16351
|
+
return {
|
|
16352
|
+
kind: "dynamodb",
|
|
16353
|
+
model: "document",
|
|
16354
|
+
close() {},
|
|
16355
|
+
listTablesAsync,
|
|
16356
|
+
describeTableAsync,
|
|
16357
|
+
scanAsync,
|
|
16358
|
+
queryAsync,
|
|
16359
|
+
getItemAsync
|
|
16360
|
+
};
|
|
15135
16361
|
}
|
|
15136
|
-
function
|
|
15137
|
-
|
|
16362
|
+
async function dynamoDbConfigFromDockerInfoAsync(info, signal) {
|
|
16363
|
+
const env = info.env;
|
|
16364
|
+
const dockerContainerName = info.hostPort ? undefined : await resolveRunningComposeContainerNameOrThrowAsync(info.serviceName, info.composeDir, signal);
|
|
16365
|
+
return {
|
|
16366
|
+
endpoint: info.hostPort ? `http://localhost:${info.hostPort}` : `http://127.0.0.1:${info.containerPort}`,
|
|
16367
|
+
...dockerContainerName ? { dockerContainerName } : {},
|
|
16368
|
+
region: env.AWS_REGION || env.AWS_DEFAULT_REGION || "us-east-1",
|
|
16369
|
+
accessKeyId: env.AWS_ACCESS_KEY_ID || "test",
|
|
16370
|
+
secretAccessKey: env.AWS_SECRET_ACCESS_KEY || "test",
|
|
16371
|
+
...env.AWS_SESSION_TOKEN ? { sessionToken: env.AWS_SESSION_TOKEN } : {}
|
|
16372
|
+
};
|
|
15138
16373
|
}
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
16374
|
+
async function openDynamoDbExplorerAsync(info, signal) {
|
|
16375
|
+
return createDynamoDbAdapter(await dynamoDbConfigFromDockerInfoAsync(info, signal));
|
|
16376
|
+
}
|
|
16377
|
+
function isDynamoDbHttpError(err) {
|
|
16378
|
+
return err instanceof DynamoDbHttpError;
|
|
16379
|
+
}
|
|
16380
|
+
var spawnSyncImpl4, spawnSyncImplIsTestOverride2 = false, DynamoDbHttpError, EMPTY_SHA2562 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", DEFAULT_DYNAMODB_REQUEST_TIMEOUT_MS = 5000, DEFAULT_DYNAMODB_DOCKER_CURL_TIMEOUT_MS = 30000, DYNAMODB_JSON_CONTENT_TYPE = "application/x-amz-json-1.0", DOCKER_CURL_BODY_MARKER = "__CODE_VIEWER_DYNAMODB_BODY__", dynamoDbRequestTimeoutMs, dynamoDbDockerCurlTimeoutMs;
|
|
16381
|
+
var init_dynamodb = __esm(() => {
|
|
16382
|
+
init_docker_utils();
|
|
16383
|
+
init_spawn_runner();
|
|
16384
|
+
spawnSyncImpl4 = spawnSync5;
|
|
16385
|
+
DynamoDbHttpError = class DynamoDbHttpError extends Error {
|
|
16386
|
+
status;
|
|
16387
|
+
constructor(status, message) {
|
|
16388
|
+
super(message);
|
|
16389
|
+
this.status = status;
|
|
16390
|
+
}
|
|
16391
|
+
};
|
|
16392
|
+
dynamoDbRequestTimeoutMs = DEFAULT_DYNAMODB_REQUEST_TIMEOUT_MS;
|
|
16393
|
+
dynamoDbDockerCurlTimeoutMs = DEFAULT_DYNAMODB_DOCKER_CURL_TIMEOUT_MS;
|
|
15142
16394
|
});
|
|
15143
16395
|
|
|
15144
16396
|
// web-src/server/database/handle-shared.ts
|
|
@@ -15441,6 +16693,247 @@ var init_handle_shared = __esm(() => {
|
|
|
15441
16693
|
logQueue = Promise.resolve();
|
|
15442
16694
|
});
|
|
15443
16695
|
|
|
16696
|
+
// web-src/server/database/handle-dynamodb.ts
|
|
16697
|
+
var exports_handle_dynamodb = {};
|
|
16698
|
+
__export(exports_handle_dynamodb, {
|
|
16699
|
+
handleDynamoDbRoute: () => handleDynamoDbRoute,
|
|
16700
|
+
closeDynamoDbAdapter: () => closeDynamoDbAdapter
|
|
16701
|
+
});
|
|
16702
|
+
function closeDynamoDbAdapter(dbId) {
|
|
16703
|
+
dynamoDbAdapterCache.close(dbId);
|
|
16704
|
+
}
|
|
16705
|
+
function resolveDynamoDb(cwd, dbParam, signal, omitDirNames) {
|
|
16706
|
+
return resolveDockerExplorerAsync(cwd, dbParam, "dynamodb", dynamoDbAdapterCache, (info) => openDynamoDbExplorerAsync(info), omitDirNames, signal);
|
|
16707
|
+
}
|
|
16708
|
+
function dynamoDbErrorResponse(err, action, signal) {
|
|
16709
|
+
if (isAbortLikeError(err, signal)) {
|
|
16710
|
+
return handleError("dynamodb", action, err, signal);
|
|
16711
|
+
}
|
|
16712
|
+
if (isDynamoDbHttpError(err))
|
|
16713
|
+
return textError(err.message, err.status);
|
|
16714
|
+
return handleError("dynamodb", action, err, signal);
|
|
16715
|
+
}
|
|
16716
|
+
function validateTableName(value) {
|
|
16717
|
+
if (!value)
|
|
16718
|
+
return textError("missing table parameter", 400);
|
|
16719
|
+
if (value.length < 3 || value.length > MAX_TABLE_NAME_LEN || hasControlCharacter(value) || !/^[A-Za-z0-9_.-]+$/.test(value)) {
|
|
16720
|
+
return textError("invalid table parameter", 400);
|
|
16721
|
+
}
|
|
16722
|
+
return value;
|
|
16723
|
+
}
|
|
16724
|
+
function validateOptionalText(value, name, maxLen = MAX_EXPRESSION_LEN) {
|
|
16725
|
+
if (value === null || value === "")
|
|
16726
|
+
return;
|
|
16727
|
+
if (value.length > maxLen || hasControlCharacter(value)) {
|
|
16728
|
+
return textError(`invalid ${name} parameter`, 400);
|
|
16729
|
+
}
|
|
16730
|
+
return value;
|
|
16731
|
+
}
|
|
16732
|
+
function parseLimit2(url, param, defaultValue) {
|
|
16733
|
+
const raw = Number(url.searchParams.get(param) || defaultValue);
|
|
16734
|
+
return Math.min(param === "limit" ? MAX_ITEMS_LIMIT : MAX_TABLES_LIMIT, Math.max(1, Number.isFinite(raw) ? raw : defaultValue));
|
|
16735
|
+
}
|
|
16736
|
+
function parseJsonParam(value, name) {
|
|
16737
|
+
if (value === null || value === "")
|
|
16738
|
+
return;
|
|
16739
|
+
if (value.length > MAX_JSON_PARAM_LEN || hasControlCharacter(value)) {
|
|
16740
|
+
return textError(`invalid ${name} parameter`, 400);
|
|
16741
|
+
}
|
|
16742
|
+
try {
|
|
16743
|
+
return JSON.parse(value);
|
|
16744
|
+
} catch {
|
|
16745
|
+
return textError(`invalid ${name} parameter`, 400);
|
|
16746
|
+
}
|
|
16747
|
+
}
|
|
16748
|
+
function parseKey(value, name) {
|
|
16749
|
+
const parsed = parseJsonParam(value, name);
|
|
16750
|
+
if (parsed instanceof Response)
|
|
16751
|
+
return parsed;
|
|
16752
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16753
|
+
return textError(`missing ${name} parameter`, 400);
|
|
16754
|
+
}
|
|
16755
|
+
return parsed;
|
|
16756
|
+
}
|
|
16757
|
+
function parseExpressionAttributeNames(url) {
|
|
16758
|
+
const parsed = parseJsonParam(url.searchParams.get("expressionAttributeNames"), "expressionAttributeNames");
|
|
16759
|
+
if (parsed instanceof Response || parsed === undefined)
|
|
16760
|
+
return parsed;
|
|
16761
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || Object.values(parsed).some((value) => typeof value !== "string")) {
|
|
16762
|
+
return textError("invalid expressionAttributeNames parameter", 400);
|
|
16763
|
+
}
|
|
16764
|
+
return parsed;
|
|
16765
|
+
}
|
|
16766
|
+
function parseExpressionAttributeValues(url) {
|
|
16767
|
+
const parsed = parseJsonParam(url.searchParams.get("expressionAttributeValues"), "expressionAttributeValues");
|
|
16768
|
+
if (parsed instanceof Response || parsed === undefined)
|
|
16769
|
+
return parsed;
|
|
16770
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16771
|
+
return textError("invalid expressionAttributeValues parameter", 400);
|
|
16772
|
+
}
|
|
16773
|
+
return parsed;
|
|
16774
|
+
}
|
|
16775
|
+
async function handleTables(req, cwd, url, omitDirNames) {
|
|
16776
|
+
const r = await resolveDynamoDb(cwd, url.searchParams.get("db"), req.signal, omitDirNames);
|
|
16777
|
+
if (r instanceof Response)
|
|
16778
|
+
return r;
|
|
16779
|
+
const exclusiveStartTableName = validateOptionalText(url.searchParams.get("exclusiveStartTableName"), "exclusiveStartTableName", MAX_TABLE_NAME_LEN);
|
|
16780
|
+
if (exclusiveStartTableName instanceof Response)
|
|
16781
|
+
return exclusiveStartTableName;
|
|
16782
|
+
try {
|
|
16783
|
+
const result = await r.explorer.listTablesAsync({
|
|
16784
|
+
limit: parseLimit2(url, "tablesLimit", MAX_TABLES_LIMIT),
|
|
16785
|
+
...exclusiveStartTableName ? { exclusiveStartTableName } : {},
|
|
16786
|
+
signal: req.signal
|
|
16787
|
+
});
|
|
16788
|
+
const body = { dbId: r.dbId, ...result };
|
|
16789
|
+
return json(body);
|
|
16790
|
+
} catch (err) {
|
|
16791
|
+
return dynamoDbErrorResponse(err, "list dynamodb tables", req.signal);
|
|
16792
|
+
}
|
|
16793
|
+
}
|
|
16794
|
+
async function handleTable(req, cwd, url, omitDirNames) {
|
|
16795
|
+
const r = await resolveDynamoDb(cwd, url.searchParams.get("db"), req.signal, omitDirNames);
|
|
16796
|
+
if (r instanceof Response)
|
|
16797
|
+
return r;
|
|
16798
|
+
const tableName = validateTableName(url.searchParams.get("table"));
|
|
16799
|
+
if (tableName instanceof Response)
|
|
16800
|
+
return tableName;
|
|
16801
|
+
try {
|
|
16802
|
+
const table = await r.explorer.describeTableAsync(tableName, req.signal);
|
|
16803
|
+
const body = { dbId: r.dbId, table };
|
|
16804
|
+
return json(body);
|
|
16805
|
+
} catch (err) {
|
|
16806
|
+
return dynamoDbErrorResponse(err, "describe dynamodb table", req.signal);
|
|
16807
|
+
}
|
|
16808
|
+
}
|
|
16809
|
+
async function handleItems(req, cwd, url, omitDirNames) {
|
|
16810
|
+
const r = await resolveDynamoDb(cwd, url.searchParams.get("db"), req.signal, omitDirNames);
|
|
16811
|
+
if (r instanceof Response)
|
|
16812
|
+
return r;
|
|
16813
|
+
const tableName = validateTableName(url.searchParams.get("table"));
|
|
16814
|
+
if (tableName instanceof Response)
|
|
16815
|
+
return tableName;
|
|
16816
|
+
const mode = url.searchParams.get("mode") || "scan";
|
|
16817
|
+
if (mode !== "scan" && mode !== "query") {
|
|
16818
|
+
return textError("invalid mode parameter", 400);
|
|
16819
|
+
}
|
|
16820
|
+
const exclusiveStartKey = parseJsonParam(url.searchParams.get("exclusiveStartKey"), "exclusiveStartKey");
|
|
16821
|
+
if (exclusiveStartKey instanceof Response)
|
|
16822
|
+
return exclusiveStartKey;
|
|
16823
|
+
const expressionAttributeNames = parseExpressionAttributeNames(url);
|
|
16824
|
+
if (expressionAttributeNames instanceof Response)
|
|
16825
|
+
return expressionAttributeNames;
|
|
16826
|
+
const expressionAttributeValues = parseExpressionAttributeValues(url);
|
|
16827
|
+
if (expressionAttributeValues instanceof Response)
|
|
16828
|
+
return expressionAttributeValues;
|
|
16829
|
+
const indexName = validateOptionalText(url.searchParams.get("index"), "index");
|
|
16830
|
+
if (indexName instanceof Response)
|
|
16831
|
+
return indexName;
|
|
16832
|
+
const projectionExpression = validateOptionalText(url.searchParams.get("projectionExpression"), "projectionExpression");
|
|
16833
|
+
if (projectionExpression instanceof Response)
|
|
16834
|
+
return projectionExpression;
|
|
16835
|
+
const filterExpression = validateOptionalText(url.searchParams.get("filterExpression"), "filterExpression");
|
|
16836
|
+
if (filterExpression instanceof Response)
|
|
16837
|
+
return filterExpression;
|
|
16838
|
+
const keyConditionExpression = validateOptionalText(url.searchParams.get("keyConditionExpression"), "keyConditionExpression");
|
|
16839
|
+
if (keyConditionExpression instanceof Response)
|
|
16840
|
+
return keyConditionExpression;
|
|
16841
|
+
if (mode === "query" && !keyConditionExpression) {
|
|
16842
|
+
return textError("missing keyConditionExpression parameter", 400);
|
|
16843
|
+
}
|
|
16844
|
+
try {
|
|
16845
|
+
const common = {
|
|
16846
|
+
tableName,
|
|
16847
|
+
limit: parseLimit2(url, "limit", DEFAULT_ITEMS_LIMIT),
|
|
16848
|
+
...exclusiveStartKey ? { exclusiveStartKey } : {},
|
|
16849
|
+
...indexName ? { indexName } : {},
|
|
16850
|
+
...projectionExpression ? { projectionExpression } : {},
|
|
16851
|
+
...filterExpression ? { filterExpression } : {},
|
|
16852
|
+
...expressionAttributeNames ? { expressionAttributeNames } : {},
|
|
16853
|
+
...expressionAttributeValues ? { expressionAttributeValues } : {},
|
|
16854
|
+
signal: req.signal
|
|
16855
|
+
};
|
|
16856
|
+
const result = mode === "query" ? await r.explorer.queryAsync({
|
|
16857
|
+
...common,
|
|
16858
|
+
keyConditionExpression,
|
|
16859
|
+
scanIndexForward: url.searchParams.get("scanIndexForward") !== "false"
|
|
16860
|
+
}) : await r.explorer.scanAsync(common);
|
|
16861
|
+
const body = {
|
|
16862
|
+
dbId: r.dbId,
|
|
16863
|
+
tableName,
|
|
16864
|
+
mode,
|
|
16865
|
+
...result
|
|
16866
|
+
};
|
|
16867
|
+
return json(body);
|
|
16868
|
+
} catch (err) {
|
|
16869
|
+
return dynamoDbErrorResponse(err, "read dynamodb items", req.signal);
|
|
16870
|
+
}
|
|
16871
|
+
}
|
|
16872
|
+
async function handleItem(req, cwd, url, omitDirNames) {
|
|
16873
|
+
const r = await resolveDynamoDb(cwd, url.searchParams.get("db"), req.signal, omitDirNames);
|
|
16874
|
+
if (r instanceof Response)
|
|
16875
|
+
return r;
|
|
16876
|
+
const tableName = validateTableName(url.searchParams.get("table"));
|
|
16877
|
+
if (tableName instanceof Response)
|
|
16878
|
+
return tableName;
|
|
16879
|
+
const key = parseKey(url.searchParams.get("key"), "key");
|
|
16880
|
+
if (key instanceof Response)
|
|
16881
|
+
return key;
|
|
16882
|
+
const expressionAttributeNames = parseExpressionAttributeNames(url);
|
|
16883
|
+
if (expressionAttributeNames instanceof Response)
|
|
16884
|
+
return expressionAttributeNames;
|
|
16885
|
+
const projectionExpression = validateOptionalText(url.searchParams.get("projectionExpression"), "projectionExpression");
|
|
16886
|
+
if (projectionExpression instanceof Response)
|
|
16887
|
+
return projectionExpression;
|
|
16888
|
+
try {
|
|
16889
|
+
const result = await r.explorer.getItemAsync({
|
|
16890
|
+
tableName,
|
|
16891
|
+
key,
|
|
16892
|
+
...projectionExpression ? { projectionExpression } : {},
|
|
16893
|
+
...expressionAttributeNames ? { expressionAttributeNames } : {},
|
|
16894
|
+
consistentRead: url.searchParams.get("consistentRead") === "true",
|
|
16895
|
+
signal: req.signal
|
|
16896
|
+
});
|
|
16897
|
+
const body = {
|
|
16898
|
+
dbId: r.dbId,
|
|
16899
|
+
tableName,
|
|
16900
|
+
key,
|
|
16901
|
+
...result
|
|
16902
|
+
};
|
|
16903
|
+
return json(body);
|
|
16904
|
+
} catch (err) {
|
|
16905
|
+
return dynamoDbErrorResponse(err, "read dynamodb item", req.signal);
|
|
16906
|
+
}
|
|
16907
|
+
}
|
|
16908
|
+
async function handleDynamoDbRoute(req, url, cwd, sideEffectAllowed, omitDirNames) {
|
|
16909
|
+
const wrap = createQueryStrippedLogger("dynamodb", req, url);
|
|
16910
|
+
return dispatchRoutes(req, url, {
|
|
16911
|
+
"/_db/dynamodb/tables": {
|
|
16912
|
+
methods: ["GET"],
|
|
16913
|
+
handler: () => handleTables(req, cwd, url, omitDirNames)
|
|
16914
|
+
},
|
|
16915
|
+
"/_db/dynamodb/table": {
|
|
16916
|
+
methods: ["GET"],
|
|
16917
|
+
handler: () => handleTable(req, cwd, url, omitDirNames)
|
|
16918
|
+
},
|
|
16919
|
+
"/_db/dynamodb/items": {
|
|
16920
|
+
methods: ["GET"],
|
|
16921
|
+
handler: () => handleItems(req, cwd, url, omitDirNames)
|
|
16922
|
+
},
|
|
16923
|
+
"/_db/dynamodb/item": {
|
|
16924
|
+
methods: ["GET"],
|
|
16925
|
+
handler: () => handleItem(req, cwd, url, omitDirNames)
|
|
16926
|
+
}
|
|
16927
|
+
}, sideEffectAllowed, wrap, (err) => handleError("dynamodb", "handle dynamodb request", err, req.signal));
|
|
16928
|
+
}
|
|
16929
|
+
var dynamoDbAdapterCache, DEFAULT_ITEMS_LIMIT = 100, MAX_ITEMS_LIMIT = 1000, MAX_TABLES_LIMIT = 100, MAX_TABLE_NAME_LEN = 255, MAX_EXPRESSION_LEN = 4096, MAX_JSON_PARAM_LEN;
|
|
16930
|
+
var init_handle_dynamodb = __esm(() => {
|
|
16931
|
+
init_dynamodb();
|
|
16932
|
+
init_handle_shared();
|
|
16933
|
+
dynamoDbAdapterCache = createDockerAdapterCache();
|
|
16934
|
+
MAX_JSON_PARAM_LEN = 64 * 1024;
|
|
16935
|
+
});
|
|
16936
|
+
|
|
15444
16937
|
// web-src/server/database/handle-elasticsearch.ts
|
|
15445
16938
|
var exports_handle_elasticsearch = {};
|
|
15446
16939
|
__export(exports_handle_elasticsearch, {
|
|
@@ -15906,7 +17399,7 @@ function validateKey(value) {
|
|
|
15906
17399
|
}
|
|
15907
17400
|
return value;
|
|
15908
17401
|
}
|
|
15909
|
-
function
|
|
17402
|
+
function validateOptionalText2(value, name, maxLen) {
|
|
15910
17403
|
if (!value)
|
|
15911
17404
|
return "";
|
|
15912
17405
|
if (value.length > maxLen || hasControlCharacter(value)) {
|
|
@@ -15914,7 +17407,7 @@ function validateOptionalText(value, name, maxLen) {
|
|
|
15914
17407
|
}
|
|
15915
17408
|
return value;
|
|
15916
17409
|
}
|
|
15917
|
-
function
|
|
17410
|
+
function parseLimit3(url) {
|
|
15918
17411
|
const raw = Number(url.searchParams.get("limit") || DEFAULT_OBJECT_LIMIT);
|
|
15919
17412
|
return Math.min(MAX_OBJECT_LIMIT, Math.max(1, Number.isFinite(raw) ? raw : DEFAULT_OBJECT_LIMIT));
|
|
15920
17413
|
}
|
|
@@ -15999,13 +17492,13 @@ async function handleObjects(cwd, req, url, omitDirNames) {
|
|
|
15999
17492
|
const bucket = validateBucket(url.searchParams.get("bucket"));
|
|
16000
17493
|
if (bucket instanceof Response)
|
|
16001
17494
|
return bucket;
|
|
16002
|
-
const prefix =
|
|
17495
|
+
const prefix = validateOptionalText2(url.searchParams.get("prefix"), "prefix", 2048);
|
|
16003
17496
|
if (prefix instanceof Response)
|
|
16004
17497
|
return prefix;
|
|
16005
|
-
const search =
|
|
17498
|
+
const search = validateOptionalText2(url.searchParams.get("q"), "q", 512);
|
|
16006
17499
|
if (search instanceof Response)
|
|
16007
17500
|
return search;
|
|
16008
|
-
const token =
|
|
17501
|
+
const token = validateOptionalText2(url.searchParams.get("token"), "token", 4096);
|
|
16009
17502
|
if (token instanceof Response)
|
|
16010
17503
|
return token;
|
|
16011
17504
|
const mode = parseMode(url);
|
|
@@ -16014,7 +17507,7 @@ async function handleObjects(cwd, req, url, omitDirNames) {
|
|
|
16014
17507
|
const sort = parseSort2(url);
|
|
16015
17508
|
if (sort instanceof Response)
|
|
16016
17509
|
return sort;
|
|
16017
|
-
const limit =
|
|
17510
|
+
const limit = parseLimit3(url);
|
|
16018
17511
|
try {
|
|
16019
17512
|
if (mode === "prefix") {
|
|
16020
17513
|
const effectivePrefix = search || prefix;
|
|
@@ -16137,10 +17630,10 @@ async function handleFolder(cwd, req, url, omitDirNames) {
|
|
|
16137
17630
|
const bucket = validateBucket(url.searchParams.get("bucket"));
|
|
16138
17631
|
if (bucket instanceof Response)
|
|
16139
17632
|
return bucket;
|
|
16140
|
-
const prefix =
|
|
17633
|
+
const prefix = validateOptionalText2(url.searchParams.get("prefix"), "prefix", 2048);
|
|
16141
17634
|
if (prefix instanceof Response)
|
|
16142
17635
|
return prefix;
|
|
16143
|
-
const token =
|
|
17636
|
+
const token = validateOptionalText2(url.searchParams.get("token"), "token", 4096);
|
|
16144
17637
|
if (token instanceof Response)
|
|
16145
17638
|
return token;
|
|
16146
17639
|
try {
|
|
@@ -16488,7 +17981,7 @@ var init_query_history = __esm(() => {
|
|
|
16488
17981
|
});
|
|
16489
17982
|
|
|
16490
17983
|
// web-src/server/database/snapshot-store.ts
|
|
16491
|
-
import { createHash as
|
|
17984
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
16492
17985
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
16493
17986
|
import { join as join12 } from "node:path";
|
|
16494
17987
|
async function getStoreDb(cwd) {
|
|
@@ -16519,7 +18012,7 @@ function makeId2(prefix) {
|
|
|
16519
18012
|
return `${prefix}-${randomBytes2(8).toString("hex")}`;
|
|
16520
18013
|
}
|
|
16521
18014
|
function hashPayload(payloadJson) {
|
|
16522
|
-
return
|
|
18015
|
+
return createHash6("sha256").update(payloadJson).digest("hex");
|
|
16523
18016
|
}
|
|
16524
18017
|
function hashLengthPrefixed(hasher, value) {
|
|
16525
18018
|
hasher.update(`${Buffer.byteLength(value, "utf8")}:`);
|
|
@@ -16594,7 +18087,7 @@ async function addSnapshotTableRows(cwd, revisionId, rows) {
|
|
|
16594
18087
|
db.exec("BEGIN");
|
|
16595
18088
|
try {
|
|
16596
18089
|
for (const row of rows) {
|
|
16597
|
-
const rowKeyHash =
|
|
18090
|
+
const rowKeyHash = createHash6("sha256").update(row.rowKeyJson).digest("hex");
|
|
16598
18091
|
const payloadHash = hashPayload(row.payloadJson);
|
|
16599
18092
|
insertRow.run(revisionId, rowKeyHash, row.rowKeyJson, row.rowHash, payloadHash);
|
|
16600
18093
|
insertPayload.run(payloadHash, row.payloadJson);
|
|
@@ -16608,7 +18101,7 @@ async function addSnapshotTableRows(cwd, revisionId, rows) {
|
|
|
16608
18101
|
}
|
|
16609
18102
|
}
|
|
16610
18103
|
function computeRevisionTableHash(db, revisionId) {
|
|
16611
|
-
const hasher =
|
|
18104
|
+
const hasher = createHash6("sha256");
|
|
16612
18105
|
hashLengthPrefixed(hasher, `snapshot-table-v${SNAPSHOT_TABLE_HASH_VERSION}`);
|
|
16613
18106
|
let rowCount = 0;
|
|
16614
18107
|
let last;
|
|
@@ -17202,6 +18695,34 @@ function sanitizeS3(v) {
|
|
|
17202
18695
|
return;
|
|
17203
18696
|
return out;
|
|
17204
18697
|
}
|
|
18698
|
+
function sanitizeDynamodb(v) {
|
|
18699
|
+
if (!v || typeof v !== "object")
|
|
18700
|
+
return;
|
|
18701
|
+
const r = v;
|
|
18702
|
+
const out = {};
|
|
18703
|
+
const table = sanitizeOptionalString(r.table, MAX_DYNAMODB_TABLE_LEN);
|
|
18704
|
+
if (table !== undefined)
|
|
18705
|
+
out.table = table;
|
|
18706
|
+
if (r.mode === "scan" || r.mode === "query")
|
|
18707
|
+
out.mode = r.mode;
|
|
18708
|
+
const keyConditionExpression = sanitizeOptionalString(r.keyConditionExpression, MAX_DYNAMODB_EXPRESSION_LEN);
|
|
18709
|
+
if (keyConditionExpression !== undefined)
|
|
18710
|
+
out.keyConditionExpression = keyConditionExpression;
|
|
18711
|
+
const filterExpression = sanitizeOptionalString(r.filterExpression, MAX_DYNAMODB_EXPRESSION_LEN);
|
|
18712
|
+
if (filterExpression !== undefined)
|
|
18713
|
+
out.filterExpression = filterExpression;
|
|
18714
|
+
const expressionAttributeValues = sanitizeOptionalString(r.expressionAttributeValues, MAX_DYNAMODB_ATTRIBUTE_VALUES_LEN);
|
|
18715
|
+
if (expressionAttributeValues !== undefined)
|
|
18716
|
+
out.expressionAttributeValues = expressionAttributeValues;
|
|
18717
|
+
if (typeof r.scanIndexForward === "boolean")
|
|
18718
|
+
out.scanIndexForward = r.scanIndexForward;
|
|
18719
|
+
const itemKey = sanitizeOptionalString(r.itemKey, MAX_DYNAMODB_ITEM_KEY_LEN);
|
|
18720
|
+
if (itemKey !== undefined)
|
|
18721
|
+
out.itemKey = itemKey;
|
|
18722
|
+
if (out.table === undefined && out.mode === undefined && out.keyConditionExpression === undefined && out.filterExpression === undefined && out.expressionAttributeValues === undefined && out.scanIndexForward === undefined && out.itemKey === undefined)
|
|
18723
|
+
return;
|
|
18724
|
+
return out;
|
|
18725
|
+
}
|
|
17205
18726
|
function sanitize(input) {
|
|
17206
18727
|
if (!input || typeof input !== "object")
|
|
17207
18728
|
return emptyState2();
|
|
@@ -17227,7 +18748,7 @@ function sanitize(input) {
|
|
|
17227
18748
|
if (isToolInternalDbId(dbId))
|
|
17228
18749
|
continue;
|
|
17229
18750
|
const schema = sanitizeOptionalString(tab.schema, MAX_SCHEMA_NAME_LEN);
|
|
17230
|
-
const table = sanitizeOptionalString(tab.table,
|
|
18751
|
+
const table = sanitizeOptionalString(tab.table, MAX_TABLE_NAME_LEN2) ?? null;
|
|
17231
18752
|
const view = typeof tab.view === "string" && VALID_VIEWS.has(tab.view) ? tab.view : "data";
|
|
17232
18753
|
const out = { id, dbId, table, view };
|
|
17233
18754
|
if (schema !== undefined)
|
|
@@ -17258,6 +18779,9 @@ function sanitize(input) {
|
|
|
17258
18779
|
const s3 = sanitizeS3(tab.s3);
|
|
17259
18780
|
if (s3 !== undefined)
|
|
17260
18781
|
out.s3 = s3;
|
|
18782
|
+
const dynamodb = sanitizeDynamodb(tab.dynamodb);
|
|
18783
|
+
if (dynamodb !== undefined)
|
|
18784
|
+
out.dynamodb = dynamodb;
|
|
17261
18785
|
tabs.push(out);
|
|
17262
18786
|
}
|
|
17263
18787
|
let activeTabId = sanitizeOptionalString(obj.activeTabId, MAX_TAB_ID_LEN) ?? null;
|
|
@@ -17272,7 +18796,7 @@ async function loadTabsAsync(cwd) {
|
|
|
17272
18796
|
async function saveTabsAsync(cwd, state) {
|
|
17273
18797
|
return tabsStore.save(cwd, state);
|
|
17274
18798
|
}
|
|
17275
|
-
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,
|
|
18799
|
+
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_LEN2 = 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_DYNAMODB_TABLE_LEN = 255, MAX_DYNAMODB_EXPRESSION_LEN = 4096, MAX_DYNAMODB_ATTRIBUTE_VALUES_LEN = 16000, MAX_DYNAMODB_ITEM_KEY_LEN = 4096, MAX_CSS_SIZE_LEN = 16, VALID_VIEWS, tabsStore;
|
|
17276
18800
|
var init_tabs_store = __esm(() => {
|
|
17277
18801
|
init_json_store();
|
|
17278
18802
|
VALID_VIEWS = new Set([
|
|
@@ -17396,6 +18920,9 @@ async function resolveDb(cwd, dbParam, omitDirNames, schemaParam, signal) {
|
|
|
17396
18920
|
if (info.kind === "s3") {
|
|
17397
18921
|
return textError("s3 services must use the /_db/s3/* routes", 400);
|
|
17398
18922
|
}
|
|
18923
|
+
if (info.kind === "dynamodb") {
|
|
18924
|
+
return textError("dynamodb services must use the /_db/dynamodb/* routes", 400);
|
|
18925
|
+
}
|
|
17399
18926
|
const resolved2 = parsed.database ? { ...info, database: parsed.database } : info;
|
|
17400
18927
|
const requestedSchema = normalizeSchemaParam(schemaParam);
|
|
17401
18928
|
if (requestedSchema instanceof Response)
|
|
@@ -17639,7 +19166,7 @@ function groupFiltersByValue(filters) {
|
|
|
17639
19166
|
}
|
|
17640
19167
|
return grouped;
|
|
17641
19168
|
}
|
|
17642
|
-
async function
|
|
19169
|
+
async function handleTable2(cwd, url, omitDirNames, signal) {
|
|
17643
19170
|
const r = await resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"), signal);
|
|
17644
19171
|
if (r instanceof Response)
|
|
17645
19172
|
return r;
|
|
@@ -18630,6 +20157,10 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
18630
20157
|
const { handleS3Route: handleS3Route2 } = await Promise.resolve().then(() => (init_handle_s3(), exports_handle_s3));
|
|
18631
20158
|
return handleS3Route2(req, url, cwd, sideEffectAllowed, omitDirNames);
|
|
18632
20159
|
}
|
|
20160
|
+
if (url.pathname.startsWith("/_db/dynamodb/")) {
|
|
20161
|
+
const { handleDynamoDbRoute: handleDynamoDbRoute2 } = await Promise.resolve().then(() => (init_handle_dynamodb(), exports_handle_dynamodb));
|
|
20162
|
+
return handleDynamoDbRoute2(req, url, cwd, sideEffectAllowed, omitDirNames);
|
|
20163
|
+
}
|
|
18633
20164
|
const start = Date.now();
|
|
18634
20165
|
const method = req.method;
|
|
18635
20166
|
const wrapResponse = (res) => {
|
|
@@ -18653,7 +20184,7 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
18653
20184
|
},
|
|
18654
20185
|
"/_db/table": {
|
|
18655
20186
|
methods: ["GET"],
|
|
18656
|
-
handler: () =>
|
|
20187
|
+
handler: () => handleTable2(cwd, url, omitDirNames, req.signal)
|
|
18657
20188
|
},
|
|
18658
20189
|
"/_db/table-count": {
|
|
18659
20190
|
methods: ["GET"],
|
|
@@ -18768,6 +20299,7 @@ var init_handle = __esm(() => {
|
|
|
18768
20299
|
init_connection_pool();
|
|
18769
20300
|
init_discovery();
|
|
18770
20301
|
init_global_search();
|
|
20302
|
+
init_handle_dynamodb();
|
|
18771
20303
|
init_handle_elasticsearch();
|
|
18772
20304
|
init_handle_redis();
|
|
18773
20305
|
init_handle_s3();
|
|
@@ -18797,7 +20329,8 @@ var init_handle = __esm(() => {
|
|
|
18797
20329
|
},
|
|
18798
20330
|
redis: closeRedisAdapter,
|
|
18799
20331
|
elasticsearch: closeElasticsearchAdapter,
|
|
18800
|
-
s3: closeS3Adapter
|
|
20332
|
+
s3: closeS3Adapter,
|
|
20333
|
+
dynamodb: closeDynamoDbAdapter
|
|
18801
20334
|
};
|
|
18802
20335
|
SNAPSHOT_DOCKER_SOURCE_REGISTRY = {
|
|
18803
20336
|
redis: async (info, requestedContainers, signal) => {
|
|
@@ -20738,13 +22271,6 @@ var init_journal2 = __esm(() => {
|
|
|
20738
22271
|
// web-src/server/search-service.ts
|
|
20739
22272
|
import { existsSync as existsSync7, lstatSync as lstatSync4, readFileSync as readFileSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
20740
22273
|
import { join as join17, relative as relative6 } from "node:path";
|
|
20741
|
-
function rgAvailable(cwd) {
|
|
20742
|
-
if (rgAvailableCache !== null)
|
|
20743
|
-
return rgAvailableCache;
|
|
20744
|
-
const proc = runSync([commandForExternal("rg"), "--version"], cwd);
|
|
20745
|
-
rgAvailableCache = proc.code === 0;
|
|
20746
|
-
return rgAvailableCache;
|
|
20747
|
-
}
|
|
20748
22274
|
async function rgAvailableAsync(cwd) {
|
|
20749
22275
|
if (rgAvailableCache !== null)
|
|
20750
22276
|
return rgAvailableCache;
|
|
@@ -20829,38 +22355,6 @@ function grepWorktreeFallback(env, query, max, paths) {
|
|
|
20829
22355
|
}
|
|
20830
22356
|
return matches;
|
|
20831
22357
|
}
|
|
20832
|
-
function grepWorktree(env, req) {
|
|
20833
|
-
const paths = filterCallerPaths(env, req.paths);
|
|
20834
|
-
if (rgAvailable(env.cwd)) {
|
|
20835
|
-
const safePaths = paths.filter((path) => safeWorktreePath(env, path));
|
|
20836
|
-
const args = buildRgArgs(req.query, req.max, safePaths, req.regex, env.omitDirNames, env.excludeNames);
|
|
20837
|
-
args[0] = commandForExternal("rg");
|
|
20838
|
-
const proc = runSync(args, env.cwd, { timeout: 5000 });
|
|
20839
|
-
const stdout = proc.stdout;
|
|
20840
|
-
const matches2 = parseRgOutput(stdout, req.max, env.omitDirNames, env.excludeNames).filter((match) => isSafePath(match.path) && !isGitInternalPath(match.path) && !isSkippableSearchPath(match.path, env.omitDirNames, env.excludeNames) && !!safeWorktreePath(env, match.path));
|
|
20841
|
-
return {
|
|
20842
|
-
ref: "worktree",
|
|
20843
|
-
engine: "rg",
|
|
20844
|
-
truncated: matches2.length >= req.max,
|
|
20845
|
-
matches: matches2
|
|
20846
|
-
};
|
|
20847
|
-
}
|
|
20848
|
-
if (req.regex) {
|
|
20849
|
-
return {
|
|
20850
|
-
ref: "worktree",
|
|
20851
|
-
engine: "fallback",
|
|
20852
|
-
truncated: false,
|
|
20853
|
-
matches: []
|
|
20854
|
-
};
|
|
20855
|
-
}
|
|
20856
|
-
const matches = grepWorktreeFallback(env, req.query, req.max, paths);
|
|
20857
|
-
return {
|
|
20858
|
-
ref: "worktree",
|
|
20859
|
-
engine: "fallback",
|
|
20860
|
-
truncated: matches.length >= req.max,
|
|
20861
|
-
matches
|
|
20862
|
-
};
|
|
20863
|
-
}
|
|
20864
22358
|
async function grepWorktreeAsync(env, req) {
|
|
20865
22359
|
const paths = filterCallerPaths(env, req.paths);
|
|
20866
22360
|
if (await rgAvailableAsync(env.cwd)) {
|
|
@@ -20899,34 +22393,6 @@ async function grepWorktreeAsync(env, req) {
|
|
|
20899
22393
|
matches
|
|
20900
22394
|
};
|
|
20901
22395
|
}
|
|
20902
|
-
function grepTreeRef(env, req) {
|
|
20903
|
-
const safePaths = filterCallerPaths(env, req.paths);
|
|
20904
|
-
const args = [
|
|
20905
|
-
commandForExternal("git"),
|
|
20906
|
-
"-c",
|
|
20907
|
-
"core.quotepath=false",
|
|
20908
|
-
"grep",
|
|
20909
|
-
"-n",
|
|
20910
|
-
"--column",
|
|
20911
|
-
"-i",
|
|
20912
|
-
req.regex ? "-E" : "-F",
|
|
20913
|
-
"--no-color",
|
|
20914
|
-
"-e",
|
|
20915
|
-
req.query,
|
|
20916
|
-
req.ref,
|
|
20917
|
-
"--",
|
|
20918
|
-
...safePaths
|
|
20919
|
-
];
|
|
20920
|
-
const proc = runSync(args, env.cwd, { timeout: 5000 });
|
|
20921
|
-
const stdout = proc.stdout;
|
|
20922
|
-
const matches = parseGitGrepOutput(stdout, req.ref, req.max, env.omitDirNames, env.excludeNames).slice(0, req.max);
|
|
20923
|
-
return {
|
|
20924
|
-
ref: req.ref,
|
|
20925
|
-
engine: "git",
|
|
20926
|
-
truncated: matches.length >= req.max,
|
|
20927
|
-
matches
|
|
20928
|
-
};
|
|
20929
|
-
}
|
|
20930
22396
|
async function grepTreeRefAsync(env, req) {
|
|
20931
22397
|
const safePaths = filterCallerPaths(env, req.paths);
|
|
20932
22398
|
const args = [
|
|
@@ -20961,38 +22427,10 @@ async function grepTreeRefAsync(env, req) {
|
|
|
20961
22427
|
matches
|
|
20962
22428
|
};
|
|
20963
22429
|
}
|
|
20964
|
-
function grepRepo(env, req) {
|
|
20965
|
-
const isWorktree = req.ref === "worktree" || req.ref === "";
|
|
20966
|
-
if (!isWorktree) {
|
|
20967
|
-
const refCheck = verifyTreeRefResult(req.ref, env.cwd);
|
|
20968
|
-
if (refCheck.ok !== true) {
|
|
20969
|
-
return {
|
|
20970
|
-
ok: false,
|
|
20971
|
-
error: refCheck.error,
|
|
20972
|
-
status: refCheck.status
|
|
20973
|
-
};
|
|
20974
|
-
}
|
|
20975
|
-
}
|
|
20976
|
-
if (!req.query.trim()) {
|
|
20977
|
-
return {
|
|
20978
|
-
ok: true,
|
|
20979
|
-
value: {
|
|
20980
|
-
ref: req.ref,
|
|
20981
|
-
engine: req.ref === "worktree" ? "fallback" : "git",
|
|
20982
|
-
truncated: false,
|
|
20983
|
-
matches: []
|
|
20984
|
-
}
|
|
20985
|
-
};
|
|
20986
|
-
}
|
|
20987
|
-
if (isWorktree) {
|
|
20988
|
-
return { ok: true, value: grepWorktree(env, req) };
|
|
20989
|
-
}
|
|
20990
|
-
return { ok: true, value: grepTreeRef(env, req) };
|
|
20991
|
-
}
|
|
20992
22430
|
async function grepRepoAsync(env, req) {
|
|
20993
22431
|
const isWorktree = req.ref === "worktree" || req.ref === "";
|
|
20994
22432
|
if (!isWorktree) {
|
|
20995
|
-
const refCheck =
|
|
22433
|
+
const refCheck = await verifyTreeRefResultAsync(req.ref, env.cwd);
|
|
20996
22434
|
if (refCheck.ok !== true) {
|
|
20997
22435
|
return {
|
|
20998
22436
|
ok: false,
|
|
@@ -21017,9 +22455,9 @@ async function grepRepoAsync(env, req) {
|
|
|
21017
22455
|
}
|
|
21018
22456
|
return { ok: true, value: await grepTreeRefAsync(env, req) };
|
|
21019
22457
|
}
|
|
21020
|
-
function
|
|
22458
|
+
async function listRepoFilesAsync(env, ref, generation) {
|
|
21021
22459
|
if (ref !== "worktree" && ref !== "") {
|
|
21022
|
-
const refCheck =
|
|
22460
|
+
const refCheck = await verifyTreeRefResultAsync(ref, env.cwd);
|
|
21023
22461
|
if (refCheck.ok !== true) {
|
|
21024
22462
|
return {
|
|
21025
22463
|
ok: false,
|
|
@@ -21029,7 +22467,7 @@ function listRepoFiles(env, ref, generation) {
|
|
|
21029
22467
|
}
|
|
21030
22468
|
}
|
|
21031
22469
|
const effectiveRef = ref || "worktree";
|
|
21032
|
-
const tree =
|
|
22470
|
+
const tree = await listTreeResultAsync(effectiveRef, "", env.cwd, {
|
|
21033
22471
|
recursive: true,
|
|
21034
22472
|
omitDirNames: env.omitDirNames,
|
|
21035
22473
|
excludeNames: env.excludeNames
|
|
@@ -21048,7 +22486,6 @@ var init_search_service = __esm(() => {
|
|
|
21048
22486
|
init_command_resolver();
|
|
21049
22487
|
init_spawn_runner();
|
|
21050
22488
|
init_git();
|
|
21051
|
-
init_runtime();
|
|
21052
22489
|
init_search();
|
|
21053
22490
|
});
|
|
21054
22491
|
|
|
@@ -21652,7 +23089,7 @@ function validateMcpIntegerLimit(raw, fallback, min, max, flag) {
|
|
|
21652
23089
|
}
|
|
21653
23090
|
return { ok: true, value: raw };
|
|
21654
23091
|
}
|
|
21655
|
-
function runFileShowTool(input, defaultCwd) {
|
|
23092
|
+
async function runFileShowTool(input, defaultCwd) {
|
|
21656
23093
|
const params = isPlainObject(input) ? input : {};
|
|
21657
23094
|
const pathRaw = params.path;
|
|
21658
23095
|
if (typeof pathRaw !== "string") {
|
|
@@ -21705,7 +23142,7 @@ function runFileShowTool(input, defaultCwd) {
|
|
|
21705
23142
|
json: true
|
|
21706
23143
|
};
|
|
21707
23144
|
try {
|
|
21708
|
-
const report =
|
|
23145
|
+
const report = await buildFileShowReportAsync(resolved.root, command);
|
|
21709
23146
|
return {
|
|
21710
23147
|
text: JSON.stringify(report, null, 2),
|
|
21711
23148
|
isError: report.error !== undefined
|
|
@@ -21715,7 +23152,7 @@ function runFileShowTool(input, defaultCwd) {
|
|
|
21715
23152
|
return { text: `file show failed: ${detail}`, isError: true };
|
|
21716
23153
|
}
|
|
21717
23154
|
}
|
|
21718
|
-
function runFileBlameTool(input, defaultCwd) {
|
|
23155
|
+
async function runFileBlameTool(input, defaultCwd) {
|
|
21719
23156
|
const params = isPlainObject(input) ? input : {};
|
|
21720
23157
|
const pathRaw = params.path;
|
|
21721
23158
|
if (typeof pathRaw !== "string") {
|
|
@@ -21752,7 +23189,7 @@ function runFileBlameTool(input, defaultCwd) {
|
|
|
21752
23189
|
json: true
|
|
21753
23190
|
};
|
|
21754
23191
|
try {
|
|
21755
|
-
const report =
|
|
23192
|
+
const report = await buildFileBlameReportAsync(resolved.root, command);
|
|
21756
23193
|
return {
|
|
21757
23194
|
text: JSON.stringify(report, null, 2),
|
|
21758
23195
|
isError: report.result.error !== undefined
|
|
@@ -21762,7 +23199,7 @@ function runFileBlameTool(input, defaultCwd) {
|
|
|
21762
23199
|
return { text: `file blame failed: ${detail}`, isError: true };
|
|
21763
23200
|
}
|
|
21764
23201
|
}
|
|
21765
|
-
function runFileHistoryTool(input, defaultCwd) {
|
|
23202
|
+
async function runFileHistoryTool(input, defaultCwd) {
|
|
21766
23203
|
const params = isPlainObject(input) ? input : {};
|
|
21767
23204
|
const pathRaw = params.path;
|
|
21768
23205
|
if (typeof pathRaw !== "string") {
|
|
@@ -21815,7 +23252,7 @@ function runFileHistoryTool(input, defaultCwd) {
|
|
|
21815
23252
|
json: true
|
|
21816
23253
|
};
|
|
21817
23254
|
try {
|
|
21818
|
-
const report =
|
|
23255
|
+
const report = await buildFileHistoryReportAsync(resolved.root, command);
|
|
21819
23256
|
return {
|
|
21820
23257
|
text: JSON.stringify(report, null, 2),
|
|
21821
23258
|
isError: report.result.error !== undefined
|
|
@@ -21825,7 +23262,7 @@ function runFileHistoryTool(input, defaultCwd) {
|
|
|
21825
23262
|
return { text: `file history failed: ${detail}`, isError: true };
|
|
21826
23263
|
}
|
|
21827
23264
|
}
|
|
21828
|
-
function runFileDiffTool(input, defaultCwd) {
|
|
23265
|
+
async function runFileDiffTool(input, defaultCwd) {
|
|
21829
23266
|
const params = isPlainObject(input) ? input : {};
|
|
21830
23267
|
const pathRaw = params.path;
|
|
21831
23268
|
if (typeof pathRaw !== "string") {
|
|
@@ -21929,7 +23366,7 @@ function runFileDiffTool(input, defaultCwd) {
|
|
|
21929
23366
|
json: true
|
|
21930
23367
|
};
|
|
21931
23368
|
try {
|
|
21932
|
-
const report =
|
|
23369
|
+
const report = await buildFileDiffReportAsync(resolved.root, command);
|
|
21933
23370
|
return {
|
|
21934
23371
|
text: JSON.stringify(report, null, 2),
|
|
21935
23372
|
isError: report.error !== undefined
|
|
@@ -21939,7 +23376,7 @@ function runFileDiffTool(input, defaultCwd) {
|
|
|
21939
23376
|
return { text: `file diff failed: ${detail}`, isError: true };
|
|
21940
23377
|
}
|
|
21941
23378
|
}
|
|
21942
|
-
function runSearchFilesTool(input, defaultCwd) {
|
|
23379
|
+
async function runSearchFilesTool(input, defaultCwd) {
|
|
21943
23380
|
const params = isPlainObject(input) ? input : {};
|
|
21944
23381
|
const termRaw = params.term;
|
|
21945
23382
|
if (typeof termRaw !== "string") {
|
|
@@ -21974,7 +23411,7 @@ function runSearchFilesTool(input, defaultCwd) {
|
|
|
21974
23411
|
omitDirNames: [],
|
|
21975
23412
|
excludeNames: DEFAULT_EXCLUDE_NAMES
|
|
21976
23413
|
};
|
|
21977
|
-
const listResult =
|
|
23414
|
+
const listResult = await listRepoFilesAsync(env, refParsed.ref, 1);
|
|
21978
23415
|
if (listResult.ok !== true) {
|
|
21979
23416
|
return { text: listResult.error, isError: true };
|
|
21980
23417
|
}
|
|
@@ -22001,7 +23438,7 @@ function runSearchFilesTool(input, defaultCwd) {
|
|
|
22001
23438
|
};
|
|
22002
23439
|
return { text: JSON.stringify(payload, null, 2) };
|
|
22003
23440
|
}
|
|
22004
|
-
function runSearchCodeTool(input, defaultCwd) {
|
|
23441
|
+
async function runSearchCodeTool(input, defaultCwd) {
|
|
22005
23442
|
const params = isPlainObject(input) ? input : {};
|
|
22006
23443
|
const termRaw = params.term;
|
|
22007
23444
|
if (typeof termRaw !== "string") {
|
|
@@ -22053,7 +23490,7 @@ function runSearchCodeTool(input, defaultCwd) {
|
|
|
22053
23490
|
omitDirNames: [],
|
|
22054
23491
|
excludeNames: DEFAULT_EXCLUDE_NAMES
|
|
22055
23492
|
};
|
|
22056
|
-
const result =
|
|
23493
|
+
const result = await grepRepoAsync(env, {
|
|
22057
23494
|
query: termRaw,
|
|
22058
23495
|
ref: refParsed.ref,
|
|
22059
23496
|
paths,
|
|
@@ -22731,6 +24168,7 @@ Examples:
|
|
|
22731
24168
|
process.exit(1);
|
|
22732
24169
|
}
|
|
22733
24170
|
const candidate = repoRoot(cwd);
|
|
24171
|
+
cwdHasGitRepository = !!candidate;
|
|
22734
24172
|
if (cwdWasExplicit) {
|
|
22735
24173
|
if (candidate === cwd)
|
|
22736
24174
|
cwd = candidate;
|
|
@@ -22946,23 +24384,23 @@ function fileToMeta(file, range, extraQs) {
|
|
|
22946
24384
|
untracked: file.untracked || false
|
|
22947
24385
|
};
|
|
22948
24386
|
}
|
|
22949
|
-
function computePayload(extras, range, pathFilter = "") {
|
|
24387
|
+
async function computePayload(extras, range, pathFilter = "", responseGeneration = generation) {
|
|
22950
24388
|
if (isSameWorktreeRange(range)) {
|
|
22951
24389
|
return {
|
|
22952
24390
|
files: [],
|
|
22953
24391
|
totals: { files: 0, additions: 0, deletions: 0 },
|
|
22954
24392
|
range: "worktree .. worktree",
|
|
22955
24393
|
project: basename3(cwd),
|
|
22956
|
-
branch:
|
|
22957
|
-
generation
|
|
24394
|
+
branch: await currentBranchMetadata(),
|
|
24395
|
+
generation: responseGeneration
|
|
22958
24396
|
};
|
|
22959
24397
|
}
|
|
22960
24398
|
const { args, refs } = buildRangeArgs(range);
|
|
22961
24399
|
const fullArgs = [...extras, ...args];
|
|
22962
|
-
const metaResult =
|
|
24400
|
+
const metaResult = await fileMetaResultAsync(fullArgs, cwd, false);
|
|
22963
24401
|
const files = metaResult.files;
|
|
22964
24402
|
if (!metaResult.error && includeUntracked(range, refs)) {
|
|
22965
|
-
files.push(...
|
|
24403
|
+
files.push(...await untrackedMetaAsync(cwd));
|
|
22966
24404
|
}
|
|
22967
24405
|
const filteredFiles = pathFilter ? files.filter((file) => file.path === pathFilter || file.old_path === pathFilter) : files;
|
|
22968
24406
|
filteredFiles.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
@@ -22989,12 +24427,13 @@ function computePayload(extras, range, pathFilter = "") {
|
|
|
22989
24427
|
totals,
|
|
22990
24428
|
range: label || "HEAD",
|
|
22991
24429
|
project: basename3(cwd),
|
|
22992
|
-
branch:
|
|
22993
|
-
generation,
|
|
24430
|
+
branch: await currentBranchMetadata(),
|
|
24431
|
+
generation: responseGeneration,
|
|
22994
24432
|
...metaResult.error ? { error: metaResult.error } : {}
|
|
22995
24433
|
};
|
|
22996
24434
|
}
|
|
22997
|
-
function handleDiffJson(url) {
|
|
24435
|
+
async function handleDiffJson(url) {
|
|
24436
|
+
const responseGeneration = generation;
|
|
22998
24437
|
const extras = [];
|
|
22999
24438
|
if (url.searchParams.get("ignore_ws") === "1")
|
|
23000
24439
|
extras.push("-w");
|
|
@@ -23008,45 +24447,40 @@ function handleDiffJson(url) {
|
|
|
23008
24447
|
if (path && !safePath(path))
|
|
23009
24448
|
return text("invalid path", 400);
|
|
23010
24449
|
const key = `${range.from}|${range.to}|${url.searchParams.get("ignore_ws") || ""}|${url.searchParams.get("ignore_blank") || ""}|${path}`;
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
if (!cached2 || cached2.sig !== sig) {
|
|
23016
|
-
generation++;
|
|
23017
|
-
payload2.generation = generation;
|
|
23018
|
-
metaCache.clear();
|
|
23019
|
-
fileCache.clear();
|
|
23020
|
-
}
|
|
23021
|
-
const body2 = JSON.stringify(payload2);
|
|
23022
|
-
setTimedCacheEntry(metaCache, key, { body: body2, sig });
|
|
23023
|
-
return new Response(body2, {
|
|
24450
|
+
const noCache = url.searchParams.get("nocache") === "1";
|
|
24451
|
+
const cached = metaCache.get(key);
|
|
24452
|
+
if (!noCache && cacheFresh(cached))
|
|
24453
|
+
return new Response(cached.body, {
|
|
23024
24454
|
headers: {
|
|
23025
24455
|
"Content-Type": "application/json; charset=utf-8",
|
|
23026
24456
|
"Cache-Control": "no-store"
|
|
23027
24457
|
}
|
|
23028
24458
|
});
|
|
23029
|
-
|
|
23030
|
-
|
|
23031
|
-
|
|
23032
|
-
|
|
24459
|
+
const requestSequence = ++diffMetaRequestSequence;
|
|
24460
|
+
latestDiffMetaRequest.set(key, requestSequence);
|
|
24461
|
+
try {
|
|
24462
|
+
const payload = await computePayload(extras, range, path, responseGeneration);
|
|
24463
|
+
if (latestDiffMetaRequest.get(key) !== requestSequence || responseGeneration !== generation)
|
|
24464
|
+
return json2(payload);
|
|
24465
|
+
const sig = JSON.stringify({ ...payload, generation: undefined });
|
|
24466
|
+
if (noCache && (!cached || cached.sig !== sig)) {
|
|
24467
|
+
generation++;
|
|
24468
|
+
payload.generation = generation;
|
|
24469
|
+
metaCache.clear();
|
|
24470
|
+
fileCache.clear();
|
|
24471
|
+
}
|
|
24472
|
+
const body = JSON.stringify(payload);
|
|
24473
|
+
setTimedCacheEntry(metaCache, key, { body, sig });
|
|
24474
|
+
return new Response(body, {
|
|
23033
24475
|
headers: {
|
|
23034
24476
|
"Content-Type": "application/json; charset=utf-8",
|
|
23035
24477
|
"Cache-Control": "no-store"
|
|
23036
24478
|
}
|
|
23037
24479
|
});
|
|
23038
|
-
|
|
23039
|
-
|
|
23040
|
-
|
|
23041
|
-
|
|
23042
|
-
sig: JSON.stringify({ ...payload, generation: undefined })
|
|
23043
|
-
});
|
|
23044
|
-
return new Response(body, {
|
|
23045
|
-
headers: {
|
|
23046
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
23047
|
-
"Cache-Control": "no-store"
|
|
23048
|
-
}
|
|
23049
|
-
});
|
|
24480
|
+
} finally {
|
|
24481
|
+
if (latestDiffMetaRequest.get(key) === requestSequence)
|
|
24482
|
+
latestDiffMetaRequest.delete(key);
|
|
24483
|
+
}
|
|
23050
24484
|
}
|
|
23051
24485
|
function safeRepoPath(path) {
|
|
23052
24486
|
return path === "" || safePath(path);
|
|
@@ -23144,16 +24578,16 @@ function worktreeFileMetadata(path, knownSize) {
|
|
|
23144
24578
|
return {};
|
|
23145
24579
|
}
|
|
23146
24580
|
}
|
|
23147
|
-
function gitFileMetadata(ref, path, knownSize) {
|
|
23148
|
-
const size = knownSize ?? rawFileSize(path, ref);
|
|
23149
|
-
const commitUpdatedAt =
|
|
24581
|
+
async function gitFileMetadata(ref, path, knownSize) {
|
|
24582
|
+
const size = knownSize ?? await rawFileSize(path, ref);
|
|
24583
|
+
const commitUpdatedAt = await lastCommitDateForPathAsync(ref, path, cwd) || undefined;
|
|
23150
24584
|
return {
|
|
23151
24585
|
size: size == null ? undefined : size,
|
|
23152
24586
|
updated_at: commitUpdatedAt,
|
|
23153
24587
|
commit_updated_at: commitUpdatedAt
|
|
23154
24588
|
};
|
|
23155
24589
|
}
|
|
23156
|
-
function directoryMetadata(target, path) {
|
|
24590
|
+
async function directoryMetadata(target, path) {
|
|
23157
24591
|
if (target === "worktree" || target === "") {
|
|
23158
24592
|
const full = path === "" ? safeOpenWorktreePath("") : safeWorktreePath2(path);
|
|
23159
24593
|
if (!full)
|
|
@@ -23168,22 +24602,22 @@ function directoryMetadata(target, path) {
|
|
|
23168
24602
|
return {};
|
|
23169
24603
|
}
|
|
23170
24604
|
}
|
|
23171
|
-
const commitUpdatedAt =
|
|
24605
|
+
const commitUpdatedAt = await lastCommitDateForPathAsync(target, path || ".", cwd) || undefined;
|
|
23172
24606
|
return { updated_at: commitUpdatedAt, commit_updated_at: commitUpdatedAt };
|
|
23173
24607
|
}
|
|
23174
|
-
function fileMetadataForTarget(target, path) {
|
|
24608
|
+
async function fileMetadataForTarget(target, path) {
|
|
23175
24609
|
return target === "worktree" || target === "" ? worktreeFileMetadata(path) : gitFileMetadata(target, path);
|
|
23176
24610
|
}
|
|
23177
|
-
function attachTreeEntryMetadata(target, entry) {
|
|
24611
|
+
async function attachTreeEntryMetadata(target, entry) {
|
|
23178
24612
|
if (entry.type === "tree")
|
|
23179
|
-
return { ...entry, ...directoryMetadata(target, entry.path) };
|
|
24613
|
+
return { ...entry, ...await directoryMetadata(target, entry.path) };
|
|
23180
24614
|
if (entry.type === "commit" && !entry.submodule && (target === "worktree" || target === ""))
|
|
23181
|
-
return { ...entry, ...directoryMetadata(target, entry.path) };
|
|
24615
|
+
return { ...entry, ...await directoryMetadata(target, entry.path) };
|
|
23182
24616
|
if (entry.type !== "blob")
|
|
23183
24617
|
return entry;
|
|
23184
|
-
return { ...entry, ...fileMetadataForTarget(target, entry.path) };
|
|
24618
|
+
return { ...entry, ...await fileMetadataForTarget(target, entry.path) };
|
|
23185
24619
|
}
|
|
23186
|
-
function readReadme(target, dirPath) {
|
|
24620
|
+
async function readReadme(target, dirPath) {
|
|
23187
24621
|
const candidates = ["README.md", "readme.md", "README.markdown", "README"];
|
|
23188
24622
|
for (const name of candidates) {
|
|
23189
24623
|
const path = dirPath ? `${dirPath}/${name}` : name;
|
|
@@ -23197,13 +24631,13 @@ function readReadme(target, dirPath) {
|
|
|
23197
24631
|
continue;
|
|
23198
24632
|
}
|
|
23199
24633
|
}
|
|
23200
|
-
const res =
|
|
24634
|
+
const res = await showAsync(target, path, cwd);
|
|
23201
24635
|
if (res.code === 0)
|
|
23202
24636
|
return { path, text: res.stdout };
|
|
23203
24637
|
}
|
|
23204
24638
|
return null;
|
|
23205
24639
|
}
|
|
23206
|
-
function handleTree(url) {
|
|
24640
|
+
async function handleTree(url) {
|
|
23207
24641
|
const target = url.searchParams.get("ref") || url.searchParams.get("target") || "worktree";
|
|
23208
24642
|
const path = (url.searchParams.get("path") || "").replace(/^\/+|\/+$/g, "");
|
|
23209
24643
|
if (!safeRepoPath(path))
|
|
@@ -23211,7 +24645,7 @@ function handleTree(url) {
|
|
|
23211
24645
|
if ((target === "worktree" || target === "") && isGitInternalPath(path))
|
|
23212
24646
|
return text("forbidden", 403);
|
|
23213
24647
|
if (target !== "worktree") {
|
|
23214
|
-
const refCheck =
|
|
24648
|
+
const refCheck = await verifyTreeRefResultAsync(target, cwd);
|
|
23215
24649
|
if (refCheck.ok !== true)
|
|
23216
24650
|
return text(refCheck.error, refCheck.status ?? 400);
|
|
23217
24651
|
}
|
|
@@ -23221,7 +24655,7 @@ function handleTree(url) {
|
|
|
23221
24655
|
if (invalidScopeExcludeNamesQuery(url))
|
|
23222
24656
|
return text("invalid exclude names", 400);
|
|
23223
24657
|
const excludeNames = scopeExcludeNamesFromQuery(url);
|
|
23224
|
-
const tree =
|
|
24658
|
+
const tree = await listTreeResultAsync(target, path, cwd, {
|
|
23225
24659
|
recursive,
|
|
23226
24660
|
omitDirNames: scopeOmitDirNamesFromQuery(url),
|
|
23227
24661
|
excludeNames
|
|
@@ -23233,17 +24667,17 @@ function handleTree(url) {
|
|
|
23233
24667
|
ref: target,
|
|
23234
24668
|
path,
|
|
23235
24669
|
project: basename3(cwd),
|
|
23236
|
-
branch:
|
|
23237
|
-
entries: recursive ? entries : entries.map((entry) => attachTreeEntryMetadata(target, entry)),
|
|
23238
|
-
readme: readReadme(target, path),
|
|
24670
|
+
branch: await currentBranchMetadata(),
|
|
24671
|
+
entries: recursive ? entries : await Promise.all(entries.map((entry) => attachTreeEntryMetadata(target, entry))),
|
|
24672
|
+
readme: await readReadme(target, path),
|
|
23239
24673
|
upload_enabled: uploadEnabled && (target === "worktree" || target === "")
|
|
23240
24674
|
});
|
|
23241
24675
|
}
|
|
23242
|
-
function handleSettings() {
|
|
24676
|
+
async function handleSettings() {
|
|
23243
24677
|
return json2({
|
|
23244
24678
|
project: basename3(cwd),
|
|
23245
|
-
branch:
|
|
23246
|
-
repo_web_url:
|
|
24679
|
+
branch: await currentBranchMetadata(),
|
|
24680
|
+
repo_web_url: cwdHasGitRepository ? await remoteWebUrlAsync(cwd) : null,
|
|
23247
24681
|
scope: {
|
|
23248
24682
|
omit_dirs_effective: scopeOmitDirNames,
|
|
23249
24683
|
omit_dirs_built_in: DEFAULT_WORKTREE_OMIT_DIR_NAMES,
|
|
@@ -23278,7 +24712,13 @@ function currentSearchEnv(omitOverride, excludeOverride) {
|
|
|
23278
24712
|
excludeNames: excludeOverride ?? scopeExcludeNames
|
|
23279
24713
|
};
|
|
23280
24714
|
}
|
|
23281
|
-
function
|
|
24715
|
+
async function currentBranchMetadata() {
|
|
24716
|
+
if (!cwdHasGitRepository)
|
|
24717
|
+
return;
|
|
24718
|
+
return await currentBranchAsync(cwd) || undefined;
|
|
24719
|
+
}
|
|
24720
|
+
async function handleFiles2(url) {
|
|
24721
|
+
const responseGeneration = generation;
|
|
23282
24722
|
const target = url.searchParams.get("ref") || url.searchParams.get("target") || "worktree";
|
|
23283
24723
|
if (invalidScopeOmitDirNamesQuery(url))
|
|
23284
24724
|
return text("invalid omit dirs", 400);
|
|
@@ -23290,10 +24730,15 @@ function handleFiles2(url) {
|
|
|
23290
24730
|
const cached = fileListCache.get(key);
|
|
23291
24731
|
if (cached && cached.generation === generation)
|
|
23292
24732
|
return json2(cached.body);
|
|
23293
|
-
const result =
|
|
24733
|
+
const result = await listRepoFilesAsync(currentSearchEnv(omitDirNames, excludeNames), target, responseGeneration);
|
|
23294
24734
|
if (result.ok !== true)
|
|
23295
24735
|
return text(result.error, result.status ?? 400);
|
|
23296
|
-
|
|
24736
|
+
if (responseGeneration !== generation)
|
|
24737
|
+
return json2(result.value);
|
|
24738
|
+
fileListCache.set(key, {
|
|
24739
|
+
generation: responseGeneration,
|
|
24740
|
+
body: result.value
|
|
24741
|
+
});
|
|
23297
24742
|
while (fileListCache.size > MAX_TIMED_CACHE_ENTRIES) {
|
|
23298
24743
|
const oldest = fileListCache.keys().next().value;
|
|
23299
24744
|
if (oldest === undefined)
|
|
@@ -23325,25 +24770,26 @@ async function handleGrep(url) {
|
|
|
23325
24770
|
return text(result.error, result.status ?? 400);
|
|
23326
24771
|
return json2(result.value);
|
|
23327
24772
|
}
|
|
23328
|
-
function handleRefCommits(url) {
|
|
24773
|
+
async function handleRefCommits(url) {
|
|
23329
24774
|
const query = url.searchParams.get("q") || "";
|
|
23330
24775
|
const parsedMax = Number(url.searchParams.get("max") || "");
|
|
23331
24776
|
const parsedSkip = Number(url.searchParams.get("skip") || "0");
|
|
23332
24777
|
const max = Number.isFinite(parsedMax) && parsedMax > 0 ? parsedMax : undefined;
|
|
23333
24778
|
const skip = Number.isFinite(parsedSkip) && parsedSkip > 0 ? parsedSkip : undefined;
|
|
23334
|
-
const result =
|
|
24779
|
+
const result = await refCommitPageResultAsync(cwd, { query, max, skip });
|
|
23335
24780
|
if (result.error)
|
|
23336
24781
|
return text(result.error, result.status ?? 500);
|
|
23337
24782
|
return json2({ commits: result.commits, hasMore: result.hasMore });
|
|
23338
24783
|
}
|
|
23339
|
-
function handleLog(url) {
|
|
24784
|
+
async function handleLog(url) {
|
|
24785
|
+
const responseGeneration = generation;
|
|
23340
24786
|
const ref = url.searchParams.get("ref") || "HEAD";
|
|
23341
24787
|
const skip = Number(url.searchParams.get("skip") || "0");
|
|
23342
24788
|
const limit = Number(url.searchParams.get("limit") || "50");
|
|
23343
24789
|
const path = url.searchParams.get("path") || "";
|
|
23344
24790
|
if (path && !safePath(path))
|
|
23345
24791
|
return text("invalid path", 400);
|
|
23346
|
-
const result =
|
|
24792
|
+
const result = await commitHistoryAsync(cwd, {
|
|
23347
24793
|
ref,
|
|
23348
24794
|
skip: Number.isFinite(skip) ? skip : 0,
|
|
23349
24795
|
limit: Number.isFinite(limit) ? limit : 50,
|
|
@@ -23356,7 +24802,7 @@ function handleLog(url) {
|
|
|
23356
24802
|
let commits = result.commits;
|
|
23357
24803
|
let hasWorktree = false;
|
|
23358
24804
|
if (wantsWorktreeHead) {
|
|
23359
|
-
const status =
|
|
24805
|
+
const status = await statusPorcelainForPathAsync(path, cwd);
|
|
23360
24806
|
if (status.ok && status.stdout.length > 0) {
|
|
23361
24807
|
const parts = status.stdout.split("\x00").filter(Boolean);
|
|
23362
24808
|
if (parts.length > 0) {
|
|
@@ -23378,7 +24824,7 @@ function handleLog(url) {
|
|
|
23378
24824
|
return json2({
|
|
23379
24825
|
commits,
|
|
23380
24826
|
hasMore: result.hasMore,
|
|
23381
|
-
generation,
|
|
24827
|
+
generation: responseGeneration,
|
|
23382
24828
|
...hasWorktree ? { hasWorktree: true } : {}
|
|
23383
24829
|
});
|
|
23384
24830
|
}
|
|
@@ -23401,7 +24847,8 @@ function rememberBlame(key, value) {
|
|
|
23401
24847
|
blameCache.delete(oldest.value);
|
|
23402
24848
|
}
|
|
23403
24849
|
}
|
|
23404
|
-
function handleFileBlame(url) {
|
|
24850
|
+
async function handleFileBlame(url) {
|
|
24851
|
+
const responseGeneration = generation;
|
|
23405
24852
|
const path = url.searchParams.get("path") || "";
|
|
23406
24853
|
if (!safePath(path))
|
|
23407
24854
|
return text("invalid path", 400);
|
|
@@ -23416,7 +24863,7 @@ function handleFileBlame(url) {
|
|
|
23416
24863
|
if (base === "worktree") {
|
|
23417
24864
|
cacheKey = `worktree|${path}|${blamePathKey(path)}`;
|
|
23418
24865
|
} else {
|
|
23419
|
-
const resolved =
|
|
24866
|
+
const resolved = await verifyCommitAsync(normalized.ref, cwd);
|
|
23420
24867
|
if (resolved.ok === false) {
|
|
23421
24868
|
const status = resolved.error === commandNotFoundDetail("git") ? 503 : 400;
|
|
23422
24869
|
return text(resolved.error || "unknown ref", status);
|
|
@@ -23429,16 +24876,21 @@ function handleFileBlame(url) {
|
|
|
23429
24876
|
blameCache.delete(cacheKey);
|
|
23430
24877
|
blameCache.set(cacheKey, cached);
|
|
23431
24878
|
}
|
|
23432
|
-
return json2({ ...cached, base, ref, generation });
|
|
24879
|
+
return json2({ ...cached, base, ref, generation: responseGeneration });
|
|
23433
24880
|
}
|
|
23434
|
-
const result =
|
|
24881
|
+
const result = await blameAsync(cwd, {
|
|
24882
|
+
path,
|
|
24883
|
+
ref: normalized.ref,
|
|
24884
|
+
base
|
|
24885
|
+
});
|
|
23435
24886
|
if (result.error && result.status)
|
|
23436
24887
|
return text(result.error, result.status);
|
|
23437
|
-
if (!result.error)
|
|
24888
|
+
if (!result.error && responseGeneration === generation)
|
|
23438
24889
|
rememberBlame(cacheKey, result);
|
|
23439
|
-
return json2({ ...result, base, ref, generation });
|
|
24890
|
+
return json2({ ...result, base, ref, generation: responseGeneration });
|
|
23440
24891
|
}
|
|
23441
|
-
function handleFileDiff(url) {
|
|
24892
|
+
async function handleFileDiff(url) {
|
|
24893
|
+
const responseGeneration = generation;
|
|
23442
24894
|
const path = url.searchParams.get("path") || "";
|
|
23443
24895
|
if (!safePath(path))
|
|
23444
24896
|
return text("invalid path", 400);
|
|
@@ -23464,7 +24916,7 @@ function handleFileDiff(url) {
|
|
|
23464
24916
|
line_count: 0,
|
|
23465
24917
|
truncated: false,
|
|
23466
24918
|
binary: false,
|
|
23467
|
-
generation
|
|
24919
|
+
generation: responseGeneration
|
|
23468
24920
|
});
|
|
23469
24921
|
}
|
|
23470
24922
|
const { args } = buildRangeArgs(range);
|
|
@@ -23491,21 +24943,21 @@ function handleFileDiff(url) {
|
|
|
23491
24943
|
diffText = cached.diffText;
|
|
23492
24944
|
} else {
|
|
23493
24945
|
if (isUntracked) {
|
|
23494
|
-
const res =
|
|
24946
|
+
const res = await untrackedFileDiffAsync(extras, path, cwd);
|
|
23495
24947
|
diffText = res.stdout || "";
|
|
23496
24948
|
if (res.code !== 0 && !(res.code === 1 && diffText)) {
|
|
23497
24949
|
errText = res.stderr || "diff failed";
|
|
23498
24950
|
errStatus = res.status ?? 500;
|
|
23499
24951
|
}
|
|
23500
24952
|
} else {
|
|
23501
|
-
const res =
|
|
24953
|
+
const res = await fileDiffTextAsync([...extras, ...args], oldPath ? [oldPath, path] : path, cwd);
|
|
23502
24954
|
diffText = res.stdout || "";
|
|
23503
24955
|
if (res.code !== 0) {
|
|
23504
24956
|
errText = res.stderr || "diff failed";
|
|
23505
24957
|
errStatus = res.status ?? 500;
|
|
23506
24958
|
}
|
|
23507
24959
|
}
|
|
23508
|
-
if (!errText)
|
|
24960
|
+
if (!errText && responseGeneration === generation)
|
|
23509
24961
|
setTimedCacheEntry(fileCache, cacheKey, { diffText });
|
|
23510
24962
|
}
|
|
23511
24963
|
if (errStatus)
|
|
@@ -23524,7 +24976,7 @@ function handleFileDiff(url) {
|
|
|
23524
24976
|
truncated: mode === "preview" && (truncated.totalHunks > truncated.renderedHunks || truncated.lineTruncated),
|
|
23525
24977
|
binary: diffText.includes("Binary files"),
|
|
23526
24978
|
error: errText,
|
|
23527
|
-
generation
|
|
24979
|
+
generation: responseGeneration
|
|
23528
24980
|
};
|
|
23529
24981
|
return json2(body);
|
|
23530
24982
|
}
|
|
@@ -23693,13 +25145,13 @@ async function handleFileRange(url) {
|
|
|
23693
25145
|
return json2(body);
|
|
23694
25146
|
} else {
|
|
23695
25147
|
const responseGeneration = generation;
|
|
23696
|
-
const refCheck =
|
|
25148
|
+
const refCheck = await verifyTreeRefResultAsync(ref, cwd);
|
|
23697
25149
|
if (refCheck.ok !== true)
|
|
23698
25150
|
return text(refCheck.error, refCheck.status ?? 400);
|
|
23699
|
-
const oid =
|
|
25151
|
+
const oid = await objectIdAsync(ref, path, cwd);
|
|
23700
25152
|
if (oid.code !== 0 || !oid.oid)
|
|
23701
25153
|
return text("not in ref", 404);
|
|
23702
|
-
const size =
|
|
25154
|
+
const size = await objectByteSizeAsync(oid.oid, cwd);
|
|
23703
25155
|
if (size.code !== 0)
|
|
23704
25156
|
return text("cannot read ref", 500);
|
|
23705
25157
|
const result = await collectIndexedGitBlobLineRange(path, oid.oid, size.size, start, end);
|
|
@@ -23724,17 +25176,17 @@ async function handleRawFile(req, url) {
|
|
|
23724
25176
|
return text("forbidden", 403);
|
|
23725
25177
|
const ref = url.searchParams.get("ref") || "worktree";
|
|
23726
25178
|
if (ref !== "worktree" && ref !== "") {
|
|
23727
|
-
const refCheck =
|
|
25179
|
+
const refCheck = await verifyTreeRefResultAsync(ref, cwd);
|
|
23728
25180
|
if (refCheck.ok !== true)
|
|
23729
25181
|
return text(refCheck.error, refCheck.status ?? 400);
|
|
23730
|
-
const oid =
|
|
25182
|
+
const oid = await objectIdAsync(ref, path, cwd);
|
|
23731
25183
|
if (oid.code !== 0 || !oid.oid)
|
|
23732
25184
|
return text("not in ref", 404);
|
|
23733
|
-
const sizeResult =
|
|
25185
|
+
const sizeResult = await objectByteSizeAsync(oid.oid, cwd);
|
|
23734
25186
|
if (sizeResult.code !== 0)
|
|
23735
25187
|
return text("cannot read ref", 500);
|
|
23736
25188
|
const size = sizeResult.size;
|
|
23737
|
-
const metadata = gitFileMetadata(ref, path, size);
|
|
25189
|
+
const metadata = await gitFileMetadata(ref, path, size);
|
|
23738
25190
|
const rangeResult = req.headers.get("range") ? parseHttpByteRange(req.headers.get("range"), size) : null;
|
|
23739
25191
|
if (rangeResult?.kind === "unsatisfiable") {
|
|
23740
25192
|
return new Response(null, {
|
|
@@ -23777,7 +25229,7 @@ async function handleRawFile(req, url) {
|
|
|
23777
25229
|
const full = safeWorktreePath2(path);
|
|
23778
25230
|
if (!full)
|
|
23779
25231
|
return text("not found", 404);
|
|
23780
|
-
const size = rawFileSize(path, ref);
|
|
25232
|
+
const size = await rawFileSize(path, ref);
|
|
23781
25233
|
if (size == null)
|
|
23782
25234
|
return text("not found", 404);
|
|
23783
25235
|
const metadata = worktreeFileMetadata(path, size);
|
|
@@ -23814,11 +25266,12 @@ async function handleRawFile(req, url) {
|
|
|
23814
25266
|
});
|
|
23815
25267
|
}
|
|
23816
25268
|
}
|
|
23817
|
-
function rawFileSize(path, ref) {
|
|
25269
|
+
async function rawFileSize(path, ref) {
|
|
23818
25270
|
if (ref !== "worktree" && ref !== "") {
|
|
23819
|
-
|
|
25271
|
+
const refCheck = await verifyTreeRefResultAsync(ref, cwd);
|
|
25272
|
+
if (refCheck.ok !== true)
|
|
23820
25273
|
return null;
|
|
23821
|
-
const res =
|
|
25274
|
+
const res = await objectSizeAsync(ref, path, cwd);
|
|
23822
25275
|
return res.code === 0 ? res.size : null;
|
|
23823
25276
|
}
|
|
23824
25277
|
const full = safeWorktreePath2(path);
|
|
@@ -24043,13 +25496,13 @@ function moveMacPathIntoTrash(path) {
|
|
|
24043
25496
|
return { ok: false, error: String(error) };
|
|
24044
25497
|
}
|
|
24045
25498
|
}
|
|
24046
|
-
function movePathToTrash(path) {
|
|
25499
|
+
async function movePathToTrash(path) {
|
|
24047
25500
|
lstatSync5(path);
|
|
24048
25501
|
if (process.platform === "darwin") {
|
|
24049
25502
|
return moveMacPathIntoTrash(path);
|
|
24050
25503
|
}
|
|
24051
25504
|
if (process.platform === "win32") {
|
|
24052
|
-
const res =
|
|
25505
|
+
const res = await runAsync([
|
|
24053
25506
|
"powershell.exe",
|
|
24054
25507
|
"-NoProfile",
|
|
24055
25508
|
"-NonInteractive",
|
|
@@ -24062,7 +25515,7 @@ function movePathToTrash(path) {
|
|
|
24062
25515
|
}
|
|
24063
25516
|
return { ok: false, error: "trash unsupported" };
|
|
24064
25517
|
}
|
|
24065
|
-
function restoreTrashPath(originalPath, trashPath) {
|
|
25518
|
+
async function restoreTrashPath(originalPath, trashPath) {
|
|
24066
25519
|
const parent = parentRepoPath(originalPath);
|
|
24067
25520
|
const parentFullPath = safeOpenWorktreePath(parent);
|
|
24068
25521
|
if (!parentFullPath)
|
|
@@ -24088,7 +25541,7 @@ function restoreTrashPath(originalPath, trashPath) {
|
|
|
24088
25541
|
}
|
|
24089
25542
|
}
|
|
24090
25543
|
if (process.platform === "win32") {
|
|
24091
|
-
const res =
|
|
25544
|
+
const res = await runAsync([
|
|
24092
25545
|
"powershell.exe",
|
|
24093
25546
|
"-NoProfile",
|
|
24094
25547
|
"-NonInteractive",
|
|
@@ -24171,7 +25624,7 @@ async function handleTrashPath(req) {
|
|
|
24171
25624
|
const originalFullPath = safeWorktreePath2(path);
|
|
24172
25625
|
if (!originalFullPath)
|
|
24173
25626
|
return text("not found", 404);
|
|
24174
|
-
const moved = movePathToTrash(worktreePath(path));
|
|
25627
|
+
const moved = await movePathToTrash(worktreePath(path));
|
|
24175
25628
|
if (!moved.ok)
|
|
24176
25629
|
return text(moved.error || "trash failed", 500);
|
|
24177
25630
|
const undo = {
|
|
@@ -24265,7 +25718,7 @@ async function handleRestoreTrash(req) {
|
|
|
24265
25718
|
return text("invalid restore target", 400);
|
|
24266
25719
|
if (isGitInternalPath(originalPath))
|
|
24267
25720
|
return text("forbidden", 403);
|
|
24268
|
-
const restored = restoreTrashPath(originalPath, trashPath || undefined);
|
|
25721
|
+
const restored = await restoreTrashPath(originalPath, trashPath || undefined);
|
|
24269
25722
|
if (!restored.ok)
|
|
24270
25723
|
return text(restored.error || "undo failed", 409);
|
|
24271
25724
|
triggerUpdate();
|
|
@@ -24399,7 +25852,7 @@ async function handleJournal(req) {
|
|
|
24399
25852
|
const labels = bodyStringList(body, "labels");
|
|
24400
25853
|
if (label)
|
|
24401
25854
|
labels.push(label);
|
|
24402
|
-
const issues =
|
|
25855
|
+
const issues = await readGithubIssueListAsync({
|
|
24403
25856
|
cwd,
|
|
24404
25857
|
repo: bodyString(body, "repo"),
|
|
24405
25858
|
labels,
|
|
@@ -24803,7 +26256,7 @@ function restartWorktreeWatch() {
|
|
|
24803
26256
|
}
|
|
24804
26257
|
worktreeWatch = startScopedWorktreeWatch();
|
|
24805
26258
|
}
|
|
24806
|
-
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, commandOverrides, cwdWasExplicit = false, scopeOmitDirNames, scopeOmitDirCliOverride = null, scopeExcludeNames, scopeWatchLimit, uploadEnabled = true, enc, sseClients, sseKeepalives, fileCache, blameCache, BLAME_CACHE_MAX = 64, metaCache, fileListCache, lineIndexCache, blobLineIndexCache, blobBytesCache, blobLineCacheBytes = 0, safePath, MCP_MAX_BODY_BYTES = 1048576, MCP_INSTRUCTIONS, JournalRequestError, isCodeViewerInternalPath, watchLimitReached = null, server, worktreeWatch = null, shuttingDown = false;
|
|
26259
|
+
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, commandOverrides, cwdWasExplicit = false, cwdHasGitRepository = false, scopeOmitDirNames, scopeOmitDirCliOverride = null, scopeExcludeNames, scopeWatchLimit, uploadEnabled = true, enc, sseClients, sseKeepalives, fileCache, blameCache, BLAME_CACHE_MAX = 64, metaCache, diffMetaRequestSequence = 0, latestDiffMetaRequest, fileListCache, lineIndexCache, blobLineIndexCache, blobBytesCache, blobLineCacheBytes = 0, safePath, MCP_MAX_BODY_BYTES = 1048576, MCP_INSTRUCTIONS, JournalRequestError, isCodeViewerInternalPath, watchLimitReached = null, databaseHandleModule, server, worktreeWatch = null, shuttingDown = false;
|
|
24807
26260
|
var init_preview = __esm(async () => {
|
|
24808
26261
|
init_journal();
|
|
24809
26262
|
init_routes();
|
|
@@ -24881,6 +26334,7 @@ var init_preview = __esm(async () => {
|
|
|
24881
26334
|
fileCache = new Map;
|
|
24882
26335
|
blameCache = new Map;
|
|
24883
26336
|
metaCache = new Map;
|
|
26337
|
+
latestDiffMetaRequest = new Map;
|
|
24884
26338
|
fileListCache = new Map;
|
|
24885
26339
|
lineIndexCache = new Map;
|
|
24886
26340
|
blobLineIndexCache = new Map;
|
|
@@ -24897,6 +26351,7 @@ var init_preview = __esm(async () => {
|
|
|
24897
26351
|
isCodeViewerInternalPath = isToolInternalPath;
|
|
24898
26352
|
parseCli();
|
|
24899
26353
|
applyPersistedSettings(await loadAppSettingsState(cwd));
|
|
26354
|
+
databaseHandleModule = Promise.resolve().then(() => (init_handle(), exports_handle));
|
|
24900
26355
|
server = await startServer({
|
|
24901
26356
|
hostname: "127.0.0.1",
|
|
24902
26357
|
port: listenPort,
|
|
@@ -24908,9 +26363,9 @@ var init_preview = __esm(async () => {
|
|
|
24908
26363
|
if (staticResponse)
|
|
24909
26364
|
return staticResponse;
|
|
24910
26365
|
if (url.pathname === "/diff.json")
|
|
24911
|
-
return handleDiffJson(url);
|
|
26366
|
+
return await handleDiffJson(url);
|
|
24912
26367
|
if (url.pathname === "/_settings")
|
|
24913
|
-
return handleSettings();
|
|
26368
|
+
return await handleSettings();
|
|
24914
26369
|
if (url.pathname === "/_doctor")
|
|
24915
26370
|
return handleDoctor({
|
|
24916
26371
|
cwd,
|
|
@@ -24918,19 +26373,19 @@ var init_preview = __esm(async () => {
|
|
|
24918
26373
|
listenPort
|
|
24919
26374
|
});
|
|
24920
26375
|
if (url.pathname === "/_tree")
|
|
24921
|
-
return handleTree(url);
|
|
26376
|
+
return await handleTree(url);
|
|
24922
26377
|
if (url.pathname === "/_files")
|
|
24923
|
-
return handleFiles2(url);
|
|
26378
|
+
return await handleFiles2(url);
|
|
24924
26379
|
if (url.pathname === "/_grep")
|
|
24925
26380
|
return await handleGrep(url);
|
|
24926
26381
|
if (url.pathname === "/_commits")
|
|
24927
|
-
return handleRefCommits(url);
|
|
26382
|
+
return await handleRefCommits(url);
|
|
24928
26383
|
if (url.pathname === "/_log")
|
|
24929
|
-
return handleLog(url);
|
|
26384
|
+
return await handleLog(url);
|
|
24930
26385
|
if (url.pathname === "/_file_blame")
|
|
24931
|
-
return handleFileBlame(url);
|
|
26386
|
+
return await handleFileBlame(url);
|
|
24932
26387
|
if (url.pathname === "/file_diff")
|
|
24933
|
-
return handleFileDiff(url);
|
|
26388
|
+
return await handleFileDiff(url);
|
|
24934
26389
|
if (url.pathname === "/file_range")
|
|
24935
26390
|
return handleFileRange(url);
|
|
24936
26391
|
if (url.pathname === "/_file")
|
|
@@ -24946,7 +26401,7 @@ var init_preview = __esm(async () => {
|
|
|
24946
26401
|
if (url.pathname === "/_upload_files")
|
|
24947
26402
|
return handleUploadFiles(req);
|
|
24948
26403
|
if (url.pathname.startsWith("/_db/")) {
|
|
24949
|
-
const { handleDatabaseRoute: handleDatabaseRoute2 } = await
|
|
26404
|
+
const { handleDatabaseRoute: handleDatabaseRoute2 } = await databaseHandleModule;
|
|
24950
26405
|
const dbResponse = await handleDatabaseRoute2(req, url, cwd, scopeOmitDirNames, sideEffectRequestAllowed, sendSse);
|
|
24951
26406
|
if (dbResponse)
|
|
24952
26407
|
return dbResponse;
|
|
@@ -24964,7 +26419,7 @@ var init_preview = __esm(async () => {
|
|
|
24964
26419
|
if (url.pathname === "/_annotations")
|
|
24965
26420
|
return handleAnnotations(req);
|
|
24966
26421
|
if (url.pathname === "/_refs") {
|
|
24967
|
-
const result =
|
|
26422
|
+
const result = await refsResultAsync(cwd);
|
|
24968
26423
|
if (result.error)
|
|
24969
26424
|
return text(result.error, result.status ?? 500);
|
|
24970
26425
|
return json2(result.refs);
|