pagegraph 0.9.0 → 0.10.1

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,4 +1,162 @@
1
1
  import { AnyRoute, AnyRouteMatch } from "@tanstack/react-router";
2
+ //#region src/core/declare.d.ts
3
+ /**
4
+ * Consumer-augmented registry.
5
+ *
6
+ * ```ts
7
+ * declare module "pagegraph" {
8
+ * interface Register {
9
+ * paths: FileRouteTypes["fullPaths"];
10
+ * kinds: "page" | "article" | "hub";
11
+ * }
12
+ * }
13
+ * ```
14
+ */
15
+ interface Register {}
16
+ /** Every public full path in the consumer's route tree (type-only; erased at runtime). */
17
+ type PublicPath = Register extends {
18
+ paths: infer P extends string;
19
+ } ? P : string;
20
+ /** The consumer's page taxonomy. A label: nothing in the core branches on it. */
21
+ type SeoKind = Register extends {
22
+ kinds: infer K extends string;
23
+ } ? K : string;
24
+ interface SitemapPolicy {
25
+ priority: number;
26
+ changeFrequency: "weekly" | "monthly";
27
+ }
28
+ interface RouteSeo {
29
+ kind: SeoKind;
30
+ /** Breadcrumb label; fn form reads the match (e.g. loaderData frontmatter title). */
31
+ crumb?: string | ((match: AnyRouteMatch) => string) | undefined;
32
+ /** false = excluded from sitemap. Absent on non-indexable kinds is fine. */
33
+ sitemap?: SitemapPolicy | false | undefined;
34
+ /** meta robots value, e.g. "noindex, follow" on a hub page. */
35
+ robots?: string | undefined;
36
+ /** Typed cross-link edges. */
37
+ related?: ReadonlyArray<PublicPath> | undefined;
38
+ /** How OTHER pages render a card for this page. */
39
+ link?: {
40
+ title: string;
41
+ description: string;
42
+ } | undefined;
43
+ /** This route is a redirect/alias. */
44
+ redirectTo?: PublicPath | undefined;
45
+ /**
46
+ * When this page's content last changed meaningfully, as an ISO 8601 date.
47
+ * Feeds the sitemap `<lastmod>` and the freshness report. Content instances
48
+ * carry their own dates instead; a param route's value is never inherited.
49
+ */
50
+ modifiedAt?: string | undefined;
51
+ }
52
+ declare module "@tanstack/react-router" {
53
+ interface StaticDataRouteOption {
54
+ seo?: RouteSeo | undefined;
55
+ }
56
+ }
57
+ //#endregion
58
+ //#region src/core/graph.d.ts
59
+ /**
60
+ * Where a node came from: `"route"` for a structural route declaration, or the
61
+ * `source` label of the collection that produced the instance (e.g. "blog").
62
+ */
63
+ type SeoSource = string;
64
+ interface SeoNode {
65
+ /** Canonical path, no origin (e.g. "/pricing", "/blog/my-post"). */
66
+ path: string;
67
+ kind: SeoKind;
68
+ source: SeoSource;
69
+ /** Route-declared policy, or synthesized (kind + inherited sitemap) for instances. */
70
+ policy: RouteSeo;
71
+ instance?: {
72
+ title: string;
73
+ description?: string | undefined;
74
+ publishedAt?: string | undefined;
75
+ modifiedAt?: string | undefined;
76
+ } | undefined;
77
+ }
78
+ type SeoEdgeType = "crumb-parent" | "related" | "redirect" | "collection-member";
79
+ interface SeoEdge {
80
+ from: string;
81
+ to: string;
82
+ type: SeoEdgeType;
83
+ }
84
+ interface SeoGraph {
85
+ nodes: Map<string, SeoNode>;
86
+ edges: Array<SeoEdge>;
87
+ /** Exact-path ownership conflicts encountered while assembling graph sources. */
88
+ collisions?: ReadonlyArray<{
89
+ path: string;
90
+ sources: ReadonlyArray<SeoSource>;
91
+ }> | undefined;
92
+ }
93
+ /** One concrete page produced by a collection. */
94
+ interface SeoInstance {
95
+ /** Canonical path of the page (e.g. "/blog/my-post"). */
96
+ readonly path: string;
97
+ readonly title: string;
98
+ readonly description?: string | undefined;
99
+ readonly publishedAt?: string | undefined;
100
+ readonly modifiedAt?: string | undefined;
101
+ }
102
+ interface SeoCollection {
103
+ /**
104
+ * The param route these instances render through (e.g. "/blog/$slug"). Instances
105
+ * inherit this route's declared policy (kind + sitemap) — the graph reads it from
106
+ * the structural node, so declarations stay the single source of truth.
107
+ */
108
+ readonly route: PublicPath;
109
+ /** The `source` stamped on every node this collection produces (e.g. "blog"). */
110
+ readonly source: SeoSource;
111
+ readonly instances: ReadonlyArray<SeoInstance>;
112
+ /** Edges the collection declares between its instances and the rest of the graph. */
113
+ readonly edges?: ReadonlyArray<SeoEdge> | undefined;
114
+ }
115
+ interface BuildSeoGraphInput {
116
+ readonly routeTree: AnyRoute;
117
+ readonly collections?: ReadonlyArray<SeoCollection> | undefined;
118
+ }
119
+ /**
120
+ * Build the SEO graph from route declarations and content collections.
121
+ *
122
+ * Synchronous: the caller materializes its collections before calling, so there is
123
+ * no async work here. Callers own the origin.
124
+ */
125
+ declare function buildSeoGraph(input: BuildSeoGraphInput): SeoGraph;
126
+ //#endregion
127
+ //#region src/core/freshness.d.ts
128
+ /** Project policy: pages whose last change is older than this are stale. */
129
+ interface FreshnessPolicy {
130
+ readonly maxAgeDays: number;
131
+ }
132
+ interface FreshnessEntry {
133
+ readonly path: string;
134
+ readonly kind: SeoKind;
135
+ readonly source: SeoSource;
136
+ /** The declared date, normalized to ISO 8601. */
137
+ readonly lastModified: string;
138
+ /** Whole days between `lastModified` and the report's `asOf`. */
139
+ readonly ageDays: number;
140
+ }
141
+ interface FreshnessReport {
142
+ readonly asOf: string;
143
+ readonly maxAgeDays: number;
144
+ /** Pages older than `maxAgeDays`, oldest first: the refresh queue. */
145
+ readonly stale: ReadonlyArray<FreshnessEntry>;
146
+ /** Dated pages within `maxAgeDays`, newest first. */
147
+ readonly fresh: ReadonlyArray<FreshnessEntry>;
148
+ /** Sitemap-eligible pages that declare no date, in graph order. */
149
+ readonly undated: ReadonlyArray<string>;
150
+ /** Sitemap-eligible pages whose date does not parse; `check` fails on each. */
151
+ readonly invalid: ReadonlyArray<string>;
152
+ }
153
+ /**
154
+ * Split the graph's sitemap-eligible pages into stale, fresh, undated, and
155
+ * invalid. An invalid date cannot be aged, but it is listed so a report never
156
+ * reads as complete while it skipped a page.
157
+ */
158
+ declare function freshnessReport(graph: SeoGraph, policy: FreshnessPolicy, now: Date): FreshnessReport;
159
+ //#endregion
2
160
  //#region src/core/links.d.ts
