@reddoorla/maintenance 0.64.0 → 0.65.1

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.
Files changed (105) hide show
  1. package/README.md +17 -10
  2. package/dist/airtable-GGZJUGQB.js +9 -0
  3. package/dist/{announce-QEON4O7E.js → announce-BRBITWUP.js} +24 -9
  4. package/dist/announce-BRBITWUP.js.map +1 -0
  5. package/dist/{bump-deps-2U6GIHM5.js → bump-deps-4BODIOEF.js} +6 -4
  6. package/dist/bump-deps-4BODIOEF.js.map +1 -0
  7. package/dist/{chunk-BMDBOPN3.js → chunk-4A5PYCGB.js} +2 -2
  8. package/dist/{chunk-BLD6AYJO.js → chunk-7RGQ4KLE.js} +5 -2
  9. package/dist/chunk-7RGQ4KLE.js.map +1 -0
  10. package/dist/{chunk-YYUY2OVP.js → chunk-CSTLFSGM.js} +2 -2
  11. package/dist/{chunk-SAOCVNX7.js → chunk-FH7WQHD7.js} +2 -2
  12. package/dist/{chunk-L5F664ZA.js → chunk-G2YRXV26.js} +2 -2
  13. package/dist/chunk-GASBX52O.js +12 -0
  14. package/dist/chunk-GASBX52O.js.map +1 -0
  15. package/dist/{chunk-3HM4S422.js → chunk-IRRZG66U.js} +18 -18
  16. package/dist/chunk-IRRZG66U.js.map +1 -0
  17. package/dist/{chunk-4REUHZAW.js → chunk-LM42CATB.js} +3 -3
  18. package/dist/{chunk-ZEX2T6JH.js → chunk-MQM5YP4J.js} +2 -2
  19. package/dist/{chunk-U3MF6RCS.js → chunk-PBDZGBGL.js} +11 -7
  20. package/dist/chunk-PBDZGBGL.js.map +1 -0
  21. package/dist/{chunk-6ZKM7OIJ.js → chunk-PTPTVVY7.js} +6 -4
  22. package/dist/chunk-PTPTVVY7.js.map +1 -0
  23. package/dist/{chunk-NPJFHBAX.js → chunk-SWSFB6IV.js} +3 -3
  24. package/dist/{chunk-KTPPKO2C.js → chunk-UCXZYS2W.js} +2 -2
  25. package/dist/chunk-VPJ7N6P7.js +27 -0
  26. package/dist/chunk-VPJ7N6P7.js.map +1 -0
  27. package/dist/{chunk-SS7BKOIX.js → chunk-WCN54744.js} +265 -15
  28. package/dist/chunk-WCN54744.js.map +1 -0
  29. package/dist/{chunk-P7VJM46I.js → chunk-XGT3I5AK.js} +4 -4
  30. package/dist/{chunk-J37CZORZ.js → chunk-XUKSP67U.js} +4 -4
  31. package/dist/{chunk-OSRFDGQM.js → chunk-YEGHUODU.js} +18 -1
  32. package/dist/chunk-YEGHUODU.js.map +1 -0
  33. package/dist/cli/bin.js +16 -16
  34. package/dist/cli/bin.js.map +1 -1
  35. package/dist/cli/commands/audit.js +10 -8
  36. package/dist/cli/commands/audit.js.map +1 -1
  37. package/dist/{client-GTMJTXBK.js → client-C2GOZD2D.js} +2 -2
  38. package/dist/{convert-to-pnpm-JG3767ZQ.js → convert-to-pnpm-S4TNJD53.js} +6 -4
  39. package/dist/convert-to-pnpm-S4TNJD53.js.map +1 -0
  40. package/dist/{db-Y6BVTNH7.js → db-YBLQR2YY.js} +2 -2
  41. package/dist/{digest-ABJROO4H.js → digest-R3ROSGLP.js} +3 -3
  42. package/dist/{github-signals-XTJLKOXJ.js → github-signals-R4FNU34K.js} +9 -9
  43. package/dist/index.d.ts +64 -10
  44. package/dist/index.js +18 -11
  45. package/dist/index.js.map +1 -1
  46. package/dist/{init-DMAIKUQE.js → init-GLT5C6S5.js} +25 -6
  47. package/dist/init-GLT5C6S5.js.map +1 -0
  48. package/dist/{launch-IBP23PNY.js → launch-CQM5D6QJ.js} +10 -9
  49. package/dist/{launch-IBP23PNY.js.map → launch-CQM5D6QJ.js.map} +1 -1
  50. package/dist/{lighthouse-airtable-HTEW6BBT.js → lighthouse-airtable-5FHELRCA.js} +3 -3
  51. package/dist/{onboard-TTIC3YZI.js → onboard-ILC3DWY4.js} +10 -6
  52. package/dist/onboard-ILC3DWY4.js.map +1 -0
  53. package/dist/{orchestrate-CYYZ737H.js → orchestrate-45URUFEK.js} +6 -6
  54. package/dist/{renovate-dispatch-QDDMTATD.js → renovate-dispatch-G4T73IUR.js} +3 -3
  55. package/dist/{report-QNE7HFOZ.js → report-BZ6QNFNM.js} +6 -6
  56. package/dist/{self-updating-2RLUP4AA.js → self-updating-DEZNP5OK.js} +9 -6
  57. package/dist/self-updating-DEZNP5OK.js.map +1 -0
  58. package/dist/{selftest-5ELLRH6Q.js → selftest-TY7X5ARI.js} +8 -8
  59. package/dist/{svelte-codemods-BPLKSVIA.js → svelte-codemods-DMLMOKSD.js} +6 -4
  60. package/dist/svelte-codemods-DMLMOKSD.js.map +1 -0
  61. package/dist/{sync-configs-VFRV3TJ4.js → sync-configs-DWKXETFO.js} +10 -4
  62. package/dist/sync-configs-DWKXETFO.js.map +1 -0
  63. package/dist/{upgrade-WCFD2TRN.js → upgrade-RUX2ODMW.js} +6 -8
  64. package/dist/upgrade-RUX2ODMW.js.map +1 -0
  65. package/dist/{websites-HM2NMZMR.js → websites-CSVLSEVL.js} +2 -2
  66. package/dist/{write-audits-to-airtable-QF26PCAR.js → write-audits-to-airtable-JCV7FYAW.js} +4 -4
  67. package/package.json +1 -1
  68. package/dist/airtable-HHAWHBNM.js +0 -9
  69. package/dist/announce-QEON4O7E.js.map +0 -1
  70. package/dist/bump-deps-2U6GIHM5.js.map +0 -1
  71. package/dist/chunk-3HM4S422.js.map +0 -1
  72. package/dist/chunk-6ZKM7OIJ.js.map +0 -1
  73. package/dist/chunk-BLD6AYJO.js.map +0 -1
  74. package/dist/chunk-OSRFDGQM.js.map +0 -1
  75. package/dist/chunk-SS7BKOIX.js.map +0 -1
  76. package/dist/chunk-U3MF6RCS.js.map +0 -1
  77. package/dist/convert-to-pnpm-JG3767ZQ.js.map +0 -1
  78. package/dist/init-DMAIKUQE.js.map +0 -1
  79. package/dist/onboard-TTIC3YZI.js.map +0 -1
  80. package/dist/self-updating-2RLUP4AA.js.map +0 -1
  81. package/dist/svelte-codemods-BPLKSVIA.js.map +0 -1
  82. package/dist/sync-configs-VFRV3TJ4.js.map +0 -1
  83. package/dist/upgrade-WCFD2TRN.js.map +0 -1
  84. /package/dist/{airtable-HHAWHBNM.js.map → airtable-GGZJUGQB.js.map} +0 -0
  85. /package/dist/{chunk-BMDBOPN3.js.map → chunk-4A5PYCGB.js.map} +0 -0
  86. /package/dist/{chunk-YYUY2OVP.js.map → chunk-CSTLFSGM.js.map} +0 -0
  87. /package/dist/{chunk-SAOCVNX7.js.map → chunk-FH7WQHD7.js.map} +0 -0
  88. /package/dist/{chunk-L5F664ZA.js.map → chunk-G2YRXV26.js.map} +0 -0
  89. /package/dist/{chunk-4REUHZAW.js.map → chunk-LM42CATB.js.map} +0 -0
  90. /package/dist/{chunk-ZEX2T6JH.js.map → chunk-MQM5YP4J.js.map} +0 -0
  91. /package/dist/{chunk-NPJFHBAX.js.map → chunk-SWSFB6IV.js.map} +0 -0
  92. /package/dist/{chunk-KTPPKO2C.js.map → chunk-UCXZYS2W.js.map} +0 -0
  93. /package/dist/{chunk-P7VJM46I.js.map → chunk-XGT3I5AK.js.map} +0 -0
  94. /package/dist/{chunk-J37CZORZ.js.map → chunk-XUKSP67U.js.map} +0 -0
  95. /package/dist/{client-GTMJTXBK.js.map → client-C2GOZD2D.js.map} +0 -0
  96. /package/dist/{db-Y6BVTNH7.js.map → db-YBLQR2YY.js.map} +0 -0
  97. /package/dist/{digest-ABJROO4H.js.map → digest-R3ROSGLP.js.map} +0 -0
  98. /package/dist/{github-signals-XTJLKOXJ.js.map → github-signals-R4FNU34K.js.map} +0 -0
  99. /package/dist/{lighthouse-airtable-HTEW6BBT.js.map → lighthouse-airtable-5FHELRCA.js.map} +0 -0
  100. /package/dist/{orchestrate-CYYZ737H.js.map → orchestrate-45URUFEK.js.map} +0 -0
  101. /package/dist/{renovate-dispatch-QDDMTATD.js.map → renovate-dispatch-G4T73IUR.js.map} +0 -0
  102. /package/dist/{report-QNE7HFOZ.js.map → report-BZ6QNFNM.js.map} +0 -0
  103. /package/dist/{selftest-5ELLRH6Q.js.map → selftest-TY7X5ARI.js.map} +0 -0
  104. /package/dist/{websites-HM2NMZMR.js.map → websites-CSVLSEVL.js.map} +0 -0
  105. /package/dist/{write-audits-to-airtable-QF26PCAR.js.map → write-audits-to-airtable-JCV7FYAW.js.map} +0 -0
