@goflag/cli 0.2.11 → 0.2.13
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/README.md +75 -7
- package/dist/{chunk-CWEJ6EXT.js → chunk-57NVXEJS.js} +858 -17
- package/dist/chunk-57NVXEJS.js.map +1 -0
- package/dist/cli.js +94 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +121 -1
- package/dist/index.js +3 -1
- package/flags.json +1 -1
- package/package.json +4 -3
- package/rules.json +58 -1
- package/dist/chunk-CWEJ6EXT.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -379,6 +379,43 @@ interface ExtractionAsset {
|
|
|
379
379
|
*/
|
|
380
380
|
sizes?: ExtractionAssetSize[];
|
|
381
381
|
}
|
|
382
|
+
/** One tag, named the way the page named it. */
|
|
383
|
+
interface ExtractionHydratedMeta {
|
|
384
|
+
name?: string;
|
|
385
|
+
property?: string;
|
|
386
|
+
httpEquiv?: string;
|
|
387
|
+
content?: string;
|
|
388
|
+
}
|
|
389
|
+
/** One link, named the way the page named it. */
|
|
390
|
+
interface ExtractionHydratedLink {
|
|
391
|
+
rel: string;
|
|
392
|
+
href?: string;
|
|
393
|
+
hreflang?: string;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* What client JavaScript added to, or took from, the `<head>`.
|
|
397
|
+
*
|
|
398
|
+
* The gap between the two passes, at tag granularity — not per `Fact`, which
|
|
399
|
+
* carries which *tag* produced a value and never which *pass*. A value present
|
|
400
|
+
* here is a value the browser shows and a non-JS crawler never sees: the
|
|
401
|
+
* failure mode every `og.*` rule is blind to, because each of them judges the
|
|
402
|
+
* declaration it is given and this is about which declaration that was.
|
|
403
|
+
*
|
|
404
|
+
* **Absent on most runs, and that is not "nothing changed".** The delta only
|
|
405
|
+
* exists where both bodies do: the auto-escalation path, where a static fetch
|
|
406
|
+
* looked empty and a headless pass followed. A `--static` run never renders,
|
|
407
|
+
* and an explicit `--headless` run keeps no static body to compare against.
|
|
408
|
+
*/
|
|
409
|
+
interface ExtractionHydration {
|
|
410
|
+
titleChanged: boolean;
|
|
411
|
+
htmlLangChanged: boolean;
|
|
412
|
+
injectedMetas: ExtractionHydratedMeta[];
|
|
413
|
+
removedMetas: ExtractionHydratedMeta[];
|
|
414
|
+
injectedLinks: ExtractionHydratedLink[];
|
|
415
|
+
removedLinks: ExtractionHydratedLink[];
|
|
416
|
+
/** JSON-LD blocks that exist only after hydration. */
|
|
417
|
+
jsonLdBlocksAdded: number;
|
|
418
|
+
}
|
|
382
419
|
/**
|
|
383
420
|
* The per-page observation. Everything a page rule (or an agent judging a
|
|
384
421
|
* prose rule) may read; nothing else.
|
|
@@ -402,6 +439,11 @@ interface Extraction {
|
|
|
402
439
|
* manifest gets.
|
|
403
440
|
*/
|
|
404
441
|
assets?: Record<string, ExtractionAsset>;
|
|
442
|
+
/**
|
|
443
|
+
* What hydration changed in the `<head>`, when both passes ran and can be
|
|
444
|
+
* compared. Absent means "not established" — never "nothing moved".
|
|
445
|
+
*/
|
|
446
|
+
hydration?: ExtractionHydration;
|
|
405
447
|
}
|
|
406
448
|
|
|
407
449
|
/**
|
|
@@ -874,6 +916,26 @@ interface GoflagReport {
|
|
|
874
916
|
* touch the summary counts, the verdict, or the exit code.
|
|
875
917
|
*/
|
|
876
918
|
advisories?: ReportAdvisory[];
|
|
919
|
+
/**
|
|
920
|
+
* What each page declared in its `<head>`, as the rules saw it. Present
|
|
921
|
+
* only when asked for (`AuditOptions.extractions`, which `goflag preview`
|
|
922
|
+
* sets).
|
|
923
|
+
*
|
|
924
|
+
* Every other section of this report is a *judgment*: a rule fired, or it
|
|
925
|
+
* did not. This one is the observation the judgments were made from, and it
|
|
926
|
+
* is here because a share card cannot be drawn from a violations list — a
|
|
927
|
+
* page that passes every `og.*` rule still has a title, an image and a
|
|
928
|
+
* description that somebody wants to look at, and none of them appear
|
|
929
|
+
* anywhere else in this file.
|
|
930
|
+
*
|
|
931
|
+
* Keyed by nothing: each entry carries its own `http.finalUrl`, which is the
|
|
932
|
+
* same string `ReportPage.url` and `SeoIssue.pageUrl` use.
|
|
933
|
+
*
|
|
934
|
+
* Covers fewer pages than `pages`, for the same reason `conformance` does:
|
|
935
|
+
* only healthy HTML documents reach the rule layer, so an unreachable page,
|
|
936
|
+
* a non-HTML resource and a canonical-declared duplicate have no entry here.
|
|
937
|
+
*/
|
|
938
|
+
extractions?: Extraction[];
|
|
877
939
|
/** Comparison against a stored baseline, when `--baseline` was given. */
|
|
878
940
|
diff?: ReportDiff;
|
|
879
941
|
diagnostics: {
|
|
@@ -1054,6 +1116,20 @@ interface AuditOptions {
|
|
|
1054
1116
|
* carry no verdict and never affect the gate.
|
|
1055
1117
|
*/
|
|
1056
1118
|
advisories?: boolean;
|
|
1119
|
+
/**
|
|
1120
|
+
* Emit each page's extraction — what its `<head>` declared, as the rules
|
|
1121
|
+
* read it. Off by default: it is the observation, not a finding, and on a
|
|
1122
|
+
* large crawl it is the biggest section of the report.
|
|
1123
|
+
*
|
|
1124
|
+
* `goflag preview` sets it, because a share card is drawn from the
|
|
1125
|
+
* declarations and not from the verdicts about them.
|
|
1126
|
+
*
|
|
1127
|
+
* Unlike `conformance` and `advisories`, this section stops at the report:
|
|
1128
|
+
* `summarize()` does not carry it — a summary of an observation is the
|
|
1129
|
+
* observation with the useful parts removed — and no terminal renderer
|
|
1130
|
+
* prints it. Its one reader is `renderPreview`.
|
|
1131
|
+
*/
|
|
1132
|
+
extractions?: boolean;
|
|
1057
1133
|
}
|
|
1058
1134
|
/**
|
|
1059
1135
|
* Routes present in some locales but absent in others. A "hole" is exactly
|
|
@@ -1225,6 +1301,50 @@ interface RenderOptions {
|
|
|
1225
1301
|
}
|
|
1226
1302
|
declare function renderSummaryTerminal(summary: GoflagSummary, options?: RenderOptions): string;
|
|
1227
1303
|
|
|
1304
|
+
/**
|
|
1305
|
+
* The share-card preview — one self-contained HTML document.
|
|
1306
|
+
*
|
|
1307
|
+
* `renderPreview` is a pure function of a `GoflagReport`, exactly like the
|
|
1308
|
+
* three terminal renderers beside it. It reads `report.extractions`, which is
|
|
1309
|
+
* the only section that carries what a page *declared* rather than what a rule
|
|
1310
|
+
* *decided*: a page can pass every `og.*` rule and still unfurl badly, and no
|
|
1311
|
+
* violations list can show that.
|
|
1312
|
+
*
|
|
1313
|
+
* Two things this file refuses to do, and they are the point.
|
|
1314
|
+
*
|
|
1315
|
+
* **It draws no geometry it cannot source.** Every surface carries the same
|
|
1316
|
+
* rigor scale the rule catalogue uses, because the surfaces are not equally
|
|
1317
|
+
* documented: Facebook, LinkedIn and WhatsApp publish real numbers; Slack calls
|
|
1318
|
+
* its own rendering a "micro-approximation"; Discord publishes nothing at all;
|
|
1319
|
+
* X's card documentation is not reachable, and its shape changed twice since
|
|
1320
|
+
* 2023. A preview that renders all seven identically states seven things with
|
|
1321
|
+
* one confidence, and six of them are not earned.
|
|
1322
|
+
*
|
|
1323
|
+
* **It escapes everything.** The input is a site under audit, which is to say
|
|
1324
|
+
* untrusted text, and the output is a file a human opens in a browser. Values
|
|
1325
|
+
* go through `esc`; URLs that are not `http(s)` never become an `src`.
|
|
1326
|
+
*
|
|
1327
|
+
* Opening the document asks for the image URLs the pages declared, which is
|
|
1328
|
+
* what makes it a preview rather than a table. Those URLs are the site's own
|
|
1329
|
+
* choice and need not point at the audited origin, so the document ships a CSP
|
|
1330
|
+
* that allows images and nothing else — no script, no frame, no fetch — and
|
|
1331
|
+
* every image is `referrerpolicy="no-referrer"`. Rendering itself never touches
|
|
1332
|
+
* the network.
|
|
1333
|
+
*/
|
|
1334
|
+
|
|
1335
|
+
interface RenderPreviewOptions {
|
|
1336
|
+
/** Document `<title>`. Defaults to the audited origin. */
|
|
1337
|
+
title?: string;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Render the whole preview.
|
|
1341
|
+
*
|
|
1342
|
+
* A report without `extractions` renders a document that says so rather than an
|
|
1343
|
+
* empty one: the caller asked for a preview and got a report that cannot make
|
|
1344
|
+
* one, and that is a fact about the run, not an error to swallow.
|
|
1345
|
+
*/
|
|
1346
|
+
declare function renderPreview(report: GoflagReport, options?: RenderPreviewOptions): string;
|
|
1347
|
+
|
|
1228
1348
|
/**
|
|
1229
1349
|
* Stable finding fingerprints.
|
|
1230
1350
|
*
|
|
@@ -1328,4 +1448,4 @@ declare class Logger {
|
|
|
1328
1448
|
private truncate;
|
|
1329
1449
|
}
|
|
1330
1450
|
|
|
1331
|
-
export { type AuditOptions, type AuditPhase, type BrokenLink, type GoflagReport, type GoflagSummary, type LogMode, Logger, type LoggerOptions, type ProgressEvent, type ReportPage, type ReportReciprocityIssue, type RollupLink, type RollupReciprocity, type RollupSeo, SAMPLE_LIMIT, type SeoIssue, type TranslationHole, type UnreachablePage, type Verdict, deriveTranslationHoles, exitCode, fingerprint, renderSummaryTerminal, renderTerminal, routeKey, runAudit, summarize, targetKey };
|
|
1451
|
+
export { type AuditOptions, type AuditPhase, type BrokenLink, type GoflagReport, type GoflagSummary, type LogMode, Logger, type LoggerOptions, type ProgressEvent, type RenderPreviewOptions, type ReportPage, type ReportReciprocityIssue, type RollupLink, type RollupReciprocity, type RollupSeo, SAMPLE_LIMIT, type SeoIssue, type TranslationHole, type UnreachablePage, type Verdict, deriveTranslationHoles, exitCode, fingerprint, renderPreview, renderSummaryTerminal, renderTerminal, routeKey, runAudit, summarize, targetKey };
|
package/dist/index.js
CHANGED
|
@@ -5,19 +5,21 @@ import {
|
|
|
5
5
|
deriveTranslationHoles,
|
|
6
6
|
exitCode,
|
|
7
7
|
fingerprint,
|
|
8
|
+
renderPreview,
|
|
8
9
|
renderSummaryTerminal,
|
|
9
10
|
renderTerminal,
|
|
10
11
|
routeKey,
|
|
11
12
|
runAudit,
|
|
12
13
|
summarize,
|
|
13
14
|
targetKey
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-57NVXEJS.js";
|
|
15
16
|
export {
|
|
16
17
|
Logger,
|
|
17
18
|
SAMPLE_LIMIT,
|
|
18
19
|
deriveTranslationHoles,
|
|
19
20
|
exitCode,
|
|
20
21
|
fingerprint,
|
|
22
|
+
renderPreview,
|
|
21
23
|
renderSummaryTerminal,
|
|
22
24
|
renderTerminal,
|
|
23
25
|
routeKey,
|
package/flags.json
CHANGED
|
@@ -241,7 +241,7 @@
|
|
|
241
241
|
"default": null,
|
|
242
242
|
"requires": null,
|
|
243
243
|
"group": "output",
|
|
244
|
-
"description": "Roll findings up (dedup by link/rule/code). Pairs with --json for a compact, agent-friendly payload."
|
|
244
|
+
"description": "Roll findings up (dedup by link/rule/code). Pairs with --json for a compact, agent-friendly payload. Not available with --baseline: there the diff is the answer, and --report writes the full report."
|
|
245
245
|
},
|
|
246
246
|
{
|
|
247
247
|
"name": "--timeout",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goflag/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Audits a site for broken links, missing translation pages, a robots.txt that contradicts your pages, and missing or misconfigured SEO metadata. JSON-first.",
|
|
6
6
|
"keywords": [
|
|
@@ -64,9 +64,10 @@
|
|
|
64
64
|
"generate:catalog": "tsx scripts/generate-catalog.ts",
|
|
65
65
|
"generate:flags": "tsx scripts/generate-flags.ts",
|
|
66
66
|
"generate:help-fixture": "tsx scripts/generate-help-fixture.ts",
|
|
67
|
+
"generate:transcripts": "tsx scripts/generate-transcripts.ts",
|
|
67
68
|
"prepack": "node ../../scripts/stage-package-docs.mjs",
|
|
68
69
|
"prepublishOnly": "pnpm build",
|
|
69
|
-
"release": "commit-and-tag-version --path . --skip.tag"
|
|
70
|
+
"release": "commit-and-tag-version --path . --skip.tag --tag-prefix v"
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
73
|
"cheerio": "^1.2.0"
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
"@vitest/coverage-v8": "^2.1.8",
|
|
86
87
|
"commit-and-tag-version": "^12.5.0",
|
|
87
88
|
"hono": "^4.12.25",
|
|
88
|
-
"playwright": "1.
|
|
89
|
+
"playwright": "1.63.0",
|
|
89
90
|
"tsup": "^8.5.1",
|
|
90
91
|
"tsx": "^4.21.0",
|
|
91
92
|
"typescript": "^5.7.2",
|
package/rules.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
|
-
"page":
|
|
3
|
+
"page": 25,
|
|
4
4
|
"site": 28,
|
|
5
5
|
"prose": 5
|
|
6
6
|
},
|
|
@@ -579,6 +579,34 @@
|
|
|
579
579
|
"language": "tsx"
|
|
580
580
|
}
|
|
581
581
|
},
|
|
582
|
+
{
|
|
583
|
+
"id": "og.image.alt.caption",
|
|
584
|
+
"scope": "page",
|
|
585
|
+
"category": "opengraph",
|
|
586
|
+
"severity": "info",
|
|
587
|
+
"kind": "boolean",
|
|
588
|
+
"summary": "`og:image:alt` describes the image, it does not repeat the title",
|
|
589
|
+
"why": "The protocol asks for a description of what is in the image, and says in the same breath that it is not a caption. A copy of the title is the one value that is always available and never describes anything: a reader who cannot see the card is handed the same words twice and learns nothing about the picture. It also satisfies `og.image.alt` forever, which is why it needs an id of its own.",
|
|
590
|
+
"rigor": "guideline",
|
|
591
|
+
"sources": [
|
|
592
|
+
"ogp"
|
|
593
|
+
],
|
|
594
|
+
"reads": [
|
|
595
|
+
"openGraph.images",
|
|
596
|
+
"openGraph.title",
|
|
597
|
+
"document.title"
|
|
598
|
+
],
|
|
599
|
+
"relates": [
|
|
600
|
+
"og.image.alt",
|
|
601
|
+
"og.image.representative"
|
|
602
|
+
],
|
|
603
|
+
"expected": "an `og:image:alt` that says something the title does not",
|
|
604
|
+
"fix": {
|
|
605
|
+
"title": "Say what the card shows, not what the page is called",
|
|
606
|
+
"snippet": "// The card is a title on a background. The alt says so — once, from a\n// template, so every page gets one and none of them is the title alone.\nconst ogAlt = (title: string) => `The title “${title}” on a dark preview card.`;",
|
|
607
|
+
"language": "ts"
|
|
608
|
+
}
|
|
609
|
+
},
|
|
582
610
|
{
|
|
583
611
|
"id": "og.image.dimensions",
|
|
584
612
|
"scope": "page",
|
|
@@ -711,6 +739,35 @@
|
|
|
711
739
|
],
|
|
712
740
|
"prose": "Does the og:image represent what this page is about, rather than being a site-wide default, and does its subject survive being cropped to the 1.91:1 aspect ratio consumers render?"
|
|
713
741
|
},
|
|
742
|
+
{
|
|
743
|
+
"id": "og.image.sizes-mismatch",
|
|
744
|
+
"scope": "page",
|
|
745
|
+
"category": "opengraph",
|
|
746
|
+
"severity": "warning",
|
|
747
|
+
"kind": "boolean",
|
|
748
|
+
"summary": "`og:image:width` and `og:image:height` must be the image's real size",
|
|
749
|
+
"why": "The declared size is what a crawler lays the card out with before it has the file, which is the whole reason `og.image.dimensions` asks for it. A wrong one is therefore worse than none: the crawler reserves a shape the image does not have, and the card is letterboxed or cropped on the first share — the one that matters most.\n\nIt is also the declaration that blinds every other check. `og.image.ratio` reads these two numbers and refuses to fetch, so an invented 1200×630 scores 1.9 and passes over an image that is square. Found on a site whose cover art is 1024×1024 and one of whose covers is a 337-byte 1×1 placeholder, all three declared 1200×630 by a library that had never looked at them.",
|
|
750
|
+
"rigor": "guideline",
|
|
751
|
+
"sources": [
|
|
752
|
+
"ogp",
|
|
753
|
+
"meta-og-sharing"
|
|
754
|
+
],
|
|
755
|
+
"reads": [
|
|
756
|
+
"openGraph.images",
|
|
757
|
+
"assets"
|
|
758
|
+
],
|
|
759
|
+
"relates": [
|
|
760
|
+
"og.image.dimensions",
|
|
761
|
+
"og.image.ratio",
|
|
762
|
+
"og.image.reachable"
|
|
763
|
+
],
|
|
764
|
+
"expected": "declared dimensions equal to the file's own",
|
|
765
|
+
"fix": {
|
|
766
|
+
"title": "Measure the image, or let the file convention do it",
|
|
767
|
+
"snippet": "// app/…/opengraph-image.tsx — `size` is the truth and the declaration at once\nexport const size = { width: 1200, height: 630 };\n\n// Naming a file instead? Then the numbers are that file's, not a default.\nopenGraph: { images: [{ url: cover.url, width: cover.width, height: cover.height }] }",
|
|
768
|
+
"language": "tsx"
|
|
769
|
+
}
|
|
770
|
+
},
|
|
714
771
|
{
|
|
715
772
|
"id": "og.locale.alternates",
|
|
716
773
|
"scope": "page",
|