@domandigital/craft 0.12.0 → 0.13.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.
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  snapshotUrl,
3
3
  snapshotUrls
4
- } from "./chunk-ULPI335S.js";
4
+ } from "./chunk-ZLDA2SYB.js";
5
5
  import {
6
6
  describeTypicality,
7
- loadNull,
8
- typicality
9
- } from "./chunk-LHGY43LL.js";
7
+ loadNull
8
+ } from "./chunk-ESCVTKPN.js";
10
9
  import {
11
10
  readSnapshot
12
- } from "./chunk-ZKVJ7K6X.js";
11
+ } from "./chunk-CZDOMIVS.js";
13
12
  import {
14
13
  SOURCE_FILE,
15
14
  finish,
@@ -19,14 +18,14 @@ import {
19
18
  prepareReport,
20
19
  readPaths,
21
20
  toJson
22
- } from "./chunk-J3QUROJ5.js";
23
- import {
24
- fingerprint
25
- } from "./chunk-PZ4MF2VV.js";
21
+ } from "./chunk-3VTPI7BU.js";
26
22
  import {
27
23
  auditSnapshot,
28
- scanSource
29
- } from "./chunk-AL6MRG6V.js";
24
+ componentTypicalities,
25
+ fingerprint,
26
+ scanSource,
27
+ typicality
28
+ } from "./chunk-GMVXADJ6.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 { url: a.snapshot.url, viewport: a.viewport, fingerprint: fp, typicality: nulls.length ? typicality(fp, pooled) : null };
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,
@@ -1,23 +1,24 @@
1
1
  import {
2
- groundName,
3
- hueName,
4
- loadNull,
5
- shapeName
6
- } from "./chunk-LHGY43LL.js";
2
+ loadNull
3
+ } from "./chunk-ESCVTKPN.js";
7
4
  import {
8
5
  readSnapshot
9
- } from "./chunk-ZKVJ7K6X.js";
6
+ } from "./chunk-CZDOMIVS.js";
10
7
  import {
11
8
  parseFlags,
12
9
  toJson
13
- } from "./chunk-J3QUROJ5.js";
10
+ } from "./chunk-3VTPI7BU.js";
14
11
  import {
12
+ COMPONENT_ROLES,
13
+ auditSnapshot,
14
+ closestComponents,
15
+ estateComponentPairs,
15
16
  fingerprint,
16
- fingerprintDistance
17
- } from "./chunk-PZ4MF2VV.js";
18
- import {
19
- auditSnapshot
20
- } from "./chunk-AL6MRG6V.js";
17
+ fingerprintDistance,
18
+ groundName,
19
+ hueName,
20
+ shapeName
21
+ } from "./chunk-GMVXADJ6.js";
21
22
 
22
23
  // src/estate/cli.ts
23
24
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -80,7 +81,7 @@ function siblingLine(briefs) {
80
81
  }
81
82
 
82
83
  // src/estate/cli.ts