@@ -5,10 +5,10 @@ import {
5
5
  } from "./chunk-4NAOR44V.js";
6
6
  import {
7
7
  resolveSites
8
- } from "./chunk-SAOCVNX7.js";
8
+ } from "./chunk-FH7WQHD7.js";
9
9
  import {
10
10
  init
11
- } from "./chunk-KTPPKO2C.js";
11
+ } from "./chunk-UCXZYS2W.js";
12
12
  import "./chunk-2X322ZES.js";
13
13
  import "./chunk-FUD4I2ZW.js";
14
14
  import "./chunk-OQPMZ6BM.js";
@@ -17,11 +17,15 @@ import "./chunk-XVR7UM6Z.js";
17
17
  import "./chunk-XKOZUVK2.js";
18
18
  import "./chunk-CASCWQQX.js";
19
19
  import "./chunk-OTGB6YQO.js";
20
- import "./chunk-SS7BKOIX.js";
20
+ import "./chunk-WCN54744.js";
21
21
  import "./chunk-LKGVSM2O.js";
22
+ import "./chunk-GASBX52O.js";
23
+ import "./chunk-YEGHUODU.js";
22
24
  import "./chunk-FDIFTIXC.js";
23
25
  import "./chunk-HVOOCK6L.js";
24
- import "./chunk-XXTZBPUY.js";
26
+ import {
27
+ siteLabel
28
+ } from "./chunk-XXTZBPUY.js";
25
29
  import "./chunk-XTK5VIQB.js";
26
30
  import "./chunk-U5SZ4FOP.js";
27
31
  import "./chunk-EYODPUHV.js";
@@ -74,7 +78,22 @@ async function runInitCommand(site, opts) {
74
78
  skipped = prep.skipped;
75
79
  }
76
80
  const results = [];
77
- for (const s of sites) results.push(await init(s));
81
+ for (const s of sites) {
82
+ try {
83
+ results.push(await init(s));
84
+ } catch (err) {
85
+ results.push({
86
+ site: siteLabel(s),
87
+ steps: [
88
+ {
89
+ name: "init",
90
+ result: { kind: "error", message: err instanceof Error ? err.message : String(err) }
91
+ }
92
+ ],
93
+ complete: false
94
+ });
95
+ }
96
+ }
78
97
  const output = results.map(formatResult).join("\n\n");
79
98
  const code = results.some((r) => exitCodeFor(r) !== 0) ? 1 : 0;
80
99
  return { output: appendSkipNotice(output, skipped), code };
@@ -82,4 +101,4 @@ async function runInitCommand(site, opts) {
82
101
  export {
83
102
  runInitCommand
84
103
  };
85
- //# sourceMappingURL=init-DMAIKUQE.js.map
104
+ //# sourceMappingURL=init-GLT5C6S5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/init.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { init, type InitResult, type InitStepResult } from \"../../recipes/init.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { prepareFleetSites, appendSkipNotice, type SkippedSite } from \"../fleet/prepare-sites.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\nimport { siteLabel } from \"../../util/site.js\";\n\nexport type InitCommandOptions = {\n fleet?: string;\n workdir?: string;\n cwd?: string;\n};\n\nfunction formatStep(name: string, r: InitStepResult): string {\n if (r.kind === \"error\") return `${name.padEnd(20)} error: ${r.message}`;\n if (r.kind === \"audit\") {\n const lines = r.results.map(\n (a) => ` ${a.audit.padEnd(12)} ${a.status.padEnd(5)} ${a.summary}`,\n );\n return `${name.padEnd(20)} ${r.results.length} audit(s):\\n${lines.join(\"\\n\")}`;\n }\n const rec = r.result;\n if (rec.status === \"noop\") return `${name.padEnd(20)} noop${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n if (rec.status === \"failed\")\n return `${name.padEnd(20)} failed${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n return `${name.padEnd(20)} applied (${rec.commits.length} commit${rec.commits.length === 1 ? \"\" : \"s\"})${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n}\n\nfunction formatResult(r: InitResult): string {\n const header = `[${r.site}] init — ${r.complete ? \"complete\" : \"STOPPED\"}`;\n const body = r.steps.map((s) => formatStep(s.name, s.result)).join(\"\\n\");\n return `${header}\\n${body}`;\n}\n\nfunction exitCodeFor(r: InitResult): number {\n if (!r.complete) return 1;\n // A complete chain can still surface a failing audit at the end. Treat\n // any `fail`-status audit as exit 1 so CI signals a regression even when\n // every recipe applied cleanly.\n for (const step of r.steps) {\n if (step.result.kind === \"audit\" && step.result.results.some((a) => a.status === \"fail\")) {\n return 1;\n }\n }\n return 0;\n}\n\nexport async function runInitCommand(\n site: string | undefined,\n opts: InitCommandOptions,\n): Promise<{ output: string; code: number }> {\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 cwd,\n });\n\n let skipped: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, { workdir });\n sites = prep.prepared;\n skipped = prep.skipped;\n }\n\n const results: InitResult[] = [];\n for (const s of sites) {\n try {\n results.push(await init(s));\n } catch (err) {\n // Isolate a per-site throw (e.g. a transient git error) so the rest of the\n // fleet still runs — the same guarantee `runRecipeOverSites` gives the other\n // fleet commands, but `init` returns an `InitResult`, so synthesize a stopped\n // one here rather than reuse the shared helper.\n results.push({\n site: siteLabel(s),\n steps: [\n {\n name: \"init\",\n result: { kind: \"error\", message: err instanceof Error ? err.message : String(err) },\n },\n ],\n complete: false,\n });\n }\n }\n\n const output = results.map(formatResult).join(\"\\n\\n\");\n const code = results.some((r) => exitCodeFor(r) !== 0) ? 1 : 0;\n return { output: appendSkipNotice(output, skipped), code };\n}\n"],"mappings":"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","names":[]}
@@ -1,18 +1,17 @@
1
1
  import {
2
2
  writeAuditsToAirtable
3
- } from "./chunk-4REUHZAW.js";
3
+ } from "./chunk-LM42CATB.js";
4
4
  import {
5
5
  hasRealScores,
6
6
  lighthouseScoresFromResult
7
- } from "./chunk-YYUY2OVP.js";
7
+ } from "./chunk-CSTLFSGM.js";
8
8
  import {
9
9
  selfUpdating
10
- } from "./chunk-3HM4S422.js";
11
- import "./chunk-OSRFDGQM.js";
10
+ } from "./chunk-IRRZG66U.js";
12
11
  import "./chunk-5L3FPQIJ.js";
13
12
  import {
14
13
  resolveSites
15
- } from "./chunk-SAOCVNX7.js";
14
+ } from "./chunk-FH7WQHD7.js";
16
15
  import {
17
16
  queueDraft
18
17
  } from "./chunk-6LIWDIXU.js";
