@reddoorla/maintenance 0.61.0 → 0.62.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.
- package/dist/airtable-HHAWHBNM.js +9 -0
- package/dist/{announce-Q2DXP5OO.js → announce-54EBITHX.js} +16 -6
- package/dist/announce-54EBITHX.js.map +1 -0
- package/dist/{bump-deps-RYHQIKW4.js → bump-deps-2U6GIHM5.js} +2 -2
- package/dist/{chunk-BWLUNY7L.js → chunk-2T3HZ3DB.js} +24 -14
- package/dist/chunk-2T3HZ3DB.js.map +1 -0
- package/dist/{chunk-D3LFQNJK.js → chunk-3HM4S422.js} +2 -2
- package/dist/{chunk-LMXIZITU.js → chunk-4REUHZAW.js} +40 -4
- package/dist/chunk-4REUHZAW.js.map +1 -0
- package/dist/chunk-5L3FPQIJ.js +92 -0
- package/dist/chunk-5L3FPQIJ.js.map +1 -0
- package/dist/{client-JA6QLEDW.js → chunk-6ZKM7OIJ.js} +5 -4
- package/dist/{chunk-KC4HU7BC.js → chunk-76VIUWR3.js} +31 -6
- package/dist/chunk-76VIUWR3.js.map +1 -0
- package/dist/{chunk-L4ULXHIN.js → chunk-BLD6AYJO.js} +15 -1
- package/dist/chunk-BLD6AYJO.js.map +1 -0
- package/dist/{chunk-VDBENQUA.js → chunk-BMDBOPN3.js} +2 -2
- package/dist/{chunk-HB7QPECR.js → chunk-KTPPKO2C.js} +6 -6
- package/dist/chunk-L5F664ZA.js +45 -0
- package/dist/chunk-L5F664ZA.js.map +1 -0
- package/dist/{chunk-XB6T4ETK.js → chunk-OSRFDGQM.js} +21 -1
- package/dist/chunk-OSRFDGQM.js.map +1 -0
- package/dist/{chunk-6CSCLBAA.js → chunk-SAOCVNX7.js} +2 -2
- package/dist/{chunk-5PHCZ6L5.js → chunk-SS7BKOIX.js} +89 -2
- package/dist/chunk-SS7BKOIX.js.map +1 -0
- package/dist/{chunk-TZN2G6FD.js → chunk-TYRCYHRA.js} +3 -3
- package/dist/{chunk-HG5DTFNF.js → chunk-XKKTK6WA.js} +17 -1
- package/dist/chunk-XKKTK6WA.js.map +1 -0
- package/dist/{chunk-JNXCDUYB.js → chunk-XVR7UM6Z.js} +4 -4
- package/dist/{chunk-HPSGCDWY.js → chunk-YYUY2OVP.js} +2 -2
- package/dist/{chunk-TLZ4V2BE.js → chunk-ZEX2T6JH.js} +3 -2
- package/dist/{chunk-TLZ4V2BE.js.map → chunk-ZEX2T6JH.js.map} +1 -1
- package/dist/cli/bin.js +16 -15
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +3 -2
- package/dist/cli/commands/audit.js +29 -8
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/client-GTMJTXBK.js +11 -0
- package/dist/{convert-to-pnpm-KNCXU6FM.js → convert-to-pnpm-JG3767ZQ.js} +2 -2
- package/dist/{db-AXKMRTVA.js → db-Y6BVTNH7.js} +3 -3
- package/dist/{digest-WAS3EJNC.js → digest-ABJROO4H.js} +3 -3
- package/dist/{github-signals-UL2BBS3Q.js → github-signals-XTJLKOXJ.js} +26 -5
- package/dist/github-signals-XTJLKOXJ.js.map +1 -0
- package/dist/index.d.ts +60 -3
- package/dist/index.js +86 -21
- package/dist/index.js.map +1 -1
- package/dist/{init-KD56UENX.js → init-DMAIKUQE.js} +8 -8
- package/dist/{launch-UWCI3JRM.js → launch-UZPPB4IE.js} +10 -9
- package/dist/{launch-UWCI3JRM.js.map → launch-UZPPB4IE.js.map} +1 -1
- package/dist/{lighthouse-airtable-N3KCBPVA.js → lighthouse-airtable-HTEW6BBT.js} +3 -3
- package/dist/migrate-BFOG2OCP.js +7 -0
- package/dist/{onboard-SCZ3UKZ2.js → onboard-TTIC3YZI.js} +2 -2
- package/dist/{orchestrate-D72PB6NG.js → orchestrate-5GGOL5EX.js} +7 -4
- package/dist/recipes/sync-configs.d.ts +1 -1
- package/dist/recipes/sync-configs.js +2 -2
- package/dist/{renovate-dispatch-NG5KUQKY.js → renovate-dispatch-QDDMTATD.js} +3 -3
- package/dist/{report-EPHXV2T2.js → report-SZGZIDDG.js} +6 -6
- package/dist/{self-updating-CFHXJSAX.js → self-updating-2RLUP4AA.js} +4 -4
- package/dist/{svelte-codemods-HNITLWQM.js → svelte-codemods-BPLKSVIA.js} +2 -2
- package/dist/{sync-configs-BZXBYKTI.js → sync-configs-VFRV3TJ4.js} +4 -4
- package/dist/{types-QG-QhCYh.d.ts → types-Dg4mHvyf.d.ts} +6 -1
- package/dist/{upgrade-6YDUELFA.js → upgrade-WCFD2TRN.js} +2 -2
- package/dist/{websites-IYP7IGLU.js → websites-HM2NMZMR.js} +2 -2
- package/dist/{write-audits-to-airtable-VDGHCZGQ.js → write-audits-to-airtable-QF26PCAR.js} +5 -4
- package/dist/write-audits-to-airtable-QF26PCAR.js.map +1 -0
- package/package.json +1 -1
- package/dist/airtable-YZ6VMSQZ.js +0 -9
- package/dist/announce-Q2DXP5OO.js.map +0 -1
- package/dist/chunk-5PHCZ6L5.js.map +0 -1
- package/dist/chunk-BWLUNY7L.js.map +0 -1
- package/dist/chunk-HG5DTFNF.js.map +0 -1
- package/dist/chunk-KC4HU7BC.js.map +0 -1
- package/dist/chunk-L4ULXHIN.js.map +0 -1
- package/dist/chunk-LMXIZITU.js.map +0 -1
- package/dist/chunk-XB6T4ETK.js.map +0 -1
- package/dist/github-signals-UL2BBS3Q.js.map +0 -1
- package/dist/migrate-P2LS4WXI.js +0 -7
- /package/dist/{airtable-YZ6VMSQZ.js.map → airtable-HHAWHBNM.js.map} +0 -0
- /package/dist/{bump-deps-RYHQIKW4.js.map → bump-deps-2U6GIHM5.js.map} +0 -0
- /package/dist/{chunk-D3LFQNJK.js.map → chunk-3HM4S422.js.map} +0 -0
- /package/dist/{client-JA6QLEDW.js.map → chunk-6ZKM7OIJ.js.map} +0 -0
- /package/dist/{chunk-VDBENQUA.js.map → chunk-BMDBOPN3.js.map} +0 -0
- /package/dist/{chunk-HB7QPECR.js.map → chunk-KTPPKO2C.js.map} +0 -0
- /package/dist/{chunk-6CSCLBAA.js.map → chunk-SAOCVNX7.js.map} +0 -0
- /package/dist/{chunk-TZN2G6FD.js.map → chunk-TYRCYHRA.js.map} +0 -0
- /package/dist/{chunk-JNXCDUYB.js.map → chunk-XVR7UM6Z.js.map} +0 -0
- /package/dist/{chunk-HPSGCDWY.js.map → chunk-YYUY2OVP.js.map} +0 -0
- /package/dist/{lighthouse-airtable-N3KCBPVA.js.map → client-GTMJTXBK.js.map} +0 -0
- /package/dist/{convert-to-pnpm-KNCXU6FM.js.map → convert-to-pnpm-JG3767ZQ.js.map} +0 -0
- /package/dist/{db-AXKMRTVA.js.map → db-Y6BVTNH7.js.map} +0 -0
- /package/dist/{digest-WAS3EJNC.js.map → digest-ABJROO4H.js.map} +0 -0
- /package/dist/{init-KD56UENX.js.map → init-DMAIKUQE.js.map} +0 -0
- /package/dist/{migrate-P2LS4WXI.js.map → lighthouse-airtable-HTEW6BBT.js.map} +0 -0
- /package/dist/{orchestrate-D72PB6NG.js.map → migrate-BFOG2OCP.js.map} +0 -0
- /package/dist/{onboard-SCZ3UKZ2.js.map → onboard-TTIC3YZI.js.map} +0 -0
- /package/dist/{websites-IYP7IGLU.js.map → orchestrate-5GGOL5EX.js.map} +0 -0
- /package/dist/{renovate-dispatch-NG5KUQKY.js.map → renovate-dispatch-QDDMTATD.js.map} +0 -0
- /package/dist/{report-EPHXV2T2.js.map → report-SZGZIDDG.js.map} +0 -0
- /package/dist/{self-updating-CFHXJSAX.js.map → self-updating-2RLUP4AA.js.map} +0 -0
- /package/dist/{svelte-codemods-HNITLWQM.js.map → svelte-codemods-BPLKSVIA.js.map} +0 -0
- /package/dist/{sync-configs-BZXBYKTI.js.map → sync-configs-VFRV3TJ4.js.map} +0 -0
- /package/dist/{upgrade-6YDUELFA.js.map → upgrade-WCFD2TRN.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-VDGHCZGQ.js.map → websites-HM2NMZMR.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/audits/github-signals.ts","../src/cli/commands/github-signals.ts"],"sourcesContent":["import type { Site } from \"../types.js\";\nimport type { CiState, PullRequestSummary } from \"../github/gh.js\";\nimport { isFailingRenovatePR } from \"../alerts/renovate.js\";\n\n/** One swept row, ready for the Airtable writer (slug-keyed by `site`). */\nexport type GitHubSignalsRow = {\n site: string; // the site name/slug the writer matches on\n repo: string; // owner/repo\n renovateFailingCis: number;\n ciState: CiState;\n lastCommitAt: string | null;\n};\n\n/** Injected GitHub reads (so the sweep is pure + testable). */\nexport type GitHubSignalsDeps = {\n openPullRequests: (repo: string) => Promise<PullRequestSummary[]>;\n defaultBranchStatus: (repo: string) => Promise<{ ciState: CiState; lastCommitAt: string | null }>;\n};\n\n/** Per repo-backed site: count its failing Renovate PRs + read its default-branch\n * status. Sites without `gitRepo` are skipped (not errors). A repo whose probe\n * throws is reported via `onSkip` and produces no row — one GitHub hiccup never\n * sinks the sweep. PURE over `deps`. */\nexport async function collectGitHubSignals(\n sites: Site[],\n deps: GitHubSignalsDeps,\n onSkip: (s: { repo: string }) => void = () => {},\n): Promise<GitHubSignalsRow[]> {\n const rows: GitHubSignalsRow[] = [];\n for (const s of sites) {\n const repo = s.gitRepo;\n if (!repo) continue;\n const name = s.name;\n if (!name) continue;\n try {\n const prs = await deps.openPullRequests(repo);\n const status = await deps.defaultBranchStatus(repo);\n rows.push({\n site: name,\n repo,\n renovateFailingCis: prs.filter(isFailingRenovatePR).length,\n ciState: status.ciState,\n lastCommitAt: status.lastCommitAt,\n });\n } catch {\n onSkip({ repo });\n }\n }\n return rows;\n}\n","import { openBase, readAirtableConfig } from \"../../reports/airtable/client.js\";\nimport { listWebsites, siteSlug, updateGitHubSignals } from \"../../reports/airtable/websites.js\";\nimport type { Site } from \"../../types.js\";\nimport { collectGitHubSignals } from \"../../audits/github-signals.js\";\nimport { makeGitHub } from \"../../github/gh.js\";\nimport {\n formatFleetWriteSummary,\n type FleetWriteResult,\n} from \"../../audits/write-audits-to-airtable.js\";\nimport { detectSignalEvents, fleetSweptEvent } from \"../../audits/fleet-event-detectors.js\";\nimport { recordFleetEventsBestEffort } from \"../../audits/fleet-events-writer.js\";\nimport type { FleetEvent } from \"../../db/fleet-events.js\";\n\n/** Exit code for a fleet github-signals run. Exit 1 when failures are the\n * MAJORITY of the fleet (`failed > written`), not only on a total wipeout —\n * a run where 11/12 repos failed but 1 wrote should still signal an outage.\n * All-success or a minority of flakes (e.g. 1/12 failed) stays exit 0. The\n * no-token clean-skip returns 0 separately (before any probe runs). */\nexport function githubSignalsExitCode(written: number, failed: number): number {\n return failed > written ? 1 : 0;\n}\n\n/** `github-signals --fleet --write-airtable`: sweep every repo-backed site for its\n * Renovate-failing count + default-branch CI state + last-commit date, write each\n * row serially (Airtable ~5 req/sec), and emit FLEET_WRITE_SUMMARY for CI. A\n * missing fleet token is a clean skip (local runs), not a failure. */\nexport async function runGitHubSignalsCommand(opts: {\n fleet?: boolean | undefined;\n writeAirtable?: boolean | undefined;\n}): Promise<{ output: string; code: number }> {\n if (!opts.fleet || !opts.writeAirtable) {\n return { output: \"github-signals currently supports only --fleet --write-airtable\", code: 2 };\n }\n const token = process.env.RENOVATE_TOKEN?.trim() || process.env.GH_TOKEN?.trim();\n if (!token) {\n return {\n output: \"github-signals skipped: no RENOVATE_TOKEN/GH_TOKEN (fleet read) configured.\",\n code: 0,\n };\n }\n const base = openBase(readAirtableConfig());\n const websites = await listWebsites(base);\n const gh = makeGitHub({ token });\n const sites: Site[] = websites.map((w) => ({\n path: \"\",\n name: w.name,\n meta: {},\n ...(w.gitRepo ? { gitRepo: w.gitRepo } : {}),\n }));\n\n const skipped: string[] = [];\n const rows = await collectGitHubSignals(\n sites,\n {\n openPullRequests: (r) => gh.openPullRequests(r),\n defaultBranchStatus: (r) => gh.defaultBranchStatus(r),\n },\n ({ repo }) => skipped.push(repo),\n );\n\n const sweptAt = new Date().toISOString();\n const result: FleetWriteResult = { written: [], failed: [] };\n const byRepo = new Map(websites.filter((w) => w.gitRepo).map((w) => [w.gitRepo, w]));\n const events: FleetEvent[] = [];\n const sweptMs = Date.parse(sweptAt);\n const since24h = new Date(sweptMs - 24 * 60 * 60 * 1000).toISOString();\n // Serial: Airtable's ~5 req/sec limit (matches writeFleetAuditsToAirtable).\n for (const row of rows) {\n const target = byRepo.get(row.repo);\n if (!target) {\n result.failed.push({ slug: siteSlug(row.site), error: \"no Websites row matched\" });\n continue;\n }\n try {\n await updateGitHubSignals(base, target.id, {\n renovateFailingCis: row.renovateFailingCis,\n ciState: row.ciState,\n lastCommitAt: row.lastCommitAt,\n sweptAt,\n });\n result.written.push({\n siteName: target.name,\n writes: [{ audit: \"github-signals\", counts: row }],\n });\n // Fleet-activity events for this repo: merged Renovate PRs since the last sweep\n // (watermark = the row's prior GitHub Signals At, else a 24h fallback) + a\n // CI-recovered transition. A PR-fetch hiccup drops only this repo's PR events.\n const since = target.githubSignalsAt ?? since24h;\n let merged: Awaited<ReturnType<typeof gh.mergedRenovatePullRequests>> = [];\n try {\n merged = await gh.mergedRenovatePullRequests(row.repo, since);\n } catch {\n // PR list unavailable this run — skip pr_automerged for this repo, keep ci_recovered\n }\n events.push(...detectSignalEvents(target, row, merged, sweptAt));\n } catch (e) {\n result.failed.push({ slug: siteSlug(row.site), error: (e as Error).message });\n }\n }\n for (const repo of skipped) result.failed.push({ slug: repo, error: \"probe failed (skipped)\" });\n\n events.push(fleetSweptEvent(\"github-signals\", result.written.length, sweptAt));\n await recordFleetEventsBestEffort(events, new Date());\n\n // Exit non-zero when failures are the MAJORITY of the fleet, not only on a\n // total wipeout. A run where 11/12 repos failed but 1 wrote used to return 0,\n // masking a large outage. The nightly cron step is `continue-on-error`, so a\n // non-zero here is an operator-visibility signal, not a red build.\n return {\n output: formatFleetWriteSummary(result),\n code: githubSignalsExitCode(result.written.length, result.failed.length),\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Site, a as AuditResult, A as AuditName, b as RecipeResult, R as RecipeName, I as InventoryProvider } from './types-
|
|
2
|
-
export { C as ConfigName } from './types-
|
|
1
|
+
import { S as Site, a as AuditResult, A as AuditName, b as RecipeResult, R as RecipeName, I as InventoryProvider } from './types-Dg4mHvyf.js';
|
|
2
|
+
export { C as ConfigName } from './types-Dg4mHvyf.js';
|
|
3
3
|
import * as airtable_lib_airtable_base_js from 'airtable/lib/airtable_base.js';
|
|
4
4
|
import { F as FormType } from './types-RXY-vY-5.js';
|
|
5
5
|
export { SyncConfigsOptions, syncConfigs } from './recipes/sync-configs.js';
|
|
@@ -68,10 +68,33 @@ type BrowserRunner = {
|
|
|
68
68
|
close?: () => Promise<void>;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
/** Result of probing a site's latest PRODUCTION Netlify deploy. Every field is
|
|
72
|
+
* nullable: a network/parse failure (or a site with no deploys yet) degrades to
|
|
73
|
+
* all-nulls rather than throwing, so the audit always returns a verdict.
|
|
74
|
+
* - `state` is Netlify's raw deploy state (`ready`/`error`/`building`/…), lower-cased,
|
|
75
|
+
* or null when it couldn't be read.
|
|
76
|
+
* - `deployedAt` is the deploy's publish/create time (ISO), or null.
|
|
77
|
+
* - `logUrl` links to the deploy (its live URL or the Netlify admin/build-log page).
|
|
78
|
+
* - `errorMessage` is Netlify's `error_message` for a failed build, or null. */
|
|
79
|
+
type NetlifyDeployCheck = {
|
|
80
|
+
state: string | null;
|
|
81
|
+
deployedAt: string | null;
|
|
82
|
+
logUrl: string | null;
|
|
83
|
+
errorMessage: string | null;
|
|
84
|
+
};
|
|
85
|
+
/** Injected IO so the check is unit-testable without hitting the Netlify API.
|
|
86
|
+
* `fetchLatestProductionDeploy` returns the parsed first (latest) production
|
|
87
|
+
* deploy object, or null when the site has none / the call failed. `now` is the
|
|
88
|
+
* clock for the checked-at stamp. */
|
|
89
|
+
type NetlifyDeployDeps = {
|
|
90
|
+
fetchLatestProductionDeploy: (siteId: string) => Promise<NetlifyDeployCheck | null>;
|
|
91
|
+
now: Date;
|
|
92
|
+
};
|
|
93
|
+
|
|
71
94
|
type AuditContext = {
|
|
72
95
|
site: Site;
|
|
73
96
|
spawn?: SpawnFn;
|
|
74
|
-
/** Clock injection (domain + browser audits). Defaults to `new Date()`. */
|
|
97
|
+
/** Clock injection (domain + browser + netlify-deploy audits). Defaults to `new Date()`. */
|
|
75
98
|
now?: Date;
|
|
76
99
|
/** DNS/TLS injection for the domain audit (tests). Defaults to real DNS+TLS. */
|
|
77
100
|
domainDeps?: DomainDeps;
|
|
@@ -79,6 +102,8 @@ type AuditContext = {
|
|
|
79
102
|
discoverDeps?: DiscoverDeps;
|
|
80
103
|
/** Playwright runner injection for the browser audit (tests). Defaults to real Playwright. */
|
|
81
104
|
browserRunner?: BrowserRunner;
|
|
105
|
+
/** Netlify API injection for the netlify-deploy audit (tests). Defaults to a real API call. */
|
|
106
|
+
netlifyDeployDeps?: NetlifyDeployDeps;
|
|
82
107
|
};
|
|
83
108
|
|
|
84
109
|
declare function depsAudit(ctx: AuditContext): Promise<AuditResult>;
|
|
@@ -326,6 +351,16 @@ type WebsiteRow = {
|
|
|
326
351
|
* expires (null = unresolved or no usable cert); `domainCheckedAt` is when it last ran. */
|
|
327
352
|
certDaysRemaining: number | null;
|
|
328
353
|
domainCheckedAt: string | null;
|
|
354
|
+
/** Netlify site id — the IDENTITY the `netlify-deploy` audit needs to query the
|
|
355
|
+
* Netlify API. Read-only input (operator-set in Airtable); null = not on Netlify
|
|
356
|
+
* (or not wired) → that audit skips. Never derived from the URL. */
|
|
357
|
+
netlifyId: string | null;
|
|
358
|
+
/** Latest PRODUCTION deploy health (the `netlify-deploy` audit). `deployStatus` is
|
|
359
|
+
* Netlify's deploy state lower-cased (`ready`/`error`/`building`/…), null = none / not
|
|
360
|
+
* checked; `lastDeployAt` is when it deployed; `deployLogUrl` links to the deploy/log. */
|
|
361
|
+
deployStatus: string | null;
|
|
362
|
+
lastDeployAt: string | null;
|
|
363
|
+
deployLogUrl: string | null;
|
|
329
364
|
/** Deployed-URL browser probe (the `browser` audit): cross-engine render OK, mobile render OK,
|
|
330
365
|
* internal-links OK + broken count, and when it last ran (one timestamp gates all three). */
|
|
331
366
|
crossbrowserOk: boolean | null;
|
|
@@ -380,7 +415,11 @@ type ResolvedCopy = {
|
|
|
380
415
|
announceBody: string;
|
|
381
416
|
announceImprovementResend: string;
|
|
382
417
|
announceImprovementSvelte5: string;
|
|
418
|
+
/** The cadence lead-in, ending at the em dash. Rendered followed by `announceCadenceNote`
|
|
419
|
+
* in italics. */
|
|
383
420
|
announceCadence: string;
|
|
421
|
+
/** The reassurance tail, rendered italic after `announceCadence`. */
|
|
422
|
+
announceCadenceNote: string;
|
|
384
423
|
announceOpenDoor: string;
|
|
385
424
|
};
|
|
386
425
|
|
|
@@ -418,6 +457,10 @@ type ReportData = {
|
|
|
418
457
|
* `| undefined` is explicit so callers can pass `undefined` under exactOptionalPropertyTypes. */
|
|
419
458
|
gaUsersCurrent?: number | undefined;
|
|
420
459
|
gaUsersPrevious?: number | undefined;
|
|
460
|
+
/** Length in days of the GA "current" window (compared against the equal-length prior window).
|
|
461
|
+
* Drives the analytics trend label ("vs the previous N days"); omitted → the generic
|
|
462
|
+
* "last period". `| undefined` for exactOptionalPropertyTypes. */
|
|
463
|
+
gaPeriodDays?: number | undefined;
|
|
421
464
|
/** Site's rounded average Google position for its query, when on page 1 (from Search Console).
|
|
422
465
|
* `undefined` = not on page 1, not checked, or unconfigured — rendered as today's plain check. */
|
|
423
466
|
searchPosition?: number | undefined;
|
|
@@ -721,6 +764,8 @@ type AttentionItem = {
|
|
|
721
764
|
autoFixExhausted?: boolean;
|
|
722
765
|
};
|
|
723
766
|
|
|
767
|
+
type FleetEventType = "pr_automerged" | "vuln_cleared" | "ci_recovered" | "site_launched" | "fleet_swept" | "cert_renewed";
|
|
768
|
+
|
|
724
769
|
type Tier = "attention" | "watch" | "healthy";
|
|
725
770
|
type SiteCard = {
|
|
726
771
|
site: WebsiteRow;
|
|
@@ -765,6 +810,16 @@ type CockpitSummary = {
|
|
|
765
810
|
/** Count of NEW submissions across the fleet (optional for back-compat). */
|
|
766
811
|
newSubmissions?: number;
|
|
767
812
|
};
|
|
813
|
+
/** A render-ready row for the cockpit "Recently" lane. `url` is an external link
|
|
814
|
+
* (PR) when present; `slug` links to `/s/<slug>` when the event names a site. */
|
|
815
|
+
type RecentEntry = {
|
|
816
|
+
type: FleetEventType;
|
|
817
|
+
summary: string;
|
|
818
|
+
siteName: string | null;
|
|
819
|
+
slug: string | null;
|
|
820
|
+
url: string | null;
|
|
821
|
+
ts: string;
|
|
822
|
+
};
|
|
768
823
|
type CockpitModel = {
|
|
769
824
|
summary: CockpitSummary;
|
|
770
825
|
/** All visible sites, ordered: attention (worst-first) → watch (A-Z) → healthy (A-Z). */
|
|
@@ -777,6 +832,8 @@ type CockpitModel = {
|
|
|
777
832
|
caught: number;
|
|
778
833
|
through: number;
|
|
779
834
|
} | null;
|
|
835
|
+
/** Recent fleet-activity events for the "Recently" lane (optional for back-compat). */
|
|
836
|
+
recent?: RecentEntry[];
|
|
780
837
|
};
|
|
781
838
|
|
|
782
839
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,34 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findDueReports
|
|
3
|
+
} from "./chunk-WVRPOXKH.js";
|
|
4
|
+
import {
|
|
5
|
+
upgradeSvelte4to5
|
|
6
|
+
} from "./chunk-O65I7G7G.js";
|
|
1
7
|
import {
|
|
2
8
|
DEFAULT_INIT_STEPS,
|
|
3
9
|
a11yFixturesPage,
|
|
4
10
|
init
|
|
5
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KTPPKO2C.js";
|
|
12
|
+
import {
|
|
13
|
+
convertToPnpm
|
|
14
|
+
} from "./chunk-2X322ZES.js";
|
|
6
15
|
import {
|
|
7
16
|
svelteCodemods
|
|
8
17
|
} from "./chunk-FUD4I2ZW.js";
|
|
18
|
+
import "./chunk-OQPMZ6BM.js";
|
|
9
19
|
import {
|
|
10
20
|
onboard,
|
|
11
21
|
selfCaretRange,
|
|
12
22
|
selfPackageVersion
|
|
13
23
|
} from "./chunk-ZYBOT5PM.js";
|
|
14
|
-
import "./chunk-VDBENQUA.js";
|
|
15
|
-
import {
|
|
16
|
-
findDueReports
|
|
17
|
-
} from "./chunk-WVRPOXKH.js";
|
|
18
24
|
import {
|
|
19
25
|
draftReportForSite
|
|
20
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-TYRCYHRA.js";
|
|
21
27
|
import "./chunk-6LIWDIXU.js";
|
|
28
|
+
import "./chunk-BMDBOPN3.js";
|
|
22
29
|
import {
|
|
23
30
|
sendApprovedReports
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-OGGRFBL4.js";
|
|
31
|
+
} from "./chunk-76VIUWR3.js";
|
|
26
32
|
import {
|
|
27
33
|
BLURRED_CID,
|
|
28
34
|
CHECK_CID,
|
|
29
35
|
loadBundledImages,
|
|
30
36
|
renderReportHtml
|
|
31
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-2T3HZ3DB.js";
|
|
38
|
+
import "./chunk-OGGRFBL4.js";
|
|
32
39
|
import {
|
|
33
40
|
checklistFor,
|
|
34
41
|
escapeHtml,
|
|
@@ -39,18 +46,11 @@ import {
|
|
|
39
46
|
import {
|
|
40
47
|
bumpDeps
|
|
41
48
|
} from "./chunk-D3DQFC32.js";
|
|
42
|
-
import {
|
|
43
|
-
upgradeSvelte4to5
|
|
44
|
-
} from "./chunk-O65I7G7G.js";
|
|
45
|
-
import "./chunk-OQPMZ6BM.js";
|
|
46
|
-
import {
|
|
47
|
-
convertToPnpm
|
|
48
|
-
} from "./chunk-2X322ZES.js";
|
|
49
49
|
import {
|
|
50
50
|
syncConfigs
|
|
51
|
-
} from "./chunk-
|
|
52
|
-
import "./chunk-CASCWQQX.js";
|
|
51
|
+
} from "./chunk-XVR7UM6Z.js";
|
|
53
52
|
import "./chunk-XKOZUVK2.js";
|
|
53
|
+
import "./chunk-CASCWQQX.js";
|
|
54
54
|
import "./chunk-OTGB6YQO.js";
|
|
55
55
|
import {
|
|
56
56
|
ALL_AUDIT_NAMES,
|
|
@@ -61,8 +61,11 @@ import {
|
|
|
61
61
|
runAudits,
|
|
62
62
|
runAuditsAcross,
|
|
63
63
|
securityAudit
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-SS7BKOIX.js";
|
|
65
65
|
import "./chunk-LKGVSM2O.js";
|
|
66
|
+
import "./chunk-L5F664ZA.js";
|
|
67
|
+
import "./chunk-6ZKM7OIJ.js";
|
|
68
|
+
import "./chunk-XKKTK6WA.js";
|
|
66
69
|
import {
|
|
67
70
|
fromJsonFile,
|
|
68
71
|
localPath
|
|
@@ -72,12 +75,12 @@ import "./chunk-XXTZBPUY.js";
|
|
|
72
75
|
import "./chunk-XTK5VIQB.js";
|
|
73
76
|
import {
|
|
74
77
|
fromAirtableBase
|
|
75
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-ZEX2T6JH.js";
|
|
76
79
|
import "./chunk-U5SZ4FOP.js";
|
|
77
80
|
import {
|
|
78
81
|
SEVERITY_RANK,
|
|
79
82
|
siteSlug
|
|
80
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-BLD6AYJO.js";
|
|
81
84
|
import "./chunk-HN6I2XWI.js";
|
|
82
85
|
import "./chunk-WEKL2HYJ.js";
|
|
83
86
|
import "./chunk-6VAL7XJT.js";
|
|
@@ -639,6 +642,14 @@ function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = n
|
|
|
639
642
|
|
|
640
643
|
// src/dashboard/fleet-cockpit.ts
|
|
641
644
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
645
|
+
var READY_DEPLOY_STATES = /* @__PURE__ */ new Set(["ready"]);
|
|
646
|
+
var FAILED_DEPLOY_STATES = /* @__PURE__ */ new Set(["error", "failed", "rejected"]);
|
|
647
|
+
function isReadyDeployStatus(status) {
|
|
648
|
+
return status !== null && READY_DEPLOY_STATES.has(status.toLowerCase());
|
|
649
|
+
}
|
|
650
|
+
function isFailedDeployStatus(status) {
|
|
651
|
+
return status !== null && FAILED_DEPLOY_STATES.has(status.toLowerCase());
|
|
652
|
+
}
|
|
642
653
|
var NEEDS_YOU_GROUP_RANK = { broken: 0, approval: 1, slipping: 2 };
|
|
643
654
|
function buildNeedsYouFeed(model) {
|
|
644
655
|
const bySlug = /* @__PURE__ */ new Map();
|
|
@@ -776,10 +787,25 @@ function card(site) {
|
|
|
776
787
|
site.securityVulnsModerate,
|
|
777
788
|
site.securityVulnsLow
|
|
778
789
|
)}
|
|
790
|
+
<span class="metric-label">deploy</span> ${deployBadge(site)}
|
|
779
791
|
</span>
|
|
780
792
|
</div>
|
|
781
793
|
</article>`;
|
|
782
794
|
}
|
|
795
|
+
function deployBadge(site) {
|
|
796
|
+
const state = site.deployStatus;
|
|
797
|
+
if (state === null) {
|
|
798
|
+
return `<span class="metric deploy unknown" title="No deploy data">${DASH2}</span>`;
|
|
799
|
+
}
|
|
800
|
+
const cls = isReadyDeployStatus(state) ? "deploy ready" : isFailedDeployStatus(state) ? "deploy failed" : "deploy building";
|
|
801
|
+
const when = relativeTimeFromNow(site.lastDeployAt);
|
|
802
|
+
const label = escapeHtml(`${state}${when !== DASH2 ? ` \xB7 ${when}` : ""}`);
|
|
803
|
+
const url = site.deployLogUrl ? safeUrl(site.deployLogUrl) : "#";
|
|
804
|
+
if (url !== "#") {
|
|
805
|
+
return `<a class="metric ${cls}" href="${escapeHtml(url)}" target="_blank" rel="noopener">${label}</a>`;
|
|
806
|
+
}
|
|
807
|
+
return `<span class="metric ${cls}">${label}</span>`;
|
|
808
|
+
}
|
|
783
809
|
function submBadge(c) {
|
|
784
810
|
const n = c.newSubmissions ?? 0;
|
|
785
811
|
return n > 0 ? `<span class="chip">\u{1F4E5} ${n} new</span>` : "";
|
|
@@ -890,6 +916,16 @@ h1 { margin: 0 0 0.25rem; font-size: 1.75rem; }
|
|
|
890
916
|
.cluster.lighthouse .score { display: inline-block; min-width: 2.25rem; text-align: right; }
|
|
891
917
|
.metric-label { color: #999; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
892
918
|
.metric { font-feature-settings: "tnum"; }
|
|
919
|
+
.metric.deploy { font-size:0.78rem; padding:0.05rem 0.4rem; border-radius:6px; text-decoration:none; }
|
|
920
|
+
.metric.deploy.ready { background:#e6f4ea; color:#1a7f37; }
|
|
921
|
+
.metric.deploy.failed { background:#fdecea; color:#b00; font-weight:600; }
|
|
922
|
+
.metric.deploy.building { background:#fff4e5; color:#9a6700; }
|
|
923
|
+
.metric.deploy.unknown { color:#999; }
|
|
924
|
+
@media (prefers-color-scheme: dark) {
|
|
925
|
+
.metric.deploy.ready { background:#0f2417; color:#4ac26b; }
|
|
926
|
+
.metric.deploy.failed { background:#3a1412; color:#ff6b6b; }
|
|
927
|
+
.metric.deploy.building { background:#2e2410; color:#e3b341; }
|
|
928
|
+
}
|
|
893
929
|
.spam-rollup { font-size:0.9rem; margin-bottom:1rem; }
|
|
894
930
|
.muted { color:#999; }
|
|
895
931
|
.filters { display:flex; flex-wrap:wrap; gap:0.4rem; margin-bottom:1.25rem; }
|
|
@@ -988,6 +1024,34 @@ function renderNeedsYouFeed(feed) {
|
|
|
988
1024
|
return `<section class="needs-you"><h2>Needs you (${feed.length})</h2>${blocks}</section>`;
|
|
989
1025
|
}
|
|
990
1026
|
var SUBMISSIONS_STRIP_CAP = 10;
|
|
1027
|
+
var RECENT_ICON = {
|
|
1028
|
+
pr_automerged: "\u{1F527}",
|
|
1029
|
+
vuln_cleared: "\u{1F6E1}",
|
|
1030
|
+
ci_recovered: "\u2705",
|
|
1031
|
+
site_launched: "\u{1F680}",
|
|
1032
|
+
cert_renewed: "\u{1F512}",
|
|
1033
|
+
fleet_swept: "\u{1F504}"
|
|
1034
|
+
};
|
|
1035
|
+
function renderRecentlyLane(model) {
|
|
1036
|
+
const events = model.recent ?? [];
|
|
1037
|
+
if (events.length === 0) return "";
|
|
1038
|
+
const rows = events.map((e) => {
|
|
1039
|
+
const icon = RECENT_ICON[e.type] ?? "\u2022";
|
|
1040
|
+
const when = escapeHtml(relativeTimeFromNow(e.ts));
|
|
1041
|
+
const site = e.siteName ? `<strong>${escapeHtml(e.siteName)}</strong> \u2014 ` : "";
|
|
1042
|
+
const link = e.url ? `<a href="${escapeHtml(e.url)}">view \u25B8</a>` : e.slug ? `<a href="/s/${escapeHtml(e.slug)}">open \u25B8</a>` : "";
|
|
1043
|
+
return `<div class="recent-row" data-type="${e.type}">
|
|
1044
|
+
<span class="recent-icon">${icon}</span>
|
|
1045
|
+
<span class="recent-what">${site}${escapeHtml(e.summary)}</span>
|
|
1046
|
+
<span class="muted">${when}</span>
|
|
1047
|
+
${link}
|
|
1048
|
+
</div>`;
|
|
1049
|
+
}).join("");
|
|
1050
|
+
return `<details class="recently">
|
|
1051
|
+
<summary>\u{1F527} Recently (${events.length})</summary>
|
|
1052
|
+
${rows}
|
|
1053
|
+
</details>`;
|
|
1054
|
+
}
|
|
991
1055
|
function renderInboxLane(model) {
|
|
992
1056
|
const subs = model.submissions ?? [];
|
|
993
1057
|
const spam = model.spam;
|
|
@@ -1151,6 +1215,7 @@ function renderCockpitHtml(model) {
|
|
|
1151
1215
|
${verdictBar(model, feed.length)}
|
|
1152
1216
|
${renderNeedsYouFeed(feed)}
|
|
1153
1217
|
${renderFleetBrowsePanel(model)}
|
|
1218
|
+
${renderRecentlyLane(model)}
|
|
1154
1219
|
${renderInboxLane(model)}
|
|
1155
1220
|
${AUDIT_SCRIPT}
|
|
1156
1221
|
${FLEET_BROWSE_SCRIPT}
|