@domandigital/craft 0.21.0 → 0.22.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/CHANGELOG.md +12 -0
- package/CHARACTER.md +134 -3
- package/COPY.md +10 -3
- package/PRINCIPLES.md +9 -0
- package/README.md +20 -1
- package/design-review.schema.json +156 -0
- package/dist/{audit-LQ2TDISV.js → audit-CDMZ6JHT.js} +3 -3
- package/dist/audit.cjs +258 -10
- package/dist/audit.js +258 -10
- package/dist/{chunk-6DRBFDWT.js → chunk-D6TAYXOS.js} +1 -1
- package/dist/{chunk-G47ZCBD5.js → chunk-IAU4ZAIU.js} +2 -2
- package/dist/{chunk-STWH5EYU.js → chunk-ICEG4S56.js} +5 -5
- package/dist/{chunk-BYXDX66D.js → chunk-JT2NLOQI.js} +4 -4
- package/dist/{chunk-FNB3NP57.js → chunk-KH56TEFK.js} +5 -5
- package/dist/{chunk-DE2ZAV4G.js → chunk-MLYHSPF7.js} +166 -3
- package/dist/{chunk-ONA73THJ.js → chunk-NV6FKMID.js} +93 -8
- package/dist/{chunk-4WAIOZ4W.js → chunk-WTR77PEM.js} +14 -14
- package/dist/{chunk-BQ7H23QP.js → chunk-XSKLVEI6.js} +109 -2
- package/dist/{cli-YSGPGQ7R.js → cli-223CUNU3.js} +9 -9
- package/dist/{cli-XAWJWK7I.js → cli-3QV4FKM4.js} +6 -6
- package/dist/{cli-PBCU7Z64.js → cli-4PXDNTZX.js} +4 -4
- package/dist/{cli-7QL26YPR.js → cli-6SMNHOCJ.js} +1 -1
- package/dist/{cli-5VHX5EJF.js → cli-7PPDBN6N.js} +5 -5
- package/dist/{cli-5QI6NIZN.js → cli-CQUORKMC.js} +6 -6
- package/dist/{cli-EYHRJJAX.js → cli-FKWJF4V4.js} +8 -5
- package/dist/{cli-RHDNHL4U.js → cli-IQ5U76AZ.js} +5 -5
- package/dist/cli.js +3 -3
- package/dist/index.cjs +261 -10
- package/dist/index.d.cts +49 -3
- package/dist/index.d.ts +49 -3
- package/dist/index.js +260 -10
- package/dist/phone.cjs +57 -7
- package/dist/phone.d.cts +28 -2
- package/dist/phone.d.ts +28 -2
- package/dist/phone.js +54 -7
- package/dist/{viewports-cli-XYYX4SKV.js → viewports-cli-H6UIUMBC.js} +43 -82
- package/package.json +1 -1
- package/word-lists.json +11 -2
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
parseFile,
|
|
10
10
|
sentencesOf,
|
|
11
11
|
specificity
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NV6FKMID.js";
|
|
13
13
|
import {
|
|
14
14
|
SNAPSHOT_VERSION
|
|
15
15
|
} from "./chunk-QYBAVU3P.js";
|
|
@@ -770,6 +770,69 @@ function eyebrowChipHits(ctx) {
|
|
|
770
770
|
}
|
|
771
771
|
return hits;
|
|
772
772
|
}
|
|
773
|
+
var HERO_OPEN = /<(section|header|div|main|article|Section|Container)\b[^>]*?\b(?:class|className|id)\s*=\s*(?:"|'|\{\s*[`"']?)[^"'`>]*?(?<![a-z])hero(?![a-z])[^>]*>/gi;
|
|
774
|
+
function closeOf(text, tag, from) {
|
|
775
|
+
const re = new RegExp(`<(/?)${tag}\\b[^>]*?(/?)>`, "g");
|
|
776
|
+
re.lastIndex = from;
|
|
777
|
+
let depth = 0;
|
|
778
|
+
for (let m = re.exec(text); m; m = re.exec(text)) {
|
|
779
|
+
if (m[2] === "/") continue;
|
|
780
|
+
depth += m[1] === "/" ? -1 : 1;
|
|
781
|
+
if (depth === 0) return m.index + m[0].length;
|
|
782
|
+
if (m.index - from > 8e3) break;
|
|
783
|
+
}
|
|
784
|
+
return Math.min(text.length, from + 4e3);
|
|
785
|
+
}
|
|
786
|
+
function heroSpans(path, text) {
|
|
787
|
+
if (/(?:^|[\\/])[\w-]*hero[\w-]*\.(?:astro|tsx|jsx|vue|svelte)$/i.test(path)) return [[0, text.length]];
|
|
788
|
+
const spans = [];
|
|
789
|
+
for (const m of text.matchAll(HERO_OPEN)) {
|
|
790
|
+
const at = m.index ?? 0;
|
|
791
|
+
if (spans.some(([a, b]) => at >= a && at < b)) continue;
|
|
792
|
+
spans.push([at, closeOf(text, m[1], at)]);
|
|
793
|
+
}
|
|
794
|
+
if (spans.length > 0) return spans;
|
|
795
|
+
const h1 = text.search(/<h1\b/);
|
|
796
|
+
if (h1 === -1) return [];
|
|
797
|
+
const rest = text.slice(h1);
|
|
798
|
+
const end = rest.search(/<\/(?:section|header)>|<section\b/);
|
|
799
|
+
return [[h1, h1 + Math.min(end === -1 ? rest.length : end, 1500)]];
|
|
800
|
+
}
|
|
801
|
+
var CONTROL = /<(a|button|Button|ButtonLink|LinkButton|Link|Cta|CTA|CallToAction)\b([^>]*)>([\s\S]{0,300}?)<\/\1>/g;
|
|
802
|
+
var BUTTON_COMPONENT = /^(?:Button|ButtonLink|LinkButton|Cta|CTA|CallToAction)$/;
|
|
803
|
+
var BUTTON_CLASS = /(?<![a-z])(?:btn|button|cta)(?![a-z])/i;
|
|
804
|
+
var TOGGLE = /\baria-(?:expanded|controls|haspopup)\b|\btype\s*=\s*["']submit["']/;
|
|
805
|
+
var NOT_A_LANDING = /(?:^|[\\/])(?:error|global-error|not-found|404|500)\.\w+$|[\\/](?:admin|portal|dashboard|account|\(qa\)|\(app\)|qa)[\\/]/i;
|
|
806
|
+
function buttonLabel(attrs, inner) {
|
|
807
|
+
const text = inner.replace(/<[^>]*>/g, " ").replace(/\{[^}]*\}/g, " ").replace(/\s+/g, " ").trim();
|
|
808
|
+
if (text && !/[{}()=]/.test(text)) return text.slice(0, 40);
|
|
809
|
+
return attrs.match(/\bhref\s*=\s*["'{`$]*\{?([^"'}`\s]+)/)?.[1] ?? "a button";
|
|
810
|
+
}
|
|
811
|
+
function heroCtaPairHits(ctx) {
|
|
812
|
+
const hits = [];
|
|
813
|
+
for (const file of ctx.files) {
|
|
814
|
+
if (file.kind !== "markup" || NOT_A_LANDING.test(file.path)) continue;
|
|
815
|
+
const text = file.text.replace(/\\"/g, ' "');
|
|
816
|
+
for (const [start, end] of heroSpans(file.path, text)) {
|
|
817
|
+
const span = text.slice(start, end).replace(/<(nav|form)\b[\s\S]*?<\/\1>/gi, (s) => " ".repeat(s.length));
|
|
818
|
+
const buttons = [];
|
|
819
|
+
for (const m of span.matchAll(CONTROL)) {
|
|
820
|
+
const [, tag, attrs, inner] = m;
|
|
821
|
+
if (TOGGLE.test(attrs)) continue;
|
|
822
|
+
const cls = attrs.match(/\b(?:class|className|variant)\s*=\s*(?:"([^"]*)"|'([^']*)'|\{([^}]*)\})/g)?.join(" ") ?? "";
|
|
823
|
+
const tw = /\bpx-\d/.test(cls) && /\bpy-\d/.test(cls) && /\b(?:bg-|border\b|border-|rounded)/.test(cls);
|
|
824
|
+
if (BUTTON_COMPONENT.test(tag) || BUTTON_CLASS.test(cls) || tw || /\brole\s*=\s*["']button["']/.test(attrs)) {
|
|
825
|
+
buttons.push({ label: buttonLabel(attrs, inner), offset: start + (m.index ?? 0) });
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (buttons.length >= 2) {
|
|
829
|
+
const names = buttons.slice(0, 3).map((b) => `"${b.label}"`).join(", ");
|
|
830
|
+
hits.push({ path: file.path, offset: buttons[1].offset, message: `${buttons.length} buttons side by side in the hero: ${names}` });
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return hits;
|
|
835
|
+
}
|
|
773
836
|
function iconTiles(file) {
|
|
774
837
|
return file.classUnits.filter((unit) => {
|
|
775
838
|
const sized = has(unit, /^size-(?:8|9|10|11|12|14)$/) || (find(unit, /^h-(?:8|9|10|11|12|14)$/) ?? "x").slice(2) === (find(unit, /^w-(?:8|9|10|11|12|14)$/) ?? "y").slice(2);
|
|
@@ -1220,6 +1283,75 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1220
1283
|
<span className="inline-flex rounded-full border px-3 py-1 text-xs">Tag</span>`)]
|
|
1221
1284
|
}
|
|
1222
1285
|
},
|
|
1286
|
+
{
|
|
1287
|
+
id: "hero-cta-pair",
|
|
1288
|
+
name: "Two buttons in the hero",
|
|
1289
|
+
generation: 1,
|
|
1290
|
+
severity: "warn",
|
|
1291
|
+
surface: "source",
|
|
1292
|
+
why: "A filled button beside an outline or ghost one ('Get a free quote' / 'See our plans') is in 140 of 140 heroes Claude builds for a brief. Two equal asks make the visitor choose before they know anything, and say nobody decided what the page is for.",
|
|
1293
|
+
fix: "Keep one button, the ask the page exists for. Put the second route in the copy as a text link ('or see the work first'), or lower down where it answers a question.",
|
|
1294
|
+
detect: heroCtaPairHits,
|
|
1295
|
+
fixtures: {
|
|
1296
|
+
flag: [
|
|
1297
|
+
// Eunice Ordor's #5 and #36 (Pastel 986559): the DD site's own hero and process close.
|
|
1298
|
+
f(
|
|
1299
|
+
"src/components/Hero.astro",
|
|
1300
|
+
`<section class="hero">
|
|
1301
|
+
<h1>We run the systems behind your website</h1>
|
|
1302
|
+
<a href="/contact" class="btn btn-primary">Get in touch</a>
|
|
1303
|
+
<a href="/work" class="btn btn-ghost">See our work</a>
|
|
1304
|
+
</section>`
|
|
1305
|
+
),
|
|
1306
|
+
// The null build's hero, verbatim in shape (calibration/null/rmp/pages/05.html).
|
|
1307
|
+
f(
|
|
1308
|
+
"index.html",
|
|
1309
|
+
`<nav><a class="btn" href="#contact">Quote</a></nav>
|
|
1310
|
+
<section id="hero" class="hero">
|
|
1311
|
+
<h1>Electricians in Uxbridge</h1>
|
|
1312
|
+
<a href="#contact" class="btn btn-primary">Book a Free Quote</a>
|
|
1313
|
+
<a href="tel:+441895555210" class="btn btn-outline">Call 01895 555 210</a>
|
|
1314
|
+
</section>`
|
|
1315
|
+
),
|
|
1316
|
+
f(
|
|
1317
|
+
"app/page.tsx",
|
|
1318
|
+
`<section className="relative py-24">
|
|
1319
|
+
<h1 className="text-5xl">Nails in Brackley</h1>
|
|
1320
|
+
<div className="flex gap-4">
|
|
1321
|
+
<Button asChild><a href="/book">Book now</a></Button>
|
|
1322
|
+
<Button variant="outline">View treatments</Button>
|
|
1323
|
+
</div>
|
|
1324
|
+
</section>`
|
|
1325
|
+
)
|
|
1326
|
+
],
|
|
1327
|
+
pass: [
|
|
1328
|
+
// One button, and the second route as a text link in the sentence.
|
|
1329
|
+
f(
|
|
1330
|
+
"src/components/Hero.astro",
|
|
1331
|
+
`<section class="hero">
|
|
1332
|
+
<h1>We run the systems behind your website</h1>
|
|
1333
|
+
<p>Tell us what's going on, or <a href="/work">see the work first</a>.</p>
|
|
1334
|
+
<a href="/contact" class="btn btn-primary">Get in touch</a>
|
|
1335
|
+
</section>`
|
|
1336
|
+
),
|
|
1337
|
+
// A button in the nav and a search form are not the hero's ask.
|
|
1338
|
+
f(
|
|
1339
|
+
"index.html",
|
|
1340
|
+
`<header class="hero">
|
|
1341
|
+
<nav><a class="btn" href="/book">Book</a></nav>
|
|
1342
|
+
<h1>Barbers in Manchester</h1>
|
|
1343
|
+
<a class="btn" href="/book">Book a cut</a>
|
|
1344
|
+
<form><button class="btn" type="submit">Search</button></form>
|
|
1345
|
+
</header>`
|
|
1346
|
+
),
|
|
1347
|
+
f("app/page.tsx", `<section className="py-24">
|
|
1348
|
+
<h1 className="text-5xl">Nails in Brackley</h1>
|
|
1349
|
+
<a className="rounded bg-ink px-5 py-3" href="/book">Book now</a>
|
|
1350
|
+
</section>
|
|
1351
|
+
<section><a className="rounded bg-ink px-5 py-3" href="/prices">Prices</a></section>`)
|
|
1352
|
+
]
|
|
1353
|
+
}
|
|
1354
|
+
},
|
|
1223
1355
|
{
|
|
1224
1356
|
id: "icon-tile-stack",
|
|
1225
1357
|
name: "Icon in a tinted tile",
|
|
@@ -2418,7 +2550,9 @@ var HOUSE = {
|
|
|
2418
2550
|
"no-x-badge": { tier: "block", label: 'standalone "No X" badge. Reads as a slapped-on kicker, and worse when reused across pieces' },
|
|
2419
2551
|
"ai-phrase": { tier: "block", label: "AI tell" },
|
|
2420
2552
|
"plainer-word": { tier: "block", label: "a plainer word exists. Name the mechanism instead" },
|
|
2421
|
-
|
|
2553
|
+
// Widened 2026-10-20 (founder) to bare "plainly" and "in plain terms/language/words";
|
|
2554
|
+
// estate hits read first (DOM-1058), nearly all the stock "stated plainly".
|
|
2555
|
+
"plain-english": { tier: "block", label: 'the plain-English family. Drop the adverb and say the thing, or use "properly explained"' },
|
|
2422
2556
|
buzzword: { tier: "block", label: "forbidden phrase" },
|
|
2423
2557
|
"negative-reassurance": { tier: "block", label: "negative reassurance, say what they get" },
|
|
2424
2558
|
// Evidence of a pasted chat reply, not a matter of style. Moved from review
|
|
@@ -2432,6 +2566,8 @@ var HOUSE = {
|
|
|
2432
2566
|
"stock-email-line": { tier: "review", label: "stock email line, start with the reason for writing and end with the next step" },
|
|
2433
2567
|
// New 2026-10-19 (founder): review first, like every new tell, until estate hits are read.
|
|
2434
2568
|
"matey-register": { tier: "review", label: "matey register, write plain and complete British English" },
|
|
2569
|
+
// New 2026-10-20 (founder, from Eunice Ordor's review): review first, like every new tell.
|
|
2570
|
+
"confession-line": { tier: "review", label: "confession line, scope the work by saying precisely what is true" },
|
|
2435
2571
|
"ai-vocabulary": { tier: "review", label: "AI vocabulary" },
|
|
2436
2572
|
"stock-phrase": { tier: "review", label: "stock phrase" },
|
|
2437
2573
|
"hollow-imperative": { tier: "review", label: "hollow imperative" },
|
|
@@ -2614,6 +2750,33 @@ var RENDERED_PATHS = {
|
|
|
2614
2750
|
detect: (s) => s.effects.eyebrowChip ? [hit(s, "a small bordered pill sits above the headline")] : [],
|
|
2615
2751
|
fixtures: { flag: [makeSnapshot({ effects: { eyebrowChip: true } })], pass: [makeSnapshot()] }
|
|
2616
2752
|
},
|
|
2753
|
+
"hero-cta-pair": {
|
|
2754
|
+
// Version 2 geometry counts buttons and button-styled links per section. A
|
|
2755
|
+
// header painted inside the hero's box adds its own button, which the
|
|
2756
|
+
// source path can tell apart and this one cannot; a ghost button with no
|
|
2757
|
+
// fill or border is not counted, so this path under-reports.
|
|
2758
|
+
detect: (s) => {
|
|
2759
|
+
const first = s.sections[0];
|
|
2760
|
+
const controls = first?.geometry?.controls ?? 0;
|
|
2761
|
+
return first && (first.role ?? first.kind) === "hero" && controls >= 2 ? [hit(s, `${controls} buttons in the hero`, first.label)] : [];
|
|
2762
|
+
},
|
|
2763
|
+
fixtures: {
|
|
2764
|
+
flag: [
|
|
2765
|
+
{
|
|
2766
|
+
...makeSnapshot(),
|
|
2767
|
+
sections: makeSnapshot().sections.map((x, i) => i === 0 ? { ...x, role: "hero", geometry: { centredShare: 0.2, mirrorSymmetry: 0.6, whitespaceRatio: 0.6, contentWidthRatio: 0.8, background: "rgb(255, 255, 255)", controls: 2 } } : x)
|
|
2768
|
+
}
|
|
2769
|
+
],
|
|
2770
|
+
pass: [
|
|
2771
|
+
makeSnapshot(),
|
|
2772
|
+
// One button, with the second route as a text link: one control.
|
|
2773
|
+
{
|
|
2774
|
+
...makeSnapshot(),
|
|
2775
|
+
sections: makeSnapshot().sections.map((x, i) => i === 0 ? { ...x, role: "hero", geometry: { centredShare: 0.2, mirrorSymmetry: 0.6, whitespaceRatio: 0.6, contentWidthRatio: 0.8, background: "rgb(255, 255, 255)", controls: 1 } } : x)
|
|
2776
|
+
}
|
|
2777
|
+
]
|
|
2778
|
+
}
|
|
2779
|
+
},
|
|
2617
2780
|
"icon-tile-grid": {
|
|
2618
2781
|
detect: (s) => s.sections.filter((x) => x.cards >= 3 && x.iconCards >= 3).map((x) => hit(s, `${x.iconCards} icon cards in a row`, x.label)),
|
|
2619
2782
|
fixtures: {
|
|
@@ -3027,7 +3190,7 @@ var IMAGERY_TELLS = [
|
|
|
3027
3190
|
];
|
|
3028
3191
|
|
|
3029
3192
|
// src/character/check.ts
|
|
3030
|
-
var CATALOGUE_VERSION = "2026.10.
|
|
3193
|
+
var CATALOGUE_VERSION = "2026.10.5";
|
|
3031
3194
|
var CATALOGUE = [
|
|
3032
3195
|
...[...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map((t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t),
|
|
3033
3196
|
...SPECIFICITY_COPY_TELLS,
|
|
@@ -616,7 +616,17 @@ var REVIEW_PHRASES = [
|
|
|
616
616
|
"by far the most",
|
|
617
617
|
"hands down the",
|
|
618
618
|
"arguably the best",
|
|
619
|
-
"bar none"
|
|
619
|
+
"bar none",
|
|
620
|
+
// "plain" as a self-description, the way "clear" is: say what the reader gets.
|
|
621
|
+
"plain answer",
|
|
622
|
+
"plain answers",
|
|
623
|
+
"plain recommendation",
|
|
624
|
+
"plain explanation",
|
|
625
|
+
"plain explanations",
|
|
626
|
+
"plain advice",
|
|
627
|
+
"plain guidance",
|
|
628
|
+
"plain-speaking",
|
|
629
|
+
"plain speaking"
|
|
620
630
|
];
|
|
621
631
|
var SALES_TAGLINES = [
|
|
622
632
|
/\bwhere it (?:really |truly )?(?:counts|matters)(?: most)?\b/i,
|
|
@@ -672,6 +682,34 @@ var CLIPPED = [
|
|
|
672
682
|
// "Alright Carlo, built you a proper site": the "I" left out
|
|
673
683
|
/(?<=^|[\n>"“]|[.!?—–-]\s*|,\s+)(?:[Bb]uilt|[Mm]ade|[Pp]ut together|[Kk]nocked up)\s+(?:you|your\s+\w+|the\s+\w+)\s+an?\b/
|
|
674
684
|
];
|
|
685
|
+
var PLAIN_FAMILY = [
|
|
686
|
+
/\b(?:plain english|explained plainly|plainly explained)\b/i,
|
|
687
|
+
/\bplainly\b(?!\s+marked\b)/i,
|
|
688
|
+
/\bin\s+plain\s+(?:terms|language|words|speak)\b/i
|
|
689
|
+
];
|
|
690
|
+
var CONFESSION = [
|
|
691
|
+
// "What this is and is not:", "what we are and aren't"
|
|
692
|
+
/\bwhat\s+(?:this|it|we|our\s+\w+)\s+(?:is|are)\s+and\s+(?:is\s+not|isn['’]t|are\s+not|aren['’]t)\b/i,
|
|
693
|
+
// "What we are not", a label on its own; not "what we are not responsible for"
|
|
694
|
+
/(?<!\band\s)\bwhat\s+we(?:\s+are|['’]re)\s+not\b(?!\s+(?:responsible|liable|able|allowed|sure)\b)/i,
|
|
695
|
+
/\bwhat\s+we\s+(?:don['’]t|do\s+not)\s+do\b/i,
|
|
696
|
+
// "We have not yet built and run", "we haven't yet worked with"
|
|
697
|
+
/\bwe(?:\s+have|['’]ve)\s+not\s+yet\s+(?:built|run|done|worked|delivered|shipped|launched)\b/i,
|
|
698
|
+
/\bwe\s+haven['’]t\s+(?:yet\s+)?(?:built|run|done|worked\s+with|delivered)\b/i,
|
|
699
|
+
/\bwhat\s+we\s+have\s+and\s+(?:haven['’]t|have\s+not)\s+(?:done|built|run)\b/i,
|
|
700
|
+
// "Our clinic experience so far is one practice"
|
|
701
|
+
/\bour\s+(?:[\w-]+\s+){0,3}experience\s+so\s+far\s+(?:is|has\s+been|covers)\b/i,
|
|
702
|
+
/\bso\s+far\s+(?:is|has\s+been)\s+(?:one|a\s+single|just\s+one)\b/i,
|
|
703
|
+
/\bwhere\s+our\s+experience\s+(?:applies|ends|stops|runs\s+out)\b/i,
|
|
704
|
+
/\bwhat\s+we\s+(?:would|['’]d|will|still)\s+need\s+to\s+learn\b/i,
|
|
705
|
+
// "what we would and wouldn't take on", "what we won't take on"
|
|
706
|
+
/\b(?:would|will)\s+and\s+(?:wouldn['’]t|would\s+not|won['’]t|will\s+not)\s+take\s+on\b/i,
|
|
707
|
+
/\bwhat\s+we\s+(?:won['’]t|wouldn['’]t|would\s+not|will\s+not)\s+take\s+on\b/i,
|
|
708
|
+
// "Who we don't work with", "who this is not for", "we're not for everyone"
|
|
709
|
+
/\bwho\s+we\s+(?:don['’]t|do\s+not|won['’]t|will\s+not)\s+(?:work\s+with|serve|help|take\s+on)\b/i,
|
|
710
|
+
/\bwho\s+(?:this|it|we)\s+(?:is|are|['’]re)\s+not\s+for\b|\bwho\s+(?:this|it)\s+isn['’]t\s+for\b/i,
|
|
711
|
+
/\b(?:we|this)\s+(?:are|is|['’]re|['’]s)\s+not\s+(?:for\s+everyone|the\s+right\s+fit\s+for)\b/i
|
|
712
|
+
];
|
|
675
713
|
var NEGATION = [
|
|
676
714
|
// "It's not just a haircut, it's an experience."
|
|
677
715
|
/\b(?:it['’]?s|it is|this is|this isn['’]t|we['’]re|we are|they['’]re)\s+(?:not|more than)\s+(?:just|only|merely|simply)?\s*[^.!?\n]{1,60}?[,;.—-]\s*(?:it['’]?s|it is|this is|we['’]re|we are|they['’]re)\b/i,
|
|
@@ -801,12 +839,25 @@ var COPY_TELLS = [
|
|
|
801
839
|
generation: 2,
|
|
802
840
|
severity: "warn",
|
|
803
841
|
surface: "copy",
|
|
804
|
-
why: "Told to avoid jargon, a model announces that it is avoiding jargon
|
|
805
|
-
fix: "
|
|
806
|
-
detect: phrases(
|
|
842
|
+
why: "Told to avoid jargon, a model announces that it is avoiding jargon: 'in plain English', 'stated plainly', 'we'll tell you plainly', 'in plain terms'. The founder bans the whole family (20 October 2026); a reader takes it as filler.",
|
|
843
|
+
fix: "Drop the adverb and say the thing: 'Our prices, stated plainly' becomes 'Our prices'. Where the claim matters, the house replacement for 'plain English' is 'properly explained'.",
|
|
844
|
+
detect: phrases(PLAIN_FAMILY, quoted),
|
|
807
845
|
fixtures: {
|
|
808
|
-
flag: [
|
|
809
|
-
|
|
846
|
+
flag: [
|
|
847
|
+
f("content/home.md", "Your options, in plain English."),
|
|
848
|
+
// Eunice Ordor's draft lines, Pastel 986559 #12 and #13 (her diagnosis taken, her wording not).
|
|
849
|
+
f("content/clinics.md", "Tell us what's happening and we'll say plainly what we would take on."),
|
|
850
|
+
f("content/pricing.md", "DD's payment terms, plainly stated."),
|
|
851
|
+
f("content/clinics.md", "We explain our findings in plain terms."),
|
|
852
|
+
f("content/clinics.md", "We share findings in plain language.")
|
|
853
|
+
],
|
|
854
|
+
pass: [
|
|
855
|
+
f("content/home.md", "Your options, properly explained."),
|
|
856
|
+
f("content/pricing.md", "Our payment terms: 30% when you sign, the rest at launch."),
|
|
857
|
+
// The Apache and zlib licence wording, copied into a repo as it is.
|
|
858
|
+
f("LICENSE.txt", "Altered source versions must be plainly marked as such."),
|
|
859
|
+
f("content/menu.md", "Plain or seeded bagels, and plain-language allergen notes.")
|
|
860
|
+
]
|
|
810
861
|
}
|
|
811
862
|
},
|
|
812
863
|
{
|
|
@@ -857,11 +908,11 @@ var COPY_TELLS = [
|
|
|
857
908
|
generation: 1,
|
|
858
909
|
severity: "warn",
|
|
859
910
|
surface: "copy",
|
|
860
|
-
why: "Truly, genuinely, clear, ultimately, 'the single most', 'bar none': normal English once, and a tell when they stack. The house rules keep them for review, never blocking.",
|
|
911
|
+
why: "Truly, genuinely, clear, ultimately, 'a plain recommendation', 'the single most', 'bar none': normal English once, and a tell when they stack. The house rules keep them for review, never blocking.",
|
|
861
912
|
fix: "Keep the one that lands hardest and cut the rest. 'What is genuinely excellent' reads better as 'what is excellent'.",
|
|
862
913
|
detect: phrases([new RegExp(`(?:^|\\b)(?:${[...REVIEW_PHRASES].sort((a, b) => b.length - a.length).map((p) => escape(p).replace(/^(\w)/, "\\b$1").replace(/(\w)$/, "$1\\b")).join("|")})`, "i")], quoted),
|
|
863
914
|
fixtures: {
|
|
864
|
-
flag: [f("content/home.md", "We are truly proud of it."), f("content/home.md", "You leave with a clear next step.")],
|
|
915
|
+
flag: [f("content/home.md", "We are truly proud of it."), f("content/home.md", "You leave with a clear next step."), f("content/process.md", "You get a plain recommendation and someone who knows your systems.")],
|
|
865
916
|
pass: [f("content/home.md", "We have fitted 400 boilers since 2009."), f("content/home.md", "Keep a safe clearance around the meter.")]
|
|
866
917
|
}
|
|
867
918
|
},
|
|
@@ -942,6 +993,40 @@ var COPY_TELLS = [
|
|
|
942
993
|
]
|
|
943
994
|
}
|
|
944
995
|
},
|
|
996
|
+
{
|
|
997
|
+
id: "confession-line",
|
|
998
|
+
name: "Confession line",
|
|
999
|
+
generation: 2,
|
|
1000
|
+
severity: "warn",
|
|
1001
|
+
surface: "copy",
|
|
1002
|
+
why: "'What this is and is not', 'we have not yet built', 'our experience so far is one practice', 'what we would and wouldn't take on': a model scopes a business by confessing its limits. It reads as small print, and it turns a buyer away before they make contact.",
|
|
1003
|
+
fix: "Scope by being precise about what is true, and leave the door open: 'Built and run by us for Sensphere, a practice whose clinicians work across several sites.' Keep contractual facts (eligibility, who runs what) and say them as what the buyer gets.",
|
|
1004
|
+
detect: phrases(CONFESSION, quoted),
|
|
1005
|
+
fixtures: {
|
|
1006
|
+
flag: [
|
|
1007
|
+
// Each one from the DD site, as Eunice Ordor flagged it (Pastel 986559 #8, #12, #13).
|
|
1008
|
+
f("content/clinics.md", "What this is and is not: a practice portal we built and run for one practice."),
|
|
1009
|
+
f("content/clinics.md", "We have not yet built and run the digital systems of a practice with several clinicians."),
|
|
1010
|
+
f("content/home.md", "Including the client portal we run for Sensphere. Our clinic experience so far is one practice."),
|
|
1011
|
+
f("content/clinics.md", "Tell us what's happening and we'll say what we would and wouldn't take on."),
|
|
1012
|
+
f("content/clinics.md", "We will say what carries over from this work and what we would need to learn first."),
|
|
1013
|
+
f("content/clinics.md", "We will tell you where our experience applies directly and where it is close."),
|
|
1014
|
+
f("content/about.md", "What we are not"),
|
|
1015
|
+
f("content/about.md", "Who we don't work with: anyone who needs a site this week."),
|
|
1016
|
+
f("content/mobile.md", "Mobile is our newest line, so we tell you what we have and haven't done.")
|
|
1017
|
+
],
|
|
1018
|
+
pass: [
|
|
1019
|
+
f("content/clinics.md", "Built and run by us for Sensphere, a growing practice whose clinicians work remotely."),
|
|
1020
|
+
f("content/home.md", "The client portal we run for Sensphere."),
|
|
1021
|
+
// Contractual facts, said as they are.
|
|
1022
|
+
f("content/pricing.md", "Pay monthly is open to UK residents aged 18 and over, subject to status."),
|
|
1023
|
+
f("content/pricing.md", "Your monthly payments do not buy the site; the buy-out price does."),
|
|
1024
|
+
f("content/terms.md", "What we are responsible for, and what we are not responsible for, is in clause 4."),
|
|
1025
|
+
f("content/insurance.md", "Ask what your policy does and does not cover."),
|
|
1026
|
+
f("emails/deposit.md", "We have not yet received your deposit, so the date is still open.")
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
945
1030
|
{
|
|
946
1031
|
id: "hollow-imperative",
|
|
947
1032
|
name: "Hollow imperative",
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
holdHouseBlocks,
|
|
11
11
|
houseRule,
|
|
12
12
|
scanSource
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-MLYHSPF7.js";
|
|
14
14
|
import {
|
|
15
15
|
compareCompetitor,
|
|
16
16
|
compareFacts,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
pageCopy,
|
|
22
22
|
protectedFacts,
|
|
23
23
|
visibleText
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-NV6FKMID.js";
|
|
25
25
|
|
|
26
26
|
// src/character/config.ts
|
|
27
27
|
var DEFAULT_IGNORE = [".claude", ".agents", ".cursor"];
|
|
@@ -622,7 +622,7 @@ async function pageSnapshot(target, cwd) {
|
|
|
622
622
|
return readSnapshot(JSON.parse(readFileSync(local, "utf8")), target);
|
|
623
623
|
}
|
|
624
624
|
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL nor a snapshot file`);
|
|
625
|
-
const { snapshotUrl } = await import("./audit-
|
|
625
|
+
const { snapshotUrl } = await import("./audit-CDMZ6JHT.js");
|
|
626
626
|
return snapshotUrl(target);
|
|
627
627
|
}
|
|
628
628
|
async function briefFor(flags, cwd) {
|
|
@@ -633,7 +633,7 @@ async function briefFor(flags, cwd) {
|
|
|
633
633
|
if (typeof brief === "string" && brief.trim()) return brief;
|
|
634
634
|
} else if (flags.direction) throw new Error(`no such file: ${flags.direction}`);
|
|
635
635
|
if (flags.null) {
|
|
636
|
-
const { loadNull } = await import("./cli-
|
|
636
|
+
const { loadNull } = await import("./cli-4PXDNTZX.js");
|
|
637
637
|
return loadNull(flags.null, cwd).brief;
|
|
638
638
|
}
|
|
639
639
|
return void 0;
|
|
@@ -659,37 +659,37 @@ async function compareWithCompetitor(flags, io) {
|
|
|
659
659
|
function run(argv, io) {
|
|
660
660
|
const [command, ...rest] = argv;
|
|
661
661
|
if (command === "direction") {
|
|
662
|
-
return import("./cli-
|
|
662
|
+
return import("./cli-IQ5U76AZ.js").then((m) => m.runDirection(rest, io));
|
|
663
663
|
}
|
|
664
664
|
if (command === "report" || command === "retrofit") {
|
|
665
|
-
return import("./cli-
|
|
665
|
+
return import("./cli-223CUNU3.js").then((m) => m.runReport(command, rest, io));
|
|
666
666
|
}
|
|
667
667
|
if (command === "register") {
|
|
668
|
-
return import("./cli-
|
|
668
|
+
return import("./cli-6SMNHOCJ.js").then((m) => m.runRegister(rest, io));
|
|
669
669
|
}
|
|
670
670
|
if (command === "estate") {
|
|
671
|
-
return import("./cli-
|
|
671
|
+
return import("./cli-CQUORKMC.js").then((m) => m.runEstate(rest, io));
|
|
672
672
|
}
|
|
673
673
|
if (command === "null") {
|
|
674
|
-
return import("./cli-
|
|
674
|
+
return import("./cli-4PXDNTZX.js").then((m) => m.runNull(rest, io));
|
|
675
675
|
}
|
|
676
676
|
if (command === "calibrate") {
|
|
677
|
-
return import("./cli-
|
|
677
|
+
return import("./cli-7PPDBN6N.js").then((m) => m.runCalibrate(rest, io));
|
|
678
678
|
}
|
|
679
679
|
if (command === "tells" && rest[0] === "harvest") {
|
|
680
|
-
return import("./cli-
|
|
680
|
+
return import("./cli-4PXDNTZX.js").then((m) => m.runHarvest(rest, io)).catch((error) => {
|
|
681
681
|
io.err(`craft: ${error.message}`);
|
|
682
682
|
return 2;
|
|
683
683
|
});
|
|
684
684
|
}
|
|
685
685
|
if (command === "phone-first") {
|
|
686
|
-
return import("./cli-
|
|
686
|
+
return import("./cli-FKWJF4V4.js").then((m) => m.runPhoneFirst(rest, io));
|
|
687
687
|
}
|
|
688
688
|
if (command === "viewports") {
|
|
689
|
-
return import("./viewports-cli-
|
|
689
|
+
return import("./viewports-cli-H6UIUMBC.js").then((m) => m.runViewports(rest, io));
|
|
690
690
|
}
|
|
691
691
|
if (command === "snapshot" || command === "audit") {
|
|
692
|
-
return import("./cli-
|
|
692
|
+
return import("./cli-3QV4FKM4.js").then((m) => m.runAudit(command, rest, io));
|
|
693
693
|
}
|
|
694
694
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
695
695
|
io.out(HELP);
|
|
@@ -147,7 +147,8 @@ var LABEL = {
|
|
|
147
147
|
input: "wrong keyboard",
|
|
148
148
|
autocomplete: "field the phone cannot fill in",
|
|
149
149
|
tel: "phone number is not a link",
|
|
150
|
-
linelength: "line too long"
|
|
150
|
+
linelength: "line too long",
|
|
151
|
+
screens: "page too long for a phone"
|
|
151
152
|
};
|
|
152
153
|
function writeBaseline(findings, routes, note, previous) {
|
|
153
154
|
const byFile = tally(findings, (f) => f.file);
|
|
@@ -245,6 +246,102 @@ function judgeRoutes(pages, measuredRoutes, excluded = []) {
|
|
|
245
246
|
return { missing: pages.filter((p) => !covered.has(p)), problems, covered: covered.size };
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
// src/phone/viewports.ts
|
|
250
|
+
var LADDER = [
|
|
251
|
+
{ name: "320", width: 320, height: 568, phone: true },
|
|
252
|
+
{ name: "375", width: 375, height: 812, phone: true },
|
|
253
|
+
{ name: "390", width: 390, height: 844, phone: true },
|
|
254
|
+
{ name: "430", width: 430, height: 932, phone: true },
|
|
255
|
+
{ name: "844x390", width: 844, height: 390, phone: true, landscape: true },
|
|
256
|
+
{ name: "768", width: 768, height: 1024, phone: false },
|
|
257
|
+
{ name: "1024", width: 1024, height: 768, phone: false },
|
|
258
|
+
{ name: "1280", width: 1280, height: 800, phone: false },
|
|
259
|
+
{ name: "1440", width: 1440, height: 900, phone: false },
|
|
260
|
+
{ name: "1920", width: 1920, height: 1080, phone: false }
|
|
261
|
+
];
|
|
262
|
+
var SHORT_PHONE = { name: "375x667", width: 375, height: 667, phone: true };
|
|
263
|
+
var DEFAULT_SWEEP = { from: 320, to: 1920, step: 16, wideStep: 32, wideFrom: 1024, height: 800 };
|
|
264
|
+
function sweepWidths(sweep = DEFAULT_SWEEP) {
|
|
265
|
+
const out = [];
|
|
266
|
+
for (let w = sweep.from; w <= sweep.to; w += w < sweep.wideFrom ? sweep.step : sweep.wideStep) out.push(w);
|
|
267
|
+
if (out.at(-1) !== sweep.to) out.push(sweep.to);
|
|
268
|
+
return out;
|
|
269
|
+
}
|
|
270
|
+
var DEFAULT_THRESHOLDS = {
|
|
271
|
+
maxLayerShare: 0.25,
|
|
272
|
+
landscapeMaxLayerShare: 0.5,
|
|
273
|
+
oversizeRatio: 2.5,
|
|
274
|
+
upscaleRatio: 1,
|
|
275
|
+
maxLineCh: 80,
|
|
276
|
+
overlapShare: 0.4,
|
|
277
|
+
minImagePx: 32
|
|
278
|
+
};
|
|
279
|
+
var mainActionFor = (config, route) => typeof config === "string" ? config : config ? config[route] ?? config["*"] : void 0;
|
|
280
|
+
function screenBudgetFor(budgets, route) {
|
|
281
|
+
if (!budgets) return void 0;
|
|
282
|
+
const path = cleanRoute(route);
|
|
283
|
+
let best;
|
|
284
|
+
for (const key of Object.keys(budgets)) {
|
|
285
|
+
const prefix = key.endsWith("/*") ? key.slice(0, -1) : null;
|
|
286
|
+
const hit = key === "*" ? 0 : prefix ? path === key.slice(0, -2) || path.startsWith(prefix) ? 1 + prefix.length : -1 : routeMatches(key, path) ? key === path ? 1e4 : 5e3 + key.length : -1;
|
|
287
|
+
if (hit >= 0 && (!best || hit > best.score)) best = { key, score: hit };
|
|
288
|
+
}
|
|
289
|
+
return best ? budgets[best.key] : void 0;
|
|
290
|
+
}
|
|
291
|
+
function checkScreenBudgets(budgets, where = "screens") {
|
|
292
|
+
if (budgets === void 0) return void 0;
|
|
293
|
+
if (!budgets || typeof budgets !== "object" || Array.isArray(budgets)) throw new Error(`${where} should map a route to its budget in phone screens, such as { "/": 6.5, "/services/*": 6 }`);
|
|
294
|
+
for (const [route, value] of Object.entries(budgets)) {
|
|
295
|
+
if (route !== "*" && !route.startsWith("/")) throw new Error(`${where}: "${route}" should start with / (or be *)`);
|
|
296
|
+
if (typeof value !== "number" || !(value > 0)) throw new Error(`${where}: "${route}" should be a number of phone screens above 0, not ${JSON.stringify(value)}`);
|
|
297
|
+
}
|
|
298
|
+
return budgets;
|
|
299
|
+
}
|
|
300
|
+
function formatScreens(routes, budgets) {
|
|
301
|
+
return routes.filter((r) => r.screens !== void 0).map((r) => {
|
|
302
|
+
const budget = screenBudgetFor(budgets, r.route);
|
|
303
|
+
return `${r.route} ${r.screens}${budget === void 0 ? "" : ` of ${budget}`}`;
|
|
304
|
+
}).join(", ");
|
|
305
|
+
}
|
|
306
|
+
function formatWidths(failing, tested) {
|
|
307
|
+
const plain = tested.filter((n) => /^\d+$/.test(n)).sort((a, b) => Number(a) - Number(b));
|
|
308
|
+
const rank2 = new Map(plain.map((name, i) => [name, i]));
|
|
309
|
+
const names = [...new Set(failing)];
|
|
310
|
+
const runs = [];
|
|
311
|
+
for (const name of names.filter((n) => rank2.has(n)).sort((a, b) => rank2.get(a) - rank2.get(b))) {
|
|
312
|
+
const last = runs.at(-1);
|
|
313
|
+
if (last && rank2.get(name) === rank2.get(last.at(-1)) + 1) last.push(name);
|
|
314
|
+
else runs.push([name]);
|
|
315
|
+
}
|
|
316
|
+
const out = runs.map((r) => r.length > 1 ? `${r[0]}-${r.at(-1)}` : r[0]);
|
|
317
|
+
return [...out, ...names.filter((n) => !rank2.has(n))].join(", ");
|
|
318
|
+
}
|
|
319
|
+
var keyOf = (f) => `${f.check}\0${f.element.replace(/("[^]*)$/, (t) => t.replace(/\d+/g, "#"))}`;
|
|
320
|
+
function mergeViewportFindings(perViewport, tested) {
|
|
321
|
+
const merged = /* @__PURE__ */ new Map();
|
|
322
|
+
for (const { viewport, findings } of perViewport) {
|
|
323
|
+
const counts = /* @__PURE__ */ new Map();
|
|
324
|
+
for (const f of findings) {
|
|
325
|
+
const k = keyOf(f);
|
|
326
|
+
const had = counts.get(k);
|
|
327
|
+
if (had) had.n++;
|
|
328
|
+
else counts.set(k, { first: f, n: 1 });
|
|
329
|
+
}
|
|
330
|
+
for (const [k, { first, n }] of counts) {
|
|
331
|
+
const had = merged.get(k);
|
|
332
|
+
if (had) {
|
|
333
|
+
had.at.push(viewport);
|
|
334
|
+
had.most = Math.max(had.most, n);
|
|
335
|
+
} else merged.set(k, { first, at: [viewport], most: n });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const out = [];
|
|
339
|
+
for (const { first, at, most } of merged.values()) {
|
|
340
|
+
for (let i = 0; i < most; i++) out.push({ ...first, detail: `${first.detail} (at ${formatWidths(at, tested)})` });
|
|
341
|
+
}
|
|
342
|
+
return out;
|
|
343
|
+
}
|
|
344
|
+
|
|
248
345
|
export {
|
|
249
346
|
parseBaseline,
|
|
250
347
|
EMPTY_BASELINE,
|
|
@@ -257,5 +354,15 @@ export {
|
|
|
257
354
|
hasUnexcusedEntries,
|
|
258
355
|
routesFromPages,
|
|
259
356
|
routesFromManifest,
|
|
260
|
-
judgeRoutes
|
|
357
|
+
judgeRoutes,
|
|
358
|
+
LADDER,
|
|
359
|
+
SHORT_PHONE,
|
|
360
|
+
DEFAULT_SWEEP,
|
|
361
|
+
sweepWidths,
|
|
362
|
+
DEFAULT_THRESHOLDS,
|
|
363
|
+
mainActionFor,
|
|
364
|
+
screenBudgetFor,
|
|
365
|
+
checkScreenBudgets,
|
|
366
|
+
formatScreens,
|
|
367
|
+
mergeViewportFindings
|
|
261
368
|
};
|
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
} from "./chunk-OKVHWXYV.js";
|
|
4
4
|
import {
|
|
5
5
|
merge
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-JT2NLOQI.js";
|
|
7
|
+
import "./chunk-D6TAYXOS.js";
|
|
8
8
|
import "./chunk-JYNXGCYZ.js";
|
|
9
9
|
import {
|
|
10
10
|
SIBLING_AT,
|
|
11
11
|
compareToEstate,
|
|
12
12
|
loadEstate
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ICEG4S56.js";
|
|
14
14
|
import {
|
|
15
15
|
PAGE_TYPES,
|
|
16
16
|
validateDirection
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-IAU4ZAIU.js";
|
|
18
18
|
import {
|
|
19
19
|
loadNull
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-KH56TEFK.js";
|
|
21
21
|
import "./chunk-OQXTQQAU.js";
|
|
22
22
|
import {
|
|
23
23
|
COPY_FILE,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
loadExceptions,
|
|
29
29
|
parseFlags,
|
|
30
30
|
readPaths
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-WTR77PEM.js";
|
|
32
32
|
import {
|
|
33
33
|
toJson
|
|
34
34
|
} from "./chunk-QIEDBL4J.js";
|
|
@@ -42,8 +42,8 @@ import {
|
|
|
42
42
|
fingerprint,
|
|
43
43
|
scanSource,
|
|
44
44
|
typicality
|
|
45
|
-
} from "./chunk-
|
|
46
|
-
import "./chunk-
|
|
45
|
+
} from "./chunk-MLYHSPF7.js";
|
|
46
|
+
import "./chunk-NV6FKMID.js";
|
|
47
47
|
import "./chunk-QYBAVU3P.js";
|
|
48
48
|
|
|
49
49
|
// src/report/cli.ts
|
|
@@ -126,7 +126,7 @@ async function runReport(command, args, io) {
|
|
|
126
126
|
if (/\.json$/i.test(target2) && existsSync(local)) {
|
|
127
127
|
snap = readSnapshot(JSON.parse(readFileSync(local, "utf8")), target2);
|
|
128
128
|
} else {
|
|
129
|
-
const { snapshotUrl } = await import("./audit-
|
|
129
|
+
const { snapshotUrl } = await import("./audit-CDMZ6JHT.js");
|
|
130
130
|
snap = await snapshotUrl(target2);
|
|
131
131
|
}
|
|
132
132
|
const fp = fingerprint(snap);
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
merge,
|
|
5
5
|
resolvePages,
|
|
6
6
|
runAudit
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-JT2NLOQI.js";
|
|
8
|
+
import "./chunk-D6TAYXOS.js";
|
|
9
9
|
import "./chunk-JYNXGCYZ.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-KH56TEFK.js";
|
|
11
11
|
import "./chunk-OQXTQQAU.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-WTR77PEM.js";
|
|
13
13
|
import "./chunk-QIEDBL4J.js";
|
|
14
14
|
import "./chunk-WUACK2HX.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-MLYHSPF7.js";
|
|
16
|
+
import "./chunk-NV6FKMID.js";
|
|
17
17
|
import "./chunk-QYBAVU3P.js";
|
|
18
18
|
export {
|
|
19
19
|
describeComponents,
|