@@ -20,7 +19,7 @@ import {
20
19
  renderReportHtml,
21
20
  resolveCopy,
22
21
  uploadAttachment
23
- } from "./chunk-U3MF6RCS.js";
22
+ } from "./chunk-PBDZGBGL.js";
24
23
  import {
25
24
  createDraft,
26
25
  findReportByPeriod,
@@ -29,8 +28,10 @@ import {
29
28
  import "./chunk-CASCWQQX.js";
30
29
  import {
31
30
  runAudits
32
- } from "./chunk-SS7BKOIX.js";
31
+ } from "./chunk-WCN54744.js";
33
32
  import "./chunk-LKGVSM2O.js";
33
+ import "./chunk-GASBX52O.js";
34
+ import "./chunk-YEGHUODU.js";
34
35
  import "./chunk-FDIFTIXC.js";
35
36
  import "./chunk-HVOOCK6L.js";
36
37
  import {
@@ -41,7 +42,7 @@ import "./chunk-U5SZ4FOP.js";
41
42
  import {
42
43
  listWebsites,
43
44
  siteSlug
44
- } from "./chunk-BLD6AYJO.js";
45
+ } from "./chunk-7RGQ4KLE.js";
45
46
  import {
46
47
  openBase,
47
48
  readAirtableConfig
@@ -211,4 +212,4 @@ async function runLaunchCommand(site, opts) {
211
212
  export {
212
213
  runLaunchCommand
213
214
  };
214
- //# sourceMappingURL=launch-IBP23PNY.js.map
215
+ //# sourceMappingURL=launch-CQM5D6QJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/commands/launch.ts","../src/recipes/launch.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { launch, type LaunchResult, type LaunchStepResult } from \"../../recipes/launch.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\n\nexport type LaunchCommandOptions = {\n cwd?: string;\n};\n\nfunction formatStep(name: string, r: LaunchStepResult): string {\n if (r.kind === \"error\") return `${name.padEnd(20)} error: ${r.message}`;\n if (r.kind === \"audit\") {\n const s = r.scores;\n return `${name.padEnd(20)} audited (P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo})`;\n }\n if (r.kind === \"draft\") {\n return `${name.padEnd(20)} drafted ${r.report.reportId}`;\n }\n const rec = r.result;\n if (rec.status === \"noop\") return `${name.padEnd(20)} noop${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n if (rec.status === \"failed\")\n return `${name.padEnd(20)} failed${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n return `${name.padEnd(20)} applied (${rec.commits.length} commit${rec.commits.length === 1 ? \"\" : \"s\"})${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n}\n\nfunction formatResult(r: LaunchResult): string {\n const header = `[${r.site}] launch — ${r.complete ? \"drafted (awaiting approval)\" : \"STOPPED\"}`;\n const body = r.steps.map((s) => formatStep(s.name, s.result)).join(\"\\n\");\n return `${header}\\n${body}`;\n}\n\n/**\n * `launch <site>` — single-site only. Bootstrap (CI + Renovate), first-audit\n * the site, and DRAFT its launch email into the M3 approve queue. Never sends;\n * the operator approves the draft and the next send run delivers the go-live\n * email (flipping Status → maintenance + stamping Launched at).\n */\nexport async function runLaunchCommand(\n site: string,\n opts: LaunchCommandOptions,\n): Promise<{ output: string; code: number }> {\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n const sites = await resolveSites({ site, cwd });\n const target = sites[0];\n if (!target) {\n return { output: `No site resolved for \"${site}\".`, code: 1 };\n }\n\n const result = await launch(target);\n return { output: formatResult(result), code: result.complete ? 0 : 1 };\n}\n","import type { AuditResult, RecipeResult, Site } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { selfUpdating } from \"./self-updating/index.js\";\nimport { runAudits } from \"../audits/index.js\";\nimport { hasRealScores, lighthouseScoresFromResult } from \"../audits/lighthouse-airtable.js\";\nimport { writeAuditsToAirtable } from \"../audits/write-audits-to-airtable.js\";\nimport { openBase, readAirtableConfig } from \"../reports/airtable/client.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { listWebsites, siteSlug } from \"../reports/airtable/websites.js\";\nimport type { WebsiteRow } from \"../reports/airtable/websites.js\";\nimport {\n createDraft,\n findReportByPeriod,\n updateReportScores,\n} from \"../reports/airtable/reports.js\";\nimport type { ReportRow } from \"../reports/airtable/reports.js\";\nimport { queueDraft } from \"../reports/queue.js\";\nimport { uploadAttachment } from \"../reports/airtable/attachments.js\";\nimport { renderReportHtml } from \"../reports/render.js\";\nimport { resolveCopy } from \"../reports/copy.js\";\nimport type { LighthouseScores } from \"../reports/types.js\";\n\nexport type LaunchStepResult =\n | { kind: \"recipe\"; result: RecipeResult }\n | { kind: \"audit\"; results: AuditResult[]; scores: LighthouseScores }\n | { kind: \"draft\"; report: ReportRow }\n | { kind: \"error\"; message: string };\n\nexport type LaunchResult = {\n site: string;\n steps: Array<{ name: string; result: LaunchStepResult }>;\n /** True if every step ran (bootstrap + audit + draft); false if a step\n * errored or a recipe `failed` short-circuited the chain. */\n complete: boolean;\n};\n\nexport type LaunchDeps = {\n /** Bootstrap step (CI + Renovate). Defaults to the real `selfUpdating`. */\n bootstrap?: (site: Site) => Promise<RecipeResult>;\n /** Audit step. Defaults to the real `runAudits`. */\n audit?: (site: Site) => Promise<AuditResult[]>;\n /** Airtable handle. Defaults to opening the live base from credentials. */\n base?: AirtableBase;\n};\n\n/**\n * Launch a site: bootstrap → first-audit → DRAFT a launch email. The M3\n * approve loop is what actually sends; `launch` never sends — it stops at a\n * dashboard-queued draft (`reportType: \"Launch\"`) carrying the just-audited\n * Lighthouse scores, so `sendOne`'s `report.lighthouse` guard passes.\n *\n * Step-chain (mirrors `init`), stopping on the first error or `failed` recipe:\n * 1. selfUpdating — CI + Renovate + auto-merge.\n * 2. runAudits + write the scores to the site's Websites row (reuses the\n * `audit --write-airtable` writer); the Lighthouse scores feed the draft.\n * 3. createDraft — reportType \"Launch\", today's period, the audited scores.\n */\nexport async function launch(site: Site, deps: LaunchDeps = {}): Promise<LaunchResult> {\n const label = siteLabel(site);\n const bootstrap = deps.bootstrap ?? selfUpdating;\n const audit = deps.audit ?? runAudits;\n const base = deps.base ?? openBase(readAirtableConfig());\n\n const steps: Array<{ name: string; result: LaunchStepResult }> = [];\n const stop = (): LaunchResult => ({ site: label, steps, complete: false });\n\n // 1. Bootstrap.\n let recipe: RecipeResult;\n try {\n recipe = await bootstrap(site);\n } catch (err) {\n steps.push({ name: \"self-updating\", result: errorOf(err) });\n return stop();\n }\n steps.push({ name: \"self-updating\", result: { kind: \"recipe\", result: recipe } });\n if (recipe.status === \"failed\") return stop();\n\n // 2. Audit + write scores back to Airtable.\n let results: AuditResult[];\n try {\n results = await audit(site);\n } catch (err) {\n steps.push({ name: \"audit\", result: errorOf(err) });\n return stop();\n }\n const lhResult = results.find((r) => r.audit === \"lighthouse\");\n if (!lhResult || !hasRealScores(lhResult)) {\n steps.push({\n name: \"audit\",\n result: { kind: \"error\", message: \"lighthouse audit produced no real scores\" },\n });\n return stop();\n }\n // The launch announcement renders a numeric score per category; a metric that\n // errored this run (now null from lighthouseScoresFromResult) keeps the prior\n // 0 behavior here rather than propagating null into the launch-email path. The\n // Airtable write path (write-audits-to-airtable) keeps the null → shows \"—\".\n const rawScores = lighthouseScoresFromResult(lhResult);\n const scores: LighthouseScores = {\n performance: rawScores.performance ?? 0,\n accessibility: rawScores.accessibility ?? 0,\n bestPractices: rawScores.bestPractices ?? 0,\n seo: rawScores.seo ?? 0,\n };\n\n const websites = await listWebsites(base);\n const target = websites.find((w) => siteSlug(w.name) === siteSlug(label));\n if (!target) {\n steps.push({\n name: \"audit\",\n result: { kind: \"error\", message: `no Websites row matched site \"${label}\"` },\n });\n return stop();\n }\n try {\n await writeAuditsToAirtable({ base, websites, slug: siteSlug(target.name), results });\n } catch (err) {\n steps.push({ name: \"audit\", result: errorOf(err) });\n return stop();\n }\n steps.push({ name: \"audit\", result: { kind: \"audit\", results, scores } });\n\n // 3. Draft the launch email (reuses draft.ts's reportId/period scheme). DRAFTS\n // ONLY — the M3 approve loop sends it and flips Status on send.\n const today = new Date();\n const period = today.toISOString().slice(0, 7);\n const slug = siteSlug(target.name);\n\n let report: ReportRow;\n try {\n // Re-run dedupe: reuse an existing Launch row for this (site, period) instead\n // of stacking a second draft. findReportByPeriod is the same idempotency\n // lookup draft.ts documents (dashboard/digest point lookup).\n const existing = await findReportByPeriod(base, target.id, \"Launch\", period);\n if (existing) {\n // Reuse path: the row was created on a prior run with THAT run's scores. This\n // re-run just produced fresh audit scores AND will re-render the preview from\n // them — so refresh the row's Lighthouse cells (+ Completed on) to match,\n // otherwise the sent email (which reads the row) ships stale scores. The\n // create path already writes fresh scores via createDraft.\n await updateReportScores(base, existing.id, scores, today);\n report = existing;\n } else {\n report = await createDraft(base, draftInputFor(target, scores, today, period));\n }\n } catch (err) {\n steps.push({ name: \"draft\", result: errorOf(err) });\n return stop();\n }\n\n // Mirror draft.ts:135-154 — render → upload \"Rendered HTML\" preview → flip\n // Draft ready. Without setDraftReady the draft never enters the approve queue\n // (every pending-approval gate requires draftReady true), so it can never be\n // approved or sent. The upload is a review convenience; the ready flag is the\n // critical step.\n try {\n const { html } = await renderReportHtml({\n siteName: target.name,\n siteUrl: target.url,\n reportType: \"Launch\",\n completedOn: today,\n lighthouse: scores,\n lastTestedDate: null,\n commentary: null,\n copy: resolveCopy(target),\n headerImageCid: `${slug}-header`,\n });\n // A preview-upload hiccup must NOT fail the launch — log and continue.\n try {\n await uploadAttachment(\n report.id,\n \"Rendered HTML\",\n html,\n `${slug}-${today.toISOString().slice(0, 10)}.html`,\n \"text/html\",\n );\n } catch (uploadErr) {\n console.warn(\n `⚠ Launch preview upload skipped for ${target.name}: ${\n uploadErr instanceof Error ? uploadErr.message : String(uploadErr)\n }`,\n );\n }\n // Critical: NOT wrapped — a failure here must surface as a failed launch. queueDraft\n // supersedes any lower-tier (Maintenance/Testing) drafts queued for this site; a Launch is\n // top tier so it only stands down if another Launch/Announcement is already queued.\n await queueDraft(base, { id: report.id, siteId: target.id, reportType: \"Launch\" });\n } catch (err) {\n steps.push({ name: \"draft\", result: errorOf(err) });\n return stop();\n }\n\n steps.push({ name: \"draft\", result: { kind: \"draft\", report } });\n\n return { site: label, steps, complete: true };\n}\n\n/** Build the Launch `DraftInput`. reportId/period mirror `draftReportForSite`\n * (draft.ts) — do not invent a new id scheme. `today`/`period` are threaded in\n * from `launch()` so a single timestamp drives the render, the draft, the\n * dedupe lookup, and the preview filename. Launch reports have no period window\n * and no prior maintenance test, so periodStart/periodEnd/completedOn all\n * collapse to \"today\" and `lastTestedDate` is null. */\nfunction draftInputFor(\n target: WebsiteRow,\n scores: LighthouseScores,\n today: Date,\n period: string,\n): Parameters<typeof createDraft>[1] {\n const reportType = \"Launch\" as const;\n const reportId = `${target.name} — ${reportType} — ${today.toISOString().slice(0, 10)}`;\n return {\n reportId,\n siteId: target.id,\n reportType,\n period,\n periodStart: today,\n periodEnd: today,\n completedOn: today,\n lighthouse: scores,\n lastTestedDate: null,\n };\n}\n\nfunction errorOf(err: unknown): LaunchStepResult {\n return { kind: \"error\", message: err instanceof Error ? err.message : String(err) };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/cli/commands/launch.ts","../src/recipes/launch.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { launch, type LaunchResult, type LaunchStepResult } from \"../../recipes/launch.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\n\nexport type LaunchCommandOptions = {\n cwd?: string;\n};\n\nfunction formatStep(name: string, r: LaunchStepResult): string {\n if (r.kind === \"error\") return `${name.padEnd(20)} error: ${r.message}`;\n if (r.kind === \"audit\") {\n const s = r.scores;\n return `${name.padEnd(20)} audited (P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo})`;\n }\n if (r.kind === \"draft\") {\n return `${name.padEnd(20)} drafted ${r.report.reportId}`;\n }\n const rec = r.result;\n if (rec.status === \"noop\") return `${name.padEnd(20)} noop${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n if (rec.status === \"failed\")\n return `${name.padEnd(20)} failed${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n return `${name.padEnd(20)} applied (${rec.commits.length} commit${rec.commits.length === 1 ? \"\" : \"s\"})${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n}\n\nfunction formatResult(r: LaunchResult): string {\n const header = `[${r.site}] launch — ${r.complete ? \"drafted (awaiting approval)\" : \"STOPPED\"}`;\n const body = r.steps.map((s) => formatStep(s.name, s.result)).join(\"\\n\");\n return `${header}\\n${body}`;\n}\n\n/**\n * `launch <site>` — single-site only. Bootstrap (CI + Renovate), first-audit\n * the site, and DRAFT its launch email into the M3 approve queue. Never sends;\n * the operator approves the draft and the next send run delivers the go-live\n * email (flipping Status → maintenance + stamping Launched at).\n */\nexport async function runLaunchCommand(\n site: string,\n opts: LaunchCommandOptions,\n): Promise<{ output: string; code: number }> {\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n const sites = await resolveSites({ site, cwd });\n const target = sites[0];\n if (!target) {\n return { output: `No site resolved for \"${site}\".`, code: 1 };\n }\n\n const result = await launch(target);\n return { output: formatResult(result), code: result.complete ? 0 : 1 };\n}\n","import type { AuditResult, RecipeResult, Site } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { selfUpdating } from \"./self-updating/index.js\";\nimport { runAudits } from \"../audits/index.js\";\nimport { hasRealScores, lighthouseScoresFromResult } from \"../audits/lighthouse-airtable.js\";\nimport { writeAuditsToAirtable } from \"../audits/write-audits-to-airtable.js\";\nimport { openBase, readAirtableConfig } from \"../reports/airtable/client.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { listWebsites, siteSlug } from \"../reports/airtable/websites.js\";\nimport type { WebsiteRow } from \"../reports/airtable/websites.js\";\nimport {\n createDraft,\n findReportByPeriod,\n updateReportScores,\n} from \"../reports/airtable/reports.js\";\nimport type { ReportRow } from \"../reports/airtable/reports.js\";\nimport { queueDraft } from \"../reports/queue.js\";\nimport { uploadAttachment } from \"../reports/airtable/attachments.js\";\nimport { renderReportHtml } from \"../reports/render.js\";\nimport { resolveCopy } from \"../reports/copy.js\";\nimport type { LighthouseScores } from \"../reports/types.js\";\n\nexport type LaunchStepResult =\n | { kind: \"recipe\"; result: RecipeResult }\n | { kind: \"audit\"; results: AuditResult[]; scores: LighthouseScores }\n | { kind: \"draft\"; report: ReportRow }\n | { kind: \"error\"; message: string };\n\nexport type LaunchResult = {\n site: string;\n steps: Array<{ name: string; result: LaunchStepResult }>;\n /** True if every step ran (bootstrap + audit + draft); false if a step\n * errored or a recipe `failed` short-circuited the chain. */\n complete: boolean;\n};\n\nexport type LaunchDeps = {\n /** Bootstrap step (CI + Renovate). Defaults to the real `selfUpdating`. */\n bootstrap?: (site: Site) => Promise<RecipeResult>;\n /** Audit step. Defaults to the real `runAudits`. */\n audit?: (site: Site) => Promise<AuditResult[]>;\n /** Airtable handle. Defaults to opening the live base from credentials. */\n base?: AirtableBase;\n};\n\n/**\n * Launch a site: bootstrap → first-audit → DRAFT a launch email. The M3\n * approve loop is what actually sends; `launch` never sends — it stops at a\n * dashboard-queued draft (`reportType: \"Launch\"`) carrying the just-audited\n * Lighthouse scores, so `sendOne`'s `report.lighthouse` guard passes.\n *\n * Step-chain (mirrors `init`), stopping on the first error or `failed` recipe:\n * 1. selfUpdating — CI + Renovate + auto-merge.\n * 2. runAudits + write the scores to the site's Websites row (reuses the\n * `audit --write-airtable` writer); the Lighthouse scores feed the draft.\n * 3. createDraft — reportType \"Launch\", today's period, the audited scores.\n */\nexport async function launch(site: Site, deps: LaunchDeps = {}): Promise<LaunchResult> {\n const label = siteLabel(site);\n const bootstrap = deps.bootstrap ?? selfUpdating;\n const audit = deps.audit ?? runAudits;\n const base = deps.base ?? openBase(readAirtableConfig());\n\n const steps: Array<{ name: string; result: LaunchStepResult }> = [];\n const stop = (): LaunchResult => ({ site: label, steps, complete: false });\n\n // 1. Bootstrap.\n let recipe: RecipeResult;\n try {\n recipe = await bootstrap(site);\n } catch (err) {\n steps.push({ name: \"self-updating\", result: errorOf(err) });\n return stop();\n }\n steps.push({ name: \"self-updating\", result: { kind: \"recipe\", result: recipe } });\n if (recipe.status === \"failed\") return stop();\n\n // 2. Audit + write scores back to Airtable.\n let results: AuditResult[];\n try {\n results = await audit(site);\n } catch (err) {\n steps.push({ name: \"audit\", result: errorOf(err) });\n return stop();\n }\n const lhResult = results.find((r) => r.audit === \"lighthouse\");\n if (!lhResult || !hasRealScores(lhResult)) {\n steps.push({\n name: \"audit\",\n result: { kind: \"error\", message: \"lighthouse audit produced no real scores\" },\n });\n return stop();\n }\n // The launch announcement renders a numeric score per category; a metric that\n // errored this run (now null from lighthouseScoresFromResult) keeps the prior\n // 0 behavior here rather than propagating null into the launch-email path. The\n // Airtable write path (write-audits-to-airtable) keeps the null → shows \"—\".\n const rawScores = lighthouseScoresFromResult(lhResult);\n const scores: LighthouseScores = {\n performance: rawScores.performance ?? 0,\n accessibility: rawScores.accessibility ?? 0,\n bestPractices: rawScores.bestPractices ?? 0,\n seo: rawScores.seo ?? 0,\n };\n\n const websites = await listWebsites(base);\n const target = websites.find((w) => siteSlug(w.name) === siteSlug(label));\n if (!target) {\n steps.push({\n name: \"audit\",\n result: { kind: \"error\", message: `no Websites row matched site \"${label}\"` },\n });\n return stop();\n }\n try {\n await writeAuditsToAirtable({ base, websites, slug: siteSlug(target.name), results });\n } catch (err) {\n steps.push({ name: \"audit\", result: errorOf(err) });\n return stop();\n }\n steps.push({ name: \"audit\", result: { kind: \"audit\", results, scores } });\n\n // 3. Draft the launch email (reuses draft.ts's reportId/period scheme). DRAFTS\n // ONLY — the M3 approve loop sends it and flips Status on send.\n const today = new Date();\n const period = today.toISOString().slice(0, 7);\n const slug = siteSlug(target.name);\n\n let report: ReportRow;\n try {\n // Re-run dedupe: reuse an existing Launch row for this (site, period) instead\n // of stacking a second draft. findReportByPeriod is the same idempotency\n // lookup draft.ts documents (dashboard/digest point lookup).\n const existing = await findReportByPeriod(base, target.id, \"Launch\", period);\n if (existing) {\n // Reuse path: the row was created on a prior run with THAT run's scores. This\n // re-run just produced fresh audit scores AND will re-render the preview from\n // them — so refresh the row's Lighthouse cells (+ Completed on) to match,\n // otherwise the sent email (which reads the row) ships stale scores. The\n // create path already writes fresh scores via createDraft.\n await updateReportScores(base, existing.id, scores, today);\n report = existing;\n } else {\n report = await createDraft(base, draftInputFor(target, scores, today, period));\n }\n } catch (err) {\n steps.push({ name: \"draft\", result: errorOf(err) });\n return stop();\n }\n\n // Mirror draft.ts:135-154 — render → upload \"Rendered HTML\" preview → flip\n // Draft ready. Without setDraftReady the draft never enters the approve queue\n // (every pending-approval gate requires draftReady true), so it can never be\n // approved or sent. The upload is a review convenience; the ready flag is the\n // critical step.\n try {\n const { html } = await renderReportHtml({\n siteName: target.name,\n siteUrl: target.url,\n reportType: \"Launch\",\n completedOn: today,\n lighthouse: scores,\n lastTestedDate: null,\n commentary: null,\n copy: resolveCopy(target),\n headerImageCid: `${slug}-header`,\n });\n // A preview-upload hiccup must NOT fail the launch — log and continue.\n try {\n await uploadAttachment(\n report.id,\n \"Rendered HTML\",\n html,\n `${slug}-${today.toISOString().slice(0, 10)}.html`,\n \"text/html\",\n );\n } catch (uploadErr) {\n console.warn(\n `⚠ Launch preview upload skipped for ${target.name}: ${\n uploadErr instanceof Error ? uploadErr.message : String(uploadErr)\n }`,\n );\n }\n // Critical: NOT wrapped — a failure here must surface as a failed launch. queueDraft\n // supersedes any lower-tier (Maintenance/Testing) drafts queued for this site; a Launch is\n // top tier so it only stands down if another Launch/Announcement is already queued.\n await queueDraft(base, { id: report.id, siteId: target.id, reportType: \"Launch\" });\n } catch (err) {\n steps.push({ name: \"draft\", result: errorOf(err) });\n return stop();\n }\n\n steps.push({ name: \"draft\", result: { kind: \"draft\", report } });\n\n return { site: label, steps, complete: true };\n}\n\n/** Build the Launch `DraftInput`. reportId/period mirror `draftReportForSite`\n * (draft.ts) — do not invent a new id scheme. `today`/`period` are threaded in\n * from `launch()` so a single timestamp drives the render, the draft, the\n * dedupe lookup, and the preview filename. Launch reports have no period window\n * and no prior maintenance test, so periodStart/periodEnd/completedOn all\n * collapse to \"today\" and `lastTestedDate` is null. */\nfunction draftInputFor(\n target: WebsiteRow,\n scores: LighthouseScores,\n today: Date,\n period: string,\n): Parameters<typeof createDraft>[1] {\n const reportType = \"Launch\" as const;\n const reportId = `${target.name} — ${reportType} — ${today.toISOString().slice(0, 10)}`;\n return {\n reportId,\n siteId: target.id,\n reportType,\n period,\n periodStart: today,\n periodEnd: today,\n completedOn: today,\n lighthouse: scores,\n lastTestedDate: null,\n };\n}\n\nfunction errorOf(err: unknown): LaunchStepResult {\n return { kind: \"error\", message: err instanceof Error ? err.message : String(err) };\n}\n"],"mappings":"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","names":[]}
@@ -2,11 +2,11 @@ import {
2
2
  hasRealScores,
3
3
  lighthouseScoresFromResult,
4
4
  resolveSlugFromCwd
5
- } from "./chunk-YYUY2OVP.js";
6
- import "./chunk-BLD6AYJO.js";
5
+ } from "./chunk-CSTLFSGM.js";
6
+ import "./chunk-7RGQ4KLE.js";
7
7
  export {
8
8
  hasRealScores,
9
9
  lighthouseScoresFromResult,
10
10
  resolveSlugFromCwd
11
11
  };
12
- //# sourceMappingURL=lighthouse-airtable-HTEW6BBT.js.map
12
+ //# sourceMappingURL=lighthouse-airtable-5FHELRCA.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ runRecipeOverSites
3
+ } from "./chunk-VPJ7N6P7.js";
1
4
  import {
2
5
  appendSkipNotice,
3
6
  fleetWorkdir,
@@ -5,7 +8,7 @@ import {
5
8
  } from "./chunk-4NAOR44V.js";
6
9
  import {
7
10
  resolveSites
8
- } from "./chunk-SAOCVNX7.js";
11
+ } from "./chunk-FH7WQHD7.js";
9
12
  import {
10
13
  onboard
11
14
  } from "./chunk-ZYBOT5PM.js";
@@ -55,10 +58,11 @@ async function runOnboardCommand(site, opts) {
55
58
  sites = prep.prepared;
56
59
  skipped = prep.skipped;
57
60
  }
58
- const results = [];
59
- for (const s of sites) {
60
- results.push(await onboard(s, audits ? { audits } : {}));
61
- }
61
+ const results = await runRecipeOverSites(
62
+ "onboard",
63
+ sites,
64
+ (s) => onboard(s, audits ? { audits } : {})
65
+ );
62
66
  const output = results.map(formatResult).join("\n");
63
67
  const code = results.some((r) => r.status === "failed") ? 1 : 0;
64
68
  return { output: appendSkipNotice(output, skipped), code };
@@ -66,4 +70,4 @@ async function runOnboardCommand(site, opts) {
66
70
  export {
67
71
  runOnboardCommand
68
72
  };
69
- //# sourceMappingURL=onboard-TTIC3YZI.js.map
73
+ //# sourceMappingURL=onboard-ILC3DWY4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/onboard.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { onboard, type OnboardAudit } from \"../../recipes/onboard.js\";\nimport type { RecipeResult } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { prepareFleetSites, appendSkipNotice, type SkippedSite } from \"../fleet/prepare-sites.js\";\nimport { runRecipeOverSites } from \"../fleet/run-recipe-over-sites.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\n\nexport type OnboardCommandOptions = {\n fleet?: string;\n workdir?: string;\n cwd?: string;\n audits?: string;\n};\n\nconst ALL_AUDITS: OnboardAudit[] = [\"lighthouse\", \"a11y\"];\n\nfunction parseAudits(value: string | undefined): OnboardAudit[] | undefined {\n if (!value) return undefined;\n const parsed = value.split(\",\").map((s) => s.trim());\n for (const a of parsed) {\n if (!ALL_AUDITS.includes(a as OnboardAudit)) {\n throw Object.assign(\n new Error(`unknown audit in --audits: ${a}. expected ${ALL_AUDITS.join(\", \")}`),\n { exitCode: 2 },\n );\n }\n }\n return parsed as OnboardAudit[];\n}\n\nfunction formatResult(r: RecipeResult): string {\n if (r.status === \"noop\") return `[${r.site}] noop: ${r.notes ?? \"\"}`;\n if (r.status === \"failed\") return `[${r.site}] failed: ${r.notes ?? \"\"}`;\n return `[${r.site}] applied: ${r.commits.length} commit(s)\\n${r.notes ?? \"\"}`;\n}\n\nexport async function runOnboardCommand(\n site: string | undefined,\n opts: OnboardCommandOptions,\n): Promise<{ output: string; code: number }> {\n const audits = parseAudits(opts.audits);\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 cwd,\n });\n\n let skipped: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, { workdir });\n sites = prep.prepared;\n skipped = prep.skipped;\n }\n\n const results = await runRecipeOverSites(\"onboard\", sites, (s) =>\n onboard(s, audits ? { audits } : {}),\n );\n\n const output = results.map(formatResult).join(\"\\n\");\n const code = results.some((r) => r.status === \"failed\") ? 1 : 0;\n return { output: appendSkipNotice(output, skipped), code };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;AAexB,IAAM,aAA6B,CAAC,cAAc,MAAM;AAExD,SAAS,YAAY,OAAuD;AAC1E,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACnD,aAAW,KAAK,QAAQ;AACtB,QAAI,CAAC,WAAW,SAAS,CAAiB,GAAG;AAC3C,YAAM,OAAO;AAAA,QACX,IAAI,MAAM,8BAA8B,CAAC,cAAc,WAAW,KAAK,IAAI,CAAC,EAAE;AAAA,QAC9E,EAAE,UAAU,EAAE;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,GAAyB;AAC7C,MAAI,EAAE,WAAW,OAAQ,QAAO,IAAI,EAAE,IAAI,WAAW,EAAE,SAAS,EAAE;AAClE,MAAI,EAAE,WAAW,SAAU,QAAO,IAAI,EAAE,IAAI,aAAa,EAAE,SAAS,EAAE;AACtE,SAAO,IAAI,EAAE,IAAI,cAAc,EAAE,QAAQ,MAAM;AAAA,EAAe,EAAE,SAAS,EAAE;AAC7E;AAEA,eAAsB,kBACpB,MACA,MAC2C;AAC3C,QAAM,SAAS,YAAY,KAAK,MAAM;AACtC,QAAM,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI,QAAQ,IAAI;AAEvD,MAAI,QAAQ,MAAM,aAAa;AAAA,IAC7B,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACrC,GAAI,KAAK,UAAU,SAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,MAAI,UAAyB,CAAC;AAC9B,MAAI,KAAK,OAAO;AACd,UAAM,UAAU,KAAK,WAAW,aAAa;AAC7C,UAAM,OAAO,MAAM,kBAAkB,OAAO,EAAE,QAAQ,CAAC;AACvD,YAAQ,KAAK;AACb,cAAU,KAAK;AAAA,EACjB;AAEA,QAAM,UAAU,MAAM;AAAA,IAAmB;AAAA,IAAW;AAAA,IAAO,CAAC,MAC1D,QAAQ,GAAG,SAAS,EAAE,OAAO,IAAI,CAAC,CAAC;AAAA,EACrC;AAEA,QAAM,SAAS,QAAQ,IAAI,YAAY,EAAE,KAAK,IAAI;AAClD,QAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,IAAI,IAAI;AAC9D,SAAO,EAAE,QAAQ,iBAAiB,QAAQ,OAAO,GAAG,KAAK;AAC3D;","names":[]}
@@ -4,16 +4,16 @@ import {
4
4
  parseAddresses,
5
5
  sendApprovedReports,
6
6
  withGlobalCc
7
- } from "./chunk-J37CZORZ.js";
7
+ } from "./chunk-XUKSP67U.js";
8
8
  import "./chunk-OGGRFBL4.js";
9
9
  import "./chunk-GECESMVS.js";
10
- import "./chunk-U3MF6RCS.js";
10
+ import "./chunk-PBDZGBGL.js";
11
11
  import "./chunk-OCS7SZUZ.js";
12
- import "./chunk-L5F664ZA.js";
13
- import "./chunk-6ZKM7OIJ.js";
12
+ import "./chunk-G2YRXV26.js";
13
+ import "./chunk-PTPTVVY7.js";
14
14
  import "./chunk-XKKTK6WA.js";
15
15
  import "./chunk-U5SZ4FOP.js";
16
- import "./chunk-BLD6AYJO.js";
16
+ import "./chunk-7RGQ4KLE.js";
17
17
  import "./chunk-HN6I2XWI.js";
18
18
  import "./chunk-WEKL2HYJ.js";
19
19
  import "./chunk-6VAL7XJT.js";
@@ -24,4 +24,4 @@ export {
24
24
  sendApprovedReports,
25
25
  withGlobalCc
26
26
  };
27
- //# sourceMappingURL=orchestrate-CYYZ737H.js.map
27
+ //# sourceMappingURL=orchestrate-45URUFEK.js.map
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-PZQ3WED2.js";
4
4
  import {
5
5
  makeGitHub
6
- } from "./chunk-OSRFDGQM.js";
6
+ } from "./chunk-YEGHUODU.js";
7
7
  import "./chunk-HVOOCK6L.js";
