@reddoorla/maintenance 0.89.0 → 0.90.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.
- package/dist/{announce-OJFAS5JT.js → announce-NDTLRQCN.js} +3 -3
- package/dist/{blux-EATHOHKK.js → blux-YGBGS24U.js} +5 -5
- package/dist/blux-YGBGS24U.js.map +1 -0
- package/dist/{chunk-3VQWSE3F.js → chunk-A6T2R63Z.js} +27 -5
- package/dist/chunk-A6T2R63Z.js.map +1 -0
- package/dist/{chunk-62CMMGEO.js → chunk-GSWTT2J6.js} +20 -5
- package/dist/chunk-GSWTT2J6.js.map +1 -0
- package/dist/{chunk-VNB5XBQ4.js → chunk-L5M6G65K.js} +1 -1
- package/dist/chunk-L5M6G65K.js.map +1 -0
- package/dist/{chunk-73UFRZKY.js → chunk-MLT4QY5X.js} +105 -2
- package/dist/chunk-MLT4QY5X.js.map +1 -0
- package/dist/{chunk-AEXJDCJN.js → chunk-QD427NIO.js} +5 -5
- package/dist/{chunk-62NYED6E.js → chunk-UXJZU23T.js} +3 -3
- package/dist/{chunk-O3GT46R2.js → chunk-YLYXW5PH.js} +14 -2
- package/dist/{chunk-O3GT46R2.js.map → chunk-YLYXW5PH.js.map} +1 -1
- package/dist/cli/bin.js +16 -16
- package/dist/cli/commands/audit.js +6 -6
- package/dist/configs/eslint.js +5 -1
- package/dist/configs/eslint.js.map +1 -1
- package/dist/configs/playwright-a11y.js +1 -1
- package/dist/{db-DAJX7NNW.js → db-LOSJ74ZJ.js} +1 -1
- package/dist/{digest-T6TAZNLF.js → digest-CS6KLCZB.js} +3 -3
- package/dist/digest-collectors-NI2M3UTC.js +1 -1
- package/dist/{ensure-site-NSNY2QHN.js → ensure-site-FFDUMY6E.js} +2 -2
- package/dist/{forms-notify-target-LYSKME5L.js → forms-notify-target-IHJX7NWA.js} +3 -3
- package/dist/{github-signals-4YML6NY5.js → github-signals-FCLNHKMF.js} +2 -2
- package/dist/{health-mirror-7LKC7Q5Z.js → health-mirror-GMSF3Z5R.js} +2 -2
- package/dist/images/index.d.ts +55 -0
- package/dist/images/index.js +16 -0
- package/dist/images/index.js.map +1 -0
- package/dist/index.js +9 -9
- package/dist/{init-NHKADHB6.js → init-NXZ4NXTL.js} +7 -7
- package/dist/{launch-VQJ4Z35C.js → launch-5V7RLUV7.js} +8 -8
- package/dist/notify-4HP4GZ2H.js +1 -1
- package/dist/orchestrate-6HROYTM6.js +1 -1
- package/dist/preflight-LIN5NCYU.js +1 -1
- package/dist/{prismic-ci-PZ5LCPWT.js → prismic-ci-6YGRIOHT.js} +5 -5
- package/dist/{prismic-models-6OYYZUR5.js → prismic-models-VNGIKS2B.js} +3 -3
- package/dist/{prospect-audit-DBBXL7EG.js → prospect-audit-JCWTKJTS.js} +1 -1
- package/dist/recipes/sync-configs.d.ts +8 -1
- package/dist/recipes/sync-configs.js +4 -2
- package/dist/{report-2N4PXO3L.js → report-AAY2HZZR.js} +8 -8
- package/dist/{report-mirror-3ENU6OOK.js → report-mirror-GMNSH5HF.js} +2 -2
- package/dist/{self-updating-NTCCWJYZ.js → self-updating-VQIW24RZ.js} +3 -3
- package/dist/selftest-CUX2FIQD.js +1 -1
- package/dist/{site-mirror-RQTEAID5.js → site-mirror-GCFLBBWK.js} +2 -2
- package/dist/{submissions-TEHPBSZJ.js → submissions-7LGJJSDL.js} +1 -1
- package/dist/{sync-configs-LVBITPLU.js → sync-configs-PVRLZRKL.js} +6 -12
- package/dist/sync-configs-PVRLZRKL.js.map +1 -0
- package/package.json +5 -1
- package/dist/blux-EATHOHKK.js.map +0 -1
- package/dist/chunk-3VQWSE3F.js.map +0 -1
- package/dist/chunk-62CMMGEO.js.map +0 -1
- package/dist/chunk-73UFRZKY.js.map +0 -1
- package/dist/chunk-VNB5XBQ4.js.map +0 -1
- package/dist/sync-configs-LVBITPLU.js.map +0 -1
- /package/dist/{announce-OJFAS5JT.js.map → announce-NDTLRQCN.js.map} +0 -0
- /package/dist/{chunk-AEXJDCJN.js.map → chunk-QD427NIO.js.map} +0 -0
- /package/dist/{chunk-62NYED6E.js.map → chunk-UXJZU23T.js.map} +0 -0
- /package/dist/{db-DAJX7NNW.js.map → db-LOSJ74ZJ.js.map} +0 -0
- /package/dist/{digest-T6TAZNLF.js.map → digest-CS6KLCZB.js.map} +0 -0
- /package/dist/{ensure-site-NSNY2QHN.js.map → ensure-site-FFDUMY6E.js.map} +0 -0
- /package/dist/{forms-notify-target-LYSKME5L.js.map → forms-notify-target-IHJX7NWA.js.map} +0 -0
- /package/dist/{github-signals-4YML6NY5.js.map → github-signals-FCLNHKMF.js.map} +0 -0
- /package/dist/{health-mirror-7LKC7Q5Z.js.map → health-mirror-GMSF3Z5R.js.map} +0 -0
- /package/dist/{init-NHKADHB6.js.map → init-NXZ4NXTL.js.map} +0 -0
- /package/dist/{launch-VQJ4Z35C.js.map → launch-5V7RLUV7.js.map} +0 -0
- /package/dist/{prismic-ci-PZ5LCPWT.js.map → prismic-ci-6YGRIOHT.js.map} +0 -0
- /package/dist/{prismic-models-6OYYZUR5.js.map → prismic-models-VNGIKS2B.js.map} +0 -0
- /package/dist/{prospect-audit-DBBXL7EG.js.map → prospect-audit-JCWTKJTS.js.map} +0 -0
- /package/dist/{report-2N4PXO3L.js.map → report-AAY2HZZR.js.map} +0 -0
- /package/dist/{report-mirror-3ENU6OOK.js.map → report-mirror-GMNSH5HF.js.map} +0 -0
- /package/dist/{self-updating-NTCCWJYZ.js.map → self-updating-VQIW24RZ.js.map} +0 -0
- /package/dist/{site-mirror-RQTEAID5.js.map → site-mirror-GCFLBBWK.js.map} +0 -0
- /package/dist/{submissions-TEHPBSZJ.js.map → submissions-7LGJJSDL.js.map} +0 -0
|
@@ -169,8 +169,111 @@ function templatesByName(which) {
|
|
|
169
169
|
return ALL_TEMPLATES.filter((t) => which.includes(t.config));
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
// src/recipes/sync-configs/renovate-action.ts
|
|
173
|
+
var DIGEST_RE = /^[0-9a-f]{40}$/;
|
|
174
|
+
function escapeRegExp(s) {
|
|
175
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
176
|
+
}
|
|
177
|
+
function stripComments(contents) {
|
|
178
|
+
return contents.replace(/^[ \t]*#[^\n]*$/gm, "");
|
|
179
|
+
}
|
|
180
|
+
var USES_LINE_RE = /^([ \t]*(?:- )?)uses:\s*([\w.-]+\/[\w.-]+)@(\S+)(?:[ \t]*#[^\r\n]*)?/gm;
|
|
181
|
+
function usesRefAndComment(contents, slug) {
|
|
182
|
+
const re = new RegExp(
|
|
183
|
+
`^[ \\t]*(?:- )?uses:\\s*${escapeRegExp(slug)}@(\\S+)([ \\t]*#[^\\r\\n]*)?`,
|
|
184
|
+
"m"
|
|
185
|
+
);
|
|
186
|
+
const m = contents.match(re);
|
|
187
|
+
if (!m) return null;
|
|
188
|
+
return { ref: m[1], comment: m[2] ?? "" };
|
|
189
|
+
}
|
|
190
|
+
function pinGap(slug, contents) {
|
|
191
|
+
const found = usesRefAndComment(contents, slug);
|
|
192
|
+
if (!found || !DIGEST_RE.test(found.ref)) {
|
|
193
|
+
return `${slug} is missing or not digest-pinned (needs @<40-hex-sha>) \u2014 this workflow mints a repo-write token, so a mutable tag ref is a supply-chain hole`;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
var KNOWN_PINNED_ACTIONS = [
|
|
198
|
+
"renovatebot/github-action",
|
|
199
|
+
"actions/create-github-app-token",
|
|
200
|
+
"actions/checkout"
|
|
201
|
+
];
|
|
202
|
+
var APP_AUTH_MARKERS = [
|
|
203
|
+
"vars.RENOVATE_APP_ID",
|
|
204
|
+
"secrets.RENOVATE_APP_PRIVATE_KEY",
|
|
205
|
+
"steps.app-token.outputs.token"
|
|
206
|
+
];
|
|
207
|
+
function renovateActionGaps(contents) {
|
|
208
|
+
const gaps = [];
|
|
209
|
+
const withoutComments = stripComments(contents);
|
|
210
|
+
for (const slug of KNOWN_PINNED_ACTIONS) {
|
|
211
|
+
const gap = pinGap(slug, withoutComments);
|
|
212
|
+
if (gap) gaps.push(gap);
|
|
213
|
+
}
|
|
214
|
+
for (const m of withoutComments.matchAll(USES_LINE_RE)) {
|
|
215
|
+
const slug = m[2];
|
|
216
|
+
const ref = m[3];
|
|
217
|
+
if (KNOWN_PINNED_ACTIONS.includes(slug)) continue;
|
|
218
|
+
if (!DIGEST_RE.test(ref)) {
|
|
219
|
+
gaps.push(
|
|
220
|
+
`${slug}@${ref} is not digest-pinned (needs @<40-hex-sha>) \u2014 this workflow mints a repo-write token, so every action reference must be digest-pinned`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const missingAuth = APP_AUTH_MARKERS.filter((m) => !withoutComments.includes(m));
|
|
225
|
+
if (missingAuth.length > 0) {
|
|
226
|
+
gaps.push(
|
|
227
|
+
`App auth is not fully wired \u2014 missing ${missingAuth.join(", ")} (Renovate authenticates as the reddoor-renovate GitHub App, not an operator PAT)`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
if (withoutComments.includes("RENOVATE_TOKEN")) {
|
|
231
|
+
gaps.push(
|
|
232
|
+
"contains RENOVATE_TOKEN \u2014 that is the retired operator-PAT identity the App migration replaced"
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (!/RENOVATE_USERNAME:\s*["']?reddoor-renovate\[bot\]/.test(withoutComments)) {
|
|
236
|
+
gaps.push("RENOVATE_USERNAME does not identify reddoor-renovate[bot]");
|
|
237
|
+
}
|
|
238
|
+
if (!/RENOVATE_GIT_AUTHOR:\s*["']?reddoor-renovate\[bot\]/.test(withoutComments)) {
|
|
239
|
+
gaps.push("RENOVATE_GIT_AUTHOR does not identify reddoor-renovate[bot]");
|
|
240
|
+
}
|
|
241
|
+
if (!/RENOVATE_REPOSITORIES:\s*["']?\$\{\{\s*github\.repository\s*\}\}["']?/.test(withoutComments)) {
|
|
242
|
+
gaps.push(
|
|
243
|
+
"RENOVATE_REPOSITORIES is not scoped to ${{ github.repository }} \u2014 this workflow must only ever touch its own repo"
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
if (!/cron:\s*["']?0 \*\/12 \* \* \*["']?/.test(withoutComments)) {
|
|
247
|
+
gaps.push(
|
|
248
|
+
`cron is not the twice-daily "0 */12 * * *" schedule \u2014 this cron IS the fleet's merge cadence (platform auto-merge is disabled fleet-wide, so Renovate merges from inside this run)`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (!withoutComments.includes("workflow_dispatch")) {
|
|
252
|
+
gaps.push("missing workflow_dispatch trigger");
|
|
253
|
+
}
|
|
254
|
+
const hasPermissionsKey = withoutComments.includes("permissions:");
|
|
255
|
+
const hasContentsRead = /^[ \t]*contents:\s*["']?read["']?[ \t]*$/m.test(withoutComments);
|
|
256
|
+
if (!hasPermissionsKey || !hasContentsRead) {
|
|
257
|
+
gaps.push("permissions: does not grant contents: read");
|
|
258
|
+
}
|
|
259
|
+
return gaps;
|
|
260
|
+
}
|
|
261
|
+
function withRenovatePinsFrom(template, current) {
|
|
262
|
+
if (current === null) return template;
|
|
263
|
+
const currentWithoutComments = stripComments(current);
|
|
264
|
+
return template.replace(USES_LINE_RE, (full, prefix, slug) => {
|
|
265
|
+
const found = usesRefAndComment(currentWithoutComments, slug);
|
|
266
|
+
if (!found || !DIGEST_RE.test(found.ref)) return full;
|
|
267
|
+
return `${prefix}uses: ${slug}@${found.ref}${found.comment}`;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
var RENOVATE_ACTION_CONFIG = "renovate-action";
|
|
271
|
+
|
|
172
272
|
export {
|
|
173
273
|
ALL_TEMPLATES,
|
|
174
|
-
templatesByName
|
|
274
|
+
templatesByName,
|
|
275
|
+
renovateActionGaps,
|
|
276
|
+
withRenovatePinsFrom,
|
|
277
|
+
RENOVATE_ACTION_CONFIG
|
|
175
278
|
};
|
|
176
|
-
//# sourceMappingURL=chunk-
|
|
279
|
+
//# sourceMappingURL=chunk-MLT4QY5X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recipes/sync-configs/templates.ts","../src/recipes/sync-configs/renovate-action.ts"],"sourcesContent":["import type { ConfigName } from \"../../types.js\";\n\nexport type ConfigTemplate = {\n config: ConfigName;\n path: string;\n contents: string;\n};\n\nconst eslint: ConfigTemplate = {\n config: \"eslint\",\n path: \"eslint.config.js\",\n contents: `import { createEslintConfig } from \"@reddoorla/maintenance/configs/eslint\";\nimport svelteConfig from \"./svelte.config.js\";\n\nexport default createEslintConfig({ svelteConfig });\n`,\n};\n\nconst prettier: ConfigTemplate = {\n config: \"prettier\",\n path: \".prettierrc.json\",\n contents: `{\n \"trailingComma\": \"all\",\n \"singleQuote\": false,\n \"printWidth\": 100,\n \"plugins\": [\"prettier-plugin-svelte\"]\n}\n`,\n};\n\nconst prettierIgnore: ConfigTemplate = {\n config: \"prettier-ignore\",\n path: \".prettierignore\",\n contents: `pnpm-lock.yaml\n.svelte-kit/\nbuild/\n.netlify/\ndist/\n`,\n};\n\nconst lighthouse: ConfigTemplate = {\n config: \"lighthouse\",\n path: \"lighthouserc.json\",\n contents: `${JSON.stringify(\n {\n $note:\n \"Generated by @reddoorla/maintenance sync-configs; edit src/configs/lighthouse.ts in the package instead.\",\n extends: \"@reddoorla/maintenance/configs/lighthouse\",\n },\n null,\n 2,\n )}\n`,\n};\n\nconst playwrightA11y: ConfigTemplate = {\n config: \"playwright-a11y\",\n path: \"playwright.config.ts\",\n contents: `export { default } from \"@reddoorla/maintenance/configs/playwright-a11y\";\n`,\n};\n\nconst svelte: ConfigTemplate = {\n config: \"svelte\",\n path: \"svelte.config.js\",\n contents: `import { createSvelteConfig } from \"@reddoorla/maintenance/configs/svelte\";\nimport adapter from \"@sveltejs/adapter-netlify\";\n\n/** @type {import('@sveltejs/kit').Config} */\nexport default createSvelteConfig({\n kit: { adapter: adapter({ edge: false, split: false }) },\n});\n`,\n};\n\n// There is deliberately NO ci.yml template. Every live fleet ci.yml carries\n// per-site values a static template cannot own (`netlify-site:`, plus\n// `permissions:` and `node-version:` inputs) — an exact-match heal against a\n// canonical byte template would strip them fleet-wide in green auto-mergeable\n// PRs (the \"armed clobber\" the 2026-08-02 architecture review flagged; it was\n// this file's `ci` entry). Ownership is split instead: the STARTER clone\n// provides each site's ci.yml shape at bootstrap, and Renovate's\n// github-actions manager bumps the pinned reusable-workflow ref per repo when\n// reddoorla/.github tags a new version. The caller job name `ci` + the\n// reusable workflow's job `ci` produce the check context \"ci / ci\" —\n// REQUIRED_CHECK in src/recipes/self-updating/index.ts depends on that naming.\n\nconst renovateAction: ConfigTemplate = {\n config: \"renovate-action\",\n path: \".github/workflows/renovate.yml\",\n contents: `name: renovate\non:\n schedule:\n # Twice daily. GitHub's platform auto-merge is disabled fleet-wide, so Renovate\n # performs its own merges from inside this run — which means this cron is the\n # MERGE cadence, not just the PR-creation cadence.\n - cron: 0 */12 * * *\n workflow_dispatch:\npermissions:\n contents: read\njobs:\n renovate:\n runs-on: ubuntu-latest\n steps:\n # Renovate authenticates as the reddoor-renovate GitHub App (installed\n # org-wide, \"All repositories\" — future repos are auto-covered), NOT as an\n # operator PAT. Why: the App is a distinct identity, so the audit trail\n # finally distinguishes bot from human (the 2026-07-26 unreviewed-major\n # forensics took hours precisely because it couldn't); the credential is\n # permission-scoped and centrally revocable instead of a fleet-write PAT;\n # and the installation gets its own API-quota pool instead of sharing the\n # operator's. The minted token is scoped to THIS repository only\n # (create-github-app-token's default when owner/repositories are omitted).\n # RENOVATE_APP_ID / RENOVATE_APP_PRIVATE_KEY are org-level (all-repos\n # visibility) — nothing to configure per repo.\n # Digest-pinned: this workflow mints a repo-write token, so a mutable tag\n # ref here is a supply-chain hole — a retagged \\`@v3\\`/\\`@v46\\` would run\n # attacker code with that token. Pin to the commit; Renovate bumps these.\n - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0\n id: app-token\n with:\n app-id: \\${{ vars.RENOVATE_APP_ID }}\n private-key: \\${{ secrets.RENOVATE_APP_PRIVATE_KEY }}\n - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7\n - uses: renovatebot/github-action@1a96852b0384df1837619d04c60b2d10d1f9ff08 # v46.1.21\n with:\n token: \\${{ steps.app-token.outputs.token }}\n env:\n RENOVATE_REPOSITORIES: \\${{ github.repository }}\n # Commit/PR identity = the App's bot user, so gitAuthor matches the\n # token identity (mismatched authors read as \"operator pushed this\").\n # 312185038 = the bot's user id (gh api 'users/reddoor-renovate[bot]' --jq .id).\n RENOVATE_USERNAME: reddoor-renovate[bot]\n RENOVATE_GIT_AUTHOR: reddoor-renovate[bot] <312185038+reddoor-renovate[bot]@users.noreply.github.com>\n`,\n};\n\nconst renovateConfig: ConfigTemplate = {\n config: \"renovate-config\",\n path: \"renovate.json\",\n contents: `{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\"github>reddoorla/.github:renovate-config\"]\n}\n`,\n};\n\nconst netlify: ConfigTemplate = {\n config: \"netlify\",\n path: \"netlify.toml\",\n contents: `[build]\n command = \"pnpm build\"\n publish = \"build/\"\n functions = \"functions/\"\n\n[build.environment]\n NODE_VERSION = \"22\"\n COREPACK_INTEGRITY_KEYS = \"0\"\n\n# Baseline security headers for all responses. CSP is emitted per-response by\n# SvelteKit (see \\`kit.csp\\` in svelte.config.js) so it is intentionally omitted\n# here to avoid conflicting duplicates.\n[[headers]]\n for = \"/*\"\n [headers.values]\n Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n X-Content-Type-Options = \"nosniff\"\n X-Frame-Options = \"SAMEORIGIN\"\n Referrer-Policy = \"strict-origin-when-cross-origin\"\n Permissions-Policy = \"camera=(), microphone=(), geolocation=(), interest-cohort=()\"\n Cross-Origin-Opener-Policy = \"same-origin\"\n\n[[headers]]\n for = \"/favicon.png\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n\n[[headers]]\n for = \"/_app/immutable/*\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n`,\n};\n\nexport const ALL_TEMPLATES: ConfigTemplate[] = [\n eslint,\n prettier,\n prettierIgnore,\n lighthouse,\n playwrightA11y,\n svelte,\n renovateAction,\n renovateConfig,\n netlify,\n];\n\nexport function templatesByName(which: ConfigName[]): ConfigTemplate[] {\n return ALL_TEMPLATES.filter((t) => which.includes(t.config));\n}\n","import type { ConfigName } from \"../../types.js\";\n\n/**\n * `.github/workflows/renovate.yml` (the `renovate-action` template) is\n * compliance-checked, not byte-matched, for the same reason `svelte.config.js`\n * and `netlify.toml` already are (see `isSvelteConfigCompliant` /\n * `isNetlifyConfigCompliant` in `sync-configs.ts`): an exact overwrite is the\n * wrong operation whenever a file legitimately diverges from the template\n * without being wrong. For renovate.yml there are TWO such legitimate\n * divergences, both observed live on 2026-08-31 (issue #651):\n *\n * 1. Renovate bumps its own digest pins. The template pins\n * `renovatebot/github-action@1a96852b...` (# v46.1.21); a site Renovate\n * maintains moves that pin forward on its own (reddoor-starter was already\n * at `@e09d604f...` / v46.2.2). A byte-match recipe reads that as drift and\n * opens a PR that DOWNGRADES the pin — reverting Renovate's own\n * supply-chain update, the exact thing the template's own comment says\n * Renovate is supposed to do. Observed live: it opened\n * `reddoorla/reddoor-starter-blux#1`, closed unmerged.\n * 2. YAML scalar quoting. The template writes an unquoted twice-daily cron and\n * `RENOVATE_USERNAME: reddoor-renovate[bot]` unquoted; a site's prettier\n * may render both quoted instead. Both forms are prettier-clean (verified\n * against the repo's own prettier), so prettier never normalizes one\n * toward the other — once a site's copy diverges in quoting, a byte-match\n * recipe reports drift FOREVER.\n *\n * The fix, following the svelte.config/netlify.toml precedent exactly: stop\n * asking \"is this file byte-identical to the template?\" and ask \"does it\n * still satisfy the invariants we actually own?\". `renovateActionGaps`\n * returns that — a `string[]` of human-readable gaps, empty meaning\n * compliant and left alone, the same shape as `rulesetGaps` (src/github/rulesets.ts)\n * and `renovateGaps` (src/audits/protection-coverage.ts).\n *\n * When a file genuinely IS non-compliant (e.g. its cron changed) the recipe\n * heals it by writing the template — but writing the template verbatim would\n * itself re-introduce the downgrade bug above, overwriting the site's newer\n * digest pins with the template's older ones. `withRenovatePinsFrom` carries\n * the site's own (still-digest-pinned) refs forward onto the template before\n * it is written — see that function's own doc comment for exactly what\n * guarantee this is (and, as importantly, isn't).\n *\n * Every check below runs against a COMMENT-STRIPPED copy of the file (see\n * `stripComments`). A raw-text `.match()`/`.includes()` takes the FIRST hit\n * anywhere in the file, comments included — a canonical pin, cron, or job\n * body sitting in a `#` comment above the real (non-compliant) line reads as\n * compliant, and worse, `withRenovatePinsFrom` would scrape a sha out of a\n * comment like `# old: uses: actions/checkout@1111…1111 # v4` and carry a\n * *commented* pin forward into the healed file — a pin downgrade delivered by\n * the very recipe that exists to prevent pin downgrades.\n */\n\nconst DIGEST_RE = /^[0-9a-f]{40}$/;\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Strip full-line `#` comments before any compliance check or ref extraction\n * runs, so a canonical-looking line that exists only in a comment can never\n * read as the real thing.\n *\n * Full-line only: a trailing `# vX.Y.Z` after a real `uses:` line is\n * meaningful (it's the human-readable version tag) and must survive — only\n * strip a line whose first non-whitespace character is `#`. This is safe\n * specifically because this workflow's YAML has no block scalars (`|`/`>`),\n * so there is no line in a compliant or non-compliant renovate.yml where a\n * leading `#` is literal string content rather than a comment.\n */\nfunction stripComments(contents: string): string {\n return contents.replace(/^[ \\t]*#[^\\n]*$/gm, \"\");\n}\n\n/** A `uses:` line, anchored to the start of a line (allowing only leading\n * indentation and an optional `- ` list marker before `uses:`) so a match\n * can only ever land on a real step, never on prose inside a comment that\n * happens to contain the word `uses:`. Always run against comment-stripped\n * text (see `stripComments`) — the anchor alone does not exclude comments,\n * since a full-line comment's content can itself start with `- uses:` at\n * matching indentation. */\nconst USES_LINE_RE = /^([ \\t]*(?:- )?)uses:\\s*([\\w.-]+\\/[\\w.-]+)@(\\S+)(?:[ \\t]*#[^\\r\\n]*)?/gm;\n\n/** The ref (and trailing `# vX.Y.Z` comment, if any) a `uses: <slug>@<ref>`\n * line carries in `contents`, or null if that action isn't referenced.\n * Callers must pass comment-stripped text — see `stripComments`. */\nfunction usesRefAndComment(\n contents: string,\n slug: string,\n): { ref: string; comment: string } | null {\n const re = new RegExp(\n `^[ \\\\t]*(?:- )?uses:\\\\s*${escapeRegExp(slug)}@(\\\\S+)([ \\\\t]*#[^\\\\r\\\\n]*)?`,\n \"m\",\n );\n const m = contents.match(re);\n if (!m) return null;\n return { ref: m[1]!, comment: m[2] ?? \"\" };\n}\n\n/** Gap text for one `uses:` action that must be present AND digest-pinned.\n * This workflow mints a repo-write token (the App-token step), so a mutable\n * tag ref on ANY step here is a supply-chain hole — a retagged `@v3`/`@v46`\n * would run attacker code with that token. (The three actions checked here\n * are the ones this workflow ships with by default; `renovateActionGaps`\n * separately widens this to ANY `uses:` line, so an action added later is\n * covered too.) `contents` must already be comment-stripped. */\nfunction pinGap(slug: string, contents: string): string | null {\n const found = usesRefAndComment(contents, slug);\n if (!found || !DIGEST_RE.test(found.ref)) {\n return `${slug} is missing or not digest-pinned (needs @<40-hex-sha>) — this workflow mints a repo-write token, so a mutable tag ref is a supply-chain hole`;\n }\n return null;\n}\n\n/** The three actions this workflow ships with by default and always requires,\n * present and digest-pinned. Checked individually (via `pinGap`, above) so a\n * gap can say which one of the three is the problem; the widen loop in\n * `renovateActionGaps` then covers every OTHER `uses:` line so a step added\n * later can't slip in on a mutable ref unnoticed. */\nconst KNOWN_PINNED_ACTIONS = [\n \"renovatebot/github-action\",\n \"actions/create-github-app-token\",\n \"actions/checkout\",\n];\n\nconst APP_AUTH_MARKERS = [\n \"vars.RENOVATE_APP_ID\",\n \"secrets.RENOVATE_APP_PRIVATE_KEY\",\n \"steps.app-token.outputs.token\",\n];\n\n/** Gaps in a site's `.github/workflows/renovate.yml`. Empty = compliant,\n * leave it alone — see the module doc comment above for why this is\n * compliance-checked rather than byte-matched. */\nexport function renovateActionGaps(contents: string): string[] {\n const gaps: string[] = [];\n const withoutComments = stripComments(contents);\n\n for (const slug of KNOWN_PINNED_ACTIONS) {\n const gap = pinGap(slug, withoutComments);\n if (gap) gaps.push(gap);\n }\n\n // Widen the pin check to every OTHER `uses:` line in the file — the doc\n // comment on `pinGap` says a mutable ref on ANY step is a supply-chain\n // hole; without this, a step added later (e.g. `pnpm/action-setup@v4`)\n // would pass clean just because it isn't one of the three known actions.\n // A local `uses: ./.github/actions/foo` path ref never matches — the regex\n // requires a literal `@<ref>`, which a bare path reference doesn't have.\n for (const m of withoutComments.matchAll(USES_LINE_RE)) {\n const slug = m[2]!;\n const ref = m[3]!;\n if (KNOWN_PINNED_ACTIONS.includes(slug)) continue; // already covered above\n if (!DIGEST_RE.test(ref)) {\n gaps.push(\n `${slug}@${ref} is not digest-pinned (needs @<40-hex-sha>) — this workflow mints a repo-write token, so every action reference must be digest-pinned`,\n );\n }\n }\n\n const missingAuth = APP_AUTH_MARKERS.filter((m) => !withoutComments.includes(m));\n if (missingAuth.length > 0) {\n gaps.push(\n `App auth is not fully wired — missing ${missingAuth.join(\", \")} (Renovate authenticates as the reddoor-renovate GitHub App, not an operator PAT)`,\n );\n }\n\n if (withoutComments.includes(\"RENOVATE_TOKEN\")) {\n gaps.push(\n \"contains RENOVATE_TOKEN — that is the retired operator-PAT identity the App migration replaced\",\n );\n }\n\n // Checked (and reported) as two independent gaps, not one folded string, so\n // a caller can tell which field actually failed.\n if (!/RENOVATE_USERNAME:\\s*[\"']?reddoor-renovate\\[bot\\]/.test(withoutComments)) {\n gaps.push(\"RENOVATE_USERNAME does not identify reddoor-renovate[bot]\");\n }\n if (!/RENOVATE_GIT_AUTHOR:\\s*[\"']?reddoor-renovate\\[bot\\]/.test(withoutComments)) {\n gaps.push(\"RENOVATE_GIT_AUTHOR does not identify reddoor-renovate[bot]\");\n }\n\n if (\n !/RENOVATE_REPOSITORIES:\\s*[\"']?\\$\\{\\{\\s*github\\.repository\\s*\\}\\}[\"']?/.test(withoutComments)\n ) {\n gaps.push(\n \"RENOVATE_REPOSITORIES is not scoped to ${{ github.repository }} — this workflow must only ever touch its own repo\",\n );\n }\n\n if (!/cron:\\s*[\"']?0 \\*\\/12 \\* \\* \\*[\"']?/.test(withoutComments)) {\n gaps.push(\n 'cron is not the twice-daily \"0 */12 * * *\" schedule — this cron IS the fleet\\'s merge cadence (platform auto-merge is disabled fleet-wide, so Renovate merges from inside this run)',\n );\n }\n\n if (!withoutComments.includes(\"workflow_dispatch\")) {\n gaps.push(\"missing workflow_dispatch trigger\");\n }\n\n // A `permissions:` key is present somewhere AND a `contents: read` line\n // (optionally quoted) exists somewhere — not anchored to top-level-first-key,\n // since this predicate doesn't actually need to assert that shape (a\n // job-level permissions block satisfies the same invariant), and requiring\n // `contents: read` to be the first entry under `permissions:` false-positived\n // on both quoting (`contents: \"read\"`) and ordering (`contents: read` listed\n // after another key, e.g. `pull-requests: write`).\n const hasPermissionsKey = withoutComments.includes(\"permissions:\");\n const hasContentsRead = /^[ \\t]*contents:\\s*[\"']?read[\"']?[ \\t]*$/m.test(withoutComments);\n if (!hasPermissionsKey || !hasContentsRead) {\n gaps.push(\"permissions: does not grant contents: read\");\n }\n\n return gaps;\n}\n\n/**\n * The canonical template with each action's `uses:` ref replaced by the ref\n * the site already has, when the site's is digest-pinned.\n *\n * Why: when a file IS genuinely non-compliant (say its cron changed) the\n * recipe heals it by writing the template — and writing the template verbatim\n * would put the template's OLDER pins over the site's newer ones, re-\n * introducing the downgrade this fix removes. So carry the site's pins\n * forward onto the (otherwise-canonical) healed file.\n *\n * This is PIN-NEUTRAL, not downgrade-avoiding: it substitutes the site's ref\n * whenever it is 40 hex characters, with no notion of \"newer\" — digests are\n * unorderable, so there is no way to tell from the strings alone. It never\n * changes a digest-pinned ref in either direction; some fleet sites carry a\n * pin OLDER than the template's and will heal onto their own older pin\n * (defensible — Renovate re-advances it on its own), so \"pin-neutral\" is the\n * accurate description, not \"downgrade-avoiding\".\n *\n * `current === null` (no existing file) returns the template unchanged — there\n * is nothing to carry forward. A non-digest ref on the site (a mutable tag) is\n * never substituted in, since that would carry the mutable-ref hole forward\n * instead of healing it. Likewise, a ref that appears only inside a `#`\n * comment in `current` is never carried forward (see `stripComments`) — a\n * commented sha must never be scraped and pinned into the healed file.\n */\nexport function withRenovatePinsFrom(template: string, current: string | null): string {\n if (current === null) return template;\n const currentWithoutComments = stripComments(current);\n return template.replace(USES_LINE_RE, (full, prefix: string, slug: string) => {\n const found = usesRefAndComment(currentWithoutComments, slug);\n if (!found || !DIGEST_RE.test(found.ref)) return full;\n return `${prefix}uses: ${slug}@${found.ref}${found.comment}`;\n });\n}\n\n/** The `ConfigName` for `.github/workflows/renovate.yml`. Exported once here\n * (rather than re-declared as a local constant in both `sync-configs.ts` and\n * `self-updating/index.ts`) so the two call sites can't drift apart. */\nexport const RENOVATE_ACTION_CONFIG: ConfigName = \"renovate-action\";\n"],"mappings":"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","names":[]}
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
assertUrlSegment
|
|
9
9
|
} from "./chunk-XHHMM5HX.js";
|
|
10
|
+
import {
|
|
11
|
+
smokeAudit
|
|
12
|
+
} from "./chunk-UDRHAQT7.js";
|
|
10
13
|
import {
|
|
11
14
|
INGEST_TIMEOUT_MS
|
|
12
15
|
} from "./chunk-A3WDWNN5.js";
|
|
@@ -14,9 +17,6 @@ import {
|
|
|
14
17
|
lighthouseAudit,
|
|
15
18
|
readSiteConfig
|
|
16
19
|
} from "./chunk-2NQUCS7O.js";
|
|
17
|
-
import {
|
|
18
|
-
smokeAudit
|
|
19
|
-
} from "./chunk-UDRHAQT7.js";
|
|
20
20
|
import {
|
|
21
21
|
findFreePort
|
|
22
22
|
} from "./chunk-PTDMVGKL.js";
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import {
|
|
30
30
|
a11yRoutes,
|
|
31
31
|
smokeRoutes
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-YLYXW5PH.js";
|
|
33
33
|
|
|
34
34
|
// src/audits/deps.ts
|
|
35
35
|
import { readFile } from "fs/promises";
|
|
@@ -1979,4 +1979,4 @@ export {
|
|
|
1979
1979
|
runAudits,
|
|
1980
1980
|
runAuditsAcross
|
|
1981
1981
|
};
|
|
1982
|
-
//# sourceMappingURL=chunk-
|
|
1982
|
+
//# sourceMappingURL=chunk-QD427NIO.js.map
|
|
@@ -15,13 +15,13 @@ import {
|
|
|
15
15
|
} from "./chunk-7RJ2HMMJ.js";
|
|
16
16
|
import {
|
|
17
17
|
syncConfigs
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-A6T2R63Z.js";
|
|
19
19
|
import {
|
|
20
20
|
withRecipe
|
|
21
21
|
} from "./chunk-3G25KIWW.js";
|
|
22
22
|
import {
|
|
23
23
|
runAudits
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-QD427NIO.js";
|
|
25
25
|
import {
|
|
26
26
|
siteLabel
|
|
27
27
|
} from "./chunk-XXTZBPUY.js";
|
|
@@ -143,4 +143,4 @@ export {
|
|
|
143
143
|
DEFAULT_INIT_STEPS,
|
|
144
144
|
init
|
|
145
145
|
};
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
146
|
+
//# sourceMappingURL=chunk-UXJZU23T.js.map
|
|
@@ -38,7 +38,19 @@ var playwrightA11yConfig = defineConfig({
|
|
|
38
38
|
reporter: process.env.CI ? "github" : "list",
|
|
39
39
|
use: {
|
|
40
40
|
baseURL: `http://localhost:${port}`,
|
|
41
|
-
trace: "on-first-retry"
|
|
41
|
+
trace: "on-first-retry",
|
|
42
|
+
// Emulate reduced motion fleet-wide: scrollIntoView lands instantly rather
|
|
43
|
+
// than animating, so Playwright's actionability checks don't flake under
|
|
44
|
+
// parallel load, and view transitions fall back to instant. Pairs with the
|
|
45
|
+
// prefers-reduced-motion gate on scroll-behavior in every site's app.css.
|
|
46
|
+
//
|
|
47
|
+
// It MUST sit under `contextOptions` — `reducedMotion` is a
|
|
48
|
+
// BrowserContextOptions member, not a top-level test option. reddoor-starter
|
|
49
|
+
// carried it at the top level of `use` from 2026-06 until 2026-09-01, where
|
|
50
|
+
// Playwright silently ignored it (unknown keys are dropped at runtime) and
|
|
51
|
+
// `pnpm check` never flagged it because svelte-check does not typecheck
|
|
52
|
+
// playwright.config.ts. The emulation was inert that whole time.
|
|
53
|
+
contextOptions: { reducedMotion: "reduce" }
|
|
42
54
|
},
|
|
43
55
|
projects: [
|
|
44
56
|
{
|
|
@@ -93,4 +105,4 @@ export {
|
|
|
93
105
|
smokeRoutes,
|
|
94
106
|
playwright_a11y_default
|
|
95
107
|
};
|
|
96
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-YLYXW5PH.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/configs/playwright-a11y.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { defineConfig, devices, type PlaywrightTestConfig } from \"@playwright/test\";\n\nexport type A11yRoute = { path: string; name: string };\n\nexport const a11yRoutes: A11yRoute[] = [\n { path: \"/dev/a11y-fixtures\", name: \"a11y fixtures\" },\n { path: \"/dev/animate-in\", name: \"animate-in demo\" },\n];\n\n// Routes smoke-loaded for client-side (hydration) errors only — NOT axe-scanned.\n// Catches the class of bug where build + SSR succeed but client hydration throws\n// and blanks the page (data-dynamiq 2026-06-09: a Svelte 4->5 `run()` referenced\n// a `$state` declared after it → TDZ ReferenceError on hydrate). `/` is the one\n// route every site has; real routes carry a11y debt we don't gate on here, so we\n// assert only that they don't crash on hydrate.\nexport const smokeRoutes: A11yRoute[] = [{ path: \"/\", name: \"home\" }];\n\n// R1.1 (health-gate): the central `smoke` audit (src/audits/smoke.ts) allocates\n// a free port and passes it as REDDOOR_SMOKE_PORT so a zombie vite already\n// squatting the default 5173 can't silently hijack the run and green a stale\n// build. The per-site R1.1 config template honors it, but sites whose\n// playwright.config.ts merely re-exports this shared base (pre-R1.1 adopters\n// the smoke-suite recipe flags-but-never-rewrites) would otherwise ignore it —\n// so honor it here too and every re-exporter inherits the port binding on its\n// next package bump. Unset (local `pnpm test:smoke`) → the fixed 5173.\nconst smokePort = process.env.REDDOOR_SMOKE_PORT;\n\n/**\n * Allocate a free port SYNCHRONOUSLY, for the local path where nothing handed\n * us one. Same trick as src/util/free-port.ts (bind :0, read the assigned port,\n * release it) — but that is async, and this value is needed at module scope\n * while Playwright is still building the config object.\n *\n * It cannot be an async default export instead: sites consume this base by\n * SPREADING it (`{ ...base, use: { ...base.use } }` — see the smoke-suite\n * recipe template). Spreading a Promise yields none of its properties, so the\n * site would get a silently empty config — the exact false-green this whole\n * change exists to remove. The export must stay a plain object.\n *\n * A subprocess is the cost of that constraint: ~30-50ms, once per Playwright\n * run. On any failure we return null and the caller falls back to 5173, which\n * (with reuseExistingServer now false) degrades to a loud \"port already in use\"\n * rather than a silent wrong-server run.\n */\nfunction allocateFreePortSync(): string | null {\n try {\n const out = execFileSync(\n process.execPath,\n [\n \"-e\",\n 'const s=require(\"node:net\").createServer();s.on(\"error\",()=>process.exit(1));' +\n 's.listen(0,\"127.0.0.1\",()=>{const p=s.address().port;s.close(()=>process.stdout.write(String(p)))});',\n ],\n { encoding: \"utf8\", timeout: 5_000, stdio: [\"ignore\", \"pipe\", \"ignore\"] },\n ).trim();\n return /^\\d+$/.test(out) ? out : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the port this run binds to, ONCE per run rather than once per\n * evaluation of this file.\n *\n * REDDOOR_SMOKE_PORT (the central audit already allocated one) wins; otherwise\n * this run allocates its own. The old fallback was the fixed 5173 — the same\n * port a dev server sits on, which is what let `reuseExistingServer` silently\n * hijack local runs (#524).\n *\n * The allocation is pinned back into the environment, and that is the whole\n * point. Playwright re-evaluates the config file in EVERY worker process, not\n * just the main one. Allocating per evaluation gave each worker a different\n * port: the main process started the dev server on one, every worker aimed\n * `baseURL` at another, and the run died with ERR_CONNECTION_REFUSED against a\n * handful of ports nothing was ever serving. Workers are forked and inherit\n * this environment, so writing it back makes every later evaluation agree.\n *\n * It broke on the site suites and not on `audit --only a11y`, which writes its\n * own config and never reads this file — so nothing here caught it.\n */\nfunction resolvePort(): string {\n if (smokePort) return smokePort;\n const allocated = allocateFreePortSync() || \"5173\";\n process.env.REDDOOR_SMOKE_PORT = allocated;\n return allocated;\n}\n\nconst port = resolvePort();\n\n// NOTE: default export only — sites consume this as `import base from\n// \"@reddoorla/maintenance/configs/playwright-a11y\"` (or re-export the default).\n// The old `playwrightA11yConfig` named alias had zero importers and was removed.\nconst playwrightA11yConfig: PlaywrightTestConfig = defineConfig({\n testDir: \"tests\",\n testMatch: /.*\\.spec\\.ts$/,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: process.env.CI ? \"github\" : \"list\",\n use: {\n baseURL: `http://localhost:${port}`,\n trace: \"on-first-retry\",\n },\n projects: [\n {\n name: \"chromium\",\n use: { ...devices[\"Desktop Chrome\"] },\n },\n ],\n webServer: {\n // Portable across pnpm and npm sites — pnpm respects `npm run` too.\n //\n // `--port ... --strictPort` in BOTH cases. It used to be applied only when\n // REDDOOR_SMOKE_PORT allocated one, on the reasoning that we should \"fail\n // loudly rather than let vite drift to a free port the baseURL doesn't\n // point at\" — but that argument covers the unset case just as well. 5173 is\n // equally a fixed port that `baseURL` and the readiness probe below are\n // pinned to, and vite left to itself drifts off it whenever something else\n // holds it.\n //\n // The symptom that exposed this: a non-vite process on 5173 sends vite to\n // 5174 while the probe keeps polling 5173, so the run dies on\n // \"Timed out waiting 120000ms from config.webServer\" — 120 seconds of\n // nothing, naming neither the port nor the squatter. With --strictPort it\n // is an immediate \"Port 5173 is already in use\".\n //\n // --strictPort now only bites if the allocated port is taken in the window\n // between releasing and binding it, which is exactly the case worth failing\n // on.\n command: `npm run vite:dev -- --port ${port} --strictPort`,\n url: `http://localhost:${port}/dev/a11y-fixtures`,\n // NEVER reuse (#524). This used to be `!process.env.CI`, so local runs\n // reused whatever answered the probe URL. The probe only asks \"does this\n // respond?\" — never \"is this serving the code I am about to test?\" — so a\n // dev server left open, or one whose tree changed under it after a\n // checkout, silently became the system under test. That fails in both\n // directions: a false red blamed on the code (beachfront 2026-08-12, where\n // it was investigated as a macOS-vs-Linux difference and written up as one\n // before being caught), and a false green where a passing suite ran against\n // an old build. CI already had it false, and that asymmetry is precisely\n // what made the failure read as a platform bug.\n //\n // The cost is a fresh vite boot per run (~10-20s against a ~2min suite).\n // Because the port above is allocated rather than fixed, your own dev\n // server on 5173 keeps running untouched.\n reuseExistingServer: false,\n timeout: 120_000,\n },\n});\n\nexport default playwrightA11yConfig;\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,cAAc,eAA0C;AAI1D,IAAM,aAA0B;AAAA,EACrC,EAAE,MAAM,sBAAsB,MAAM,gBAAgB;AAAA,EACpD,EAAE,MAAM,mBAAmB,MAAM,kBAAkB;AACrD;AAQO,IAAM,cAA2B,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC;AAUpE,IAAM,YAAY,QAAQ,IAAI;AAmB9B,SAAS,uBAAsC;AAC7C,MAAI;AACF,UAAM,MAAM;AAAA,MACV,QAAQ;AAAA,MACR;AAAA,QACE;AAAA,QACA;AAAA,MAEF;AAAA,MACA,EAAE,UAAU,QAAQ,SAAS,KAAO,OAAO,CAAC,UAAU,QAAQ,QAAQ,EAAE;AAAA,IAC1E,EAAE,KAAK;AACP,WAAO,QAAQ,KAAK,GAAG,IAAI,MAAM;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAsBA,SAAS,cAAsB;AAC7B,MAAI,UAAW,QAAO;AACtB,QAAM,YAAY,qBAAqB,KAAK;AAC5C,UAAQ,IAAI,qBAAqB;AACjC,SAAO;AACT;AAEA,IAAM,OAAO,YAAY;AAKzB,IAAM,uBAA6C,aAAa;AAAA,EAC9D,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe;AAAA,EACf,YAAY,CAAC,CAAC,QAAQ,IAAI;AAAA,EAC1B,SAAS,QAAQ,IAAI,KAAK,IAAI;AAAA,EAC9B,UAAU,QAAQ,IAAI,KAAK,WAAW;AAAA,EACtC,KAAK;AAAA,IACH,SAAS,oBAAoB,IAAI;AAAA,IACjC,OAAO;AAAA,
|
|
1
|
+
{"version":3,"sources":["../src/configs/playwright-a11y.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { defineConfig, devices, type PlaywrightTestConfig } from \"@playwright/test\";\n\nexport type A11yRoute = { path: string; name: string };\n\nexport const a11yRoutes: A11yRoute[] = [\n { path: \"/dev/a11y-fixtures\", name: \"a11y fixtures\" },\n { path: \"/dev/animate-in\", name: \"animate-in demo\" },\n];\n\n// Routes smoke-loaded for client-side (hydration) errors only — NOT axe-scanned.\n// Catches the class of bug where build + SSR succeed but client hydration throws\n// and blanks the page (data-dynamiq 2026-06-09: a Svelte 4->5 `run()` referenced\n// a `$state` declared after it → TDZ ReferenceError on hydrate). `/` is the one\n// route every site has; real routes carry a11y debt we don't gate on here, so we\n// assert only that they don't crash on hydrate.\nexport const smokeRoutes: A11yRoute[] = [{ path: \"/\", name: \"home\" }];\n\n// R1.1 (health-gate): the central `smoke` audit (src/audits/smoke.ts) allocates\n// a free port and passes it as REDDOOR_SMOKE_PORT so a zombie vite already\n// squatting the default 5173 can't silently hijack the run and green a stale\n// build. The per-site R1.1 config template honors it, but sites whose\n// playwright.config.ts merely re-exports this shared base (pre-R1.1 adopters\n// the smoke-suite recipe flags-but-never-rewrites) would otherwise ignore it —\n// so honor it here too and every re-exporter inherits the port binding on its\n// next package bump. Unset (local `pnpm test:smoke`) → the fixed 5173.\nconst smokePort = process.env.REDDOOR_SMOKE_PORT;\n\n/**\n * Allocate a free port SYNCHRONOUSLY, for the local path where nothing handed\n * us one. Same trick as src/util/free-port.ts (bind :0, read the assigned port,\n * release it) — but that is async, and this value is needed at module scope\n * while Playwright is still building the config object.\n *\n * It cannot be an async default export instead: sites consume this base by\n * SPREADING it (`{ ...base, use: { ...base.use } }` — see the smoke-suite\n * recipe template). Spreading a Promise yields none of its properties, so the\n * site would get a silently empty config — the exact false-green this whole\n * change exists to remove. The export must stay a plain object.\n *\n * A subprocess is the cost of that constraint: ~30-50ms, once per Playwright\n * run. On any failure we return null and the caller falls back to 5173, which\n * (with reuseExistingServer now false) degrades to a loud \"port already in use\"\n * rather than a silent wrong-server run.\n */\nfunction allocateFreePortSync(): string | null {\n try {\n const out = execFileSync(\n process.execPath,\n [\n \"-e\",\n 'const s=require(\"node:net\").createServer();s.on(\"error\",()=>process.exit(1));' +\n 's.listen(0,\"127.0.0.1\",()=>{const p=s.address().port;s.close(()=>process.stdout.write(String(p)))});',\n ],\n { encoding: \"utf8\", timeout: 5_000, stdio: [\"ignore\", \"pipe\", \"ignore\"] },\n ).trim();\n return /^\\d+$/.test(out) ? out : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the port this run binds to, ONCE per run rather than once per\n * evaluation of this file.\n *\n * REDDOOR_SMOKE_PORT (the central audit already allocated one) wins; otherwise\n * this run allocates its own. The old fallback was the fixed 5173 — the same\n * port a dev server sits on, which is what let `reuseExistingServer` silently\n * hijack local runs (#524).\n *\n * The allocation is pinned back into the environment, and that is the whole\n * point. Playwright re-evaluates the config file in EVERY worker process, not\n * just the main one. Allocating per evaluation gave each worker a different\n * port: the main process started the dev server on one, every worker aimed\n * `baseURL` at another, and the run died with ERR_CONNECTION_REFUSED against a\n * handful of ports nothing was ever serving. Workers are forked and inherit\n * this environment, so writing it back makes every later evaluation agree.\n *\n * It broke on the site suites and not on `audit --only a11y`, which writes its\n * own config and never reads this file — so nothing here caught it.\n */\nfunction resolvePort(): string {\n if (smokePort) return smokePort;\n const allocated = allocateFreePortSync() || \"5173\";\n process.env.REDDOOR_SMOKE_PORT = allocated;\n return allocated;\n}\n\nconst port = resolvePort();\n\n// NOTE: default export only — sites consume this as `import base from\n// \"@reddoorla/maintenance/configs/playwright-a11y\"` (or re-export the default).\n// The old `playwrightA11yConfig` named alias had zero importers and was removed.\nconst playwrightA11yConfig: PlaywrightTestConfig = defineConfig({\n testDir: \"tests\",\n testMatch: /.*\\.spec\\.ts$/,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: process.env.CI ? \"github\" : \"list\",\n use: {\n baseURL: `http://localhost:${port}`,\n trace: \"on-first-retry\",\n // Emulate reduced motion fleet-wide: scrollIntoView lands instantly rather\n // than animating, so Playwright's actionability checks don't flake under\n // parallel load, and view transitions fall back to instant. Pairs with the\n // prefers-reduced-motion gate on scroll-behavior in every site's app.css.\n //\n // It MUST sit under `contextOptions` — `reducedMotion` is a\n // BrowserContextOptions member, not a top-level test option. reddoor-starter\n // carried it at the top level of `use` from 2026-06 until 2026-09-01, where\n // Playwright silently ignored it (unknown keys are dropped at runtime) and\n // `pnpm check` never flagged it because svelte-check does not typecheck\n // playwright.config.ts. The emulation was inert that whole time.\n contextOptions: { reducedMotion: \"reduce\" as const },\n },\n projects: [\n {\n name: \"chromium\",\n use: { ...devices[\"Desktop Chrome\"] },\n },\n ],\n webServer: {\n // Portable across pnpm and npm sites — pnpm respects `npm run` too.\n //\n // `--port ... --strictPort` in BOTH cases. It used to be applied only when\n // REDDOOR_SMOKE_PORT allocated one, on the reasoning that we should \"fail\n // loudly rather than let vite drift to a free port the baseURL doesn't\n // point at\" — but that argument covers the unset case just as well. 5173 is\n // equally a fixed port that `baseURL` and the readiness probe below are\n // pinned to, and vite left to itself drifts off it whenever something else\n // holds it.\n //\n // The symptom that exposed this: a non-vite process on 5173 sends vite to\n // 5174 while the probe keeps polling 5173, so the run dies on\n // \"Timed out waiting 120000ms from config.webServer\" — 120 seconds of\n // nothing, naming neither the port nor the squatter. With --strictPort it\n // is an immediate \"Port 5173 is already in use\".\n //\n // --strictPort now only bites if the allocated port is taken in the window\n // between releasing and binding it, which is exactly the case worth failing\n // on.\n command: `npm run vite:dev -- --port ${port} --strictPort`,\n url: `http://localhost:${port}/dev/a11y-fixtures`,\n // NEVER reuse (#524). This used to be `!process.env.CI`, so local runs\n // reused whatever answered the probe URL. The probe only asks \"does this\n // respond?\" — never \"is this serving the code I am about to test?\" — so a\n // dev server left open, or one whose tree changed under it after a\n // checkout, silently became the system under test. That fails in both\n // directions: a false red blamed on the code (beachfront 2026-08-12, where\n // it was investigated as a macOS-vs-Linux difference and written up as one\n // before being caught), and a false green where a passing suite ran against\n // an old build. CI already had it false, and that asymmetry is precisely\n // what made the failure read as a platform bug.\n //\n // The cost is a fresh vite boot per run (~10-20s against a ~2min suite).\n // Because the port above is allocated rather than fixed, your own dev\n // server on 5173 keeps running untouched.\n reuseExistingServer: false,\n timeout: 120_000,\n },\n});\n\nexport default playwrightA11yConfig;\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,cAAc,eAA0C;AAI1D,IAAM,aAA0B;AAAA,EACrC,EAAE,MAAM,sBAAsB,MAAM,gBAAgB;AAAA,EACpD,EAAE,MAAM,mBAAmB,MAAM,kBAAkB;AACrD;AAQO,IAAM,cAA2B,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC;AAUpE,IAAM,YAAY,QAAQ,IAAI;AAmB9B,SAAS,uBAAsC;AAC7C,MAAI;AACF,UAAM,MAAM;AAAA,MACV,QAAQ;AAAA,MACR;AAAA,QACE;AAAA,QACA;AAAA,MAEF;AAAA,MACA,EAAE,UAAU,QAAQ,SAAS,KAAO,OAAO,CAAC,UAAU,QAAQ,QAAQ,EAAE;AAAA,IAC1E,EAAE,KAAK;AACP,WAAO,QAAQ,KAAK,GAAG,IAAI,MAAM;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAsBA,SAAS,cAAsB;AAC7B,MAAI,UAAW,QAAO;AACtB,QAAM,YAAY,qBAAqB,KAAK;AAC5C,UAAQ,IAAI,qBAAqB;AACjC,SAAO;AACT;AAEA,IAAM,OAAO,YAAY;AAKzB,IAAM,uBAA6C,aAAa;AAAA,EAC9D,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe;AAAA,EACf,YAAY,CAAC,CAAC,QAAQ,IAAI;AAAA,EAC1B,SAAS,QAAQ,IAAI,KAAK,IAAI;AAAA,EAC9B,UAAU,QAAQ,IAAI,KAAK,WAAW;AAAA,EACtC,KAAK;AAAA,IACH,SAAS,oBAAoB,IAAI;AAAA,IACjC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYP,gBAAgB,EAAE,eAAe,SAAkB;AAAA,EACrD;AAAA,EACA,UAAU;AAAA,IACR;AAAA,MACE,MAAM;AAAA,MACN,KAAK,EAAE,GAAG,QAAQ,gBAAgB,EAAE;AAAA,IACtC;AAAA,EACF;AAAA,EACA,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAoBT,SAAS,8BAA8B,IAAI;AAAA,IAC3C,KAAK,oBAAoB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAe7B,qBAAqB;AAAA,IACrB,SAAS;AAAA,EACX;AACF,CAAC;AAED,IAAO,0BAAQ;","names":[]}
|
package/dist/cli/bin.js
CHANGED
|
@@ -111,7 +111,7 @@ cli.command("sync-configs [site]", "Sync canonical configs into a site.").option
|
|
|
111
111
|
'Inventory file (.json or .mjs/.js), or "airtable" to read from Websites table'
|
|
112
112
|
).option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
|
|
113
113
|
async (site, opts) => runOrExit(
|
|
114
|
-
async () => (await import("../sync-configs-
|
|
114
|
+
async () => (await import("../sync-configs-PVRLZRKL.js")).runSyncConfigsCommand(site, opts),
|
|
115
115
|
opts
|
|
116
116
|
)
|
|
117
117
|
);
|
|
@@ -129,7 +129,7 @@ cli.command(
|
|
|
129
129
|
"Bootstrap a repo to keep itself updated (Renovate + protection; platform auto-merge off; ci.yml is the starter's, not managed here)."
|
|
130
130
|
).option("--dry", "List what would be enabled without writing or opening PRs").option("--fleet <inventory>", 'Inventory file (.json or .mjs/.js), or "airtable"').option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
|
|
131
131
|
async (site, opts) => runOrExit(
|
|
132
|
-
async () => (await import("../self-updating-
|
|
132
|
+
async () => (await import("../self-updating-VQIW24RZ.js")).runSelfUpdatingCommand(site, opts),
|
|
133
133
|
opts
|
|
134
134
|
)
|
|
135
135
|
);
|
|
@@ -138,7 +138,7 @@ cli.command(
|
|
|
138
138
|
"Add the Prismic model delivery workflow to a site via PR (one PR per repo; never pushes to main)."
|
|
139
139
|
).option("--dry", "List the sites that would be offered the workflow, without opening any PR").option("--fleet <inventory>", 'Inventory file (.json or .mjs/.js), or "airtable"').option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
|
|
140
140
|
async (site, opts) => runOrExit(
|
|
141
|
-
async () => (await import("../prismic-ci-
|
|
141
|
+
async () => (await import("../prismic-ci-6YGRIOHT.js")).runPrismicCiCommand(site, opts),
|
|
142
142
|
opts
|
|
143
143
|
)
|
|
144
144
|
);
|
|
@@ -153,7 +153,7 @@ cli.command(
|
|
|
153
153
|
"Write the report to this file (the CI workflow posts it as a PR comment)"
|
|
154
154
|
).action(
|
|
155
155
|
async (site, opts) => runOrExit(
|
|
156
|
-
async () => (await import("../prismic-models-
|
|
156
|
+
async () => (await import("../prismic-models-VNGIKS2B.js")).runPrismicModelsCommand(site, opts),
|
|
157
157
|
opts
|
|
158
158
|
)
|
|
159
159
|
);
|
|
@@ -230,14 +230,14 @@ cli.command(
|
|
|
230
230
|
"--fleet <inventory>",
|
|
231
231
|
'Inventory file (.json or .mjs/.js), or "airtable" to read from Websites table'
|
|
232
232
|
).option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
|
|
233
|
-
async (site, opts) => runOrExit(async () => (await import("../init-
|
|
233
|
+
async (site, opts) => runOrExit(async () => (await import("../init-NXZ4NXTL.js")).runInitCommand(site, opts), opts)
|
|
234
234
|
);
|
|
235
235
|
cli.command(
|
|
236
236
|
"launch <site>",
|
|
237
237
|
"Bootstrap + first-audit a site, then draft its launch email for approval."
|
|
238
238
|
).action(
|
|
239
239
|
async (site, opts) => runOrExit(
|
|
240
|
-
async () => (await import("../launch-
|
|
240
|
+
async () => (await import("../launch-5V7RLUV7.js")).runLaunchCommand(site, opts),
|
|
241
241
|
opts
|
|
242
242
|
)
|
|
243
243
|
);
|
|
@@ -252,7 +252,7 @@ cli.command(
|
|
|
252
252
|
"Draft the monthly-report announcement email for maintenance sites (all, or one) for approval."
|
|
253
253
|
).action(
|
|
254
254
|
async (site, opts) => runOrExit(
|
|
255
|
-
async () => (await import("../announce-
|
|
255
|
+
async () => (await import("../announce-NDTLRQCN.js")).runAnnounceCommand(site, opts),
|
|
256
256
|
opts
|
|
257
257
|
)
|
|
258
258
|
);
|
|
@@ -261,7 +261,7 @@ cli.command(
|
|
|
261
261
|
"Show who a form submission would email, and optionally flip the pre-launch guard (read-back confirmed)."
|
|
262
262
|
).option("--set <on|off>", "Flip the guard: on = route to operator, off = restore.").option("--restore <status>", "Status to restore with --set off. Required; never inferred.").action(
|
|
263
263
|
async (site, opts) => runOrExit(
|
|
264
|
-
async () => (await import("../forms-notify-target-
|
|
264
|
+
async () => (await import("../forms-notify-target-IHJX7NWA.js")).runFormsNotifyTargetCommand(
|
|
265
265
|
site,
|
|
266
266
|
opts
|
|
267
267
|
),
|
|
@@ -291,7 +291,7 @@ cli.command(
|
|
|
291
291
|
);
|
|
292
292
|
cli.command("ensure-site <slug>", "Create/verify the Airtable Websites row for a new site.").option("--name <display name>", "Human Name for the row (client-facing copy uses it verbatim).").option("--url <url>", "Deployed URL (e.g. the Netlify site URL).").option("--contact <email>", "point of contact \u2014 the client address reports resolve to.").option("--git-repo <owner/repo>", "GitHub identity. Default on create: reddoorla/<slug>.").action(
|
|
293
293
|
async (slug, opts) => runOrExit(
|
|
294
|
-
async () => (await import("../ensure-site-
|
|
294
|
+
async () => (await import("../ensure-site-FFDUMY6E.js")).runEnsureSiteCommand(slug, opts),
|
|
295
295
|
opts
|
|
296
296
|
)
|
|
297
297
|
);
|
|
@@ -309,7 +309,7 @@ cli.command(
|
|
|
309
309
|
"Email the internal sheet (scores, what wasn't measured, top fixes, the link) to PROSPECT_AUDIT_RECIPIENTS."
|
|
310
310
|
).action(
|
|
311
311
|
async (url, opts) => runOrExit(
|
|
312
|
-
async () => (await import("../prospect-audit-
|
|
312
|
+
async () => (await import("../prospect-audit-JCWTKJTS.js")).runProspectAuditCommand(url, opts),
|
|
313
313
|
opts
|
|
314
314
|
)
|
|
315
315
|
);
|
|
@@ -330,7 +330,7 @@ cli.command("report [site]", "Draft or send maintenance/testing reports.").optio
|
|
|
330
330
|
"Regenerate one UNSENT report's stored HTML from its current row, so the console preview shows edited commentary. Refuses a sent report."
|
|
331
331
|
).action(
|
|
332
332
|
async (site, opts) => runOrExit(
|
|
333
|
-
async () => (await import("../report-
|
|
333
|
+
async () => (await import("../report-AAY2HZZR.js")).runReportCommand(site, opts),
|
|
334
334
|
opts
|
|
335
335
|
)
|
|
336
336
|
);
|
|
@@ -339,7 +339,7 @@ cli.command(
|
|
|
339
339
|
"Sweep the fleet for GitHub signals (Renovate-failing/CI/last-commit) and write Airtable."
|
|
340
340
|
).option("--fleet", "Run across every site in the Airtable inventory.").option("--write-airtable", "Write each site's signals back to its Websites row.").action(
|
|
341
341
|
async (opts) => runOrExit(
|
|
342
|
-
async () => (await import("../github-signals-
|
|
342
|
+
async () => (await import("../github-signals-FCLNHKMF.js")).runGitHubSignalsCommand({
|
|
343
343
|
fleet: opts.fleet,
|
|
344
344
|
writeAirtable: opts.writeAirtable
|
|
345
345
|
}),
|
|
@@ -370,7 +370,7 @@ cli.command(
|
|
|
370
370
|
"--force",
|
|
371
371
|
"import-airtable / sync: run despite the freeze (#643) \u2014 a deliberate rollback-window converge from the frozen Airtable shadow. Without it both refuse while Turso is authoritative, because an import overwrites authoritative rows."
|
|
372
372
|
).action(
|
|
373
|
-
async (action, opts) => runOrExit(async () => (await import("../db-
|
|
373
|
+
async (action, opts) => runOrExit(async () => (await import("../db-LOSJ74ZJ.js")).runDbCommand(action, opts), opts)
|
|
374
374
|
);
|
|
375
375
|
cli.command(
|
|
376
376
|
"submissions <action>",
|
|
@@ -380,7 +380,7 @@ cli.command(
|
|
|
380
380
|
"Write the re-buckets: status \u2192 spam_auto, spam_score/spam_reason replaced with the new verdict + 'retro-rescore' marker."
|
|
381
381
|
).action(
|
|
382
382
|
async (action, opts) => runOrExit(
|
|
383
|
-
async () => (await import("../submissions-
|
|
383
|
+
async () => (await import("../submissions-7LGJJSDL.js")).runSubmissionsCommand(action, opts),
|
|
384
384
|
opts
|
|
385
385
|
)
|
|
386
386
|
);
|
|
@@ -392,7 +392,7 @@ cli.command(
|
|
|
392
392
|
async () => (await import("../renovate-dispatch-6GQWTRKX.js")).runRenovateDispatchCommand({
|
|
393
393
|
fleet: opts.fleet,
|
|
394
394
|
// #539 Phase 5: mirror the auto-fix counter into site_health.
|
|
395
|
-
siteMirror: await (await import("../site-mirror-
|
|
395
|
+
siteMirror: await (await import("../site-mirror-GCFLBBWK.js")).makeSiteMirror()
|
|
396
396
|
}),
|
|
397
397
|
opts
|
|
398
398
|
)
|
|
@@ -417,7 +417,7 @@ cli.command(
|
|
|
417
417
|
"freeze: JSON declaring slots the template has no token for (video poster, a panel rebuilt from a baked image). Keys must start with 'x.'"
|
|
418
418
|
).action(
|
|
419
419
|
async (action, dir, opts) => runOrExit(
|
|
420
|
-
async () => (await import("../blux-
|
|
420
|
+
async () => (await import("../blux-YGBGS24U.js")).runBluxCommand(action, dir, opts),
|
|
421
421
|
opts
|
|
422
422
|
)
|
|
423
423
|
);
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
ALL_AUDIT_NAMES,
|
|
14
14
|
runOneAudit
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-QD427NIO.js";
|
|
16
16
|
import "../../chunk-LKGVSM2O.js";
|
|
17
17
|
import "../../chunk-GASBX52O.js";
|
|
18
18
|
import "../../chunk-XHHMM5HX.js";
|
|
@@ -20,22 +20,22 @@ import "../../chunk-Z3JXLULD.js";
|
|
|
20
20
|
import {
|
|
21
21
|
recordFleetEventsBestEffort
|
|
22
22
|
} from "../../chunk-W45FZEGE.js";
|
|
23
|
+
import "../../chunk-WS6NU475.js";
|
|
24
|
+
import "../../chunk-UDRHAQT7.js";
|
|
23
25
|
import {
|
|
24
26
|
isHttpUrl
|
|
25
27
|
} from "../../chunk-RARREDQE.js";
|
|
26
|
-
import "../../chunk-WS6NU475.js";
|
|
27
28
|
import "../../chunk-MFHS7IQ2.js";
|
|
28
29
|
import "../../chunk-A3WDWNN5.js";
|
|
29
30
|
import "../../chunk-2OP4JKIQ.js";
|
|
30
31
|
import "../../chunk-XTK5VIQB.js";
|
|
31
32
|
import "../../chunk-2NQUCS7O.js";
|
|
32
|
-
import "../../chunk-UDRHAQT7.js";
|
|
33
33
|
import "../../chunk-PTDMVGKL.js";
|
|
34
34
|
import "../../chunk-LBYOLBW7.js";
|
|
35
35
|
import "../../chunk-XXTZBPUY.js";
|
|
36
36
|
import "../../chunk-6VAL7XJT.js";
|
|
37
37
|
import "../../chunk-NMAWXBJA.js";
|
|
38
|
-
import "../../chunk-
|
|
38
|
+
import "../../chunk-YLYXW5PH.js";
|
|
39
39
|
|
|
40
40
|
// src/cli/commands/audit.ts
|
|
41
41
|
import { resolve } from "path";
|
|
@@ -267,7 +267,7 @@ ${wb.summary}`;
|
|
|
267
267
|
task.output = "writing scores\u2026";
|
|
268
268
|
writeSummary = await writeAuditsToAirtable({ base, websites, slug, results });
|
|
269
269
|
if (writeSummary.siteId && writeSummary.fields) {
|
|
270
|
-
const { makeSiteMirror } = await import("../../site-mirror-
|
|
270
|
+
const { makeSiteMirror } = await import("../../site-mirror-GCFLBBWK.js");
|
|
271
271
|
await (await makeSiteMirror()).health(writeSummary.siteId, writeSummary.fields);
|
|
272
272
|
}
|
|
273
273
|
task.title = `Wrote to Websites[${writeSummary.siteName}] (${writeSummary.writes.length} audit type${writeSummary.writes.length === 1 ? "" : "s"})`;
|
|
@@ -304,7 +304,7 @@ async function runFleetWriteBack(args) {
|
|
|
304
304
|
const { listWebsites } = await import("../../websites-D6WYFAES.js");
|
|
305
305
|
const websites = await listWebsites(base);
|
|
306
306
|
const makeMirror = deps.makeMirror ?? (async () => {
|
|
307
|
-
const { makeHealthMirrorBestEffort } = await import("../../health-mirror-
|
|
307
|
+
const { makeHealthMirrorBestEffort } = await import("../../health-mirror-GMSF3Z5R.js");
|
|
308
308
|
return makeHealthMirrorBestEffort();
|
|
309
309
|
});
|
|
310
310
|
const mirror = await makeMirror();
|
package/dist/configs/eslint.js
CHANGED
|
@@ -64,7 +64,11 @@ function createEslintConfig(opts) {
|
|
|
64
64
|
"node_modules/",
|
|
65
65
|
"static/",
|
|
66
66
|
"customtypes/",
|
|
67
|
-
"src/lib/slices/**/index.js"
|
|
67
|
+
"src/lib/slices/**/index.js",
|
|
68
|
+
// Agency process artifacts: git-ignored in every repo, but present on
|
|
69
|
+
// disk locally, so `pnpm lint` would otherwise try to parse them.
|
|
70
|
+
"docs/superpowers/",
|
|
71
|
+
"scratchpad/"
|
|
68
72
|
]
|
|
69
73
|
}
|
|
70
74
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/configs/eslint.ts"],"sourcesContent":["import js from \"@eslint/js\";\nimport ts from \"typescript-eslint\";\nimport svelte from \"eslint-plugin-svelte\";\nimport prettier from \"eslint-config-prettier\";\nimport globals from \"globals\";\nimport type { Linter } from \"eslint\";\n\nexport type CreateEslintConfigOptions = {\n svelteConfig: unknown;\n};\n\nexport function createEslintConfig(opts: CreateEslintConfigOptions): Linter.Config[] {\n return [\n js.configs.recommended,\n ...ts.configs.recommended,\n ...svelte.configs.recommended,\n prettier,\n ...svelte.configs.prettier,\n {\n languageOptions: {\n globals: {\n ...globals.browser,\n ...globals.node,\n },\n },\n rules: {\n \"@typescript-eslint/no-unused-vars\": [\n \"error\",\n {\n argsIgnorePattern: \"^_\",\n varsIgnorePattern: \"^_\",\n caughtErrorsIgnorePattern: \"^_\",\n },\n ],\n \"svelte/no-navigation-without-resolve\": \"off\",\n },\n },\n {\n files: [\"**/*.svelte\", \"**/*.svelte.js\", \"**/*.svelte.ts\"],\n languageOptions: {\n parserOptions: {\n parser: ts.parser,\n svelteConfig: opts.svelteConfig,\n },\n },\n },\n {\n // eslint-plugin-svelte 3.20+ allows only an `error` prop in +error.svelte,\n // but SvelteKit really does pass merged layout `data` to error pages\n // (typed by hand since kit generates no ./$types for +error). The rule\n // takes no options (schema: []), so scope it off for error pages only.\n files: [\"**/+error.svelte\"],\n rules: {\n \"svelte/valid-prop-names-in-kit-pages\": \"off\",\n },\n },\n {\n files: [\"**/*.d.ts\"],\n rules: {\n \"no-var\": \"off\",\n \"@typescript-eslint/no-unused-vars\": \"off\",\n },\n },\n {\n ignores: [\n \"build/\",\n \".svelte-kit/\",\n \".netlify/\",\n \"node_modules/\",\n \"static/\",\n \"customtypes/\",\n \"src/lib/slices/**/index.js\",\n ],\n },\n ] as Linter.Config[];\n}\n\nexport default createEslintConfig;\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,YAAY;AACnB,OAAO,cAAc;AACrB,OAAO,aAAa;AAOb,SAAS,mBAAmB,MAAkD;AACnF,SAAO;AAAA,IACL,GAAG,QAAQ;AAAA,IACX,GAAG,GAAG,QAAQ;AAAA,IACd,GAAG,OAAO,QAAQ;AAAA,IAClB;AAAA,IACA,GAAG,OAAO,QAAQ;AAAA,IAClB;AAAA,MACE,iBAAiB;AAAA,QACf,SAAS;AAAA,UACP,GAAG,QAAQ;AAAA,UACX,GAAG,QAAQ;AAAA,QACb;AAAA,MACF;AAAA,MACA,OAAO;AAAA,QACL,qCAAqC;AAAA,UACnC;AAAA,UACA;AAAA,YACE,mBAAmB;AAAA,YACnB,mBAAmB;AAAA,YACnB,2BAA2B;AAAA,UAC7B;AAAA,QACF;AAAA,QACA,wCAAwC;AAAA,MAC1C;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,eAAe,kBAAkB,gBAAgB;AAAA,MACzD,iBAAiB;AAAA,QACf,eAAe;AAAA,UACb,QAAQ,GAAG;AAAA,UACX,cAAc,KAAK;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKE,OAAO,CAAC,kBAAkB;AAAA,MAC1B,OAAO;AAAA,QACL,wCAAwC;AAAA,MAC1C;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,WAAW;AAAA,MACnB,OAAO;AAAA,QACL,UAAU;AAAA,QACV,qCAAqC;AAAA,MACvC;AAAA,IACF;AAAA,IACA;AAAA,MACE,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/configs/eslint.ts"],"sourcesContent":["import js from \"@eslint/js\";\nimport ts from \"typescript-eslint\";\nimport svelte from \"eslint-plugin-svelte\";\nimport prettier from \"eslint-config-prettier\";\nimport globals from \"globals\";\nimport type { Linter } from \"eslint\";\n\nexport type CreateEslintConfigOptions = {\n svelteConfig: unknown;\n};\n\nexport function createEslintConfig(opts: CreateEslintConfigOptions): Linter.Config[] {\n return [\n js.configs.recommended,\n ...ts.configs.recommended,\n ...svelte.configs.recommended,\n prettier,\n ...svelte.configs.prettier,\n {\n languageOptions: {\n globals: {\n ...globals.browser,\n ...globals.node,\n },\n },\n rules: {\n \"@typescript-eslint/no-unused-vars\": [\n \"error\",\n {\n argsIgnorePattern: \"^_\",\n varsIgnorePattern: \"^_\",\n caughtErrorsIgnorePattern: \"^_\",\n },\n ],\n \"svelte/no-navigation-without-resolve\": \"off\",\n },\n },\n {\n files: [\"**/*.svelte\", \"**/*.svelte.js\", \"**/*.svelte.ts\"],\n languageOptions: {\n parserOptions: {\n parser: ts.parser,\n svelteConfig: opts.svelteConfig,\n },\n },\n },\n {\n // eslint-plugin-svelte 3.20+ allows only an `error` prop in +error.svelte,\n // but SvelteKit really does pass merged layout `data` to error pages\n // (typed by hand since kit generates no ./$types for +error). The rule\n // takes no options (schema: []), so scope it off for error pages only.\n files: [\"**/+error.svelte\"],\n rules: {\n \"svelte/valid-prop-names-in-kit-pages\": \"off\",\n },\n },\n {\n files: [\"**/*.d.ts\"],\n rules: {\n \"no-var\": \"off\",\n \"@typescript-eslint/no-unused-vars\": \"off\",\n },\n },\n {\n ignores: [\n \"build/\",\n \".svelte-kit/\",\n \".netlify/\",\n \"node_modules/\",\n \"static/\",\n \"customtypes/\",\n \"src/lib/slices/**/index.js\",\n // Agency process artifacts: git-ignored in every repo, but present on\n // disk locally, so `pnpm lint` would otherwise try to parse them.\n \"docs/superpowers/\",\n \"scratchpad/\",\n ],\n },\n ] as Linter.Config[];\n}\n\nexport default createEslintConfig;\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,YAAY;AACnB,OAAO,cAAc;AACrB,OAAO,aAAa;AAOb,SAAS,mBAAmB,MAAkD;AACnF,SAAO;AAAA,IACL,GAAG,QAAQ;AAAA,IACX,GAAG,GAAG,QAAQ;AAAA,IACd,GAAG,OAAO,QAAQ;AAAA,IAClB;AAAA,IACA,GAAG,OAAO,QAAQ;AAAA,IAClB;AAAA,MACE,iBAAiB;AAAA,QACf,SAAS;AAAA,UACP,GAAG,QAAQ;AAAA,UACX,GAAG,QAAQ;AAAA,QACb;AAAA,MACF;AAAA,MACA,OAAO;AAAA,QACL,qCAAqC;AAAA,UACnC;AAAA,UACA;AAAA,YACE,mBAAmB;AAAA,YACnB,mBAAmB;AAAA,YACnB,2BAA2B;AAAA,UAC7B;AAAA,QACF;AAAA,QACA,wCAAwC;AAAA,MAC1C;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,eAAe,kBAAkB,gBAAgB;AAAA,MACzD,iBAAiB;AAAA,QACf,eAAe;AAAA,UACb,QAAQ,GAAG;AAAA,UACX,cAAc,KAAK;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKE,OAAO,CAAC,kBAAkB;AAAA,MAC1B,OAAO;AAAA,QACL,wCAAwC;AAAA,MAC1C;AAAA,IACF;AAAA,IACA;AAAA,MACE,OAAO,CAAC,WAAW;AAAA,MACnB,OAAO;AAAA,QACL,UAAU;AAAA,QACV,qCAAqC;AAAA,MACvC;AAAA,IACF;AAAA,IACA;AAAA,MACE,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;","names":[]}
|
|
@@ -23,11 +23,11 @@ import {
|
|
|
23
23
|
isPendingApproval,
|
|
24
24
|
listAllReports
|
|
25
25
|
} from "./chunk-F3JWOPTG.js";
|
|
26
|
+
import "./chunk-VAD43MOR.js";
|
|
26
27
|
import {
|
|
27
28
|
diffAttention,
|
|
28
29
|
writeDigestState
|
|
29
30
|
} from "./chunk-BRYXFH77.js";
|
|
30
|
-
import "./chunk-VAD43MOR.js";
|
|
31
31
|
import "./chunk-IY6256GK.js";
|
|
32
32
|
import {
|
|
33
33
|
openBase,
|
|
@@ -48,8 +48,8 @@ import {
|
|
|
48
48
|
defaultResendClient
|
|
49
49
|
} from "./chunk-WEKL2HYJ.js";
|
|
50
50
|
import "./chunk-W45FZEGE.js";
|
|
51
|
-
import "./chunk-RARREDQE.js";
|
|
52
51
|
import "./chunk-WS6NU475.js";
|
|
52
|
+
import "./chunk-RARREDQE.js";
|
|
53
53
|
import "./chunk-MFHS7IQ2.js";
|
|
54
54
|
import "./chunk-6VAL7XJT.js";
|
|
55
55
|
|
|
@@ -424,4 +424,4 @@ export {
|
|
|
424
424
|
renderDigestHtml,
|
|
425
425
|
runDigest
|
|
426
426
|
};
|
|
427
|
-
//# sourceMappingURL=digest-
|
|
427
|
+
//# sourceMappingURL=digest-CS6KLCZB.js.map
|
|
@@ -28,8 +28,8 @@ import "./chunk-BGTYPVLT.js";
|
|
|
28
28
|
import "./chunk-7NYN2P6I.js";
|
|
29
29
|
import "./chunk-WEKL2HYJ.js";
|
|
30
30
|
import "./chunk-W45FZEGE.js";
|
|
31
|
-
import "./chunk-RARREDQE.js";
|
|
32
31
|
import "./chunk-WS6NU475.js";
|
|
32
|
+
import "./chunk-RARREDQE.js";
|
|
33
33
|
import "./chunk-MFHS7IQ2.js";
|
|
34
34
|
import "./chunk-6VAL7XJT.js";
|
|
35
35
|
export {
|
|
@@ -66,7 +66,7 @@ async function runEnsureSiteCommand(slug, opts) {
|
|
|
66
66
|
if (!slug) return { output: "Provide a <slug> (e.g. `ensure-site roalson`).", code: 2 };
|
|
67
67
|
try {
|
|
68
68
|
const base = openBase(readAirtableConfig());
|
|
69
|
-
const { makeSiteMirror } = await import("./site-mirror-
|
|
69
|
+
const { makeSiteMirror } = await import("./site-mirror-GCFLBBWK.js");
|
|
70
70
|
const result = await ensureSite(
|
|
71
71
|
base,
|
|
72
72
|
{
|
|
@@ -93,4 +93,4 @@ async function runEnsureSiteCommand(slug, opts) {
|
|
|
93
93
|
export {
|
|
94
94
|
runEnsureSiteCommand
|
|
95
95
|
};
|
|
96
|
-
//# sourceMappingURL=ensure-site-
|
|
96
|
+
//# sourceMappingURL=ensure-site-FFDUMY6E.js.map
|
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
describeNotifyTarget
|
|
3
3
|
} from "./chunk-SJOXWJ5Y.js";
|
|
4
4
|
import "./chunk-JRHIFA5R.js";
|
|
5
|
-
import "./chunk-NO74DXDP.js";
|
|
6
5
|
import "./chunk-5ML7FMBD.js";
|
|
6
|
+
import "./chunk-NO74DXDP.js";
|
|
7
7
|
import {
|
|
8
8
|
openBase,
|
|
9
9
|
readAirtableConfig
|
|
@@ -143,7 +143,7 @@ async function runFormsNotifyTargetCommand(site, opts) {
|
|
|
143
143
|
return { output: `--set must be 'on' or 'off' (got '${opts.set}')`, code: 2 };
|
|
144
144
|
}
|
|
145
145
|
try {
|
|
146
|
-
const { makeSiteMirror } = await import("./site-mirror-
|
|
146
|
+
const { makeSiteMirror } = await import("./site-mirror-GCFLBBWK.js");
|
|
147
147
|
const result = await formsNotifyTarget({
|
|
148
148
|
siteMirror: await makeSiteMirror(),
|
|
149
149
|
site: site.trim(),
|
|
@@ -163,4 +163,4 @@ export {
|
|
|
163
163
|
formatNotifyTarget,
|
|
164
164
|
runFormsNotifyTargetCommand
|
|
165
165
|
};
|
|
166
|
-
//# sourceMappingURL=forms-notify-target-
|
|
166
|
+
//# sourceMappingURL=forms-notify-target-IHJX7NWA.js.map
|