@reddoorla/maintenance 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,8 +137,8 @@ __export(lighthouse_airtable_exports, {
137
137
  lighthouseScoresFromResult: () => lighthouseScoresFromResult,
138
138
  resolveSlugFromCwd: () => resolveSlugFromCwd
139
139
  });
140
- import { readFile as readFile6 } from "fs/promises";
141
- import { join as join6 } from "path";
140
+ import { readFile as readFile7 } from "fs/promises";
141
+ import { join as join7 } from "path";
142
142
  function lighthouseScoresFromResult(result) {
143
143
  if (result.audit !== "lighthouse") {
144
144
  throw new Error(`Expected a 'lighthouse' AuditResult, got '${result.audit}'`);
@@ -155,8 +155,8 @@ function lighthouseScoresFromResult(result) {
155
155
  }
156
156
  async function resolveSlugFromCwd(cwd) {
157
157
  try {
158
- const pkgPath = join6(cwd, "package.json");
159
- const raw = await readFile6(pkgPath, "utf-8");
158
+ const pkgPath = join7(cwd, "package.json");
159
+ const raw = await readFile7(pkgPath, "utf-8");
160
160
  const pkg = JSON.parse(raw);
161
161
  if (!pkg.name) throw new Error("package.json has no 'name' field");
162
162
  return siteSlug(pkg.name);
@@ -514,9 +514,9 @@ async function securityAudit(ctx) {
514
514
  }
515
515
 
516
516
  // src/audits/lighthouse.ts
517
- import { readFile as readFile3, writeFile, mkdtemp, rm } from "fs/promises";
517
+ import { readFile as readFile4, writeFile, mkdtemp, rm } from "fs/promises";
518
518
  import { tmpdir } from "os";
519
- import { join as join3 } from "path";
519
+ import { join as join4 } from "path";
520
520
 
521
521
  // src/configs/lighthouse.ts
522
522
  var lighthouseConfig = {
@@ -549,10 +549,37 @@ var lighthouseConfig = {
549
549
  }
550
550
  };
551
551
 
552
+ // src/audits/util/site-config.ts
553
+ import { readFile as readFile3 } from "fs/promises";
554
+ import { join as join3 } from "path";
555
+ async function readSiteConfig(sitePath) {
556
+ let raw;
557
+ try {
558
+ raw = await readFile3(join3(sitePath, "package.json"), "utf-8");
559
+ } catch {
560
+ return {};
561
+ }
562
+ let pkg;
563
+ try {
564
+ pkg = JSON.parse(raw);
565
+ } catch {
566
+ return {};
567
+ }
568
+ if (!pkg || typeof pkg !== "object") return {};
569
+ const cfg = pkg.reddoor;
570
+ if (!cfg || typeof cfg !== "object") return {};
571
+ const out = {};
572
+ const url = cfg.lighthouseUrl;
573
+ if (typeof url === "string" && url.length > 0) {
574
+ out.lighthouseUrl = url;
575
+ }
576
+ return out;
577
+ }
578
+
552
579
  // src/audits/lighthouse.ts
553
580
  async function readJsonMaybe(path) {
554
581
  try {
555
- const raw = await readFile3(path, "utf-8");
582
+ const raw = await readFile4(path, "utf-8");
556
583
  return JSON.parse(raw);
557
584
  } catch {
558
585
  return null;
@@ -588,15 +615,28 @@ async function lighthouseAudit(ctx) {
588
615
  const spawn2 = ctx.spawn ?? defaultSpawn;
589
616
  const site = ctx.site;
590
617
  const label = siteLabel(site);
591
- const configDir = await mkdtemp(join3(tmpdir(), "reddoor-lhci-"));
592
- const configPath = join3(configDir, "lighthouserc.json");
593
- await writeFile(configPath, JSON.stringify(lighthouseConfig), "utf-8");
594
- const resultsDir = join3(site.path, ".lighthouseci");
618
+ const siteCfg = await readSiteConfig(site.path);
619
+ const resolvedConfig = siteCfg.lighthouseUrl ? {
620
+ ...lighthouseConfig,
621
+ ci: {
622
+ ...lighthouseConfig.ci,
623
+ collect: { ...lighthouseConfig.ci.collect, url: [siteCfg.lighthouseUrl] }
624
+ }
625
+ } : lighthouseConfig;
626
+ const configDir = await mkdtemp(join4(tmpdir(), "reddoor-lhci-"));
627
+ const configPath = join4(configDir, "lighthouserc.json");
628
+ await writeFile(configPath, JSON.stringify(resolvedConfig), "utf-8");
629
+ const resultsDir = join4(site.path, ".lighthouseci");
595
630
  await rm(resultsDir, { recursive: true, force: true });
596
631
  let raw;
597
632
  try {
598
633
  raw = await spawn2("npx", ["--yes", "@lhci/cli", "autorun", `--config=${configPath}`], {
599
- cwd: site.path
634
+ cwd: site.path,
635
+ // lhci autorun boots the site's dev server, downloads Chrome on first
636
+ // use, and runs the audit — easily 2–3 min on a cold tree. The shared
637
+ // 30 s default in runAudits is fine for deps/lint/security but starves
638
+ // lhci.
639
+ timeoutMs: 5 * 6e4
600
640
  });
601
641
  } catch (err) {
602
642
  await rm(configDir, { recursive: true, force: true });
@@ -612,7 +652,7 @@ async function lighthouseAudit(ctx) {
612
652
  throw err;
613
653
  }
614
654
  await rm(configDir, { recursive: true, force: true });
615
- const manifest = await readJsonMaybe(join3(resultsDir, "manifest.json"));
655
+ const manifest = await readJsonMaybe(join4(resultsDir, "manifest.json"));
616
656
  if (!manifest || manifest.length === 0) {
617
657
  return {
618
658
  audit: "lighthouse",
@@ -621,7 +661,7 @@ async function lighthouseAudit(ctx) {
621
661
  summary: `lighthouse: no manifest written (exit ${raw.code})${raw.stderr ? ` \u2014 ${raw.stderr.slice(0, 200)}` : ""}`
622
662
  };
623
663
  }
624
- const assertionResults = await readJsonMaybe(join3(resultsDir, "assertion-results.json")) ?? [];
664
+ const assertionResults = await readJsonMaybe(join4(resultsDir, "assertion-results.json")) ?? [];
625
665
  const failed = assertionResults.filter((a) => !a.passed);
626
666
  const assertions = failed.map((a) => ({
627
667
  category: categoryFromAssertion(a),
@@ -647,9 +687,9 @@ async function lighthouseAudit(ctx) {
647
687
  }
648
688
 
649
689
  // src/audits/a11y.ts
650
- import { readFile as readFile4, writeFile as writeFile2, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
690
+ import { readFile as readFile5, writeFile as writeFile2, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
651
691
  import { tmpdir as tmpdir2 } from "os";
652
- import { join as join4 } from "path";
692
+ import { join as join5 } from "path";
653
693
 
654
694
  // src/configs/playwright-a11y.ts
655
695
  import { defineConfig, devices } from "@playwright/test";
@@ -687,7 +727,7 @@ var playwrightA11yConfig = defineConfig({
687
727
  var RESULTS_REL = ".reddoor-a11y/results.json";
688
728
  async function readJsonMaybe2(path) {
689
729
  try {
690
- const raw = await readFile4(path, "utf-8");
730
+ const raw = await readFile5(path, "utf-8");
691
731
  return JSON.parse(raw);
692
732
  } catch {
693
733
  return null;
@@ -743,11 +783,11 @@ async function a11yAudit(ctx) {
743
783
  const spawn2 = ctx.spawn ?? defaultSpawn;
744
784
  const site = ctx.site;
745
785
  const label = siteLabel(site);
746
- const specDir = await mkdtemp2(join4(tmpdir2(), "reddoor-a11y-spec-"));
747
- const specPath = join4(specDir, "a11y.spec.ts");
786
+ const specDir = await mkdtemp2(join5(tmpdir2(), "reddoor-a11y-spec-"));
787
+ const specPath = join5(specDir, "a11y.spec.ts");
748
788
  await writeFile2(specPath, buildSpec(), "utf-8");
749
- const resultsPath = join4(site.path, RESULTS_REL);
750
- await rm2(join4(site.path, ".reddoor-a11y"), { recursive: true, force: true });
789
+ const resultsPath = join5(site.path, RESULTS_REL);
790
+ await rm2(join5(site.path, ".reddoor-a11y"), { recursive: true, force: true });
751
791
  let raw;
752
792
  try {
753
793
  raw = await spawn2("npx", ["--yes", "playwright", "test", "--reporter=line", specPath], {
@@ -840,7 +880,7 @@ function localPath(path, opts = {}) {
840
880
  }
841
881
 
842
882
  // src/inventory/json.ts
843
- import { readFile as readFile5 } from "fs/promises";
883
+ import { readFile as readFile6 } from "fs/promises";
844
884
  import { isAbsolute } from "path";
845
885
  function validate(raw) {
846
886
  if (!Array.isArray(raw)) {
@@ -870,7 +910,7 @@ function validate(raw) {
870
910
  }
871
911
  function fromJsonFile(path) {
872
912
  return async () => {
873
- const raw = JSON.parse(await readFile5(path, "utf-8"));
913
+ const raw = JSON.parse(await readFile6(path, "utf-8"));
874
914
  return validate(raw);
875
915
  };
876
916
  }
@@ -916,7 +956,7 @@ async function resolveSites(input) {
916
956
 
917
957
  // src/cli/fleet/clone-if-needed.ts
918
958
  import { stat, readdir, mkdir } from "fs/promises";
919
- import { isAbsolute as isAbsolute2, join as join5 } from "path";
959
+ import { isAbsolute as isAbsolute2, join as join6 } from "path";
920
960
  function deriveNameFromRepoUrl(repoUrl) {
921
961
  const slash = repoUrl.split("/").pop() ?? repoUrl;
922
962
  return slash.replace(/\.git$/, "");
@@ -957,7 +997,7 @@ async function cloneIfNeeded(site, opts) {
957
997
  const name = site.name ?? deriveNameFromRepoUrl(site.repoUrl);
958
998
  assertSafeName(name);
959
999
  assertSafeRepoUrl(site.repoUrl);
960
- const target = join5(opts.workdir, name);
1000
+ const target = join6(opts.workdir, name);
961
1001
  await mkdir(opts.workdir, { recursive: true });
962
1002
  if (await isNonEmptyDir(target)) {
963
1003
  return { ...site, name, path: target };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/reports/airtable/client.ts","../../../src/reports/airtable/websites.ts","../../../src/inventory/airtable.ts","../../../src/audits/lighthouse-airtable.ts","../../../src/cli/commands/audit.ts","../../../src/audits/util/spawn.ts","../../../src/audits/deps.ts","../../../src/util/site.ts","../../../src/configs/baseline-versions.ts","../../../src/audits/lint.ts","../../../src/audits/security.ts","../../../src/audits/lighthouse.ts","../../../src/configs/lighthouse.ts","../../../src/audits/a11y.ts","../../../src/configs/playwright-a11y.ts","../../../src/audits/index.ts","../../../src/cli/fleet/resolve-sites.ts","../../../src/inventory/local.ts","../../../src/inventory/json.ts","../../../src/cli/fleet/clone-if-needed.ts"],"sourcesContent":["import Airtable from \"airtable\";\n\nexport type AirtableConfig = {\n apiKey: string;\n baseId: string;\n};\n\nexport function readAirtableConfig(): AirtableConfig {\n const apiKey = process.env.AIRTABLE_PAT;\n const baseId = process.env.AIRTABLE_BASE_ID;\n if (!apiKey) throw Object.assign(new Error(\"AIRTABLE_PAT not set\"), { exitCode: 2 });\n if (!baseId) throw Object.assign(new Error(\"AIRTABLE_BASE_ID not set\"), { exitCode: 2 });\n return { apiKey, baseId };\n}\n\nexport type AirtableBase = ReturnType<typeof openBase>;\n\nexport function openBase(cfg: AirtableConfig) {\n return new Airtable({ apiKey: cfg.apiKey }).base(cfg.baseId);\n}\n","import type { FieldSet } from \"airtable\";\nimport type { AirtableBase } from \"./client.js\";\nimport type { LighthouseScores } from \"../types.js\";\n\nexport const WEBSITES_TABLE = \"Websites\";\n\nexport type Frequency = \"None\" | \"Monthly\" | \"Quarterly\" | \"Yearly\";\n\nexport type Status =\n | \"in development\"\n | \"launch period\"\n | \"maintenance\"\n | \"hosting\"\n | \"probably not our problem\"\n | \"deprecated\";\n\nexport type WebsiteRow = {\n id: string;\n name: string;\n url: string;\n status: Status | null;\n pointOfContact: string | null;\n maintenanceFreq: Frequency;\n testingFreq: Frequency;\n /** Last manually-recorded maintenance day (used as fallback when no Reports row exists). */\n maintenanceDay: string | null;\n testingDay: string | null;\n ga4PropertyId: string | null;\n reportRecipientsTo: string | null;\n reportRecipientsCc: string | null;\n /** First attachment in the Header image field (Airtable's signed URL — fetch before expiry). */\n headerImage: { url: string; filename: string; type: string } | null;\n /** Lighthouse \"current state\" snapshot, kept fresh by `audit lighthouse --write-airtable`. */\n pScore: number | null;\n rScore: number | null;\n bpScore: number | null;\n seoScore: number | null;\n /** ISO timestamp set by `audit lighthouse --write-airtable` when scores were last refreshed. */\n lastLighthouseAuditAt: string | null;\n};\n\nexport function siteSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction mapRow(rec: { id: string; fields: Record<string, unknown> }): WebsiteRow {\n const f = rec.fields;\n const attachments =\n (f[\"Header image\"] as Array<{ url: string; filename: string; type: string }> | undefined) ?? [];\n const header = attachments[0] ?? null;\n return {\n id: rec.id,\n name: String(f[\"Name\"] ?? \"\"),\n url: String(f[\"url\"] ?? \"\"),\n status: (f[\"Status\"] as Status | undefined) ?? null,\n pointOfContact: (f[\"point of contact\"] as string | undefined) ?? null,\n maintenanceFreq: ((f[\"maintenence freq\"] as string | undefined) ?? \"None\") as Frequency,\n testingFreq: ((f[\"testing freq\"] as string | undefined) ?? \"None\") as Frequency,\n maintenanceDay: (f[\"maintenance day\"] as string | undefined) ?? null,\n testingDay: (f[\"testing day\"] as string | undefined) ?? null,\n ga4PropertyId: (f[\"GA4 property ID\"] as string | undefined) ?? null,\n reportRecipientsTo: (f[\"Report recipients (To)\"] as string | undefined) ?? null,\n reportRecipientsCc: (f[\"Report recipients (CC)\"] as string | undefined) ?? null,\n headerImage: header,\n pScore: (f[\"pScore\"] as number | undefined) ?? null,\n rScore: (f[\"rScore\"] as number | undefined) ?? null,\n bpScore: (f[\"bpScore\"] as number | undefined) ?? null,\n seoScore: (f[\"seoScore\"] as number | undefined) ?? null,\n lastLighthouseAuditAt: (f[\"Last lighthouse audit at\"] as string | undefined) ?? null,\n };\n}\n\nexport async function listWebsites(base: AirtableBase): Promise<WebsiteRow[]> {\n const out: WebsiteRow[] = [];\n await base(WEBSITES_TABLE)\n .select({ pageSize: 100 })\n .eachPage((records, fetchNextPage) => {\n for (const rec of records) out.push(mapRow({ id: rec.id, fields: rec.fields }));\n fetchNextPage();\n });\n return out;\n}\n\nexport async function getWebsiteBySlug(\n base: AirtableBase,\n slug: string,\n): Promise<WebsiteRow | null> {\n const all = await listWebsites(base);\n return all.find((w) => siteSlug(w.name) === slug) ?? null;\n}\n\n/**\n * Write the four Lighthouse scores + a refreshed-at timestamp onto a Websites row.\n * Called by `audit lighthouse --write-airtable` after a successful audit run, so\n * the operator never has to paste numbers manually before drafting a report.\n */\nexport async function updateScores(\n base: AirtableBase,\n recordId: string,\n scores: LighthouseScores,\n): Promise<void> {\n const fields: FieldSet = {\n pScore: scores.performance,\n rScore: scores.accessibility,\n bpScore: scores.bestPractices,\n seoScore: scores.seo,\n \"Last lighthouse audit at\": new Date().toISOString(),\n };\n await base(WEBSITES_TABLE).update([{ id: recordId, fields }]);\n}\n","import type { Site, InventoryProvider } from \"../types.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { listWebsites, siteSlug } from \"../reports/airtable/websites.js\";\n\nexport type AirtableInventoryOptions = {\n /**\n * Local workdir to compute each site's path as `{workdir}/{slug}`.\n * Defaults to REDDOOR_FLEET_WORKDIR env var if not provided.\n * Airtable doesn't store local checkout paths, so this is required.\n */\n workdir?: string;\n};\n\n/**\n * Read sites from the Airtable Websites table as an InventoryProvider.\n * Each row becomes one Site; `path` is computed as `{workdir}/{slug}`.\n * Sites where BOTH maintenance freq AND testing freq are \"None\" are excluded\n * (they're inactive — no scheduled audits or reports).\n *\n * Note: `repoUrl` is set to the production URL (Websites.url). For sites\n * cloned via `--workdir` semantics this is wrong — the convention should be\n * tightened (e.g. add a `repo` field to Websites) when fleet-clone-from-\n * airtable becomes a real flow. For local audits where the site is already\n * checked out at `path`, the `repoUrl` is unused.\n */\nexport function fromAirtableBase(\n base: AirtableBase,\n opts: AirtableInventoryOptions = {},\n): InventoryProvider {\n return async (): Promise<Site[]> => {\n const workdir = opts.workdir ?? process.env.REDDOOR_FLEET_WORKDIR;\n if (!workdir) {\n throw new Error(\n \"fromAirtableBase requires `workdir` option or REDDOOR_FLEET_WORKDIR env (sites need a local path)\",\n );\n }\n const websites = await listWebsites(base);\n return websites\n .filter((w) => w.maintenanceFreq !== \"None\" || w.testingFreq !== \"None\")\n .map((w) => {\n const slug = siteSlug(w.name);\n const site: Site = {\n path: `${workdir}/${slug}`,\n name: slug,\n meta: { airtableRowId: w.id, displayName: w.name },\n };\n if (w.url) site.repoUrl = w.url;\n return site;\n });\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport type { LighthouseScores } from \"../reports/types.js\";\nimport { siteSlug } from \"../reports/airtable/websites.js\";\n\n/**\n * Extract the four Lighthouse scores (as integer percentages) from a\n * `lighthouse` AuditResult. LHCI manifest summaries are floats in [0,1];\n * multiply + round.\n */\nexport function lighthouseScoresFromResult(result: AuditResult): LighthouseScores {\n if (result.audit !== \"lighthouse\") {\n throw new Error(`Expected a 'lighthouse' AuditResult, got '${result.audit}'`);\n }\n const details = (result.details ?? {}) as { summary?: Record<string, number> };\n const summary = details.summary ?? {};\n const toPct = (n: number | undefined) =>\n typeof n === \"number\" && !Number.isNaN(n) ? Math.round(n * 100) : 0;\n return {\n performance: toPct(summary[\"performance\"]),\n accessibility: toPct(summary[\"accessibility\"]),\n bestPractices: toPct(summary[\"best-practices\"]),\n seo: toPct(summary[\"seo\"]),\n };\n}\n\n/**\n * Derive a site slug from the cwd's package.json#name. Used by\n * `audit lighthouse --write-airtable` when the operator doesn't pass an\n * explicit slug — the cwd is the site checkout, so package.json#name is\n * usually the canonical site name.\n */\nexport async function resolveSlugFromCwd(cwd: string): Promise<string> {\n try {\n const pkgPath = join(cwd, \"package.json\");\n const raw = await readFile(pkgPath, \"utf-8\");\n const pkg = JSON.parse(raw) as { name?: string };\n if (!pkg.name) throw new Error(\"package.json has no 'name' field\");\n return siteSlug(pkg.name);\n } catch (e) {\n throw new Error(\n `Could not derive site slug from ${cwd}/package.json: ${(e as Error).message}. ` +\n `Pass --write-airtable=<slug> explicitly.`,\n { cause: e },\n );\n }\n}\n","import { resolve } from \"node:path\";\nimport { runAuditsAcross, ALL_AUDIT_NAMES } from \"../../audits/index.js\";\nimport type { AuditName, AuditResult } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { cloneIfNeeded } from \"../fleet/clone-if-needed.js\";\n\nexport type AuditCommandOptions = {\n only?: string;\n json?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n /**\n * After running, push the lighthouse scores to the matching Websites row\n * in Airtable. `true` (no value) = derive slug from cwd/package.json#name;\n * string = explicit slug (e.g. \"med-solutions-of-texas\").\n */\n writeAirtable?: string | boolean;\n};\n\nfunction parseOnly(value: string | undefined): AuditName[] | undefined {\n if (!value) return undefined;\n const names = value.split(\",\").map((s) => s.trim());\n for (const n of names) {\n if (!ALL_AUDIT_NAMES.includes(n as AuditName)) {\n throw Object.assign(new Error(`unknown audit in --only: ${n}`), { exitCode: 2 });\n }\n }\n return names as AuditName[];\n}\n\nfunction formatTable(results: AuditResult[]): string {\n return results\n .map((r) => `${r.audit.padEnd(12)} ${r.status.padEnd(5)} ${r.site}\\n ${r.summary}`)\n .join(\"\\n\");\n}\n\nfunction exitCode(results: AuditResult[]): number {\n return results.some((r) => r.status === \"fail\") ? 1 : 0;\n}\n\nexport async function runAuditCommand(\n site: string | undefined,\n opts: AuditCommandOptions,\n): Promise<{ output: string; code: number }> {\n const which = parseOnly(opts.only);\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n\n let sites = await resolveSites({\n ...(site !== undefined ? { site } : {}),\n ...(opts.fleet !== undefined ? { fleet: opts.fleet } : {}),\n ...(opts.workdir !== undefined ? { workdir: opts.workdir } : {}),\n cwd,\n });\n\n if (opts.fleet) {\n const workdir = opts.workdir ?? `${process.env.HOME ?? \"\"}/.reddoor-maint/sites`;\n sites = await Promise.all(sites.map((s) => cloneIfNeeded(s, { workdir })));\n }\n\n // Run sites in parallel via runAuditsAcross — for a fleet of 30 this is the\n // difference between minutes and ~max(per-site) seconds.\n const results: AuditResult[] = await runAuditsAcross(sites, which);\n\n let output = opts.json ? JSON.stringify(results, null, 2) : formatTable(results);\n\n if (opts.writeAirtable !== undefined) {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const { listWebsites, updateScores, siteSlug } =\n await import(\"../../reports/airtable/websites.js\");\n const { lighthouseScoresFromResult, resolveSlugFromCwd } =\n await import(\"../../audits/lighthouse-airtable.js\");\n\n const slug =\n typeof opts.writeAirtable === \"string\" && opts.writeAirtable.length > 0\n ? opts.writeAirtable\n : await resolveSlugFromCwd(cwd);\n\n const lhResult = results.find((r) => r.audit === \"lighthouse\");\n if (!lhResult) {\n throw Object.assign(\n new Error(\n \"--write-airtable requires a lighthouse result; did you pass --only without lighthouse?\",\n ),\n { exitCode: 2 },\n );\n }\n if (lhResult.status === \"fail\") {\n throw Object.assign(\n new Error(\n `Lighthouse audit failed; refusing to write scores to Airtable. Summary: ${lhResult.summary}`,\n ),\n { exitCode: 1 },\n );\n }\n\n const base = openBase(readAirtableConfig());\n const websites = await listWebsites(base);\n const target = websites.find((w) => siteSlug(w.name) === slug);\n if (!target) {\n throw Object.assign(new Error(`No Websites row matched slug \"${slug}\"`), { exitCode: 2 });\n }\n\n const scores = lighthouseScoresFromResult(lhResult);\n await updateScores(base, target.id, scores);\n output += `\\n\\n→ wrote scores to Websites[${target.name}]: P=${scores.performance} A=${scores.accessibility} BP=${scores.bestPractices} SEO=${scores.seo}`;\n }\n\n return { output, code: exitCode(results) };\n}\n","import { spawn } from \"node:child_process\";\n\nexport type SpawnResult = { code: number; stdout: string; stderr: string };\n\nexport type SpawnOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n timeoutMs?: number;\n /** When true, the child inherits stdout/stderr so the user sees live\n * progress (useful for long-running `pnpm up` / `npm install`). The\n * returned `stdout` and `stderr` will be empty strings in that case. */\n streaming?: boolean;\n};\n\nexport type SpawnFn = (\n cmd: string,\n args: readonly string[],\n opts?: SpawnOptions,\n) => Promise<SpawnResult>;\n\nexport const defaultSpawn: SpawnFn = (cmd, args, opts = {}) =>\n new Promise((resolve, reject) => {\n const streaming = opts.streaming === true;\n const child = spawn(cmd, [...args], {\n cwd: opts.cwd,\n env: opts.env ?? process.env,\n stdio: streaming ? [\"ignore\", \"inherit\", \"inherit\"] : [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n if (!streaming) {\n child.stdout?.on(\"data\", (chunk) => (stdout += String(chunk)));\n child.stderr?.on(\"data\", (chunk) => (stderr += String(chunk)));\n }\n\n const timer = opts.timeoutMs\n ? setTimeout(() => {\n child.kill(\"SIGTERM\");\n reject(new Error(`spawn timeout after ${opts.timeoutMs}ms: ${cmd}`));\n }, opts.timeoutMs)\n : undefined;\n\n child.on(\"error\", (err) => {\n if (timer) clearTimeout(timer);\n reject(err);\n });\n child.on(\"close\", (code) => {\n if (timer) clearTimeout(timer);\n resolve({ code: code ?? -1, stdout, stderr });\n });\n });\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { baselineVersions } from \"../configs/baseline-versions.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\nexport type Drift = \"same\" | \"patch\" | \"minor\" | \"major\" | \"newer\";\n\nexport type DepsDriftEntry = {\n pkg: string;\n baseline: string;\n actual: string;\n drift: Drift;\n};\n\nfunction stripCaret(range: string): string {\n return range.replace(/^[\\^~]/, \"\");\n}\n\nfunction parseSemver(v: string): [number, number, number] {\n const cleaned = stripCaret(v).split(\"-\")[0] ?? \"0.0.0\";\n const parts = cleaned.split(\".\").map((n) => Number.parseInt(n, 10));\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\nfunction compareSemver(actual: string, baseline: string): Drift {\n const [aMajor, aMinor, aPatch] = parseSemver(actual);\n const [bMajor, bMinor, bPatch] = parseSemver(baseline);\n if (aMajor > bMajor) return \"newer\";\n if (aMajor < bMajor) return \"major\";\n if (aMinor > bMinor) return \"newer\";\n if (aMinor < bMinor) return \"minor\";\n if (aPatch > bPatch) return \"newer\";\n if (aPatch < bPatch) return \"patch\";\n return \"same\";\n}\n\nexport async function depsAudit(ctx: AuditContext): Promise<AuditResult> {\n const pkgPath = join(ctx.site.path, \"package.json\");\n let pkgRaw: string;\n try {\n pkgRaw = await readFile(pkgPath, \"utf-8\");\n } catch (err) {\n return {\n audit: \"deps\",\n site: siteLabel(ctx.site),\n status: \"skip\",\n summary: `no package.json at ${pkgPath}`,\n details: { error: String(err) },\n };\n }\n\n const pkg = JSON.parse(pkgRaw) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const installed: Record<string, string> = {\n ...(pkg.dependencies ?? {}),\n ...(pkg.devDependencies ?? {}),\n };\n\n const details: DepsDriftEntry[] = [];\n for (const [name, baseline] of Object.entries(baselineVersions)) {\n const actual = installed[name];\n if (!actual) continue;\n details.push({\n pkg: name,\n baseline,\n actual,\n drift: compareSemver(actual, baseline),\n });\n }\n\n const anyMajor = details.some((d) => d.drift === \"major\");\n const anyMinor = details.some((d) => d.drift === \"minor\");\n const anyNewer = details.some((d) => d.drift === \"newer\");\n\n const status: AuditResult[\"status\"] = anyMajor ? \"fail\" : anyMinor || anyNewer ? \"warn\" : \"pass\";\n\n const summary =\n status === \"pass\"\n ? `all ${details.length} tracked deps in line with baseline`\n : status === \"warn\"\n ? `${details.filter((d) => d.drift !== \"same\").length} of ${details.length} tracked deps drifted`\n : `${details.filter((d) => d.drift === \"major\").length} deps lagging by a major version`;\n\n return {\n audit: \"deps\",\n site: siteLabel(ctx.site),\n status,\n summary,\n details,\n };\n}\n","import type { Site } from \"../types.js\";\n\n/** Human-friendly label for log/output formatting. Prefer the inventory's\n * `name` when present (e.g. \"caltex-landing\") and fall back to the\n * filesystem `path` when unnamed. Every audit + recipe uses this. */\nexport function siteLabel(site: Site): string {\n return site.name ?? site.path;\n}\n","// Curated map of the framework deps reddoor sites should stay close to.\n// Refreshed at each package release from reddoor-starter's package.json.\n// Versions are caret ranges to mirror what `pnpm add` would produce.\n\nexport const baselineVersions: Record<string, string> = {\n // SvelteKit core\n svelte: \"^5.55.5\",\n \"@sveltejs/kit\": \"^2.59.0\",\n \"@sveltejs/adapter-netlify\": \"^6.0.4\",\n \"@sveltejs/adapter-auto\": \"^7.0.0\",\n \"@sveltejs/vite-plugin-svelte\": \"^7.0.0\",\n \"svelte-check\": \"^4.4.7\",\n\n // Build tooling\n vite: \"^8.0.10\",\n vitest: \"^4.1.1\",\n typescript: \"^6.0.3\",\n\n // Tailwind 4\n tailwindcss: \"^4.0.14\",\n \"@tailwindcss/vite\": \"^4.3.0\",\n\n // Prismic\n \"@prismicio/client\": \"^7.3.1\",\n \"@prismicio/svelte\": \"^2.0.0\",\n \"@slicemachine/adapter-sveltekit\": \"^0.3.36\",\n \"slice-machine-ui\": \"^2.11.1\",\n\n // Test tooling\n \"@playwright/test\": \"^1.59.1\",\n \"@axe-core/playwright\": \"^4.11.3\",\n \"@lhci/cli\": \"^0.15.1\",\n\n // Lint\n eslint: \"^10.3.0\",\n \"eslint-plugin-svelte\": \"^3.1.0\",\n \"eslint-config-prettier\": \"^10.1.1\",\n prettier: \"^3.1.1\",\n \"prettier-plugin-svelte\": \"^3.2.6\",\n \"typescript-eslint\": \"^8.59.1\",\n \"@eslint/js\": \"^10.0.1\",\n globals: \"^17.6.0\",\n\n // Misc\n \"@lucide/svelte\": \"^1.14.0\",\n \"@zerodevx/svelte-img\": \"^2.1.2\",\n};\n\nexport default baselineVersions;\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { ESLint } from \"eslint\";\nimport { check as prettierCheck, resolveConfig as prettierResolveConfig } from \"prettier\";\nimport { glob } from \"tinyglobby\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\nconst TARGET_GLOBS = [\"**/*.{ts,js,svelte}\"];\nconst IGNORE = [\"node_modules/**\", \"dist/**\", \".svelte-kit/**\", \"build/**\", \".netlify/**\"];\n\nasync function listFiles(cwd: string): Promise<string[]> {\n return glob(TARGET_GLOBS, { cwd, ignore: IGNORE, absolute: false });\n}\n\nexport async function lintAudit(ctx: AuditContext): Promise<AuditResult> {\n const { site } = ctx;\n const configPath = join(site.path, \"eslint.config.js\");\n\n if (!existsSync(configPath)) {\n return {\n audit: \"lint\",\n site: siteLabel(site),\n status: \"skip\",\n summary: \"no eslint config at site root\",\n };\n }\n\n const eslint = new ESLint({\n cwd: site.path,\n overrideConfigFile: configPath,\n errorOnUnmatchedPattern: false,\n });\n\n const relFiles = await listFiles(site.path);\n\n // Pass relative paths to ESLint; its cwd is already site.path. Avoids\n // dereferencing symlinks on pnpm workspaces.\n const eslintResults = await eslint.lintFiles(relFiles);\n const eslintErrors = eslintResults.reduce((n, r) => n + r.errorCount, 0);\n const eslintWarnings = eslintResults.reduce((n, r) => n + r.warningCount, 0);\n\n const prettierUnformatted: string[] = [];\n for (const rel of relFiles) {\n const absForResolve = join(site.path, rel);\n const source = await readFile(absForResolve, \"utf-8\");\n const options = (await prettierResolveConfig(absForResolve)) ?? {};\n const ok = await prettierCheck(source, { ...options, filepath: absForResolve });\n if (!ok) prettierUnformatted.push(rel);\n }\n\n const status: AuditResult[\"status\"] =\n eslintErrors > 0 || prettierUnformatted.length > 0\n ? \"fail\"\n : eslintWarnings > 0\n ? \"warn\"\n : \"pass\";\n\n const summary =\n status === \"pass\"\n ? `lint clean across ${relFiles.length} files`\n : `${eslintErrors} eslint errors, ${eslintWarnings} warnings, ${prettierUnformatted.length} unformatted`;\n\n return {\n audit: \"lint\",\n site: siteLabel(site),\n status,\n summary,\n details: {\n eslintErrors,\n eslintWarnings,\n prettierUnformatted,\n files: relFiles.length,\n },\n };\n}\n","import type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { defaultSpawn, type SpawnResult } from \"./util/spawn.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\ntype Severity = \"low\" | \"moderate\" | \"high\" | \"critical\";\n\ntype Counts = { low: number; moderate: number; high: number; critical: number };\n\ntype AdvisoryEntry = {\n module: string;\n severity: Severity;\n title: string;\n cves?: string[];\n url?: string;\n};\n\n// pnpm audit output (npm-compat with extra advisories map keyed by ID).\ntype PnpmAuditJson = {\n metadata?: { vulnerabilities?: Partial<Counts> };\n advisories?: Record<\n string,\n {\n id?: number;\n title?: string;\n module_name?: string;\n severity?: string;\n cves?: string[];\n url?: string;\n }\n >;\n};\n\n// npm v7+ shape (vulnerabilities keyed by package name).\ntype NpmAuditJson = {\n metadata?: { vulnerabilities?: Partial<Counts> };\n vulnerabilities?: Record<\n string,\n {\n name?: string;\n severity?: string;\n via?: unknown;\n url?: string;\n }\n >;\n};\n\nfunction classify(v: Counts) {\n if (v.critical > 0 || v.high > 0) return \"fail\" as const;\n if (v.moderate > 0 || v.low > 0) return \"warn\" as const;\n return \"pass\" as const;\n}\n\nfunction normalizeSeverity(s: unknown): Severity {\n if (s === \"low\" || s === \"moderate\" || s === \"high\" || s === \"critical\") return s;\n // npm/pnpm sometimes emit \"info\" for informational advisories. Map down\n // rather than defaulting to \"moderate\" (which would inflate severity).\n return \"low\";\n}\n\nfunction extractAdvisoriesFromPnpm(parsed: PnpmAuditJson): AdvisoryEntry[] {\n const out: AdvisoryEntry[] = [];\n for (const a of Object.values(parsed.advisories ?? {})) {\n if (!a) continue;\n out.push({\n module: a.module_name ?? \"unknown\",\n severity: normalizeSeverity(a.severity),\n title: a.title ?? \"(no title)\",\n ...(a.cves ? { cves: a.cves } : {}),\n ...(a.url ? { url: a.url } : {}),\n });\n }\n return out;\n}\n\n/** Walk an npm v7+ `via` chain to find the root entry whose `via` array\n * contains a real advisory object (rather than another package name string).\n * Returns the package name at the root and the advisory detail. */\nfunction resolveNpmAdvisoryRoot(\n startName: string,\n vulnerabilities: NonNullable<NpmAuditJson[\"vulnerabilities\"]>,\n): { rootName: string; detail?: { title?: string; url?: string } } {\n const seen = new Set<string>();\n let current = startName;\n while (!seen.has(current)) {\n seen.add(current);\n const entry = vulnerabilities[current];\n if (!entry || !Array.isArray(entry.via)) return { rootName: current };\n\n const detailed = entry.via.find(\n (e): e is { title?: string; url?: string } => typeof e === \"object\" && e !== null,\n );\n if (detailed) return { rootName: current, detail: detailed };\n\n const next = entry.via.find((e): e is string => typeof e === \"string\");\n if (!next || next === current) return { rootName: current };\n current = next;\n }\n return { rootName: current };\n}\n\nfunction extractAdvisoriesFromNpm(parsed: NpmAuditJson): AdvisoryEntry[] {\n const vulnerabilities = parsed.vulnerabilities ?? {};\n const roots = new Map<string, AdvisoryEntry>();\n\n for (const [name, v] of Object.entries(vulnerabilities)) {\n if (!v) continue;\n const { rootName, detail } = resolveNpmAdvisoryRoot(name, vulnerabilities);\n if (roots.has(rootName)) continue; // already surfaced via another transitive entry\n\n const rootEntry = vulnerabilities[rootName];\n const severity = normalizeSeverity(rootEntry?.severity ?? v.severity);\n const title = detail?.title ?? rootName;\n const url = detail?.url;\n\n roots.set(rootName, {\n module: rootEntry?.name ?? rootName,\n severity,\n title,\n ...(url ? { url } : {}),\n });\n }\n\n return [...roots.values()];\n}\n\ntype ToolResult =\n | { kind: \"missing\" }\n | { kind: \"error\"; reason: string }\n | { kind: \"ok\"; parsed: PnpmAuditJson & NpmAuditJson };\n\nasync function runAuditTool(\n spawn: (cmd: string, args: readonly string[], opts?: { cwd?: string }) => Promise<SpawnResult>,\n cmd: string,\n args: readonly string[],\n cwd: string,\n): Promise<ToolResult> {\n let raw: SpawnResult;\n try {\n raw = await spawn(cmd, args, { cwd });\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) return { kind: \"missing\" };\n return { kind: \"error\", reason: `spawn failed: ${String(err).slice(0, 200)}` };\n }\n\n // 0 = clean, 1 = vulns found. Anything else is a real error.\n if (raw.code !== 0 && raw.code !== 1) {\n return {\n kind: \"error\",\n reason: `exit ${raw.code}${raw.stderr ? `: ${raw.stderr.slice(0, 150)}` : \"\"}`,\n };\n }\n\n let parsed: PnpmAuditJson & NpmAuditJson;\n try {\n parsed = JSON.parse(raw.stdout || \"{}\") as PnpmAuditJson & NpmAuditJson;\n } catch (err) {\n return { kind: \"error\", reason: `unparseable JSON: ${String(err).slice(0, 100)}` };\n }\n\n // pnpm error envelope: { error: { code, message } }. npm sometimes emits\n // a top-level error too. Either means the audit didn't actually run.\n const errEnvelope = (parsed as unknown as { error?: { code?: string } }).error;\n if (errEnvelope && typeof errEnvelope === \"object\") {\n return { kind: \"error\", reason: errEnvelope.code ?? \"error envelope returned\" };\n }\n\n // Without metadata.vulnerabilities there are no counts to report and we\n // can't trust the result. An empty `{}` is just as suspect as a missing\n // key — counts default to 0 and we'd silently report \"pass\". Treat both\n // as a tool failure so the caller can fall through to the other audit.\n const vulnsMeta = parsed.metadata?.vulnerabilities;\n if (!vulnsMeta || Object.keys(vulnsMeta).length === 0) {\n return { kind: \"error\", reason: \"no metadata.vulnerabilities in output\" };\n }\n\n return { kind: \"ok\", parsed };\n}\n\nexport async function securityAudit(ctx: AuditContext): Promise<AuditResult> {\n const spawn = ctx.spawn ?? defaultSpawn;\n const site = ctx.site;\n const label = siteLabel(site);\n\n let used: \"pnpm audit\" | \"npm audit\" = \"pnpm audit\";\n let result = await runAuditTool(spawn, \"pnpm\", [\"audit\", \"--json\", \"--prod\"], site.path);\n\n // Fall through to npm if pnpm is missing OR pnpm couldn't actually\n // audit the project (e.g., no pnpm-lock.yaml). Previously we only fell\n // through on ENOENT, which meant npm-using sites silently reported \"pass\"\n // because pnpm returned an error envelope with no metadata.\n if (result.kind !== \"ok\") {\n const pnpmReason = result.kind === \"missing\" ? \"not installed\" : result.reason;\n const npmResult = await runAuditTool(\n spawn,\n \"npm\",\n [\"audit\", \"--json\", \"--omit=dev\"],\n site.path,\n );\n if (npmResult.kind === \"ok\") {\n result = npmResult;\n used = \"npm audit\";\n } else {\n const npmReason = npmResult.kind === \"missing\" ? \"not installed\" : npmResult.reason;\n return {\n audit: \"security\",\n site: label,\n status: \"skip\",\n summary: `cannot run audit — pnpm: ${pnpmReason}; npm: ${npmReason}`,\n };\n }\n }\n\n const parsed = result.parsed;\n\n const counts: Counts = {\n low: parsed.metadata?.vulnerabilities?.low ?? 0,\n moderate: parsed.metadata?.vulnerabilities?.moderate ?? 0,\n high: parsed.metadata?.vulnerabilities?.high ?? 0,\n critical: parsed.metadata?.vulnerabilities?.critical ?? 0,\n };\n\n const advisories =\n used === \"pnpm audit\" ? extractAdvisoriesFromPnpm(parsed) : extractAdvisoriesFromNpm(parsed);\n\n const status = classify(counts);\n const total = counts.low + counts.moderate + counts.high + counts.critical;\n const summary =\n status === \"pass\"\n ? `${used}: 0 vulnerabilities`\n : `${used}: ${total} vulnerabilities (${counts.critical}C/${counts.high}H/${counts.moderate}M/${counts.low}L)`;\n\n return {\n audit: \"security\",\n site: label,\n status,\n summary,\n details: { counts, advisories },\n };\n}\n","import { readFile, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { lighthouseConfig } from \"../configs/lighthouse.js\";\nimport { defaultSpawn } from \"./util/spawn.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\ntype ManifestEntry = {\n url: string;\n summary: Record<string, number>;\n htmlPath?: string;\n jsonPath?: string;\n};\n\ntype AssertionResult = {\n name: string;\n actual: number;\n expected: number;\n operator: string;\n passed: boolean;\n level: \"warn\" | \"error\";\n auditProperty?: string;\n auditId?: string;\n};\n\ntype NormalizedLhciResult = {\n summary: Record<string, number>;\n assertionsFailed: number;\n assertions: Array<{ category: string; level: \"warn\" | \"error\"; message: string }>;\n};\n\nasync function readJsonMaybe<T>(path: string): Promise<T | null> {\n try {\n const raw = await readFile(path, \"utf-8\");\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n\nfunction averageSummaries(entries: ManifestEntry[]): Record<string, number> {\n if (entries.length === 0) return {};\n const sums: Record<string, number> = {};\n const counts: Record<string, number> = {};\n for (const e of entries) {\n for (const [k, v] of Object.entries(e.summary ?? {})) {\n if (typeof v !== \"number\") continue;\n sums[k] = (sums[k] ?? 0) + v;\n counts[k] = (counts[k] ?? 0) + 1;\n }\n }\n const out: Record<string, number> = {};\n for (const k of Object.keys(sums)) {\n const total = sums[k] ?? 0;\n const count = counts[k] ?? 1;\n out[k] = total / count;\n }\n return out;\n}\n\nfunction categoryFromAssertion(a: AssertionResult): string {\n // `name` looks like \"categories:accessibility\" or \"audits:uses-http2\".\n const colonIdx = a.name.indexOf(\":\");\n return colonIdx >= 0 ? a.name.slice(colonIdx + 1) : a.name;\n}\n\nfunction messageForAssertion(a: AssertionResult): string {\n return `${a.name} ${a.operator} ${a.expected} (actual: ${a.actual.toFixed(2)})`;\n}\n\nexport async function lighthouseAudit(ctx: AuditContext): Promise<AuditResult> {\n const spawn = ctx.spawn ?? defaultSpawn;\n const site = ctx.site;\n const label = siteLabel(site);\n\n const configDir = await mkdtemp(join(tmpdir(), \"reddoor-lhci-\"));\n const configPath = join(configDir, \"lighthouserc.json\");\n await writeFile(configPath, JSON.stringify(lighthouseConfig), \"utf-8\");\n\n const resultsDir = join(site.path, \".lighthouseci\");\n // Clear any stale artifacts before the run so we never confuse a failed\n // spawn with old results.\n await rm(resultsDir, { recursive: true, force: true });\n\n let raw;\n try {\n raw = await spawn(\"npx\", [\"--yes\", \"@lhci/cli\", \"autorun\", `--config=${configPath}`], {\n cwd: site.path,\n });\n } catch (err) {\n await rm(configDir, { recursive: true, force: true });\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) {\n return {\n audit: \"lighthouse\",\n site: label,\n status: \"skip\",\n summary: \"npx/@lhci/cli not available\",\n };\n }\n throw err;\n }\n await rm(configDir, { recursive: true, force: true });\n\n const manifest = await readJsonMaybe<ManifestEntry[]>(join(resultsDir, \"manifest.json\"));\n\n if (!manifest || manifest.length === 0) {\n return {\n audit: \"lighthouse\",\n site: label,\n status: \"fail\",\n summary: `lighthouse: no manifest written (exit ${raw.code})${\n raw.stderr ? ` — ${raw.stderr.slice(0, 200)}` : \"\"\n }`,\n };\n }\n\n const assertionResults =\n (await readJsonMaybe<AssertionResult[]>(join(resultsDir, \"assertion-results.json\"))) ?? [];\n\n const failed = assertionResults.filter((a) => !a.passed);\n const assertions = failed.map((a) => ({\n category: categoryFromAssertion(a),\n level: a.level,\n message: messageForAssertion(a),\n }));\n\n const anyError = assertions.some((a) => a.level === \"error\");\n const anyWarn = assertions.some((a) => a.level === \"warn\");\n const status: AuditResult[\"status\"] = anyError ? \"fail\" : anyWarn ? \"warn\" : \"pass\";\n\n const normalized: NormalizedLhciResult = {\n summary: averageSummaries(manifest),\n assertionsFailed: failed.length,\n assertions,\n };\n\n const summary =\n status === \"pass\"\n ? \"lighthouse: all categories passing\"\n : `lighthouse: ${failed.length} assertion(s) failed`;\n\n return {\n audit: \"lighthouse\",\n site: label,\n status,\n summary,\n details: normalized,\n };\n}\n","export const lighthouseConfig = {\n ci: {\n collect: {\n url: [\"http://localhost:5173/dev/a11y-fixtures\"],\n // `npm run vite:dev` works on both pnpm and npm sites — pnpm respects\n // the `run` form too. Keeps this config portable across the fleet\n // while sites transition to pnpm.\n startServerCommand: \"npm run vite:dev\",\n startServerReadyPattern: \"ready in\",\n startServerReadyTimeout: 120_000,\n numberOfRuns: 1,\n settings: {\n preset: \"desktop\",\n skipAudits: [\"uses-http2\"],\n },\n },\n assert: {\n assertions: {\n \"categories:accessibility\": [\"error\", { minScore: 0.95 }],\n \"categories:best-practices\": [\"error\", { minScore: 0.9 }],\n \"categories:seo\": [\"error\", { minScore: 0.9 }],\n \"categories:performance\": [\"warn\", { minScore: 0.7 }],\n },\n },\n upload: {\n target: \"temporary-public-storage\",\n },\n },\n} as const;\n\nexport default lighthouseConfig;\n","import { readFile, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { a11yRoutes } from \"../configs/playwright-a11y.js\";\nimport { defaultSpawn } from \"./util/spawn.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\ntype Impact = \"minor\" | \"moderate\" | \"serious\" | \"critical\";\n\ntype AxeViolation = {\n id: string;\n impact: Impact;\n route: string;\n help?: string;\n helpUrl?: string;\n nodes?: Array<{ html?: string; target?: string[] }>;\n};\n\ntype NormalizedA11y = {\n totalViolations: number;\n byImpact: Partial<Record<Impact, number>>;\n violations: AxeViolation[];\n};\n\nconst RESULTS_REL = \".reddoor-a11y/results.json\";\n\nasync function readJsonMaybe<T>(path: string): Promise<T | null> {\n try {\n const raw = await readFile(path, \"utf-8\");\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n\n// The spec the audit writes runs all configured routes through axe in a single\n// test (so worker isolation doesn't fragment the collected violations) and\n// writes the structured result to <cwd>/.reddoor-a11y/results.json before\n// asserting. That way, the audit can read real axe details even when the\n// expect(...).toEqual([]) assertion fails.\nfunction buildSpec(): string {\n return `import { test, expect } from \"@playwright/test\";\nimport AxeBuilder from \"@axe-core/playwright\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nconst pages = ${JSON.stringify(a11yRoutes)};\nconst OUTPUT = process.env.REDDOOR_A11Y_OUTPUT;\n\n// Playwright's default per-test timeout is 30s. We loop through every\n// configured route in a single test, so the budget needs to scale.\ntest.setTimeout(5 * 60_000);\n\ntest(\"a11y across configured routes\", async ({ page }) => {\n const violations = [];\n for (const { path, name } of pages) {\n await page.goto(path);\n const results = await new AxeBuilder({ page })\n .withTags([\"wcag2a\",\"wcag2aa\",\"wcag21a\",\"wcag21aa\",\"wcag22aa\"])\n .analyze();\n for (const v of results.violations) {\n violations.push({\n id: v.id,\n impact: v.impact ?? \"moderate\",\n route: name,\n help: v.help,\n helpUrl: v.helpUrl,\n nodes: v.nodes.map((n) => ({ html: n.html, target: n.target })),\n });\n }\n }\n const byImpact = {};\n for (const v of violations) {\n byImpact[v.impact] = (byImpact[v.impact] ?? 0) + 1;\n }\n if (OUTPUT) {\n await mkdir(dirname(OUTPUT), { recursive: true });\n await writeFile(\n OUTPUT,\n JSON.stringify({ totalViolations: violations.length, byImpact, violations }, null, 2),\n );\n }\n expect(violations).toEqual([]);\n});\n`;\n}\n\nexport async function a11yAudit(ctx: AuditContext): Promise<AuditResult> {\n const spawn = ctx.spawn ?? defaultSpawn;\n const site = ctx.site;\n const label = siteLabel(site);\n\n const specDir = await mkdtemp(join(tmpdir(), \"reddoor-a11y-spec-\"));\n const specPath = join(specDir, \"a11y.spec.ts\");\n await writeFile(specPath, buildSpec(), \"utf-8\");\n\n const resultsPath = join(site.path, RESULTS_REL);\n // Clear stale artifacts so a failed spawn never reports old data.\n await rm(join(site.path, \".reddoor-a11y\"), { recursive: true, force: true });\n\n let raw;\n try {\n raw = await spawn(\"npx\", [\"--yes\", \"playwright\", \"test\", \"--reporter=line\", specPath], {\n cwd: site.path,\n env: { ...process.env, REDDOOR_A11Y_OUTPUT: resultsPath },\n });\n } catch (err) {\n await rm(specDir, { recursive: true, force: true });\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) {\n return {\n audit: \"a11y\",\n site: label,\n status: \"skip\",\n summary: \"npx/playwright not available\",\n };\n }\n throw err;\n }\n await rm(specDir, { recursive: true, force: true });\n\n const artifact = await readJsonMaybe<NormalizedA11y>(resultsPath);\n\n if (!artifact) {\n return {\n audit: \"a11y\",\n site: label,\n status: \"fail\",\n summary: `a11y: no results written (exit ${raw.code})${\n raw.stderr ? ` — ${raw.stderr.slice(0, 200)}` : \"\"\n }`,\n };\n }\n\n const hasSerious = (artifact.byImpact.serious ?? 0) > 0 || (artifact.byImpact.critical ?? 0) > 0;\n const hasAny = artifact.totalViolations > 0;\n\n const status: AuditResult[\"status\"] = hasSerious ? \"fail\" : hasAny ? \"warn\" : \"pass\";\n const summary =\n status === \"pass\"\n ? `a11y: 0 violations across ${a11yRoutes.length} routes`\n : `a11y: ${artifact.totalViolations} violations`;\n\n return {\n audit: \"a11y\",\n site: label,\n status,\n summary,\n details: artifact,\n };\n}\n","import { defineConfig, devices, type PlaywrightTestConfig } from \"@playwright/test\";\n\nexport type A11yRoute = { path: string; name: string };\n\nexport const a11yRoutes: A11yRoute[] = [\n { path: \"/dev/a11y-fixtures\", name: \"a11y fixtures\" },\n { path: \"/dev/animate-in\", name: \"animate-in demo\" },\n];\n\nexport const playwrightA11yConfig: PlaywrightTestConfig = defineConfig({\n testDir: \"tests\",\n testMatch: /.*\\.spec\\.ts$/,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: process.env.CI ? \"github\" : \"list\",\n use: {\n baseURL: \"http://localhost:5173\",\n trace: \"on-first-retry\",\n },\n projects: [\n {\n name: \"chromium\",\n use: { ...devices[\"Desktop Chrome\"] },\n },\n ],\n webServer: {\n // Portable across pnpm and npm sites — pnpm respects `npm run` too.\n command: \"npm run vite:dev\",\n url: \"http://localhost:5173/dev/a11y-fixtures\",\n reuseExistingServer: !process.env.CI,\n timeout: 120_000,\n },\n});\n\nexport default playwrightA11yConfig;\n","import type { AuditName, AuditResult, Site } from \"../types.js\";\nimport type { AuditContext } from \"./util/inject.js\";\nimport { defaultSpawn } from \"./util/spawn.js\";\nimport type { SpawnFn } from \"./util/spawn.js\";\nimport { depsAudit } from \"./deps.js\";\nimport { lintAudit } from \"./lint.js\";\nimport { securityAudit } from \"./security.js\";\nimport { lighthouseAudit } from \"./lighthouse.js\";\nimport { a11yAudit } from \"./a11y.js\";\n\nconst REGISTRY: Record<AuditName, (ctx: AuditContext) => Promise<AuditResult>> = {\n deps: depsAudit,\n lint: lintAudit,\n security: securityAudit,\n lighthouse: lighthouseAudit,\n a11y: a11yAudit,\n};\n\nexport const ALL_AUDIT_NAMES = Object.keys(REGISTRY) as AuditName[];\n\n/** Default per-audit spawn timeout when running via runAudits (30 s). */\nconst DEFAULT_AUDIT_TIMEOUT_MS = 30_000;\n\nfunction timedSpawn(timeoutMs: number): SpawnFn {\n return (cmd, args, opts = {}) =>\n defaultSpawn(cmd, args, { ...opts, timeoutMs: opts.timeoutMs ?? timeoutMs });\n}\n\nexport async function runAudits(site: Site, which?: AuditName[]): Promise<AuditResult[]> {\n const names = which ?? ALL_AUDIT_NAMES;\n for (const n of names) {\n if (!(n in REGISTRY)) throw new Error(`unknown audit: ${n}`);\n }\n const spawn = timedSpawn(DEFAULT_AUDIT_TIMEOUT_MS);\n const label = site.name ?? site.path;\n return Promise.all(\n names.map((n) =>\n REGISTRY[n]({ site, spawn }).catch(\n (err): AuditResult => ({\n audit: n,\n site: label,\n status: \"fail\",\n summary: `${n}: unexpected error — ${String(err)}`,\n }),\n ),\n ),\n );\n}\n\nexport async function runAuditsAcross(sites: Site[], which?: AuditName[]): Promise<AuditResult[]> {\n const all = await Promise.all(sites.map((s) => runAudits(s, which)));\n return all.flat();\n}\n\nexport { depsAudit, lintAudit, securityAudit, lighthouseAudit, a11yAudit };\n","import { pathToFileURL } from \"node:url\";\nimport { resolve, extname } from \"node:path\";\nimport type { InventoryProvider, Site } from \"../../types.js\";\nimport { localPath } from \"../../inventory/local.js\";\nimport { fromJsonFile } from \"../../inventory/json.js\";\n\nexport type ResolveSitesInput = {\n site?: string;\n fleet?: string;\n /** Optional workdir for the `--fleet airtable` keyword path (computes site.path as {workdir}/{slug}). */\n workdir?: string;\n cwd: string;\n};\n\nexport async function resolveSites(input: ResolveSitesInput): Promise<Site[]> {\n if (input.site && input.fleet) {\n throw Object.assign(new Error(\"cannot combine a positional [site] with --fleet\"), {\n exitCode: 2,\n });\n }\n\n if (input.fleet === \"airtable\") {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const { fromAirtableBase } = await import(\"../../inventory/airtable.js\");\n const base = openBase(readAirtableConfig());\n const provider = fromAirtableBase(base, input.workdir ? { workdir: input.workdir } : {});\n return provider();\n }\n\n if (input.fleet) {\n const fleetPath = resolve(input.cwd, input.fleet);\n const ext = extname(fleetPath).toLowerCase();\n let provider: InventoryProvider;\n if (ext === \".json\") {\n provider = fromJsonFile(fleetPath);\n } else if (ext === \".js\" || ext === \".mjs\" || ext === \".cjs\") {\n const mod = (await import(pathToFileURL(fleetPath).href)) as {\n default?: InventoryProvider;\n };\n if (!mod.default || typeof mod.default !== \"function\") {\n throw Object.assign(new Error(`--fleet ${input.fleet}: default export is not a function`), {\n exitCode: 2,\n });\n }\n provider = mod.default;\n } else {\n throw Object.assign(\n new Error(`--fleet ${input.fleet}: unsupported extension ${ext || \"(none)\"}`),\n { exitCode: 2 },\n );\n }\n return provider();\n }\n\n return localPath(resolve(input.cwd, input.site ?? input.cwd))();\n}\n","import { basename } from \"node:path\";\nimport type { InventoryProvider, Site } from \"../types.js\";\n\nexport type LocalPathOptions = {\n name?: string;\n};\n\nexport function localPath(path: string, opts: LocalPathOptions = {}): InventoryProvider {\n const site: Site = { path, name: opts.name ?? basename(path) };\n return async () => [site];\n}\n","import { readFile } from \"node:fs/promises\";\nimport { isAbsolute } from \"node:path\";\nimport type { InventoryProvider, Site } from \"../types.js\";\n\nfunction validate(raw: unknown): Site[] {\n if (!Array.isArray(raw)) {\n throw new Error(\"inventory JSON must be an array of sites\");\n }\n return raw.map((entry, i) => {\n if (!entry || typeof entry !== \"object\") {\n throw new Error(`inventory entry ${i} is not an object`);\n }\n const e = entry as Record<string, unknown>;\n if (typeof e.path !== \"string\" || e.path.length === 0) {\n throw new Error(`inventory entry ${i} is missing required field: path`);\n }\n if (!isAbsolute(e.path)) {\n throw new Error(\n `inventory entry ${i}: path must be absolute (got \"${e.path}\"). ` +\n `Relative paths are rejected so cwd at invocation can't change which site is targeted.`,\n );\n }\n const site: Site = { path: e.path };\n if (typeof e.name === \"string\") site.name = e.name;\n if (typeof e.repoUrl === \"string\") site.repoUrl = e.repoUrl;\n if (typeof e.meta === \"object\" && e.meta !== null) {\n site.meta = e.meta as Record<string, unknown>;\n }\n return site;\n });\n}\n\nexport function fromJsonFile(path: string): InventoryProvider {\n return async () => {\n const raw = JSON.parse(await readFile(path, \"utf-8\")) as unknown;\n return validate(raw);\n };\n}\n","import { stat, readdir, mkdir } from \"node:fs/promises\";\nimport { isAbsolute, join } from \"node:path\";\nimport type { Site } from \"../../types.js\";\nimport { defaultSpawn, type SpawnFn } from \"../../audits/util/spawn.js\";\n\nexport type CloneIfNeededOptions = {\n workdir: string;\n spawn?: SpawnFn;\n};\n\nfunction deriveNameFromRepoUrl(repoUrl: string): string {\n const slash = repoUrl.split(\"/\").pop() ?? repoUrl;\n return slash.replace(/\\.git$/, \"\");\n}\n\n/** Reject names that would let an inventory entry write outside `workdir`. */\nfunction assertSafeName(name: string): void {\n if (isAbsolute(name)) {\n throw new Error(`unsafe site name (absolute path not allowed): ${name}`);\n }\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n throw new Error(`unsafe site name (path separator not allowed): ${name}`);\n }\n if (name.split(/[\\\\/]/).some((seg) => seg === \"..\")) {\n throw new Error(`unsafe site name (traversal segment not allowed): ${name}`);\n }\n}\n\n/**\n * Reject repo URLs that don't look like a normal clone target. Without this,\n * a `repoUrl` starting with `-` would be interpreted by git as a flag\n * (CVE-2017-1000117 family) — e.g. `--upload-pack=…` allows arbitrary command\n * execution. Inventory files are usually trusted, but `.mjs`/`.js` inventories\n * could pull from environments or external sources, so harden the boundary.\n *\n * Accepts: `https://`, `http://`, `ssh://`, `git://`, `file://`, and\n * `[user@]host:path` shorthand (e.g. `git@github.com:org/repo.git`).\n */\nfunction assertSafeRepoUrl(repoUrl: string): void {\n if (\n !/^(https?|ssh|git|file):\\/\\//.test(repoUrl) &&\n !/^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+:/.test(repoUrl)\n ) {\n throw new Error(\n `unsafe repoUrl: must start with a scheme (https://, ssh://, git://, file://) ` +\n `or use scp-style \"user@host:path\" (got: ${JSON.stringify(repoUrl)})`,\n );\n }\n}\n\nasync function isNonEmptyDir(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n if (!s.isDirectory()) return false;\n const entries = await readdir(path);\n return entries.length > 0;\n } catch {\n return false;\n }\n}\n\nexport async function cloneIfNeeded(site: Site, opts: CloneIfNeededOptions): Promise<Site> {\n if (await isNonEmptyDir(site.path)) return site;\n\n if (!site.repoUrl) {\n throw new Error(`site path does not exist (${site.path}) and no repoUrl is set — cannot clone`);\n }\n\n const name = site.name ?? deriveNameFromRepoUrl(site.repoUrl);\n assertSafeName(name);\n assertSafeRepoUrl(site.repoUrl);\n const target = join(opts.workdir, name);\n await mkdir(opts.workdir, { recursive: true });\n\n if (await isNonEmptyDir(target)) {\n return { ...site, name, path: target };\n }\n\n const spawn = opts.spawn ?? defaultSpawn;\n // `--` separator so git won't treat repoUrl as a flag if validation slips.\n const result = await spawn(\"git\", [\"clone\", \"--\", site.repoUrl, target], {\n cwd: opts.workdir,\n timeoutMs: 5 * 60_000,\n });\n if (result.code !== 0) {\n throw new Error(`git clone failed (code ${result.code}): ${result.stderr}`);\n }\n return { ...site, name, path: target };\n}\n"],"mappings":"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","names":["readFile","join","resolve","resolve","readFile","join","join","readFile","spawn","readFile","join","readFile","spawn","join","readFile","writeFile","mkdtemp","rm","tmpdir","join","readJsonMaybe","readFile","spawn","mkdtemp","join","tmpdir","writeFile","rm","spawn","readFile","openBase","readAirtableConfig","fromAirtableBase","isAbsolute","join","isAbsolute","join","spawn","resolve","openBase","readAirtableConfig","listWebsites","updateScores","siteSlug","lighthouseScoresFromResult","resolveSlugFromCwd"]}
1
+ {"version":3,"sources":["../../../src/reports/airtable/client.ts","../../../src/reports/airtable/websites.ts","../../../src/inventory/airtable.ts","../../../src/audits/lighthouse-airtable.ts","../../../src/cli/commands/audit.ts","../../../src/audits/util/spawn.ts","../../../src/audits/deps.ts","../../../src/util/site.ts","../../../src/configs/baseline-versions.ts","../../../src/audits/lint.ts","../../../src/audits/security.ts","../../../src/audits/lighthouse.ts","../../../src/configs/lighthouse.ts","../../../src/audits/util/site-config.ts","../../../src/audits/a11y.ts","../../../src/configs/playwright-a11y.ts","../../../src/audits/index.ts","../../../src/cli/fleet/resolve-sites.ts","../../../src/inventory/local.ts","../../../src/inventory/json.ts","../../../src/cli/fleet/clone-if-needed.ts"],"sourcesContent":["import Airtable from \"airtable\";\n\nexport type AirtableConfig = {\n apiKey: string;\n baseId: string;\n};\n\nexport function readAirtableConfig(): AirtableConfig {\n const apiKey = process.env.AIRTABLE_PAT;\n const baseId = process.env.AIRTABLE_BASE_ID;\n if (!apiKey) throw Object.assign(new Error(\"AIRTABLE_PAT not set\"), { exitCode: 2 });\n if (!baseId) throw Object.assign(new Error(\"AIRTABLE_BASE_ID not set\"), { exitCode: 2 });\n return { apiKey, baseId };\n}\n\nexport type AirtableBase = ReturnType<typeof openBase>;\n\nexport function openBase(cfg: AirtableConfig) {\n return new Airtable({ apiKey: cfg.apiKey }).base(cfg.baseId);\n}\n","import type { FieldSet } from \"airtable\";\nimport type { AirtableBase } from \"./client.js\";\nimport type { LighthouseScores } from \"../types.js\";\n\nexport const WEBSITES_TABLE = \"Websites\";\n\nexport type Frequency = \"None\" | \"Monthly\" | \"Quarterly\" | \"Yearly\";\n\nexport type Status =\n | \"in development\"\n | \"launch period\"\n | \"maintenance\"\n | \"hosting\"\n | \"probably not our problem\"\n | \"deprecated\";\n\nexport type WebsiteRow = {\n id: string;\n name: string;\n url: string;\n status: Status | null;\n pointOfContact: string | null;\n maintenanceFreq: Frequency;\n testingFreq: Frequency;\n /** Last manually-recorded maintenance day (used as fallback when no Reports row exists). */\n maintenanceDay: string | null;\n testingDay: string | null;\n ga4PropertyId: string | null;\n reportRecipientsTo: string | null;\n reportRecipientsCc: string | null;\n /** First attachment in the Header image field (Airtable's signed URL — fetch before expiry). */\n headerImage: { url: string; filename: string; type: string } | null;\n /** Lighthouse \"current state\" snapshot, kept fresh by `audit lighthouse --write-airtable`. */\n pScore: number | null;\n rScore: number | null;\n bpScore: number | null;\n seoScore: number | null;\n /** ISO timestamp set by `audit lighthouse --write-airtable` when scores were last refreshed. */\n lastLighthouseAuditAt: string | null;\n};\n\nexport function siteSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction mapRow(rec: { id: string; fields: Record<string, unknown> }): WebsiteRow {\n const f = rec.fields;\n const attachments =\n (f[\"Header image\"] as Array<{ url: string; filename: string; type: string }> | undefined) ?? [];\n const header = attachments[0] ?? null;\n return {\n id: rec.id,\n name: String(f[\"Name\"] ?? \"\"),\n url: String(f[\"url\"] ?? \"\"),\n status: (f[\"Status\"] as Status | undefined) ?? null,\n pointOfContact: (f[\"point of contact\"] as string | undefined) ?? null,\n maintenanceFreq: ((f[\"maintenence freq\"] as string | undefined) ?? \"None\") as Frequency,\n testingFreq: ((f[\"testing freq\"] as string | undefined) ?? \"None\") as Frequency,\n maintenanceDay: (f[\"maintenance day\"] as string | undefined) ?? null,\n testingDay: (f[\"testing day\"] as string | undefined) ?? null,\n ga4PropertyId: (f[\"GA4 property ID\"] as string | undefined) ?? null,\n reportRecipientsTo: (f[\"Report recipients (To)\"] as string | undefined) ?? null,\n reportRecipientsCc: (f[\"Report recipients (CC)\"] as string | undefined) ?? null,\n headerImage: header,\n pScore: (f[\"pScore\"] as number | undefined) ?? null,\n rScore: (f[\"rScore\"] as number | undefined) ?? null,\n bpScore: (f[\"bpScore\"] as number | undefined) ?? null,\n seoScore: (f[\"seoScore\"] as number | undefined) ?? null,\n lastLighthouseAuditAt: (f[\"Last lighthouse audit at\"] as string | undefined) ?? null,\n };\n}\n\nexport async function listWebsites(base: AirtableBase): Promise<WebsiteRow[]> {\n const out: WebsiteRow[] = [];\n await base(WEBSITES_TABLE)\n .select({ pageSize: 100 })\n .eachPage((records, fetchNextPage) => {\n for (const rec of records) out.push(mapRow({ id: rec.id, fields: rec.fields }));\n fetchNextPage();\n });\n return out;\n}\n\nexport async function getWebsiteBySlug(\n base: AirtableBase,\n slug: string,\n): Promise<WebsiteRow | null> {\n const all = await listWebsites(base);\n return all.find((w) => siteSlug(w.name) === slug) ?? null;\n}\n\n/**\n * Write the four Lighthouse scores + a refreshed-at timestamp onto a Websites row.\n * Called by `audit lighthouse --write-airtable` after a successful audit run, so\n * the operator never has to paste numbers manually before drafting a report.\n */\nexport async function updateScores(\n base: AirtableBase,\n recordId: string,\n scores: LighthouseScores,\n): Promise<void> {\n const fields: FieldSet = {\n pScore: scores.performance,\n rScore: scores.accessibility,\n bpScore: scores.bestPractices,\n seoScore: scores.seo,\n \"Last lighthouse audit at\": new Date().toISOString(),\n };\n await base(WEBSITES_TABLE).update([{ id: recordId, fields }]);\n}\n","import type { Site, InventoryProvider } from \"../types.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { listWebsites, siteSlug } from \"../reports/airtable/websites.js\";\n\nexport type AirtableInventoryOptions = {\n /**\n * Local workdir to compute each site's path as `{workdir}/{slug}`.\n * Defaults to REDDOOR_FLEET_WORKDIR env var if not provided.\n * Airtable doesn't store local checkout paths, so this is required.\n */\n workdir?: string;\n};\n\n/**\n * Read sites from the Airtable Websites table as an InventoryProvider.\n * Each row becomes one Site; `path` is computed as `{workdir}/{slug}`.\n * Sites where BOTH maintenance freq AND testing freq are \"None\" are excluded\n * (they're inactive — no scheduled audits or reports).\n *\n * Note: `repoUrl` is set to the production URL (Websites.url). For sites\n * cloned via `--workdir` semantics this is wrong — the convention should be\n * tightened (e.g. add a `repo` field to Websites) when fleet-clone-from-\n * airtable becomes a real flow. For local audits where the site is already\n * checked out at `path`, the `repoUrl` is unused.\n */\nexport function fromAirtableBase(\n base: AirtableBase,\n opts: AirtableInventoryOptions = {},\n): InventoryProvider {\n return async (): Promise<Site[]> => {\n const workdir = opts.workdir ?? process.env.REDDOOR_FLEET_WORKDIR;\n if (!workdir) {\n throw new Error(\n \"fromAirtableBase requires `workdir` option or REDDOOR_FLEET_WORKDIR env (sites need a local path)\",\n );\n }\n const websites = await listWebsites(base);\n return websites\n .filter((w) => w.maintenanceFreq !== \"None\" || w.testingFreq !== \"None\")\n .map((w) => {\n const slug = siteSlug(w.name);\n const site: Site = {\n path: `${workdir}/${slug}`,\n name: slug,\n meta: { airtableRowId: w.id, displayName: w.name },\n };\n if (w.url) site.repoUrl = w.url;\n return site;\n });\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport type { LighthouseScores } from \"../reports/types.js\";\nimport { siteSlug } from \"../reports/airtable/websites.js\";\n\n/**\n * Extract the four Lighthouse scores (as integer percentages) from a\n * `lighthouse` AuditResult. LHCI manifest summaries are floats in [0,1];\n * multiply + round.\n */\nexport function lighthouseScoresFromResult(result: AuditResult): LighthouseScores {\n if (result.audit !== \"lighthouse\") {\n throw new Error(`Expected a 'lighthouse' AuditResult, got '${result.audit}'`);\n }\n const details = (result.details ?? {}) as { summary?: Record<string, number> };\n const summary = details.summary ?? {};\n const toPct = (n: number | undefined) =>\n typeof n === \"number\" && !Number.isNaN(n) ? Math.round(n * 100) : 0;\n return {\n performance: toPct(summary[\"performance\"]),\n accessibility: toPct(summary[\"accessibility\"]),\n bestPractices: toPct(summary[\"best-practices\"]),\n seo: toPct(summary[\"seo\"]),\n };\n}\n\n/**\n * Derive a site slug from the cwd's package.json#name. Used by\n * `audit lighthouse --write-airtable` when the operator doesn't pass an\n * explicit slug — the cwd is the site checkout, so package.json#name is\n * usually the canonical site name.\n */\nexport async function resolveSlugFromCwd(cwd: string): Promise<string> {\n try {\n const pkgPath = join(cwd, \"package.json\");\n const raw = await readFile(pkgPath, \"utf-8\");\n const pkg = JSON.parse(raw) as { name?: string };\n if (!pkg.name) throw new Error(\"package.json has no 'name' field\");\n return siteSlug(pkg.name);\n } catch (e) {\n throw new Error(\n `Could not derive site slug from ${cwd}/package.json: ${(e as Error).message}. ` +\n `Pass --write-airtable=<slug> explicitly.`,\n { cause: e },\n );\n }\n}\n","import { resolve } from \"node:path\";\nimport { runAuditsAcross, ALL_AUDIT_NAMES } from \"../../audits/index.js\";\nimport type { AuditName, AuditResult } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { cloneIfNeeded } from \"../fleet/clone-if-needed.js\";\n\nexport type AuditCommandOptions = {\n only?: string;\n json?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n /**\n * After running, push the lighthouse scores to the matching Websites row\n * in Airtable. `true` (no value) = derive slug from cwd/package.json#name;\n * string = explicit slug (e.g. \"med-solutions-of-texas\").\n */\n writeAirtable?: string | boolean;\n};\n\nfunction parseOnly(value: string | undefined): AuditName[] | undefined {\n if (!value) return undefined;\n const names = value.split(\",\").map((s) => s.trim());\n for (const n of names) {\n if (!ALL_AUDIT_NAMES.includes(n as AuditName)) {\n throw Object.assign(new Error(`unknown audit in --only: ${n}`), { exitCode: 2 });\n }\n }\n return names as AuditName[];\n}\n\nfunction formatTable(results: AuditResult[]): string {\n return results\n .map((r) => `${r.audit.padEnd(12)} ${r.status.padEnd(5)} ${r.site}\\n ${r.summary}`)\n .join(\"\\n\");\n}\n\nfunction exitCode(results: AuditResult[]): number {\n return results.some((r) => r.status === \"fail\") ? 1 : 0;\n}\n\nexport async function runAuditCommand(\n site: string | undefined,\n opts: AuditCommandOptions,\n): Promise<{ output: string; code: number }> {\n const which = parseOnly(opts.only);\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n\n let sites = await resolveSites({\n ...(site !== undefined ? { site } : {}),\n ...(opts.fleet !== undefined ? { fleet: opts.fleet } : {}),\n ...(opts.workdir !== undefined ? { workdir: opts.workdir } : {}),\n cwd,\n });\n\n if (opts.fleet) {\n const workdir = opts.workdir ?? `${process.env.HOME ?? \"\"}/.reddoor-maint/sites`;\n sites = await Promise.all(sites.map((s) => cloneIfNeeded(s, { workdir })));\n }\n\n // Run sites in parallel via runAuditsAcross — for a fleet of 30 this is the\n // difference between minutes and ~max(per-site) seconds.\n const results: AuditResult[] = await runAuditsAcross(sites, which);\n\n let output = opts.json ? JSON.stringify(results, null, 2) : formatTable(results);\n\n if (opts.writeAirtable !== undefined) {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const { listWebsites, updateScores, siteSlug } =\n await import(\"../../reports/airtable/websites.js\");\n const { lighthouseScoresFromResult, resolveSlugFromCwd } =\n await import(\"../../audits/lighthouse-airtable.js\");\n\n const slug =\n typeof opts.writeAirtable === \"string\" && opts.writeAirtable.length > 0\n ? opts.writeAirtable\n : await resolveSlugFromCwd(cwd);\n\n const lhResult = results.find((r) => r.audit === \"lighthouse\");\n if (!lhResult) {\n throw Object.assign(\n new Error(\n \"--write-airtable requires a lighthouse result; did you pass --only without lighthouse?\",\n ),\n { exitCode: 2 },\n );\n }\n if (lhResult.status === \"fail\") {\n throw Object.assign(\n new Error(\n `Lighthouse audit failed; refusing to write scores to Airtable. Summary: ${lhResult.summary}`,\n ),\n { exitCode: 1 },\n );\n }\n\n const base = openBase(readAirtableConfig());\n const websites = await listWebsites(base);\n const target = websites.find((w) => siteSlug(w.name) === slug);\n if (!target) {\n throw Object.assign(new Error(`No Websites row matched slug \"${slug}\"`), { exitCode: 2 });\n }\n\n const scores = lighthouseScoresFromResult(lhResult);\n await updateScores(base, target.id, scores);\n output += `\\n\\n→ wrote scores to Websites[${target.name}]: P=${scores.performance} A=${scores.accessibility} BP=${scores.bestPractices} SEO=${scores.seo}`;\n }\n\n return { output, code: exitCode(results) };\n}\n","import { spawn } from \"node:child_process\";\n\nexport type SpawnResult = { code: number; stdout: string; stderr: string };\n\nexport type SpawnOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n timeoutMs?: number;\n /** When true, the child inherits stdout/stderr so the user sees live\n * progress (useful for long-running `pnpm up` / `npm install`). The\n * returned `stdout` and `stderr` will be empty strings in that case. */\n streaming?: boolean;\n};\n\nexport type SpawnFn = (\n cmd: string,\n args: readonly string[],\n opts?: SpawnOptions,\n) => Promise<SpawnResult>;\n\nexport const defaultSpawn: SpawnFn = (cmd, args, opts = {}) =>\n new Promise((resolve, reject) => {\n const streaming = opts.streaming === true;\n const child = spawn(cmd, [...args], {\n cwd: opts.cwd,\n env: opts.env ?? process.env,\n stdio: streaming ? [\"ignore\", \"inherit\", \"inherit\"] : [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n if (!streaming) {\n child.stdout?.on(\"data\", (chunk) => (stdout += String(chunk)));\n child.stderr?.on(\"data\", (chunk) => (stderr += String(chunk)));\n }\n\n const timer = opts.timeoutMs\n ? setTimeout(() => {\n child.kill(\"SIGTERM\");\n reject(new Error(`spawn timeout after ${opts.timeoutMs}ms: ${cmd}`));\n }, opts.timeoutMs)\n : undefined;\n\n child.on(\"error\", (err) => {\n if (timer) clearTimeout(timer);\n reject(err);\n });\n child.on(\"close\", (code) => {\n if (timer) clearTimeout(timer);\n resolve({ code: code ?? -1, stdout, stderr });\n });\n });\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { baselineVersions } from \"../configs/baseline-versions.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\nexport type Drift = \"same\" | \"patch\" | \"minor\" | \"major\" | \"newer\";\n\nexport type DepsDriftEntry = {\n pkg: string;\n baseline: string;\n actual: string;\n drift: Drift;\n};\n\nfunction stripCaret(range: string): string {\n return range.replace(/^[\\^~]/, \"\");\n}\n\nfunction parseSemver(v: string): [number, number, number] {\n const cleaned = stripCaret(v).split(\"-\")[0] ?? \"0.0.0\";\n const parts = cleaned.split(\".\").map((n) => Number.parseInt(n, 10));\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\nfunction compareSemver(actual: string, baseline: string): Drift {\n const [aMajor, aMinor, aPatch] = parseSemver(actual);\n const [bMajor, bMinor, bPatch] = parseSemver(baseline);\n if (aMajor > bMajor) return \"newer\";\n if (aMajor < bMajor) return \"major\";\n if (aMinor > bMinor) return \"newer\";\n if (aMinor < bMinor) return \"minor\";\n if (aPatch > bPatch) return \"newer\";\n if (aPatch < bPatch) return \"patch\";\n return \"same\";\n}\n\nexport async function depsAudit(ctx: AuditContext): Promise<AuditResult> {\n const pkgPath = join(ctx.site.path, \"package.json\");\n let pkgRaw: string;\n try {\n pkgRaw = await readFile(pkgPath, \"utf-8\");\n } catch (err) {\n return {\n audit: \"deps\",\n site: siteLabel(ctx.site),\n status: \"skip\",\n summary: `no package.json at ${pkgPath}`,\n details: { error: String(err) },\n };\n }\n\n const pkg = JSON.parse(pkgRaw) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const installed: Record<string, string> = {\n ...(pkg.dependencies ?? {}),\n ...(pkg.devDependencies ?? {}),\n };\n\n const details: DepsDriftEntry[] = [];\n for (const [name, baseline] of Object.entries(baselineVersions)) {\n const actual = installed[name];\n if (!actual) continue;\n details.push({\n pkg: name,\n baseline,\n actual,\n drift: compareSemver(actual, baseline),\n });\n }\n\n const anyMajor = details.some((d) => d.drift === \"major\");\n const anyMinor = details.some((d) => d.drift === \"minor\");\n const anyNewer = details.some((d) => d.drift === \"newer\");\n\n const status: AuditResult[\"status\"] = anyMajor ? \"fail\" : anyMinor || anyNewer ? \"warn\" : \"pass\";\n\n const summary =\n status === \"pass\"\n ? `all ${details.length} tracked deps in line with baseline`\n : status === \"warn\"\n ? `${details.filter((d) => d.drift !== \"same\").length} of ${details.length} tracked deps drifted`\n : `${details.filter((d) => d.drift === \"major\").length} deps lagging by a major version`;\n\n return {\n audit: \"deps\",\n site: siteLabel(ctx.site),\n status,\n summary,\n details,\n };\n}\n","import type { Site } from \"../types.js\";\n\n/** Human-friendly label for log/output formatting. Prefer the inventory's\n * `name` when present (e.g. \"caltex-landing\") and fall back to the\n * filesystem `path` when unnamed. Every audit + recipe uses this. */\nexport function siteLabel(site: Site): string {\n return site.name ?? site.path;\n}\n","// Curated map of the framework deps reddoor sites should stay close to.\n// Refreshed at each package release from reddoor-starter's package.json.\n// Versions are caret ranges to mirror what `pnpm add` would produce.\n\nexport const baselineVersions: Record<string, string> = {\n // SvelteKit core\n svelte: \"^5.55.5\",\n \"@sveltejs/kit\": \"^2.59.0\",\n \"@sveltejs/adapter-netlify\": \"^6.0.4\",\n \"@sveltejs/adapter-auto\": \"^7.0.0\",\n \"@sveltejs/vite-plugin-svelte\": \"^7.0.0\",\n \"svelte-check\": \"^4.4.7\",\n\n // Build tooling\n vite: \"^8.0.10\",\n vitest: \"^4.1.1\",\n typescript: \"^6.0.3\",\n\n // Tailwind 4\n tailwindcss: \"^4.0.14\",\n \"@tailwindcss/vite\": \"^4.3.0\",\n\n // Prismic\n \"@prismicio/client\": \"^7.3.1\",\n \"@prismicio/svelte\": \"^2.0.0\",\n \"@slicemachine/adapter-sveltekit\": \"^0.3.36\",\n \"slice-machine-ui\": \"^2.11.1\",\n\n // Test tooling\n \"@playwright/test\": \"^1.59.1\",\n \"@axe-core/playwright\": \"^4.11.3\",\n \"@lhci/cli\": \"^0.15.1\",\n\n // Lint\n eslint: \"^10.3.0\",\n \"eslint-plugin-svelte\": \"^3.1.0\",\n \"eslint-config-prettier\": \"^10.1.1\",\n prettier: \"^3.1.1\",\n \"prettier-plugin-svelte\": \"^3.2.6\",\n \"typescript-eslint\": \"^8.59.1\",\n \"@eslint/js\": \"^10.0.1\",\n globals: \"^17.6.0\",\n\n // Misc\n \"@lucide/svelte\": \"^1.14.0\",\n \"@zerodevx/svelte-img\": \"^2.1.2\",\n};\n\nexport default baselineVersions;\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { ESLint } from \"eslint\";\nimport { check as prettierCheck, resolveConfig as prettierResolveConfig } from \"prettier\";\nimport { glob } from \"tinyglobby\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\nconst TARGET_GLOBS = [\"**/*.{ts,js,svelte}\"];\nconst IGNORE = [\"node_modules/**\", \"dist/**\", \".svelte-kit/**\", \"build/**\", \".netlify/**\"];\n\nasync function listFiles(cwd: string): Promise<string[]> {\n return glob(TARGET_GLOBS, { cwd, ignore: IGNORE, absolute: false });\n}\n\nexport async function lintAudit(ctx: AuditContext): Promise<AuditResult> {\n const { site } = ctx;\n const configPath = join(site.path, \"eslint.config.js\");\n\n if (!existsSync(configPath)) {\n return {\n audit: \"lint\",\n site: siteLabel(site),\n status: \"skip\",\n summary: \"no eslint config at site root\",\n };\n }\n\n const eslint = new ESLint({\n cwd: site.path,\n overrideConfigFile: configPath,\n errorOnUnmatchedPattern: false,\n });\n\n const relFiles = await listFiles(site.path);\n\n // Pass relative paths to ESLint; its cwd is already site.path. Avoids\n // dereferencing symlinks on pnpm workspaces.\n const eslintResults = await eslint.lintFiles(relFiles);\n const eslintErrors = eslintResults.reduce((n, r) => n + r.errorCount, 0);\n const eslintWarnings = eslintResults.reduce((n, r) => n + r.warningCount, 0);\n\n const prettierUnformatted: string[] = [];\n for (const rel of relFiles) {\n const absForResolve = join(site.path, rel);\n const source = await readFile(absForResolve, \"utf-8\");\n const options = (await prettierResolveConfig(absForResolve)) ?? {};\n const ok = await prettierCheck(source, { ...options, filepath: absForResolve });\n if (!ok) prettierUnformatted.push(rel);\n }\n\n const status: AuditResult[\"status\"] =\n eslintErrors > 0 || prettierUnformatted.length > 0\n ? \"fail\"\n : eslintWarnings > 0\n ? \"warn\"\n : \"pass\";\n\n const summary =\n status === \"pass\"\n ? `lint clean across ${relFiles.length} files`\n : `${eslintErrors} eslint errors, ${eslintWarnings} warnings, ${prettierUnformatted.length} unformatted`;\n\n return {\n audit: \"lint\",\n site: siteLabel(site),\n status,\n summary,\n details: {\n eslintErrors,\n eslintWarnings,\n prettierUnformatted,\n files: relFiles.length,\n },\n };\n}\n","import type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { defaultSpawn, type SpawnResult } from \"./util/spawn.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\ntype Severity = \"low\" | \"moderate\" | \"high\" | \"critical\";\n\ntype Counts = { low: number; moderate: number; high: number; critical: number };\n\ntype AdvisoryEntry = {\n module: string;\n severity: Severity;\n title: string;\n cves?: string[];\n url?: string;\n};\n\n// pnpm audit output (npm-compat with extra advisories map keyed by ID).\ntype PnpmAuditJson = {\n metadata?: { vulnerabilities?: Partial<Counts> };\n advisories?: Record<\n string,\n {\n id?: number;\n title?: string;\n module_name?: string;\n severity?: string;\n cves?: string[];\n url?: string;\n }\n >;\n};\n\n// npm v7+ shape (vulnerabilities keyed by package name).\ntype NpmAuditJson = {\n metadata?: { vulnerabilities?: Partial<Counts> };\n vulnerabilities?: Record<\n string,\n {\n name?: string;\n severity?: string;\n via?: unknown;\n url?: string;\n }\n >;\n};\n\nfunction classify(v: Counts) {\n if (v.critical > 0 || v.high > 0) return \"fail\" as const;\n if (v.moderate > 0 || v.low > 0) return \"warn\" as const;\n return \"pass\" as const;\n}\n\nfunction normalizeSeverity(s: unknown): Severity {\n if (s === \"low\" || s === \"moderate\" || s === \"high\" || s === \"critical\") return s;\n // npm/pnpm sometimes emit \"info\" for informational advisories. Map down\n // rather than defaulting to \"moderate\" (which would inflate severity).\n return \"low\";\n}\n\nfunction extractAdvisoriesFromPnpm(parsed: PnpmAuditJson): AdvisoryEntry[] {\n const out: AdvisoryEntry[] = [];\n for (const a of Object.values(parsed.advisories ?? {})) {\n if (!a) continue;\n out.push({\n module: a.module_name ?? \"unknown\",\n severity: normalizeSeverity(a.severity),\n title: a.title ?? \"(no title)\",\n ...(a.cves ? { cves: a.cves } : {}),\n ...(a.url ? { url: a.url } : {}),\n });\n }\n return out;\n}\n\n/** Walk an npm v7+ `via` chain to find the root entry whose `via` array\n * contains a real advisory object (rather than another package name string).\n * Returns the package name at the root and the advisory detail. */\nfunction resolveNpmAdvisoryRoot(\n startName: string,\n vulnerabilities: NonNullable<NpmAuditJson[\"vulnerabilities\"]>,\n): { rootName: string; detail?: { title?: string; url?: string } } {\n const seen = new Set<string>();\n let current = startName;\n while (!seen.has(current)) {\n seen.add(current);\n const entry = vulnerabilities[current];\n if (!entry || !Array.isArray(entry.via)) return { rootName: current };\n\n const detailed = entry.via.find(\n (e): e is { title?: string; url?: string } => typeof e === \"object\" && e !== null,\n );\n if (detailed) return { rootName: current, detail: detailed };\n\n const next = entry.via.find((e): e is string => typeof e === \"string\");\n if (!next || next === current) return { rootName: current };\n current = next;\n }\n return { rootName: current };\n}\n\nfunction extractAdvisoriesFromNpm(parsed: NpmAuditJson): AdvisoryEntry[] {\n const vulnerabilities = parsed.vulnerabilities ?? {};\n const roots = new Map<string, AdvisoryEntry>();\n\n for (const [name, v] of Object.entries(vulnerabilities)) {\n if (!v) continue;\n const { rootName, detail } = resolveNpmAdvisoryRoot(name, vulnerabilities);\n if (roots.has(rootName)) continue; // already surfaced via another transitive entry\n\n const rootEntry = vulnerabilities[rootName];\n const severity = normalizeSeverity(rootEntry?.severity ?? v.severity);\n const title = detail?.title ?? rootName;\n const url = detail?.url;\n\n roots.set(rootName, {\n module: rootEntry?.name ?? rootName,\n severity,\n title,\n ...(url ? { url } : {}),\n });\n }\n\n return [...roots.values()];\n}\n\ntype ToolResult =\n | { kind: \"missing\" }\n | { kind: \"error\"; reason: string }\n | { kind: \"ok\"; parsed: PnpmAuditJson & NpmAuditJson };\n\nasync function runAuditTool(\n spawn: (cmd: string, args: readonly string[], opts?: { cwd?: string }) => Promise<SpawnResult>,\n cmd: string,\n args: readonly string[],\n cwd: string,\n): Promise<ToolResult> {\n let raw: SpawnResult;\n try {\n raw = await spawn(cmd, args, { cwd });\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) return { kind: \"missing\" };\n return { kind: \"error\", reason: `spawn failed: ${String(err).slice(0, 200)}` };\n }\n\n // 0 = clean, 1 = vulns found. Anything else is a real error.\n if (raw.code !== 0 && raw.code !== 1) {\n return {\n kind: \"error\",\n reason: `exit ${raw.code}${raw.stderr ? `: ${raw.stderr.slice(0, 150)}` : \"\"}`,\n };\n }\n\n let parsed: PnpmAuditJson & NpmAuditJson;\n try {\n parsed = JSON.parse(raw.stdout || \"{}\") as PnpmAuditJson & NpmAuditJson;\n } catch (err) {\n return { kind: \"error\", reason: `unparseable JSON: ${String(err).slice(0, 100)}` };\n }\n\n // pnpm error envelope: { error: { code, message } }. npm sometimes emits\n // a top-level error too. Either means the audit didn't actually run.\n const errEnvelope = (parsed as unknown as { error?: { code?: string } }).error;\n if (errEnvelope && typeof errEnvelope === \"object\") {\n return { kind: \"error\", reason: errEnvelope.code ?? \"error envelope returned\" };\n }\n\n // Without metadata.vulnerabilities there are no counts to report and we\n // can't trust the result. An empty `{}` is just as suspect as a missing\n // key — counts default to 0 and we'd silently report \"pass\". Treat both\n // as a tool failure so the caller can fall through to the other audit.\n const vulnsMeta = parsed.metadata?.vulnerabilities;\n if (!vulnsMeta || Object.keys(vulnsMeta).length === 0) {\n return { kind: \"error\", reason: \"no metadata.vulnerabilities in output\" };\n }\n\n return { kind: \"ok\", parsed };\n}\n\nexport async function securityAudit(ctx: AuditContext): Promise<AuditResult> {\n const spawn = ctx.spawn ?? defaultSpawn;\n const site = ctx.site;\n const label = siteLabel(site);\n\n let used: \"pnpm audit\" | \"npm audit\" = \"pnpm audit\";\n let result = await runAuditTool(spawn, \"pnpm\", [\"audit\", \"--json\", \"--prod\"], site.path);\n\n // Fall through to npm if pnpm is missing OR pnpm couldn't actually\n // audit the project (e.g., no pnpm-lock.yaml). Previously we only fell\n // through on ENOENT, which meant npm-using sites silently reported \"pass\"\n // because pnpm returned an error envelope with no metadata.\n if (result.kind !== \"ok\") {\n const pnpmReason = result.kind === \"missing\" ? \"not installed\" : result.reason;\n const npmResult = await runAuditTool(\n spawn,\n \"npm\",\n [\"audit\", \"--json\", \"--omit=dev\"],\n site.path,\n );\n if (npmResult.kind === \"ok\") {\n result = npmResult;\n used = \"npm audit\";\n } else {\n const npmReason = npmResult.kind === \"missing\" ? \"not installed\" : npmResult.reason;\n return {\n audit: \"security\",\n site: label,\n status: \"skip\",\n summary: `cannot run audit — pnpm: ${pnpmReason}; npm: ${npmReason}`,\n };\n }\n }\n\n const parsed = result.parsed;\n\n const counts: Counts = {\n low: parsed.metadata?.vulnerabilities?.low ?? 0,\n moderate: parsed.metadata?.vulnerabilities?.moderate ?? 0,\n high: parsed.metadata?.vulnerabilities?.high ?? 0,\n critical: parsed.metadata?.vulnerabilities?.critical ?? 0,\n };\n\n const advisories =\n used === \"pnpm audit\" ? extractAdvisoriesFromPnpm(parsed) : extractAdvisoriesFromNpm(parsed);\n\n const status = classify(counts);\n const total = counts.low + counts.moderate + counts.high + counts.critical;\n const summary =\n status === \"pass\"\n ? `${used}: 0 vulnerabilities`\n : `${used}: ${total} vulnerabilities (${counts.critical}C/${counts.high}H/${counts.moderate}M/${counts.low}L)`;\n\n return {\n audit: \"security\",\n site: label,\n status,\n summary,\n details: { counts, advisories },\n };\n}\n","import { readFile, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { lighthouseConfig } from \"../configs/lighthouse.js\";\nimport { defaultSpawn } from \"./util/spawn.js\";\nimport type { AuditContext } from \"./util/inject.js\";\nimport { readSiteConfig } from \"./util/site-config.js\";\n\ntype ManifestEntry = {\n url: string;\n summary: Record<string, number>;\n htmlPath?: string;\n jsonPath?: string;\n};\n\ntype AssertionResult = {\n name: string;\n actual: number;\n expected: number;\n operator: string;\n passed: boolean;\n level: \"warn\" | \"error\";\n auditProperty?: string;\n auditId?: string;\n};\n\ntype NormalizedLhciResult = {\n summary: Record<string, number>;\n assertionsFailed: number;\n assertions: Array<{ category: string; level: \"warn\" | \"error\"; message: string }>;\n};\n\nasync function readJsonMaybe<T>(path: string): Promise<T | null> {\n try {\n const raw = await readFile(path, \"utf-8\");\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n\nfunction averageSummaries(entries: ManifestEntry[]): Record<string, number> {\n if (entries.length === 0) return {};\n const sums: Record<string, number> = {};\n const counts: Record<string, number> = {};\n for (const e of entries) {\n for (const [k, v] of Object.entries(e.summary ?? {})) {\n if (typeof v !== \"number\") continue;\n sums[k] = (sums[k] ?? 0) + v;\n counts[k] = (counts[k] ?? 0) + 1;\n }\n }\n const out: Record<string, number> = {};\n for (const k of Object.keys(sums)) {\n const total = sums[k] ?? 0;\n const count = counts[k] ?? 1;\n out[k] = total / count;\n }\n return out;\n}\n\nfunction categoryFromAssertion(a: AssertionResult): string {\n // `name` looks like \"categories:accessibility\" or \"audits:uses-http2\".\n const colonIdx = a.name.indexOf(\":\");\n return colonIdx >= 0 ? a.name.slice(colonIdx + 1) : a.name;\n}\n\nfunction messageForAssertion(a: AssertionResult): string {\n return `${a.name} ${a.operator} ${a.expected} (actual: ${a.actual.toFixed(2)})`;\n}\n\nexport async function lighthouseAudit(ctx: AuditContext): Promise<AuditResult> {\n const spawn = ctx.spawn ?? defaultSpawn;\n const site = ctx.site;\n const label = siteLabel(site);\n\n const siteCfg = await readSiteConfig(site.path);\n const resolvedConfig = siteCfg.lighthouseUrl\n ? {\n ...lighthouseConfig,\n ci: {\n ...lighthouseConfig.ci,\n collect: { ...lighthouseConfig.ci.collect, url: [siteCfg.lighthouseUrl] },\n },\n }\n : lighthouseConfig;\n\n const configDir = await mkdtemp(join(tmpdir(), \"reddoor-lhci-\"));\n const configPath = join(configDir, \"lighthouserc.json\");\n await writeFile(configPath, JSON.stringify(resolvedConfig), \"utf-8\");\n\n const resultsDir = join(site.path, \".lighthouseci\");\n // Clear any stale artifacts before the run so we never confuse a failed\n // spawn with old results.\n await rm(resultsDir, { recursive: true, force: true });\n\n let raw;\n try {\n raw = await spawn(\"npx\", [\"--yes\", \"@lhci/cli\", \"autorun\", `--config=${configPath}`], {\n cwd: site.path,\n // lhci autorun boots the site's dev server, downloads Chrome on first\n // use, and runs the audit — easily 2–3 min on a cold tree. The shared\n // 30 s default in runAudits is fine for deps/lint/security but starves\n // lhci.\n timeoutMs: 5 * 60_000,\n });\n } catch (err) {\n await rm(configDir, { recursive: true, force: true });\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) {\n return {\n audit: \"lighthouse\",\n site: label,\n status: \"skip\",\n summary: \"npx/@lhci/cli not available\",\n };\n }\n throw err;\n }\n await rm(configDir, { recursive: true, force: true });\n\n const manifest = await readJsonMaybe<ManifestEntry[]>(join(resultsDir, \"manifest.json\"));\n\n if (!manifest || manifest.length === 0) {\n return {\n audit: \"lighthouse\",\n site: label,\n status: \"fail\",\n summary: `lighthouse: no manifest written (exit ${raw.code})${\n raw.stderr ? ` — ${raw.stderr.slice(0, 200)}` : \"\"\n }`,\n };\n }\n\n const assertionResults =\n (await readJsonMaybe<AssertionResult[]>(join(resultsDir, \"assertion-results.json\"))) ?? [];\n\n const failed = assertionResults.filter((a) => !a.passed);\n const assertions = failed.map((a) => ({\n category: categoryFromAssertion(a),\n level: a.level,\n message: messageForAssertion(a),\n }));\n\n const anyError = assertions.some((a) => a.level === \"error\");\n const anyWarn = assertions.some((a) => a.level === \"warn\");\n const status: AuditResult[\"status\"] = anyError ? \"fail\" : anyWarn ? \"warn\" : \"pass\";\n\n const normalized: NormalizedLhciResult = {\n summary: averageSummaries(manifest),\n assertionsFailed: failed.length,\n assertions,\n };\n\n const summary =\n status === \"pass\"\n ? \"lighthouse: all categories passing\"\n : `lighthouse: ${failed.length} assertion(s) failed`;\n\n return {\n audit: \"lighthouse\",\n site: label,\n status,\n summary,\n details: normalized,\n };\n}\n","export const lighthouseConfig = {\n ci: {\n collect: {\n url: [\"http://localhost:5173/dev/a11y-fixtures\"],\n // `npm run vite:dev` works on both pnpm and npm sites — pnpm respects\n // the `run` form too. Keeps this config portable across the fleet\n // while sites transition to pnpm.\n startServerCommand: \"npm run vite:dev\",\n startServerReadyPattern: \"ready in\",\n startServerReadyTimeout: 120_000,\n numberOfRuns: 1,\n settings: {\n preset: \"desktop\",\n skipAudits: [\"uses-http2\"],\n },\n },\n assert: {\n assertions: {\n \"categories:accessibility\": [\"error\", { minScore: 0.95 }],\n \"categories:best-practices\": [\"error\", { minScore: 0.9 }],\n \"categories:seo\": [\"error\", { minScore: 0.9 }],\n \"categories:performance\": [\"warn\", { minScore: 0.7 }],\n },\n },\n upload: {\n target: \"temporary-public-storage\",\n },\n },\n} as const;\n\nexport default lighthouseConfig;\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport type SiteConfig = {\n /** Override URL the lighthouse audit hits. Sites without the default\n * `/dev/a11y-fixtures` dev route set this to their homepage. */\n lighthouseUrl?: string;\n};\n\n/**\n * Read per-site overrides from `package.json#reddoor`. Returns `{}` on any\n * failure (missing file, malformed JSON, missing key, wrong type) so every\n * caller can safely fall back to its built-in default. Never throws.\n */\nexport async function readSiteConfig(sitePath: string): Promise<SiteConfig> {\n let raw: string;\n try {\n raw = await readFile(join(sitePath, \"package.json\"), \"utf-8\");\n } catch {\n return {};\n }\n let pkg: unknown;\n try {\n pkg = JSON.parse(raw);\n } catch {\n return {};\n }\n if (!pkg || typeof pkg !== \"object\") return {};\n const cfg = (pkg as { reddoor?: unknown }).reddoor;\n if (!cfg || typeof cfg !== \"object\") return {};\n\n const out: SiteConfig = {};\n const url = (cfg as { lighthouseUrl?: unknown }).lighthouseUrl;\n if (typeof url === \"string\" && url.length > 0) {\n out.lighthouseUrl = url;\n }\n return out;\n}\n","import { readFile, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AuditResult } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { a11yRoutes } from \"../configs/playwright-a11y.js\";\nimport { defaultSpawn } from \"./util/spawn.js\";\nimport type { AuditContext } from \"./util/inject.js\";\n\ntype Impact = \"minor\" | \"moderate\" | \"serious\" | \"critical\";\n\ntype AxeViolation = {\n id: string;\n impact: Impact;\n route: string;\n help?: string;\n helpUrl?: string;\n nodes?: Array<{ html?: string; target?: string[] }>;\n};\n\ntype NormalizedA11y = {\n totalViolations: number;\n byImpact: Partial<Record<Impact, number>>;\n violations: AxeViolation[];\n};\n\nconst RESULTS_REL = \".reddoor-a11y/results.json\";\n\nasync function readJsonMaybe<T>(path: string): Promise<T | null> {\n try {\n const raw = await readFile(path, \"utf-8\");\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n\n// The spec the audit writes runs all configured routes through axe in a single\n// test (so worker isolation doesn't fragment the collected violations) and\n// writes the structured result to <cwd>/.reddoor-a11y/results.json before\n// asserting. That way, the audit can read real axe details even when the\n// expect(...).toEqual([]) assertion fails.\nfunction buildSpec(): string {\n return `import { test, expect } from \"@playwright/test\";\nimport AxeBuilder from \"@axe-core/playwright\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nconst pages = ${JSON.stringify(a11yRoutes)};\nconst OUTPUT = process.env.REDDOOR_A11Y_OUTPUT;\n\n// Playwright's default per-test timeout is 30s. We loop through every\n// configured route in a single test, so the budget needs to scale.\ntest.setTimeout(5 * 60_000);\n\ntest(\"a11y across configured routes\", async ({ page }) => {\n const violations = [];\n for (const { path, name } of pages) {\n await page.goto(path);\n const results = await new AxeBuilder({ page })\n .withTags([\"wcag2a\",\"wcag2aa\",\"wcag21a\",\"wcag21aa\",\"wcag22aa\"])\n .analyze();\n for (const v of results.violations) {\n violations.push({\n id: v.id,\n impact: v.impact ?? \"moderate\",\n route: name,\n help: v.help,\n helpUrl: v.helpUrl,\n nodes: v.nodes.map((n) => ({ html: n.html, target: n.target })),\n });\n }\n }\n const byImpact = {};\n for (const v of violations) {\n byImpact[v.impact] = (byImpact[v.impact] ?? 0) + 1;\n }\n if (OUTPUT) {\n await mkdir(dirname(OUTPUT), { recursive: true });\n await writeFile(\n OUTPUT,\n JSON.stringify({ totalViolations: violations.length, byImpact, violations }, null, 2),\n );\n }\n expect(violations).toEqual([]);\n});\n`;\n}\n\nexport async function a11yAudit(ctx: AuditContext): Promise<AuditResult> {\n const spawn = ctx.spawn ?? defaultSpawn;\n const site = ctx.site;\n const label = siteLabel(site);\n\n const specDir = await mkdtemp(join(tmpdir(), \"reddoor-a11y-spec-\"));\n const specPath = join(specDir, \"a11y.spec.ts\");\n await writeFile(specPath, buildSpec(), \"utf-8\");\n\n const resultsPath = join(site.path, RESULTS_REL);\n // Clear stale artifacts so a failed spawn never reports old data.\n await rm(join(site.path, \".reddoor-a11y\"), { recursive: true, force: true });\n\n let raw;\n try {\n raw = await spawn(\"npx\", [\"--yes\", \"playwright\", \"test\", \"--reporter=line\", specPath], {\n cwd: site.path,\n env: { ...process.env, REDDOOR_A11Y_OUTPUT: resultsPath },\n });\n } catch (err) {\n await rm(specDir, { recursive: true, force: true });\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) {\n return {\n audit: \"a11y\",\n site: label,\n status: \"skip\",\n summary: \"npx/playwright not available\",\n };\n }\n throw err;\n }\n await rm(specDir, { recursive: true, force: true });\n\n const artifact = await readJsonMaybe<NormalizedA11y>(resultsPath);\n\n if (!artifact) {\n return {\n audit: \"a11y\",\n site: label,\n status: \"fail\",\n summary: `a11y: no results written (exit ${raw.code})${\n raw.stderr ? ` — ${raw.stderr.slice(0, 200)}` : \"\"\n }`,\n };\n }\n\n const hasSerious = (artifact.byImpact.serious ?? 0) > 0 || (artifact.byImpact.critical ?? 0) > 0;\n const hasAny = artifact.totalViolations > 0;\n\n const status: AuditResult[\"status\"] = hasSerious ? \"fail\" : hasAny ? \"warn\" : \"pass\";\n const summary =\n status === \"pass\"\n ? `a11y: 0 violations across ${a11yRoutes.length} routes`\n : `a11y: ${artifact.totalViolations} violations`;\n\n return {\n audit: \"a11y\",\n site: label,\n status,\n summary,\n details: artifact,\n };\n}\n","import { defineConfig, devices, type PlaywrightTestConfig } from \"@playwright/test\";\n\nexport type A11yRoute = { path: string; name: string };\n\nexport const a11yRoutes: A11yRoute[] = [\n { path: \"/dev/a11y-fixtures\", name: \"a11y fixtures\" },\n { path: \"/dev/animate-in\", name: \"animate-in demo\" },\n];\n\nexport const playwrightA11yConfig: PlaywrightTestConfig = defineConfig({\n testDir: \"tests\",\n testMatch: /.*\\.spec\\.ts$/,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: process.env.CI ? \"github\" : \"list\",\n use: {\n baseURL: \"http://localhost:5173\",\n trace: \"on-first-retry\",\n },\n projects: [\n {\n name: \"chromium\",\n use: { ...devices[\"Desktop Chrome\"] },\n },\n ],\n webServer: {\n // Portable across pnpm and npm sites — pnpm respects `npm run` too.\n command: \"npm run vite:dev\",\n url: \"http://localhost:5173/dev/a11y-fixtures\",\n reuseExistingServer: !process.env.CI,\n timeout: 120_000,\n },\n});\n\nexport default playwrightA11yConfig;\n","import type { AuditName, AuditResult, Site } from \"../types.js\";\nimport type { AuditContext } from \"./util/inject.js\";\nimport { defaultSpawn } from \"./util/spawn.js\";\nimport type { SpawnFn } from \"./util/spawn.js\";\nimport { depsAudit } from \"./deps.js\";\nimport { lintAudit } from \"./lint.js\";\nimport { securityAudit } from \"./security.js\";\nimport { lighthouseAudit } from \"./lighthouse.js\";\nimport { a11yAudit } from \"./a11y.js\";\n\nconst REGISTRY: Record<AuditName, (ctx: AuditContext) => Promise<AuditResult>> = {\n deps: depsAudit,\n lint: lintAudit,\n security: securityAudit,\n lighthouse: lighthouseAudit,\n a11y: a11yAudit,\n};\n\nexport const ALL_AUDIT_NAMES = Object.keys(REGISTRY) as AuditName[];\n\n/** Default per-audit spawn timeout when running via runAudits (30 s). */\nconst DEFAULT_AUDIT_TIMEOUT_MS = 30_000;\n\nfunction timedSpawn(timeoutMs: number): SpawnFn {\n return (cmd, args, opts = {}) =>\n defaultSpawn(cmd, args, { ...opts, timeoutMs: opts.timeoutMs ?? timeoutMs });\n}\n\nexport async function runAudits(site: Site, which?: AuditName[]): Promise<AuditResult[]> {\n const names = which ?? ALL_AUDIT_NAMES;\n for (const n of names) {\n if (!(n in REGISTRY)) throw new Error(`unknown audit: ${n}`);\n }\n const spawn = timedSpawn(DEFAULT_AUDIT_TIMEOUT_MS);\n const label = site.name ?? site.path;\n return Promise.all(\n names.map((n) =>\n REGISTRY[n]({ site, spawn }).catch(\n (err): AuditResult => ({\n audit: n,\n site: label,\n status: \"fail\",\n summary: `${n}: unexpected error — ${String(err)}`,\n }),\n ),\n ),\n );\n}\n\nexport async function runAuditsAcross(sites: Site[], which?: AuditName[]): Promise<AuditResult[]> {\n const all = await Promise.all(sites.map((s) => runAudits(s, which)));\n return all.flat();\n}\n\nexport { depsAudit, lintAudit, securityAudit, lighthouseAudit, a11yAudit };\n","import { pathToFileURL } from \"node:url\";\nimport { resolve, extname } from \"node:path\";\nimport type { InventoryProvider, Site } from \"../../types.js\";\nimport { localPath } from \"../../inventory/local.js\";\nimport { fromJsonFile } from \"../../inventory/json.js\";\n\nexport type ResolveSitesInput = {\n site?: string;\n fleet?: string;\n /** Optional workdir for the `--fleet airtable` keyword path (computes site.path as {workdir}/{slug}). */\n workdir?: string;\n cwd: string;\n};\n\nexport async function resolveSites(input: ResolveSitesInput): Promise<Site[]> {\n if (input.site && input.fleet) {\n throw Object.assign(new Error(\"cannot combine a positional [site] with --fleet\"), {\n exitCode: 2,\n });\n }\n\n if (input.fleet === \"airtable\") {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const { fromAirtableBase } = await import(\"../../inventory/airtable.js\");\n const base = openBase(readAirtableConfig());\n const provider = fromAirtableBase(base, input.workdir ? { workdir: input.workdir } : {});\n return provider();\n }\n\n if (input.fleet) {\n const fleetPath = resolve(input.cwd, input.fleet);\n const ext = extname(fleetPath).toLowerCase();\n let provider: InventoryProvider;\n if (ext === \".json\") {\n provider = fromJsonFile(fleetPath);\n } else if (ext === \".js\" || ext === \".mjs\" || ext === \".cjs\") {\n const mod = (await import(pathToFileURL(fleetPath).href)) as {\n default?: InventoryProvider;\n };\n if (!mod.default || typeof mod.default !== \"function\") {\n throw Object.assign(new Error(`--fleet ${input.fleet}: default export is not a function`), {\n exitCode: 2,\n });\n }\n provider = mod.default;\n } else {\n throw Object.assign(\n new Error(`--fleet ${input.fleet}: unsupported extension ${ext || \"(none)\"}`),\n { exitCode: 2 },\n );\n }\n return provider();\n }\n\n return localPath(resolve(input.cwd, input.site ?? input.cwd))();\n}\n","import { basename } from \"node:path\";\nimport type { InventoryProvider, Site } from \"../types.js\";\n\nexport type LocalPathOptions = {\n name?: string;\n};\n\nexport function localPath(path: string, opts: LocalPathOptions = {}): InventoryProvider {\n const site: Site = { path, name: opts.name ?? basename(path) };\n return async () => [site];\n}\n","import { readFile } from \"node:fs/promises\";\nimport { isAbsolute } from \"node:path\";\nimport type { InventoryProvider, Site } from \"../types.js\";\n\nfunction validate(raw: unknown): Site[] {\n if (!Array.isArray(raw)) {\n throw new Error(\"inventory JSON must be an array of sites\");\n }\n return raw.map((entry, i) => {\n if (!entry || typeof entry !== \"object\") {\n throw new Error(`inventory entry ${i} is not an object`);\n }\n const e = entry as Record<string, unknown>;\n if (typeof e.path !== \"string\" || e.path.length === 0) {\n throw new Error(`inventory entry ${i} is missing required field: path`);\n }\n if (!isAbsolute(e.path)) {\n throw new Error(\n `inventory entry ${i}: path must be absolute (got \"${e.path}\"). ` +\n `Relative paths are rejected so cwd at invocation can't change which site is targeted.`,\n );\n }\n const site: Site = { path: e.path };\n if (typeof e.name === \"string\") site.name = e.name;\n if (typeof e.repoUrl === \"string\") site.repoUrl = e.repoUrl;\n if (typeof e.meta === \"object\" && e.meta !== null) {\n site.meta = e.meta as Record<string, unknown>;\n }\n return site;\n });\n}\n\nexport function fromJsonFile(path: string): InventoryProvider {\n return async () => {\n const raw = JSON.parse(await readFile(path, \"utf-8\")) as unknown;\n return validate(raw);\n };\n}\n","import { stat, readdir, mkdir } from \"node:fs/promises\";\nimport { isAbsolute, join } from \"node:path\";\nimport type { Site } from \"../../types.js\";\nimport { defaultSpawn, type SpawnFn } from \"../../audits/util/spawn.js\";\n\nexport type CloneIfNeededOptions = {\n workdir: string;\n spawn?: SpawnFn;\n};\n\nfunction deriveNameFromRepoUrl(repoUrl: string): string {\n const slash = repoUrl.split(\"/\").pop() ?? repoUrl;\n return slash.replace(/\\.git$/, \"\");\n}\n\n/** Reject names that would let an inventory entry write outside `workdir`. */\nfunction assertSafeName(name: string): void {\n if (isAbsolute(name)) {\n throw new Error(`unsafe site name (absolute path not allowed): ${name}`);\n }\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n throw new Error(`unsafe site name (path separator not allowed): ${name}`);\n }\n if (name.split(/[\\\\/]/).some((seg) => seg === \"..\")) {\n throw new Error(`unsafe site name (traversal segment not allowed): ${name}`);\n }\n}\n\n/**\n * Reject repo URLs that don't look like a normal clone target. Without this,\n * a `repoUrl` starting with `-` would be interpreted by git as a flag\n * (CVE-2017-1000117 family) — e.g. `--upload-pack=…` allows arbitrary command\n * execution. Inventory files are usually trusted, but `.mjs`/`.js` inventories\n * could pull from environments or external sources, so harden the boundary.\n *\n * Accepts: `https://`, `http://`, `ssh://`, `git://`, `file://`, and\n * `[user@]host:path` shorthand (e.g. `git@github.com:org/repo.git`).\n */\nfunction assertSafeRepoUrl(repoUrl: string): void {\n if (\n !/^(https?|ssh|git|file):\\/\\//.test(repoUrl) &&\n !/^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+:/.test(repoUrl)\n ) {\n throw new Error(\n `unsafe repoUrl: must start with a scheme (https://, ssh://, git://, file://) ` +\n `or use scp-style \"user@host:path\" (got: ${JSON.stringify(repoUrl)})`,\n );\n }\n}\n\nasync function isNonEmptyDir(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n if (!s.isDirectory()) return false;\n const entries = await readdir(path);\n return entries.length > 0;\n } catch {\n return false;\n }\n}\n\nexport async function cloneIfNeeded(site: Site, opts: CloneIfNeededOptions): Promise<Site> {\n if (await isNonEmptyDir(site.path)) return site;\n\n if (!site.repoUrl) {\n throw new Error(`site path does not exist (${site.path}) and no repoUrl is set — cannot clone`);\n }\n\n const name = site.name ?? deriveNameFromRepoUrl(site.repoUrl);\n assertSafeName(name);\n assertSafeRepoUrl(site.repoUrl);\n const target = join(opts.workdir, name);\n await mkdir(opts.workdir, { recursive: true });\n\n if (await isNonEmptyDir(target)) {\n return { ...site, name, path: target };\n }\n\n const spawn = opts.spawn ?? defaultSpawn;\n // `--` separator so git won't treat repoUrl as a flag if validation slips.\n const result = await spawn(\"git\", [\"clone\", \"--\", site.repoUrl, target], {\n cwd: opts.workdir,\n timeoutMs: 5 * 60_000,\n });\n if (result.code !== 0) {\n throw new Error(`git clone failed (code ${result.code}): ${result.stderr}`);\n }\n return { ...site, name, path: target };\n}\n"],"mappings":"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","names":["readFile","join","resolve","resolve","readFile","join","join","readFile","spawn","readFile","join","readFile","join","readFile","spawn","join","readFile","writeFile","mkdtemp","rm","tmpdir","join","readJsonMaybe","readFile","spawn","mkdtemp","join","tmpdir","writeFile","rm","spawn","readFile","openBase","readAirtableConfig","fromAirtableBase","isAbsolute","join","isAbsolute","join","spawn","resolve","openBase","readAirtableConfig","listWebsites","updateScores","siteSlug","lighthouseScoresFromResult","resolveSlugFromCwd"]}