8
8
  import {
9
9
  isDashboardVisible,
10
10
  listWebsites,
11
11
  updateAutoFixAttempts
12
- } from "./chunk-BLD6AYJO.js";
12
+ } from "./chunk-7RGQ4KLE.js";
13
13
  import {
14
14
  openBase,
15
15
  readAirtableConfig
@@ -136,4 +136,4 @@ No active repo-backed sites with critical/high vulnerabilities \u2014 nothing to
136
136
  export {
137
137
  runRenovateDispatchCommand
138
138
  };
139
- //# sourceMappingURL=renovate-dispatch-QDDMTATD.js.map
139
+ //# sourceMappingURL=renovate-dispatch-G4T73IUR.js.map
@@ -5,11 +5,11 @@ import {
5
5
  import {
6
6
  draftReportForSite,
7
7
  readGaConfig
8
- } from "./chunk-NPJFHBAX.js";
8
+ } from "./chunk-SWSFB6IV.js";
9
9
  import {
10
10
  reportTier
11
11
  } from "./chunk-6LIWDIXU.js";
12
- import "./chunk-U3MF6RCS.js";
12
+ import "./chunk-PBDZGBGL.js";
13
13
  import {
14
14
  escapeHtml,
15
15
  listAllReports
@@ -18,7 +18,7 @@ import "./chunk-U5SZ4FOP.js";
18
18
  import {
19
19
  listWebsites,
20
20
  siteSlug
21
- } from "./chunk-BLD6AYJO.js";
21
+ } from "./chunk-7RGQ4KLE.js";
22
22
  import {
23
23
  openBase,
24
24
  readAirtableConfig
@@ -68,11 +68,11 @@ function dashboardBaseUrl() {
68
68
  }
69
69
  async function runReportCommand(slug, opts) {
70
70
  if (opts.digest) {
71
- const { runDigest } = await import("./digest-ABJROO4H.js");
71
+ const { runDigest } = await import("./digest-R3ROSGLP.js");
72
72
  return runDigest({ baseUrl: dashboardBaseUrl() });
73
73
  }
74
74
  if (opts.sendReady) {
75
- const { sendApprovedReports } = await import("./orchestrate-CYYZ737H.js");
75
+ const { sendApprovedReports } = await import("./orchestrate-45URUFEK.js");
76
76
  return sendApprovedReports();
77
77
  }
78
78
  if (opts.due) {
@@ -237,4 +237,4 @@ export {
237
237
  parseSingleSiteReportType,
238
238
  runReportCommand
239
239
  };
240
- //# sourceMappingURL=report-QNE7HFOZ.js.map
240
+ //# sourceMappingURL=report-BZ6QNFNM.js.map
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  selfUpdating
3
- } from "./chunk-3HM4S422.js";
4
- import "./chunk-OSRFDGQM.js";
3
+ } from "./chunk-IRRZG66U.js";
4
+ import {
5
+ runRecipeOverSites
6
+ } from "./chunk-VPJ7N6P7.js";
5
7
  import {
6
8
  appendSkipNotice,
7
9
  fleetWorkdir,
@@ -9,8 +11,10 @@ import {
9
11
  } from "./chunk-4NAOR44V.js";
10
12
  import {
11
13
  resolveSites
12
- } from "./chunk-SAOCVNX7.js";
14
+ } from "./chunk-FH7WQHD7.js";
13
15
  import "./chunk-CASCWQQX.js";
16
+ import "./chunk-GASBX52O.js";
17
+ import "./chunk-YEGHUODU.js";
14
18
  import "./chunk-FDIFTIXC.js";
15
19
  import "./chunk-HVOOCK6L.js";
16
20
  import "./chunk-XXTZBPUY.js";
@@ -48,8 +52,7 @@ async function runSelfUpdatingCommand(site, opts) {
48
52
  code: 0
49
53
  };
50
54
  }
51
- const results = [];
52
- for (const s of sites) results.push(await selfUpdating(s));
55
+ const results = await runRecipeOverSites("self-updating", sites, (s) => selfUpdating(s));
53
56
  return {
54
57
  output: appendSkipNotice(results.map(formatResult).join("\n"), skipped),
55
58
  code: results.some((r) => r.status === "failed") ? 1 : 0
@@ -58,4 +61,4 @@ async function runSelfUpdatingCommand(site, opts) {
58
61
  export {
59
62
  runSelfUpdatingCommand
60
63
  };
61
- //# sourceMappingURL=self-updating-2RLUP4AA.js.map
64
+ //# sourceMappingURL=self-updating-DEZNP5OK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/self-updating.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { prepareFleetSites, appendSkipNotice, type SkippedSite } from \"../fleet/prepare-sites.js\";\nimport { runRecipeOverSites } from \"../fleet/run-recipe-over-sites.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\nimport { selfUpdating } from \"../../recipes/self-updating/index.js\";\nimport type { RecipeResult } from \"../../types.js\";\n\nexport type SelfUpdatingCommandOptions = {\n fleet?: string;\n workdir?: string;\n dry?: boolean;\n cwd?: string;\n};\n\nfunction formatResult(r: RecipeResult): string {\n if (r.status === \"noop\") return `[${r.site}] noop: ${r.notes ?? \"already self-updating\"}`;\n if (r.status === \"failed\") return `[${r.site}] failed: ${r.notes ?? \"\"}`;\n return `[${r.site}] applied\\n${r.notes ?? \"\"}`;\n}\n\nexport async function runSelfUpdatingCommand(\n site: string | undefined,\n opts: SelfUpdatingCommandOptions,\n): Promise<{ output: string; code: number }> {\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 cwd,\n });\n\n let skipped: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, { workdir });\n sites = prep.prepared;\n skipped = prep.skipped;\n }\n\n if (opts.dry) {\n return {\n output: appendSkipNotice(\n sites.map((s) => `[${s.name || s.path}] would enable self-updating`).join(\"\\n\"),\n skipped,\n ),\n code: 0,\n };\n }\n\n const results = await runRecipeOverSites(\"self-updating\", sites, (s) => selfUpdating(s));\n\n return {\n output: appendSkipNotice(results.map(formatResult).join(\"\\n\"), skipped),\n code: results.some((r) => r.status === \"failed\") ? 1 : 0,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;AAexB,SAAS,aAAa,GAAyB;AAC7C,MAAI,EAAE,WAAW,OAAQ,QAAO,IAAI,EAAE,IAAI,WAAW,EAAE,SAAS,uBAAuB;AACvF,MAAI,EAAE,WAAW,SAAU,QAAO,IAAI,EAAE,IAAI,aAAa,EAAE,SAAS,EAAE;AACtE,SAAO,IAAI,EAAE,IAAI;AAAA,EAAc,EAAE,SAAS,EAAE;AAC9C;AAEA,eAAsB,uBACpB,MACA,MAC2C;AAC3C,QAAM,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI,QAAQ,IAAI;AAEvD,MAAI,QAAQ,MAAM,aAAa;AAAA,IAC7B,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACrC,GAAI,KAAK,UAAU,SAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,MAAI,UAAyB,CAAC;AAC9B,MAAI,KAAK,OAAO;AACd,UAAM,UAAU,KAAK,WAAW,aAAa;AAC7C,UAAM,OAAO,MAAM,kBAAkB,OAAO,EAAE,QAAQ,CAAC;AACvD,YAAQ,KAAK;AACb,cAAU,KAAK;AAAA,EACjB;AAEA,MAAI,KAAK,KAAK;AACZ,WAAO;AAAA,MACL,QAAQ;AAAA,QACN,MAAM,IAAI,CAAC,MAAM,IAAI,EAAE,QAAQ,EAAE,IAAI,8BAA8B,EAAE,KAAK,IAAI;AAAA,QAC9E;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,mBAAmB,iBAAiB,OAAO,CAAC,MAAM,aAAa,CAAC,CAAC;AAEvF,SAAO;AAAA,IACL,QAAQ,iBAAiB,QAAQ,IAAI,YAAY,EAAE,KAAK,IAAI,GAAG,OAAO;AAAA,IACtE,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,IAAI,IAAI;AAAA,EACzD;AACF;","names":[]}
@@ -1,29 +1,29 @@
1
1
  import {
2
2
  buildReportDataForSite,
3
3
  scoresFromRow
4
- } from "./chunk-P7VJM46I.js";
5
- import "./chunk-NPJFHBAX.js";
4
+ } from "./chunk-XGT3I5AK.js";
5
+ import "./chunk-SWSFB6IV.js";
6
6
  import "./chunk-6LIWDIXU.js";
7
7
  import {
8
8
  isProbablyEmail,
9
9
  parseAddresses,
10
10
  prepareHeaderImage,
11
11
  renderReportEmail
12
- } from "./chunk-J37CZORZ.js";
12
+ } from "./chunk-XUKSP67U.js";
13
13
  import "./chunk-OGGRFBL4.js";
14
14
  import "./chunk-GECESMVS.js";
15
15
  import {
16
16
  fetchAttachmentBytes
17
- } from "./chunk-U3MF6RCS.js";
17
+ } from "./chunk-PBDZGBGL.js";
18
18
  import "./chunk-OCS7SZUZ.js";
