@domandigital/craft 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/CHARACTER.md +140 -1
- package/COPY.md +5 -0
- package/dist/{audit-5QWFLZ6L.js → audit-6WHR7ZTV.js} +7 -3
- package/dist/audit.cjs +1267 -165
- package/dist/audit.d.cts +95 -1
- package/dist/audit.d.ts +95 -1
- package/dist/audit.js +1265 -165
- package/dist/{chunk-J3QUROJ5.js → chunk-3OTKMKJQ.js} +66 -91
- package/dist/{chunk-PU5G5DUC.js → chunk-7T5WR74C.js} +27 -14
- package/dist/{chunk-AL6MRG6V.js → chunk-GQVEYTBY.js} +3592 -2173
- package/dist/{chunk-S3NZAVU6.js → chunk-OOZOEWGD.js} +1 -1
- package/dist/{chunk-CDAJUS5U.js → chunk-PBK6SYH5.js} +51 -16
- package/dist/{chunk-ULPI335S.js → chunk-Q7ILL2HV.js} +211 -10
- package/dist/chunk-QYBAVU3P.js +6 -0
- package/dist/{chunk-ZKVJ7K6X.js → chunk-WUACK2HX.js} +2 -1
- package/dist/chunk-XVBR2UPH.js +217 -0
- package/dist/cli-3CGUKXY4.js +19 -0
- package/dist/{cli-ZPJIWVEP.js → cli-O3CDSONO.js} +7 -8
- package/dist/{cli-ZB2VJ7CY.js → cli-WG3UJNUF.js} +15 -16
- package/dist/cli-WSHPSWVU.js +21 -0
- package/dist/{cli-C3JLFXMG.js → cli-Z2IQG3LC.js} +5 -5
- package/dist/cli.js +3 -2
- package/dist/index.cjs +1719 -636
- package/dist/index.d.cts +570 -196
- package/dist/index.d.ts +570 -196
- package/dist/index.js +1684 -636
- package/dist/migrate-VIFAYIBM.js +9 -0
- package/package.json +1 -1
- package/word-lists.json +1 -1
- package/dist/chunk-LHGY43LL.js +0 -462
- package/dist/chunk-PZ4MF2VV.js +0 -117
- package/dist/cli-2DY3V3SF.js +0 -17
- package/dist/cli-QM2AQQMF.js +0 -19
|
@@ -4,13 +4,19 @@ import {
|
|
|
4
4
|
HOUSE_LOCKED,
|
|
5
5
|
applyHouseGate,
|
|
6
6
|
checkCopy,
|
|
7
|
+
compareCompetitor,
|
|
8
|
+
compareFacts,
|
|
7
9
|
extractCopy,
|
|
8
|
-
extractStrings,
|
|
9
10
|
fileKind,
|
|
11
|
+
formatComparison,
|
|
12
|
+
formatCompetitor,
|
|
10
13
|
holdHouseBlocks,
|
|
11
14
|
houseRule,
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
pageCopy,
|
|
16
|
+
protectedFacts,
|
|
17
|
+
scanSource,
|
|
18
|
+
visibleText
|
|
19
|
+
} from "./chunk-GQVEYTBY.js";
|
|
14
20
|
|
|
15
21
|
// src/character/config.ts
|
|
16
22
|
var DEFAULT_IGNORE = [".claude", ".agents", ".cursor"];
|
|
@@ -205,87 +211,6 @@ function toSarif(report, options) {
|
|
|
205
211
|
};
|
|
206
212
|
}
|
|
207
213
|
|
|
208
|
-
// src/character/facts.ts
|
|
209
|
-
var MONTH = "(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)";
|
|
210
|
-
var PATTERNS = [
|
|
211
|
-
["url", /\bhttps?:\/\/[^\s<>"'`)\]]+|\bwww\.[^\s<>"'`)\]]+/gi],
|
|
212
|
-
["email", /\b[\w.+-]+@[\w-]+(?:\.[\w-]+)+\b/g],
|
|
213
|
-
["phone", /(?:\+44\s?\(?0?\)?\s?|\b0)\d(?:[\s-]?\d){8,9}\b/g],
|
|
214
|
-
["postcode", /\b[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}\b/g],
|
|
215
|
-
["money", /[£$€]\s?\d[\d,]*(?:\.\d{1,2})?(?:\s?(?:k|m|bn)\b)?|\b\d[\d,]*(?:\.\d{1,2})?\s?(?:pounds|GBP)\b/gi],
|
|
216
|
-
["date", new RegExp(`\\b\\d{1,2}(?:st|nd|rd|th)?\\s+${MONTH}\\b(?:\\s+\\d{4})?|\\b${MONTH}\\s+\\d{1,2}(?:st|nd|rd|th)?\\b(?:,?\\s+\\d{4})?|\\b${MONTH}\\s+\\d{4}\\b|\\b\\d{1,2}\\/\\d{1,2}\\/\\d{2,4}\\b|\\b(?:19|20)\\d{2}\\b`, "g")],
|
|
217
|
-
["time", /\b\d{1,2}(?:[:.]\d{2})?\s?(?:am|pm)\b|\b\d{1,2}:\d{2}\b/gi],
|
|
218
|
-
["number", /\b\d[\d,]*(?:\.\d+)?%?|\b\d+(?:\.\d+)?\s?(?:per ?cent)\b/gi]
|
|
219
|
-
];
|
|
220
|
-
var NAME = /[A-Z][a-zA-Z’'&-]*[a-zA-Z](?:\s+(?:of|and|&|the)?\s*[A-Z][a-zA-Z’'&-]*[a-zA-Z])*/g;
|
|
221
|
-
var normalise = (kind, value) => {
|
|
222
|
-
const v = value.trim();
|
|
223
|
-
if (kind === "phone") return v.replace(/[^\d+]/g, "").replace(/^\+44\(?0?\)?/, "0").replace(/^\+44/, "0");
|
|
224
|
-
if (kind === "money" || kind === "number") return v.replace(/[\s,]/g, "").toLowerCase();
|
|
225
|
-
if (kind === "postcode") return v.replace(/\s/g, "").toUpperCase();
|
|
226
|
-
if (kind === "url") return v.replace(/[.,;:]+$/, "").toLowerCase();
|
|
227
|
-
return v.replace(/\s+/g, " ").toLowerCase();
|
|
228
|
-
};
|
|
229
|
-
function visibleText(file) {
|
|
230
|
-
const prose = /\.(md|markdown|mdx|txt)$/i.test(file.path);
|
|
231
|
-
return (prose ? extractCopy(file) : extractStrings(file)).map((b) => b.text).join("\n");
|
|
232
|
-
}
|
|
233
|
-
function protectedFacts(text) {
|
|
234
|
-
const facts = [];
|
|
235
|
-
let rest = text;
|
|
236
|
-
const mask = (start, length) => {
|
|
237
|
-
rest = rest.slice(0, start) + " ".repeat(length) + rest.slice(start + length);
|
|
238
|
-
};
|
|
239
|
-
for (const [kind, pattern] of PATTERNS) {
|
|
240
|
-
for (const m of [...rest.matchAll(pattern)]) {
|
|
241
|
-
const value = m[0].replace(/[.,;:]+$/, "");
|
|
242
|
-
facts.push({ kind, value, key: `${kind}:${normalise(kind, value)}` });
|
|
243
|
-
mask(m.index ?? 0, m[0].length);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const lines = rest.split("\n");
|
|
247
|
-
for (const line of lines) {
|
|
248
|
-
if (/^\s{0,3}#{1,6}\s/.test(line)) continue;
|
|
249
|
-
for (const m of line.matchAll(NAME)) {
|
|
250
|
-
const at = m.index ?? 0;
|
|
251
|
-
const before = line.slice(0, at).trimEnd();
|
|
252
|
-
const opensSentence = before === "" || /[.!?:"“(>*-]$/.test(before) || /^\s*(?:[-*+]|\d+\.)$/.test(before);
|
|
253
|
-
const word = m[0];
|
|
254
|
-
const acronym = /^[A-Z]{2,}$/.test(word);
|
|
255
|
-
if (opensSentence && !acronym && !/\s/.test(word)) continue;
|
|
256
|
-
if (word === "I") continue;
|
|
257
|
-
facts.push({ kind: "name", value: word, key: `name:${normalise("name", word)}` });
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return facts;
|
|
261
|
-
}
|
|
262
|
-
var unique = (facts) => [...new Map(facts.map((f) => [f.key, f])).values()];
|
|
263
|
-
function compareFacts(before, after) {
|
|
264
|
-
const a = unique(protectedFacts(before));
|
|
265
|
-
const b = unique(protectedFacts(after));
|
|
266
|
-
const inA = new Set(a.map((f) => f.key));
|
|
267
|
-
const inB = new Set(b.map((f) => f.key));
|
|
268
|
-
return {
|
|
269
|
-
before: a.length,
|
|
270
|
-
after: b.length,
|
|
271
|
-
lost: a.filter((f) => !inB.has(f.key)),
|
|
272
|
-
added: b.filter((f) => !inA.has(f.key))
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
function formatComparison(result, beforePath, afterPath) {
|
|
276
|
-
const lines = [`craft copy compare: ${beforePath} -> ${afterPath}`, ` ${result.before} protected facts before, ${result.after} after`];
|
|
277
|
-
if (result.lost.length) {
|
|
278
|
-
lines.push("", ` Lost (${result.lost.length}): restore each, or say why it went`);
|
|
279
|
-
for (const f of result.lost) lines.push(` ${f.kind.padEnd(9)} ${f.value}`);
|
|
280
|
-
}
|
|
281
|
-
if (result.added.length) {
|
|
282
|
-
lines.push("", ` Added (${result.added.length}): each needs a source, or it is invented`);
|
|
283
|
-
for (const f of result.added) lines.push(` ${f.kind.padEnd(9)} ${f.value}`);
|
|
284
|
-
}
|
|
285
|
-
if (!result.lost.length && !result.added.length) lines.push("", " Every fact kept, nothing added.");
|
|
286
|
-
return lines.join("\n");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
214
|
// src/character/claims.ts
|
|
290
215
|
var CLAIM_KINDS = /* @__PURE__ */ new Set(["money", "number", "date"]);
|
|
291
216
|
var PROPER = "[A-Z][\\w&.\u2019'-]*(?:\\s+(?:of|and|&|the|for)?\\s*[A-Z][\\w&.\u2019'-]*)*";
|
|
@@ -445,6 +370,7 @@ Usage
|
|
|
445
370
|
craft scan [paths...] [--staged] [--json] [--strict] [--direction <file>] [ci options]
|
|
446
371
|
craft copy [paths...] [--gate] [--json] [--strict] [--direction <file>] [ci options]
|
|
447
372
|
craft copy compare <before> <after> [--json]
|
|
373
|
+
craft copy compare <url | snapshot.json> --competitor <url | snapshot.json> [--brief "<text>"] [--json]
|
|
448
374
|
craft copy claims <paths...> [--json]
|
|
449
375
|
craft tells list [--json]
|
|
450
376
|
craft tells harvest <null.json | dir>... [--share 0.25] [--json]
|
|
@@ -468,6 +394,11 @@ copy The prose in Markdown, markup and content files.
|
|
|
468
394
|
compare lists the protected facts (prices, numbers, dates, times,
|
|
469
395
|
phones, emails, links, postcodes, names) a rewrite lost or added.
|
|
470
396
|
Exit 1 if any: restore it, source it, or say why.
|
|
397
|
+
With --competitor, reads both rendered pages instead and lists the
|
|
398
|
+
hero and service sentences holding no specific (place, name,
|
|
399
|
+
number, price, job) the other page does not also say. The brief's
|
|
400
|
+
terms come from --brief, else art-direction.json, else --null's
|
|
401
|
+
null.json. A measure, not a gate: always exit 0.
|
|
471
402
|
claims lists every sentence holding a price, figure, date or named
|
|
472
403
|
source, marked sourced or UNSOURCED, for a person to check against
|
|
473
404
|
the primary source. It cannot tell true from false. Always exit 0.
|
|
@@ -528,7 +459,9 @@ var VALUE_FLAGS = {
|
|
|
528
459
|
"--baseline": "baseline",
|
|
529
460
|
"--sarif": "sarif",
|
|
530
461
|
"--pages": "pages",
|
|
531
|
-
"--viewport": "viewport"
|
|
462
|
+
"--viewport": "viewport",
|
|
463
|
+
"--component": "component",
|
|
464
|
+
"--competitor": "competitor"
|
|
532
465
|
};
|
|
533
466
|
function parseFlags(args) {
|
|
534
467
|
const flags = { positional: [], json: false, strict: false, staged: false, gate: false, updateBaseline: false };
|
|
@@ -636,28 +569,69 @@ function finish(report, flags, title, io) {
|
|
|
636
569
|
var SOURCE_FILE = (p) => ["markup", "script", "css"].includes(fileKind(p)) && !/\.d\.ts$|\.min\.js$/.test(p);
|
|
637
570
|
var COPY_FILE = (p) => (["markup", "prose", "script"].includes(fileKind(p)) || /\.jsonl?$/i.test(p)) && !NOT_SITE_COPY.test(p.split("/").pop() ?? p) && !/(?:^|\/)(?:package(?:-lock)?|tsconfig[\w.-]*|art-direction)\.json$/.test(p);
|
|
638
571
|
var NOT_COPY_DIR = (name) => name.startsWith("_");
|
|
572
|
+
async function pageSnapshot(target, cwd) {
|
|
573
|
+
const local = resolve(cwd, target);
|
|
574
|
+
if (/\.json$/i.test(target) && existsSync(local)) {
|
|
575
|
+
const { readSnapshot } = await import("./migrate-VIFAYIBM.js");
|
|
576
|
+
return readSnapshot(JSON.parse(readFileSync(local, "utf8")), target);
|
|
577
|
+
}
|
|
578
|
+
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL nor a snapshot file`);
|
|
579
|
+
const { snapshotUrl } = await import("./audit-6WHR7ZTV.js");
|
|
580
|
+
return snapshotUrl(target);
|
|
581
|
+
}
|
|
582
|
+
async function briefFor(flags, cwd) {
|
|
583
|
+
if (flags.brief) return flags.brief;
|
|
584
|
+
const direction = resolve(cwd, flags.direction ?? "art-direction.json");
|
|
585
|
+
if (existsSync(direction)) {
|
|
586
|
+
const brief = JSON.parse(readFileSync(direction, "utf8")).brief;
|
|
587
|
+
if (typeof brief === "string" && brief.trim()) return brief;
|
|
588
|
+
} else if (flags.direction) throw new Error(`no such file: ${flags.direction}`);
|
|
589
|
+
if (flags.null) {
|
|
590
|
+
const { loadNull } = await import("./cli-Z2IQG3LC.js");
|
|
591
|
+
return loadNull(flags.null, cwd).brief;
|
|
592
|
+
}
|
|
593
|
+
return void 0;
|
|
594
|
+
}
|
|
595
|
+
async function compareWithCompetitor(flags, io) {
|
|
596
|
+
try {
|
|
597
|
+
if (flags.positional.length !== 1 || !flags.competitor) throw new Error('usage: craft copy compare <url | snapshot.json> --competitor <url | snapshot.json> [--brief "<text>"] [--json]');
|
|
598
|
+
const brief = await briefFor(flags, io.cwd);
|
|
599
|
+
const [ours, theirs] = await Promise.all([pageSnapshot(flags.positional[0], io.cwd), pageSnapshot(flags.competitor, io.cwd)]);
|
|
600
|
+
const copy = [ours, theirs].map((snap, i) => {
|
|
601
|
+
const page = pageCopy(snap);
|
|
602
|
+
if (!page) throw new Error(`${[flags.positional[0], flags.competitor][i]} has no page text: snapshot it again with this craft`);
|
|
603
|
+
return page;
|
|
604
|
+
});
|
|
605
|
+
const result = compareCompetitor(copy[0], copy[1], brief);
|
|
606
|
+
io.out(flags.json ? toJson(result) : formatCompetitor(result));
|
|
607
|
+
return 0;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
io.err(`craft: ${error.message}`);
|
|
610
|
+
return 2;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
639
613
|
function run(argv, io) {
|
|
640
614
|
const [command, ...rest] = argv;
|
|
641
615
|
if (command === "direction") {
|
|
642
|
-
return import("./cli-
|
|
616
|
+
return import("./cli-O3CDSONO.js").then((m) => m.runDirection(rest, io));
|
|
643
617
|
}
|
|
644
618
|
if (command === "report" || command === "retrofit") {
|
|
645
|
-
return import("./cli-
|
|
619
|
+
return import("./cli-WG3UJNUF.js").then((m) => m.runReport(command, rest, io));
|
|
646
620
|
}
|
|
647
621
|
if (command === "estate") {
|
|
648
|
-
return import("./cli-
|
|
622
|
+
return import("./cli-3CGUKXY4.js").then((m) => m.runEstate(rest, io));
|
|
649
623
|
}
|
|
650
624
|
if (command === "null") {
|
|
651
|
-
return import("./cli-
|
|
625
|
+
return import("./cli-Z2IQG3LC.js").then((m) => m.runNull(rest, io));
|
|
652
626
|
}
|
|
653
627
|
if (command === "tells" && rest[0] === "harvest") {
|
|
654
|
-
return import("./cli-
|
|
628
|
+
return import("./cli-Z2IQG3LC.js").then((m) => m.runHarvest(rest, io)).catch((error) => {
|
|
655
629
|
io.err(`craft: ${error.message}`);
|
|
656
630
|
return 2;
|
|
657
631
|
});
|
|
658
632
|
}
|
|
659
633
|
if (command === "snapshot" || command === "audit") {
|
|
660
|
-
return import("./cli-
|
|
634
|
+
return import("./cli-WSHPSWVU.js").then((m) => m.runAudit(command, rest, io));
|
|
661
635
|
}
|
|
662
636
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
663
637
|
io.out(HELP);
|
|
@@ -689,6 +663,7 @@ function run(argv, io) {
|
|
|
689
663
|
if (command === "copy" && rest[0] === "compare") {
|
|
690
664
|
const flags = parseFlags(rest.slice(1));
|
|
691
665
|
if (typeof flags === "string") throw new Error(flags);
|
|
666
|
+
if (flags.competitor) return compareWithCompetitor(flags, io);
|
|
692
667
|
if (flags.positional.length !== 2) throw new Error("usage: craft copy compare <before> <after> [--json]");
|
|
693
668
|
const [before, after] = flags.positional.map((p) => {
|
|
694
669
|
const abs = resolve(io.cwd, p);
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
snapshotUrl,
|
|
3
3
|
snapshotUrls
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Q7ILL2HV.js";
|
|
5
5
|
import {
|
|
6
6
|
describeTypicality,
|
|
7
|
-
loadNull
|
|
8
|
-
|
|
9
|
-
} from "./chunk-LHGY43LL.js";
|
|
10
|
-
import {
|
|
11
|
-
readSnapshot
|
|
12
|
-
} from "./chunk-ZKVJ7K6X.js";
|
|
7
|
+
loadNull
|
|
8
|
+
} from "./chunk-XVBR2UPH.js";
|
|
13
9
|
import {
|
|
14
10
|
SOURCE_FILE,
|
|
15
11
|
finish,
|
|
@@ -19,14 +15,17 @@ import {
|
|
|
19
15
|
prepareReport,
|
|
20
16
|
readPaths,
|
|
21
17
|
toJson
|
|
22
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-3OTKMKJQ.js";
|
|
23
19
|
import {
|
|
24
|
-
|
|
25
|
-
} from "./chunk-
|
|
20
|
+
readSnapshot
|
|
21
|
+
} from "./chunk-WUACK2HX.js";
|
|
26
22
|
import {
|
|
27
23
|
auditSnapshot,
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
componentTypicalities,
|
|
25
|
+
fingerprint,
|
|
26
|
+
scanSource,
|
|
27
|
+
typicality
|
|
28
|
+
} from "./chunk-GQVEYTBY.js";
|
|
30
29
|
|
|
31
30
|
// src/audit/cli.ts
|
|
32
31
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -51,6 +50,12 @@ function parseViewports(value) {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
// src/audit/cli.ts
|
|
53
|
+
function describeComponents(components, pageMeasured, nullMeasured) {
|
|
54
|
+
if (!pageMeasured) return " components not measured: the page's snapshot predates version 2";
|
|
55
|
+
if (!nullMeasured) return " components not measured: the null model predates snapshot version 2; rebuild it to compare components";
|
|
56
|
+
if (components.length === 0) return " components none the null model has enough pages of to compare";
|
|
57
|
+
return components.map((c, i) => `${i === 0 ? " components" : " "} ${c.role.padEnd(13)} ${c.score.toFixed(2)}: ${c.typical ? "typical" : "not typical"} (${c.runs} null pages have one)`).join("\n");
|
|
58
|
+
}
|
|
54
59
|
var fmt = (o) => o ? `oklch(${o.l.toFixed(3)} ${o.c.toFixed(3)} ${o.h.toFixed(1)})` : "none";
|
|
55
60
|
function describeFingerprint(fp) {
|
|
56
61
|
return [
|
|
@@ -167,11 +172,17 @@ ${failed.map((f) => ` ${f.url}: ${f.error}`).join("\n")}`);
|
|
|
167
172
|
const pooled = nulls.flatMap((m, i) => m.runs.map((r) => nulls.length > 1 ? { ...r, id: `${i + 1}/${r.id}` } : r));
|
|
168
173
|
const pages = audited.map((a) => {
|
|
169
174
|
const fp = fingerprint(a.snapshot);
|
|
170
|
-
return {
|
|
175
|
+
return {
|
|
176
|
+
url: a.snapshot.url,
|
|
177
|
+
viewport: a.viewport,
|
|
178
|
+
fingerprint: fp,
|
|
179
|
+
typicality: nulls.length ? typicality(fp, pooled) : null,
|
|
180
|
+
components: nulls.length ? componentTypicalities(fp, pooled) : []
|
|
181
|
+
};
|
|
171
182
|
});
|
|
172
183
|
const unmeasured = failed.length > 0 && flags.strict ? 1 : 0;
|
|
173
184
|
if (flags.json) {
|
|
174
|
-
const extra = single ? { fingerprint: pages[0].fingerprint, typicality: pages[0].typicality, snapshot: flags.out ?? null } : {};
|
|
185
|
+
const extra = single ? { fingerprint: pages[0].fingerprint, typicality: pages[0].typicality, components: pages[0].components, snapshot: flags.out ?? null } : {};
|
|
175
186
|
io.out(toJson({ report: prepared, ...extra, pages, failed }));
|
|
176
187
|
return Math.max(prepared.summary.blocking > 0 || flags.strict && prepared.summary.findings > 0 ? 1 : 0, unmeasured);
|
|
177
188
|
}
|
|
@@ -184,6 +195,7 @@ ${failed.map((f) => ` ${f.url}: ${f.error}`).join("\n")}`);
|
|
|
184
195
|
if (page.typicality) {
|
|
185
196
|
io.out("");
|
|
186
197
|
io.out(describeTypicality(page.typicality, pooled.length, nulls.length));
|
|
198
|
+
io.out(describeComponents(page.components, page.fingerprint.sections !== void 0, pooled.some((r) => r.fingerprint.sections !== void 0)));
|
|
187
199
|
}
|
|
188
200
|
}
|
|
189
201
|
if (failed.length) {
|
|
@@ -199,6 +211,7 @@ ${failed.map((f) => ` ${f.url}: ${f.error}`).join("\n")}`);
|
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
export {
|
|
214
|
+
describeComponents,
|
|
202
215
|
describeFingerprint,
|
|
203
216
|
merge,
|
|
204
217
|
resolvePages,
|