83
- var USAGE = "usage: craft estate add <url | snapshot.json> --id <id> [--client <name>] | craft estate compare [<url | snapshot.json | id>] [--null <dir>] [--json] [--strict]";
84
+ var USAGE = "usage: craft estate add <url | snapshot.json> --id <id> [--client <name>] | craft estate compare [<url | snapshot.json | id>] [--null <dir>] [--component <role>] [--json] [--strict]";
84
85
  function loadEstate(path) {
85
86
  if (!existsSync(path)) return emptyEstate();
86
87
  const data = JSON.parse(readFileSync(path, "utf8"));
@@ -94,7 +95,7 @@ async function snapshotOf(target, cwd) {
94
95
  return snap;
95
96
  }
96
97
  if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL, a snapshot file, nor a site in the register`);
97
- const { snapshotUrl } = await import("./audit-5QWFLZ6L.js");
98
+ const { snapshotUrl } = await import("./audit-5TYDYRXV.js");
98
99
  return snapshotUrl(target);
99
100
  }
100
101
  var line = (distance, sibling, shared) => `${distance.toFixed(2)}${sibling ? " SIBLING" : ""}${shared.length ? ` shares ${shared.join(", ")}` : ""}`;
@@ -110,6 +111,21 @@ function formatPairs(pairs, at, sites) {
110
111
  for (const p of pairs) lines.push(` ${`${p.a} + ${p.b}`.padEnd(34)} ${line(p.distance, p.sibling, p.shared)}`);
111
112
  return lines.join("\n");
112
113
  }
114
+ function componentRole(name) {
115
+ if (!COMPONENT_ROLES.includes(name)) throw new Error(`--component takes one of ${COMPONENT_ROLES.join(", ")}`);
116
+ return name;
117
+ }
118
+ var withRole = (register, role) => register.sites.filter((s) => (s.fingerprint.sections ?? []).some((x) => x.role === role)).length;
119
+ function formatComponentPairs(role, pairs, measured, sites) {
120
+ const lines = [`craft estate: ${role} components, ${measured} of ${sites} sites have one, closest pairs first (no sibling line yet: ranked, not judged)`];
121
+ for (const p of pairs) lines.push(` ${`${p.a} + ${p.b}`.padEnd(34)} ${p.distance.toFixed(2)}${p.shared.length ? ` shares ${p.shared.join(", ")}` : ""}`);
122
+ if (pairs.length === 0) {
123
+ lines.push(
124
+ measured < 2 ? ` fewer than two sites have a ${role} section measured. Roles come from snapshot version 2: re-add the sites from a fresh snapshot.` : " no pairs"
125
+ );
126
+ }
127
+ return lines.join("\n");
128
+ }
113
129
  async function runEstate(args, io) {
114
130
  try {
115
131
  const flags = parseFlags(args);
@@ -136,6 +152,24 @@ async function runEstate(args, io) {
136
152
  io.out(formatMatches(flags.id, compareToEstate(fp, next, { exclude: flags.id, siblingAt: at }).slice(0, 3), at));
137
153
  return 0;
138
154
  }
155
+ if (sub === "compare" && flags.component) {
156
+ const role = componentRole(flags.component);
157
+ if (!target) {
158
+ const pairs2 = estateComponentPairs(register, role);
159
+ const measured = withRole(register, role);
160
+ io.out(flags.json ? toJson({ role, measured, sites: register.sites.length, pairs: pairs2 }) : formatComponentPairs(role, pairs2, measured, register.sites.length));
161
+ return 0;
162
+ }
163
+ const known = register.sites.find((s) => s.id === target);
164
+ const fp = known ? known.fingerprint : fingerprint(await snapshotOf(target, io.cwd));
165
+ if (!(fp.sections ?? []).some((x) => x.role === role)) throw new Error(`${known?.id ?? target} has no ${role} section measured (roles need snapshot version 2)`);
166
+ const pairs = register.sites.filter((s) => s.id !== known?.id).flatMap((s) => {
167
+ const c = closestComponents(fp, s.fingerprint, role);
168
+ return c ? [{ a: known?.id ?? target, b: s.id, ...c }] : [];
169
+ }).sort((x, y) => x.distance - y.distance);
170
+ io.out(flags.json ? toJson({ role, measured: withRole(register, role), sites: register.sites.length, pairs }) : formatComponentPairs(role, pairs, withRole(register, role), register.sites.length));
171
+ return 0;
172
+ }
139
173
  if (sub === "compare") {
140
174
  if (!target) {
141
175
  const pairs = estatePairs(register, at);
@@ -161,5 +195,6 @@ export {
161
195
  loadEstate,
162
196
  formatMatches,
163
197
  formatPairs,
198
+ formatComponentPairs,
164
199
  runEstate
165
200
  };
@@ -1,13 +1,11 @@
1
1
  import {
2
2
  decodePng
3
3
  } from "./chunk-JYNXGCYZ.js";
4
- import {
5
- fingerprint
6
- } from "./chunk-PZ4MF2VV.js";
7
4
  import {
8
5
  SNAPSHOT_VERSION,
9
- auditSnapshot
10
- } from "./chunk-AL6MRG6V.js";
6
+ auditSnapshot,
7
+ fingerprint
8
+ } from "./chunk-GMVXADJ6.js";
11
9
 
12
10
  // src/audit/collect.ts
13
11
  function collectInPage() {
@@ -277,22 +275,50 @@ function collectInPage() {
277
275
  if (cards >= 3 && iconCards >= 2) return "features";
278
276
  return kind;
279
277
  };
278
+ const partsOf = (el) => {
279
+ const inside = Array.from(el.querySelectorAll("*")).slice(0, 1500).filter(shown);
280
+ const badges = [];
281
+ for (const n of inside) {
282
+ const t = ownText(n);
283
+ if (t.length < 2 || t.length > 24 || badges.length >= 5) continue;
284
+ const cs = style(n);
285
+ const r = box(n);
286
+ const radius = px(cs.borderTopLeftRadius);
287
+ const drawn = painted(cs.backgroundColor) || px(cs.borderTopWidth) >= 1;
288
+ if (drawn && r.height <= 40 && r.width <= 240 && radius >= r.height / 3 && !isControl(n)) badges.push(t);
289
+ }
290
+ const avatars = inside.filter((n) => {
291
+ if (n.tagName !== "IMG") return false;
292
+ const r = box(n);
293
+ const rad = style(n).borderTopLeftRadius;
294
+ const round = rad.endsWith("%") ? parseFloat(rad) >= 40 : px(rad) >= r.width * 0.4;
295
+ return r.width <= 96 && r.width >= 16 && Math.abs(r.width - r.height) <= r.width * 0.15 && round;
296
+ }).length;
297
+ const carousel = inside.some((n) => {
298
+ const cs = style(n);
299
+ if (n.getAttribute("aria-roledescription") === "carousel" || cs.scrollSnapType && cs.scrollSnapType !== "none") return true;
300
+ if (/^(auto|scroll|hidden)$/.test(cs.overflowX) && n.children.length >= 3 && n.scrollWidth > n.clientWidth * 1.2) return true;
301
+ return /^(BUTTON|A)$/.test(n.tagName) && /\b(prev|previous|next)\b/i.test(n.getAttribute("aria-label") ?? "");
302
+ });
303
+ return { badges, avatars, carousel };
304
+ };
280
305
  const kept = blocks.slice(0, 40);
281
306
  const sections = kept.map((el, index) => {
282
307
  const b = box(el);
283
308
  const heading = el.querySelector("h1, h2, h3");
284
309
  const label = (heading?.textContent ?? el.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 60);
285
- const images = Array.from(el.querySelectorAll("img, svg")).filter((i) => shown(i) && box(i).height <= 90);
310
+ const images2 = Array.from(el.querySelectorAll("img, svg")).filter((i) => shown(i) && box(i).height <= 90);
286
311
  const textChars = (el.textContent ?? "").replace(/\s+/g, "").length;
287
312
  const numbers = Array.from(el.querySelectorAll("*")).filter((n) => /^[£$€]?\d[\d,.]*\s?(?:[%+kKmM★]|\/5|\+)?$/.test(ownText(n)) && px(style(n).fontSize) >= 24).length;
288
313
  const { cards, iconCards } = cardsIn(el);
289
314
  let kind = "text";
290
315
  if (marqueeTargets.some((t) => el.contains(t))) kind = "marquee";
291
316
  else if (h1 && el.contains(h1) || index === 0 && b.top < vh) kind = "hero";
292
- else if (images.length >= 4 && textChars < 300) kind = "logos";
317
+ else if (images2.length >= 4 && textChars < 300) kind = "logos";
293
318
  else if (numbers >= 3 && textChars < 600) kind = "stats";
294
319
  else if (cards >= 3) kind = "cards";
295
320
  const geometry = geometryOf(el, b);
321
+ const parts = partsOf(el);
296
322
  return {
297
323
  top: Math.round(b.top),
298
324
  height: Math.round(b.height),
@@ -302,7 +328,9 @@ function collectInPage() {
302
328
  iconCards,
303
329
  hiddenAtLoad: b.top > vh && hiddenAtLoad(el),
304
330
  role: roleOf(el, kind, geometry, b, cards, iconCards, index === kept.length - 1),
305
- geometry
331
+ geometry,
332
+ ...parts,
333
+ figures: numbers
306
334
  };
307
335
  });
308
336
  if (header && sections.length === 0) {
@@ -384,6 +412,81 @@ function collectInPage() {
384
412
  const width = para.getBoundingClientRect().width - px(cs.paddingLeft) - px(cs.paddingRight);
385
413
  if (avg > 0 && width > 0) bodyLineLengthCh = Math.round(width / avg);
386
414
  }
415
+ const unwrap = (raw) => {
416
+ if (raw.startsWith("data:")) return raw.slice(0, 64);
417
+ try {
418
+ const u = new URL(raw, location.href);
419
+ const inner = /\/_(?:next|vercel)\/image$/.test(u.pathname) ? u.searchParams.get("url") : null;
420
+ if (inner) return new URL(inner, location.href).href;
421
+ const cf = u.pathname.match(/^\/cdn-cgi\/image\/[^/]+\/(.+)$/);
422
+ if (cf) return /^https?:\/\//.test(cf[1]) ? cf[1] : new URL(`/${cf[1]}`, location.href).href;
423
+ return u.href;
424
+ } catch {
425
+ return raw.slice(0, 200);
426
+ }
427
+ };
428
+ const roundBox = (el, w) => {
429
+ const rad = style(el).borderTopLeftRadius;
430
+ return rad.endsWith("%") ? parseFloat(rad) >= 40 : px(rad) >= w * 0.4;
431
+ };
432
+ const sectionOf = (el) => {
433
+ const i = kept.findIndex((k) => k.contains(el));
434
+ return i === -1 ? null : i;
435
+ };
436
+ const roleOfImage = (el, src, w, h, alt) => {
437
+ const vector = /\.svg(?:[?#]|$)/i.test(src) || src.startsWith("data:image/svg");
438
+ const square = Math.abs(w - h) <= w * 0.15;
439
+ if (w >= 16 && w <= 96 && square && (roundBox(el, w) || el.parentElement !== null && roundBox(el.parentElement, w))) return "avatar";
440
+ const section = sectionOf(el);
441
+ const hint = `${alt ?? ""} ${src} ${typeof el.className === "string" ? el.className : ""}`;
442
+ if (/\blogo\b/i.test(hint) || section !== null && sections[section]?.kind === "logos" || el.closest("header, footer, nav") && h <= 120) return "logo";
443
+ if (Math.max(w, h) <= (vector ? 120 : 72)) return "icon";
444
+ if (vector) return "illustration";
445
+ return w >= 160 && h >= 120 ? "photo" : "icon";
446
+ };
447
+ const images = [];
448
+ const seenImage = /* @__PURE__ */ new Set();
449
+ const addImage = (el, raw, alt, decorative, background) => {
450
+ const b = box(el);
451
+ if (!raw || b.width < 8 || b.height < 8) return;
452
+ const src = unwrap(raw);
453
+ const key = `${src} ${Math.round(b.top)} ${Math.round(b.left)}`;
454
+ if (seenImage.has(key)) return;
455
+ seenImage.add(key);
456
+ let host = null;
457
+ try {
458
+ host = src.startsWith("data:") ? null : new URL(src).hostname;
459
+ } catch {
460
+ host = null;
461
+ }
462
+ images.push({
463
+ src,
464
+ host,
465
+ width: Math.round(b.width),
466
+ height: Math.round(b.height),
467
+ top: Math.round(b.top),
468
+ role: roleOfImage(el, src, b.width, b.height, alt),
469
+ alt,
470
+ decorative,
471
+ section: sectionOf(el),
472
+ background
473
+ });
474
+ };
475
+ for (const img of Array.from(document.querySelectorAll("img")).slice(0, 400)) {
476
+ if (!shown(img)) continue;
477
+ const alt = img.getAttribute("alt");
478
+ const decorative = alt === "" || img.getAttribute("aria-hidden") === "true" || /^(?:presentation|none)$/.test(img.getAttribute("role") ?? "");
479
+ addImage(img, img.currentSrc || img.getAttribute("src") || "", alt, decorative, false);
480
+ }
481
+ for (const el of all) {
482
+ const bgImage = style(el).backgroundImage || "";
483
+ if (!bgImage.includes("url(")) continue;
484
+ const b = box(el);
485
+ if (b.width < 160 || b.height < 120 || !shown(el)) continue;
486
+ const m = bgImage.match(/url\(\s*["']?([^"')]+)["']?\s*\)/);
487
+ if (m) addImage(el, m[1], null, true, true);
488
+ }
489
+ images.sort((a, b) => b.width * b.height - a.width * a.height);
387
490
  const toList = (m) => Array.from(m, ([value, v]) => ({ value, area: Math.round(v.area), chars: v.chars })).sort((a, b) => b.area - a.area || b.chars - a.chars);
388
491
  const nums = (s) => Array.from(s).sort((a, b) => a - b);
389
492
  return {
@@ -403,6 +506,7 @@ function collectInPage() {
403
506
  return mean > 0 ? Math.round(sd / mean * 1e3) / 1e3 : null;
404
507
  })(),
405
508
  effects,
509
+ images: images.slice(0, 60),
406
510
  motion: {
407
511
  hiddenSections: sections.filter((s) => s.hiddenAtLoad).length,
408
512
  sections: sections.length,
@@ -494,7 +598,60 @@ function visualMeasures(img, maxSamples = 1e6) {
494
598
  };
495
599
  }
496
600
 
601
+ // src/snapshot/provenance.ts
602
+ var PROVENANCE_SCAN_BYTES = 256 * 1024;
603
+ var AI_SOURCE_TYPES = ["compositeWithTrainedAlgorithmicMedia", "trainedAlgorithmicMedia"];
604
+ var MARKER = new RegExp(`(?:${AI_SOURCE_TYPES.join("|")})`);
605
+ var C2PA_BOX = /jumd[\s\S]{16,24}c2pa/;
606
+ var C2PA_PNG = /caBX/;
607
+ function latin1(bytes) {
608
+ let out = "";
609
+ for (let i = 0; i < bytes.length; i += 8192) {
610
+ out += String.fromCharCode(...bytes.subarray(i, Math.min(bytes.length, i + 8192)));
611
+ }
612
+ return out;
613
+ }
614
+ function xmpPackets(text) {
615
+ const out = [];
616
+ const open = /<x:xmpmeta\b|<\?xpacket begin/g;
617
+ let m;
618
+ while (m = open.exec(text)) {
619
+ const close = text.indexOf(m[0].startsWith("<x:") ? "</x:xmpmeta>" : "<?xpacket end", m.index);
620
+ out.push([m.index, close === -1 ? text.length : close]);
621
+ if (close === -1) break;
622
+ open.lastIndex = close;
623
+ }
624
+ return out;
625
+ }
626
+ function scanProvenance(bytes) {
627
+ const head = bytes.subarray(0, PROVENANCE_SCAN_BYTES);
628
+ const text = latin1(head);
629
+ const c2pa = C2PA_BOX.test(text) || text.startsWith("\x89PNG") && C2PA_PNG.test(text);
630
+ const m = MARKER.exec(text);
631
+ if (!m) return { digitalSourceType: null, source: null, c2pa, bytes: head.length };
632
+ const inXmp = xmpPackets(text).some(([a, b]) => m.index >= a && m.index < b);
633
+ return { digitalSourceType: m[0], source: inXmp ? "xmp" : c2pa ? "c2pa" : "other", c2pa, bytes: head.length };
634
+ }
635
+
497
636
  // src/audit/index.ts
637
+ var PROVENANCE_MAX_IMAGES = 24;
638
+ var PROVENANCE_BUDGET_MS = 2e4;
639
+ async function readProvenance(page, images, timeoutMs) {
640
+ if (!page.request) return;
641
+ const deadline = Date.now() + PROVENANCE_BUDGET_MS;
642
+ const wanted = [...new Set(images.filter((i) => i.role !== "icon" && i.role !== "logo" && /^https?:\/\//.test(i.src)).map((i) => i.src))].slice(0, PROVENANCE_MAX_IMAGES);
643
+ for (const src of wanted) {
644
+ const left = deadline - Date.now();
645
+ if (left < 500) break;
646
+ try {
647
+ const res = await page.request.get(src, { headers: { Range: `bytes=0-${PROVENANCE_SCAN_BYTES - 1}` }, timeout: Math.min(timeoutMs, left) });
648
+ if (!res.ok()) continue;
649
+ const provenance = scanProvenance(await res.body());
650
+ for (const image of images) if (image.src === src) image.provenance = provenance;
651
+ } catch {
652
+ }
653
+ }
654
+ }
498
655
  var VISUAL_MAX_HEIGHT = 6e3;
499
656
  async function loadPlaywright() {
500
657
  for (const name of ["playwright", "playwright-core"]) {
@@ -513,6 +670,7 @@ async function snapshotPage(page, url, options = {}) {
513
670
  await page.waitForTimeout(options.introWindowMs ?? 5e3);
514
671
  const late = await page.evaluate(overlayInPage).catch(() => false);
515
672
  const data = await page.evaluate(collectInPage);
673
+ if (data.images && options.provenance !== false) await readProvenance(page, data.images, 8e3);
516
674
  let visual;
517
675
  if (page.screenshot && options.visual !== false) {
518
676
  try {
@@ -578,6 +736,8 @@ async function audit(url, options = {}) {
578
736
  export {
579
737
  collectInPage,
580
738
  overlayInPage,
739
+ PROVENANCE_MAX_IMAGES,
740
+ PROVENANCE_BUDGET_MS,
581
741
  VISUAL_MAX_HEIGHT,
582
742
  loadPlaywright,
583
743
  snapshotPage,
@@ -6,7 +6,7 @@ import {
6
6
  isCream,
7
7
  normaliseFamily,
8
8
  parseColour
9
- } from "./chunk-AL6MRG6V.js";
9
+ } from "./chunk-GMVXADJ6.js";
10
10
 
11
11
  // src/direction/licences.ts
12
12
  var LICENCES = [
@@ -0,0 +1,18 @@
1
+ import {
2
+ formatComponentPairs,
3
+ formatMatches,
4
+ formatPairs,
5
+ loadEstate,
6
+ runEstate
7
+ } from "./chunk-K2BZN4GA.js";
8
+ import "./chunk-ESCVTKPN.js";
9
+ import "./chunk-CZDOMIVS.js";
10
+ import "./chunk-3VTPI7BU.js";
11
+ import "./chunk-GMVXADJ6.js";
12
+ export {
13
+ formatComponentPairs,
14
+ formatMatches,
15
+ formatPairs,
16
+ loadEstate,
17
+ runEstate
18
+ };
@@ -1,29 +1,27 @@
1
1
  import {
2
2
  DIRECTION_VERSION,
3
3
  validateDirection
4
- } from "./chunk-S3NZAVU6.js";
4
+ } from "./chunk-ZZ5CKOJP.js";
5
5
  import {
6
6
  decodePng
7
7
  } from "./chunk-JYNXGCYZ.js";
8
8
  import {
9
9
  readSnapshot
10
- } from "./chunk-ZKVJ7K6X.js";
10
+ } from "./chunk-CZDOMIVS.js";
11
11
  import {
12
12
  parseFlags,
13
13
  toJson
14
- } from "./chunk-J3QUROJ5.js";
15
- import {
16
- fingerprint
17
- } from "./chunk-PZ4MF2VV.js";
14
+ } from "./chunk-3VTPI7BU.js";
18
15
  import {
19
16
  deltaEOk,
17
+ fingerprint,
20
18
  formatHex,
21
19
  hexToOklch,
22
20
  isAiViolet,
23
21
  isCream,
24
22
  oklchToRgb,
25
23
  parseColour
26
- } from "./chunk-AL6MRG6V.js";
24
+ } from "./chunk-GMVXADJ6.js";
27
25
 
28
26
  // src/direction/cli.ts
29
27
  import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs";
@@ -6,11 +6,10 @@ import {
6
6
  nullPath,
7
7
  runHarvest,
8
8
  runNull
9
- } from "./chunk-LHGY43LL.js";
10
- import "./chunk-ZKVJ7K6X.js";
11
- import "./chunk-J3QUROJ5.js";
12
- import "./chunk-PZ4MF2VV.js";
13
- import "./chunk-AL6MRG6V.js";
9
+ } from "./chunk-ESCVTKPN.js";
10
+ import "./chunk-CZDOMIVS.js";
11
+ import "./chunk-3VTPI7BU.js";
12
+ import "./chunk-GMVXADJ6.js";
14
13
  export {
15
14
  describeTypicality,
16
15
  formatHarvest,
@@ -1,23 +1,22 @@
1
1
  import {
2
2
  validateDirection
3
- } from "./chunk-S3NZAVU6.js";
3
+ } from "./chunk-ZZ5CKOJP.js";
4
4
  import {
5
5
  merge
6
- } from "./chunk-PU5G5DUC.js";
7
- import "./chunk-ULPI335S.js";
6
+ } from "./chunk-IMB4V34S.js";
7
+ import "./chunk-ZLDA2SYB.js";
8
8
  import "./chunk-JYNXGCYZ.js";
9
9
  import {
10
10
  SIBLING_AT,
11
11
  compareToEstate,
12
12
  loadEstate
13
- } from "./chunk-CDAJUS5U.js";
13
+ } from "./chunk-K2BZN4GA.js";
14
14
  import {
15
- loadNull,
16
- typicality
17
- } from "./chunk-LHGY43LL.js";
15
+ loadNull
16
+ } from "./chunk-ESCVTKPN.js";
18
17
  import {
19
18
  readSnapshot
20
- } from "./chunk-ZKVJ7K6X.js";
19
+ } from "./chunk-CZDOMIVS.js";
21
20
  import {
22
21
  COPY_FILE,
23
22
  NOT_COPY_DIR,
@@ -28,16 +27,15 @@ import {
28
27
  parseFlags,
29
28
  readPaths,
30
29
  toJson
31
- } from "./chunk-J3QUROJ5.js";
32
- import {
33
- fingerprint
34
- } from "./chunk-PZ4MF2VV.js";
30
+ } from "./chunk-3VTPI7BU.js";
35
31
  import {
36
32
  CATALOGUE,
37
33
  auditSnapshot,
38
34
  checkCopy,
39
- scanSource
40
- } from "./chunk-AL6MRG6V.js";
35
+ fingerprint,
36
+ scanSource,
37
+ typicality
38
+ } from "./chunk-GMVXADJ6.js";
41
39
 
42
40
  // src/report/cli.ts
43
41
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -264,7 +262,7 @@ async function runReport(command, args, io) {
264
262
  if (/\.json$/i.test(target2) && existsSync(local)) {
265
263
  snap = readSnapshot(JSON.parse(readFileSync(local, "utf8")), target2);
266
264
  } else {
267
- const { snapshotUrl } = await import("./audit-5QWFLZ6L.js");
265
+ const { snapshotUrl } = await import("./audit-5TYDYRXV.js");
268
266
  snap = await snapshotUrl(target2);
269
267
  }
270
268
  const fp = fingerprint(snap);
@@ -0,0 +1,20 @@
1
+ import {
2
+ describeComponents,
3
+ describeFingerprint,
4
+ merge,
5
+ resolvePages,
6
+ runAudit
7
+ } from "./chunk-IMB4V34S.js";
8
+ import "./chunk-ZLDA2SYB.js";
9
+ import "./chunk-JYNXGCYZ.js";
10
+ import "./chunk-ESCVTKPN.js";
11
+ import "./chunk-CZDOMIVS.js";
12
+ import "./chunk-3VTPI7BU.js";
13
+ import "./chunk-GMVXADJ6.js";
14
+ export {
15
+ describeComponents,
16
+ describeFingerprint,
17
+ merge,
18
+ resolvePages,
19
+ runAudit
20
+ };
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-J3QUROJ5.js";
5
- import "./chunk-AL6MRG6V.js";
4
+ } from "./chunk-3VTPI7BU.js";
5
+ import "./chunk-GMVXADJ6.js";
6
6
 
7
7
  // src/cli.ts
8
8
  process.stdout.on("error", (error) => {