pagetrace 0.2.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.
@@ -0,0 +1,232 @@
1
+ import { HTMLElement } from 'node-html-parser';
2
+
3
+ type Severity = 'error' | 'warn' | 'info';
4
+ /** A single structured-data entity found on a page. */
5
+ interface JsonLdEntity {
6
+ type: string;
7
+ id?: string;
8
+ /** Sorted list of top-level property names present on the entity. */
9
+ properties: string[];
10
+ }
11
+ /** The normalized SEO/AEO surface of one page. */
12
+ interface PageFingerprint {
13
+ route: string;
14
+ title: string | null;
15
+ description: string | null;
16
+ canonical: string | null;
17
+ /** Content of <meta name="robots">, lowercased. */
18
+ robots: string | null;
19
+ og: Record<string, string>;
20
+ twitter: Record<string, string>;
21
+ /** hreflang value -> href */
22
+ hreflang: Record<string, string>;
23
+ h1: string[];
24
+ /** Heading tag sequence in document order, e.g. ["h1","h2","h2","h3"]. */
25
+ headingOutline: string[];
26
+ jsonLd: JsonLdEntity[];
27
+ wordCount: number;
28
+ images: {
29
+ total: number;
30
+ missingAlt: number;
31
+ };
32
+ /** Whether the page exposes an answer-shaped opening paragraph (AEO signal). */
33
+ leadAnswerWords: number;
34
+ /** Content of <meta name="generator">, used for platform detection. */
35
+ generator: string | null;
36
+ }
37
+ /** Site-wide signals that live outside any single page. */
38
+ interface SiteFingerprint {
39
+ /**
40
+ * The origin this snapshot was crawled from, e.g. "https://example.com".
41
+ * Absent for a filesystem crawl and for lockfiles written before 0.2.0.
42
+ */
43
+ origin?: string | null;
44
+ robotsTxt: {
45
+ present: boolean;
46
+ /** agent name -> whether the root path is crawlable */
47
+ aiAgents: Record<string, 'allowed' | 'disallowed'>;
48
+ sitemaps: string[];
49
+ } | null;
50
+ llmsTxt: {
51
+ present: boolean;
52
+ /** H2 section titles, used to detect silent truncation. */
53
+ sections: string[];
54
+ bytes: number;
55
+ } | null;
56
+ }
57
+ interface Snapshot {
58
+ schemaVersion: 1;
59
+ createdAt: string;
60
+ site: SiteFingerprint;
61
+ pages: Record<string, PageFingerprint>;
62
+ }
63
+ type Platform = 'wordpress' | 'nextjs' | 'shopify' | 'webflow' | 'wix' | 'squarespace' | 'drupal' | 'unknown';
64
+ interface Finding {
65
+ /** Stable machine code, e.g. "canonical.removed". Integrations key on this. */
66
+ code: string;
67
+ severity: Severity;
68
+ route: string | null;
69
+ message: string;
70
+ before?: unknown;
71
+ after?: unknown;
72
+ /** Why this matters, for audit output. */
73
+ detail?: string;
74
+ /** How to fix it, platform-specific where known. */
75
+ fix?: string;
76
+ }
77
+ /** One issue rolled up across every route it affects. */
78
+ interface Aggregate {
79
+ code: string;
80
+ severity: Severity;
81
+ count: number;
82
+ routes: string[];
83
+ message: string;
84
+ detail?: string;
85
+ fix?: string;
86
+ }
87
+ interface Config {
88
+ /** Per-code severity overrides. Set to "off" to silence a rule. */
89
+ severity?: Record<string, Severity | 'off'>;
90
+ /** Routes to skip entirely (exact match or trailing-* prefix). */
91
+ ignoreRoutes?: string[];
92
+ /** Extra AI user agents to check in robots.txt. */
93
+ aiAgents?: string[];
94
+ /** Minimum word count before a page is flagged as thin. */
95
+ minWordCount?: number;
96
+ /**
97
+ * The site's own origin, e.g. "https://example.com". Lets a --dir crawl detect
98
+ * canonicals pointing at another host; an origin crawl infers it.
99
+ */
100
+ siteUrl?: string;
101
+ }
102
+
103
+ /**
104
+ * Rules that hold regardless of history. These overlap with what any auditor
105
+ * reports; the diff engine in `diff.ts` is what catches regressions.
106
+ */
107
+ declare function auditPage(page: PageFingerprint, config?: Config): Finding[];
108
+ declare function auditSite(snapshot: Snapshot): Finding[];
109
+ /**
110
+ * Rules that only exist when you look at the whole site at once. These are the
111
+ * findings that matter most on a large CMS site, where the defects come from
112
+ * templates rather than individual pages.
113
+ */
114
+ declare function auditCrossPage(snapshot: Snapshot): Finding[];
115
+ /**
116
+ * hreflang is the rule set most worth automating: Google requires the
117
+ * annotations to be reciprocal, and a one-sided set is silently ignored rather
118
+ * than reported anywhere. You cannot see this from a single page, which is why
119
+ * it lives here rather than in auditPage.
120
+ */
121
+ declare function auditHreflang(snapshot: Snapshot): Finding[];
122
+ declare function auditSnapshot(snapshot: Snapshot, config?: Config): Finding[];
123
+
124
+ declare function diffPage(before: PageFingerprint, after: PageFingerprint): Finding[];
125
+ declare function diffSite(before: Snapshot['site'], after: Snapshot['site']): Finding[];
126
+ declare function diffSnapshots(before: Snapshot, after: Snapshot): Finding[];
127
+
128
+ declare function extractJsonLd(root: HTMLElement): JsonLdEntity[];
129
+ declare function extractPage(html: string, route: string): PageFingerprint;
130
+ /** Parse robots.txt into per-agent crawlability of the site root. */
131
+ declare function extractRobotsTxt(body: string, agents: string[]): {
132
+ present: boolean;
133
+ aiAgents: Record<string, "allowed" | "disallowed">;
134
+ sitemaps: string[];
135
+ };
136
+ /** Parse llms.txt, capturing section headings so truncation is detectable. */
137
+ declare function extractLlmsTxt(body: string): {
138
+ present: boolean;
139
+ sections: string[];
140
+ bytes: number;
141
+ };
142
+ /** Pull <loc> entries out of a sitemap or sitemap index. */
143
+ declare function extractSitemapUrls(xml: string): string[];
144
+
145
+ interface Guidance {
146
+ /** Why the issue costs you traffic or citations. */
147
+ why: string;
148
+ /** Generic remedy. */
149
+ fix: string;
150
+ /** Platform-specific remedy, used when the platform is detected. */
151
+ byPlatform?: Partial<Record<Platform, string>>;
152
+ }
153
+ /**
154
+ * Explanations attached to findings in audit output. Diff output stays terse —
155
+ * you already know what a canonical is when you are reviewing a regression.
156
+ * An audit handed to a client or a content team needs the reasoning.
157
+ */
158
+ declare const GUIDANCE: Record<string, Guidance>;
159
+ /** Detect the publishing platform from generator meta and URL shape. */
160
+ declare function detectPlatform(generators: (string | null)[], urls?: string[]): Platform;
161
+ /** Attach why/fix text to a finding, preferring platform-specific advice. */
162
+ declare function withGuidance<T extends {
163
+ code: string;
164
+ }>(finding: T, platform?: Platform): T & {
165
+ detail?: string;
166
+ fix?: string;
167
+ };
168
+
169
+ /** Apply user severity overrides and drop anything switched off. */
170
+ declare function applyConfig(findings: Finding[], config?: Config): Finding[];
171
+ declare function summarize(findings: Finding[]): {
172
+ error: number;
173
+ warn: number;
174
+ info: number;
175
+ };
176
+ declare function shouldFail(findings: Finding[], failOn: Severity): boolean;
177
+ declare function formatPretty(findings: Finding[]): string;
178
+ declare function formatJson(findings: Finding[]): string;
179
+ /** Markdown table, sized for a PR comment. */
180
+ declare function formatMarkdown(findings: Finding[]): string;
181
+ /** GitHub Actions workflow-command annotations. */
182
+ declare function formatGithub(findings: Finding[]): string;
183
+ /**
184
+ * Roll findings up by issue rather than by page. On a large CMS site the same
185
+ * template defect produces hundreds of identical findings; the useful unit is
186
+ * "canonical missing on 43 pages", not 43 separate lines.
187
+ */
188
+ declare function aggregate(findings: Finding[]): Aggregate[];
189
+ interface AuditMeta {
190
+ target: string;
191
+ platform: Platform;
192
+ pageCount: number;
193
+ generatedAt: string;
194
+ }
195
+ declare function formatAuditPretty(groups: Aggregate[], meta: AuditMeta): string;
196
+ declare function formatAuditMarkdown(groups: Aggregate[], meta: AuditMeta): string;
197
+ /** Self-contained HTML report, suitable for handing to a client. */
198
+ declare function formatAuditHtml(groups: Aggregate[], meta: AuditMeta): string;
199
+
200
+ /**
201
+ * Required and recommended properties for the Schema.org types Google supports
202
+ * as rich results. Sourced from Google Search Central's structured data
203
+ * reference. Deliberately a plain data table so it can be updated without
204
+ * touching the engine, and so consumers can extend it.
205
+ */
206
+ interface RichResultRule {
207
+ required: string[];
208
+ recommended: string[];
209
+ /** Groups where at least one member must be present. */
210
+ oneOf?: string[][];
211
+ }
212
+ declare const RICH_RESULT_RULES: Record<string, RichResultRule>;
213
+ /** AI crawler user agents checked against robots.txt by default. */
214
+ declare const DEFAULT_AI_AGENTS: string[];
215
+
216
+ declare function routeFromFilePath(root: string, filePath: string): string;
217
+ declare function routeFromUrl(url: string): string;
218
+ declare function shouldIgnore(route: string, patterns?: string[]): boolean;
219
+ /** Build a snapshot from a directory of pre-rendered HTML (next export, dist, out). */
220
+ declare function snapshotFromDir(dir: string, config?: Config): Promise<Snapshot>;
221
+ interface CrawlOptions extends Config {
222
+ /** Cap the number of pages fetched. */
223
+ limit?: number;
224
+ /** Parallel requests. */
225
+ concurrency?: number;
226
+ /** Per-request timeout in milliseconds. */
227
+ timeout?: number;
228
+ }
229
+ /** Build a snapshot by fetching a live origin, discovering routes via sitemap. */
230
+ declare function snapshotFromOrigin(origin: string, options?: CrawlOptions): Promise<Snapshot>;
231
+
232
+ export { type Aggregate, type AuditMeta, type Config, DEFAULT_AI_AGENTS, type Finding, GUIDANCE, type Guidance, type JsonLdEntity, type PageFingerprint, type Platform, RICH_RESULT_RULES, type Severity, type SiteFingerprint, type Snapshot, aggregate, applyConfig, auditCrossPage, auditHreflang, auditPage, auditSite, auditSnapshot, detectPlatform, diffPage, diffSite, diffSnapshots, extractJsonLd, extractLlmsTxt, extractPage, extractRobotsTxt, extractSitemapUrls, formatAuditHtml, formatAuditMarkdown, formatAuditPretty, formatGithub, formatJson, formatMarkdown, formatPretty, routeFromFilePath, routeFromUrl, shouldFail, shouldIgnore, snapshotFromDir, snapshotFromOrigin, summarize, withGuidance };