UNPKG

pi-lens

Version:

Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo

1,611 lines • 54.7 kB
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
  deserializeWordIndex,
  recordPersistedWordIndexWireBytes,
  serializeWordIndex
} from "./chunk-YGVYKNSN.js";
import {
  fingerprintProjectSnapshotJson
} from "./chunk-2BEMIWEW.js";
import {
  getProjectDataDir,
  isStaleStageFile,
  withGenerationLockSync
} from "./chunk-VN2AXLEX.js";
import {
  writeFileAtomic
} from "./chunk-2EECBNC5.js";
import {
  incrementDegradationCount
} from "./chunk-N3YQJI6O.js";
import {
  logLatency
} from "./chunk-5THXD6X5.js";
import {
  BoundedFifoMap
} from "./chunk-ABBOA7UD.js";
import {
  normalizeMapKey
} from "./chunk-Q5U6FMDI.js";

// dist/clients/project-snapshot.js
import * as fs3 from "node:fs";
import * as path2 from "node:path";
import { fileURLToPath } from "node:url";
import { Worker } from "node:worker_threads";
import { gunzipSync, gzipSync } from "node:zlib";

// dist/clients/json-cache-read.js
import * as fs from "node:fs";
import { promises as fsPromises } from "node:fs";
function readJsonCache(path3, validate, onError) {
  try {
    const parsed = JSON.parse(fs.readFileSync(path3, "utf-8"));
    return validate(parsed);
  } catch (err) {
    try {
      onError?.(err);
    } catch {
    }
    return void 0;
  }
}
async function readJsonCacheAsync(path3, validate, onError) {
  try {
    const raw = await fsPromises.readFile(path3, "utf-8");
    const parsed = JSON.parse(raw);
    return validate(parsed);
  } catch (err) {
    try {
      onError?.(err);
    } catch {
    }
    return void 0;
  }
}

// dist/clients/project-conventions.js
import * as fs2 from "node:fs";
import * as path from "node:path";
function readPackageJson(cwd) {
  const pkgPath = path.join(cwd, "package.json");
  try {
    const raw = fs2.readFileSync(pkgPath, "utf-8");
    return JSON.parse(raw);
  } catch {
    return void 0;
  }
}
function hasDep(pkg, name) {
  if (!pkg)
    return false;
  return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name]);
}
function fileExists(cwd, ...segments) {
  try {
    return fs2.existsSync(path.join(cwd, ...segments));
  } catch {
    return false;
  }
}
function dirExists(cwd, ...segments) {
  try {
    const target = path.join(cwd, ...segments);
    const stat = fs2.statSync(target);
    return stat.isDirectory();
  } catch {
    return false;
  }
}
function detectReact(_cwd, pkg) {
  const signals = [];
  if (hasDep(pkg, "react"))
    signals.push("package.json:dependencies.react");
  if (hasDep(pkg, "react-dom"))
    signals.push("package.json:dependencies.react-dom");
  if (hasDep(pkg, "@types/react"))
    signals.push("package.json:devDependencies.@types/react");
  if (signals.length === 0)
    return void 0;
  const confidence = hasDep(pkg, "react-dom") ? "high" : "medium";
  return { id: "react", confidence, signals };
}
function detectNext(cwd, pkg) {
  const signals = [];
  if (hasDep(pkg, "next"))
    signals.push("package.json:dependencies.next");
  for (const candidate of [
    "next.config.js",
    "next.config.mjs",
    "next.config.ts"
  ]) {
    if (fileExists(cwd, candidate))
      signals.push(candidate);
  }
  if (dirExists(cwd, "src", "pages"))
    signals.push("src/pages/");
  else if (dirExists(cwd, "pages"))
    signals.push("pages/");
  if (dirExists(cwd, "src", "app"))
    signals.push("src/app/");
  else if (dirExists(cwd, "app"))
    signals.push("app/");
  if (signals.length === 0)
    return void 0;
  const confidence = hasDep(pkg, "next") && signals.some((s) => s.startsWith("next.config.")) ? "high" : hasDep(pkg, "next") ? "medium" : "low";
  return { id: "next", confidence, signals };
}
function detectVite(cwd, pkg) {
  const signals = [];
  if (hasDep(pkg, "vite"))
    signals.push("package.json:devDependencies.vite");
  for (const candidate of [
    "vite.config.ts",
    "vite.config.js",
    "vite.config.mjs",
    "vite.config.mts"
  ]) {
    if (fileExists(cwd, candidate))
      signals.push(candidate);
  }
  if (signals.length === 0)
    return void 0;
  const confidence = hasDep(pkg, "vite") && signals.some((s) => s.startsWith("vite.config.")) ? "high" : "medium";
  return { id: "vite", confidence, signals };
}
function detectVitest(cwd, pkg) {
  const signals = [];
  if (hasDep(pkg, "vitest"))
    signals.push("package.json:devDependencies.vitest");
  for (const candidate of [
    "vitest.config.ts",
    "vitest.config.js",
    "vitest.config.mjs",
    "vitest.config.mts"
  ]) {
    if (fileExists(cwd, candidate))
      signals.push(candidate);
  }
  if (signals.length === 0)
    return void 0;
  const confidence = hasDep(pkg, "vitest") ? "high" : "medium";
  return { id: "vitest", confidence, signals };
}
var AGENT_DOC_CANDIDATES = [
  "AGENTS.md",
  "CLAUDE.md",
  ".cursorrules"
];
function summarizeAgentDocs(cwd) {
  const docs = [];
  for (const candidate of AGENT_DOC_CANDIDATES) {
    const abs = path.join(cwd, candidate);
    try {
      const raw = fs2.readFileSync(abs, "utf-8");
      const lineCount = raw.split(/\r?\n/).length;
      docs.push({ filePath: candidate, lineCount });
    } catch {
    }
  }
  return docs;
}
function detectProjectConventions(cwd) {
  let cwdExists = false;
  try {
    cwdExists = fs2.statSync(cwd).isDirectory();
  } catch {
    cwdExists = false;
  }
  if (!cwdExists) {
    return { frameworks: [], testRunners: [], buildTools: [], agentDocs: [] };
  }
  const pkg = readPackageJson(cwd);
  const detectors = [detectReact, detectNext, detectVite, detectVitest];
  const frameworks = [];
  for (const detector of detectors) {
    const detection = detector(cwd, pkg);
    if (detection)
      frameworks.push(detection);
  }
  const testRunners = [];
  if (frameworks.some((f) => f.id === "vitest"))
    testRunners.push("vitest");
  const buildTools = [];
  if (frameworks.some((f) => f.id === "vite"))
    buildTools.push("vite");
  if (frameworks.some((f) => f.id === "next"))
    buildTools.push("next");
  const agentDocs = summarizeAgentDocs(cwd);
  return { frameworks, testRunners, buildTools, agentDocs };
}

// dist/clients/persist-worker-stats.js
async function readWorkerHeapStatistics(worker) {
  const stats = await worker.getHeapStatistics();
  return {
    heapUsedBytes: stats.used_heap_size,
    heapTotalBytes: stats.total_heap_size,
    heapSizeLimitBytes: stats.heap_size_limit
  };
}

