vde-open 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -11
- package/dist/cli.js +2 -2
- package/dist/daemon.js +2 -2
- package/dist/{heap-Dev_gSQn.js → heap-C3qHm7SE.js} +9 -4
- package/dist/{main-lxBamyBU.js → main-CqENdUsw.js} +832 -59
- package/dist/{render-yCayIbhf.js → render-BNZz0CjQ.js} +101 -9
- package/dist/{search-index-C0szMSlZ.js → search-index-__laOomT.js} +70 -5
- package/dist/web/assets/index-CC0ZOdtr.css +2 -0
- package/dist/web/assets/index-DsIZiz-K.js +97 -0
- package/dist/web/index.html +2 -2
- package/dist/workers/parse-worker.js +6 -2
- package/dist/workers/search-worker.js +8 -5
- package/docs/agent-usage.md +11 -0
- package/package.json +1 -1
- package/skills/vde-open/SKILL.md +2 -2
- package/dist/web/assets/index-BYu5_12H.css +0 -2
- package/dist/web/assets/index-CLggJFeG.js +0 -95
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "./rolldown-runtime-DC62tzP2.js";
|
|
2
|
-
import { $ as literal, A as successEnvelope, B as readParamsSchema, C as VdeError, D as ExitCode, E as isVdeError, F as htmlModeChangeParamsSchema, G as sha256Schema, H as reorderParamsSchema, I as htmlModeSchema, J as watchRuleSchema, K as sourceKindSchema, L as linkOpenParamsSchema, M as closeParamsSchema, N as documentFormatSchema, P as documentIdSchema, Q as boolean, R as listParamsSchema, S as validateAnswers, U as revisionSchema, V as renderGrantParamsSchema, W as searchParamsSchema, X as _enum, Y as LIMITS, Z as array, _ as feedbackWaitParamsSchema, a as uniqueTokens, at as canonicalJson, b as submissionSchema, c as feedbackAckParamsSchema, d as feedbackDraftParamsSchema, et as number, f as feedbackForgetParamsSchema, g as feedbackSubmitParamsSchema, h as feedbackStatusSchema, i as codePointLength, it as ZodError, j as assetRecordSchema, k as errorEnvelope, l as feedbackCancelParamsSchema, m as feedbackListParamsSchema, n as batchesOf, nt as strictObject, o as answersSchema, p as feedbackIdParamsSchema, q as sourceStateSchema, r as partsOf, rt as string, s as cancellationSchema, tt as record, u as feedbackCreateParamsSchema, v as questionnaireSchema, w as exitCodeForError, x as utf8Length, y as requestIdSchema, z as openParamsSchema } from "./search-index-
|
|
3
|
-
import { a as analyzeDocument, c as classifyReference, d as hasHiddenSegment, f as isValidLogicalPath, i as ParseLimitError, l as dirnameOfLogicalPath, n as scanReferences, o as assetTypeOf, p as roleAllowed, r as extractTitle, s as classifyLink, u as encodeLogicalPath } from "./render-
|
|
4
|
-
import {
|
|
2
|
+
import { $ as literal, A as successEnvelope, B as readParamsSchema, C as VdeError, D as ExitCode, E as isVdeError, F as htmlModeChangeParamsSchema, G as sha256Schema, H as reorderParamsSchema, I as htmlModeSchema, J as watchRuleSchema, K as sourceKindSchema, L as linkOpenParamsSchema, M as closeParamsSchema, N as documentFormatSchema, P as documentIdSchema, Q as boolean, R as listParamsSchema, S as validateAnswers, U as revisionSchema, V as renderGrantParamsSchema, W as searchParamsSchema, X as _enum, Y as LIMITS, Z as array, _ as feedbackWaitParamsSchema, a as uniqueTokens, at as canonicalJson, b as submissionSchema, c as feedbackAckParamsSchema, d as feedbackDraftParamsSchema, et as number, f as feedbackForgetParamsSchema, g as feedbackSubmitParamsSchema, h as feedbackStatusSchema, i as codePointLength, it as ZodError, j as assetRecordSchema, k as errorEnvelope, l as feedbackCancelParamsSchema, m as feedbackListParamsSchema, n as batchesOf, nt as strictObject, o as answersSchema, p as feedbackIdParamsSchema, q as sourceStateSchema, r as partsOf, rt as string, s as cancellationSchema, tt as record, u as feedbackCreateParamsSchema, v as questionnaireSchema, w as exitCodeForError, x as utf8Length, y as requestIdSchema, z as openParamsSchema } from "./search-index-__laOomT.js";
|
|
3
|
+
import { a as analyzeDocument, c as classifyReference, d as hasHiddenSegment, f as isValidLogicalPath, i as ParseLimitError, l as dirnameOfLogicalPath, n as scanReferences, o as assetTypeOf, p as roleAllowed, r as extractTitle, s as classifyLink, u as encodeLogicalPath } from "./render-BNZz0CjQ.js";
|
|
4
|
+
import { n as measureHeap, t as collectGarbage } from "./heap-C3qHm7SE.js";
|
|
5
5
|
import { constants, existsSync, stat, unwatchFile, watch, watchFile } from "node:fs";
|
|
6
|
-
import { appendFile, chmod, link, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, rmdir, stat as stat$1, writeFile } from "node:fs/promises";
|
|
6
|
+
import { appendFile, chmod, link, lstat, mkdir, open, readFile, readdir, readlink, realpath, rename, rm, rmdir, stat as stat$1, writeFile } from "node:fs/promises";
|
|
7
7
|
import * as sp from "node:path";
|
|
8
|
-
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep, win32 } from "node:path";
|
|
8
|
+
import { basename, dirname, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
|
|
9
9
|
import { EventEmitter } from "node:events";
|
|
10
10
|
import { execFile } from "node:child_process";
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
12
|
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
13
13
|
import * as nativeFs from "fs";
|
|
14
14
|
import { readdir as readdir$1, readdirSync, realpath as realpath$1, realpathSync, stat as stat$2, statSync } from "fs";
|
|
15
|
-
import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, normalize, posix, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
15
|
+
import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, normalize, posix as posix$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
17
|
import { createRequire } from "module";
|
|
18
18
|
import { fileURLToPath as fileURLToPath$1 } from "node:url";
|
|
19
19
|
import { Worker } from "node:worker_threads";
|
|
20
|
+
import { performance } from "node:perf_hooks";
|
|
21
|
+
import { getHeapStatistics } from "node:v8";
|
|
20
22
|
import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
21
23
|
import { Http2ServerRequest, constants as constants$1 } from "node:http2";
|
|
22
24
|
import { Readable } from "node:stream";
|
|
@@ -152,7 +154,7 @@ function parseStrictJson(text) {
|
|
|
152
154
|
}
|
|
153
155
|
//#endregion
|
|
154
156
|
//#region package.json
|
|
155
|
-
var version = "0.1.
|
|
157
|
+
var version = "0.1.2";
|
|
156
158
|
//#endregion
|
|
157
159
|
//#region src/documents/cursor.ts
|
|
158
160
|
function invalidCursor(reason) {
|
|
@@ -184,7 +186,7 @@ function createCursorCodec(secret, now = Date.now) {
|
|
|
184
186
|
//#endregion
|
|
185
187
|
//#region ../../packages/document/src/profiles.ts
|
|
186
188
|
const MARKDOWN_PARSER_PROFILE = "markdown-tanstack-v1";
|
|
187
|
-
const HTML_STATIC_PARSER_PROFILE = "html-static-
|
|
189
|
+
const HTML_STATIC_PARSER_PROFILE = "html-static-v2";
|
|
188
190
|
//#endregion
|
|
189
191
|
//#region ../../packages/document/src/source-lines.ts
|
|
190
192
|
const LF = 10;
|
|
@@ -3150,8 +3152,8 @@ function buildFormat(cwd, root, absolute) {
|
|
|
3150
3152
|
};
|
|
3151
3153
|
return (p, isDir) => isDir && p !== "." ? p.slice(0, -1) : p;
|
|
3152
3154
|
}
|
|
3153
|
-
if (absolute) return (p) => posix.relative(cwd, p) || ".";
|
|
3154
|
-
return (p) => posix.relative(cwd, `${root}/${p}`) || ".";
|
|
3155
|
+
if (absolute) return (p) => posix$1.relative(cwd, p) || ".";
|
|
3156
|
+
return (p) => posix$1.relative(cwd, `${root}/${p}`) || ".";
|
|
3155
3157
|
}
|
|
3156
3158
|
function buildRelative(cwd, root) {
|
|
3157
3159
|
if (root.startsWith(`${cwd}/`)) {
|
|
@@ -3159,7 +3161,7 @@ function buildRelative(cwd, root) {
|
|
|
3159
3161
|
return (p) => `${prefix}/${p}`;
|
|
3160
3162
|
}
|
|
3161
3163
|
return (p) => {
|
|
3162
|
-
const result = posix.relative(cwd, `${root}/${p}`);
|
|
3164
|
+
const result = posix$1.relative(cwd, `${root}/${p}`);
|
|
3163
3165
|
return p[p.length - 1] === "/" && result !== "" ? `${result}/` : result || ".";
|
|
3164
3166
|
};
|
|
3165
3167
|
}
|
|
@@ -3214,7 +3216,7 @@ function normalizePattern(pattern, opts, props, isIgnore) {
|
|
|
3214
3216
|
if (pattern[pattern.length - 1] === "/") result = pattern.slice(0, -1);
|
|
3215
3217
|
if (result[result.length - 1] !== "*" && opts.expandDirectories) result += "/**";
|
|
3216
3218
|
const escapedCwd = escapePath(cwd);
|
|
3217
|
-
result = isAbsolute$1(result.replace(ESCAPING_BACKSLASHES, "")) ? posix.relative(escapedCwd, result) : posix.normalize(result);
|
|
3219
|
+
result = isAbsolute$1(result.replace(ESCAPING_BACKSLASHES, "")) ? posix$1.relative(escapedCwd, result) : posix$1.normalize(result);
|
|
3218
3220
|
const parentDir = (_PARENT_DIRECTORY$exe = PARENT_DIRECTORY.exec(result)) === null || _PARENT_DIRECTORY$exe === void 0 ? void 0 : _PARENT_DIRECTORY$exe[0];
|
|
3219
3221
|
const parts = splitPattern(result);
|
|
3220
3222
|
if (parentDir) {
|
|
@@ -3225,7 +3227,7 @@ function normalizePattern(pattern, opts, props, isIgnore) {
|
|
|
3225
3227
|
result = result.slice(0, (n - i - 1) * 3) + result.slice((n - i) * 3 + parts[i + n].length + 1) || ".";
|
|
3226
3228
|
i++;
|
|
3227
3229
|
}
|
|
3228
|
-
const potentialRoot = posix.join(cwd, parentDir.slice(i * 3));
|
|
3230
|
+
const potentialRoot = posix$1.join(cwd, parentDir.slice(i * 3));
|
|
3229
3231
|
if (potentialRoot[0] !== "." && props.root.length > potentialRoot.length) {
|
|
3230
3232
|
props.root = ensureNonDriveRelativePath(potentialRoot);
|
|
3231
3233
|
props.depthOffset = -n + i;
|
|
@@ -3247,7 +3249,7 @@ function normalizePattern(pattern, opts, props, isIgnore) {
|
|
|
3247
3249
|
}
|
|
3248
3250
|
props.depthOffset = newCommonPath.length;
|
|
3249
3251
|
props.commonPath = newCommonPath;
|
|
3250
|
-
props.root = ensureNonDriveRelativePath(newCommonPath.length > 0 ? posix.join(cwd, ...newCommonPath) : cwd);
|
|
3252
|
+
props.root = ensureNonDriveRelativePath(newCommonPath.length > 0 ? posix$1.join(cwd, ...newCommonPath) : cwd);
|
|
3251
3253
|
}
|
|
3252
3254
|
return result;
|
|
3253
3255
|
}
|
|
@@ -3513,6 +3515,550 @@ function isExcludedDirectoryName(name) {
|
|
|
3513
3515
|
return isHidden(name) || EXCLUDED_DIRECTORIES.has(name);
|
|
3514
3516
|
}
|
|
3515
3517
|
//#endregion
|
|
3518
|
+
//#region src/documents/repository.ts
|
|
3519
|
+
const METADATA_LIMIT = 4096;
|
|
3520
|
+
function kindOf(stats) {
|
|
3521
|
+
if (stats.isSymbolicLink()) return "symlink";
|
|
3522
|
+
if (stats.isFile()) return "file";
|
|
3523
|
+
if (stats.isDirectory()) return "directory";
|
|
3524
|
+
return "other";
|
|
3525
|
+
}
|
|
3526
|
+
function notRegularFile(path) {
|
|
3527
|
+
return Object.assign(/* @__PURE__ */ new Error(`${path} is not a regular file.`), { code: "EFTYPE" });
|
|
3528
|
+
}
|
|
3529
|
+
const nodeRepositoryFs = {
|
|
3530
|
+
lstat: async (path) => kindOf(await lstat(path)),
|
|
3531
|
+
stat: async (path) => kindOf(await stat$1(path)),
|
|
3532
|
+
readlink: (path) => readlink(path),
|
|
3533
|
+
realpath: (path) => realpath(path),
|
|
3534
|
+
readSmall: async (path, limit) => {
|
|
3535
|
+
const before = await lstat(path);
|
|
3536
|
+
if (!before.isFile()) throw notRegularFile(path);
|
|
3537
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW);
|
|
3538
|
+
try {
|
|
3539
|
+
const opened = await handle.stat();
|
|
3540
|
+
if (!opened.isFile() || opened.ino !== before.ino || opened.dev !== before.dev) throw notRegularFile(path);
|
|
3541
|
+
return await readBounded(handle, limit);
|
|
3542
|
+
} finally {
|
|
3543
|
+
await handle.close();
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
};
|
|
3547
|
+
function createLimiter(limit) {
|
|
3548
|
+
let active = 0;
|
|
3549
|
+
const waiting = [];
|
|
3550
|
+
return {
|
|
3551
|
+
async run(work) {
|
|
3552
|
+
if (active < limit) active += 1;
|
|
3553
|
+
else await new Promise((resolveTurn) => waiting.push(resolveTurn));
|
|
3554
|
+
try {
|
|
3555
|
+
return await work();
|
|
3556
|
+
} finally {
|
|
3557
|
+
const next = waiting.shift();
|
|
3558
|
+
if (next) next();
|
|
3559
|
+
else active -= 1;
|
|
3560
|
+
}
|
|
3561
|
+
},
|
|
3562
|
+
get active() {
|
|
3563
|
+
return active;
|
|
3564
|
+
}
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
function limitRepositoryFs(fs, limiter) {
|
|
3568
|
+
return {
|
|
3569
|
+
lstat: (path) => limiter.run(() => fs.lstat(path)),
|
|
3570
|
+
stat: (path) => limiter.run(() => fs.stat(path)),
|
|
3571
|
+
readlink: (path) => limiter.run(() => fs.readlink(path)),
|
|
3572
|
+
realpath: (path) => limiter.run(() => fs.realpath(path)),
|
|
3573
|
+
readSmall: (path, size) => limiter.run(() => fs.readSmall(path, size))
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3576
|
+
var Unresolved = class extends Error {
|
|
3577
|
+
reason;
|
|
3578
|
+
constructor(reason) {
|
|
3579
|
+
super(reason);
|
|
3580
|
+
this.reason = reason;
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
function failureOf(error, invalid) {
|
|
3584
|
+
if (error instanceof Unresolved) return error;
|
|
3585
|
+
const code = error?.code;
|
|
3586
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP" || code === "EFTYPE") return new Unresolved(invalid);
|
|
3587
|
+
return new Unresolved("unreadable");
|
|
3588
|
+
}
|
|
3589
|
+
const HEX_OBJECT_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
3590
|
+
const REFTABLE_HEAD = "refs/heads/.invalid";
|
|
3591
|
+
function isValidRefName(ref) {
|
|
3592
|
+
if (ref === "@" || ref.endsWith("/") || ref.endsWith(".")) return false;
|
|
3593
|
+
if (ref.includes("..") || ref.includes("@{") || ref.includes("//")) return false;
|
|
3594
|
+
if (/[\u0000- \u007f~^:?*[\\]/.test(ref)) return false;
|
|
3595
|
+
return ref.split("/").every((part) => part !== "" && !part.startsWith(".") && !part.endsWith(".lock"));
|
|
3596
|
+
}
|
|
3597
|
+
const utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
3598
|
+
function parseHead(bytes) {
|
|
3599
|
+
const content = bytes.toString("latin1").replace(/\s+$/, "");
|
|
3600
|
+
if (HEX_OBJECT_ID.test(content)) return null;
|
|
3601
|
+
if (!content.startsWith("ref:")) throw new Unresolved("invalid-git-dir");
|
|
3602
|
+
const ref = content.slice(4).replace(/^\s+/, "");
|
|
3603
|
+
if (ref === REFTABLE_HEAD) return null;
|
|
3604
|
+
if (!ref.startsWith("refs/") || !isValidRefName(ref)) throw new Unresolved("invalid-git-dir");
|
|
3605
|
+
if (!ref.startsWith("refs/heads/")) return null;
|
|
3606
|
+
try {
|
|
3607
|
+
return utf8.decode(Buffer.from(ref.slice(11), "latin1"));
|
|
3608
|
+
} catch {
|
|
3609
|
+
return null;
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
function parseLinkFile(bytes, prefix, invalid) {
|
|
3613
|
+
let content;
|
|
3614
|
+
try {
|
|
3615
|
+
content = utf8.decode(bytes);
|
|
3616
|
+
} catch {
|
|
3617
|
+
throw new Unresolved(invalid);
|
|
3618
|
+
}
|
|
3619
|
+
content = content.replace(/\r?\n$/, "");
|
|
3620
|
+
if (!content.startsWith(prefix)) throw new Unresolved(invalid);
|
|
3621
|
+
const path = content.slice(prefix.length);
|
|
3622
|
+
if (path === "" || path.includes("\n") || path.includes("\0")) throw new Unresolved(invalid);
|
|
3623
|
+
return path;
|
|
3624
|
+
}
|
|
3625
|
+
function createRepositoryDetector(options = {}) {
|
|
3626
|
+
const fs = options.fs ?? nodeRepositoryFs;
|
|
3627
|
+
const path = options.platform === "win32" ? win32 : posix;
|
|
3628
|
+
const isWindows = options.platform === "win32";
|
|
3629
|
+
const probes = /* @__PURE__ */ new Map();
|
|
3630
|
+
const checkouts = /* @__PURE__ */ new Map();
|
|
3631
|
+
const segmentsOf = (absolute) => absolute.split(path.sep).filter((part) => part !== "");
|
|
3632
|
+
const relativeSegments = (from, to) => segmentsOf(path.relative(from, to));
|
|
3633
|
+
const permits = (documentRoot, target) => {
|
|
3634
|
+
if (!isWindows) return true;
|
|
3635
|
+
const root = path.parse(target).root;
|
|
3636
|
+
if (/^[A-Za-z]:[\\/]$/.test(root)) return true;
|
|
3637
|
+
return root.toLowerCase() === documentRoot.toLowerCase();
|
|
3638
|
+
};
|
|
3639
|
+
const allowed = (documentRoot, target) => {
|
|
3640
|
+
if (!permits(documentRoot, target)) throw new Unresolved("blocked-path");
|
|
3641
|
+
return target;
|
|
3642
|
+
};
|
|
3643
|
+
const probe = (directory) => {
|
|
3644
|
+
let pending = probes.get(directory);
|
|
3645
|
+
if (!pending) {
|
|
3646
|
+
pending = fs.lstat(path.join(directory, ".git")).then((kind) => kind, (error) => error.code === "ENOENT" || error.code === "ENOTDIR" ? "absent" : "error");
|
|
3647
|
+
probes.set(directory, pending);
|
|
3648
|
+
}
|
|
3649
|
+
return pending;
|
|
3650
|
+
};
|
|
3651
|
+
const readMetadata = async (file, invalid) => {
|
|
3652
|
+
let bytes;
|
|
3653
|
+
try {
|
|
3654
|
+
bytes = await fs.readSmall(file, METADATA_LIMIT);
|
|
3655
|
+
} catch (error) {
|
|
3656
|
+
throw failureOf(error, invalid);
|
|
3657
|
+
}
|
|
3658
|
+
if (bytes === null) throw new Unresolved("limit-exceeded");
|
|
3659
|
+
return bytes;
|
|
3660
|
+
};
|
|
3661
|
+
const realpathOf = async (target, invalid) => {
|
|
3662
|
+
try {
|
|
3663
|
+
return await fs.realpath(target);
|
|
3664
|
+
} catch (error) {
|
|
3665
|
+
throw failureOf(error, invalid);
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
const expectDirectory = async (target) => {
|
|
3669
|
+
let kind;
|
|
3670
|
+
try {
|
|
3671
|
+
kind = await fs.stat(target);
|
|
3672
|
+
} catch (error) {
|
|
3673
|
+
throw failureOf(error, "invalid-git-dir");
|
|
3674
|
+
}
|
|
3675
|
+
if (kind !== "directory") throw new Unresolved("invalid-git-dir");
|
|
3676
|
+
};
|
|
3677
|
+
const validated = /* @__PURE__ */ new Map();
|
|
3678
|
+
const validateGitDir = (gitDir) => {
|
|
3679
|
+
let pending = validated.get(gitDir);
|
|
3680
|
+
if (!pending) {
|
|
3681
|
+
pending = (async () => {
|
|
3682
|
+
await expectDirectory(gitDir);
|
|
3683
|
+
const branch = parseHead(await readMetadata(path.join(gitDir, "HEAD"), "invalid-git-dir"));
|
|
3684
|
+
await expectDirectory(path.join(gitDir, "objects"));
|
|
3685
|
+
await expectDirectory(path.join(gitDir, "refs"));
|
|
3686
|
+
return branch;
|
|
3687
|
+
})();
|
|
3688
|
+
validated.set(gitDir, pending);
|
|
3689
|
+
}
|
|
3690
|
+
return pending;
|
|
3691
|
+
};
|
|
3692
|
+
const repositoryNameSegments = (commonDir) => {
|
|
3693
|
+
const segments = segmentsOf(commonDir);
|
|
3694
|
+
const last = segments.at(-1) ?? "";
|
|
3695
|
+
if (last === ".git") return segments.slice(0, -1);
|
|
3696
|
+
if (last.length > 4 && last.endsWith(".git")) return [...segments.slice(0, -1), last.slice(0, -4)];
|
|
3697
|
+
return segments;
|
|
3698
|
+
};
|
|
3699
|
+
const standalone = (checkoutDir) => ({
|
|
3700
|
+
state: "resolved",
|
|
3701
|
+
id: checkoutDir,
|
|
3702
|
+
nameSegments: segmentsOf(checkoutDir),
|
|
3703
|
+
checkout: {
|
|
3704
|
+
id: checkoutDir,
|
|
3705
|
+
kind: "main"
|
|
3706
|
+
}
|
|
3707
|
+
});
|
|
3708
|
+
const commonDirOf = async (gitDir, documentRoot) => {
|
|
3709
|
+
try {
|
|
3710
|
+
await fs.lstat(path.join(gitDir, "commondir"));
|
|
3711
|
+
} catch (error) {
|
|
3712
|
+
const code = error.code;
|
|
3713
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
3714
|
+
throw failureOf(error, "invalid-git-file");
|
|
3715
|
+
}
|
|
3716
|
+
const written = parseLinkFile(await readMetadata(path.join(gitDir, "commondir"), "invalid-git-file"), "", "invalid-git-file");
|
|
3717
|
+
return realpathOf(allowed(documentRoot, path.resolve(gitDir, written)), "invalid-git-dir");
|
|
3718
|
+
};
|
|
3719
|
+
const resolveGitFile = async (checkoutDir, gitFile, documentRoot) => {
|
|
3720
|
+
const written = parseLinkFile(await readMetadata(gitFile, "invalid-git-file"), "gitdir: ", "invalid-git-file");
|
|
3721
|
+
const gitDir = await realpathOf(allowed(documentRoot, path.resolve(checkoutDir, written)), "invalid-git-file");
|
|
3722
|
+
const commonDir = await commonDirOf(gitDir, documentRoot);
|
|
3723
|
+
if (commonDir === null) {
|
|
3724
|
+
await validateGitDir(gitDir);
|
|
3725
|
+
return standalone(checkoutDir);
|
|
3726
|
+
}
|
|
3727
|
+
await validateGitDir(commonDir);
|
|
3728
|
+
if (path.dirname(gitDir) !== path.join(commonDir, "worktrees")) throw new Unresolved("link-mismatch");
|
|
3729
|
+
const branch = parseHead(await readMetadata(path.join(gitDir, "HEAD"), "invalid-git-dir"));
|
|
3730
|
+
const backWritten = parseLinkFile(await readMetadata(path.join(gitDir, "gitdir"), "link-mismatch"), "", "link-mismatch");
|
|
3731
|
+
const back = allowed(documentRoot, path.resolve(gitDir, backWritten));
|
|
3732
|
+
if (path.basename(back) !== ".git") throw new Unresolved("link-mismatch");
|
|
3733
|
+
if (await realpathOf(path.dirname(back), "link-mismatch") !== checkoutDir) throw new Unresolved("link-mismatch");
|
|
3734
|
+
return {
|
|
3735
|
+
state: "resolved",
|
|
3736
|
+
id: commonDir,
|
|
3737
|
+
nameSegments: repositoryNameSegments(commonDir),
|
|
3738
|
+
checkout: {
|
|
3739
|
+
id: checkoutDir,
|
|
3740
|
+
kind: "linked",
|
|
3741
|
+
name: path.basename(gitDir),
|
|
3742
|
+
branch
|
|
3743
|
+
}
|
|
3744
|
+
};
|
|
3745
|
+
};
|
|
3746
|
+
const resolveGitLink = async (checkoutDir, gitLink, documentRoot) => {
|
|
3747
|
+
let target;
|
|
3748
|
+
try {
|
|
3749
|
+
target = allowed(documentRoot, path.resolve(checkoutDir, await fs.readlink(gitLink)));
|
|
3750
|
+
} catch (error) {
|
|
3751
|
+
throw failureOf(error, "invalid-git-dir");
|
|
3752
|
+
}
|
|
3753
|
+
let kind;
|
|
3754
|
+
try {
|
|
3755
|
+
kind = await fs.lstat(target);
|
|
3756
|
+
} catch (error) {
|
|
3757
|
+
throw failureOf(error, "invalid-git-dir");
|
|
3758
|
+
}
|
|
3759
|
+
if (kind === "directory") {
|
|
3760
|
+
await validateGitDir(target);
|
|
3761
|
+
return standalone(checkoutDir);
|
|
3762
|
+
}
|
|
3763
|
+
if (kind === "file") {
|
|
3764
|
+
const written = parseLinkFile(await readMetadata(target, "invalid-git-file"), "gitdir: ", "invalid-git-file");
|
|
3765
|
+
const gitDir = await realpathOf(allowed(documentRoot, path.resolve(checkoutDir, written)), "invalid-git-file");
|
|
3766
|
+
const common = await commonDirOf(gitDir, documentRoot);
|
|
3767
|
+
if (common === null) await validateGitDir(gitDir);
|
|
3768
|
+
else {
|
|
3769
|
+
await validateGitDir(common);
|
|
3770
|
+
parseHead(await readMetadata(path.join(gitDir, "HEAD"), "invalid-git-dir"));
|
|
3771
|
+
}
|
|
3772
|
+
return standalone(checkoutDir);
|
|
3773
|
+
}
|
|
3774
|
+
throw new Unresolved("invalid-git-dir");
|
|
3775
|
+
};
|
|
3776
|
+
const resolveCheckout = (checkoutDir, kind, documentRoot) => {
|
|
3777
|
+
let pending = checkouts.get(checkoutDir);
|
|
3778
|
+
if (!pending) {
|
|
3779
|
+
const gitEntry = path.join(checkoutDir, ".git");
|
|
3780
|
+
const work = async () => {
|
|
3781
|
+
if (kind === "directory") {
|
|
3782
|
+
await validateGitDir(gitEntry);
|
|
3783
|
+
return {
|
|
3784
|
+
state: "resolved",
|
|
3785
|
+
id: gitEntry,
|
|
3786
|
+
nameSegments: repositoryNameSegments(gitEntry),
|
|
3787
|
+
checkout: {
|
|
3788
|
+
id: checkoutDir,
|
|
3789
|
+
kind: "main"
|
|
3790
|
+
}
|
|
3791
|
+
};
|
|
3792
|
+
}
|
|
3793
|
+
if (kind === "file") return resolveGitFile(checkoutDir, gitEntry, documentRoot);
|
|
3794
|
+
if (kind === "symlink") return resolveGitLink(checkoutDir, gitEntry, documentRoot);
|
|
3795
|
+
throw new Unresolved("invalid-git-dir");
|
|
3796
|
+
};
|
|
3797
|
+
pending = work().catch((error) => ({
|
|
3798
|
+
state: "unresolved",
|
|
3799
|
+
reason: failureOf(error, "invalid-git-dir").reason
|
|
3800
|
+
}));
|
|
3801
|
+
checkouts.set(checkoutDir, pending);
|
|
3802
|
+
}
|
|
3803
|
+
return pending;
|
|
3804
|
+
};
|
|
3805
|
+
const detect = async (canonicalPath) => {
|
|
3806
|
+
const documentRoot = path.parse(canonicalPath).root;
|
|
3807
|
+
let directory = path.dirname(canonicalPath);
|
|
3808
|
+
let kind;
|
|
3809
|
+
for (;;) {
|
|
3810
|
+
const found = await probe(directory);
|
|
3811
|
+
if (found === "error") return {
|
|
3812
|
+
state: "unresolved",
|
|
3813
|
+
id: directory,
|
|
3814
|
+
nameSegments: segmentsOf(directory),
|
|
3815
|
+
pathInCheckout: relativeSegments(directory, canonicalPath),
|
|
3816
|
+
reason: "unreadable"
|
|
3817
|
+
};
|
|
3818
|
+
if (found !== "absent") {
|
|
3819
|
+
kind = found;
|
|
3820
|
+
break;
|
|
3821
|
+
}
|
|
3822
|
+
const parent = path.dirname(directory);
|
|
3823
|
+
if (parent === directory || directory === options.stopAt) return { state: "outside" };
|
|
3824
|
+
directory = parent;
|
|
3825
|
+
}
|
|
3826
|
+
const gitEntry = path.join(directory, ".git");
|
|
3827
|
+
if (kind === "directory" && canonicalPath.startsWith(gitEntry + path.sep)) {
|
|
3828
|
+
try {
|
|
3829
|
+
await validateGitDir(gitEntry);
|
|
3830
|
+
} catch (error) {
|
|
3831
|
+
return {
|
|
3832
|
+
state: "unresolved",
|
|
3833
|
+
id: directory,
|
|
3834
|
+
nameSegments: segmentsOf(directory),
|
|
3835
|
+
pathInCheckout: relativeSegments(directory, canonicalPath),
|
|
3836
|
+
reason: failureOf(error, "invalid-git-dir").reason
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3839
|
+
return {
|
|
3840
|
+
state: "resolved",
|
|
3841
|
+
id: gitEntry,
|
|
3842
|
+
nameSegments: repositoryNameSegments(gitEntry),
|
|
3843
|
+
checkout: null,
|
|
3844
|
+
pathInCheckout: relativeSegments(directory, canonicalPath)
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
const resolution = await resolveCheckout(directory, kind, documentRoot);
|
|
3848
|
+
const pathInCheckout = relativeSegments(directory, canonicalPath);
|
|
3849
|
+
if (resolution.state === "unresolved") return {
|
|
3850
|
+
state: "unresolved",
|
|
3851
|
+
id: directory,
|
|
3852
|
+
nameSegments: segmentsOf(directory),
|
|
3853
|
+
pathInCheckout,
|
|
3854
|
+
reason: resolution.reason ?? "invalid-git-dir"
|
|
3855
|
+
};
|
|
3856
|
+
return {
|
|
3857
|
+
state: "resolved",
|
|
3858
|
+
id: resolution.id,
|
|
3859
|
+
nameSegments: resolution.nameSegments,
|
|
3860
|
+
checkout: resolution.checkout,
|
|
3861
|
+
pathInCheckout
|
|
3862
|
+
};
|
|
3863
|
+
};
|
|
3864
|
+
return { detect };
|
|
3865
|
+
}
|
|
3866
|
+
function supersedes(current, sequence) {
|
|
3867
|
+
return current === void 0 || current.sequence < sequence || current.sequence === sequence && current.value?.state === "pending";
|
|
3868
|
+
}
|
|
3869
|
+
var RepositoryTracker = class {
|
|
3870
|
+
#fs;
|
|
3871
|
+
#platform;
|
|
3872
|
+
#limiter = createLimiter(2);
|
|
3873
|
+
#stopAt;
|
|
3874
|
+
#createDetector;
|
|
3875
|
+
waitMs;
|
|
3876
|
+
lateApplyMs;
|
|
3877
|
+
lateApplyMaxMs;
|
|
3878
|
+
#sequence = 0;
|
|
3879
|
+
#documents = /* @__PURE__ */ new Map();
|
|
3880
|
+
#checkouts = /* @__PURE__ */ new Map();
|
|
3881
|
+
#members = /* @__PURE__ */ new Map();
|
|
3882
|
+
constructor(options = {}) {
|
|
3883
|
+
this.#fs = limitRepositoryFs(options.fs ?? nodeRepositoryFs, this.#limiter);
|
|
3884
|
+
this.#platform = options.platform ?? (process.platform === "win32" ? "win32" : "posix");
|
|
3885
|
+
this.waitMs = options.waitMs ?? 2e3;
|
|
3886
|
+
this.lateApplyMs = options.lateApplyMs ?? 250;
|
|
3887
|
+
this.lateApplyMaxMs = options.lateApplyMaxMs ?? 1e3;
|
|
3888
|
+
this.#stopAt = options.stopAt;
|
|
3889
|
+
this.#createDetector = options.createDetector;
|
|
3890
|
+
}
|
|
3891
|
+
begin(paths) {
|
|
3892
|
+
this.#sequence += 1;
|
|
3893
|
+
const detector = this.#createDetector?.() ?? createRepositoryDetector({
|
|
3894
|
+
fs: this.#fs,
|
|
3895
|
+
platform: this.#platform,
|
|
3896
|
+
...this.#stopAt === void 0 ? {} : { stopAt: this.#stopAt }
|
|
3897
|
+
});
|
|
3898
|
+
const results = /* @__PURE__ */ new Map();
|
|
3899
|
+
const settled = /* @__PURE__ */ new Map();
|
|
3900
|
+
const work = [...new Set(paths)].map((path) => {
|
|
3901
|
+
const finished = detector.detect(path).then((detection) => {
|
|
3902
|
+
results.set(path, detection);
|
|
3903
|
+
}, () => {
|
|
3904
|
+
results.set(path, {
|
|
3905
|
+
state: "unresolved",
|
|
3906
|
+
id: path,
|
|
3907
|
+
nameSegments: [],
|
|
3908
|
+
pathInCheckout: [],
|
|
3909
|
+
reason: "unreadable"
|
|
3910
|
+
});
|
|
3911
|
+
});
|
|
3912
|
+
settled.set(path, finished);
|
|
3913
|
+
return finished;
|
|
3914
|
+
});
|
|
3915
|
+
return {
|
|
3916
|
+
sequence: this.#sequence,
|
|
3917
|
+
startedAt: Date.now(),
|
|
3918
|
+
results,
|
|
3919
|
+
done: Promise.all(work).then(() => void 0),
|
|
3920
|
+
settled: (path) => settled.get(path) ?? Promise.resolve()
|
|
3921
|
+
};
|
|
3922
|
+
}
|
|
3923
|
+
async wait(batch) {
|
|
3924
|
+
const remaining = batch.startedAt + this.waitMs - Date.now();
|
|
3925
|
+
if (remaining <= 0) return;
|
|
3926
|
+
let timer;
|
|
3927
|
+
await Promise.race([batch.done, new Promise((resolveTimeout) => {
|
|
3928
|
+
timer = setTimeout(resolveTimeout, remaining);
|
|
3929
|
+
})]);
|
|
3930
|
+
clearTimeout(timer);
|
|
3931
|
+
}
|
|
3932
|
+
#compose(stored, checkouts = this.#checkouts) {
|
|
3933
|
+
if (stored === void 0) return { state: "pending" };
|
|
3934
|
+
if (stored.state === "outside") return null;
|
|
3935
|
+
if (stored.state !== "resolved") return stored;
|
|
3936
|
+
if (stored.checkoutId === null) {
|
|
3937
|
+
const { checkoutId: _none, ...rest } = stored;
|
|
3938
|
+
return {
|
|
3939
|
+
...rest,
|
|
3940
|
+
checkout: null
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3943
|
+
const entry = checkouts.get(stored.checkoutId);
|
|
3944
|
+
if (entry === void 0) return { state: "pending" };
|
|
3945
|
+
return {
|
|
3946
|
+
state: "resolved",
|
|
3947
|
+
id: entry.repositoryId,
|
|
3948
|
+
nameSegments: entry.nameSegments,
|
|
3949
|
+
checkout: entry.info,
|
|
3950
|
+
pathInCheckout: stored.pathInCheckout
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
view(documentId) {
|
|
3954
|
+
return this.#compose(this.#documents.get(documentId)?.value);
|
|
3955
|
+
}
|
|
3956
|
+
stage(batch, targets, options) {
|
|
3957
|
+
const documents = /* @__PURE__ */ new Map();
|
|
3958
|
+
const checkouts = /* @__PURE__ */ new Map();
|
|
3959
|
+
const unfinished = [];
|
|
3960
|
+
const appliesTo = (current) => supersedes(current, batch.sequence);
|
|
3961
|
+
for (const target of targets) {
|
|
3962
|
+
const current = this.#documents.get(target.documentId);
|
|
3963
|
+
const detection = batch.results.get(target.path);
|
|
3964
|
+
if (detection === void 0) {
|
|
3965
|
+
unfinished.push(target);
|
|
3966
|
+
if (options.pendingWhenUnfinished && current === void 0) documents.set(target.documentId, {
|
|
3967
|
+
sequence: batch.sequence,
|
|
3968
|
+
value: { state: "pending" }
|
|
3969
|
+
});
|
|
3970
|
+
continue;
|
|
3971
|
+
}
|
|
3972
|
+
if (!appliesTo(current)) continue;
|
|
3973
|
+
let value;
|
|
3974
|
+
if (detection.state === "resolved" && detection.checkout !== null) {
|
|
3975
|
+
const { checkout } = detection;
|
|
3976
|
+
value = {
|
|
3977
|
+
state: "resolved",
|
|
3978
|
+
checkoutId: checkout.id,
|
|
3979
|
+
pathInCheckout: detection.pathInCheckout
|
|
3980
|
+
};
|
|
3981
|
+
if (appliesTo(this.#checkouts.get(checkout.id))) checkouts.set(checkout.id, {
|
|
3982
|
+
sequence: batch.sequence,
|
|
3983
|
+
info: checkout,
|
|
3984
|
+
repositoryId: detection.id,
|
|
3985
|
+
nameSegments: detection.nameSegments
|
|
3986
|
+
});
|
|
3987
|
+
} else if (detection.state === "resolved") {
|
|
3988
|
+
const { checkout: _none, ...rest } = detection;
|
|
3989
|
+
value = {
|
|
3990
|
+
...rest,
|
|
3991
|
+
checkoutId: null
|
|
3992
|
+
};
|
|
3993
|
+
} else value = detection;
|
|
3994
|
+
documents.set(target.documentId, {
|
|
3995
|
+
sequence: batch.sequence,
|
|
3996
|
+
value
|
|
3997
|
+
});
|
|
3998
|
+
}
|
|
3999
|
+
const merged = new Map(this.#checkouts);
|
|
4000
|
+
for (const [id, entry] of checkouts) merged.set(id, entry);
|
|
4001
|
+
const affected = new Set(documents.keys());
|
|
4002
|
+
for (const id of checkouts.keys()) for (const member of this.#members.get(id) ?? []) affected.add(member);
|
|
4003
|
+
let changed = false;
|
|
4004
|
+
for (const documentId of affected) {
|
|
4005
|
+
const before = this.view(documentId);
|
|
4006
|
+
const after = this.#compose(documents.get(documentId)?.value ?? this.#documents.get(documentId)?.value, merged);
|
|
4007
|
+
if (canonicalJson(before) !== canonicalJson(after)) {
|
|
4008
|
+
changed = true;
|
|
4009
|
+
break;
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
return {
|
|
4013
|
+
changed,
|
|
4014
|
+
unfinished,
|
|
4015
|
+
documents,
|
|
4016
|
+
checkouts
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
apply(staged) {
|
|
4020
|
+
for (const [id, entry] of staged.checkouts) if (supersedes(this.#checkouts.get(id), entry.sequence)) this.#checkouts.set(id, entry);
|
|
4021
|
+
for (const [documentId, entry] of staged.documents) {
|
|
4022
|
+
const current = this.#documents.get(documentId);
|
|
4023
|
+
if (entry.value.state === "pending" && current !== void 0) continue;
|
|
4024
|
+
if (supersedes(current, entry.sequence)) this.#setDocument(documentId, entry);
|
|
4025
|
+
}
|
|
4026
|
+
this.#dropUnused();
|
|
4027
|
+
}
|
|
4028
|
+
#setDocument(documentId, entry) {
|
|
4029
|
+
const before = this.#documents.get(documentId)?.value;
|
|
4030
|
+
if (before?.state === "resolved" && before.checkoutId !== null) this.#members.get(before.checkoutId)?.delete(documentId);
|
|
4031
|
+
this.#documents.set(documentId, entry);
|
|
4032
|
+
if (entry.value.state === "resolved" && entry.value.checkoutId !== null) {
|
|
4033
|
+
let members = this.#members.get(entry.value.checkoutId);
|
|
4034
|
+
if (!members) {
|
|
4035
|
+
members = /* @__PURE__ */ new Set();
|
|
4036
|
+
this.#members.set(entry.value.checkoutId, members);
|
|
4037
|
+
}
|
|
4038
|
+
members.add(documentId);
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
forget(documentIds) {
|
|
4042
|
+
for (const documentId of documentIds) {
|
|
4043
|
+
const before = this.#documents.get(documentId)?.value;
|
|
4044
|
+
if (before?.state === "resolved" && before.checkoutId !== null) this.#members.get(before.checkoutId)?.delete(documentId);
|
|
4045
|
+
this.#documents.delete(documentId);
|
|
4046
|
+
}
|
|
4047
|
+
this.#dropUnused();
|
|
4048
|
+
}
|
|
4049
|
+
#dropUnused() {
|
|
4050
|
+
for (const [id, members] of this.#members) if (members.size === 0) this.#members.delete(id);
|
|
4051
|
+
for (const id of this.#checkouts.keys()) if (!this.#members.has(id)) this.#checkouts.delete(id);
|
|
4052
|
+
}
|
|
4053
|
+
retainedCounts() {
|
|
4054
|
+
return {
|
|
4055
|
+
repositoryDocuments: this.#documents.size,
|
|
4056
|
+
repositoryCheckouts: this.#checkouts.size,
|
|
4057
|
+
repositoryOperations: this.#limiter.active
|
|
4058
|
+
};
|
|
4059
|
+
}
|
|
4060
|
+
};
|
|
4061
|
+
//#endregion
|
|
3516
4062
|
//#region src/documents/revision.ts
|
|
3517
4063
|
function sha256Hex(bytes) {
|
|
3518
4064
|
return createHash("sha256").update(bytes).digest("hex");
|
|
@@ -3542,7 +4088,7 @@ const MISSING_RETRY_INTERVAL_MS = 100;
|
|
|
3542
4088
|
function profileOf(format) {
|
|
3543
4089
|
return format === "markdown" ? MARKDOWN_PARSER_PROFILE : HTML_STATIC_PARSER_PROFILE;
|
|
3544
4090
|
}
|
|
3545
|
-
function toSummary(record, order, searchState = "excluded", pendingRequestIds = [], interactiveAllowed = false) {
|
|
4091
|
+
function toSummary(record, order, searchState = "excluded", pendingRequestIds = [], interactiveAllowed = false, repository = null) {
|
|
3546
4092
|
return {
|
|
3547
4093
|
documentId: record.documentId,
|
|
3548
4094
|
key: record.key,
|
|
@@ -3551,6 +4097,8 @@ function toSummary(record, order, searchState = "excluded", pendingRequestIds =
|
|
|
3551
4097
|
title: record.title,
|
|
3552
4098
|
displayPath: record.displayPath,
|
|
3553
4099
|
pathSegments: record.pathSegments,
|
|
4100
|
+
canonicalPath: record.sourceKind === "file" ? record.canonicalPath : null,
|
|
4101
|
+
repository,
|
|
3554
4102
|
revision: record.currentRevision,
|
|
3555
4103
|
sourceState: record.sourceState,
|
|
3556
4104
|
searchState,
|
|
@@ -3625,6 +4173,7 @@ var DocumentService = class {
|
|
|
3625
4173
|
#interactive = /* @__PURE__ */ new Map();
|
|
3626
4174
|
#interactiveGenerations = 0;
|
|
3627
4175
|
#linkConfirmations = /* @__PURE__ */ new Map();
|
|
4176
|
+
#repositories;
|
|
3628
4177
|
constructor(options) {
|
|
3629
4178
|
this.#store = options.store;
|
|
3630
4179
|
this.#cursors = options.cursors;
|
|
@@ -3634,6 +4183,68 @@ var DocumentService = class {
|
|
|
3634
4183
|
this.#readSource = options.readSource ?? readSourceFile;
|
|
3635
4184
|
this.#scan = options.scan ?? ((kind, text) => Promise.resolve(scanReferences(kind, text)));
|
|
3636
4185
|
this.#searchStateOf = options.searchStateOf ?? (() => "excluded");
|
|
4186
|
+
this.#repositories = new RepositoryTracker(options.repositories);
|
|
4187
|
+
}
|
|
4188
|
+
async initializeRepositories() {
|
|
4189
|
+
const state = this.#store.payload;
|
|
4190
|
+
const targets = this.#trackedFiles(state, state.openOrder);
|
|
4191
|
+
if (targets.length === 0) return;
|
|
4192
|
+
const batch = this.#repositories.begin(targets.map((target) => target.path));
|
|
4193
|
+
await this.#repositories.wait(batch);
|
|
4194
|
+
const staged = this.#repositories.stage(batch, targets, { pendingWhenUnfinished: true });
|
|
4195
|
+
this.#repositories.apply(staged);
|
|
4196
|
+
this.#applyWhenDone(batch, staged.unfinished);
|
|
4197
|
+
}
|
|
4198
|
+
#trackedFiles(state, documentIds) {
|
|
4199
|
+
const targets = [];
|
|
4200
|
+
for (const documentId of documentIds) {
|
|
4201
|
+
const record = state.documents[documentId];
|
|
4202
|
+
if (record?.isOpen && record.sourceKind === "file" && record.canonicalPath !== null) targets.push({
|
|
4203
|
+
documentId,
|
|
4204
|
+
path: record.canonicalPath,
|
|
4205
|
+
epoch: this.openEpoch(documentId)
|
|
4206
|
+
});
|
|
4207
|
+
}
|
|
4208
|
+
return targets;
|
|
4209
|
+
}
|
|
4210
|
+
async #applyBatch(batch, targets) {
|
|
4211
|
+
let staged = null;
|
|
4212
|
+
await this.#store.transaction((tx) => {
|
|
4213
|
+
const live = targets.filter((target) => tx.state.documents[target.documentId]?.isOpen === true && this.openEpoch(target.documentId) === target.epoch);
|
|
4214
|
+
const next = this.#repositories.stage(batch, live, { pendingWhenUnfinished: false });
|
|
4215
|
+
if (next.changed) tx.state.catalogVersion += 1;
|
|
4216
|
+
staged = next;
|
|
4217
|
+
});
|
|
4218
|
+
const applied = staged;
|
|
4219
|
+
if (applied === null) return [];
|
|
4220
|
+
this.#repositories.apply(applied);
|
|
4221
|
+
if (applied.changed) this.#emit({ type: "catalog-changed" });
|
|
4222
|
+
return applied.unfinished;
|
|
4223
|
+
}
|
|
4224
|
+
#applyWhenDone(batch, targets) {
|
|
4225
|
+
if (targets.length === 0) return;
|
|
4226
|
+
let ready = [];
|
|
4227
|
+
let timer = null;
|
|
4228
|
+
let delay = this.#repositories.lateApplyMs;
|
|
4229
|
+
const flush = () => {
|
|
4230
|
+
timer = null;
|
|
4231
|
+
const now = ready;
|
|
4232
|
+
ready = [];
|
|
4233
|
+
delay = Math.min(delay * 2, this.#repositories.lateApplyMaxMs);
|
|
4234
|
+
this.#applyBatch(batch, now).catch(() => void 0);
|
|
4235
|
+
};
|
|
4236
|
+
for (const target of targets) batch.settled(target.path).then(() => {
|
|
4237
|
+
ready.push(target);
|
|
4238
|
+
timer ??= setTimeout(flush, delay);
|
|
4239
|
+
});
|
|
4240
|
+
}
|
|
4241
|
+
async #redetect(documentIds) {
|
|
4242
|
+
const state = this.#store.payload;
|
|
4243
|
+
const targets = this.#trackedFiles(state, documentIds.filter((documentId) => state.documents[documentId]?.sourceState !== "missing"));
|
|
4244
|
+
if (targets.length === 0) return;
|
|
4245
|
+
const batch = this.#repositories.begin(targets.map((target) => target.path));
|
|
4246
|
+
await this.#repositories.wait(batch);
|
|
4247
|
+
this.#applyWhenDone(batch, await this.#applyBatch(batch, targets));
|
|
3637
4248
|
}
|
|
3638
4249
|
async #withPathLocks(paths, work) {
|
|
3639
4250
|
const releases = [];
|
|
@@ -3805,7 +4416,8 @@ var DocumentService = class {
|
|
|
3805
4416
|
pathQueues: this.#pathQueues.size,
|
|
3806
4417
|
openEpochs: this.#openEpochs.size,
|
|
3807
4418
|
interactive: this.#interactive.size,
|
|
3808
|
-
linkConfirmations: this.#linkConfirmations.size
|
|
4419
|
+
linkConfirmations: this.#linkConfirmations.size,
|
|
4420
|
+
...this.#repositories.retainedCounts()
|
|
3809
4421
|
};
|
|
3810
4422
|
}
|
|
3811
4423
|
async open(rawParams) {
|
|
@@ -3854,15 +4466,18 @@ var DocumentService = class {
|
|
|
3854
4466
|
const settings = await this.#assetSettings(params);
|
|
3855
4467
|
if (settings.explicit !== void 0 && resolved.unique.size !== 1) throw new VdeError("E_INVALID_ARGUMENT", "--asset can only be specified when opening a single document.", { documents: resolved.unique.size });
|
|
3856
4468
|
if (params.htmlMode !== void 0 && ![...resolved.unique.values()].some((entry) => entry.format === "html")) throw new VdeError("E_INVALID_ARGUMENT", "--html-mode applies to HTML documents.");
|
|
4469
|
+
const batch = this.#repositories.begin(resolved.unique.keys());
|
|
3857
4470
|
return this.#withPathLocks(resolved.unique.keys(), async () => {
|
|
3858
4471
|
const incoming = await this.#readCandidates(resolved, settings, warnings);
|
|
3859
4472
|
if (incoming.length === 0 && watchTargets.length === 0) throw new VdeError("E_PATH_NOT_FOUND", "No documents found.", { paths: params.paths });
|
|
3860
|
-
|
|
4473
|
+
await this.#repositories.wait(batch);
|
|
4474
|
+
return this.#commitOpen(params, incoming, watchTargets, settings, warnings, batch);
|
|
3861
4475
|
});
|
|
3862
4476
|
}
|
|
3863
|
-
async #commitOpen(params, incoming, watchTargets, settings, warnings) {
|
|
4477
|
+
async #commitOpen(params, incoming, watchTargets, settings, warnings, batch) {
|
|
3864
4478
|
if ((params.title !== void 0 || params.key !== void 0) && incoming.length !== 1) throw new VdeError("E_INVALID_ARGUMENT", "--title and --key can only be specified when opening a single document.", { documents: incoming.length });
|
|
3865
4479
|
const events = [];
|
|
4480
|
+
let repositories = null;
|
|
3866
4481
|
const result = await this.#store.transaction((tx) => {
|
|
3867
4482
|
const state = tx.state;
|
|
3868
4483
|
const timestamp = this.#now().toISOString();
|
|
@@ -3906,9 +4521,11 @@ var DocumentService = class {
|
|
|
3906
4521
|
rules.push(rule);
|
|
3907
4522
|
}
|
|
3908
4523
|
assertOpenLimits(state);
|
|
3909
|
-
if (
|
|
4524
|
+
if (batch !== void 0) repositories = this.#repositories.stage(batch, this.#trackedFiles(state, touched), { pendingWhenUnfinished: true });
|
|
4525
|
+
const repositoryChanged = repositories?.changed === true;
|
|
4526
|
+
if (created + updated > 0 || repositoryChanged) state.catalogVersion += 1;
|
|
3910
4527
|
if (state.activeDocumentId === null) state.activeDocumentId = state.openOrder[0] ?? null;
|
|
3911
|
-
if (openSetChanged) events.unshift({ type: "catalog-changed" });
|
|
4528
|
+
if (openSetChanged || repositoryChanged) events.unshift({ type: "catalog-changed" });
|
|
3912
4529
|
return {
|
|
3913
4530
|
data: {
|
|
3914
4531
|
documents: touched.map((documentId) => this.#summary(state, documentId)),
|
|
@@ -3921,6 +4538,11 @@ var DocumentService = class {
|
|
|
3921
4538
|
warnings
|
|
3922
4539
|
};
|
|
3923
4540
|
});
|
|
4541
|
+
const staged = repositories;
|
|
4542
|
+
if (staged !== null && batch !== void 0) {
|
|
4543
|
+
this.#repositories.apply(staged);
|
|
4544
|
+
this.#applyWhenDone(batch, staged.unfinished);
|
|
4545
|
+
}
|
|
3924
4546
|
for (const item of incoming) this.#record(item);
|
|
3925
4547
|
const permissionChanged = [];
|
|
3926
4548
|
result.data.documents.forEach((summary, index) => {
|
|
@@ -4183,7 +4805,7 @@ var DocumentService = class {
|
|
|
4183
4805
|
};
|
|
4184
4806
|
}
|
|
4185
4807
|
#summary(state, documentId) {
|
|
4186
|
-
return toSummary(state.documents[documentId], state.openOrder.indexOf(documentId), this.#searchStateOf(documentId), pendingRequestIdsOf(state, documentId), this.interactiveAllowed(documentId));
|
|
4808
|
+
return toSummary(state.documents[documentId], state.openOrder.indexOf(documentId), this.#searchStateOf(documentId), pendingRequestIdsOf(state, documentId), this.interactiveAllowed(documentId), state.documents[documentId]?.sourceKind === "file" ? this.#repositories.view(documentId) : null);
|
|
4187
4809
|
}
|
|
4188
4810
|
#findExisting(state, item, key) {
|
|
4189
4811
|
if (item.sourceKind === "file") return Object.values(state.documents).find((record) => record.sourceKind === "file" && record.canonicalPath === item.canonicalPath);
|
|
@@ -4518,6 +5140,7 @@ var DocumentService = class {
|
|
|
4518
5140
|
};
|
|
4519
5141
|
});
|
|
4520
5142
|
for (const documentId of result.data.closed) this.#openEpochs.set(documentId, this.openEpoch(documentId) + 1);
|
|
5143
|
+
this.#repositories.forget(result.data.closed);
|
|
4521
5144
|
if (changed) this.#emit({ type: "catalog-changed" });
|
|
4522
5145
|
for (const event of cancelled) this.#emit(event);
|
|
4523
5146
|
return result;
|
|
@@ -4595,6 +5218,7 @@ var DocumentService = class {
|
|
|
4595
5218
|
const targets = raw === void 0 ? this.#store.payload.openOrder : [this.#requireOpen(documentIdSchema.parse(raw)).documentId];
|
|
4596
5219
|
const changed = [];
|
|
4597
5220
|
for (const documentId of targets) if ((await this.refreshFromDisk(documentId)).changed) changed.push(documentId);
|
|
5221
|
+
await this.#redetect(targets);
|
|
4598
5222
|
const state = this.#store.payload;
|
|
4599
5223
|
return {
|
|
4600
5224
|
data: {
|
|
@@ -4730,6 +5354,7 @@ var DocumentService = class {
|
|
|
4730
5354
|
const before = this.#store.payload;
|
|
4731
5355
|
const rule = before.watchRules.find((candidate) => candidate.watchId === watchId);
|
|
4732
5356
|
if (!rule) return [];
|
|
5357
|
+
const batch = this.#repositories.begin(candidates.keys());
|
|
4733
5358
|
const isOpenPath = (state, path) => Object.values(state.documents).some((record) => record.isOpen && record.sourceKind === "file" && record.canonicalPath === path);
|
|
4734
5359
|
let totalBytes = openSourceBytes(before);
|
|
4735
5360
|
const discovered = [];
|
|
@@ -4769,7 +5394,9 @@ var DocumentService = class {
|
|
|
4769
5394
|
}
|
|
4770
5395
|
}
|
|
4771
5396
|
if (discovered.length === 0) return [];
|
|
5397
|
+
await this.#repositories.wait(batch);
|
|
4772
5398
|
const appliedItems = [];
|
|
5399
|
+
let repositories = null;
|
|
4773
5400
|
const registered = await this.#store.transaction((tx) => {
|
|
4774
5401
|
const state = tx.state;
|
|
4775
5402
|
const current = state.watchRules.find((candidate) => candidate.watchId === watchId);
|
|
@@ -4785,10 +5412,16 @@ var DocumentService = class {
|
|
|
4785
5412
|
}
|
|
4786
5413
|
if (added.length === 0) return [];
|
|
4787
5414
|
assertOpenLimits(state);
|
|
5415
|
+
repositories = this.#repositories.stage(batch, this.#trackedFiles(state, added), { pendingWhenUnfinished: true });
|
|
4788
5416
|
state.catalogVersion += 1;
|
|
4789
5417
|
if (state.activeDocumentId === null) state.activeDocumentId = state.openOrder[0] ?? null;
|
|
4790
5418
|
return added;
|
|
4791
5419
|
});
|
|
5420
|
+
const staged = repositories;
|
|
5421
|
+
if (staged !== null) {
|
|
5422
|
+
this.#repositories.apply(staged);
|
|
5423
|
+
this.#applyWhenDone(batch, staged.unfinished);
|
|
5424
|
+
}
|
|
4792
5425
|
for (const item of appliedItems) this.#record(item);
|
|
4793
5426
|
if (registered.length > 0) this.#emit({ type: "catalog-changed" });
|
|
4794
5427
|
return registered;
|
|
@@ -5869,7 +6502,8 @@ function createRenderService(options) {
|
|
|
5869
6502
|
role
|
|
5870
6503
|
})),
|
|
5871
6504
|
links: output.links,
|
|
5872
|
-
diagnostics: output.diagnostics
|
|
6505
|
+
diagnostics: output.diagnostics,
|
|
6506
|
+
headingTargets: output.headingTargets
|
|
5873
6507
|
};
|
|
5874
6508
|
snapshots.set(cacheKey, snapshot);
|
|
5875
6509
|
while (snapshots.size > SNAPSHOT_CACHE_SIZE) {
|
|
@@ -5965,6 +6599,7 @@ function createRenderService(options) {
|
|
|
5965
6599
|
target: null,
|
|
5966
6600
|
count: 1
|
|
5967
6601
|
}, ...snapshot.diagnostics] : snapshot.diagnostics,
|
|
6602
|
+
headingTargets: snapshot.headingTargets,
|
|
5968
6603
|
bridge: bridge === null ? null : {
|
|
5969
6604
|
instanceId: bridge.instanceId,
|
|
5970
6605
|
requestId: bridge.requestId
|
|
@@ -6063,6 +6698,108 @@ function createRenderService(options) {
|
|
|
6063
6698
|
};
|
|
6064
6699
|
}
|
|
6065
6700
|
//#endregion
|
|
6701
|
+
//#region src/diagnostics/idle-collect.ts
|
|
6702
|
+
const COLLECT_AFTER_IDLE_MS = 2e3;
|
|
6703
|
+
function createIdleTrigger(run, delayMs = COLLECT_AFTER_IDLE_MS) {
|
|
6704
|
+
let timer = null;
|
|
6705
|
+
const cancel = () => {
|
|
6706
|
+
if (timer) clearTimeout(timer);
|
|
6707
|
+
timer = null;
|
|
6708
|
+
};
|
|
6709
|
+
return {
|
|
6710
|
+
touch() {
|
|
6711
|
+
cancel();
|
|
6712
|
+
timer = setTimeout(() => {
|
|
6713
|
+
timer = null;
|
|
6714
|
+
run();
|
|
6715
|
+
}, delayMs);
|
|
6716
|
+
timer.unref();
|
|
6717
|
+
},
|
|
6718
|
+
cancel
|
|
6719
|
+
};
|
|
6720
|
+
}
|
|
6721
|
+
function createWorkerCollect(options) {
|
|
6722
|
+
let collecting = null;
|
|
6723
|
+
let closed = false;
|
|
6724
|
+
const held = [];
|
|
6725
|
+
const release = () => {
|
|
6726
|
+
if (collecting) clearTimeout(collecting.timer);
|
|
6727
|
+
collecting = null;
|
|
6728
|
+
for (const request of held.splice(0)) request.send();
|
|
6729
|
+
};
|
|
6730
|
+
const idle = createIdleTrigger(() => {
|
|
6731
|
+
const worker = options.worker();
|
|
6732
|
+
if (closed || worker === null || collecting) return;
|
|
6733
|
+
if (options.busy()) {
|
|
6734
|
+
idle.touch();
|
|
6735
|
+
return;
|
|
6736
|
+
}
|
|
6737
|
+
const id = options.nextId();
|
|
6738
|
+
const timer = setTimeout(release, options.waitLimitMs ?? 1e4);
|
|
6739
|
+
timer.unref();
|
|
6740
|
+
collecting = {
|
|
6741
|
+
id,
|
|
6742
|
+
timer
|
|
6743
|
+
};
|
|
6744
|
+
worker.postMessage({
|
|
6745
|
+
id,
|
|
6746
|
+
op: "collect"
|
|
6747
|
+
});
|
|
6748
|
+
}, options.delayMs);
|
|
6749
|
+
return {
|
|
6750
|
+
dispatch(send, reject) {
|
|
6751
|
+
if (collecting) held.push({
|
|
6752
|
+
send,
|
|
6753
|
+
reject
|
|
6754
|
+
});
|
|
6755
|
+
else send();
|
|
6756
|
+
},
|
|
6757
|
+
touch: () => idle.touch(),
|
|
6758
|
+
settle(id) {
|
|
6759
|
+
if (collecting?.id !== id) return false;
|
|
6760
|
+
release();
|
|
6761
|
+
return true;
|
|
6762
|
+
},
|
|
6763
|
+
reset: () => {
|
|
6764
|
+
if (collecting) release();
|
|
6765
|
+
},
|
|
6766
|
+
close(error) {
|
|
6767
|
+
closed = true;
|
|
6768
|
+
idle.cancel();
|
|
6769
|
+
if (collecting) clearTimeout(collecting.timer);
|
|
6770
|
+
collecting = null;
|
|
6771
|
+
for (const request of held.splice(0)) request.reject(error);
|
|
6772
|
+
}
|
|
6773
|
+
};
|
|
6774
|
+
}
|
|
6775
|
+
function utilizationSincePreviousCall() {
|
|
6776
|
+
let previous = performance.eventLoopUtilization();
|
|
6777
|
+
return () => {
|
|
6778
|
+
const current = performance.eventLoopUtilization();
|
|
6779
|
+
const { utilization } = performance.eventLoopUtilization(current, previous);
|
|
6780
|
+
previous = current;
|
|
6781
|
+
return utilization;
|
|
6782
|
+
};
|
|
6783
|
+
}
|
|
6784
|
+
function startHeapWatch(options = {}) {
|
|
6785
|
+
const growthBytes = options.growthBytes ?? 8388608;
|
|
6786
|
+
const idleUtilization = options.idleUtilization ?? .05;
|
|
6787
|
+
const heapUsed = options.heapUsed ?? (() => getHeapStatistics().used_heap_size);
|
|
6788
|
+
const utilization = options.utilization ?? utilizationSincePreviousCall();
|
|
6789
|
+
const collect = options.collect ?? collectGarbage;
|
|
6790
|
+
let baseline = heapUsed();
|
|
6791
|
+
const timer = setInterval(() => {
|
|
6792
|
+
const busy = utilization() >= idleUtilization;
|
|
6793
|
+
const used = heapUsed();
|
|
6794
|
+
baseline = Math.min(baseline, used);
|
|
6795
|
+
if (busy || used - baseline < growthBytes) return;
|
|
6796
|
+
collect();
|
|
6797
|
+
baseline = heapUsed();
|
|
6798
|
+
}, options.intervalMs ?? 2e3);
|
|
6799
|
+
timer.unref();
|
|
6800
|
+
return { stop: () => clearInterval(timer) };
|
|
6801
|
+
}
|
|
6802
|
+
//#endregion
|
|
6066
6803
|
//#region src/diagnostics/until-aborted.ts
|
|
6067
6804
|
function untilAborted(promise, signal) {
|
|
6068
6805
|
if (!signal) return promise;
|
|
@@ -6148,6 +6885,15 @@ function createSearchService(options) {
|
|
|
6148
6885
|
let closed = false;
|
|
6149
6886
|
let indexedAt = now().toISOString();
|
|
6150
6887
|
let syncing = null;
|
|
6888
|
+
const collector = createWorkerCollect({
|
|
6889
|
+
worker: () => worker,
|
|
6890
|
+
busy: () => pending.size > 0,
|
|
6891
|
+
nextId: () => {
|
|
6892
|
+
const id = nextId;
|
|
6893
|
+
nextId += 1;
|
|
6894
|
+
return id;
|
|
6895
|
+
}
|
|
6896
|
+
});
|
|
6151
6897
|
let syncRequested = false;
|
|
6152
6898
|
const failAll = (error) => {
|
|
6153
6899
|
for (const waiter of pending.values()) {
|
|
@@ -6168,6 +6914,7 @@ function createSearchService(options) {
|
|
|
6168
6914
|
const created = new Worker(workerPath);
|
|
6169
6915
|
created.unref();
|
|
6170
6916
|
created.on("message", (reply) => {
|
|
6917
|
+
if (collector.settle(reply.id)) return;
|
|
6171
6918
|
const waiter = pending.get(reply.id);
|
|
6172
6919
|
if (!waiter) return;
|
|
6173
6920
|
pending.delete(reply.id);
|
|
@@ -6178,31 +6925,36 @@ function createSearchService(options) {
|
|
|
6178
6925
|
created.on("error", () => {
|
|
6179
6926
|
if (worker === created) discardWorker();
|
|
6180
6927
|
failAll(new VdeError("E_INDEX_NOT_READY", "The search index is not available."));
|
|
6928
|
+
collector.reset();
|
|
6181
6929
|
});
|
|
6182
6930
|
worker = created;
|
|
6183
6931
|
return created;
|
|
6184
6932
|
};
|
|
6185
6933
|
const request = (work) => {
|
|
6186
6934
|
if (closed) return Promise.reject(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6935
|
+
const result = new Promise((resolve, reject) => {
|
|
6936
|
+
collector.dispatch(() => {
|
|
6937
|
+
const id = nextId;
|
|
6938
|
+
nextId += 1;
|
|
6939
|
+
const timer = setTimeout(() => {
|
|
6940
|
+
pending.delete(id);
|
|
6941
|
+
discardWorker();
|
|
6942
|
+
failAll(new VdeError("E_INDEX_NOT_READY", "The search index is not available."));
|
|
6943
|
+
reject(new VdeError("E_INDEX_NOT_READY", "The search index operation did not finish in time.", { reason: "timeout" }));
|
|
6944
|
+
}, timeoutMs);
|
|
6945
|
+
pending.set(id, {
|
|
6946
|
+
resolve,
|
|
6947
|
+
reject,
|
|
6948
|
+
timer
|
|
6949
|
+
});
|
|
6950
|
+
ensureWorker().postMessage({
|
|
6951
|
+
id,
|
|
6952
|
+
...work
|
|
6953
|
+
});
|
|
6954
|
+
}, reject);
|
|
6205
6955
|
});
|
|
6956
|
+
if (work.op !== "diagnostics") result.then(collector.touch, collector.touch);
|
|
6957
|
+
return result;
|
|
6206
6958
|
};
|
|
6207
6959
|
const analyzeOnce = async (format, text) => {
|
|
6208
6960
|
try {
|
|
@@ -6513,6 +7265,7 @@ function createSearchService(options) {
|
|
|
6513
7265
|
},
|
|
6514
7266
|
async close() {
|
|
6515
7267
|
closed = true;
|
|
7268
|
+
collector.close(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
|
|
6516
7269
|
failAll(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
|
|
6517
7270
|
const current = worker;
|
|
6518
7271
|
worker = null;
|
|
@@ -12505,6 +13258,15 @@ function createParseService(options = {}) {
|
|
|
12505
13258
|
let nextId = 1;
|
|
12506
13259
|
let closed = false;
|
|
12507
13260
|
const pending = /* @__PURE__ */ new Map();
|
|
13261
|
+
const collector = createWorkerCollect({
|
|
13262
|
+
worker: () => worker,
|
|
13263
|
+
busy: () => pending.size > 0,
|
|
13264
|
+
nextId: () => {
|
|
13265
|
+
const id = nextId;
|
|
13266
|
+
nextId += 1;
|
|
13267
|
+
return id;
|
|
13268
|
+
}
|
|
13269
|
+
});
|
|
12508
13270
|
const failAll = (error) => {
|
|
12509
13271
|
for (const waiter of pending.values()) {
|
|
12510
13272
|
clearTimeout(waiter.timer);
|
|
@@ -12522,6 +13284,7 @@ function createParseService(options = {}) {
|
|
|
12522
13284
|
const created = new Worker(workerPath);
|
|
12523
13285
|
created.unref();
|
|
12524
13286
|
created.on("message", (reply) => {
|
|
13287
|
+
if (collector.settle(reply.id)) return;
|
|
12525
13288
|
const waiter = pending.get(reply.id);
|
|
12526
13289
|
if (!waiter) return;
|
|
12527
13290
|
pending.delete(reply.id);
|
|
@@ -12532,32 +13295,37 @@ function createParseService(options = {}) {
|
|
|
12532
13295
|
created.on("error", () => {
|
|
12533
13296
|
if (worker === created) worker = null;
|
|
12534
13297
|
failAll(new VdeError("E_PARSE_FAILED", "The document could not be parsed.", { reason: "worker" }));
|
|
13298
|
+
collector.reset();
|
|
12535
13299
|
});
|
|
12536
13300
|
worker = created;
|
|
12537
13301
|
return created;
|
|
12538
13302
|
};
|
|
12539
13303
|
const request = (work) => {
|
|
12540
13304
|
if (closed) return Promise.reject(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
13305
|
+
const result = new Promise((resolve, reject) => {
|
|
13306
|
+
collector.dispatch(() => {
|
|
13307
|
+
const id = nextId;
|
|
13308
|
+
nextId += 1;
|
|
13309
|
+
const timer = setTimeout(() => {
|
|
13310
|
+
discardWorker();
|
|
13311
|
+
failAll(new VdeError("E_PARSE_FAILED", "Parsing the document did not finish in time.", {
|
|
13312
|
+
reason: "timeout",
|
|
13313
|
+
timeoutMs
|
|
13314
|
+
}));
|
|
13315
|
+
}, timeoutMs);
|
|
13316
|
+
pending.set(id, {
|
|
13317
|
+
resolve,
|
|
13318
|
+
reject,
|
|
13319
|
+
timer
|
|
13320
|
+
});
|
|
13321
|
+
ensureWorker().postMessage({
|
|
13322
|
+
id,
|
|
13323
|
+
...work
|
|
13324
|
+
});
|
|
13325
|
+
}, reject);
|
|
12560
13326
|
});
|
|
13327
|
+
if (work.op !== "diagnostics") result.then(collector.touch, collector.touch);
|
|
13328
|
+
return result;
|
|
12561
13329
|
};
|
|
12562
13330
|
return {
|
|
12563
13331
|
diagnostics: (collectGarbage, signal) => worker === null ? Promise.resolve(null) : untilAborted(request({
|
|
@@ -12580,6 +13348,7 @@ function createParseService(options = {}) {
|
|
|
12580
13348
|
}),
|
|
12581
13349
|
async close() {
|
|
12582
13350
|
closed = true;
|
|
13351
|
+
collector.close(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
|
|
12583
13352
|
failAll(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
|
|
12584
13353
|
const current = worker;
|
|
12585
13354
|
worker = null;
|
|
@@ -12944,6 +13713,7 @@ async function startDaemon(options) {
|
|
|
12944
13713
|
let feedback = null;
|
|
12945
13714
|
let announceStopping = () => void 0;
|
|
12946
13715
|
let lockTimer = null;
|
|
13716
|
+
let heapWatch = null;
|
|
12947
13717
|
let stopping = null;
|
|
12948
13718
|
const stopRequested = new AbortController();
|
|
12949
13719
|
const owned = {
|
|
@@ -12977,6 +13747,7 @@ async function startDaemon(options) {
|
|
|
12977
13747
|
};
|
|
12978
13748
|
stopRequested.abort();
|
|
12979
13749
|
if (lockTimer) clearInterval(lockTimer);
|
|
13750
|
+
heapWatch?.stop();
|
|
12980
13751
|
await step("watcher", () => watcher?.close(), SHUTDOWN_CLOSE_TIMEOUT_MS);
|
|
12981
13752
|
await step("feedback", () => feedback?.close());
|
|
12982
13753
|
await step("drain", () => ipc?.drain());
|
|
@@ -13060,6 +13831,7 @@ async function startDaemon(options) {
|
|
|
13060
13831
|
scan: (kind, text) => parser.scan(kind, text),
|
|
13061
13832
|
emit
|
|
13062
13833
|
});
|
|
13834
|
+
await documents.initializeRepositories();
|
|
13063
13835
|
feedback = new FeedbackService({
|
|
13064
13836
|
store: openedStore,
|
|
13065
13837
|
documents,
|
|
@@ -13238,6 +14010,7 @@ async function startDaemon(options) {
|
|
|
13238
14010
|
};
|
|
13239
14011
|
lockTimer = setInterval(() => void checkLock(), LOCK_CHECK_INTERVAL_MS);
|
|
13240
14012
|
lockTimer.unref();
|
|
14013
|
+
heapWatch = startHeapWatch();
|
|
13241
14014
|
logger.log("daemon.started", { openDocuments: openedStore.payload.openOrder.length });
|
|
13242
14015
|
} catch (error) {
|
|
13243
14016
|
logger.log("daemon.start-failed", { code: error instanceof VdeError ? error.code : "E_INTERNAL" });
|