{"version":3,"sources":["../src/index.ts","../src/parsers/index.ts","../src/types.ts","../src/parsers/consistency.ts","../src/parsers/bun.ts","../src/parsers/report.ts","../src/parsers/npm.ts","../src/parsers/node_modules.ts","../src/parsers/pnpm.ts","../src/parsers/yarn.ts","../src/normalize.ts","../src/pulse-token.ts","../src/client.ts","../src/config.ts","../src/stack.ts","../src/widget.ts"],"sourcesContent":["import { scanLockfile } from './parsers/index.js';\nimport { buildWirePayload } from './normalize.js';\nimport { postManifest } from './client.js';\nimport { persistApiKey, persistSiteUuid, resolveConfig } from './config.js';\nimport type { Config, Manifest, StoreManifestResponse } from './types.js';\n\nexport { scanLockfile, detectLockfile } from './parsers/index.js';\nexport { buildWirePayload, compareVersions } from './normalize.js';\nexport { postManifest, buildClaimUrl, buildEndpointUrl, DEFAULT_ENDPOINT } from './client.js';\nexport { persistApiKey, persistPulseAuth, persistSiteUuid, resolveConfig, writeConfigFile } from './config.js';\nexport {\n  detectStack,\n  collectHostingEnvKeys,\n  isEmptyStack,\n  type StackDescriptor,\n} from './stack.js';\nexport {\n  WIDGET_SCRIPT_URL,\n  WIDGET_MARKER_ATTR,\n  buildWidgetTag,\n  ensureWidgetInHtml,\n  ensureSourceWidget,\n  findSourceShell,\n  type WidgetEnsureAction,\n  type WidgetEnsureResult,\n  type SourceWidgetResult,\n} from './widget.js';\nexport {\n  PatchstackError,\n  type Config,\n  type Ecosystem,\n  type Manifest,\n  type PackageEntry,\n  type StoreManifestResponse,\n} from './types.js';\n\nexport interface ScanAndReportOptions {\n  cwd?: string;\n  config?: Config;\n}\n\nexport interface ScanAndReportResult {\n  manifest: Manifest;\n  response: StoreManifestResponse;\n  duplicateNames: string[];\n  uniqueNames: number;\n  totalEntries: number;\n}\n\nexport async function scanAndReport(\n  options: ScanAndReportOptions = {},\n): Promise<ScanAndReportResult> {\n  const cwd = options.cwd ?? process.cwd();\n  const config = options.config ?? (await resolveConfig({ cwd }));\n  const manifest = await scanLockfile(cwd);\n  const { payload, stats } = buildWirePayload(manifest);\n  const response = await postManifest(config, payload);\n\n  // First-run convenience: if we didn't have a UUID and the server provisioned\n  // one for us, persist it so subsequent runs target the same site.\n  if (config.siteUuid === null && response.uuid !== undefined && response.uuid.length > 0) {\n    await persistSiteUuid(cwd, response.uuid);\n  }\n  if (typeof response.api_key === 'string' && response.api_key.length > 0) {\n    await persistApiKey(cwd, response.api_key);\n  }\n\n  return {\n    manifest,\n    response,\n    duplicateNames: stats.duplicateNames,\n    uniqueNames: stats.uniqueNames,\n    totalEntries: stats.totalEntries,\n  };\n}\n","import { access } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type Manifest, type PackageEntry } from '../types.js';\nimport { disagreements, missingDependencies, readDeclaredDependencyNames } from './consistency.js';\nimport { parseBunLockfile } from './bun.js';\nimport { parseNpmLockfile } from './npm.js';\nimport { walkNodeModules } from './node_modules.js';\nimport { parsePnpmLockfile } from './pnpm.js';\nimport { parseYarnLockfile } from './yarn.js';\nimport { newReport, unreadableWarning, type ParseReport } from './report.js';\n\ntype LockfileFilename =\n  | 'package-lock.json'\n  | 'bun.lock'\n  | 'bun.lockb'\n  | 'yarn.lock'\n  | 'pnpm-lock.yaml';\n\ntype DetectionStrategy =\n  | 'npm-lockfile'\n  | 'bun-lockfile'\n  | 'node-modules-walk'\n  | 'pnpm-lockfile'\n  | 'yarn-lockfile';\n\ninterface DetectedLockfile {\n  ecosystem: 'npm';\n  filePath: string;\n  filename: LockfileFilename;\n  strategy: DetectionStrategy;\n}\n\ninterface LockfileCandidate {\n  filename: LockfileFilename;\n  strategy: DetectionStrategy;\n}\n\n// Checked in priority order: the first candidate present in `cwd` wins.\nconst LOCKFILE_CANDIDATES: readonly LockfileCandidate[] = [\n  { filename: 'package-lock.json', strategy: 'npm-lockfile' },\n  // Read directly, not walked. Bun's isolated layout puts packages under a dot-directory and makes the\n  // top level symlinks into it, so a `node_modules` walk finds nothing there — and isolated is the default\n  // for new workspaces.\n  { filename: 'bun.lock', strategy: 'bun-lockfile' },\n  { filename: 'bun.lockb', strategy: 'node-modules-walk' },\n  { filename: 'pnpm-lock.yaml', strategy: 'pnpm-lockfile' },\n  { filename: 'yarn.lock', strategy: 'yarn-lockfile' },\n];\n\nexport async function detectLockfile(cwd: string): Promise<DetectedLockfile> {\n  // Probe every candidate concurrently, then resolve by declaration order so\n  // the result is identical to a sequential first-match scan — but bounded by a\n  // single I/O round-trip instead of up to one `stat` per candidate.\n  const probed = await Promise.all(\n    LOCKFILE_CANDIDATES.map(async (candidate) => {\n      const filePath = path.join(cwd, candidate.filename);\n      return { ...candidate, filePath, present: await exists(filePath) };\n    }),\n  );\n\n  const match = probed.find((candidate) => candidate.present);\n  if (match) {\n    return {\n      ecosystem: 'npm',\n      filePath: match.filePath,\n      filename: match.filename,\n      strategy: match.strategy,\n    };\n  }\n\n  const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(', ');\n  throw new PatchstackError(\n    `No lockfile found in ${cwd}. Expected one of: ${expected}.`,\n    'LOCKFILE_NOT_FOUND',\n  );\n}\n\nexport async function scanLockfile(cwd: string): Promise<Manifest> {\n  const candidates = await presentLockfiles(cwd);\n  if (candidates.length === 0) {\n    // Preserve the exact historical error for the no-lockfile case.\n    await detectLockfile(cwd);\n  }\n\n  // Validate each source against package.json before trusting it. A lockfile\n  // missing declared dependencies is a fossil — e.g. `npm install` planted a\n  // package-lock.json once on a bun-managed platform (Lovable), and the native\n  // dependency flow never updates it again. Trusting it would freeze the\n  // manifest and the build fingerprint while the real dependency set drifts.\n  const declared = await readDeclaredDependencyNames(cwd);\n  const warnings: string[] = [];\n  let firstParsed: { packages: PackageEntry[]; filename: string } | null = null;\n  // The installed tree, if some lockfile already routed us to it (a `bun.lockb` cannot be read, so it\n  // does). Kept rather than just remembered as \"tried\": on a version conflict the installed tree is what\n  // decides, and a flag meant that decision fell back to the lockfile it had just been overruled by.\n  let walked: PackageEntry[] | null = null;\n  let walkWasCandidate = false;\n\n  // Every source is parsed before one is chosen, so a disagreement between two of them can be seen at all.\n  // Taking the first source that merely lists the right NAMES is what let a stale lockfile decide the\n  // version: a fossil that still names every dependency looks complete, and the version is what decides\n  // whether a package is vulnerable.\n  const parsedSources: Array<{ packages: PackageEntry[]; filename: string }> = [];\n\n  for (const candidate of candidates) {\n    let packages: PackageEntry[];\n    const report = newReport();\n    try {\n      packages = await runStrategy(candidate, cwd, report);\n    } catch {\n      continue;\n    }\n    const unreadable = unreadableWarning(candidate.filename, report);\n    if (unreadable !== null) warnings.push(unreadable);\n    if (candidate.strategy === 'node-modules-walk') {\n      walked = packages;\n      walkWasCandidate = true;\n    }\n    firstParsed ??= { packages, filename: candidate.filename };\n    parsedSources.push({ packages, filename: candidate.filename });\n  }\n\n  for (const source of parsedSources) {\n    const missing = missingDependencies(declared, source.packages);\n    if (missing.length > 0) {\n      warnings.push(staleWarning(source.filename, missing));\n      continue;\n    }\n\n    // Names complete. Now: does any other source that is also name-complete report a different VERSION for\n    // a shared package? If so, one of the two is stale in a way no name check can see, and neither can be\n    // preferred on the evidence available here — so the installed tree decides, and the disagreement is\n    // reported either way.\n    const conflicts = otherCompleteSources(parsedSources, source, declared).flatMap((other) =>\n      disagreements(source.packages, other.packages).map((conflict) => ({ other: other.filename, conflict })),\n    );\n\n    if (conflicts.length === 0) {\n      return manifestWith(source.packages, warnings);\n    }\n\n    warnings.push(conflictWarning(source.filename, conflicts));\n    break; // fall through to node_modules, which is what the build actually loads\n  }\n\n  // Last resort: the installed truth. node_modules reflects what the build\n  // actually compiles against, whichever package manager wrote it.\n  if (walked === null) {\n    try {\n      walked = await walkNodeModules(cwd);\n    } catch {\n      walked = null; // no installed tree either — fall through to the best lockfile we parsed\n    }\n  }\n\n  if (walked !== null) {\n    const missing = missingDependencies(declared, walked);\n    if (missing.length > 0) {\n      warnings.push(staleWarning('node_modules/', missing));\n    }\n    warnings.push(\n      walkWasCandidate\n        ? 'Reporting node_modules/, which is what the build loads.'\n        : 'Scanned node_modules/ instead. Delete the stale lockfile to silence this warning.',\n    );\n\n    return manifestWith(walked, warnings);\n  }\n\n  if (firstParsed === null) {\n    const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(', ');\n    throw new PatchstackError(\n      `No readable lockfile found in ${cwd}. Expected one of: ${expected}.`,\n      'LOCKFILE_NOT_FOUND',\n    );\n  }\n\n  warnings.push(\n    `No fully-consistent source found; reporting ${firstParsed.filename}. The manifest may understate the real dependency set.`,\n  );\n\n  return manifestWith(firstParsed.packages, warnings);\n}\n\nfunction manifestWith(packages: PackageEntry[], warnings: string[]): Manifest {\n  return warnings.length > 0\n    ? { ecosystem: 'npm', packages, warnings }\n    : { ecosystem: 'npm', packages };\n}\n\n/** The other sources that are themselves name-complete — the only ones whose versions are worth comparing. */\nfunction otherCompleteSources(\n  all: Array<{ packages: PackageEntry[]; filename: string }>,\n  current: { filename: string },\n  declared: string[],\n): Array<{ packages: PackageEntry[]; filename: string }> {\n  return all.filter(\n    (source) => source.filename !== current.filename && missingDependencies(declared, source.packages).length === 0,\n  );\n}\n\nfunction conflictWarning(\n  filename: string,\n  conflicts: Array<{ other: string; conflict: { name: string; version: string; otherVersion: string } }>,\n): string {\n  const shown = conflicts\n    .slice(0, 3)\n    .map(({ other, conflict }) => `${conflict.name} ${conflict.version} vs ${conflict.otherVersion} in ${other}`)\n    .join('; ');\n\n  return (\n    `${filename} and another lockfile disagree about installed versions (${shown}` +\n    `${conflicts.length > 3 ? `, +${conflicts.length - 3} more` : ''}). ` +\n    'Scanned node_modules/ instead. Remove whichever lockfile your package manager does not maintain.'\n  );\n}\n\nfunction staleWarning(source: string, missing: string[]): string {\n  const sample = missing.slice(0, 3).join(', ');\n  const suffix = missing.length > 3 ? `, +${missing.length - 3} more` : '';\n  return `${source} looks stale: package.json declares ${missing.length} dependenc${missing.length === 1 ? 'y' : 'ies'} it does not contain (${sample}${suffix}).`;\n}\n\nasync function presentLockfiles(cwd: string): Promise<DetectedLockfile[]> {\n  const probed = await Promise.all(\n    LOCKFILE_CANDIDATES.map(async (candidate) => {\n      const filePath = path.join(cwd, candidate.filename);\n      return { ...candidate, filePath, present: await exists(filePath) };\n    }),\n  );\n\n  return probed\n    .filter((candidate) => candidate.present)\n    .map(({ filename, strategy, filePath }) => ({ ecosystem: 'npm' as const, filename, strategy, filePath }));\n}\n\nasync function runStrategy(\n  detected: DetectedLockfile,\n  cwd: string,\n  report: ParseReport,\n): Promise<PackageEntry[]> {\n  switch (detected.strategy) {\n    case 'npm-lockfile':\n      return parseNpmLockfile(detected.filePath);\n    case 'bun-lockfile':\n      return parseBunLockfile(detected.filePath, report);\n    // The hand-written scanners. Each meets entries it was not written for, and each is asked to say how\n    // many it dropped, because a manifest short a package reads exactly like a project without it.\n    case 'pnpm-lockfile':\n      return parsePnpmLockfile(detected.filePath, report);\n    case 'yarn-lockfile':\n      return parseYarnLockfile(detected.filePath, report);\n    case 'node-modules-walk':\n      return walkNodeModules(cwd);\n  }\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n  try {\n    await access(filePath);\n    return true;\n  } catch {\n    return false;\n  }\n}\n","export type Ecosystem = 'npm' | 'composer';\n\n/**\n * Which environment a manifest was captured in. The connector reports from real\n * builds (prebuild scan / build hooks), so it defaults to 'production'. Override\n * with PATCHSTACK_ENVIRONMENT=sandbox (or \"environment\" in .patchstackrc.json)\n * for test manifests.\n */\nexport type Environment = 'production' | 'sandbox';\n\nexport interface PackageEntry {\n  name: string;\n  version: string;\n  path?: string;\n  direct?: boolean;\n}\n\nexport interface Manifest {\n  ecosystem: Ecosystem;\n  packages: PackageEntry[];\n  /**\n   * Human-readable scan diagnostics — set when the preferred lockfile looked\n   * stale (missing dependencies declared in package.json) and another source\n   * was used, or when no fully-consistent source existed. Never fatal.\n   */\n  warnings?: string[];\n}\n\nexport interface Config {\n  /**\n   * The site UUID. `null` means we don't have one yet — `postManifest` will then\n   * post to the bare endpoint, the server will provision a fresh site, and the\n   * UUID it returns should be persisted via `persistSiteUuid()`.\n   */\n  siteUuid: string | null;\n  /**\n   * WP-format site API key (`{oauth.secret}-{oauth.id}`) for authenticated\n   * block-log reporting via connector `/api/logs/log`. Issued once as `api_key`\n   * on first provision. Prefer `PATCHSTACK_API_KEY` in production deploys.\n   */\n  apiKey: string | null;\n  /**\n   * Credential for the authenticated Pulse endpoints (ADR-0018). Exchanged for\n   * a short-lived bearer token at `monitor/pulse/token`; never sent directly.\n   * Falls back to `apiKey` when unset. Prefer `PATCHSTACK_PULSE_AUTH`.\n   */\n  pulseAuth: string | null;\n  endpoint: string;\n  timeoutMs: number;\n  /** Environment to report the manifest under. Defaults to 'production'. */\n  environment: Environment;\n  /**\n   * Whether the connector manages the disclosure-widget tag (source shell on\n   * `scan`, built HTML on `mark-build`). Defaults to true; persist\n   * `\"widget\": false` in .patchstackrc.json for dependency-scanning only.\n   */\n  widget: boolean;\n}\n\nexport interface StoreManifestResponse {\n  /** The UUID of the site the manifest was stored against. Always returned. */\n  uuid?: string;\n  /**\n   * WP-format API key for connector log ingest. Present only when oauth\n   * credentials are created in this request (first provision / backfill).\n   */\n  api_key?: string;\n  stored: boolean;\n  manifest_id?: number;\n  checksum?: string;\n  reason?: string;\n  message?: string;\n  error?: string;\n}\n\nexport class PatchstackError extends Error {\n  constructor(\n    message: string,\n    public readonly code:\n      | 'CONFIG_MISSING'\n      | 'CONFIG_INVALID'\n      | 'LOCKFILE_NOT_FOUND'\n      | 'LOCKFILE_UNSUPPORTED'\n      | 'LOCKFILE_PARSE_ERROR'\n      | 'NETWORK_ERROR'\n      | 'NETWORK_TIMEOUT'\n      | 'SITE_NOT_FOUND'\n      | 'UNAUTHORIZED'\n      | 'VALIDATION_ERROR'\n      | 'SERVER_ERROR',\n    public readonly cause?: unknown,\n  ) {\n    super(message);\n    this.name = 'PatchstackError';\n  }\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { PackageEntry } from '../types.js';\n\n/**\n * Registry-installable dependency names declared in package.json\n * (dependencies + devDependencies). Non-registry specifiers (file:, link:,\n * workspace:, portal:) are excluded — lockfile parsers skip those too, so\n * their absence from a parsed set says nothing about staleness. Returns []\n * when package.json is missing or unreadable (nothing to validate against).\n */\nexport async function readDeclaredDependencyNames(cwd: string): Promise<string[]> {\n  let raw: string;\n  try {\n    raw = await readFile(path.join(cwd, 'package.json'), 'utf8');\n  } catch {\n    return [];\n  }\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    return [];\n  }\n  if (typeof parsed !== 'object' || parsed === null) {\n    return [];\n  }\n\n  const names = new Set<string>();\n  // `optionalDependencies` included: they are installed when the platform supports them, they can be\n  // vulnerable, and leaving them out meant a lockfile that omitted one still read as complete.\n  for (const group of ['dependencies', 'devDependencies', 'optionalDependencies'] as const) {\n    const deps = (parsed as Record<string, unknown>)[group];\n    if (typeof deps !== 'object' || deps === null) {\n      continue;\n    }\n    for (const [name, spec] of Object.entries(deps as Record<string, unknown>)) {\n      if (typeof spec === 'string' && /^(file|link|workspace|portal):/.test(spec)) {\n        continue;\n      }\n      names.add(name);\n    }\n  }\n\n  return [...names];\n}\n\n/**\n * Declared names absent from a parsed package set. A non-empty result means\n * the source predates package.json — the fossil-lockfile failure mode where\n * e.g. `npm install` planted a package-lock.json once and the platform's\n * native package manager (bun on Lovable) never updates it again.\n */\nexport function missingDependencies(declared: string[], packages: PackageEntry[]): string[] {\n  const present = new Set(packages.map((entry) => entry.name));\n  return declared.filter((name) => !present.has(name));\n}\n\n/**\n * Packages two sources both list at DIFFERENT versions.\n *\n * A name check cannot see this, and the version is what decides whether a package is vulnerable: a stale\n * lockfile naming every dependency looks complete while reporting the versions of an older install. It\n * fails in both directions — an old version reported for a patched install raises a finding that is not\n * real, and a new version reported for an old install hides one that is.\n *\n * Only direct-name collisions are compared. A package present in one source and absent from the other is\n * not a disagreement about a version; it is a difference in what got installed, which the staleness check\n * above is for.\n */\nexport function disagreements(\n  packages: PackageEntry[],\n  other: PackageEntry[],\n): Array<{ name: string; version: string; otherVersion: string }> {\n  const otherVersions = new Map<string, string>();\n  for (const entry of other) {\n    // First occurrence wins, mirroring how a lockfile's own top-level entry precedes nested copies. A\n    // package installed at two versions in one tree is not a disagreement BETWEEN sources.\n    if (!otherVersions.has(entry.name)) otherVersions.set(entry.name, entry.version);\n  }\n\n  const seen = new Set<string>();\n  const conflicts: Array<{ name: string; version: string; otherVersion: string }> = [];\n\n  for (const entry of packages) {\n    if (seen.has(entry.name)) continue;\n    seen.add(entry.name);\n\n    const otherVersion = otherVersions.get(entry.name);\n    if (otherVersion !== undefined && otherVersion !== entry.version) {\n      conflicts.push({ name: entry.name, version: entry.version, otherVersion });\n    }\n  }\n\n  return conflicts;\n}\n","import { readFile } from 'node:fs/promises';\nimport { PatchstackError, type PackageEntry } from '../types.js';\nimport { recordUnreadable, type ParseReport } from './report.js';\n\n/**\n * Parses `bun.lock` — Bun's text lockfile — without a JSON5 dependency.\n *\n * Reading the lockfile matters more here than for the other package managers, because Bun's isolated\n * layout defeats a `node_modules` walk entirely: packages live under a dot-directory and the top level is\n * symlinks into it, so a walker that skips dotfiles and symlinks (both correct on their own — one avoids\n * caches, the other avoids symlink cycles) finds nothing at all and reports a project with no packages.\n * Isolated is the default for new workspaces, so that is not an edge case.\n *\n * The file is JSON with trailing commas, which `JSON.parse` rejects. Rather than take a parser dependency\n * for one file, the commas are stripped with a string-aware pass; anything else in the file is ordinary\n * JSON.\n *\n * ## Shape\n *\n * ```\n * \"packages\": {\n *   \"lodash\": [\"lodash@4.17.21\", \"\", {}, \"sha512-…\"],\n *   \"@scope/pkg\": [\"@scope/pkg@1.2.3\", \"\", {}, \"sha512-…\"],\n * }\n * ```\n *\n * The first element is the resolved descriptor. Entries whose descriptor is not a registry version —\n * `workspace:`, `file:`, a git URL — are skipped: they are not registry packages, and inventing a version\n * for them would put something in a vulnerability inventory that no advisory can ever match.\n */\nexport async function parseBunLockfile(lockfilePath: string, report?: ParseReport): Promise<PackageEntry[]> {\n  let raw: string;\n  try {\n    raw = await readFile(lockfilePath, 'utf8');\n  } catch (cause) {\n    throw new PatchstackError(`Could not read lockfile at ${lockfilePath}`, 'LOCKFILE_NOT_FOUND', cause);\n  }\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(stripTrailingCommas(raw));\n  } catch (cause) {\n    throw new PatchstackError(`Lockfile at ${lockfilePath} is not valid JSON`, 'LOCKFILE_PARSE_ERROR', cause);\n  }\n\n  const packages = (parsed as { packages?: unknown } | null)?.packages;\n  if (typeof packages !== 'object' || packages === null) {\n    throw new PatchstackError(`Lockfile at ${lockfilePath} has no \"packages\" entries`, 'LOCKFILE_PARSE_ERROR');\n  }\n\n  const direct = directDependencyNames(parsed);\n  const entries: PackageEntry[] = [];\n  let skipped = 0;\n\n  for (const [key, value] of Object.entries(packages as Record<string, unknown>)) {\n    const descriptor = Array.isArray(value) ? value[0] : value;\n    if (typeof descriptor !== 'string') {\n      skipped++;\n      // Not a shape this scanner reads. Counted out loud, because a manifest short a package reads exactly\n      // like a project that does not have it.\n      recordUnreadable(report, key);\n      continue;\n    }\n\n    const split = splitDescriptor(descriptor);\n    if (split === null) {\n      skipped++;\n      // Only the ones this scanner did not UNDERSTAND. A workspace, a `file:` entry or a git URL resolved\n      // to no version on purpose, and reporting those would put a warning on every monorepo.\n      if (!isDeliberatelyExcluded(descriptor)) recordUnreadable(report, key);\n      continue;\n    }\n\n    // A workspace key can be a path rather than a package name (`\"packages/api\"`), so the name comes from\n    // the descriptor. The key is what the file is indexed by, not what the package is called.\n    entries.push({\n      name: split.name,\n      version: split.version,\n      ...(direct.has(split.name) ? { direct: true } : {}),\n      ...(key === split.name ? {} : { path: key }),\n    });\n  }\n\n  if (entries.length === 0) {\n    throw new PatchstackError(\n      `Lockfile at ${lockfilePath} lists no registry packages${skipped > 0 ? ` (${skipped} non-registry entr${skipped === 1 ? 'y' : 'ies'} skipped)` : ''}`,\n      'LOCKFILE_PARSE_ERROR',\n    );\n  }\n\n  return entries;\n}\n\n/**\n * `name@version` split on the LAST `@`, so a scoped name keeps its leading one.\n *\n * Returns null when the version is not a registry version. `workspace:`, `file:`, `link:` and git or URL\n * descriptors all resolve to something that is installed but is not a published release, and a\n * vulnerability inventory that carried them would be naming things no advisory can match.\n */\nfunction splitDescriptor(descriptor: string): { name: string; version: string } | null {\n  const at = descriptor.lastIndexOf('@');\n  if (at <= 0) return null;\n\n  const name = descriptor.slice(0, at);\n  const version = descriptor.slice(at + 1);\n  if (name === '' || version === '') return null;\n  if (NON_REGISTRY_VERSION.test(version)) return null;\n\n  return { name, version };\n}\n\n/** A protocol instead of a version, or a URL or path — installed, but not a published release. */\nconst NON_REGISTRY_VERSION = /^[a-z][a-z0-9+.-]*:|\\//i;\n\n/**\n * Was this descriptor left out on purpose?\n *\n * A workspace, a local path, a git URL: all resolve to something installed that no advisory can be about,\n * so excluding it is correct and silent. Anything else that failed to split is a shape this scanner does\n * not read, which is a package that may be installed and missing from the inventory.\n */\nfunction isDeliberatelyExcluded(descriptor: string): boolean {\n  const at = descriptor.lastIndexOf('@');\n  if (at <= 0) return false;\n\n  return NON_REGISTRY_VERSION.test(descriptor.slice(at + 1));\n}\n\n/**\n * Names declared as direct dependencies by any workspace in the file.\n *\n * Used only to mark entries; a name that cannot be resolved here just goes unmarked.\n */\nfunction directDependencyNames(parsed: unknown): Set<string> {\n  const names = new Set<string>();\n  const workspaces = (parsed as { workspaces?: unknown } | null)?.workspaces;\n  if (typeof workspaces !== 'object' || workspaces === null) return names;\n\n  for (const workspace of Object.values(workspaces as Record<string, unknown>)) {\n    if (typeof workspace !== 'object' || workspace === null) continue;\n    for (const group of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {\n      const deps = (workspace as Record<string, unknown>)[group];\n      if (typeof deps !== 'object' || deps === null) continue;\n      for (const name of Object.keys(deps as Record<string, unknown>)) names.add(name);\n    }\n  }\n\n  return names;\n}\n\n/**\n * Remove trailing commas before `}` or `]`, leaving anything inside a string alone.\n *\n * The only difference between this file and JSON. Done by hand rather than with a regex because a regex\n * cannot tell a comma in a string from a comma in the structure, and integrity hashes are strings full of\n * characters that look structural.\n */\nfunction stripTrailingCommas(source: string): string {\n  const out: string[] = [];\n  let inString = false;\n  let pendingComma = -1; // index in `out` of a comma not yet known to be trailing\n\n  for (let i = 0; i < source.length; i++) {\n    const ch = source[i] as string;\n\n    if (inString) {\n      out.push(ch);\n      if (ch === '\\\\') {\n        const next = source[i + 1];\n        if (next !== undefined) {\n          out.push(next);\n          i++;\n        }\n      } else if (ch === '\"') {\n        inString = false;\n      }\n      continue;\n    }\n\n    if (ch === '\"') {\n      inString = true;\n      pendingComma = -1;\n      out.push(ch);\n      continue;\n    }\n\n    if (ch === ',') {\n      pendingComma = out.length;\n      out.push(ch);\n      continue;\n    }\n\n    if (ch === '}' || ch === ']') {\n      if (pendingComma !== -1) out[pendingComma] = ' ';\n      pendingComma = -1;\n      out.push(ch);\n      continue;\n    }\n\n    // Whitespace between a comma and the closing bracket keeps the comma pending; anything else settles it.\n    if (!/\\s/.test(ch)) pendingComma = -1;\n    out.push(ch);\n  }\n\n  return out.join('');\n}\n","/**\n * What a hand-written lockfile scanner could not read.\n *\n * These scanners walk their formats by hand rather than take a YAML dependency, so they meet entries they\n * were not written for: a protocol nobody has seen here, a block shaped differently by a newer release of\n * the package manager. Skipping such an entry is the right thing to do with it — inventing a name and a\n * version would put something in a vulnerability inventory that no advisory can match. Skipping it in\n * SILENCE is not: the result then reads as the project's whole dependency set, and a package that was\n * dropped is indistinguishable from a package that is not installed.\n *\n * Filled in by the parser when the caller passes one, and reported alongside the manifest.\n */\nexport interface ParseReport {\n  /** Entries seen and not turned into a package. */\n  unreadable: number;\n  /** A few of them, verbatim, so the reader can tell what the scanner does not handle. */\n  samples: string[];\n}\n\nconst MAX_SAMPLES = 3;\n\nexport function newReport(): ParseReport {\n  return { unreadable: 0, samples: [] };\n}\n\nexport function recordUnreadable(report: ParseReport | undefined, sample: string): void {\n  if (!report) return;\n  report.unreadable++;\n  if (report.samples.length < MAX_SAMPLES) report.samples.push(sample);\n}\n\n/** The warning for a source that dropped entries, or null when it read everything. */\nexport function unreadableWarning(filename: string, report: ParseReport): string | null {\n  if (report.unreadable === 0) return null;\n\n  const shown = report.samples.join(', ');\n\n  const one = report.unreadable === 1;\n\n  return (\n    `${filename}: ${report.unreadable} entr${one ? 'y was' : 'ies were'} not in a form this scanner reads ` +\n    `(${shown}${report.unreadable > report.samples.length ? ', …' : ''}), so ${one ? 'it is' : 'they are'} absent from ` +\n    `this manifest. Anything installed only through ${one ? 'that entry' : 'those entries'} is not being checked.`\n  );\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\ninterface LockfileV2Package {\n  name?: string;\n  version?: string;\n  link?: boolean;\n  resolved?: string;\n}\n\ninterface LockfileV2 {\n  lockfileVersion: number;\n  packages?: Record<string, LockfileV2Package>;\n  dependencies?: Record<string, LockfileV1Dependency>;\n}\n\ninterface LockfileV1Dependency {\n  version: string;\n  dependencies?: Record<string, LockfileV1Dependency>;\n}\n\nexport async function parseNpmLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n  let raw: string;\n  try {\n    raw = await readFile(lockfilePath, 'utf8');\n  } catch (cause) {\n    throw new PatchstackError(`Could not read lockfile at ${lockfilePath}`, 'LOCKFILE_NOT_FOUND', cause);\n  }\n\n  let parsed: LockfileV2;\n  try {\n    parsed = JSON.parse(raw) as LockfileV2;\n  } catch (cause) {\n    throw new PatchstackError(`Lockfile at ${lockfilePath} is not valid JSON`, 'LOCKFILE_PARSE_ERROR', cause);\n  }\n\n  if (parsed.packages) {\n    return extractFromV2(parsed.packages);\n  }\n\n  if (parsed.dependencies) {\n    return extractFromV1(parsed.dependencies);\n  }\n\n  throw new PatchstackError(\n    `Lockfile at ${lockfilePath} has no \"packages\" or \"dependencies\" key`,\n    'LOCKFILE_PARSE_ERROR',\n  );\n}\n\nfunction extractFromV2(packages: Record<string, LockfileV2Package>): PackageEntry[] {\n  const entries: PackageEntry[] = [];\n\n  for (const [pkgPath, pkg] of Object.entries(packages)) {\n    if (pkgPath === '') {\n      continue;\n    }\n    if (pkg.link === true) {\n      continue;\n    }\n    if (typeof pkg.version !== 'string' || pkg.version.length === 0) {\n      continue;\n    }\n\n    const name = pkg.name ?? extractNameFromPath(pkgPath);\n    if (name === null) {\n      continue;\n    }\n\n    entries.push({\n      name,\n      version: pkg.version,\n      path: pkgPath,\n      direct: isDirectV2(pkgPath),\n    });\n  }\n\n  return entries;\n}\n\nfunction extractFromV1(\n  deps: Record<string, LockfileV1Dependency>,\n  acc: PackageEntry[] = [],\n  depth = 0,\n): PackageEntry[] {\n  for (const [name, dep] of Object.entries(deps)) {\n    if (typeof dep.version === 'string' && dep.version.length > 0) {\n      acc.push({ name, version: dep.version, direct: depth === 0 });\n    }\n    if (dep.dependencies) {\n      extractFromV1(dep.dependencies, acc, depth + 1);\n    }\n  }\n  return acc;\n}\n\nfunction extractNameFromPath(pkgPath: string): string | null {\n  const segments = pkgPath.split('node_modules' + path.sep === pkgPath ? path.sep : '/');\n  const parts = pkgPath.split('/');\n  const nmIndex = parts.lastIndexOf('node_modules');\n  if (nmIndex === -1 || nmIndex >= parts.length - 1) {\n    return segments[segments.length - 1] ?? null;\n  }\n  const tail = parts.slice(nmIndex + 1);\n  if (tail.length === 0) {\n    return null;\n  }\n  const first = tail[0];\n  if (first !== undefined && first.startsWith('@') && tail.length >= 2) {\n    return `${first}/${tail[1]}`;\n  }\n  return first ?? null;\n}\n\nfunction isDirectV2(pkgPath: string): boolean {\n  const parts = pkgPath.split('/');\n  const nmCount = parts.filter((p) => p === 'node_modules').length;\n  return nmCount === 1;\n}\n","import { lstat, readFile, readdir, realpath, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\n/**\n * Dot-directories that hold real packages rather than caches.\n *\n * Skipping every dotfile is right for `.cache` and `.bin`; it is wrong for `.bun` and `.pnpm`, which is\n * where an isolated install actually keeps the packages. A project scanned that way reports no packages at\n * all, and an empty inventory reads exactly like a project with nothing vulnerable in it.\n */\nconst PACKAGE_STORE_DIRS = new Set(['.bun', '.pnpm', '.store']);\n\nexport async function walkNodeModules(cwd: string): Promise<PackageEntry[]> {\n  const root = path.join(cwd, 'node_modules');\n\n  try {\n    const info = await stat(root);\n    if (!info.isDirectory()) {\n      throw new PatchstackError(\n        `${root} exists but is not a directory.`,\n        'LOCKFILE_NOT_FOUND',\n      );\n    }\n  } catch (cause) {\n    if (cause instanceof PatchstackError) {\n      throw cause;\n    }\n    throw new PatchstackError(\n      `node_modules/ not found at ${cwd}. Install dependencies first (e.g. \\`bun install\\` or \\`npm install\\`).`,\n      'LOCKFILE_NOT_FOUND',\n      cause,\n    );\n  }\n\n  // The shallowest depth each real path has been reached at. An isolated layout is symlinks into a store,\n  // so the same package is reachable by several paths — and following symlinks without this is how a walk\n  // stops terminating.\n  //\n  // A DEPTH rather than a visited flag, because depth is what decides `direct`, and the two paths to one\n  // package do not agree about it: the top-level symlink is depth 0 and the copy inside the store is\n  // deeper. With a flag, whichever traversal arrived first owned the answer — which under concurrent reads\n  // is not the same answer twice. Re-traversal is allowed only when strictly shallower, so it terminates.\n  //\n  // Seeded with the project itself, because a link pointing back up the tree resolves there: followed, the\n  // project's own manifest would be read as one of its dependencies. A project is not installed into itself.\n  const seen = new Map<string, number>();\n  try {\n    seen.set(await realpath(cwd), -1);\n  } catch {\n    // Unresolvable cwd. The walk still terminates on every other repeated path.\n  }\n\n  return dedupeByShallowest(await walk(root, 0, seen));\n}\n\n/**\n * One entry per real path, at the shallowest depth it was found.\n *\n * The traversal can emit a package twice — once through a top-level symlink and once inside the store it\n * points into — and the two differ in `direct`. Resolved here rather than during the walk so the answer\n * does not depend on which asynchronous read finished first.\n */\nexport function dedupeByShallowest(found: Found[]): PackageEntry[] {\n  const best = new Map<string, Found>();\n  for (const item of found) {\n    const current = best.get(item.realpath);\n    if (current === undefined || item.depth < current.depth) best.set(item.realpath, item);\n  }\n\n  return [...best.values()].map(({ entry }) => entry);\n}\n\n/**\n * A package as found, with what is needed to choose between two findings of the same one.\n *\n * Exported, with `claim` and `dedupeByShallowest`, only so the tests can pin what decides between them —\n * the walk's own ordering cannot be controlled from outside it. Not part of this package's public surface.\n */\nexport interface Found {\n  entry: PackageEntry;\n  realpath: string;\n  depth: number;\n}\n\n/**\n * Directories read at a time.\n *\n * A dependency tree is thousands of tiny reads, and doing them one after another spends nearly all of the\n * scan waiting. A cap rather than everything at once: an unbounded fan-out over a large tree opens more file\n * handles than the process is allowed and the scan fails with EMFILE.\n */\nconst CONCURRENCY = 32;\n\nasync function walk(dir: string, depth: number, seen: Map<string, number>): Promise<Found[]> {\n  let names: string[];\n  try {\n    names = await readdir(dir);\n  } catch {\n    return [];\n  }\n\n  // Results are collected per entry and concatenated in directory order, so reading concurrently does not\n  // make the manifest depend on which read finished first.\n  const perEntry = await mapLimit(names, async (name) => {\n    if (name.startsWith('.') && !PACKAGE_STORE_DIRS.has(name)) return [];\n\n    const fullPath = path.join(dir, name);\n    const resolved = await claim(fullPath, seen, depth);\n    if (resolved === null) return [];\n\n    // A store directory holds packages one level down and is not itself a package: descend without trying\n    // to read a manifest from it, and without counting its depth against the direct-dependency marker.\n    if (PACKAGE_STORE_DIRS.has(name)) return walk(fullPath, depth, seen);\n\n    if (name.startsWith('@')) {\n      let subNames: string[];\n      try {\n        subNames = await readdir(fullPath);\n      } catch {\n        return [];\n      }\n\n      const scoped = await mapLimit(subNames, async (sub) => {\n        if (sub.startsWith('.')) return [];\n        const scopedDir = path.join(fullPath, sub);\n        const scopedReal = await claim(scopedDir, seen, depth);\n        if (scopedReal === null) return [];\n\n        return packageAndNested(scopedDir, scopedReal, depth, seen);\n      });\n\n      return scoped.flat();\n    }\n\n    return packageAndNested(fullPath, resolved, depth, seen);\n  });\n\n  return perEntry.flat();\n}\n\nasync function packageAndNested(\n  pkgDir: string,\n  resolved: string,\n  depth: number,\n  seen: Map<string, number>,\n): Promise<Found[]> {\n  const [own, nested] = await Promise.all([readPackage(pkgDir, depth), walkNested(pkgDir, depth, seen)]);\n\n  return own === null ? nested : [{ entry: own, realpath: resolved, depth }, ...nested];\n}\n\n/** `items.map(fn)` with at most `CONCURRENCY` in flight, results in input order. */\nasync function mapLimit<T, R>(items: T[], fn: (item: T) => Promise<R>): Promise<R[]> {\n  const results = new Array<R>(items.length);\n  let next = 0;\n  const workers = Array.from({ length: Math.min(CONCURRENCY, items.length) }, async () => {\n    while (next < items.length) {\n      const index = next++;\n      results[index] = await fn(items[index] as T);\n    }\n  });\n  await Promise.all(workers);\n\n  return results;\n}\n\nasync function readPackage(pkgDir: string, depth: number): Promise<PackageEntry | null> {\n  let raw: string;\n  try {\n    raw = await readFile(path.join(pkgDir, 'package.json'), 'utf8');\n  } catch {\n    return null;\n  }\n\n  let parsed: { name?: unknown; version?: unknown };\n  try {\n    parsed = JSON.parse(raw) as { name?: unknown; version?: unknown };\n  } catch {\n    return null;\n  }\n\n  if (typeof parsed.name !== 'string' || parsed.name.length === 0) {\n    return null;\n  }\n  if (typeof parsed.version !== 'string' || parsed.version.length === 0) {\n    return null;\n  }\n\n  return { name: parsed.name, version: parsed.version, direct: depth === 0 };\n}\n\nasync function walkNested(pkgDir: string, depth: number, seen: Map<string, number>): Promise<Found[]> {\n  const nested = path.join(pkgDir, 'node_modules');\n  if ((await claim(nested, seen, depth + 1)) === null) {\n    return [];\n  }\n\n  return walk(nested, depth + 1, seen);\n}\n\n/**\n * Claim a directory for traversal at `depth`, returning its real path, or null to skip it.\n *\n * Symlinks ARE followed, because an isolated install makes every top-level package one — refusing them\n * finds nothing there. The cycle risk that refusing them avoided is handled by the real path instead: a\n * target already reached at the same depth or shallower is skipped, so a link back up the tree ends the\n * walk rather than repeating it. Links pointing outside are followed too; a store lives outside\n * `node_modules` on some layouts, and the realpath map is what bounds the traversal either way.\n *\n * A strictly shallower arrival is allowed through, because it is a better answer about the same package\n * and refusing it would leave that answer to whichever read happened to finish first. Depth only ever\n * decreases on such a re-entry, so the traversal still terminates.\n *\n * The claim is made in one synchronous step after the last await, so concurrent callers cannot both take\n * it at the same depth.\n */\nexport async function claim(dir: string, seen: Map<string, number>, depth: number): Promise<string | null> {\n  let resolved: string;\n  try {\n    const info = await lstat(dir);\n    if (!info.isDirectory() && !info.isSymbolicLink()) return null;\n\n    resolved = await realpath(dir);\n    const target = await stat(resolved);\n    if (!target.isDirectory()) return null;\n  } catch {\n    return null; // a broken link, or a path that vanished mid-walk\n  }\n\n  const claimed = seen.get(resolved);\n  if (claimed !== undefined && claimed <= depth) return null;\n  seen.set(resolved, depth);\n\n  return resolved;\n}\n","import { readFile } from 'node:fs/promises';\nimport { PatchstackError, type PackageEntry } from '../types.js';\nimport { recordUnreadable, type ParseReport } from './report.js';\n\n/**\n * Parses pnpm-lock.yaml without pulling in a YAML library. We don't need full\n * YAML semantics — only the `packages:` block (the canonical list of every\n * installed package) and, for direct-dep marking, the dependency sections under\n * `importers:` (v9+) or at the top level (v6-v8 single-project).\n *\n * Supported pnpm-lock key formats:\n *   v5:    /pkg/1.0.0                /@scope/pkg/1.0.0\n *   v6-v8: /pkg@1.0.0                /@scope/pkg@1.0.0     (optional `(peer@x)` suffix)\n *   v9:    pkg@1.0.0                 @scope/pkg@1.0.0      (optional `(peer@x)` suffix, may be quoted)\n */\nexport async function parsePnpmLockfile(lockfilePath: string, report?: ParseReport): Promise<PackageEntry[]> {\n  let raw: string;\n  try {\n    raw = await readFile(lockfilePath, 'utf8');\n  } catch (cause) {\n    throw new PatchstackError(\n      `Could not read lockfile at ${lockfilePath}`,\n      'LOCKFILE_NOT_FOUND',\n      cause,\n    );\n  }\n\n  const lines = raw.split(/\\r?\\n/);\n  const directNames = collectDirectDepNames(lines);\n  const packageKeys = collectPackagesBlockKeys(lines);\n\n  if (packageKeys.length === 0) {\n    throw new PatchstackError(\n      `Lockfile at ${lockfilePath} has no \"packages\" entries`,\n      'LOCKFILE_PARSE_ERROR',\n    );\n  }\n\n  const entries: PackageEntry[] = [];\n  for (const key of packageKeys) {\n    const parsed = parsePackageKey(key);\n    if (parsed === null) {\n      recordUnreadable(report, key);\n      continue;\n    }\n    entries.push({\n      name: parsed.name,\n      version: parsed.version,\n      direct: directNames.has(parsed.name),\n    });\n  }\n\n  return entries;\n}\n\ninterface ParsedKey {\n  name: string;\n  version: string;\n}\n\nexport function parsePackageKey(rawKey: string): ParsedKey | null {\n  let k = rawKey.trim();\n  if (k.length === 0) {\n    return null;\n  }\n\n  if (\n    (k.startsWith(\"'\") && k.endsWith(\"'\")) ||\n    (k.startsWith('\"') && k.endsWith('\"'))\n  ) {\n    k = k.slice(1, -1);\n  }\n\n  if (k.startsWith('/')) {\n    k = k.slice(1);\n  }\n\n  // Strip peer-dependency suffix used by v6+: `pkg@1.0.0(peer@1.0.0)(other@2)`.\n  const parenIdx = k.indexOf('(');\n  if (parenIdx >= 0) {\n    k = k.slice(0, parenIdx);\n  }\n\n  // Split off the optional `@scope/` prefix so the remaining \"body\" contains\n  // exactly one separator between the bare name and the version. npm forbids\n  // both `@` and `/` inside the bare-name portion, so the first occurrence of\n  // either character in the body is unambiguously the name/version separator.\n  let scopePrefix = '';\n  let body = k;\n  if (k.startsWith('@')) {\n    const firstSlash = k.indexOf('/');\n    if (firstSlash <= 0) {\n      return null;\n    }\n    scopePrefix = k.slice(0, firstSlash + 1);\n    body = k.slice(firstSlash + 1);\n  }\n\n  const slashIdx = body.indexOf('/');\n  const atIdx = body.indexOf('@');\n\n  let sepIdx: number;\n  if (slashIdx < 0 && atIdx < 0) {\n    return null;\n  } else if (slashIdx < 0) {\n    sepIdx = atIdx;\n  } else if (atIdx < 0) {\n    sepIdx = slashIdx;\n  } else {\n    sepIdx = Math.min(slashIdx, atIdx);\n  }\n\n  const name = scopePrefix + body.slice(0, sepIdx);\n  let version = body.slice(sepIdx + 1);\n\n  // v5 peer suffix uses `_`: `1.0.0_react@18.2.0`.\n  const underscoreIdx = version.indexOf('_');\n  if (underscoreIdx >= 0) {\n    version = version.slice(0, underscoreIdx);\n  }\n\n  if (name.length === 0 || version.length === 0) {\n    return null;\n  }\n\n  return { name, version };\n}\n\nfunction indentOf(line: string): number {\n  let i = 0;\n  while (i < line.length && line[i] === ' ') {\n    i++;\n  }\n  return i;\n}\n\nfunction isBlankOrComment(line: string): boolean {\n  const trimmed = line.trim();\n  return trimmed.length === 0 || trimmed.startsWith('#');\n}\n\n/**\n * Collects keys directly nested under the top-level `packages:` block. Stops\n * when another top-level key (e.g. `snapshots:`) appears.\n */\nfunction collectPackagesBlockKeys(lines: string[]): string[] {\n  const keys: string[] = [];\n  let inBlock = false;\n  let childIndent: number | null = null;\n\n  for (const line of lines) {\n    if (isBlankOrComment(line)) {\n      continue;\n    }\n\n    const indent = indentOf(line);\n\n    if (!inBlock) {\n      if (indent === 0 && line.trim() === 'packages:') {\n        inBlock = true;\n      }\n      continue;\n    }\n\n    if (indent === 0) {\n      break;\n    }\n\n    if (childIndent === null) {\n      childIndent = indent;\n    }\n\n    if (indent !== childIndent) {\n      continue;\n    }\n\n    const content = line.slice(indent);\n    if (!content.endsWith(':')) {\n      continue;\n    }\n\n    keys.push(content.slice(0, -1));\n  }\n\n  return keys;\n}\n\n/**\n * Collects direct dependency names. Looks in two places, since the format\n * differs across lockfile versions and workspaces:\n *\n *   1. `importers:` > `<importer>:` > `{dependencies,devDependencies,optionalDependencies}:`\n *      (v9 always, v6-v8 in workspaces)\n *   2. Top-level `{dependencies,devDependencies,optionalDependencies}:`\n *      (v6-v8 single-project lockfiles)\n *\n * Direct-dep marking is best-effort: a name absent from both is simply left as\n * not-direct, matching the npm parser's behaviour for transitive packages.\n */\nfunction collectDirectDepNames(lines: string[]): Set<string> {\n  const names = new Set<string>();\n  collectFromImporters(lines, names);\n  for (const section of ['dependencies', 'devDependencies', 'optionalDependencies']) {\n    collectFromTopLevelSection(lines, section, names);\n  }\n  return names;\n}\n\nconst DEP_SECTIONS = new Set([\n  'dependencies',\n  'devDependencies',\n  'optionalDependencies',\n]);\n\nfunction collectFromImporters(lines: string[], out: Set<string>): void {\n  let inImporters = false;\n  let importerIndent: number | null = null;\n  let inDepSection = false;\n  let depSectionIndent: number | null = null;\n  let leafIndent: number | null = null;\n\n  for (const line of lines) {\n    if (isBlankOrComment(line)) {\n      continue;\n    }\n    const indent = indentOf(line);\n    const trimmed = line.trim();\n\n    if (!inImporters) {\n      if (indent === 0 && trimmed === 'importers:') {\n        inImporters = true;\n      }\n      continue;\n    }\n\n    if (indent === 0) {\n      break;\n    }\n\n    // Track the indent at which importer entries (e.g. `.:`, `packages/foo:`) sit.\n    if (importerIndent === null) {\n      importerIndent = indent;\n    }\n\n    if (indent === importerIndent) {\n      inDepSection = false;\n      depSectionIndent = null;\n      leafIndent = null;\n      continue;\n    }\n\n    if (!inDepSection) {\n      const key = stripTrailingColon(trimmed);\n      if (key !== null && DEP_SECTIONS.has(key)) {\n        inDepSection = true;\n        depSectionIndent = indent;\n      }\n      continue;\n    }\n\n    if (depSectionIndent !== null && indent <= depSectionIndent) {\n      // Reset — we've exited the dep section. Reconsider this line.\n      inDepSection = false;\n      depSectionIndent = null;\n      leafIndent = null;\n      const key = stripTrailingColon(trimmed);\n      if (key !== null && DEP_SECTIONS.has(key)) {\n        inDepSection = true;\n        depSectionIndent = indent;\n      }\n      continue;\n    }\n\n    if (leafIndent === null) {\n      leafIndent = indent;\n    }\n\n    if (indent !== leafIndent) {\n      continue;\n    }\n\n    const name = extractLeafName(trimmed);\n    if (name !== null) {\n      out.add(name);\n    }\n  }\n}\n\nfunction collectFromTopLevelSection(\n  lines: string[],\n  section: string,\n  out: Set<string>,\n): void {\n  let inSection = false;\n  let leafIndent: number | null = null;\n\n  for (const line of lines) {\n    if (isBlankOrComment(line)) {\n      continue;\n    }\n    const indent = indentOf(line);\n    const trimmed = line.trim();\n\n    if (!inSection) {\n      if (indent === 0 && trimmed === `${section}:`) {\n        inSection = true;\n      }\n      continue;\n    }\n\n    if (indent === 0) {\n      break;\n    }\n\n    if (leafIndent === null) {\n      leafIndent = indent;\n    }\n\n    if (indent !== leafIndent) {\n      continue;\n    }\n\n    const name = extractLeafName(trimmed);\n    if (name !== null) {\n      out.add(name);\n    }\n  }\n}\n\nfunction stripTrailingColon(s: string): string | null {\n  if (!s.endsWith(':')) {\n    return null;\n  }\n  return s.slice(0, -1).trim();\n}\n\n/**\n * Extracts the package name from a dependency-section leaf, handling both the\n * short form (`pkg: 1.0.0`) and the v9 expanded form (`pkg:` followed by\n * `specifier:` / `version:` children). Quoted scoped names like `'@scope/pkg':`\n * are unquoted.\n */\nfunction extractLeafName(trimmed: string): string | null {\n  const colonIdx = trimmed.indexOf(':');\n  if (colonIdx < 0) {\n    return null;\n  }\n  let name = trimmed.slice(0, colonIdx).trim();\n  if (name.length === 0) {\n    return null;\n  }\n  if (\n    (name.startsWith(\"'\") && name.endsWith(\"'\")) ||\n    (name.startsWith('\"') && name.endsWith('\"'))\n  ) {\n    name = name.slice(1, -1);\n  }\n  return name.length > 0 ? name : null;\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\nimport { recordUnreadable, type ParseReport } from './report.js';\n\n/**\n * Parses yarn.lock (yarn classic v1 and yarn berry v2+) without a YAML\n * dependency. Both generations share the same block structure — a top-level\n * mapping of comma-separated descriptor lists to a block containing a\n * `version` field — so we walk them with the same scanner and only branch on\n * the `version` syntax (`version \"x\"` for v1, `version: x` for berry).\n *\n * Direct vs transitive can't be derived from yarn.lock alone (yarn does not\n * record an importer manifest the way pnpm v9 does), so we cross-reference\n * the sibling `package.json` when present.\n */\nexport async function parseYarnLockfile(lockfilePath: string, report?: ParseReport): Promise<PackageEntry[]> {\n  let raw: string;\n  try {\n    raw = await readFile(lockfilePath, 'utf8');\n  } catch (cause) {\n    throw new PatchstackError(\n      `Could not read lockfile at ${lockfilePath}`,\n      'LOCKFILE_NOT_FOUND',\n      cause,\n    );\n  }\n\n  const blocks = parseBlocks(raw, report);\n  if (blocks.length === 0) {\n    throw new PatchstackError(\n      `Lockfile at ${lockfilePath} contains no package entries`,\n      'LOCKFILE_PARSE_ERROR',\n    );\n  }\n\n  const directNames = await readDirectDepNames(path.dirname(lockfilePath));\n\n  const entries: PackageEntry[] = [];\n  const seen = new Set<string>();\n  for (const block of blocks) {\n    if (block.version.length === 0 || block.names.size === 0) {\n      continue;\n    }\n    for (const name of block.names) {\n      const dedupKey = `${name}@${block.version}`;\n      if (seen.has(dedupKey)) {\n        continue;\n      }\n      seen.add(dedupKey);\n      entries.push({\n        name,\n        version: block.version,\n        direct: directNames.has(name),\n      });\n    }\n  }\n\n  return entries;\n}\n\ninterface Block {\n  /** Set when no descriptor resolved and at least one was in a form this scanner does not read. */\n  unreadable: boolean;\n  names: Set<string>;\n  version: string;\n  /** The key line this block came from, kept so an unreadable one can be named rather than just counted. */\n  key: string;\n}\n\n/** Keys that are not packages and are not meant to become one. */\nconst NON_PACKAGE_KEYS = new Set(['__metadata']);\n\nfunction parseBlocks(raw: string, report?: ParseReport): Block[] {\n  const lines = raw.split(/\\r?\\n/);\n  const blocks: Block[] = [];\n  let current: Block | null = null;\n\n  const finalize = () => {\n    if (current === null) {\n      return;\n    }\n    if (current.version.length > 0 && current.names.size > 0) {\n      blocks.push(current);\n    } else if (current.unreadable) {\n      // Only a block whose descriptors this scanner did not UNDERSTAND. A workspace or a file: entry\n      // resolved no name on purpose, and reporting those would bury the one that matters.\n      recordUnreadable(report, current.key);\n    }\n    current = null;\n  };\n\n  for (const line of lines) {\n    const trimmed = line.trim();\n    if (trimmed.length === 0 || trimmed.startsWith('#')) {\n      continue;\n    }\n\n    const indent = countLeadingSpaces(line);\n\n    if (indent === 0) {\n      finalize();\n      if (!trimmed.endsWith(':')) {\n        continue;\n      }\n      // `__metadata:` (yarn berry header) has no `@` in any descriptor and\n      // produces an empty names set, so it's naturally skipped on finalize.\n      const keyLine = trimmed.slice(0, -1);\n      const names = new Set<string>();\n      let unreadable = false;\n      for (const spec of splitDescriptors(keyLine)) {\n        const classified = classifyDescriptor(spec);\n        if (classified.name !== undefined) names.add(classified.name);\n        else if (!classified.excluded) unreadable = true;\n      }\n      current = { names, version: '', key: keyLine, unreadable: unreadable && names.size === 0 };\n      continue;\n    }\n\n    if (current === null) {\n      continue;\n    }\n\n    const version = parseVersionField(trimmed);\n    if (version !== null) {\n      current.version = version;\n    }\n  }\n\n  finalize();\n  return blocks;\n}\n\nfunction countLeadingSpaces(line: string): number {\n  let i = 0;\n  while (i < line.length && line[i] === ' ') {\n    i++;\n  }\n  return i;\n}\n\n/**\n * Splits a yarn descriptor key list on top-level commas. yarn quotes any\n * descriptor that contains characters needing escaping, so we respect quotes\n * while splitting to avoid breaking on commas inside (rare in practice but\n * cheap to handle).\n */\nexport function splitDescriptors(keyLine: string): string[] {\n  const parts: string[] = [];\n  let current = '';\n  let quote: '\"' | \"'\" | null = null;\n\n  for (let i = 0; i < keyLine.length; i++) {\n    const c = keyLine[i];\n    if (quote !== null) {\n      current += c;\n      if (c === quote) {\n        quote = null;\n      }\n      continue;\n    }\n    if (c === '\"' || c === \"'\") {\n      quote = c;\n      current += c;\n      continue;\n    }\n    if (c === ',') {\n      const piece = current.trim();\n      if (piece.length > 0) {\n        parts.push(piece);\n      }\n      current = '';\n      continue;\n    }\n    current += c;\n  }\n  const tail = current.trim();\n  if (tail.length > 0) {\n    parts.push(tail);\n  }\n  return parts;\n}\n\n/**\n * Protocols in a Berry descriptor that mean the entry is not a published registry package.\n *\n * `workspace:` is a local package in this repository; `patch:`, `virtual:` and `portal:` wrap another\n * descriptor; `file:`, `link:` and `exec:` point at the disk. None of them names something an advisory can\n * be about, and each of them puts an `@` in the range — so splitting on the last `@` treats part of the\n * range as part of the NAME and produces an entry naming a thing that does not exist.\n */\nconst NON_REGISTRY_PROTOCOLS = /^(?:workspace|patch|virtual|portal|file|link|exec|git|github|https?|ssh):/i;\n\n/**\n * The package name from a yarn descriptor, or null when the descriptor is not a registry package.\n *\n * Handles `axios@^1.6.0`, `\"@scope/pkg@^2.1.0\"` and `\"@scope/pkg@npm:2.1.0\"`. The range is discarded — the\n * resolved version comes from the block's `version` field.\n *\n * Two things it must not do. It must not split inside a range: a Berry descriptor's range can contain `@`\n * of its own (`lodash@patch:lodash@npm%3A4.17.20#…`), and the last `@` is then inside the range. And it\n * must not report a non-registry entry as a package at all, because the resulting name is not one.\n *\n * An alias (`alias@npm:real@1.2.3`) resolves to the REAL package: that is what is installed, and what an\n * advisory would be about. The alias is what the app imports it by, which is not this inventory's question.\n */\nexport function extractName(rawSpec: string): string | null {\n  return classifyDescriptor(rawSpec).name ?? null;\n}\n\n/**\n * A descriptor's package name, or why there isn't one.\n *\n * The two reasons are not the same and must not be reported the same way. `excluded` is a deliberate\n * exclusion — a workspace, a file, a link, a key that was never a package — and the manifest is complete\n * without it. Neither field set means this scanner did not recognise the descriptor at all, which is a\n * package that may well be installed and is missing from the inventory. One is silence; the other is a\n * warning, and telling them apart is the whole reason this returns more than a string.\n */\nexport function classifyDescriptor(rawSpec: string): { name?: string; excluded?: boolean } {\n  let s = rawSpec.trim();\n  if (s.length === 0) {\n    return { excluded: true };\n  }\n  if (\n    (s.startsWith('\"') && s.endsWith('\"')) ||\n    (s.startsWith(\"'\") && s.endsWith(\"'\"))\n  ) {\n    s = s.slice(1, -1);\n  }\n\n  if (NON_PACKAGE_KEYS.has(s)) {\n    return { excluded: true };\n  }\n\n  // Split at the FIRST `@` that starts a range, not the last character that happens to be one. A scope's\n  // leading `@` is at position 0 and never a separator.\n  const atIdx = s.indexOf('@', 1);\n  if (atIdx <= 0) {\n    return {};\n  }\n\n  const name = s.slice(0, atIdx);\n  const range = s.slice(atIdx + 1);\n  if (name.length === 0) {\n    return {};\n  }\n\n  if (NON_REGISTRY_PROTOCOLS.test(range)) {\n    return { excluded: true };\n  }\n\n  // `npm:` is the registry protocol, and it is the one place a second package name appears: an alias points\n  // at the package actually installed, which is the one an advisory can be about.\n  const alias = /^npm:(.+)$/i.exec(range);\n  if (alias !== null) {\n    const target = alias[1] ?? '';\n    // `npm:1.2.3` is a plain version for THIS package; `npm:other@1.2.3` renames another one.\n    const targetAt = target.indexOf('@', 1);\n\n    return { name: targetAt > 0 ? target.slice(0, targetAt) : name };\n  }\n\n  return { name };\n}\n\nfunction parseVersionField(content: string): string | null {\n  if (!content.startsWith('version')) {\n    return null;\n  }\n  const after = content.slice('version'.length);\n  // yarn v1: `version \"1.2.3\"` (whitespace then quoted)\n  // yarn berry: `version: 1.2.3` or `version: \"1.2.3\"`\n  const firstChar = after.charAt(0);\n  if (firstChar !== ' ' && firstChar !== '\\t' && firstChar !== ':') {\n    return null;\n  }\n  let rest = firstChar === ':' ? after.slice(1) : after;\n  rest = rest.trim();\n  if (rest.length === 0) {\n    return null;\n  }\n  if (\n    (rest.startsWith('\"') && rest.endsWith('\"')) ||\n    (rest.startsWith(\"'\") && rest.endsWith(\"'\"))\n  ) {\n    rest = rest.slice(1, -1);\n  }\n  return rest.length > 0 ? rest : null;\n}\n\nasync function readDirectDepNames(cwd: string): Promise<Set<string>> {\n  const names = new Set<string>();\n  let raw: string;\n  try {\n    raw = await readFile(path.join(cwd, 'package.json'), 'utf8');\n  } catch {\n    return names;\n  }\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    return names;\n  }\n\n  if (typeof parsed !== 'object' || parsed === null) {\n    return names;\n  }\n  const obj = parsed as Record<string, unknown>;\n\n  for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {\n    const section = obj[field];\n    if (typeof section !== 'object' || section === null) {\n      continue;\n    }\n    for (const name of Object.keys(section)) {\n      names.add(name);\n    }\n  }\n\n  return names;\n}\n","import type { Manifest, PackageEntry } from './types.js';\n\nexport interface WirePackage {\n  name: string;\n  version: string;\n}\n\nexport interface WirePayload {\n  ecosystem: Manifest['ecosystem'];\n  packages: WirePackage[];\n}\n\nexport interface NormalizeStats {\n  uniqueNames: number;\n  duplicateNames: string[];\n  totalEntries: number;\n}\n\nexport interface NormalizeResult {\n  payload: WirePayload;\n  stats: NormalizeStats;\n}\n\nexport function buildWirePayload(manifest: Manifest): NormalizeResult {\n  const seen = new Map<string, Set<string>>();\n  const wirePackages: WirePackage[] = [];\n\n  for (const entry of manifest.packages) {\n    const versions = seen.get(entry.name);\n    if (versions) {\n      if (versions.has(entry.version)) {\n        continue;\n      }\n      versions.add(entry.version);\n    } else {\n      seen.set(entry.name, new Set([entry.version]));\n    }\n    wirePackages.push({ name: entry.name, version: entry.version });\n  }\n\n  wirePackages.sort((a, b) => {\n    if (a.name === b.name) {\n      return compareVersions(a.version, b.version);\n    }\n    return a.name < b.name ? -1 : 1;\n  });\n\n  const duplicateNames: string[] = [];\n  for (const [name, versions] of seen) {\n    if (versions.size > 1) {\n      duplicateNames.push(name);\n    }\n  }\n\n  return {\n    payload: { ecosystem: manifest.ecosystem, packages: wirePackages },\n    stats: {\n      uniqueNames: seen.size,\n      duplicateNames,\n      totalEntries: manifest.packages.length,\n    },\n  };\n}\n\nexport function compareVersions(a: string, b: string): number {\n  if (a === b) {\n    return 0;\n  }\n\n  const [aBase, aPre] = splitPrerelease(a);\n  const [bBase, bPre] = splitPrerelease(b);\n\n  const baseCmp = compareSegments(aBase.split('.'), bBase.split('.'));\n  if (baseCmp !== 0) {\n    return baseCmp;\n  }\n\n  if (aPre === null && bPre === null) {\n    return 0;\n  }\n  if (aPre === null) {\n    return 1;\n  }\n  if (bPre === null) {\n    return -1;\n  }\n  return compareSegments(aPre.split('.'), bPre.split('.'));\n}\n\nfunction splitPrerelease(version: string): [string, string | null] {\n  const cleaned = version.replace(/^[v=]+/, '').split('+')[0]!;\n  const dashIndex = cleaned.indexOf('-');\n  if (dashIndex === -1) {\n    return [cleaned, null];\n  }\n  return [cleaned.slice(0, dashIndex), cleaned.slice(dashIndex + 1)];\n}\n\nfunction compareSegments(a: string[], b: string[]): number {\n  const max = Math.max(a.length, b.length);\n  for (let i = 0; i < max; i++) {\n    const aPart = a[i];\n    const bPart = b[i];\n    if (aPart === undefined) {\n      return -1;\n    }\n    if (bPart === undefined) {\n      return 1;\n    }\n    const aNum = /^\\d+$/.test(aPart);\n    const bNum = /^\\d+$/.test(bPart);\n    if (aNum && bNum) {\n      const diff = Number(aPart) - Number(bPart);\n      if (diff !== 0) {\n        return diff < 0 ? -1 : 1;\n      }\n      continue;\n    }\n    if (aNum) {\n      return -1;\n    }\n    if (bNum) {\n      return 1;\n    }\n    if (aPart < bPart) {\n      return -1;\n    }\n    if (aPart > bPart) {\n      return 1;\n    }\n  }\n  return 0;\n}\n\nexport function findPackageInManifest(\n  manifest: Manifest,\n  name: string,\n): PackageEntry[] {\n  return manifest.packages.filter((p) => p.name === name);\n}\n","import type { Config } from './types.js';\n\n/**\n * Bearer tokens for the authenticated Pulse endpoints (ADR-0018).\n *\n * Deliberately separate from the block-log token flow in\n * `src/protect/firewall-log.js`: that path talks to the auth/ Lambda's\n * /oauth/token and must keep working exactly as it does today.\n */\n\nconst TOKEN_SKEW_MS = 60_000;\n\n/** Build the Pulse token URL corresponding to a manifest endpoint override. */\nexport function buildTokenUrl(manifestEndpoint: string): string {\n  const url = new URL(manifestEndpoint);\n  const path = url.pathname.replace(/\\/$/, '');\n  url.pathname = path.endsWith('/manifest')\n    ? `${path.slice(0, -'/manifest'.length)}/token`\n    : '/monitor/pulse/token';\n  url.search = '';\n  url.hash = '';\n  return url.toString();\n}\n\n/** Split the WP-format `{secret}-{oauth.id}` credential on its last hyphen. */\nexport function parsePulseAuth(credential: string): { clientId: string; clientSecret: string } | null {\n  const index = credential.lastIndexOf('-');\n  if (index <= 0 || index === credential.length - 1) return null;\n\n  const clientId = credential.slice(index + 1);\n  if (!/^\\d+$/.test(clientId)) return null;\n\n  return { clientId, clientSecret: credential.slice(0, index) };\n}\n\nlet cached: { token: string; expiresAt: number } | null = null;\nlet inflight: Promise<string | null> | null = null;\n\n/** Drops the cached token. Exported for tests and for 401 handling. */\nexport function clearPulseToken(): void {\n  cached = null;\n}\n\n/**\n * Resolve a bearer token for `config.pulseAuth`, exchanging one if needed.\n *\n * Returns null whenever a token cannot be obtained — no credential, a rejected\n * exchange, a network failure. Callers then send the request unauthenticated,\n * and every site-addressed Pulse endpoint refuses it: only a first-time\n * provisioning call is anonymous. Returning null rather than throwing keeps that\n * refusal on the caller's own error path, where it can fall back or report.\n */\nexport async function getPulseToken(\n  config: Config,\n  fetchImpl: typeof fetch = fetch,\n): Promise<string | null> {\n  // Not `=== null`: Config is public, so callers can hand us an object that\n  // predates this field, and an unusable credential must never throw here.\n  if (typeof config.pulseAuth !== 'string' || config.pulseAuth.length === 0) return null;\n\n  if (cached !== null && Date.now() < cached.expiresAt - TOKEN_SKEW_MS) {\n    return cached.token;\n  }\n  if (inflight !== null) return inflight;\n\n  const credentials = parsePulseAuth(config.pulseAuth);\n  if (credentials === null) return null;\n\n  inflight = (async () => {\n    try {\n      const response = await fetchImpl(buildTokenUrl(config.endpoint), {\n        method: 'POST',\n        headers: {\n          'Content-Type': 'application/json',\n          Accept: 'application/json',\n          'User-Agent': '@patchstack/connect',\n        },\n        body: JSON.stringify({\n          grant_type: 'client_credentials',\n          client_id: credentials.clientId,\n          client_secret: credentials.clientSecret,\n        }),\n        signal: AbortSignal.timeout(config.timeoutMs),\n      });\n\n      if (!response.ok) return null;\n\n      const body = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n      if (typeof body.access_token !== 'string' || body.access_token.length === 0) return null;\n\n      const expiresIn = Number(body.expires_in);\n      const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n      cached = { token: body.access_token, expiresAt: Date.now() + ttlMs };\n\n      return body.access_token;\n    } catch {\n      return null;\n    } finally {\n      inflight = null;\n    }\n  })();\n\n  return inflight;\n}\n\n/**\n * `Authorization` header for a Pulse request, or `{}` when unauthenticated.\n * Spread into an existing header object so call sites stay one line.\n */\nexport async function pulseAuthHeader(\n  config: Config,\n  fetchImpl: typeof fetch = fetch,\n): Promise<Record<string, string>> {\n  const token = await getPulseToken(config, fetchImpl);\n  return token === null ? {} : { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Send a Pulse request, attaching the bearer token and retrying once if the\n * server rejects it.\n *\n * A cached token can stop being valid before it expires — the credential may\n * have been rotated or revoked meanwhile — so the server's 401 is authoritative\n * over our own clock. Without this a long-running process would keep presenting\n * a dead token until its local expiry.\n *\n * Only 401 retries: a 403 is a scope or site mismatch, which a fresh token\n * would not fix.\n */\nexport async function pulseFetch(\n  config: Config,\n  url: string,\n  init: RequestInit,\n  fetchImpl: typeof fetch = fetch,\n): Promise<Response> {\n  const send = async () => {\n    const auth = await pulseAuthHeader(config, fetchImpl);\n    const response = await fetchImpl(url, {\n      ...init,\n      headers: { ...(init.headers as Record<string, string> | undefined), ...auth },\n    });\n\n    return { response, authenticated: auth.Authorization !== undefined };\n  };\n\n  const first = await send();\n\n  // Retrying an unauthenticated request would just repeat it: the 401 was\n  // about something other than our token.\n  if (first.response.status === 401 && first.authenticated) {\n    clearPulseToken();\n\n    return (await send()).response;\n  }\n\n  return first.response;\n}\n","import { PatchstackError, type Config, type StoreManifestResponse } from './types.js';\nimport type { WirePayload } from './normalize.js';\nimport { pulseFetch } from './pulse-token.js';\n\nexport const DEFAULT_ENDPOINT = 'https://api.patchstack.com/monitor/pulse/manifest';\nexport const DEFAULT_TIMEOUT_MS = 30_000;\n\nexport function buildEndpointUrl(base: string, siteUuid?: string | null): string {\n  const trimmed = base.replace(/\\/$/, '');\n  return siteUuid !== undefined && siteUuid !== null && siteUuid.length > 0\n    ? `${trimmed}/${encodeURIComponent(siteUuid)}`\n    : trimmed;\n}\n\n/**\n * What a refusal means, in terms someone can act on.\n *\n * Every Pulse route that addresses an existing site requires a credential, so a rejection is now the\n * most likely failure a misconfigured project hits — and the three causes have three different fixes.\n * Reporting them as `Patchstack returned 401` names none of them, and this output is frequently read by\n * an AI agent that has no other way to find out what to do next.\n *\n * The distinction 401 cannot make on its own is whether we HELD a credential. Having none is a setup\n * step that was never run; having one rejected is a credential that has expired, been revoked, or whose\n * site no longer exists. A 403 is different again: the credential is valid and simply is not for this\n * site, which usually means a `.patchstackrc.json` carrying someone else's UUID.\n *\n * @returns the message, or null when the status is not an authentication failure\n */\nexport function authFailureMessage(status: number, config: Config): string | null {\n  const hasCredential = typeof config.pulseAuth === 'string' && config.pulseAuth.length > 0;\n\n  if (status === 401 && !hasCredential) {\n    return 'Patchstack requires an API credential for this site and none is configured. Run `npx patchstack-connect login`, or set PATCHSTACK_API_KEY.';\n  }\n  if (status === 401) {\n    return 'Patchstack rejected this API credential. It may have expired, been revoked, or the site may no longer exist. Run `npx patchstack-connect login` to issue a new one.';\n  }\n  if (status === 403) {\n    return 'This API credential is not permitted to act on this site. Check that siteUuid in .patchstackrc.json matches the credential (a credential is issued for one site).';\n  }\n\n  return null;\n}\n\n/** Build the live Pulse rules URL corresponding to a manifest endpoint override. */\nexport function buildRulesUrl(manifestEndpoint: string, siteUuid: string): string {\n  const url = new URL(manifestEndpoint);\n  const path = url.pathname.replace(/\\/$/, '');\n  url.pathname = path.endsWith('/manifest')\n    ? `${path.slice(0, -'/manifest'.length)}/rules/${encodeURIComponent(siteUuid)}`\n    : `/monitor/pulse/rules/${encodeURIComponent(siteUuid)}`;\n  url.search = '';\n  url.hash = '';\n  return url.toString();\n}\n\n/**\n * Build the claim URL for a site. The claim page lives on the same origin as\n * the API endpoint, at `/monitor/claim?site=<uuid>`. Using the API endpoint's\n * origin (rather than a hard-coded https://api.patchstack.com) means staging,\n * ngrok tunnels and local dev environments all produce a claim URL on the same\n * host the connector is already talking to.\n */\nexport function buildClaimUrl(endpoint: string, siteUuid: string): string {\n  const origin = new URL(endpoint).origin;\n  return `${origin}/monitor/claim?site=${encodeURIComponent(siteUuid)}`;\n}\n\n/**\n * Build the public widget-settings URL for a site. Like the claim URL, it\n * lives on the API endpoint's origin, at `/monitor/widget/settings/<uuid>`.\n */\nexport function buildSettingsUrl(endpoint: string, siteUuid: string): string {\n  const origin = new URL(endpoint).origin;\n  return `${origin}/monitor/widget/settings/${encodeURIComponent(siteUuid)}`;\n}\n\n/** Build the package-removed signal URL corresponding to a manifest endpoint override. */\nexport function buildPackageRemovedUrl(manifestEndpoint: string, siteUuid: string): string {\n  const url = new URL(manifestEndpoint);\n  const path = url.pathname.replace(/\\/$/, '');\n  url.pathname = path.endsWith('/manifest')\n    ? `${path.slice(0, -'/manifest'.length)}/package-removed/${encodeURIComponent(siteUuid)}`\n    : `/monitor/pulse/package-removed/${encodeURIComponent(siteUuid)}`;\n  url.search = '';\n  url.hash = '';\n  return url.toString();\n}\n\n/** Build the input-map ingest URL corresponding to a manifest endpoint override. */\nexport function buildInputMapUrl(manifestEndpoint: string, siteUuid: string): string {\n  const url = new URL(manifestEndpoint);\n  const path = url.pathname.replace(/\\/$/, '');\n  url.pathname = path.endsWith('/manifest')\n    ? `${path.slice(0, -'/manifest'.length)}/input-map/${encodeURIComponent(siteUuid)}`\n    : `/monitor/pulse/input-map/${encodeURIComponent(siteUuid)}`;\n  url.search = '';\n  url.hash = '';\n  return url.toString();\n}\n\n/**\n * Outcome of uploading an attack-surface map. `unchanged` is a first-class result, not a failure: most\n * builds do not change the surface, and the server keeps one revision per distinct surface rather than\n * one per deploy. `skipped` covers the cases where there is nothing to send or nowhere to send it.\n */\nexport type InputMapUploadOutcome =\n  | { result: 'stored'; revision: number }\n  | { result: 'unchanged'; revision: number }\n  | { result: 'skipped'; message: string }\n  | { result: 'failed'; message: string };\n\n/**\n * Upload an attack-surface map for this site.\n *\n * Fail-open by construction: every failure path returns a result rather than throwing, because this runs\n * during someone's build and a Patchstack outage must never break it. The caller decides what to print.\n */\nexport async function postInputMap(\n  config: Config,\n  map: { version: number; endpoints: unknown[] },\n): Promise<InputMapUploadOutcome> {\n  if (config.siteUuid === null) {\n    return { result: 'skipped', message: 'No site UUID configured — run `patchstack-connect scan` first.' };\n  }\n\n  const url = buildInputMapUrl(config.endpoint, config.siteUuid);\n  try {\n    const response = await pulseFetch(config, url, {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/json',\n        'User-Agent': '@patchstack/connect',\n      },\n      body: JSON.stringify(map),\n      signal: AbortSignal.timeout(config.timeoutMs),\n    });\n    if (response.status === 404) {\n      return { result: 'failed', message: 'Site not found — the configured site UUID is unknown to Patchstack.' };\n    }\n    if (response.status === 422) {\n      // The server implements a different map schema than this client emits. Say so plainly: it means\n      // one side is out of date, and guessing at compatibility is how a consumer misreads a document.\n      return { result: 'failed', message: `Patchstack does not accept this map schema (version ${map.version}). Update @patchstack/connect.` };\n    }\n    const refused = authFailureMessage(response.status, config);\n    if (refused !== null) {\n      return { result: 'failed', message: refused };\n    }\n    if (!response.ok) {\n      return { result: 'failed', message: `Patchstack returned ${response.status}.` };\n    }\n    const body = (await response.json()) as { result?: string; revision?: number };\n    // The revision is part of the contract, not decoration: \"stored, revision 0\" is not a state the server\n    // can be in, so accepting it would report a successful upload that cannot be pointed at afterwards.\n    const revision = Number(body.revision);\n    if ((body.result === 'stored' || body.result === 'unchanged') && Number.isInteger(revision) && revision > 0) {\n      return { result: body.result, revision };\n    }\n    return { result: 'failed', message: 'Patchstack returned an unexpected response.' };\n  } catch {\n    return { result: 'failed', message: `Could not reach Patchstack at ${url}.` };\n  }\n}\n\n/**\n * Outcome of the package-removed signal. 'deleted' — the site was unclaimed\n * and its record was removed; 'flagged' — the site is claimed, so it was only\n * marked for its owner to confirm removal in the dashboard; 'gone' — the\n * record no longer existed; 'failed' — Patchstack could not be reached or\n * returned an unexpected response.\n */\nexport interface PackageRemovedOutcome {\n  result: 'deleted' | 'flagged' | 'gone' | 'failed';\n  message: string | null;\n}\n\n/**\n * Tell Patchstack the @patchstack/connect package is being uninstalled from\n * this project. The site UUID is the only credential, so the server deletes\n * only unclaimed (anonymous) sites; claimed sites are merely flagged for\n * their owner. Never throws — an unreachable server must not block the local\n * uninstall.\n */\nexport async function postPackageRemoved(config: Config): Promise<PackageRemovedOutcome> {\n  if (config.siteUuid === null) {\n    return { result: 'failed', message: 'No site UUID configured.' };\n  }\n\n  const url = buildPackageRemovedUrl(config.endpoint, config.siteUuid);\n  try {\n    const response = await pulseFetch(config, url, {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'User-Agent': '@patchstack/connect',\n      },\n      signal: AbortSignal.timeout(config.timeoutMs),\n    });\n    if (response.status === 404) {\n      return { result: 'gone', message: null };\n    }\n    // A site that has been deleted answers 401 here, not 404: this route resolves the site FROM the\n    // credential, so once the site record is gone the credential resolves to nothing. Reporting that as\n    // an auth failure would tell someone to re-run `login` over a site that no longer exists, so the\n    // question is put to the endpoint that can still answer it — widget settings, which needs no\n    // credential and 404s for a removed site.\n    if (response.status === 401 && (await fetchSiteStatus(config)) === 'removed') {\n      return { result: 'gone', message: null };\n    }\n    const refused = authFailureMessage(response.status, config);\n    if (refused !== null) {\n      return { result: 'failed', message: refused };\n    }\n    if (!response.ok) {\n      return { result: 'failed', message: `Patchstack returned ${response.status}.` };\n    }\n    const body = (await response.json()) as { status?: string; message?: string };\n    if (body.status === 'deleted' || body.status === 'flagged') {\n      return { result: body.status, message: body.message ?? null };\n    }\n    return { result: 'failed', message: 'Patchstack returned an unexpected response.' };\n  } catch {\n    return { result: 'failed', message: `Could not reach Patchstack at ${url}.` };\n  }\n}\n\n/**\n * Whether the site record still exists on Patchstack's side. Removing a site\n * (dashboard delete or the widget's uninstall flow) only deletes the remote\n * record — the local integration files stay in the project — so this is the\n * signal that distinguishes \"removed from Patchstack\" from \"still active\".\n */\nexport type SiteStatus = 'active' | 'removed' | 'unknown';\n\n/**\n * Check the remote site status via the public widget-settings endpoint:\n * 200 means the site record exists, 404 means it was removed. Any other\n * response or a network failure is 'unknown' — never throws. The endpoint's\n * 200 responses are cacheable for an hour, so a cache-busting query param\n * keeps intermediaries from reporting a just-removed site as active.\n */\nexport async function fetchSiteStatus(config: Config): Promise<SiteStatus> {\n  if (config.siteUuid === null) return 'unknown';\n\n  const url = new URL(buildSettingsUrl(config.endpoint, config.siteUuid));\n  url.searchParams.set('t', Date.now().toString());\n\n  try {\n    const response = await fetch(url.toString(), {\n      method: 'GET',\n      headers: {\n        Accept: 'application/json',\n        'Cache-Control': 'no-cache',\n        'User-Agent': '@patchstack/connect',\n      },\n      signal: AbortSignal.timeout(config.timeoutMs),\n    });\n    if (response.status === 404) return 'removed';\n    if (response.ok) return 'active';\n    return 'unknown';\n  } catch {\n    return 'unknown';\n  }\n}\n\nexport async function postManifest(\n  config: Config,\n  payload: WirePayload,\n): Promise<StoreManifestResponse> {\n  const url = buildEndpointUrl(config.endpoint, config.siteUuid);\n  const timeoutMs = config.timeoutMs;\n\n  let response: Response;\n  try {\n    // Unauthenticated on the bootstrap POST: there is no credential until this\n    // request issues one.\n    response = await pulseFetch(config, url, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        Accept: 'application/json',\n        'User-Agent': '@patchstack/connect',\n      },\n      body: JSON.stringify({ ...payload, environment: config.environment }),\n      signal: AbortSignal.timeout(timeoutMs),\n    });\n  } catch (cause) {\n    if (isTimeoutError(cause)) {\n      throw new PatchstackError(\n        `Patchstack request to ${url} timed out after ${timeoutMs}ms. Override with PATCHSTACK_TIMEOUT_MS.`,\n        'NETWORK_TIMEOUT',\n        cause,\n      );\n    }\n    throw new PatchstackError(\n      `Could not reach Patchstack at ${url}. Check your network connection.`,\n      'NETWORK_ERROR',\n      cause,\n    );\n  }\n\n  const text = await response.text();\n  let body: StoreManifestResponse | null = null;\n  try {\n    body = text.length > 0 ? (JSON.parse(text) as StoreManifestResponse) : null;\n  } catch {\n    body = null;\n  }\n\n  if (response.status === 404) {\n    throw new PatchstackError(\n      body?.error ?? 'Site not found. Check that your site UUID is correct and that the app is registered as a Pulse app in your Patchstack dashboard.',\n      'SITE_NOT_FOUND',\n    );\n  }\n\n  if (response.status === 422) {\n    throw new PatchstackError(\n      body?.message ?? 'Patchstack rejected the manifest payload (validation failed).',\n      'VALIDATION_ERROR',\n    );\n  }\n\n  const refused = authFailureMessage(response.status, config);\n  if (refused !== null) {\n    throw new PatchstackError(refused, 'UNAUTHORIZED');\n  }\n\n  if (response.status < 200 || response.status >= 300) {\n    throw new PatchstackError(\n      `Patchstack returned ${response.status}: ${text.slice(0, 200)}`,\n      'SERVER_ERROR',\n    );\n  }\n\n  if (body === null) {\n    throw new PatchstackError('Patchstack returned an empty response.', 'SERVER_ERROR');\n  }\n\n  return body;\n}\n\nfunction isTimeoutError(cause: unknown): boolean {\n  if (cause instanceof Error) {\n    return cause.name === 'TimeoutError' || cause.name === 'AbortError';\n  }\n  return false;\n}\n","import { readFile, writeFile, chmod } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type Config, type Environment } from './types.js';\nimport { DEFAULT_ENDPOINT, DEFAULT_TIMEOUT_MS } from './client.js';\n\nconst CONFIG_FILENAME = '.patchstackrc.json';\n\n/**\n * Where the CREDENTIAL lives, separately from the public config.\n *\n * The site UUID is public by design — it ships in the widget tag in served HTML — so its file is meant to\n * be committed, and setup says so. The API key is not: it authenticates ingest and block-log reporting for\n * the site. One file for both meant the instruction to commit the config was an instruction to commit a\n * secret, and this package's own ignore file protects nothing in somebody else's repository.\n *\n * Two files, one of which setup adds to the project's ignore list. A credential still read from the\n * committed file keeps working — several already exist — with a warning, because breaking those installs\n * would be a worse outcome than the warning.\n */\nconst SECRET_FILENAME = '.patchstackrc.local.json';\n\nexport const DEFAULT_ENVIRONMENT: Environment = 'production';\n\ninterface ConfigFile {\n  siteUuid?: string;\n  /** WP-format `{secret}-{oauth.id}` for connector /api/logs/log. Server-only. */\n  apiKey?: string;\n  /**\n   * Credential for the authenticated Pulse endpoints (ADR-0018). Same format as\n   * `apiKey` and today the same value, but kept as its own field so the Pulse\n   * and block-log paths can diverge without disturbing each other. Server-only.\n   */\n  pulseAuth?: string;\n  endpoint?: string;\n  timeoutMs?: number;\n  environment?: string;\n  widget?: boolean;\n}\n\nexport interface ResolveConfigOptions {\n  cwd: string;\n  cliSiteUuid?: string;\n  cliEndpoint?: string;\n  /**\n   * When true, resolveConfig throws CONFIG_MISSING if no site UUID is configured.\n   * Defaults to false: callers that can run without a UUID (the first `scan` after\n   * `npm install`) just get `siteUuid: null` back and learn the UUID from the\n   * server response.\n   */\n  requireSiteUuid?: boolean;\n}\n\nexport async function resolveConfig(options: ResolveConfigOptions): Promise<Config> {\n  const fromFile = await readConfigFile(options.cwd);\n  const fromSecretFile = await readSecretFile(options.cwd);\n  const fromEnv = readEnv();\n\n  const siteUuid =\n    options.cliSiteUuid ??\n    fromEnv.siteUuid ??\n    fromFile.siteUuid ??\n    null;\n\n  const endpoint =\n    options.cliEndpoint ??\n    fromEnv.endpoint ??\n    fromFile.endpoint ??\n    DEFAULT_ENDPOINT;\n\n  const timeoutMs = fromEnv.timeoutMs ?? fromFile.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n  const environmentRaw = fromEnv.environment ?? fromFile.environment;\n  if (environmentRaw !== undefined && !isEnvironment(environmentRaw)) {\n    throw new PatchstackError(\n      `Environment must be \"production\" or \"sandbox\"; got \"${environmentRaw}\".`,\n      'CONFIG_INVALID',\n    );\n  }\n  const environment: Environment = environmentRaw ?? DEFAULT_ENVIRONMENT;\n\n  if (siteUuid !== null && siteUuid.length > 0 && !isUuid(siteUuid)) {\n    throw new PatchstackError(\n      `Site UUID \"${siteUuid}\" does not look like a valid UUID.`,\n      'CONFIG_INVALID',\n    );\n  }\n\n  if (options.requireSiteUuid && (siteUuid === null || siteUuid.length === 0)) {\n    throw new PatchstackError(\n      'No site UUID configured. Run `patchstack-connect scan` to provision one, or set PATCHSTACK_SITE_UUID.',\n      'CONFIG_MISSING',\n    );\n  }\n\n  // The credential file first, then the committed config. Both are read: a credential written by an\n  // earlier version lives in the committed file, and dropping support for it would break those installs\n  // rather than move them.\n  const apiKeyRaw = fromEnv.apiKey ?? fromSecretFile.apiKey ?? fromFile.apiKey ?? null;\n  // Falls back to apiKey so sites provisioned before ADR-0018 authenticate\n  // without re-provisioning: today both hold the same credential.\n  const pulseAuthRaw = fromEnv.pulseAuth ?? fromSecretFile.pulseAuth ?? fromFile.pulseAuth ?? apiKeyRaw;\n\n  return {\n    siteUuid: siteUuid === null || siteUuid.length === 0 ? null : siteUuid,\n    apiKey: apiKeyRaw === null || apiKeyRaw.length === 0 ? null : apiKeyRaw,\n    pulseAuth: pulseAuthRaw === null || pulseAuthRaw.length === 0 ? null : pulseAuthRaw,\n    endpoint,\n    timeoutMs,\n    environment,\n    widget: fromFile.widget !== false,\n  };\n}\n\nexport async function writeConfigFile(cwd: string, config: ConfigFile): Promise<string> {\n  const target = path.join(cwd, CONFIG_FILENAME);\n  const content = JSON.stringify(config, null, 2) + '\\n';\n  await writeFile(target, content, 'utf8');\n  return target;\n}\n\n/** The credential file's name, for callers that tell a person where their credential went. */\nexport const SECRET_CONFIG_FILENAME = SECRET_FILENAME;\n\n/**\n * What happened to the credential file, so a caller can say only what is true.\n *\n * `ignored` is the OUTCOME of the ignore entry, verified by reading the file back — not the fact that a\n * write was attempted. Telling somebody their credential is ignored when it is not is worse than telling\n * them nothing: they stop looking, and a credential committed once is in the history whether or not the\n * file is removed afterwards.\n */\nexport interface SecretFileResult {\n  /** Absolute path of the credential file. */\n  path: string;\n  /** True only when `.gitignore` was read back and really covers it. */\n  ignored: boolean;\n  /** Why not, when it is not — for the caller to print instead of a false assurance. */\n  reason?: string;\n}\n\n/**\n * Write the credential file, and make sure the project ignores it.\n *\n * The ignore entry is added here rather than left to the user, because the failure it prevents is silent\n * and permanent. The file is created owner-only: it holds a credential, and the default mode on a shared\n * machine is readable by everybody.\n */\nexport async function writeSecretFile(cwd: string, secrets: SecretFile): Promise<SecretFileResult> {\n  const target = path.join(cwd, SECRET_FILENAME);\n  await writeFile(target, JSON.stringify(secrets, null, 2) + '\\n', { encoding: 'utf8', mode: 0o600 });\n  // Explicit chmod as well as the create mode: an existing file keeps its old permissions, and a rotation\n  // writes over one that may predate this.\n  try {\n    await chmod(target, 0o600);\n  } catch {\n    // Windows and some mounted filesystems have no POSIX mode. Not a reason to fail the write.\n  }\n\n  const ignore = await ensureIgnored(cwd, SECRET_FILENAME);\n\n  return { path: target, ...ignore };\n}\n\n/**\n * Add a line to the project's `.gitignore` if it is not already covered, and report whether it now is.\n *\n * Deliberately conservative about the file: it appends one entry and never rewrites or reorders what is\n * there. A missing `.gitignore` is created, because a project without one is exactly the project that\n * would commit this.\n *\n * Not conservative about the ANSWER. Every failure — unreadable file, unwritable file, an entry that is\n * there but negated further down — comes back as `ignored: false` with a reason, because the caller's next\n * line is either an assurance or a warning and it has to be the right one.\n */\nasync function ensureIgnored(cwd: string, entry: string): Promise<{ ignored: boolean; reason?: string }> {\n  const target = path.join(cwd, '.gitignore');\n  let existing = '';\n  try {\n    existing = await readFile(target, 'utf8');\n  } catch (err) {\n    const code = (err as NodeJS.ErrnoException).code;\n    if (code !== 'ENOENT') {\n      return { ignored: false, reason: `.gitignore could not be read (${code ?? 'unknown error'})` };\n    }\n  }\n\n  if (ignoresEntry(existing, entry)) return { ignored: true };\n\n  const separator = existing === '' || existing.endsWith('\\n') ? '' : '\\n';\n  const block = `${separator}\\n# Patchstack credential — never commit this\\n${entry}\\n`;\n  try {\n    await writeFile(target, existing + block, 'utf8');\n  } catch (err) {\n    const code = (err as NodeJS.ErrnoException).code;\n\n    return { ignored: false, reason: `.gitignore could not be written (${code ?? 'unknown error'})` };\n  }\n\n  // Read back rather than assume. The write can succeed and still not produce coverage — a symlink into a\n  // read-only location, a filesystem that silently truncates — and this is the only claim being made.\n  try {\n    const written = await readFile(target, 'utf8');\n    if (ignoresEntry(written, entry)) return { ignored: true };\n\n    return { ignored: false, reason: '.gitignore does not cover the file after writing it' };\n  } catch (err) {\n    const code = (err as NodeJS.ErrnoException).code;\n\n    return { ignored: false, reason: `.gitignore could not be read back (${code ?? 'unknown error'})` };\n  }\n}\n\n/**\n * Whether the project currently ignores the credential file.\n *\n * A fresh verified read, for callers that are about to tell somebody their credential is safe but did not\n * write it themselves — the login flow rotates the credential several layers below the line that prints.\n */\nexport async function secretFileIgnored(cwd: string): Promise<{ ignored: boolean; reason?: string }> {\n  try {\n    const contents = await readFile(path.join(cwd, '.gitignore'), 'utf8');\n    if (ignoresEntry(contents, SECRET_FILENAME)) return { ignored: true };\n\n    return { ignored: false, reason: `.gitignore does not cover ${SECRET_FILENAME}` };\n  } catch (err) {\n    const code = (err as NodeJS.ErrnoException).code;\n\n    return {\n      ignored: false,\n      reason: code === 'ENOENT' ? 'this project has no .gitignore' : `.gitignore could not be read (${code ?? 'unknown error'})`,\n    };\n  }\n}\n\n/**\n * Does this `.gitignore` text actually ignore `entry`?\n *\n * Last match wins, as git resolves it: a `!entry` line below a matching pattern un-ignores the file, so a\n * scan that stops at the first positive line reports coverage a `git status` would contradict. Only exact\n * entries are recognised — a glob that happens to cover the file is not claimed, because claiming it wrong\n * is the failure this function exists to prevent.\n */\nfunction ignoresEntry(contents: string, entry: string): boolean {\n  let ignored = false;\n  for (const raw of contents.split('\\n')) {\n    const line = raw.trim();\n    if (line === '' || line.startsWith('#')) continue;\n    const negated = line.startsWith('!');\n    const pattern = negated ? line.slice(1).trim() : line;\n    if (pattern === entry || pattern === `/${entry}`) ignored = !negated;\n  }\n\n  return ignored;\n}\n\n/**\n * Merge a new siteUuid into the existing `.patchstackrc.json` (or create it).\n * Preserves any `endpoint` / `timeoutMs` / `apiKey` the user already wrote.\n */\nexport async function persistSiteUuid(cwd: string, siteUuid: string): Promise<string> {\n  const existing = await readConfigFile(cwd);\n  return writeConfigFile(cwd, { ...existing, siteUuid });\n}\n\n/**\n * Persist the WP-format api_key issued at provision. Authenticates both the Pulse endpoints and connector\n * log reporting. Never embed it in the public disclosure widget.\n *\n * Written to the credential file, which setup adds to the project's ignore list — the public config is\n * meant to be committed, so a credential in it is a credential in the repository.\n *\n * Any copy in the committed config is REMOVED at the same time. Leaving it would mean the value that just\n * got moved is still in the file everyone commits, which is the state this split exists to end. `pulseAuth`\n * goes with it: that field resolves ahead of `apiKey`, so a stale copy would keep authenticating with a\n * credential the server has replaced.\n */\nexport async function persistApiKey(cwd: string, apiKey: string): Promise<SecretFileResult> {\n  const { apiKey: _movedKey, pulseAuth: _movedPulse, ...publicConfig } = await readConfigFile(cwd);\n  const existingSecrets = await readSecretFile(cwd);\n\n  const target = await writeSecretFile(cwd, { ...existingSecrets, apiKey, pulseAuth: undefined });\n\n  // Only rewritten when it actually held a credential, so a normal provision does not touch it.\n  if (_movedKey !== undefined || _movedPulse !== undefined) {\n    await writeConfigFile(cwd, publicConfig);\n  }\n\n  return target;\n}\n\n/**\n * Persist a Pulse-specific credential.\n *\n * Only needed when Pulse ingest and block-log reporting must use *different*\n * credentials; they share one today, so `persistApiKey` covers both. Retained\n * for callers that separate them.\n */\nexport async function persistPulseAuth(cwd: string, pulseAuth: string): Promise<SecretFileResult> {\n  const existing = await readSecretFile(cwd);\n\n  return writeSecretFile(cwd, { ...existing, pulseAuth });\n}\n\n/**\n * Whether the committed config still holds a credential, so a caller can say so.\n *\n * Reported rather than fixed silently: the file is in the project's history if it was ever committed, and\n * rotating the credential is the only thing that actually resolves that. Moving it and saying nothing would\n * leave someone believing they were fine.\n */\nexport async function credentialInCommittedConfig(cwd: string): Promise<boolean> {\n  const config = await readConfigFile(cwd);\n\n  return config.apiKey !== undefined || config.pulseAuth !== undefined;\n}\n\n/** The credential file. Same shape as the config's credential fields, and nothing else. */\ninterface SecretFile {\n  apiKey?: string;\n  pulseAuth?: string;\n}\n\nasync function readSecretFile(cwd: string): Promise<SecretFile> {\n  const file = await readJsonFile(cwd, SECRET_FILENAME);\n\n  return { apiKey: file.apiKey, pulseAuth: file.pulseAuth };\n}\n\nasync function readConfigFile(cwd: string): Promise<ConfigFile> {\n  return readJsonFile(cwd, CONFIG_FILENAME);\n}\n\nasync function readJsonFile(cwd: string, filename: string): Promise<ConfigFile> {\n  const target = path.join(cwd, filename);\n  let raw: string;\n  try {\n    raw = await readFile(target, 'utf8');\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n      return {};\n    }\n    throw new PatchstackError(\n      `Could not read ${target}: ${(err as Error).message}`,\n      'CONFIG_INVALID',\n      err,\n    );\n  }\n\n  try {\n    return JSON.parse(raw) as ConfigFile;\n  } catch (err) {\n    throw new PatchstackError(\n      `Config file ${target} contains invalid JSON.`,\n      'CONFIG_INVALID',\n      err,\n    );\n  }\n}\n\nfunction readEnv(): ConfigFile {\n  const timeoutRaw = process.env.PATCHSTACK_TIMEOUT_MS;\n  let timeoutMs: number | undefined;\n  if (timeoutRaw !== undefined && timeoutRaw.length > 0) {\n    const parsed = Number(timeoutRaw);\n    if (!Number.isFinite(parsed) || parsed <= 0) {\n      throw new PatchstackError(\n        `PATCHSTACK_TIMEOUT_MS must be a positive number; got \"${timeoutRaw}\".`,\n        'CONFIG_INVALID',\n      );\n    }\n    timeoutMs = parsed;\n  }\n  const environmentRaw = process.env.PATCHSTACK_ENVIRONMENT;\n  return {\n    siteUuid: process.env.PATCHSTACK_SITE_UUID ?? undefined,\n    apiKey: process.env.PATCHSTACK_API_KEY ?? undefined,\n    pulseAuth: process.env.PATCHSTACK_PULSE_AUTH ?? undefined,\n    endpoint: process.env.PATCHSTACK_ENDPOINT ?? undefined,\n    timeoutMs,\n    environment:\n      environmentRaw !== undefined && environmentRaw.length > 0 ? environmentRaw : undefined,\n  };\n}\n\nfunction isUuid(value: string): boolean {\n  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);\n}\n\nfunction isEnvironment(value: string): value is Environment {\n  return value === 'production' || value === 'sandbox';\n}\n","import type { WirePackage } from './normalize.js';\n\n/**\n * A best-effort description of the stack a build was produced with, derived\n * entirely from the lockfile (ground truth) plus the build-time environment.\n *\n * The disclosure widget reads this from `window.__PATCHSTACK_STACK__` (injected\n * by `mark-build`) and reports it to Patchstack, so we learn how the sites we\n * protect are actually built and hosted — across every \"vibe\" platform — without\n * shipping a runtime probe onto the host server. Every field is a coarse label\n * or a bare key *name*: no versions beyond the framework, and never an env value.\n */\nexport interface StackDescriptor {\n  /** App / meta-framework, e.g. \"next\", \"nuxt\", \"tanstack-start\", \"remix\". */\n  framework: string | null;\n  /** UI runtime, e.g. \"react\", \"vue\", \"svelte\", \"solid\". */\n  ui: string | null;\n  /** Build tool / bundler, e.g. \"vite\", \"webpack\", \"rspack\". */\n  bundler: string | null;\n  /** Deployment-runtime hint from deps, e.g. \"cloudflare-workers\", \"vercel\". */\n  runtime: string | null;\n  /** The vibe/builder platform that generated the project, e.g. \"lovable\". */\n  builder: string | null;\n  /** Package ecosystem the manifest came from. */\n  ecosystem: 'npm';\n  /** Hosting-related build-environment variable NAMES (never their values). */\n  hostingEnvKeys: string[];\n}\n\ntype Category = 'framework' | 'ui' | 'bundler' | 'runtime' | 'builder';\n\ninterface StackRule {\n  category: Category;\n  /** Exact package name that signals this label. */\n  pkg: string;\n  label: string;\n}\n\n/**\n * Package → stack-label registry. First match per category wins, so order\n * within a category is priority order (most specific first). Add a row to teach\n * the connector a new framework, bundler, or vibe platform.\n */\nconst STACK_RULES: readonly StackRule[] = [\n  // Meta-frameworks (most specific first).\n  { category: 'framework', pkg: '@tanstack/react-start', label: 'tanstack-start' },\n  { category: 'framework', pkg: '@tanstack/start', label: 'tanstack-start' },\n  { category: 'framework', pkg: 'next', label: 'next' },\n  { category: 'framework', pkg: 'nuxt', label: 'nuxt' },\n  { category: 'framework', pkg: '@remix-run/react', label: 'remix' },\n  { category: 'framework', pkg: '@remix-run/node', label: 'remix' },\n  { category: 'framework', pkg: 'react-router', label: 'react-router' },\n  { category: 'framework', pkg: 'astro', label: 'astro' },\n  { category: 'framework', pkg: '@sveltejs/kit', label: 'sveltekit' },\n  { category: 'framework', pkg: '@builder.io/qwik-city', label: 'qwik-city' },\n  { category: 'framework', pkg: 'gatsby', label: 'gatsby' },\n  { category: 'framework', pkg: 'express', label: 'express' },\n  { category: 'framework', pkg: 'fastify', label: 'fastify' },\n\n  // UI runtimes.\n  { category: 'ui', pkg: '@angular/core', label: 'angular' },\n  { category: 'ui', pkg: 'react-dom', label: 'react' },\n  { category: 'ui', pkg: 'react', label: 'react' },\n  { category: 'ui', pkg: 'vue', label: 'vue' },\n  { category: 'ui', pkg: 'svelte', label: 'svelte' },\n  { category: 'ui', pkg: 'solid-js', label: 'solid' },\n  { category: 'ui', pkg: 'preact', label: 'preact' },\n\n  // Bundlers / build tools.\n  { category: 'bundler', pkg: 'vite', label: 'vite' },\n  { category: 'bundler', pkg: '@rspack/core', label: 'rspack' },\n  { category: 'bundler', pkg: 'webpack', label: 'webpack' },\n  { category: 'bundler', pkg: 'parcel', label: 'parcel' },\n  { category: 'bundler', pkg: 'rollup', label: 'rollup' },\n  { category: 'bundler', pkg: 'esbuild', label: 'esbuild' },\n\n  // Deployment-runtime hints from build deps.\n  { category: 'runtime', pkg: 'wrangler', label: 'cloudflare-workers' },\n  { category: 'runtime', pkg: '@cloudflare/workers-types', label: 'cloudflare-workers' },\n  { category: 'runtime', pkg: '@cloudflare/vite-plugin', label: 'cloudflare-workers' },\n  { category: 'runtime', pkg: '@vercel/node', label: 'vercel' },\n  { category: 'runtime', pkg: '@netlify/functions', label: 'netlify' },\n  { category: 'runtime', pkg: '@netlify/blobs', label: 'netlify' },\n\n  // Vibe / builder platforms — the \"learn from each platform\" signal.\n  { category: 'builder', pkg: 'lovable-tagger', label: 'lovable' },\n  { category: 'builder', pkg: '@replit/vite-plugin-runtime-error-modal', label: 'replit' },\n  { category: 'builder', pkg: '@replit/vite-plugin-cartographer', label: 'replit' },\n];\n\n/** Build-environment variable-name patterns that fingerprint a host, from server-insight. */\nconst HOSTING_ENV_PATTERNS: readonly RegExp[] = [\n  /^CF_/,\n  /^CLOUDFLARE_/,\n  /^VERCEL/,\n  /^NETLIFY/,\n  /^AWS_(LAMBDA|REGION|EXECUTION)/,\n  /^FLY_/,\n  /^RENDER/,\n  /^RAILWAY_/,\n  /^DENO_/,\n  /^EDGE_RUNTIME/,\n  /^DYNO$/,\n  /^K_SERVICE$/,\n  /^GAE_/,\n  /^FUNCTION_/,\n];\n\n/**\n * Return the sorted NAMES of hosting-related environment variables present in\n * `env`. Only names are surfaced — never values — so a build fingerprint can\n * distinguish \"deployed on Cloudflare\" from \"deployed on Vercel\" without ever\n * disclosing a secret.\n */\nexport function collectHostingEnvKeys(env: NodeJS.ProcessEnv = process.env): string[] {\n  return Object.keys(env)\n    .filter((key) => HOSTING_ENV_PATTERNS.some((pattern) => pattern.test(key)))\n    .sort();\n}\n\n/**\n * Derive a {@link StackDescriptor} from the wire packages and the build\n * environment. Never throws: an unrecognised stack just yields nulls.\n */\nexport function detectStack(\n  packages: readonly WirePackage[],\n  env: NodeJS.ProcessEnv = process.env,\n): StackDescriptor {\n  const present = new Set(packages.map((pkg) => pkg.name));\n\n  const firstMatch = (category: Category): string | null => {\n    for (const rule of STACK_RULES) {\n      if (rule.category === category && present.has(rule.pkg)) {\n        return rule.label;\n      }\n    }\n    return null;\n  };\n\n  return {\n    framework: firstMatch('framework'),\n    ui: firstMatch('ui'),\n    bundler: firstMatch('bundler'),\n    runtime: firstMatch('runtime'),\n    builder: firstMatch('builder'),\n    ecosystem: 'npm',\n    hostingEnvKeys: collectHostingEnvKeys(env),\n  };\n}\n\n/** True when the descriptor carries no useful signal (nothing worth injecting). */\nexport function isEmptyStack(stack: StackDescriptor): boolean {\n  return (\n    stack.framework === null &&\n    stack.ui === null &&\n    stack.bundler === null &&\n    stack.runtime === null &&\n    stack.builder === null &&\n    stack.hostingEnvKeys.length === 0\n  );\n}\n","// Managed disclosure-widget tag — the connector installs the widget for you.\n//\n// After a successful scan the connector ensures the site's root HTML shell\n// carries the widget's one-liner CDN tag (the canonical install form from the\n// widget docs: a single <script> with `data-site-uuid`, auto-initialising on\n// DOMContentLoaded). The tag carries an ownership attribute so re-runs update\n// it in place instead of stacking copies, and so `uninstall` flows can find it.\n// A manual/legacy install (any other reference to the widget script) is always\n// left untouched — we never convert or duplicate someone's hand-rolled tag.\n\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\n\nexport const WIDGET_SCRIPT_URL = 'https://cdn.patchstack.com/patchstack-widget.js';\n\n/** Attribute that tags the connector-managed widget tag so re-runs update it. */\nexport const WIDGET_MARKER_ATTR = 'data-patchstack-connect-widget';\n\n/** Substring that marks any widget install (managed or manual) in HTML. */\nconst WIDGET_NEEDLE = 'patchstack-widget';\n\n/**\n * Root HTML shells the connector is willing to edit, in priority order:\n * Vite/plain SPA, CRA-style, SvelteKit. Framework layouts that are code rather\n * than HTML (Next/Nuxt/Astro layouts) are never edited automatically — `guide`\n * prints the snippet and the right file for those.\n */\nexport const SOURCE_SHELL_CANDIDATES = ['index.html', 'public/index.html', 'src/app.html'];\n\nexport function buildWidgetTag(siteUuid: string): string {\n  return `<script src=\"${WIDGET_SCRIPT_URL}\" data-site-uuid=\"${siteUuid}\" defer ${WIDGET_MARKER_ATTR}=\"true\"></script>`;\n}\n\nexport type WidgetEnsureAction =\n  /** No widget present — the managed tag was inserted before </body>. */\n  | 'added'\n  /** A managed tag existed with a different UUID — replaced in place. */\n  | 'updated'\n  /** The managed tag is already present and current. */\n  | 'unchanged'\n  /** A manual (non-managed) widget install exists — left untouched. */\n  | 'manual'\n  /** The document has no </body> to anchor on — nothing was changed. */\n  | 'no-body';\n\nexport interface WidgetEnsureResult {\n  html: string;\n  action: WidgetEnsureAction;\n}\n\nconst MANAGED_TAG_RE = new RegExp(\n  `<script[^>]*${WIDGET_MARKER_ATTR}[^>]*>\\\\s*</script>`,\n  'i',\n);\n\n/**\n * Ensure a single HTML document carries the managed widget tag. Idempotent:\n * updates the managed tag in place, adopts (leaves alone) manual installs, and\n * only ever inserts immediately before </body>.\n */\nexport function ensureWidgetInHtml(html: string, siteUuid: string): WidgetEnsureResult {\n  const tag = buildWidgetTag(siteUuid);\n\n  const managed = html.match(MANAGED_TAG_RE);\n  if (managed !== null) {\n    if (managed[0].includes(`data-site-uuid=\"${siteUuid}\"`)) {\n      return { html, action: 'unchanged' };\n    }\n    return { html: html.replace(MANAGED_TAG_RE, tag), action: 'updated' };\n  }\n\n  if (html.includes(WIDGET_NEEDLE)) {\n    return { html, action: 'manual' };\n  }\n\n  const bodyClose = html.match(/([ \\t]*)<\\/body>/i);\n  if (bodyClose === null || bodyClose.index === undefined) {\n    return { html, action: 'no-body' };\n  }\n  const indent = bodyClose[1] ?? '';\n  const insertion = `${indent}  ${tag}\\n${indent}</body>`;\n  return {\n    html: html.slice(0, bodyClose.index) + insertion + html.slice(bodyClose.index + bodyClose[0].length),\n    action: 'added',\n  };\n}\n\n/** First editable root HTML shell in the project, or null when there is none. */\nexport function findSourceShell(cwd: string): string | null {\n  for (const candidate of SOURCE_SHELL_CANDIDATES) {\n    if (existsSync(path.join(cwd, candidate))) {\n      return candidate;\n    }\n  }\n  return null;\n}\n\nexport interface SourceWidgetResult {\n  /** Project-relative path of the shell that was inspected, or null. */\n  shell: string | null;\n  action: WidgetEnsureAction | 'no-shell';\n}\n\n/**\n * Ensure the managed widget tag in the project's root HTML shell. Edits at most\n * that one file; returns what happened so the caller can report it.\n */\nexport function ensureSourceWidget(cwd: string, siteUuid: string): SourceWidgetResult {\n  const shell = findSourceShell(cwd);\n  if (shell === null) {\n    return { shell: null, action: 'no-shell' };\n  }\n  const file = path.join(cwd, shell);\n  const before = readFileSync(file, 'utf8');\n  const { html, action } = ensureWidgetInHtml(before, siteUuid);\n  if (html !== before) {\n    writeFileSync(file, html);\n  }\n  return { shell, action };\n}\n"],"mappings":"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","names":["import_promises","import_node_path","path","import_promises","import_promises","import_node_path","path","import_promises","import_node_path","path","import_promises","import_promises","import_node_path","path","path","path","path","import_promises","import_node_path","path","path","path","import_node_path","path"]}