// dist/clients/project-snapshot.js
var PROJECT_SNAPSHOT_VERSION = 2;
function parseSequenceIndex(value) {
  if (!value || typeof value !== "object")
    return void 0;
  const index = value;
  if (typeof index.projectSeq !== "number")
    return void 0;
  if (!Array.isArray(index.fileSeqByPath))
    return void 0;
  const fileSeqByPath = index.fileSeqByPath.filter((entry) => Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "number");
  return { projectSeq: index.projectSeq, fileSeqByPath };
}
function getProjectSnapshotPath(cwd) {
  return path2.join(getProjectDataDir(cwd), "cache", "project-snapshot.json.gz");
}
function getProjectSnapshotLegacyPath(cwd) {
  return path2.join(getProjectDataDir(cwd), "cache", "project-snapshot.json");
}
function getProjectSnapshotMetaPath(cwd) {
  return path2.join(getProjectDataDir(cwd), "cache", "project-snapshot.meta.json");
}
var INCOMPLETE_STORED_SEQ = -2;
function storedSeq(snapshot) {
  return snapshot.incomplete ? { seq: INCOMPLETE_STORED_SEQ, incompleteSeq: snapshot.seq } : { seq: snapshot.seq };
}
function storedSnapshot(snapshot) {
  if (!snapshot.incomplete)
    return snapshot;
  const stored = { ...snapshot };
  delete stored.incomplete;
  return { ...stored, ...storedSeq(snapshot) };
}
function readStoredSeq(stored) {
  if (typeof stored.incompleteSeq === "number") {
    return { seq: stored.incompleteSeq, incomplete: true };
  }
  return typeof stored.seq === "number" ? { seq: stored.seq } : null;
}
function isProjectSnapshotFresh(snapshot, currentProjectSeq, unlockedThrough = 0) {
  return !!snapshot && snapshot.version === PROJECT_SNAPSHOT_VERSION && !snapshot.incomplete && (snapshot.logEntries ?? 0) >= unlockedThrough && snapshot.seq === currentProjectSeq;
}
function parseSnapshot(value) {
  if (!value || typeof value !== "object")
    return null;
  const snapshot = value;
  if (snapshot.version !== PROJECT_SNAPSHOT_VERSION)
    return null;
  if (typeof snapshot.projectRoot !== "string")
    return null;
  if (typeof snapshot.generatedAt !== "string")
    return null;
  const stored = readStoredSeq(value);
  if (!stored)
    return null;
  if (!Array.isArray(snapshot.cachedExports))
    return null;
  return {
    version: PROJECT_SNAPSHOT_VERSION,
    projectRoot: snapshot.projectRoot,
    generatedAt: snapshot.generatedAt,
    seq: stored.seq,
    files: snapshot.files ?? {},
    symbols: snapshot.symbols ?? {},
    reverseDeps: snapshot.reverseDeps ?? {},
    cachedExports: snapshot.cachedExports.filter((entry) => Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "string"),
    ...stored.incomplete ? { incomplete: true } : {},
    logEntries: typeof snapshot.logEntries === "number" ? snapshot.logEntries : void 0,
    sequenceIndex: parseSequenceIndex(snapshot.sequenceIndex),
    wordIndex: snapshot.wordIndex,
    projectRulesScan: snapshot.projectRulesScan,
    startupScan: snapshot.startupScan,
    languageProfile: snapshot.languageProfile,
    conventions: snapshot.conventions
  };
}
function parseSnapshotMeta(value) {
  if (!value || typeof value !== "object")
    return null;
  const meta = value;
  if (typeof meta.version !== "number")
    return null;
  const stored = readStoredSeq(value);
  if (!stored)
    return null;
  return {
    timestamp: typeof meta.timestamp === "string" ? meta.timestamp : "",
    version: meta.version,
    seq: stored.seq,
    fingerprint: typeof meta.fingerprint === "string" && /^[a-f0-9]{64}$/.test(meta.fingerprint) ? meta.fingerprint : void 0,
    gzBytes: typeof meta.gzBytes === "number" && Number.isInteger(meta.gzBytes) && meta.gzBytes > 0 ? meta.gzBytes : void 0,
    ...stored.incomplete ? { incomplete: true } : {},
    logEntries: typeof meta.logEntries === "number" ? meta.logEntries : void 0,
    sequenceIndex: parseSequenceIndex(meta.sequenceIndex)
  };
}
function readProjectSnapshotMeta(cwd) {
  const meta = readJsonCache(getProjectSnapshotMetaPath(cwd), (parsed) => parseSnapshotMeta(parsed) ?? void 0);
  return meta ?? null;
}
function isProjectSnapshotMetaStale(meta, currentProjectSeq, unlockedThrough = 0) {
  return meta.version !== PROJECT_SNAPSHOT_VERSION || meta.incomplete === true || (meta.logEntries ?? 0) < unlockedThrough || meta.seq !== currentProjectSeq;
}
var SNAPSHOT_PARSE_CACHE_MAX = 4;
var SNAPSHOT_PARSE_CACHE_MAX_BYTES = 24 * 1024 * 1024;
var snapshotParseCache = new BoundedFifoMap(SNAPSHOT_PARSE_CACHE_MAX);
function withoutWordIndex(snapshot) {
  if (!snapshot?.wordIndex)
    return snapshot;
  const { wordIndex: _releasedPostings, ...stripped } = snapshot;
  return stripped;
}
function cacheParsedSnapshot(snapshotPath, entry) {
  snapshotParseCache.delete(snapshotPath);
  snapshotParseCache.set(snapshotPath, entry);
}
var authoritativeSnapshots = /* @__PURE__ */ new Map();
var PROJECT_SNAPSHOT_MAX_WARM_ROOTS = 8;
var PROJECT_SNAPSHOT_IDLE_EVICT_MS_DEFAULT = 20 * 6e4;
function projectSnapshotIdleEvictMs() {
  const value = Number.parseInt(process.env.PI_LENS_PROJECT_SNAPSHOT_IDLE_EVICT_MS ?? "", 10);
  return Number.isSafeInteger(value) && value > 0 ? value : PROJECT_SNAPSHOT_IDLE_EVICT_MS_DEFAULT;
}
function clearAuthoritativeSnapshotTimer(entry) {
  if (entry.idleTimer)
    clearTimeout(entry.idleTimer);
  entry.idleTimer = void 0;
}
function deleteAuthoritativeSnapshot(key) {
  const entry = authoritativeSnapshots.get(key);
  if (entry)
    clearAuthoritativeSnapshotTimer(entry);
  authoritativeSnapshots.delete(key);
}
function scheduleAuthoritativeSnapshotEviction(key, entry) {
  clearAuthoritativeSnapshotTimer(entry);
  const generation = entry.lastUsedAt;
  entry.idleTimer = setTimeout(() => {
    entry.idleTimer = void 0;
    if (authoritativeSnapshots.get(key) !== entry || entry.lastUsedAt !== generation)
      return;
    deleteAuthoritativeSnapshot(key);
  }, projectSnapshotIdleEvictMs());
  entry.idleTimer.unref?.();
}
function touchAuthoritativeSnapshot(key, entry) {
  entry.lastUsedAt = Date.now();
  scheduleAuthoritativeSnapshotEviction(key, entry);
}
function enforceAuthoritativeSnapshotCap() {
  while (authoritativeSnapshots.size > PROJECT_SNAPSHOT_MAX_WARM_ROOTS) {
    const victim = [...authoritativeSnapshots.entries()].sort(([, a], [, b]) => a.lastUsedAt - b.lastUsedAt)[0];
    if (!victim)
      return;
    clearAuthoritativeSnapshotTimer(victim[1]);
    deleteAuthoritativeSnapshot(victim[0]);
  }
}
function _getAuthoritativeSnapshotCacheKeysForTests() {
  return [...authoritativeSnapshots.entries()].sort(([, a], [, b]) => a.lastUsedAt - b.lastUsedAt).map(([key]) => key);
}
function _resetProjectSnapshotParseCacheForTests() {
  snapshotParseCache.clear();
  for (const entry of authoritativeSnapshots.values())
    clearAuthoritativeSnapshotTimer(entry);
  authoritativeSnapshots.clear();
}
function _projectSnapshotParseCacheRetainsWordIndexForTests() {
  return [...snapshotParseCache.values()].some((entry) => entry.snapshot?.wordIndex !== void 0);
}
function resolveSnapshotBodyPath(cwd) {
  const gzPath = getProjectSnapshotPath(cwd);
  try {
    const stat = fs3.statSync(gzPath);
    return { path: gzPath, gz: true, mtimeMs: stat.mtimeMs, size: stat.size };
  } catch {
  }
  const legacyPath = getProjectSnapshotLegacyPath(cwd);
  try {
    const stat = fs3.statSync(legacyPath);
    return {
      path: legacyPath,
      gz: false,
      mtimeMs: stat.mtimeMs,
      size: stat.size
    };
  } catch {
    return null;
  }
}
var _snapshotBodyReadCountForTests = 0;
function getSnapshotBodyReadCountForTests() {
  return _snapshotBodyReadCountForTests;
}
function resetSnapshotBodyReadCountForTests() {
  _snapshotBodyReadCountForTests = 0;
}
function readSnapshotBody(bodyPath, gz) {
  _snapshotBodyReadCountForTests++;
  if (!gz) {
    const snapshot = readJsonCache(bodyPath, (parsed) => parseSnapshot(parsed) ?? void 0) ?? null;
    let rawBytes = 0;
    try {
      rawBytes = fs3.statSync(bodyPath).size;
    } catch {
    }
    return { snapshot, rawBytes };
  }
  try {
    const json = gunzipSync(fs3.readFileSync(bodyPath)).toString("utf-8");
    const snapshot = parseSnapshot(JSON.parse(json)) ?? null;
    return { snapshot, rawBytes: Buffer.byteLength(json) };
  } catch (err) {
    logLatency({
      type: "phase",
      phase: "project_snapshot_body_corrupt",
      filePath: bodyPath,
      durationMs: 0,
      metadata: { error: err instanceof Error ? err.message : String(err) }
    });
    return { snapshot: null, rawBytes: 0 };
  }
}
function skipJsonValue(text, start) {
  let i = start;
  while (i < text.length && /\s/.test(text[i]))
    i++;
  const ch = text[i];
  if (ch === '"') {
    i++;
    while (i < text.length) {
      if (text[i] === "\\") {
        i += 2;
        continue;
      }
      if (text[i] === '"') {
        i++;
        break;
      }
      i++;
    }
    return i;
  }
  if (ch === "{" || ch === "[") {
    const open = ch;
    const close = open === "{" ? "}" : "]";
    let depth = 0;
    let inString = false;
    for (; i < text.length; i++) {
      const c = text[i];
      if (inString) {
        if (c === "\\") {
          i++;
          continue;
        }
        if (c === '"')
          inString = false;
        continue;
      }
      if (c === '"') {
        inString = true;
        continue;
      }
      if (c === open)
        depth++;
      else if (c === close) {
        depth--;
        if (depth === 0) {
          i++;
          break;
        }
      }
    }
    return i;
  }
  while (i < text.length && !",}]".includes(text[i]))
    i++;
  return i;
}
function stripTopLevelJsonKeys(text, keysToStrip) {
  const objStart = text.indexOf("{");
  if (objStart === -1)
    return text;
  let out = text.slice(0, objStart + 1);
  let i = objStart + 1;
  let wroteField = false;
  while (i < text.length) {
    while (i < text.length && /\s/.test(text[i]))
      i++;
    if (text[i] === "}" || i >= text.length) {
      out += text.slice(i);
      break;
    }
    if (text[i] === ",") {
      i++;
      continue;
    }
    const keyStart = i;
    const keyTextEnd = skipJsonValue(text, keyStart);
    let key;
    try {
      key = JSON.parse(text.slice(keyStart, keyTextEnd));
    } catch {
      return text;
    }
    let j = keyTextEnd;
    while (j < text.length && /\s/.test(text[j]))
      j++;
    const valueStart = j + 1;
    const valueEnd = skipJsonValue(text, valueStart);
    if (!keysToStrip.has(key)) {
      out += (wroteField ? "," : "") + text.slice(keyStart, valueEnd);
      wroteField = true;
    }
    i = valueEnd;
  }
  return out;
}
function _stripTopLevelJsonKeysForTests(text, keysToStrip) {
  return stripTopLevelJsonKeys(text, new Set(keysToStrip));
}
var HEAVY_SNAPSHOT_KEYS = /* @__PURE__ */ new Set([
  "wordIndex",
  "files",
  "symbols",
  "reverseDeps"
]);
var NARROW_DIGEST_HEAVY_KEY_LITERALS = [
  "wordIndex",
  "files",
  "symbols",
  "reverseDeps"
];
var _lastNarrowParseDigestForTests;
function getLastNarrowParseDigestForTests() {
  return _lastNarrowParseDigestForTests;
}
function resetLastNarrowParseDigestForTests() {
  _lastNarrowParseDigestForTests = void 0;
}
function parseExportsAndRulesOnly(json) {
  const narrowed = stripTopLevelJsonKeys(json, HEAVY_SNAPSHOT_KEYS);
  _lastNarrowParseDigestForTests = {
    length: narrowed.length,
    containsHeavyKey: NARROW_DIGEST_HEAVY_KEY_LITERALS.some((key) => narrowed.includes(`"${key}":`))
  };
  const parsed = JSON.parse(narrowed);
  if (parsed.version !== PROJECT_SNAPSHOT_VERSION)
    return null;
  const stored = readStoredSeq(parsed);
  if (!stored)
    return null;
  if (!Array.isArray(parsed.cachedExports))
    return null;
  return {
    version: PROJECT_SNAPSHOT_VERSION,
    seq: stored.seq,
    ...stored.incomplete ? { incomplete: true } : {},
    logEntries: typeof parsed.logEntries === "number" ? parsed.logEntries : void 0,
    cachedExports: parsed.cachedExports.filter((entry) => Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "string"),
    projectRulesScan: parsed.projectRulesScan
  };
}
function readSnapshotExportsAndRulesBody(bodyPath, gz) {
  try {
    const json = gz ? gunzipSync(fs3.readFileSync(bodyPath)).toString("utf-8") : fs3.readFileSync(bodyPath, "utf-8");
    return parseExportsAndRulesOnly(json);
  } catch (err) {
    logLatency({
      type: "phase",
      phase: "project_snapshot_body_corrupt",
      filePath: bodyPath,
      durationMs: 0,
      metadata: {
        error: err instanceof Error ? err.message : String(err),
        narrow: true
      }
    });
    return null;
  }
}
function loadProjectSnapshotExportsAndRules(cwd) {
  const key = normalizeMapKey(cwd);
  const body = resolveSnapshotBodyPath(cwd);
  const authoritative = authoritativeSnapshots.get(key);
  if (authoritative) {
    const notSuperseded = body === null || body.mtimeMs <= authoritative.knownMtime && body.size === authoritative.knownSize;
    if (notSuperseded) {
      const { version, seq, incomplete, logEntries, cachedExports, projectRulesScan } = authoritative.snapshot;
      return {
        version,
        seq,
        ...incomplete ? { incomplete } : {},
        logEntries,
        cachedExports,
        projectRulesScan
      };
    }
  }
  if (!body)
    return null;
  return readSnapshotExportsAndRulesBody(body.path, body.gz);
}
function loadProjectSnapshotInternal(cwd, requireWordIndex) {
  const key = normalizeMapKey(cwd);
  const body = resolveSnapshotBodyPath(cwd);
  const authoritative = authoritativeSnapshots.get(key);
  if (authoritative) {
    const notSuperseded = body === null || body.mtimeMs <= authoritative.knownMtime && body.size === authoritative.knownSize;
    if (notSuperseded) {
      touchAuthoritativeSnapshot(key, authoritative);
      return authoritative.snapshot;
    }
    deleteAuthoritativeSnapshot(key);
  }
  if (!body) {
    snapshotParseCache.delete(getProjectSnapshotPath(cwd));
    return null;
  }
  const cacheKey = body.path;
  const cached = snapshotParseCache.get(cacheKey);
  if (cached && cached.mtimeMs === body.mtimeMs && cached.size === body.size) {
    if (!requireWordIndex || !cached.snapshot || cached.snapshot.wordIndex) {
      return cached.snapshot;
    }
  }
  const { snapshot, rawBytes } = readSnapshotBody(body.path, body.gz);
  const cacheSnapshot = withoutWordIndex(snapshot);
  if (rawBytes > 0 && (rawBytes <= SNAPSHOT_PARSE_CACHE_MAX_BYTES || snapshot?.wordIndex)) {
    cacheParsedSnapshot(cacheKey, {
      mtimeMs: body.mtimeMs,
      size: body.size,
      snapshot: cacheSnapshot
    });
  } else {
    snapshotParseCache.delete(cacheKey);
  }
  return snapshot;
}
function loadProjectSnapshot(cwd) {
  return loadProjectSnapshotInternal(cwd, true);
}
function loadProjectSnapshotWithoutWordIndex(cwd) {
  return loadProjectSnapshotInternal(cwd, false);
}
var _snapshotGenerationStates = /* @__PURE__ */ new Map();
var _successfulSnapshotPersists = new BoundedFifoMap(PROJECT_SNAPSHOT_MAX_WARM_ROOTS);
var _failedSnapshotPersists = new BoundedFifoMap(PROJECT_SNAPSHOT_MAX_WARM_ROOTS);
var _activeSnapshotPersists = /* @__PURE__ */ new Map();
var _queuedSnapshotPersists = /* @__PURE__ */ new Map();
var _snapshotWorkerRequests = /* @__PURE__ */ new Map();
var _snapshotPersistWorker;
var _snapshotWorkerRequestId = 0;
var _snapshotWorkerDisabled = false;
var _snapshotGenerationGateEnabledForTests = true;
var _snapshotPromotionSeamForTests;
var _lastSnapshotPersistErrorForTests;
var _snapshotExiting = false;
var _snapshotWorkerBodyWritesForTests = 0;
var _snapshotPersistWorkerHeapStatistics = null;
function refreshProjectSnapshotPersistWorkerHeapStatistics() {
  const worker = _snapshotPersistWorker;
  if (!worker || typeof worker.getHeapStatistics !== "function") {
    return Promise.resolve();
  }
  return readWorkerHeapStatistics(worker).then((stats) => {
    if (_snapshotPersistWorker === worker)
      _snapshotPersistWorkerHeapStatistics = stats;
  }).catch(() => {
  });
}
function getProjectSnapshotPersistWorkerHeapStatistics() {
  return _snapshotPersistWorkerHeapStatistics;
}
function snapshotWorkerEnabled() {
  const raw = process.env.PI_LENS_SNAPSHOT_PERSIST_SYNC;
  return !(raw === "1" || raw === "true");
}
function pendingSnapshotIsCurrent(pending) {
  return !_snapshotGenerationGateEnabledForTests || _snapshotGenerationStates.get(pending.key)?.generation === pending.generation;
}
function rememberSuccessfulSnapshotPersist(key, record) {
  _successfulSnapshotPersists.delete(key);
  _successfulSnapshotPersists.set(key, record);
}
function assessSnapshotBodyIntegrity(meta, body) {
  if (meta.gzBytes === void 0) {
    return { outcome: "legacy-meta", actualBytes: body.size };
  }
  if (!body.gz || body.size !== meta.gzBytes) {
    return {
      outcome: "size-mismatch",
      expectedGzBytes: meta.gzBytes,
      actualBytes: body.size
    };
  }
  return {
    outcome: "ok",
    expectedGzBytes: meta.gzBytes,
    actualBytes: body.size
  };
}
function noteSnapshotBodyIntegrityWithheld(args) {
  incrementDegradationCount({
    kind: "snapshot-integrity",
    subject: args.gzPath,
    reason: args.verdict
  });
  logLatency({
    type: "phase",
    phase: "project_snapshot_body_integrity",
    filePath: args.gzPath,
    durationMs: 0,
    metadata: {
      outcome: "dedupe_withheld",
      reason: args.verdict,
      ...args.seq !== void 0 ? { seq: args.seq } : {},
      ...args.expectedGzBytes === void 0 ? {} : { expectedGzBytes: args.expectedGzBytes },
      actualBytes: args.actualBytes
    }
  });
}
function noteSnapshotSkipRefused(args) {
  logLatency({
    type: "phase",
    phase: "project_snapshot_body_integrity",
    filePath: args.gzPath,
    durationMs: 0,
    metadata: {
      outcome: "skip_refused_rewrite",
      reason: args.verdict,
      seq: args.seq
    }
  });
}
function snapshotPersistBaselinesFor(cwd, key) {
  const local = _successfulSnapshotPersists.get(key);
  const meta = readProjectSnapshotMeta(cwd);
  const body = resolveSnapshotBodyPath(cwd);
  if (!meta?.fingerprint || !body) {
    if (!body)
      _successfulSnapshotPersists.delete(key);
    return { fingerprints: [] };
  }
  const integrity = assessSnapshotBodyIntegrity(meta, body);
  if (integrity.outcome !== "ok") {
    noteSnapshotBodyIntegrityWithheld({
      gzPath: getProjectSnapshotPath(cwd),
      verdict: integrity.outcome,
      seq: meta.seq,
      expectedGzBytes: integrity.expectedGzBytes,
      actualBytes: integrity.actualBytes
    });
    return { fingerprints: [] };
  }
  const durable = {
    seq: meta.seq,
    fingerprint: meta.fingerprint,
    generatedAt: meta.timestamp,
    generation: _snapshotGenerationStates.get(key)?.generation ?? 0,
    gzBytes: meta.gzBytes
  };
  const fingerprints = [durable.fingerprint];
  if (local && local.fingerprint !== durable.fingerprint) {
    fingerprints.push(local.fingerprint);
  }
  return { durable, fingerprints, integrity: "ok" };
}
function seedSnapshotPersistBaselineFromDurable(key, durable) {
  if (!durable || _successfulSnapshotPersists.has(key))
    return;
  rememberSuccessfulSnapshotPersist(key, durable);
}
function writeProjectSnapshotMeta(metaPath, snapshot, bodyRecord) {
  writeFileAtomic(metaPath, JSON.stringify({
    timestamp: bodyRecord?.generatedAt ?? snapshot.generatedAt,
    version: snapshot.version,
    ...storedSeq(snapshot),
    ...bodyRecord ? {
      fingerprint: bodyRecord.fingerprint,
      ...bodyRecord.gzBytes === void 0 ? {} : { gzBytes: bodyRecord.gzBytes }
    } : {},
    logEntries: snapshot.logEntries,
    ...snapshot.sequenceIndex ? { sequenceIndex: snapshot.sequenceIndex } : {}
  }), { bestEffort: false });
}
function recordSnapshotPersistFailure(pending, error, bodyPersisted = false) {
  _lastSnapshotPersistErrorForTests = error;
  _failedSnapshotPersists.set(pending.key, {
    seq: pending.snapshot.seq,
    generation: pending.generation
  });
  deleteAuthoritativeSnapshot(pending.key);
  logLatency({
    type: "phase",
    phase: "project_snapshot_persist_failed",
    filePath: pending.gzPath,
    durationMs: 0,
    metadata: {
      error,
      seq: pending.snapshot.seq,
      outcome: bodyPersisted ? "metadata_repair_required" : "failed",
      ...bodyPersisted ? { bodyPersisted: true } : {}
    }
  });
}
function logSnapshotPersistSuccess(pending, fingerprint, stats) {
  rememberSuccessfulSnapshotPersist(pending.key, {
    seq: pending.snapshot.seq,
    fingerprint,
    generatedAt: pending.snapshot.generatedAt,
    generation: pending.generation,
    rawBytes: stats.rawBytes,
    gzBytes: stats.gzBytes
  });
  _failedSnapshotPersists.delete(pending.key);
  logLatency({
    type: "phase",
    phase: "project_snapshot_persist",
    filePath: pending.gzPath,
    durationMs: stats.serializeMs + stats.writeMs,
    metadata: { seq: pending.snapshot.seq, outcome: "executed", ...stats }
  });
}
function logSnapshotPersistDecision(args) {
  logLatency({
    type: "phase",
    phase: "project_snapshot_persist_decision",
    filePath: getProjectSnapshotPath(args.cwd),
    durationMs: 0,
    metadata: {
      seq: args.seq,
      decision: args.decision,
      ...args.fingerprint ? { fingerprint: args.fingerprint.slice(0, 12) } : {},
      ...args.avoidedRawBytes === void 0 ? {} : { avoidedRawBytes: args.avoidedRawBytes },
      ...args.avoidedGzipBytes === void 0 ? {} : { avoidedGzipBytes: args.avoidedGzipBytes },
      ...args.diskSeq === void 0 ? {} : { diskSeq: args.diskSeq }
    }
  });
}
function reconcileAuthoritativeAfterWrite(pending, rawBytes) {
  const entry = authoritativeSnapshots.get(pending.key);
  if (!entry || entry.snapshot !== pending.snapshot)
    return;
  if (pending.snapshot.wordIndex) {
    try {
      const stat = fs3.statSync(pending.gzPath);
      cacheParsedSnapshot(pending.gzPath, {
        mtimeMs: stat.mtimeMs,
        size: stat.size,
        snapshot: withoutWordIndex(pending.snapshot)
      });
    } catch {
    }
    deleteAuthoritativeSnapshot(pending.key);
    logLatency({
      type: "phase",
      phase: "project_snapshot_word_index_released",
      filePath: pending.gzPath,
      durationMs: 0,
      metadata: { rawBytes }
    });
    return;
  }
  if (rawBytes > SNAPSHOT_PARSE_CACHE_MAX_BYTES) {
    logLatency({
      type: "phase",
      phase: "project_snapshot_authoritative_dropped_oversized",
      filePath: pending.gzPath,
      durationMs: 0,
      metadata: { rawBytes, maxBytes: SNAPSHOT_PARSE_CACHE_MAX_BYTES }
    });
    deleteAuthoritativeSnapshot(pending.key);
    return;
  }
  try {
    const stat = fs3.statSync(pending.gzPath);
    entry.knownMtime = stat.mtimeMs;
    entry.knownSize = stat.size;
  } catch {
  }
}
function finalizeProjectSnapshotMeta(pending, record) {
  try {
    writeProjectSnapshotMeta(getProjectSnapshotMetaPath(pending.cwd), pending.snapshot, record);
    return true;
  } catch (err) {
    recordSnapshotPersistFailure(pending, err instanceof Error ? err.message : String(err), true);
    return false;
  }
}
function completeSnapshotPersist(pending) {
  if (_activeSnapshotPersists.get(pending.key) !== pending)
    return;
  _activeSnapshotPersists.delete(pending.key);
  if (_snapshotExiting)
    return;
  const queued = _queuedSnapshotPersists.get(pending.key);
  if (!queued)
    return;
  _queuedSnapshotPersists.delete(pending.key);
  dispatchSnapshotPersist(queued);
}
var SNAPSHOT_CACHE_LOCK = { staleMs: 5e3, waitMs: 500 };
function withSnapshotCacheLock(gzPath, purpose, op) {
  const result = withGenerationLockSync(`${gzPath}.locks`, SNAPSHOT_CACHE_LOCK, op);
  if (!result.held) {
    const code = result.cause?.code;
    incrementDegradationCount({
      kind: "project-snapshot-lock-unavailable",
      subject: gzPath,
      reason: `${purpose} skipped: ${code ? `lock failed (${code})` : "lock wait ran out"}`
    });
  }
  return result;
}
function promoteSnapshotBody(pending, body, record) {
  const dropStage = () => {
    if ("stagePath" in body)
      fs3.rmSync(body.stagePath, { force: true });
  };
  const locked = withSnapshotCacheLock(pending.gzPath, "body promotion", () => {
    const onDisk = readProjectSnapshotMeta(pending.cwd);
    if (onDisk && onDisk.seq > pending.snapshot.seq) {
      dropStage();
      return onDisk.seq;
    }
    if ("stagePath" in body)
      fs3.renameSync(body.stagePath, pending.gzPath);
    else
      writeFileAtomic(pending.gzPath, body.gzip, { bestEffort: false });
    fs3.rmSync(pending.legacyPath, { force: true });
    return finalizeProjectSnapshotMeta(pending, record);
  });
  if (!locked.held) {
    dropStage();
    recordSnapshotPersistFailure(pending, "project snapshot cache lock unavailable");
    return false;
  }
  if (typeof locked.value === "boolean")
    return locked.value;
  deleteAuthoritativeSnapshot(pending.key);
  logSnapshotPersistDecision({
    cwd: pending.cwd,
    seq: pending.snapshot.seq,
    decision: "superseded_on_disk",
    diskSeq: locked.value
  });
  return false;
}
function serializeSnapshotBody(snapshot) {
  const started = performance.now();
  const stored = storedSnapshot(snapshot);
  const spliced = stored.wordIndex === void 0 ? void 0 : spliceWordIndexBody(stored);
  const bytes = spliced?.bytes ?? new TextEncoder().encode(JSON.stringify(stored));
  return {
    bytes,
    serializeMs: performance.now() - started,
    wordIndexWireBytes: spliced?.wordIndexWireBytes
  };
}
var WORD_INDEX_PLACEHOLDER = `\0pi-lens-word-index-${process.pid}-${Date.now()}\0`;
var WORD_INDEX_KEY_JSON = '"wordIndex":';
var WORD_INDEX_PLACEHOLDER_JSON = `${WORD_INDEX_KEY_JSON}${JSON.stringify(WORD_INDEX_PLACEHOLDER)}`;
function spliceWordIndexBody(stored) {
  const marker = WORD_INDEX_PLACEHOLDER_JSON;
  const head = JSON.stringify({ ...stored, wordIndex: WORD_INDEX_PLACEHOLDER });
  const at = head.indexOf(marker);
  if (at < 0)
    return void 0;
  const prefix = head.slice(0, at + WORD_INDEX_KEY_JSON.length);
  const suffix = head.slice(at + marker.length);
  const wordIndex = JSON.stringify(stored.wordIndex);
  const prefixBytes = Buffer.byteLength(prefix);
  const wordIndexWireBytes = Buffer.byteLength(wordIndex);
  const bytes = new Uint8Array(prefixBytes + wordIndexWireBytes + Buffer.byteLength(suffix));
  const encoder = new TextEncoder();
  encoder.encodeInto(prefix, bytes.subarray(0, prefixBytes));
  encoder.encodeInto(wordIndex, bytes.subarray(prefixBytes, prefixBytes + wordIndexWireBytes));
  encoder.encodeInto(suffix, bytes.subarray(prefixBytes + wordIndexWireBytes));
  return { bytes, wordIndexWireBytes };
}
function writeSnapshotBodyOnMainThread(pending, reason) {
  if (!pendingSnapshotIsCurrent(pending)) {
    completeSnapshotPersist(pending);
    return;
  }
  if (reason) {
    logLatency({
      type: "phase",
      phase: "project_snapshot_worker_fallback",
      filePath: pending.gzPath,
      durationMs: 0,
      metadata: {
        reason,
        seq: pending.snapshot.seq,
        degraded: reason !== "exit_hook"
      }
    });
  }
  try {
    const { bytes, serializeMs, wordIndexWireBytes } = serializeSnapshotBody(pending.snapshot);
    pending.wordIndexWireBytes = wordIndexWireBytes;
    const rawBytes = bytes.byteLength;
    const fingerprint = fingerprintProjectSnapshotJson(bytes, pending.snapshot.generatedAt);
    if (pending.dedupeFingerprints.includes(fingerprint)) {
      const current = snapshotPersistBaselinesFor(pending.cwd, pending.key);
      if (current.integrity === "ok") {
        const prior = current.durable ?? pending.durablePersist;
        if (authoritativeSnapshots.get(pending.key)?.snapshot === pending.snapshot) {
          deleteAuthoritativeSnapshot(pending.key);
        }
        logSnapshotPersistDecision({
          cwd: pending.cwd,
          seq: pending.snapshot.seq,
          fingerprint,
          decision: "skipped_unchanged",
          avoidedRawBytes: rawBytes,
          avoidedGzipBytes: prior?.gzBytes
        });
        return;
      }
      if (current.integrity !== void 0) {
        noteSnapshotSkipRefused({
          gzPath: pending.gzPath,
          verdict: current.integrity,
          seq: pending.snapshot.seq
        });
      }
    }
    const writeStarted = performance.now();
    const gzip = gzipSync(bytes);
    fs3.mkdirSync(path2.dirname(pending.gzPath), { recursive: true });
    const promoted = promoteSnapshotBody(pending, { gzip }, {
      fingerprint,
      generatedAt: pending.snapshot.generatedAt,
      gzBytes: gzip.byteLength
    });
    if (!promoted)
      return;
    recordPersistedWordIndexWireBytes(pending.snapshot.wordIndex, pending.wordIndexWireBytes);
    reconcileAuthoritativeAfterWrite(pending, rawBytes);
    logSnapshotPersistSuccess(pending, fingerprint, {
      rawBytes,
      gzBytes: gzip.byteLength,
      serializeMs,
      writeMs: performance.now() - writeStarted,
      offloaded: false
    });
    if (reason)
      _lastSnapshotPersistErrorForTests = reason;
  } catch (err) {
    recordSnapshotPersistFailure(pending, err instanceof Error ? err.message : String(err));
  } finally {
    completeSnapshotPersist(pending);
  }
}
function dispatchMainThreadWriteThroughSeam(pending, reason) {
  if (_snapshotPromotionSeamForTests) {
    void _snapshotPromotionSeamForTests().then(() => writeSnapshotBodyOnMainThread(pending, reason));
    return;
  }
  writeSnapshotBodyOnMainThread(pending, reason);
}
function handleSnapshotWorkerResult(result) {
  const pending = _snapshotWorkerRequests.get(result.id);
  if (!pending) {
    fs3.rmSync(result.stagePath, { force: true });
    return;
  }
  _snapshotWorkerRequests.delete(result.id);
  if (result.error || result.rawBytes === void 0 || result.gzBytes === void 0 || result.serializeMs === void 0 || result.writeMs === void 0 || result.semanticFingerprint === void 0) {
    fs3.rmSync(result.stagePath, { force: true });
    dispatchMainThreadWriteThroughSeam(pending, result.error ?? "invalid worker result");
    return;
  }
  if (!result.skippedUnchanged)
    _snapshotWorkerBodyWritesForTests++;
  if (_snapshotGenerationGateEnabledForTests && _snapshotGenerationStates.get(pending.key)?.generation !== result.generation) {
    fs3.rmSync(result.stagePath, { force: true });
    completeSnapshotPersist(pending);
    return;
  }
  if (result.skippedUnchanged) {
    const baselines = snapshotPersistBaselinesFor(pending.cwd, pending.key);
    if (baselines.integrity !== "ok") {
      if (baselines.integrity !== void 0) {
        noteSnapshotSkipRefused({
          gzPath: pending.gzPath,
          verdict: baselines.integrity,
          seq: pending.snapshot.seq
        });
      }
      dispatchMainThreadWriteThroughSeam(pending, "snapshot body integrity regressed");
      return;
    }
    const prior = baselines.durable ?? pending.durablePersist;
    if (authoritativeSnapshots.get(pending.key)?.snapshot === pending.snapshot) {
      deleteAuthoritativeSnapshot(pending.key);
    }
    logSnapshotPersistDecision({
      cwd: pending.cwd,
      seq: pending.snapshot.seq,
      fingerprint: result.semanticFingerprint,
      decision: "skipped_unchanged",
      avoidedRawBytes: result.rawBytes,
      avoidedGzipBytes: prior?.gzBytes
    });
    completeSnapshotPersist(pending);
    return;
  }
  try {
    const promoted = promoteSnapshotBody(pending, { stagePath: result.stagePath }, {
      fingerprint: result.semanticFingerprint,
      generatedAt: pending.snapshot.generatedAt,
      gzBytes: result.gzBytes
    });
    if (!promoted) {
      completeSnapshotPersist(pending);
      return;
    }
    recordPersistedWordIndexWireBytes(pending.snapshot.wordIndex, pending.wordIndexWireBytes);
    reconcileAuthoritativeAfterWrite(pending, result.rawBytes);
    logSnapshotPersistSuccess(pending, result.semanticFingerprint, {
      rawBytes: result.rawBytes,
      gzBytes: result.gzBytes,
      serializeMs: pending.serializeMs ?? result.serializeMs,
      writeMs: result.writeMs,
      offloaded: true
    });
    completeSnapshotPersist(pending);
  } catch (err) {
    fs3.rm(result.stagePath, { force: true }, () => {
    });
    dispatchMainThreadWriteThroughSeam(pending, err instanceof Error ? err.message : String(err));
  }
}
function dispatchSnapshotPersist(pending) {
  const baselines = snapshotPersistBaselinesFor(pending.cwd, pending.key);
  pending.durablePersist = baselines.durable;
  pending.dedupeFingerprints = baselines.fingerprints;
  seedSnapshotPersistBaselineFromDurable(pending.key, baselines.durable);
  _activeSnapshotPersists.set(pending.key, pending);
  if (!snapshotWorkerEnabled()) {
    dispatchMainThreadWriteThroughSeam(pending, void 0);
    return;
  }
  const worker = getSnapshotPersistWorker();
  if (!worker) {
    dispatchMainThreadWriteThroughSeam(pending, "persist worker unavailable");
    return;
  }
  let serialized;
  try {
    serialized = serializeSnapshotBody(pending.snapshot);
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    incrementDegradationCount({
      kind: "project-snapshot-serialize-failed",
      subject: pending.gzPath,
      reason: `snapshot body serialization failed: ${message}`
    });
    recordSnapshotPersistFailure(pending, message);
    completeSnapshotPersist(pending);
    return;
  }
  pending.serializeMs = serialized.serializeMs;
  pending.wordIndexWireBytes = serialized.wordIndexWireBytes;
  const id = ++_snapshotWorkerRequestId;
  _snapshotWorkerRequests.set(id, pending);
  const request = {
    id,
    generation: pending.generation,
    stagePath: pending.stagePath,
    data: serialized.bytes,
    generatedAt: pending.snapshot.generatedAt,
    priorFingerprints: pending.dedupeFingerprints,
    testDelayMs: process.env.NODE_ENV === "test" ? Number(process.env.PI_LENS_TEST_SNAPSHOT_PERSIST_WORKER_DELAY_MS) || void 0 : void 0
  };
  try {
    worker.postMessage(request, [serialized.bytes.buffer]);
  } catch (err) {
    _snapshotWorkerRequests.delete(id);
    dispatchMainThreadWriteThroughSeam(pending, err instanceof Error ? err.message : String(err));
  }
}
function handleSnapshotWorkerDeath(reason) {
  _snapshotPersistWorker = void 0;
  _snapshotPersistWorkerHeapStatistics = null;
  _snapshotWorkerDisabled = true;
  const requests = [..._snapshotWorkerRequests.values()];
  _snapshotWorkerRequests.clear();
  for (const pending of requests)
    dispatchMainThreadWriteThroughSeam(pending, reason);
}
function resolveSnapshotPersistWorkerPath() {
  const candidates = [
    new URL("./project-snapshot-persist-worker.js", import.meta.url),
    new URL("./workers/project-snapshot-persist-worker.js", import.meta.url),
    new URL("../workers/project-snapshot-persist-worker.js", import.meta.url)
  ];
  for (const url of candidates) {
    try {
      const resolved = fileURLToPath(url);
      if (fs3.existsSync(resolved))
        return resolved;
    } catch {
    }
  }
  return void 0;
}
function getSnapshotPersistWorker() {
  if (_snapshotWorkerDisabled)
    return void 0;
  if (_snapshotPersistWorker)
    return _snapshotPersistWorker;
  try {
    const workerPath = resolveSnapshotPersistWorkerPath();
    if (workerPath === void 0) {
      handleSnapshotWorkerDeath("persist worker script not found in any layout");
      return void 0;
    }
    const worker = new Worker(workerPath);
    worker.on("message", (result) => {
      if (_snapshotPromotionSeamForTests) {
        void _snapshotPromotionSeamForTests().then(() => handleSnapshotWorkerResult(result));
        return;
      }
      handleSnapshotWorkerResult(result);
    });
    worker.on("error", (err) => handleSnapshotWorkerDeath(err.message));
    worker.on("exit", (code) => {
      if (_snapshotPersistWorker === worker) {
        _snapshotPersistWorker = void 0;
        _snapshotPersistWorkerHeapStatistics = null;
      }
      const stranded = [..._snapshotWorkerRequests.values()];
      if (stranded.length > 0) {
        _snapshotWorkerRequests.clear();
        for (const pending of stranded) {
          dispatchMainThreadWriteThroughSeam(pending, `persist worker exited with code ${code}`);
        }
      }
      if (code !== 0)
        _snapshotWorkerDisabled = true;
    });
    worker.unref();
    _snapshotPersistWorker = worker;
    return worker;
  } catch (err) {
    handleSnapshotWorkerDeath(err instanceof Error ? err.message : String(err));
    return void 0;
  }
}
var _sweptSnapshotStageDirs = /* @__PURE__ */ new Set();
function sweepStaleSnapshotStageFiles(cacheDir) {
  if (_sweptSnapshotStageDirs.has(cacheDir))
    return;
  _sweptSnapshotStageDirs.add(cacheDir);
  fs3.readdir(cacheDir, (err, entries) => {
    if (err)
      return;
    for (const entry of entries) {
      if (!isStaleStageFile(entry, "project-snapshot.json.gz.stage-"))
        continue;
      fs3.rm(path2.join(cacheDir, entry), { force: true }, () => {
      });
    }
  });
}
function flushSnapshotPersistsAtExit() {
  _snapshotExiting = true;
  const latestByKey = /* @__PURE__ */ new Map();
  for (const pending of _snapshotWorkerRequests.values()) {
    latestByKey.set(pending.key, pending);
  }
  for (const pending of _queuedSnapshotPersists.values()) {
    const prior = latestByKey.get(pending.key);
    if (!prior || prior.generation <= pending.generation) {
      latestByKey.set(pending.key, pending);
    }
  }
  _snapshotWorkerRequests.clear();
  _queuedSnapshotPersists.clear();
  _activeSnapshotPersists.clear();
  for (const pending of latestByKey.values()) {
    writeSnapshotBodyOnMainThread(pending, "exit_hook");
  }
  void _snapshotPersistWorker?.terminate();
}
var _snapshotExitHookInstalled = false;
function ensureSnapshotPersistExitHook() {
  if (_snapshotExitHookInstalled)
    return;
  _snapshotExitHookInstalled = true;
  process.once("exit", flushSnapshotPersistsAtExit);
}
function runSnapshotPersistExitFlushForTests() {
  flushSnapshotPersistsAtExit();
}
function saveProjectSnapshot(cwd, snapshot) {
  const gzPath = getProjectSnapshotPath(cwd);
  const legacyPath = getProjectSnapshotLegacyPath(cwd);
  const metaPath = getProjectSnapshotMetaPath(cwd);
  const cacheDir = path2.dirname(gzPath);
  const key = normalizeMapKey(cwd);
  fs3.mkdirSync(cacheDir, { recursive: true });
  const baselines = snapshotPersistBaselinesFor(cwd, key);
  const priorPersist = baselines.durable;
  logSnapshotPersistDecision({
    cwd,
    seq: snapshot.seq,
    decision: "requested"
  });
  if (_failedSnapshotPersists.delete(key)) {
    logSnapshotPersistDecision({ cwd, seq: snapshot.seq, decision: "retry" });
  }
  withSnapshotCacheLock(gzPath, "admission meta write", () => {
    const onDisk = readProjectSnapshotMeta(cwd);
    if (!onDisk || onDisk.seq < snapshot.seq) {
      writeProjectSnapshotMeta(metaPath, snapshot);
    }
  });
  const priorBody = resolveSnapshotBodyPath(cwd);
  const knownMtime = priorBody ? priorBody.mtimeMs : Number.NEGATIVE_INFINITY;
  const knownSize = priorBody ? priorBody.size : Number.NEGATIVE_INFINITY;
  const authoritativeEntry = {
    snapshot,
    knownMtime,
    knownSize,
    lastUsedAt: Date.now()
  };
  const previousAuthoritativeEntry = authoritativeSnapshots.get(key);
  if (previousAuthoritativeEntry)
    clearAuthoritativeSnapshotTimer(previousAuthoritativeEntry);
  authoritativeSnapshots.set(key, authoritativeEntry);
  scheduleAuthoritativeSnapshotEviction(key, authoritativeEntry);
  enforceAuthoritativeSnapshotCap();
  snapshotParseCache.delete(gzPath);
  const generationState = _snapshotGenerationStates.get(key);
  const generation = generationState?.seq === snapshot.seq ? generationState.generation : (generationState?.generation ?? 0) + 1;
  _snapshotGenerationStates.set(key, { generation, seq: snapshot.seq });
  const stagePath = `${gzPath}.stage-${process.pid}-${generation}`;
  const pending = {
    key,
    cwd,
    gzPath,
    legacyPath,
    stagePath,
    snapshot,
    generation,
    durablePersist: priorPersist,
    dedupeFingerprints: baselines.fingerprints
  };
  sweepStaleSnapshotStageFiles(cacheDir);
  ensureSnapshotPersistExitHook();
  if (_activeSnapshotPersists.has(key)) {
    _queuedSnapshotPersists.set(key, pending);
    logSnapshotPersistDecision({
      cwd,
      seq: snapshot.seq,
      decision: "coalesced"
    });
    return;
  }
  dispatchSnapshotPersist(pending);
}
async function waitForProjectSnapshotPersistsForTests() {
  for (let attempts = 0; attempts < 200 && (_snapshotWorkerRequests.size > 0 || _activeSnapshotPersists.size > 0 || _queuedSnapshotPersists.size > 0); attempts++) {
    await new Promise((resolve2) => setTimeout(resolve2, 10));
  }
}
function flushProjectSnapshotPersistsForTests() {
  const requests = [..._snapshotWorkerRequests.values()];
  _snapshotWorkerRequests.clear();
  for (const pending of requests) {
    dispatchMainThreadWriteThroughSeam(pending, void 0);
  }
}
async function terminateProjectSnapshotPersistWorkerForTests() {
  const worker = _snapshotPersistWorker;
  if (worker)
    await worker.terminate();
}
function resetProjectSnapshotPersistWorkerForTests() {
  _snapshotWorkerDisabled = false;
  _snapshotGenerationGateEnabledForTests = true;
  _snapshotPromotionSeamForTests = void 0;
  _snapshotPersistWorker = void 0;
  _snapshotPersistWorkerHeapStatistics = null;
  _snapshotWorkerRequests.clear();
  _snapshotGenerationStates.clear();
  _successfulSnapshotPersists.clear();
  _failedSnapshotPersists.clear();
  _activeSnapshotPersists.clear();
  _queuedSnapshotPersists.clear();
  _snapshotExiting = false;
  _snapshotWorkerBodyWritesForTests = 0;
  _lastSnapshotPersistErrorForTests = void 0;
}
function getProjectSnapshotPersistStateForTests(cwd) {
  const key = normalizeMapKey(cwd);
  return {
    active: _activeSnapshotPersists.has(key),
    queued: _queuedSnapshotPersists.has(key),
    successfulFingerprint: _successfulSnapshotPersists.get(key)?.fingerprint,
    workerBodyWrites: _snapshotWorkerBodyWritesForTests
  };
}
function setProjectSnapshotGenerationGateForTests(enabled) {
  _snapshotGenerationGateEnabledForTests = enabled;
}
function setProjectSnapshotPromotionSeamForTests(seam) {
  _snapshotPromotionSeamForTests = seam;
}
function getProjectSnapshotPersistErrorForTests() {
  return _lastSnapshotPersistErrorForTests;
}
function _seedSnapshotParseCacheForTests(key, entry) {
  cacheParsedSnapshot(key, entry);
}
function _snapshotParseCacheKeysForTests() {
  return [...snapshotParseCache.keys()];
}
function _snapshotParseCacheGetForTests(key) {
  return snapshotParseCache.get(key) !== void 0;
}
function _seedSuccessfulSnapshotPersistForTests(key, record) {
  rememberSuccessfulSnapshotPersist(key, record);
}
function _successfulSnapshotPersistKeysForTests() {
  return [..._successfulSnapshotPersists.keys()];
}
function _successfulSnapshotPersistGetForTests(key) {
  return _successfulSnapshotPersists.get(key) !== void 0;
}
function _seedFailedSnapshotPersistForTests(key, record) {
  _failedSnapshotPersists.set(key, record);
}
function _failedSnapshotPersistKeysForTests() {
  return [..._failedSnapshotPersists.keys()];
}
function buildProjectSnapshotFromRuntime(args) {
  const incomplete = args.runtime.viewMissesLoggedEntries;
  return {
    version: PROJECT_SNAPSHOT_VERSION,
    projectRoot: normalizeMapKey(path2.resolve(args.cwd)),
    generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
    seq: args.runtime.projectSeq,
    ...incomplete ? { incomplete: true } : {},
    logEntries: args.runtime.viewLogEntries,
    files: {},
    symbols: {},
    reverseDeps: {},
    cachedExports: [...args.runtime.cachedExports.entries()].sort((a, b) => a[0].localeCompare(b[0])),
    // #1019: capture the runtime's live sequence index AT this seq. The runtime
    // is seeded from the change log at session start and bumped in lockstep with
    // every append, so `getFileSeqEntries()` IS the fold of the log up to
    // `projectSeq` — and its keys are already `normalizeMapKey(path.resolve())`,
    // the exact form the change-log replay produces.
    sequenceIndex: incomplete ? void 0 : {
      projectSeq: args.runtime.projectSeq,
      fileSeqByPath: args.runtime.getFileSeqEntries()
    },
    wordIndex: args.runtime.wordIndex ? serializeWordIndex(args.runtime.wordIndex) : void 0,
    projectRulesScan: args.runtime.projectRulesScan,
    startupScan: args.startupScan,
    languageProfile: args.languageProfile,
    conventions: args.conventions
  };
}
function hydrateRuntimeFromProjectSnapshot(runtime, snapshot) {
  runtime.cachedExports.clear();
  for (const [name, filePath] of snapshot.cachedExports) {
    runtime.cachedExports.set(name, filePath);
  }
  if (snapshot.projectRulesScan) {
    runtime.projectRulesScan = snapshot.projectRulesScan;
  }
  runtime.wordIndex = deserializeWordIndex(snapshot.wordIndex);
}
function hydrateRuntimeFromProjectSnapshotIfIdle(runtime, snapshot) {
  let hydratedAnything = false;
  if (runtime.cachedExports.size === 0 && snapshot.cachedExports.length > 0) {
    runtime.cachedExports.clear();
    for (const [name, filePath] of snapshot.cachedExports) {
      runtime.cachedExports.set(name, filePath);
    }
    hydratedAnything = true;
  }
  if (!runtime.projectRulesScan.hasCustomRules && runtime.projectRulesScan.rules.length === 0 && snapshot.projectRulesScan) {
    runtime.projectRulesScan = snapshot.projectRulesScan;
    hydratedAnything = true;
  }
  return hydratedAnything;
}
function saveRuntimeProjectSnapshot(args) {
  try {
    if (typeof args.runtime.projectSeq !== "number")
      return;
    const existing = loadProjectSnapshot(args.cwd);
    let conventions = args.conventions ?? existing?.conventions;
    if (!conventions) {
      try {
        conventions = detectProjectConventions(args.cwd);
      } catch (err) {
        args.dbg?.(`project_snapshot: convention detection failed: ${err}`);
      }
    }
    const snapshot = buildProjectSnapshotFromRuntime({
      ...args,
      startupScan: args.startupScan ?? existing?.startupScan,
      languageProfile: args.languageProfile ?? existing?.languageProfile,
      conventions
    });
    if (existing) {
      snapshot.files = existing.files ?? {};
      snapshot.symbols = existing.symbols ?? {};
      snapshot.reverseDeps = existing.reverseDeps ?? {};
      if (!snapshot.wordIndex && existing.wordIndex && existing.seq === snapshot.seq && (snapshot.incomplete || !existing.incomplete)) {
        snapshot.wordIndex = existing.wordIndex;
      }
    }
    saveProjectSnapshot(args.cwd, snapshot);
    args.dbg?.(`project_snapshot: saved seq=${snapshot.seq} exports=${snapshot.cachedExports.length}`);
  } catch (err) {
    args.dbg?.(`project_snapshot: save failed: ${err}`);
  }
}