3
161
  /**
4
162
  * Pure link-graph utilities: read anchors out of rendered HTML, model the
@@ -146,125 +304,6 @@ declare const RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION: 1;
146
304
  */
147
305
  declare const decodeRenderedEdgeArtifact: (input: unknown) => RenderedEdgeArtifact;
148
306
  //#endregion
149
- //#region src/core/declare.d.ts
150
- /**
151
- * Consumer-augmented registry.
152
- *
153
- * ```ts
154
- * declare module "pagegraph" {
155
- * interface Register {
156
- * paths: FileRouteTypes["fullPaths"];
157
- * kinds: "page" | "article" | "hub";
158
- * }
159
- * }
160
- * ```
161
- */
162
- interface Register {}
163
- /** Every public full path in the consumer's route tree (type-only; erased at runtime). */
164
- type PublicPath = Register extends {
165
- paths: infer P extends string;
166
- } ? P : string;
167
- /** The consumer's page taxonomy. A label: nothing in the core branches on it. */
168
- type SeoKind = Register extends {
169
- kinds: infer K extends string;
170
- } ? K : string;
171
- interface SitemapPolicy {
172
- priority: number;
173
- changeFrequency: "weekly" | "monthly";
174
- }
175
- interface RouteSeo {
176
- kind: SeoKind;
177
- /** Breadcrumb label; fn form reads the match (e.g. loaderData frontmatter title). */
178
- crumb?: string | ((match: AnyRouteMatch) => string) | undefined;
179
- /** false = excluded from sitemap. Absent on non-indexable kinds is fine. */
180
- sitemap?: SitemapPolicy | false | undefined;
181
- /** meta robots value, e.g. "noindex, follow" on a hub page. */
182
- robots?: string | undefined;
183
- /** Typed cross-link edges. */
184
- related?: ReadonlyArray<PublicPath> | undefined;
185
- /** How OTHER pages render a card for this page. */
186
- link?: {
187
- title: string;
188
- description: string;
189
- } | undefined;
190
- /** This route is a redirect/alias. */
191
- redirectTo?: PublicPath | undefined;
192
- }
193
- declare module "@tanstack/react-router" {
194
- interface StaticDataRouteOption {
195
- seo?: RouteSeo | undefined;
196
- }
197
- }
198
- //#endregion
199
- //#region src/core/graph.d.ts
200
- /**
201
- * Where a node came from: `"route"` for a structural route declaration, or the
202
- * `source` label of the collection that produced the instance (e.g. "blog").
203
- */
204
- type SeoSource = string;
205
- interface SeoNode {
206
- /** Canonical path, no origin (e.g. "/pricing", "/blog/my-post"). */
207
- path: string;
208
- kind: SeoKind;
209
- source: SeoSource;
210
- /** Route-declared policy, or synthesized (kind + inherited sitemap) for instances. */
211
- policy: RouteSeo;
212
- instance?: {
213
- title: string;
214
- description?: string | undefined;
215
- publishedAt?: string | undefined;
216
- modifiedAt?: string | undefined;
217
- } | undefined;
218
- }
219
- type SeoEdgeType = "crumb-parent" | "related" | "redirect" | "collection-member";
220
- interface SeoEdge {
221
- from: string;
222
- to: string;
223
- type: SeoEdgeType;
224
- }
225
- interface SeoGraph {
226
- nodes: Map<string, SeoNode>;
227
- edges: Array<SeoEdge>;
228
- /** Exact-path ownership conflicts encountered while assembling graph sources. */
229
- collisions?: ReadonlyArray<{
230
- path: string;
231
- sources: ReadonlyArray<SeoSource>;
232
- }> | undefined;
233
- }
234
- /** One concrete page produced by a collection. */
235
- interface SeoInstance {
236
- /** Canonical path of the page (e.g. "/blog/my-post"). */
237
- readonly path: string;
238
- readonly title: string;
239
- readonly description?: string | undefined;
240
- readonly publishedAt?: string | undefined;
241
- readonly modifiedAt?: string | undefined;
242
- }
243
- interface SeoCollection {
244
- /**
245
- * The param route these instances render through (e.g. "/blog/$slug"). Instances
246
- * inherit this route's declared policy (kind + sitemap) — the graph reads it from
247
- * the structural node, so declarations stay the single source of truth.
248
- */
249
- readonly route: PublicPath;
250
- /** The `source` stamped on every node this collection produces (e.g. "blog"). */
251
- readonly source: SeoSource;
252
- readonly instances: ReadonlyArray<SeoInstance>;
253
- /** Edges the collection declares between its instances and the rest of the graph. */
254
- readonly edges?: ReadonlyArray<SeoEdge> | undefined;
255
- }
256
- interface BuildSeoGraphInput {
257
- readonly routeTree: AnyRoute;
258
- readonly collections?: ReadonlyArray<SeoCollection> | undefined;
259
- }
260
- /**
261
- * Build the SEO graph from route declarations and content collections.
262
- *
263
- * Synchronous: the caller materializes its collections before calling, so there is
264
- * no async work here. Callers own the origin.
265
- */
266
- declare function buildSeoGraph(input: BuildSeoGraphInput): SeoGraph;
267
- //#endregion
268
307
  //#region src/core/checks.d.ts