19
- import "./chunk-L5F664ZA.js";
20
- import "./chunk-6ZKM7OIJ.js";
19
+ import "./chunk-G2YRXV26.js";
20
+ import "./chunk-PTPTVVY7.js";
21
21
  import "./chunk-XKKTK6WA.js";
22
22
  import "./chunk-U5SZ4FOP.js";
23
23
  import {
24
24
  listWebsites,
25
25
  siteSlug
26
- } from "./chunk-BLD6AYJO.js";
26
+ } from "./chunk-7RGQ4KLE.js";
27
27
  import {
28
28
  openBase,
29
29
  readAirtableConfig
@@ -158,4 +158,4 @@ async function runSelftestCommand(kind, site, opts) {
158
158
  export {
159
159
  runSelftestCommand
160
160
  };
161
- //# sourceMappingURL=selftest-5ELLRH6Q.js.map
161
+ //# sourceMappingURL=selftest-TY7X5ARI.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ runRecipeOverSites
3
+ } from "./chunk-VPJ7N6P7.js";
1
4
  import {
2
5
  appendSkipNotice,
3
6
  fleetWorkdir,
@@ -5,7 +8,7 @@ import {
5
8
  } from "./chunk-4NAOR44V.js";
6
9
  import {
7
10
  resolveSites
8
- } from "./chunk-SAOCVNX7.js";
11
+ } from "./chunk-FH7WQHD7.js";
9
12
  import {
10
13
  svelteCodemods
11
14
  } from "./chunk-FUD4I2ZW.js";
@@ -39,8 +42,7 @@ async function runSvelteCodemodsCommand(site, opts) {
39
42
  sites = prep.prepared;
40
43
  skipped = prep.skipped;
41
44
  }
42
- const results = [];
43
- for (const s of sites) results.push(await svelteCodemods(s));
45
+ const results = await runRecipeOverSites("svelte-codemods", sites, (s) => svelteCodemods(s));
44
46
  const output = results.map(formatResult).join("\n");
45
47
  const code = results.some((r) => r.status === "failed") ? 1 : 0;
46
48
  return { output: appendSkipNotice(output, skipped), code };
@@ -48,4 +50,4 @@ async function runSvelteCodemodsCommand(site, opts) {
48
50
  export {
49
51
  runSvelteCodemodsCommand
50
52
  };
51
- //# sourceMappingURL=svelte-codemods-BPLKSVIA.js.map
53
+ //# sourceMappingURL=svelte-codemods-DMLMOKSD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/svelte-codemods.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { svelteCodemods } from \"../../recipes/svelte-codemods.js\";\nimport type { RecipeResult } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { prepareFleetSites, appendSkipNotice, type SkippedSite } from \"../fleet/prepare-sites.js\";\nimport { runRecipeOverSites } from \"../fleet/run-recipe-over-sites.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\n\nexport type SvelteCodemodsCommandOptions = {\n fleet?: string;\n workdir?: string;\n cwd?: string;\n};\n\nfunction formatResult(r: RecipeResult): string {\n if (r.status === \"noop\") return `[${r.site}] noop: ${r.notes ?? \"\"}`;\n if (r.status === \"failed\") return `[${r.site}] failed: ${r.notes ?? \"\"}`;\n return `[${r.site}] applied: ${r.commits.length} commit(s)\\n${r.notes ?? \"\"}`;\n}\n\nexport async function runSvelteCodemodsCommand(\n site: string | undefined,\n opts: SvelteCodemodsCommandOptions,\n): Promise<{ output: string; code: number }> {\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 cwd,\n });\n\n let skipped: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, { workdir });\n sites = prep.prepared;\n skipped = prep.skipped;\n }\n\n const results = await runRecipeOverSites(\"svelte-codemods\", sites, (s) => svelteCodemods(s));\n\n const output = results.map(formatResult).join(\"\\n\");\n const code = results.some((r) => r.status === \"failed\") ? 1 : 0;\n return { output: appendSkipNotice(output, skipped), code };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;AAcxB,SAAS,aAAa,GAAyB;AAC7C,MAAI,EAAE,WAAW,OAAQ,QAAO,IAAI,EAAE,IAAI,WAAW,EAAE,SAAS,EAAE;AAClE,MAAI,EAAE,WAAW,SAAU,QAAO,IAAI,EAAE,IAAI,aAAa,EAAE,SAAS,EAAE;AACtE,SAAO,IAAI,EAAE,IAAI,cAAc,EAAE,QAAQ,MAAM;AAAA,EAAe,EAAE,SAAS,EAAE;AAC7E;AAEA,eAAsB,yBACpB,MACA,MAC2C;AAC3C,QAAM,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI,QAAQ,IAAI;AAEvD,MAAI,QAAQ,MAAM,aAAa;AAAA,IAC7B,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACrC,GAAI,KAAK,UAAU,SAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,MAAI,UAAyB,CAAC;AAC9B,MAAI,KAAK,OAAO;AACd,UAAM,UAAU,KAAK,WAAW,aAAa;AAC7C,UAAM,OAAO,MAAM,kBAAkB,OAAO,EAAE,QAAQ,CAAC;AACvD,YAAQ,KAAK;AACb,cAAU,KAAK;AAAA,EACjB;AAEA,QAAM,UAAU,MAAM,mBAAmB,mBAAmB,OAAO,CAAC,MAAM,eAAe,CAAC,CAAC;AAE3F,QAAM,SAAS,QAAQ,IAAI,YAAY,EAAE,KAAK,IAAI;AAClD,QAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,IAAI,IAAI;AAC9D,SAAO,EAAE,QAAQ,iBAAiB,QAAQ,OAAO,GAAG,KAAK;AAC3D;","names":[]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ runRecipeOverSites
3
+ } from "./chunk-VPJ7N6P7.js";
1
4
  import {
2
5
  appendSkipNotice,
3
6
  fleetWorkdir,
@@ -5,7 +8,7 @@ import {
5
8
  } from "./chunk-4NAOR44V.js";
6
9
  import {
7
10
  resolveSites
8
- } from "./chunk-SAOCVNX7.js";
11
+ } from "./chunk-FH7WQHD7.js";
9
12
  import {
10
13
  ALL_CONFIG_NAMES,
11
14
  CANONICAL_GITIGNORE_ENTRIES,
@@ -97,8 +100,11 @@ async function runSyncConfigsCommand(site, opts) {
97
100
  }
98
101
  return { output: appendSkipNotice(blocks.join("\n\n"), skipped), code: 0 };
99
102
  }
100
- const results = [];
101
- for (const s of sites) results.push(await syncConfigs(s, which ? { which } : {}));
103
+ const results = await runRecipeOverSites(
104
+ "sync-configs",
105
+ sites,
106
+ (s) => syncConfigs(s, which ? { which } : {})
107
+ );
102
108
  const output = results.map(formatResult).join("\n");
103
109
  const code = results.some((r) => r.status === "failed") ? 1 : 0;
104
110
  return { output: appendSkipNotice(output, skipped), code };
@@ -106,4 +112,4 @@ async function runSyncConfigsCommand(site, opts) {
106
112
  export {
107
113
  runSyncConfigsCommand
108
114
  };
109
- //# sourceMappingURL=sync-configs-VFRV3TJ4.js.map
115
+ //# sourceMappingURL=sync-configs-DWKXETFO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/sync-configs.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { syncConfigs, ALL_CONFIG_NAMES, isConfigName } from \"../../recipes/sync-configs.js\";\nimport { ALL_TEMPLATES, templatesByName } from \"../../recipes/sync-configs/templates.js\";\nimport {\n CANONICAL_GITIGNORE_ENTRIES,\n mergeGitignore,\n} from \"../../recipes/sync-configs/gitignore.js\";\nimport type { ConfigName, RecipeResult } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { prepareFleetSites, appendSkipNotice, type SkippedSite } from \"../fleet/prepare-sites.js\";\nimport { runRecipeOverSites } from \"../fleet/run-recipe-over-sites.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\n\nexport type SyncConfigsCommandOptions = {\n only?: string;\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n};\n\nfunction parseOnly(value?: string): ConfigName[] | undefined {\n if (!value) return undefined;\n const names = value.split(\",\").map((s) => s.trim());\n for (const n of names) {\n if (!isConfigName(n)) {\n throw Object.assign(\n new Error(`unknown config in --only: \"${n}\". Valid: ${ALL_CONFIG_NAMES.join(\", \")}`),\n { exitCode: 2 },\n );\n }\n }\n return names as ConfigName[];\n}\n\nasync function dryPlanGitignore(cwd: string): Promise<string | null> {\n let existing: string | null;\n try {\n existing = await readFile(join(cwd, \".gitignore\"), \"utf-8\");\n } catch {\n return \"would create .gitignore\";\n }\n const merge = mergeGitignore(existing, CANONICAL_GITIGNORE_ENTRIES);\n if (merge.added.length === 0) return null;\n return `would update .gitignore (${merge.added.length} canonical entries to add)`;\n}\n\nasync function dryPlan(cwd: string, which?: ConfigName[]): Promise<string> {\n const includeGitignore = which ? which.includes(\"gitignore\") : true;\n const templateTargets = which\n ? templatesByName(which.filter((c): c is ConfigName => c !== \"gitignore\"))\n : ALL_TEMPLATES;\n\n const lines: string[] = [];\n for (const t of templateTargets) {\n let existing = \"\";\n try {\n existing = await readFile(join(cwd, t.path), \"utf-8\");\n } catch {\n // missing file => will be created\n }\n if (existing !== t.contents) lines.push(`would update ${t.path} (config: ${t.config})`);\n }\n if (includeGitignore) {\n const gi = await dryPlanGitignore(cwd);\n if (gi) lines.push(gi);\n }\n return lines.length === 0 ? \"no changes needed\" : lines.join(\"\\n\");\n}\n\nfunction formatResult(r: RecipeResult): string {\n if (r.status === \"noop\") return `[${r.site}] noop: ${r.notes ?? \"all configs in sync\"}`;\n return `[${r.site}] applied: ${r.commits.length} commit(s)\\n${r.notes ?? \"\"}`;\n}\n\nexport async function runSyncConfigsCommand(\n site: string | undefined,\n opts: SyncConfigsCommandOptions,\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 cwd,\n });\n\n let skipped: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, { workdir });\n sites = prep.prepared;\n skipped = prep.skipped;\n }\n\n if (opts.dry) {\n const blocks: string[] = [];\n for (const s of sites) {\n blocks.push(`[${s.name || s.path}]\\n` + (await dryPlan(s.path, which)));\n }\n return { output: appendSkipNotice(blocks.join(\"\\n\\n\"), skipped), code: 0 };\n }\n\n const results = await runRecipeOverSites(\"sync-configs\", sites, (s) =>\n syncConfigs(s, which ? { which } : {}),\n );\n\n const output = results.map(formatResult).join(\"\\n\");\n const code = results.some((r) => r.status === \"failed\") ? 1 : 0;\n return { output: appendSkipNotice(output, skipped), code };\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ runRecipeOverSites
3
+ } from "./chunk-VPJ7N6P7.js";
1
4
  import {
2
5
  appendSkipNotice,
3
6
  fleetWorkdir,
@@ -5,7 +8,7 @@ import {
5
8
  } from "./chunk-4NAOR44V.js";
6
9
  import {
7
10
  resolveSites
8
- } from "./chunk-SAOCVNX7.js";
11
+ } from "./chunk-FH7WQHD7.js";
9
12
  import {
10
13
  upgradeSvelte4to5
11
14
  } from "./chunk-O65I7G7G.js";
@@ -48,12 +51,7 @@ async function runUpgradeCommand(upgradeName, site, opts = {}) {
48
51
  sites = prep.prepared;
49
52
  skipped = prep.skipped;
50
53
  }
51
- const results = [];
52
- for (const s of sites) {
53
- if (upgradeName === "svelte-4-to-5") {
54
- results.push(await upgradeSvelte4to5(s));
55
- }
56
- }
54
+ const results = await runRecipeOverSites("svelte-4-to-5", sites, (s) => upgradeSvelte4to5(s));
57
55
  const output = results.map(formatResult).join("\n");
58
56
  const code = results.some((r) => r.status === "failed") ? 1 : 0;
59
57
  return { output: appendSkipNotice(output, skipped), code };
@@ -61,4 +59,4 @@ async function runUpgradeCommand(upgradeName, site, opts = {}) {
61
59
  export {
62
60
  runUpgradeCommand
63
61
  };
64
- //# sourceMappingURL=upgrade-WCFD2TRN.js.map
62
+ //# sourceMappingURL=upgrade-RUX2ODMW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/upgrade.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { upgradeSvelte4to5 } from \"../../recipes/svelte-5/index.js\";\nimport type { RecipeResult } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport { prepareFleetSites, appendSkipNotice, type SkippedSite } from \"../fleet/prepare-sites.js\";\nimport { runRecipeOverSites } from \"../fleet/run-recipe-over-sites.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\n\nconst KNOWN_UPGRADES = new Set([\"svelte-4-to-5\"]);\n\nexport type UpgradeCommandOptions = {\n fleet?: string;\n workdir?: string;\n cwd?: string;\n};\n\nfunction formatResult(r: RecipeResult): string {\n if (r.status === \"noop\") return `[${r.site}] noop: ${r.notes ?? \"\"}`;\n return `[${r.site}] applied: ${r.commits.length} commit(s)\\n${r.notes ?? \"\"}`;\n}\n\nexport async function runUpgradeCommand(\n upgradeName: string | undefined,\n site: string | undefined,\n opts: UpgradeCommandOptions = {},\n): Promise<{ output: string; code: number }> {\n if (!upgradeName || !KNOWN_UPGRADES.has(upgradeName)) {\n throw Object.assign(\n new Error(\n `unknown upgrade: ${upgradeName ?? \"(none)\"}. expected one of ${[...KNOWN_UPGRADES].join(\", \")}`,\n ),\n { exitCode: 2 },\n );\n }\n\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 cwd,\n });\n\n let skipped: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, { workdir });\n sites = prep.prepared;\n skipped = prep.skipped;\n }\n\n // `upgradeName` is validated against KNOWN_UPGRADES above (throws otherwise),\n // so the only reachable upgrade here is the svelte-4-to-5 recipe.\n const results = await runRecipeOverSites(\"svelte-4-to-5\", sites, (s) => upgradeSvelte4to5(s));\n\n const output = results.map(formatResult).join(\"\\n\");\n const code = results.some((r) => r.status === \"failed\") ? 1 : 0;\n return { output: appendSkipNotice(output, skipped), code };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;AAQxB,IAAM,iBAAiB,oBAAI,IAAI,CAAC,eAAe,CAAC;AAQhD,SAAS,aAAa,GAAyB;AAC7C,MAAI,EAAE,WAAW,OAAQ,QAAO,IAAI,EAAE,IAAI,WAAW,EAAE,SAAS,EAAE;AAClE,SAAO,IAAI,EAAE,IAAI,cAAc,EAAE,QAAQ,MAAM;AAAA,EAAe,EAAE,SAAS,EAAE;AAC7E;AAEA,eAAsB,kBACpB,aACA,MACA,OAA8B,CAAC,GACY;AAC3C,MAAI,CAAC,eAAe,CAAC,eAAe,IAAI,WAAW,GAAG;AACpD,UAAM,OAAO;AAAA,MACX,IAAI;AAAA,QACF,oBAAoB,eAAe,QAAQ,qBAAqB,CAAC,GAAG,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,MAChG;AAAA,MACA,EAAE,UAAU,EAAE;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI,QAAQ,IAAI;AAEvD,MAAI,QAAQ,MAAM,aAAa;AAAA,IAC7B,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACrC,GAAI,KAAK,UAAU,SAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,MAAI,UAAyB,CAAC;AAC9B,MAAI,KAAK,OAAO;AACd,UAAM,UAAU,KAAK,WAAW,aAAa;AAC7C,UAAM,OAAO,MAAM,kBAAkB,OAAO,EAAE,QAAQ,CAAC;AACvD,YAAQ,KAAK;AACb,cAAU,KAAK;AAAA,EACjB;AAIA,QAAM,UAAU,MAAM,mBAAmB,iBAAiB,OAAO,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAE5F,QAAM,SAAS,QAAQ,IAAI,YAAY,EAAE,KAAK,IAAI;AAClD,QAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,IAAI,IAAI;AAC9D,SAAO,EAAE,QAAQ,iBAAiB,QAAQ,OAAO,GAAG,KAAK;AAC3D;","names":[]}
@@ -20,7 +20,7 @@ import {
20
20
  updateScores,
21
21
  updateSecurityCounts,
22
22
  updateSiteField
23
- } from "./chunk-BLD6AYJO.js";
23
+ } from "./chunk-7RGQ4KLE.js";
24
24
  export {
25
25
  ACTIVE_STATUSES,
26
26
  SEVERITY_RANK,
@@ -44,4 +44,4 @@ export {
44
44
  updateSecurityCounts,
45
45
  updateSiteField
46
46
  };
47
- //# sourceMappingURL=websites-HM2NMZMR.js.map
47
+ //# sourceMappingURL=websites-CSVLSEVL.js.map
@@ -2,13 +2,13 @@ import {
2
2
  formatFleetWriteSummary,
3
3
  writeAuditsToAirtable,
4
4
  writeFleetAuditsToAirtable
5
- } from "./chunk-4REUHZAW.js";
6
- import "./chunk-YYUY2OVP.js";
5
+ } from "./chunk-LM42CATB.js";
6
+ import "./chunk-CSTLFSGM.js";
7
7
  import "./chunk-5L3FPQIJ.js";
8
- import "./chunk-BLD6AYJO.js";
8
+ import "./chunk-7RGQ4KLE.js";
9
9
  export {
10
10
  formatFleetWriteSummary,
11
11
  writeAuditsToAirtable,
12
12
  writeFleetAuditsToAirtable
13
13
  };
14
- //# sourceMappingURL=write-audits-to-airtable-QF26PCAR.js.map
14
+ //# sourceMappingURL=write-audits-to-airtable-JCV7FYAW.js.map