export {
  readJsonCache,
  readJsonCacheAsync,
  readWorkerHeapStatistics,
  PROJECT_SNAPSHOT_VERSION,
  getProjectSnapshotPath,
  getProjectSnapshotLegacyPath,
  getProjectSnapshotMetaPath,
  isProjectSnapshotFresh,
  readProjectSnapshotMeta,
  isProjectSnapshotMetaStale,
  SNAPSHOT_PARSE_CACHE_MAX,
  PROJECT_SNAPSHOT_MAX_WARM_ROOTS,
  _getAuthoritativeSnapshotCacheKeysForTests,
  _resetProjectSnapshotParseCacheForTests,
  _projectSnapshotParseCacheRetainsWordIndexForTests,
  getSnapshotBodyReadCountForTests,
  resetSnapshotBodyReadCountForTests,
  _stripTopLevelJsonKeysForTests,
  getLastNarrowParseDigestForTests,
  resetLastNarrowParseDigestForTests,
  loadProjectSnapshotExportsAndRules,
  loadProjectSnapshot,
  loadProjectSnapshotWithoutWordIndex,
  refreshProjectSnapshotPersistWorkerHeapStatistics,
  getProjectSnapshotPersistWorkerHeapStatistics,
  runSnapshotPersistExitFlushForTests,
  saveProjectSnapshot,
  waitForProjectSnapshotPersistsForTests,
  flushProjectSnapshotPersistsForTests,
  terminateProjectSnapshotPersistWorkerForTests,
  resetProjectSnapshotPersistWorkerForTests,
  getProjectSnapshotPersistStateForTests,
  setProjectSnapshotGenerationGateForTests,
  setProjectSnapshotPromotionSeamForTests,
  getProjectSnapshotPersistErrorForTests,
  _seedSnapshotParseCacheForTests,
  _snapshotParseCacheKeysForTests,
  _snapshotParseCacheGetForTests,
  _seedSuccessfulSnapshotPersistForTests,
  _successfulSnapshotPersistKeysForTests,
  _successfulSnapshotPersistGetForTests,
  _seedFailedSnapshotPersistForTests,
  _failedSnapshotPersistKeysForTests,
  buildProjectSnapshotFromRuntime,
  hydrateRuntimeFromProjectSnapshot,
  hydrateRuntimeFromProjectSnapshotIfIdle,
  saveRuntimeProjectSnapshot
};