@domandigital/craft 0.11.0 → 0.12.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 +16 -0
- package/CHARACTER.md +9 -1
- package/COPY.md +6 -2
- package/README.md +5 -0
- package/dist/{audit-QIPXL7E4.js → audit-5QWFLZ6L.js} +6 -3
- package/dist/audit.cjs +276 -15
- package/dist/audit.d.cts +81 -4
- package/dist/audit.d.ts +81 -4
- package/dist/audit.js +275 -15
- package/dist/{chunk-3NGWXZZE.js → chunk-AL6MRG6V.js} +40 -3
- package/dist/{chunk-HOHHQB4N.js → chunk-CDAJUS5U.js} +9 -8
- package/dist/{chunk-2AJB27BJ.js → chunk-J3QUROJ5.js} +24 -10
- package/dist/chunk-JYNXGCYZ.js +75 -0
- package/dist/{chunk-3OZQ4C5R.js → chunk-LHGY43LL.js} +8 -5
- package/dist/{chunk-MSQ57PJU.js → chunk-PU5G5DUC.js} +11 -11
- package/dist/{chunk-QMYERCBB.js → chunk-PZ4MF2VV.js} +19 -7
- package/dist/{chunk-JWHC67ZS.js → chunk-S3NZAVU6.js} +121 -1
- package/dist/{chunk-JP75P2DU.js → chunk-ULPI335S.js} +178 -12
- package/dist/chunk-ZKVJ7K6X.js +19 -0
- package/dist/cli-2DY3V3SF.js +17 -0
- package/dist/{cli-4ESXOLKV.js → cli-C3JLFXMG.js} +5 -4
- package/dist/cli-QM2AQQMF.js +19 -0
- package/dist/{cli-DBZMVYYV.js → cli-ZB2VJ7CY.js} +14 -12
- package/dist/{cli-U3SZVHSK.js → cli-ZPJIWVEP.js} +12 -78
- package/dist/cli.js +2 -2
- package/dist/index.cjs +270 -9
- package/dist/index.d.cts +180 -6
- package/dist/index.d.ts +180 -6
- package/dist/index.js +260 -9
- package/licences.json +104 -0
- package/package.json +3 -2
- package/word-lists.json +1 -1
- package/dist/cli-T5RD2BTK.js +0 -17
- package/dist/cli-VREYWCT4.js +0 -16
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodePng
|
|
3
|
+
} from "./chunk-JYNXGCYZ.js";
|
|
1
4
|
import {
|
|
2
5
|
fingerprint
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PZ4MF2VV.js";
|
|
4
7
|
import {
|
|
5
8
|
SNAPSHOT_VERSION,
|
|
6
9
|
auditSnapshot
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AL6MRG6V.js";
|
|
8
11
|
|
|
9
12
|
// src/audit/collect.ts
|
|
10
13
|
function collectInPage() {
|
|
@@ -103,18 +106,26 @@ function collectInPage() {
|
|
|
103
106
|
top: Math.round(box(h).top)
|
|
104
107
|
};
|
|
105
108
|
});
|
|
109
|
+
const isButtonEl = (el) => el.tagName === "BUTTON" || el.tagName === "INPUT" && /^(submit|button)$/i.test(el.type);
|
|
110
|
+
const isControl = (el) => {
|
|
111
|
+
const isButton = isButtonEl(el);
|
|
112
|
+
if (!isButton && el.tagName !== "A") return false;
|
|
113
|
+
if (!shown(el)) return false;
|
|
114
|
+
const cs = style(el);
|
|
115
|
+
const b = box(el);
|
|
116
|
+
const label = (el.textContent ?? el.value ?? "").replace(/\s+/g, " ").trim();
|
|
117
|
+
if (!label || b.height < 24 || b.height > 90) return false;
|
|
118
|
+
if (!isButton && !(painted(cs.backgroundColor) || px(cs.borderTopWidth) >= 1)) return false;
|
|
119
|
+
if (!isButton && px(cs.paddingLeft) < 8) return false;
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
106
122
|
const controls = [];
|
|
107
123
|
for (const el of all) {
|
|
108
|
-
|
|
109
|
-
const isButton =
|
|
110
|
-
if (!isButton && tag !== "A") continue;
|
|
111
|
-
if (!shown(el)) continue;
|
|
124
|
+
if (!isControl(el)) continue;
|
|
125
|
+
const isButton = isButtonEl(el);
|
|
112
126
|
const cs = style(el);
|
|
113
127
|
const b = box(el);
|
|
114
128
|
const label = (el.textContent ?? el.value ?? "").replace(/\s+/g, " ").trim();
|
|
115
|
-
if (!label || b.height < 24 || b.height > 90) continue;
|
|
116
|
-
if (!isButton && !(painted(cs.backgroundColor) || px(cs.borderTopWidth) >= 1)) continue;
|
|
117
|
-
if (!isButton && px(cs.paddingLeft) < 8) continue;
|
|
118
129
|
const r = cs.borderTopLeftRadius;
|
|
119
130
|
const radius = r.endsWith("%") ? parseFloat(r) / 100 * b.height : px(r);
|
|
120
131
|
controls.push({
|
|
@@ -194,7 +205,80 @@ function collectInPage() {
|
|
|
194
205
|
}
|
|
195
206
|
return best;
|
|
196
207
|
};
|
|
197
|
-
const
|
|
208
|
+
const backgroundOf = (el) => {
|
|
209
|
+
for (let n = el; n && n !== document.documentElement; n = n.parentElement) {
|
|
210
|
+
const c = style(n).backgroundColor;
|
|
211
|
+
if (painted(c)) return c;
|
|
212
|
+
}
|
|
213
|
+
return ground;
|
|
214
|
+
};
|
|
215
|
+
const geometryOf = (el, b) => {
|
|
216
|
+
const leaves = Array.from(el.querySelectorAll("*")).slice(0, 1500).filter((n) => (ownText(n).length > 1 || /^(IMG|SVG|VIDEO|PICTURE|BUTTON|INPUT|TEXTAREA|SELECT)$/i.test(n.tagName)) && shown(n)).slice(0, 400);
|
|
217
|
+
const centreX = b.left + b.width / 2;
|
|
218
|
+
let left = 0;
|
|
219
|
+
let right = 0;
|
|
220
|
+
let covered = 0;
|
|
221
|
+
let minX = Infinity;
|
|
222
|
+
let maxX = -Infinity;
|
|
223
|
+
let textBlocks = 0;
|
|
224
|
+
let centred = 0;
|
|
225
|
+
for (const n of leaves) {
|
|
226
|
+
const r = box(n);
|
|
227
|
+
const area = r.width * r.height;
|
|
228
|
+
covered += area;
|
|
229
|
+
minX = Math.min(minX, r.left);
|
|
230
|
+
maxX = Math.max(maxX, r.left + r.width);
|
|
231
|
+
const split = Math.max(0, Math.min(r.width, centreX - r.left));
|
|
232
|
+
left += split * r.height;
|
|
233
|
+
right += (r.width - split) * r.height;
|
|
234
|
+
if (ownText(n).length > 1) {
|
|
235
|
+
textBlocks += 1;
|
|
236
|
+
const mid = r.left + r.width / 2;
|
|
237
|
+
if (style(n).textAlign === "center" || Math.abs(mid - centreX) < vw * 0.03 && r.width < b.width * 0.8) centred += 1;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const round = (v) => Math.round(v * 1e3) / 1e3;
|
|
241
|
+
return {
|
|
242
|
+
centredShare: textBlocks ? round(centred / textBlocks) : 0,
|
|
243
|
+
mirrorSymmetry: left + right > 0 ? round(1 - Math.abs(left - right) / (left + right)) : 1,
|
|
244
|
+
whitespaceRatio: round(Math.max(0, 1 - Math.min(1, covered / Math.max(1, b.width * b.height)))),
|
|
245
|
+
contentWidthRatio: leaves.length ? round(Math.min(1, (maxX - minX) / vw)) : 0,
|
|
246
|
+
background: backgroundOf(el),
|
|
247
|
+
controls: Array.from(el.querySelectorAll("a, button, input")).slice(0, 200).filter(isControl).length
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
const CURRENCY = /[£$€]\s?\d/;
|
|
251
|
+
const roleOf = (el, kind, geo, b, cards, iconCards, last) => {
|
|
252
|
+
if (kind === "marquee" || kind === "hero") return kind;
|
|
253
|
+
const heading = (el.querySelector("h1, h2, h3")?.textContent ?? "").toLowerCase();
|
|
254
|
+
const leaves = Array.from(el.querySelectorAll("*")).slice(0, 1500);
|
|
255
|
+
const own = leaves.map(ownText);
|
|
256
|
+
const questions = own.filter((t) => /\?$/.test(t) && t.length > 8).length;
|
|
257
|
+
if (el.querySelectorAll("details").length >= 3 || el.querySelectorAll("[aria-expanded]").length >= 3 || /\bfaq|frequently asked|questions\b/.test(heading) && questions >= 3) return "faq";
|
|
258
|
+
const prices = own.filter((t) => CURRENCY.test(t) && t.length < 40).length;
|
|
259
|
+
if (prices >= 2 && (cards >= 2 || /pric|plan|package/.test(heading))) return "pricing";
|
|
260
|
+
const quotes = el.querySelectorAll("blockquote, q").length;
|
|
261
|
+
if (quotes >= 2 || /testimonial|review|what (?:our )?(?:clients|customers|people) say/.test(heading)) return "testimonials";
|
|
262
|
+
const steps = Math.max(0, ...Array.from(el.querySelectorAll("ol")).map((ol) => ol.children.length));
|
|
263
|
+
const numbered = own.filter((t) => /^(?:step\s*)?0?[1-9][.:]?$/i.test(t)).length;
|
|
264
|
+
if (steps >= 3 || numbered >= 3 || /how it works|our process|\bsteps?\b/.test(heading)) return "process";
|
|
265
|
+
const form = el.querySelector("form");
|
|
266
|
+
const fields = form ? form.querySelectorAll("input:not([type=hidden]):not([type=submit]), textarea, select").length : 0;
|
|
267
|
+
if (fields >= 2 || /contact|get in touch/.test(heading)) return "contact";
|
|
268
|
+
const portraits = Array.from(el.querySelectorAll("img")).filter((i) => {
|
|
269
|
+
const r = box(i);
|
|
270
|
+
return r.width >= 80 && r.width <= 420 && Math.abs(r.width - r.height) < r.width * 0.25;
|
|
271
|
+
}).length;
|
|
272
|
+
if (portraits >= 3 && /team|people|meet|who we are/.test(heading)) return "team";
|
|
273
|
+
if (kind === "stats" || kind === "logos") return kind;
|
|
274
|
+
const words = (el.textContent ?? "").replace(/\s+/g, " ").trim().length;
|
|
275
|
+
const setApart = geo.background !== ground || geo.centredShare >= 0.6;
|
|
276
|
+
if (setApart && heading && b.height <= vh * 0.9 && geo.controls >= 1 && geo.controls <= 2 && words < 400 && cards < 3) return last ? "footer-cta" : "cta-band";
|
|
277
|
+
if (cards >= 3 && iconCards >= 2) return "features";
|
|
278
|
+
return kind;
|
|
279
|
+
};
|
|
280
|
+
const kept = blocks.slice(0, 40);
|
|
281
|
+
const sections = kept.map((el, index) => {
|
|
198
282
|
const b = box(el);
|
|
199
283
|
const heading = el.querySelector("h1, h2, h3");
|
|
200
284
|
const label = (heading?.textContent ?? el.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 60);
|
|
@@ -208,6 +292,7 @@ function collectInPage() {
|
|
|
208
292
|
else if (images.length >= 4 && textChars < 300) kind = "logos";
|
|
209
293
|
else if (numbers >= 3 && textChars < 600) kind = "stats";
|
|
210
294
|
else if (cards >= 3) kind = "cards";
|
|
295
|
+
const geometry = geometryOf(el, b);
|
|
211
296
|
return {
|
|
212
297
|
top: Math.round(b.top),
|
|
213
298
|
height: Math.round(b.height),
|
|
@@ -215,7 +300,9 @@ function collectInPage() {
|
|
|
215
300
|
label,
|
|
216
301
|
cards,
|
|
217
302
|
iconCards,
|
|
218
|
-
hiddenAtLoad: b.top > vh && hiddenAtLoad(el)
|
|
303
|
+
hiddenAtLoad: b.top > vh && hiddenAtLoad(el),
|
|
304
|
+
role: roleOf(el, kind, geometry, b, cards, iconCards, index === kept.length - 1),
|
|
305
|
+
geometry
|
|
219
306
|
};
|
|
220
307
|
});
|
|
221
308
|
if (header && sections.length === 0) {
|
|
@@ -308,6 +395,13 @@ function collectInPage() {
|
|
|
308
395
|
headings,
|
|
309
396
|
controls,
|
|
310
397
|
sections,
|
|
398
|
+
rhythmVariance: (() => {
|
|
399
|
+
const hs = sections.map((x) => x.height);
|
|
400
|
+
if (hs.length < 2) return null;
|
|
401
|
+
const mean = hs.reduce((t, v) => t + v, 0) / hs.length;
|
|
402
|
+
const sd = Math.sqrt(hs.reduce((t, v) => t + (v - mean) ** 2, 0) / hs.length);
|
|
403
|
+
return mean > 0 ? Math.round(sd / mean * 1e3) / 1e3 : null;
|
|
404
|
+
})(),
|
|
311
405
|
effects,
|
|
312
406
|
motion: {
|
|
313
407
|
hiddenSections: sections.filter((s) => s.hiddenAtLoad).length,
|
|
@@ -341,7 +435,67 @@ function overlayInPage() {
|
|
|
341
435
|
return false;
|
|
342
436
|
}
|
|
343
437
|
|
|
438
|
+
// src/snapshot/visual.ts
|
|
439
|
+
var EDGE_STEP = 32;
|
|
440
|
+
function visualMeasures(img, maxSamples = 1e6) {
|
|
441
|
+
const { width: w, height: h, rgba } = img;
|
|
442
|
+
if (w < 2 || h < 2 || rgba.length < w * h * 4) throw new Error(`visualMeasures: ${w}x${h} image with ${rgba.length} bytes`);
|
|
443
|
+
const step = Math.max(1, Math.ceil(Math.sqrt(w * h / maxSamples)));
|
|
444
|
+
const lum = (x, y) => {
|
|
445
|
+
const i = (y * w + x) * 4;
|
|
446
|
+
return 0.2126 * rgba[i] + 0.7152 * rgba[i + 1] + 0.0722 * rgba[i + 2];
|
|
447
|
+
};
|
|
448
|
+
let n = 0;
|
|
449
|
+
let sumRg = 0;
|
|
450
|
+
let sumYb = 0;
|
|
451
|
+
let sumRg2 = 0;
|
|
452
|
+
let sumYb2 = 0;
|
|
453
|
+
let edges = 0;
|
|
454
|
+
let edgeN = 0;
|
|
455
|
+
let asym = 0;
|
|
456
|
+
let asymN = 0;
|
|
457
|
+
for (let y = 0; y < h; y += step) {
|
|
458
|
+
for (let x = 0; x < w; x += step) {
|
|
459
|
+
const i = (y * w + x) * 4;
|
|
460
|
+
const r = rgba[i];
|
|
461
|
+
const g = rgba[i + 1];
|
|
462
|
+
const b = rgba[i + 2];
|
|
463
|
+
const rg = r - g;
|
|
464
|
+
const yb = 0.5 * (r + g) - b;
|
|
465
|
+
sumRg += rg;
|
|
466
|
+
sumYb += yb;
|
|
467
|
+
sumRg2 += rg * rg;
|
|
468
|
+
sumYb2 += yb * yb;
|
|
469
|
+
n += 1;
|
|
470
|
+
const l = lum(x, y);
|
|
471
|
+
if (x + step < w && y + step < h) {
|
|
472
|
+
if (Math.abs(lum(x + step, y) - l) + Math.abs(lum(x, y + step) - l) > EDGE_STEP) edges += 1;
|
|
473
|
+
edgeN += 1;
|
|
474
|
+
}
|
|
475
|
+
if (x < w / 2) {
|
|
476
|
+
asym += Math.abs(l - lum(w - 1 - x, y));
|
|
477
|
+
asymN += 1;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const meanRg = sumRg / n;
|
|
482
|
+
const meanYb = sumYb / n;
|
|
483
|
+
const sdRg = Math.sqrt(Math.max(0, sumRg2 / n - meanRg * meanRg));
|
|
484
|
+
const sdYb = Math.sqrt(Math.max(0, sumYb2 / n - meanYb * meanYb));
|
|
485
|
+
const colourfulness = Math.sqrt(sdRg ** 2 + sdYb ** 2) + 0.3 * Math.sqrt(meanRg ** 2 + meanYb ** 2);
|
|
486
|
+
const round = (v, d = 3) => Math.round(v * 10 ** d) / 10 ** d;
|
|
487
|
+
return {
|
|
488
|
+
colourfulness: round(colourfulness, 1),
|
|
489
|
+
edgeDensity: round(edgeN ? edges / edgeN : 0),
|
|
490
|
+
symmetry: round(asymN ? 1 - asym / asymN / 255 : 1),
|
|
491
|
+
width: w,
|
|
492
|
+
height: h,
|
|
493
|
+
sampled: n
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
344
497
|
// src/audit/index.ts
|
|
498
|
+
var VISUAL_MAX_HEIGHT = 6e3;
|
|
345
499
|
async function loadPlaywright() {
|
|
346
500
|
for (const name of ["playwright", "playwright-core"]) {
|
|
347
501
|
try {
|
|
@@ -359,12 +513,23 @@ async function snapshotPage(page, url, options = {}) {
|
|
|
359
513
|
await page.waitForTimeout(options.introWindowMs ?? 5e3);
|
|
360
514
|
const late = await page.evaluate(overlayInPage).catch(() => false);
|
|
361
515
|
const data = await page.evaluate(collectInPage);
|
|
516
|
+
let visual;
|
|
517
|
+
if (page.screenshot && options.visual !== false) {
|
|
518
|
+
try {
|
|
519
|
+
const height = Math.min(data.pageHeight, VISUAL_MAX_HEIGHT);
|
|
520
|
+
const png = await page.screenshot({ fullPage: true, type: "png", clip: { x: 0, y: 0, width: data.viewport.width, height } });
|
|
521
|
+
visual = visualMeasures(decodePng(png));
|
|
522
|
+
} catch {
|
|
523
|
+
visual = void 0;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
362
526
|
return {
|
|
363
527
|
version: SNAPSHOT_VERSION,
|
|
364
528
|
url,
|
|
365
529
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
366
530
|
...data,
|
|
367
|
-
motion: { ...data.motion, introOverlay: early && !late }
|
|
531
|
+
motion: { ...data.motion, introOverlay: early && !late },
|
|
532
|
+
...visual ? { visual } : {}
|
|
368
533
|
};
|
|
369
534
|
}
|
|
370
535
|
async function launch(pw, url, options) {
|
|
@@ -413,6 +578,7 @@ async function audit(url, options = {}) {
|
|
|
413
578
|
export {
|
|
414
579
|
collectInPage,
|
|
415
580
|
overlayInPage,
|
|
581
|
+
VISUAL_MAX_HEIGHT,
|
|
416
582
|
loadPlaywright,
|
|
417
583
|
snapshotPage,
|
|
418
584
|
snapshotUrl,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SNAPSHOT_VERSION
|
|
3
|
+
} from "./chunk-AL6MRG6V.js";
|
|
4
|
+
|
|
5
|
+
// src/snapshot/migrate.ts
|
|
6
|
+
var READABLE_SNAPSHOT_VERSIONS = [1, 2];
|
|
7
|
+
function readSnapshot(data, source = "snapshot") {
|
|
8
|
+
const v = data?.version;
|
|
9
|
+
if (typeof data !== "object" || data === null || !READABLE_SNAPSHOT_VERSIONS.includes(v)) {
|
|
10
|
+
throw new Error(`${source} is not a snapshot this craft reads (version ${String(v)}; reads ${READABLE_SNAPSHOT_VERSIONS.join(" and ")})`);
|
|
11
|
+
}
|
|
12
|
+
const snap = data;
|
|
13
|
+
if (snap.version === 1) return { ...snap, version: SNAPSHOT_VERSION, migratedFrom: 1 };
|
|
14
|
+
return snap;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
readSnapshot
|
|
19
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatMatches,
|
|
3
|
+
formatPairs,
|
|
4
|
+
loadEstate,
|
|
5
|
+
runEstate
|
|
6
|
+
} from "./chunk-CDAJUS5U.js";
|
|
7
|
+
import "./chunk-LHGY43LL.js";
|
|
8
|
+
import "./chunk-ZKVJ7K6X.js";
|
|
9
|
+
import "./chunk-J3QUROJ5.js";
|
|
10
|
+
import "./chunk-PZ4MF2VV.js";
|
|
11
|
+
import "./chunk-AL6MRG6V.js";
|
|
12
|
+
export {
|
|
13
|
+
formatMatches,
|
|
14
|
+
formatPairs,
|
|
15
|
+
loadEstate,
|
|
16
|
+
runEstate
|
|
17
|
+
};
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
nullPath,
|
|
7
7
|
runHarvest,
|
|
8
8
|
runNull
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
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";
|
|
13
14
|
export {
|
|
14
15
|
describeTypicality,
|
|
15
16
|
formatHarvest,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describeFingerprint,
|
|
3
|
+
merge,
|
|
4
|
+
resolvePages,
|
|
5
|
+
runAudit
|
|
6
|
+
} from "./chunk-PU5G5DUC.js";
|
|
7
|
+
import "./chunk-ULPI335S.js";
|
|
8
|
+
import "./chunk-JYNXGCYZ.js";
|
|
9
|
+
import "./chunk-LHGY43LL.js";
|
|
10
|
+
import "./chunk-ZKVJ7K6X.js";
|
|
11
|
+
import "./chunk-J3QUROJ5.js";
|
|
12
|
+
import "./chunk-PZ4MF2VV.js";
|
|
13
|
+
import "./chunk-AL6MRG6V.js";
|
|
14
|
+
export {
|
|
15
|
+
describeFingerprint,
|
|
16
|
+
merge,
|
|
17
|
+
resolvePages,
|
|
18
|
+
runAudit
|
|
19
|
+
};
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateDirection
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-S3NZAVU6.js";
|
|
4
4
|
import {
|
|
5
5
|
merge
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-PU5G5DUC.js";
|
|
7
|
+
import "./chunk-ULPI335S.js";
|
|
8
|
+
import "./chunk-JYNXGCYZ.js";
|
|
8
9
|
import {
|
|
9
10
|
SIBLING_AT,
|
|
10
11
|
compareToEstate,
|
|
11
12
|
loadEstate
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CDAJUS5U.js";
|
|
13
14
|
import {
|
|
14
15
|
loadNull,
|
|
15
16
|
typicality
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-LHGY43LL.js";
|
|
18
|
+
import {
|
|
19
|
+
readSnapshot
|
|
20
|
+
} from "./chunk-ZKVJ7K6X.js";
|
|
17
21
|
import {
|
|
18
22
|
COPY_FILE,
|
|
19
23
|
NOT_COPY_DIR,
|
|
@@ -24,17 +28,16 @@ import {
|
|
|
24
28
|
parseFlags,
|
|
25
29
|
readPaths,
|
|
26
30
|
toJson
|
|
27
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-J3QUROJ5.js";
|
|
28
32
|
import {
|
|
29
33
|
fingerprint
|
|
30
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-PZ4MF2VV.js";
|
|
31
35
|
import {
|
|
32
36
|
CATALOGUE,
|
|
33
|
-
SNAPSHOT_VERSION,
|
|
34
37
|
auditSnapshot,
|
|
35
38
|
checkCopy,
|
|
36
39
|
scanSource
|
|
37
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-AL6MRG6V.js";
|
|
38
41
|
|
|
39
42
|
// src/report/cli.ts
|
|
40
43
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -259,10 +262,9 @@ async function runReport(command, args, io) {
|
|
|
259
262
|
const local = resolve(io.cwd, target2);
|
|
260
263
|
let snap;
|
|
261
264
|
if (/\.json$/i.test(target2) && existsSync(local)) {
|
|
262
|
-
snap = JSON.parse(readFileSync(local, "utf8"));
|
|
263
|
-
if (snap.version !== SNAPSHOT_VERSION) throw new Error(`${target2} is not a snapshot`);
|
|
265
|
+
snap = readSnapshot(JSON.parse(readFileSync(local, "utf8")), target2);
|
|
264
266
|
} else {
|
|
265
|
-
const { snapshotUrl } = await import("./audit-
|
|
267
|
+
const { snapshotUrl } = await import("./audit-5QWFLZ6L.js");
|
|
266
268
|
snap = await snapshotUrl(target2);
|
|
267
269
|
}
|
|
268
270
|
const fp = fingerprint(snap);
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DIRECTION_VERSION,
|
|
3
3
|
validateDirection
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-S3NZAVU6.js";
|
|
5
|
+
import {
|
|
6
|
+
decodePng
|
|
7
|
+
} from "./chunk-JYNXGCYZ.js";
|
|
8
|
+
import {
|
|
9
|
+
readSnapshot
|
|
10
|
+
} from "./chunk-ZKVJ7K6X.js";
|
|
5
11
|
import {
|
|
6
12
|
parseFlags,
|
|
7
13
|
toJson
|
|
8
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-J3QUROJ5.js";
|
|
9
15
|
import {
|
|
10
16
|
fingerprint
|
|
11
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-PZ4MF2VV.js";
|
|
12
18
|
import {
|
|
13
19
|
deltaEOk,
|
|
14
20
|
formatHex,
|
|
@@ -17,7 +23,7 @@ import {
|
|
|
17
23
|
isCream,
|
|
18
24
|
oklchToRgb,
|
|
19
25
|
parseColour
|
|
20
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-AL6MRG6V.js";
|
|
21
27
|
|
|
22
28
|
// src/direction/cli.ts
|
|
23
29
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -50,78 +56,6 @@ function initDirection(input) {
|
|
|
50
56
|
};
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
// src/direction/png.ts
|
|
54
|
-
import { inflateSync } from "zlib";
|
|
55
|
-
function decodePng(buf) {
|
|
56
|
-
const sig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
57
|
-
if (!sig.every((b, i) => buf[i] === b)) throw new Error("not a PNG");
|
|
58
|
-
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
59
|
-
let pos = 8;
|
|
60
|
-
let width = 0;
|
|
61
|
-
let height = 0;
|
|
62
|
-
let depth = 0;
|
|
63
|
-
let colour = 0;
|
|
64
|
-
let interlace = 0;
|
|
65
|
-
const idat = [];
|
|
66
|
-
while (pos < buf.length) {
|
|
67
|
-
const len = view.getUint32(pos);
|
|
68
|
-
const type = String.fromCharCode(...buf.subarray(pos + 4, pos + 8));
|
|
69
|
-
const data = buf.subarray(pos + 8, pos + 8 + len);
|
|
70
|
-
if (type === "IHDR") {
|
|
71
|
-
width = view.getUint32(pos + 8);
|
|
72
|
-
height = view.getUint32(pos + 12);
|
|
73
|
-
depth = data[8];
|
|
74
|
-
colour = data[9];
|
|
75
|
-
interlace = data[12];
|
|
76
|
-
} else if (type === "IDAT") idat.push(data);
|
|
77
|
-
else if (type === "IEND") break;
|
|
78
|
-
pos += 12 + len;
|
|
79
|
-
}
|
|
80
|
-
const channels = { 0: 1, 2: 3, 4: 2, 6: 4 }[colour];
|
|
81
|
-
if (depth !== 8 || !channels || interlace !== 0) throw new Error(`unsupported PNG (depth ${depth}, colour type ${colour}${interlace ? ", interlaced" : ""}); export it as 8-bit RGB`);
|
|
82
|
-
const raw = inflateSync(Buffer.concat(idat.map((d) => Buffer.from(d))));
|
|
83
|
-
const stride = width * channels;
|
|
84
|
-
const out = new Uint8Array(width * height * 4);
|
|
85
|
-
const prev = new Uint8Array(stride);
|
|
86
|
-
const line = new Uint8Array(stride);
|
|
87
|
-
for (let y = 0; y < height; y += 1) {
|
|
88
|
-
const filter = raw[y * (stride + 1)];
|
|
89
|
-
const src = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
|
|
90
|
-
for (let x = 0; x < stride; x += 1) {
|
|
91
|
-
const a = x >= channels ? line[x - channels] : 0;
|
|
92
|
-
const b = prev[x];
|
|
93
|
-
const c = x >= channels ? prev[x - channels] : 0;
|
|
94
|
-
let v = src[x];
|
|
95
|
-
if (filter === 1) v += a;
|
|
96
|
-
else if (filter === 2) v += b;
|
|
97
|
-
else if (filter === 3) v += a + b >> 1;
|
|
98
|
-
else if (filter === 4) {
|
|
99
|
-
const p = a + b - c;
|
|
100
|
-
const pa = Math.abs(p - a);
|
|
101
|
-
const pb = Math.abs(p - b);
|
|
102
|
-
const pc = Math.abs(p - c);
|
|
103
|
-
v += pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
|
|
104
|
-
}
|
|
105
|
-
line[x] = v & 255;
|
|
106
|
-
}
|
|
107
|
-
for (let x = 0; x < width; x += 1) {
|
|
108
|
-
const i = x * channels;
|
|
109
|
-
const o = (y * width + x) * 4;
|
|
110
|
-
if (channels >= 3) {
|
|
111
|
-
out[o] = line[i];
|
|
112
|
-
out[o + 1] = line[i + 1];
|
|
113
|
-
out[o + 2] = line[i + 2];
|
|
114
|
-
out[o + 3] = channels === 4 ? line[i + 3] : 255;
|
|
115
|
-
} else {
|
|
116
|
-
out[o] = out[o + 1] = out[o + 2] = line[i];
|
|
117
|
-
out[o + 3] = channels === 2 ? line[i + 1] : 255;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
prev.set(line);
|
|
121
|
-
}
|
|
122
|
-
return { width, height, rgba: out };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
59
|
// src/direction/propose.ts
|
|
126
60
|
var PROPOSED = "PROPOSED:";
|
|
127
61
|
function srgbToLinear(c) {
|
|
@@ -253,8 +187,8 @@ function estateFrom(dir, cwd) {
|
|
|
253
187
|
}
|
|
254
188
|
return readdirSync(root).filter((f) => f.endsWith(".json")).flatMap((f) => {
|
|
255
189
|
try {
|
|
256
|
-
const snap = readJson(join(root, f));
|
|
257
|
-
return snap.
|
|
190
|
+
const snap = readSnapshot(readJson(join(root, f)), f);
|
|
191
|
+
return snap.fonts ? [{ id: f.replace(/(\.snapshot)?\.json$/, ""), fingerprint: fingerprint(snap) }] : [];
|
|
258
192
|
} catch {
|
|
259
193
|
return [];
|
|
260
194
|
}
|