269
308
  type Severity = "structural" | "editorial";
270
309
  interface Violation {
@@ -292,13 +331,31 @@ interface CoverageRule {
292
331
  readonly minInbound: number;
293
332
  }
294
333
  /**
295
- * Run every static rule against the graph, then any caller-supplied
296
- * {@link CoverageRule}s, and return the flat list of violations. With no
297
- * `coverage` option the result is exactly the static rule set.
334
+ * Options for {@link checkGraph}. Freshness needs a clock, and the engine stays
335
+ * pure, so a freshness policy is only accepted together with the `now` it is
336
+ * judged against.
298
337
  */
299
- declare function checkGraph(graph: SeoGraph, options?: {
338
+ type CheckGraphOptions = {
300
339
  readonly coverage?: ReadonlyArray<CoverageRule> | undefined;
301
- }): Array<Violation>;
340
+ } & ({
341
+ readonly freshness: FreshnessPolicy;
342
+ readonly now: Date;
343
+ } | {
344
+ readonly freshness?: undefined;
345
+ readonly now?: undefined;
346
+ });
347
+ /**
348
+ * Run every static rule against the graph, then any caller-supplied
349
+ * {@link CoverageRule}s and freshness policy, and return the flat list of
350
+ * violations. With no options the result is exactly the static rule set.
351
+ */
352
+ declare function checkGraph(graph: SeoGraph, options?: CheckGraphOptions): Array<Violation>;
353
+ /**
354
+ * One editorial `stale-page` violation per sitemap-eligible page whose last
355
+ * declared change is older than the policy allows. Undated pages are not
356
+ * violations: see {@link freshnessReport} for the full picture.
357
+ */
358
+ declare function checkFreshness(graph: SeoGraph, policy: FreshnessPolicy, now: Date): Array<Violation>;
302
359
  /**
303
360
  * Enforce contextual-link coverage against the *declared* graph: only `related`
304
361
  * edges count — breadcrumb ancestry is navigation, not context.
@@ -319,5 +376,86 @@ declare function checkRenderedCoverage(graph: SeoGraph, renderedEdges: ReadonlyA
319
376
  /** Structural violations fail `pagegraph check`; editorial-only stays green. */
320
377
  declare const hasStructuralViolations: (violations: ReadonlyArray<Violation>) => boolean;
321
378
  //#endregion
322
- export { RenderedGraph as A, AnchorRegion as C, RenderedEdgeArtifact as D, RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION as E, diffLinkGraph as F, extractAnchors as I, normalizePath as L, SimpleEdge as M, buildRenderedGraph as N, RenderedEdgeArtifactCrawl as O, decodeRenderedEdgeArtifact as P, renderedGraphFromEdges as R, Anchor as S, LinkGraphDiff as T, PublicPath as _, checkGraph as a, SeoKind as b, BuildSeoGraphInput as c, SeoEdgeType as d, SeoGraph as f, buildSeoGraph as g, SeoSource as h, checkCoverage as i, RenderedPage as j, RenderedEdgeFailure as k, SeoCollection as l, SeoNode as m, Severity as n, checkRenderedCoverage as o, SeoInstance as p, Violation as r, hasStructuralViolations as s, CoverageRule as t, SeoEdge as u, Register as v, LinkEdge as w, SitemapPolicy as x, RouteSeo as y };
323
- //# sourceMappingURL=checks-_X8yjMvV.d.ts.map
379
+ //#region src/core/content.d.ts
380
+ interface Heading {
381
+ readonly level: number;
382
+ readonly text: string;
383
+ }
384
+ /** A JSON-LD object that carries an `@type`, found at any depth of any block. */
385
+ type JsonLdNode = Readonly<Record<string, unknown>>;
386
+ /** What a rendered document says, reduced to the fields the content rules read. */
387
+ interface PageContent {
388
+ readonly title: string | undefined;
389
+ readonly description: string | undefined;
390
+ readonly robots: string | undefined;
391
+ readonly canonical: string | undefined;
392
+ /** Every H1 in the body, in document order, by accessible text. */
393
+ readonly h1: ReadonlyArray<string>;
394
+ /** Headings in the main content region (`<main>`, else the body without nav and footer). */
395
+ readonly outline: ReadonlyArray<Heading>;
396
+ /** Words of visible text in the main content region. */
397
+ readonly wordCount: number;
398
+ /** Visible body text with whitespace collapsed; scripts, styles, and templates removed. */
399
+ readonly text: string;
400
+ /** Every typed JSON-LD node on the page, flattened out of arrays, `@graph`, and nesting. */
401
+ readonly jsonLd: ReadonlyArray<JsonLdNode>;
402
+ }
403
+ type ContentRuleName = "missing-h1" | "multiple-h1" | "empty-h1" | "heading-level-skip" | "h1-title-mismatch" | "faq-not-visible" | "offer-price-not-visible" | "webpage-head-mismatch" | "thin-content";
404
+ interface ContentFinding {
405
+ readonly rule: ContentRuleName;
406
+ readonly severity: Severity;
407
+ readonly message: string;
408
+ readonly fix: string;
409
+ }
410
+ interface ContentCheckOptions {
411
+ /** Minimum words of main-region text; omit to skip the `thin-content` rule. */
412
+ readonly minWords?: number | undefined;
413
+ }
414
+ /** Reduce a rendered HTML document to the fields the content rules read. Pure. */
415
+ declare const extractPageContent: (html: string) => PageContent;
416
+ /** Run every content rule against one rendered page. Graph-independent; pure. */
417
+ declare const checkPageContent: (content: PageContent, options?: ContentCheckOptions) => Array<ContentFinding>;
418
+ /** A word-count floor for sitemap-eligible pages matching a path glob. */
419
+ interface WordCountRule {
420
+ /** Path glob: `*` matches within a segment, `**` matches across segments. */
421
+ readonly path: string;
422
+ readonly minWords: number;
423
+ }
424
+ /** Project policy for rendered content checks. */
425
+ interface ContentPolicy {
426
+ /** Word-count floors; a page matching several rules must meet the highest. */
427
+ readonly minWords?: ReadonlyArray<WordCountRule> | undefined;
428
+ }
429
+ /** One declared page as the server rendered it, or why it could not be read. */
430
+ type RenderedDocument = {
431
+ readonly path: string;
432
+ readonly ok: true;
433
+ /** Path of the final response URL, after redirects, encoded or not. */
434
+ readonly finalPath: string;
435
+ readonly html: string;
436
+ } | {
437
+ readonly path: string;
438
+ readonly ok: false;
439
+ readonly error: string;
440
+ };
441
+ /**
442
+ * The declared pages a rendered check fetches: every sitemap-eligible page,
443
+ * plus concrete pages that declare `noindex` so the rendered robots meta can be
444
+ * held to that declaration. Param templates and redirects render no page of
445
+ * their own.
446
+ */
447
+ declare const renderedCheckTargets: (graph: SeoGraph) => Array<string>;
448
+ /**
449
+ * Judge rendered documents against the declared graph. Every indexable page must
450
+ * render, stay on its path, keep a self-referencing canonical and indexable
451
+ * robots meta, and pass {@link checkPageContent}; a page that declares `noindex`
452
+ * must render a `noindex` robots meta.
453
+ *
454
+ * Robots are read from the `<meta name="robots">` tag only. An `X-Robots-Tag`
455
+ * header is usually host policy (preview hosts noindex everything), so holding
456
+ * pages to it would fail every local run for a reason no declaration controls.
457
+ */
458
+ declare function checkRenderedContent(graph: SeoGraph, documents: ReadonlyArray<RenderedDocument>, policy?: ContentPolicy): Array<Violation>;
459
+ //#endregion
460
+ export { PublicPath as $, RenderedEdgeFailure as A, FreshnessEntry as B, Anchor as C, RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION as D, LinkGraphDiff as E, decodeRenderedEdgeArtifact as F, SeoCollection as G, FreshnessReport as H, diffLinkGraph as I, SeoGraph as J, SeoEdge as K, extractAnchors as L, RenderedPage as M, SimpleEdge as N, RenderedEdgeArtifact as O, buildRenderedGraph as P, buildSeoGraph as Q, normalizePath as R, hasStructuralViolations as S, LinkEdge as T, freshnessReport as U, FreshnessPolicy as V, BuildSeoGraphInput as W, SeoNode as X, SeoInstance as Y, SeoSource as Z, Violation as _, Heading as a, checkGraph as b, RenderedDocument as c, checkRenderedContent as d, Register as et, extractPageContent as f, Severity as g, CoverageRule as h, ContentRuleName as i, RenderedGraph as j, RenderedEdgeArtifactCrawl as k, WordCountRule as l, CheckGraphOptions as m, ContentFinding as n, SeoKind as nt, JsonLdNode as o, renderedCheckTargets as p, SeoEdgeType as q, ContentPolicy as r, SitemapPolicy as rt, PageContent as s, ContentCheckOptions as t, RouteSeo as tt, checkPageContent as u, checkCoverage as v, AnchorRegion as w, checkRenderedCoverage as x, checkFreshness as y, renderedGraphFromEdges as z };
461
+ //# sourceMappingURL=content-DVL2iyGU.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as RenderedGraph, C as AnchorRegion, D as RenderedEdgeArtifact, E as RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION, F as diffLinkGraph, I as extractAnchors, L as normalizePath, M as SimpleEdge, N as buildRenderedGraph, O as RenderedEdgeArtifactCrawl, P as decodeRenderedEdgeArtifact, R as renderedGraphFromEdges, S as Anchor, T as LinkGraphDiff, _ as PublicPath, a as checkGraph, b as SeoKind, c as BuildSeoGraphInput, d as SeoEdgeType, f as SeoGraph, g as buildSeoGraph, h as SeoSource, i as checkCoverage, j as RenderedPage, k as RenderedEdgeFailure, l as SeoCollection, m as SeoNode, n as Severity, o as checkRenderedCoverage, p as SeoInstance, r as Violation, s as hasStructuralViolations, t as CoverageRule, u as SeoEdge, v as Register, w as LinkEdge, x as SitemapPolicy, y as RouteSeo } from "./checks-_X8yjMvV.js";
1
+ import { $ as PublicPath, A as RenderedEdgeFailure, B as FreshnessEntry, C as Anchor, D as RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION, E as LinkGraphDiff, F as decodeRenderedEdgeArtifact, G as SeoCollection, H as FreshnessReport, I as diffLinkGraph, J as SeoGraph, K as SeoEdge, L as extractAnchors, M as RenderedPage, N as SimpleEdge, O as RenderedEdgeArtifact, P as buildRenderedGraph, Q as buildSeoGraph, R as normalizePath, S as hasStructuralViolations, T as LinkEdge, U as freshnessReport, V as FreshnessPolicy, W as BuildSeoGraphInput, X as SeoNode, Y as SeoInstance, Z as SeoSource, _ as Violation, a as Heading, b as checkGraph, c as RenderedDocument, d as checkRenderedContent, et as Register, f as extractPageContent, g as Severity, h as CoverageRule, i as ContentRuleName, j as RenderedGraph, k as RenderedEdgeArtifactCrawl, l as WordCountRule, m as CheckGraphOptions, n as ContentFinding, nt as SeoKind, o as JsonLdNode, p as renderedCheckTargets, q as SeoEdgeType, r as ContentPolicy, rt as SitemapPolicy, s as PageContent, t as ContentCheckOptions, tt as RouteSeo, u as checkPageContent, v as checkCoverage, w as AnchorRegion, x as checkRenderedCoverage, y as checkFreshness, z as renderedGraphFromEdges } from "./content-DVL2iyGU.js";
2
2
  import { AnyRouter } from "@tanstack/react-router";
3
3
  //#region src/core/projections.d.ts
4
4
  interface ProjectionConfig {
@@ -42,9 +42,16 @@ interface NodeReport {
42
42
  */
43
43
  declare function isSitemapEligible(node: SeoNode): boolean;
44
44
  /**
45
- * Render sitemap.xml from the graph. Structural route entries emit no `<lastmod>`
46
- * (a route has no publish date); content instances emit it from their frontmatter.
47
- * Route entries are sorted by path, then instances follow in collection order.
45
+ * The date a node's content last changed, as declared: an instance's
46
+ * `modifiedAt`, else its `publishedAt`; a route's own `modifiedAt`. A node with
47
+ * no date (a docs page without frontmatter dates, an undated route) returns
48
+ * undefined, and the sitemap omits its `<lastmod>`.
49
+ */
50
+ declare function lastModified(node: SeoNode): string | undefined;
51
+ /**
52
+ * Render sitemap.xml from the graph. Each entry's `<lastmod>` is its
53
+ * {@link lastModified} date: frontmatter for content instances, the declared
54
+ * `modifiedAt` for routes. Route entries are sorted by path, then instances follow in collection order.
48
55
  * `indexable` is intentionally unused — the sitemap body is host-independent;
49
56
  * robots.txt is what gates crawling.
50
57
  */
@@ -185,5 +192,5 @@ declare const hasBlockingIssues: (report: LiveHeadReport) => boolean;
185
192
  */
186
193
  declare function resolveRouteLink(router: AnyRouter, path: string): RouteSeo["link"];
187
194
  //#endregion
188
- export { type Anchor, type AnchorRegion, type BuildSeoGraphInput, type CoverageRule, type JsonLdReport, type LinkCandidateOptions, type LinkCandidatePair, type LinkCandidateResult, type LinkClusterSummary, type LinkEdge, type LinkGraphDiff, type LiveHeadReport, type NodeReport, type ProjectionConfig, type PublicPath, RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION, type Register, type RenderedEdgeArtifact, type RenderedEdgeArtifactCrawl, type RenderedEdgeFailure, type RenderedGraph, type RenderedPage, type RobotsConfig, type RouteSeo, type SeoCollection, type SeoEdge, type SeoEdgeType, type SeoGraph, type SeoInstance, type SeoKind, type SeoNode, type SeoSource, type Severity, type SimpleEdge, type SitemapPolicy, type Violation, buildRenderedGraph, buildSeoGraph, candidateSourceText, checkCoverage, checkGraph, checkRenderedCoverage, contentSignal, decodeRenderedEdgeArtifact, decodeRenderedEdges, diffLinkGraph, extractAnchors, generateLinkCandidates, hasBlockingIssues, hasStructuralViolations, inspectHtml, inspectNode, isSitemapEligible, matchesClusterFilter, normalizePath, renderRobots, renderSitemap, renderedGraphFromEdges, resolveRouteLink, undirectedEdgeKey };
195
+ export { type Anchor, type AnchorRegion, type BuildSeoGraphInput, type CheckGraphOptions, type ContentCheckOptions, type ContentFinding, type ContentPolicy, type ContentRuleName, type CoverageRule, type FreshnessEntry, type FreshnessPolicy, type FreshnessReport, type Heading, type JsonLdNode, type JsonLdReport, type LinkCandidateOptions, type LinkCandidatePair, type LinkCandidateResult, type LinkClusterSummary, type LinkEdge, type LinkGraphDiff, type LiveHeadReport, type NodeReport, type PageContent, type ProjectionConfig, type PublicPath, RENDERED_EDGE_ARTIFACT_SCHEMA_VERSION, type Register, type RenderedDocument, type RenderedEdgeArtifact, type RenderedEdgeArtifactCrawl, type RenderedEdgeFailure, type RenderedGraph, type RenderedPage, type RobotsConfig, type RouteSeo, type SeoCollection, type SeoEdge, type SeoEdgeType, type SeoGraph, type SeoInstance, type SeoKind, type SeoNode, type SeoSource, type Severity, type SimpleEdge, type SitemapPolicy, type Violation, type WordCountRule, buildRenderedGraph, buildSeoGraph, candidateSourceText, checkCoverage, checkFreshness, checkGraph, checkPageContent, checkRenderedContent, checkRenderedCoverage, contentSignal, decodeRenderedEdgeArtifact, decodeRenderedEdges, diffLinkGraph, extractAnchors, extractPageContent, freshnessReport, generateLinkCandidates, hasBlockingIssues, hasStructuralViolations, inspectHtml, inspectNode, isSitemapEligible, lastModified, matchesClusterFilter, normalizePath, renderRobots, renderSitemap, renderedCheckTargets, renderedGraphFromEdges, resolveRouteLink, undirectedEdgeKey };
189
196
  //# sourceMappingURL=index.d.ts.map