pagegraph 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.
Files changed (88) hide show
  1. package/README.md +277 -25
  2. package/dist/audit.d.ts +2 -2
  3. package/dist/audit.js +4 -4
  4. package/dist/build-only/audit.js +1 -1
  5. package/dist/build-only/claims.d.ts +1 -0
  6. package/dist/build-only/claims.js +6 -0
  7. package/dist/build-only/claims.js.map +1 -0
  8. package/dist/build-only/config.js +1 -1
  9. package/dist/build-only/oxlint.d.ts +1 -0
  10. package/dist/build-only/oxlint.js +6 -0
  11. package/dist/build-only/oxlint.js.map +1 -0
  12. package/dist/build-only/tanstack-start.js +1 -1
  13. package/dist/build-only/vite.js +1 -1
  14. package/dist/{checks-uZk5Em9A.js → checks-BS-aDXB_.js} +2 -2
  15. package/dist/checks-BS-aDXB_.js.map +1 -0
  16. package/dist/claims-Bch7Lxlo.d.ts +92 -0
  17. package/dist/claims-Bv7voeEV.js +431 -0
  18. package/dist/claims-Bv7voeEV.js.map +1 -0
  19. package/dist/claims.d.ts +94 -0
  20. package/dist/claims.js +2 -0
  21. package/dist/cli.js +10095 -8318
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config.d.ts +4 -24
  24. package/dist/config.js +2 -2
  25. package/dist/config.js.map +1 -1
  26. package/dist/{content-kcEnQdfi.js → content-CHd0X4s6.js} +2 -2
  27. package/dist/{content-kcEnQdfi.js.map → content-CHd0X4s6.js.map} +1 -1
  28. package/dist/{content-B1cPtL8b.d.ts → content-DgNr7apS.d.ts} +4 -5
  29. package/dist/declare-D5o7hwmv.js.map +1 -1
  30. package/dist/document-CztEu3Vd.d.ts +109 -0
  31. package/dist/document-Dvemcv7V.js +379 -0
  32. package/dist/document-Dvemcv7V.js.map +1 -0
  33. package/dist/facts-1OKclPOk.js +115 -0
  34. package/dist/facts-1OKclPOk.js.map +1 -0
  35. package/dist/{declare-DVgdEdOL.d.ts → graph-D1PIndsC.d.ts} +117 -6
  36. package/dist/{graph-Bcsourlv.js → graph-DMNOLXLu.js} +23 -13
  37. package/dist/graph-DMNOLXLu.js.map +1 -0
  38. package/dist/index-C5w9ripz.d.ts +204 -0
  39. package/dist/index.d.ts +6 -160
  40. package/dist/index.js +37 -5
  41. package/dist/index.js.map +1 -1
  42. package/dist/inline-BbS93GAT.js +682 -0
  43. package/dist/inline-BbS93GAT.js.map +1 -0
  44. package/dist/llms-Dhr_5u2O.js +19 -0
  45. package/dist/llms-Dhr_5u2O.js.map +1 -0
  46. package/dist/markdown-C6dRE9zD.js +168 -0
  47. package/dist/markdown-C6dRE9zD.js.map +1 -0
  48. package/dist/markdown-path-lJB9BAVZ.js +6 -0
  49. package/dist/markdown-path-lJB9BAVZ.js.map +1 -0
  50. package/dist/{message-DT_CjUdt.js → message-CuBjgZ74.js} +2 -2
  51. package/dist/{message-DT_CjUdt.js.map → message-CuBjgZ74.js.map} +1 -1
  52. package/dist/oxlint.d.ts +58 -0
  53. package/dist/oxlint.js +202 -0
  54. package/dist/oxlint.js.map +1 -0
  55. package/dist/page-heads-BfjWQvau.d.ts +108 -0
  56. package/dist/{page-heads-6y-CARRQ.js → page-heads-BgbaIjj9.js} +3 -3
  57. package/dist/page-heads-BgbaIjj9.js.map +1 -0
  58. package/dist/{projections-DeEFe7XT.d.ts → projections-Bk-qOOsR.d.ts} +2 -2
  59. package/dist/react.d.ts +3 -2
  60. package/dist/react.js +2 -1
  61. package/dist/react.js.map +1 -1
  62. package/dist/tanstack-start/index.d.ts +29 -13
  63. package/dist/tanstack-start/index.js +391 -42
  64. package/dist/tanstack-start/index.js.map +1 -1
  65. package/dist/tanstack-start/markdown.d.ts +12 -0
  66. package/dist/tanstack-start/markdown.js +149 -0
  67. package/dist/tanstack-start/markdown.js.map +1 -0
  68. package/dist/tanstack-start/prerender-worker.d.ts +2 -0
  69. package/dist/tanstack-start/prerender-worker.js +7 -0
  70. package/dist/tanstack-start/prerender-worker.js.map +1 -0
  71. package/dist/tanstack-start/react.d.ts +7 -0
  72. package/dist/tanstack-start/react.js +12 -0
  73. package/dist/tanstack-start/react.js.map +1 -0
  74. package/dist/tanstack-start/server.d.ts +9 -3
  75. package/dist/tanstack-start/server.js +26 -2
  76. package/dist/tanstack-start/server.js.map +1 -1
  77. package/dist/{vite-graph-loader-01p1WxEq.js → vite-graph-loader-CsGiYNWE.js} +6 -5
  78. package/dist/vite-graph-loader-CsGiYNWE.js.map +1 -0
  79. package/dist/vite-graph-loader-DZCwejuJ.d.ts +133 -0
  80. package/package.json +44 -3
  81. package/dist/checks-uZk5Em9A.js.map +0 -1
  82. package/dist/graph-Bcsourlv.js.map +0 -1
  83. package/dist/graph-BxCY90vq.d.ts +0 -104
  84. package/dist/graph-CBIY71w6.d.ts +0 -57
  85. package/dist/page-heads-6y-CARRQ.js.map +0 -1
  86. package/dist/page-heads-DNkjMUEN.d.ts +0 -21
  87. package/dist/vite-graph-loader-01p1WxEq.js.map +0 -1
  88. package/dist/vite-graph-loader-Btgjz5RH.d.ts +0 -56
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts-1OKclPOk.js","names":[],"sources":["../src/markdown/facts.ts"],"sourcesContent":["export type Json = null | boolean | number | string | ReadonlyArray<Json> | { readonly [key: string]: Json };\nexport interface FactDefinition {\n readonly kind: \"number\" | \"money\" | \"list\" | \"text\";\n readonly value: Json;\n readonly text: string;\n readonly items?: ReadonlyArray<string>;\n}\nexport type Facts = Readonly<Record<string, FactDefinition>>;\nconst registeredFacts = new Map<string, FactDefinition>();\n\nfunction isJson(value: unknown, parents = new Set<object>()): value is Json {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return true;\n if (typeof value === \"number\") return Number.isFinite(value);\n if (typeof value !== \"object\" || parents.has(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n if (!Array.isArray(value) && prototype !== null && prototype !== Object.prototype) return false;\n parents.add(value);\n const entries = Array.isArray(value) ? Array.from(value) : Object.values(value);\n const valid = entries.every((entry) => isJson(entry, parents));\n parents.delete(value);\n return valid;\n}\nfunction validateDefinition(value: unknown): FactDefinition {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) throw new Error(\"Expected object\");\n for (const key of [\"kind\", \"value\", \"text\"]) {\n if (!(key in value)) throw new Error(`Missing key\\n at [\"${key}\"]`);\n }\n if (!(\"kind\" in value) || ![\"number\", \"money\", \"list\", \"text\"].includes(String(value.kind)))\n throw new Error('Expected \"number\" | \"money\" | \"list\" | \"text\"\\n at [\"kind\"]');\n if (!(\"value\" in value) || !isJson(value.value)) throw new Error('Expected JSON value\\n at [\"value\"]');\n if (!(\"text\" in value) || typeof value.text !== \"string\") throw new Error('Expected string\\n at [\"text\"]');\n let items: Array<string> | undefined;\n if (\"items\" in value) {\n if (!Array.isArray(value.items)) throw new Error('Expected array\\n at [\"items\"]');\n items = Array.from(value.items, (item, index) => {\n if (typeof item !== \"string\") throw new Error(`Expected string\\n at [\"items\"][${index}]`);\n return item;\n });\n }\n const kind = value.kind;\n if (kind !== \"number\" && kind !== \"money\" && kind !== \"list\" && kind !== \"text\")\n throw new Error('Expected \"number\" | \"money\" | \"list\" | \"text\"\\n at [\"kind\"]');\n return { kind, value: value.value, text: value.text, ...(items === undefined ? {} : { items }) };\n}\n\n/** Decode facts loaded from an application module before shipping them to the runtime. */\nexport function decodeFacts(input: unknown): Facts {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) {\n throw new Error(\"Document facts must be an object\");\n }\n return Object.fromEntries(Object.entries(input).map(([id, definition]) => {\n if (!id) throw new Error(\"A document fact needs a nonempty id\");\n return [id, validateDefinition(definition)];\n }));\n}\n\n/** Register code-owned values for ordinary React rendering as well as capture. */\nexport function defineFacts<const F extends Facts>(facts: F): F {\n for (const [id, definition] of Object.entries(facts)) {\n if (!id) throw new Error(\"A document fact needs a nonempty id\");\n registeredFacts.set(id, validateDefinition(definition));\n }\n return facts;\n}\nfunction finite(value: number): number {\n if (!Number.isFinite(value)) throw new Error(`Document fact must be finite: ${value}`);\n return value;\n}\nexport const fact = {\n number(value: number, options?: Intl.NumberFormatOptions): FactDefinition {\n const validated = finite(value);\n return { kind: \"number\", value: validated, text: new Intl.NumberFormat(\"en-US\", options).format(validated) };\n },\n money(value: number, options: { currency?: string; per?: string } = {}): FactDefinition {\n const validated = finite(value);\n const text = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\", currency: options.currency ?? \"USD\",\n }).format(validated);\n return { kind: \"money\", value: validated, text: options.per === undefined ? text : `${text} per ${options.per}` };\n },\n list<T extends Json>(values: ReadonlyArray<T>, options?: { format: (value: T) => string }): FactDefinition {\n const items = values.map((value) => options === undefined ? String(value) : options.format(value));\n return { kind: \"list\", value: [...values], items, text: new Intl.ListFormat(\"en-US\", { style: \"long\", type: \"conjunction\" }).format(items) };\n },\n text(value: string): FactDefinition { return { kind: \"text\", value, text: value }; },\n};\nexport function resolveFact(id: string, facts?: Facts): FactDefinition {\n const value = facts === undefined ? registeredFacts.get(id) : facts[id];\n if (value === undefined) throw new Error(`Unknown document fact \"${id}\"; register it with defineFacts`);\n return value;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AnyRouteMatch } from "@tanstack/react-router";
1
+ import { AnyRoute, AnyRouteMatch } from "@tanstack/react-router";
2
2
  //#region node_modules/schema-dts-lib/dist/index.d.ts
3
3
  /**
4
4
  * Minimal valid JSON-LD object.
@@ -2302,7 +2302,7 @@ interface CreativeWorkLeaf extends CreativeWorkBase {
2302
2302
  "@type": "CreativeWork";
2303
2303
  }
2304
2304
  /** The most generic kind of creative work, including books, movies, photographs, software programs, etc. */
2305
- type CreativeWork = CreativeWorkLeaf | AmpStory | ArchiveComponent | Article | Atlas | Blog | Book | Certification | Chapter | Claim | Clip | Code | Collection | ComicStory | Comment | Conversation | Course | CreativeWorkSeason | CreativeWorkSeries | Credential | DataCatalog | Dataset | DefinedTermSet | Diet | DigitalDocument | Drawing | Episode | ExercisePlan | Game | Guide | HowTo | HowToDirection | HowToSection | HowToStep | HowToTip | HyperToc | HyperTocEntry | LearningResource | Legislation | Manuscript | Map | MathSolver | MediaObject | MediaReviewItem | Menu | MenuSection | Message | Movie | MusicComposition | MusicPlaylist | MusicRecording | Painting | Photograph | Play | Poster | PublicationIssue | PublicationVolume | Quotation | Review | Sculpture | Season | SheetMusic | ShortStory | SoftwareApplication | SoftwareSourceCode | SpecialAnnouncement | Statement | Thesis | TVSeason | TVSeries | VisualArtwork | WebContent | WebPage | WebPageElement | WebSite;
2305
+ type CreativeWork = CreativeWorkLeaf | AmpStory | ArchiveComponent | Article | Atlas | Blog | Book | Certification | Chapter | Claim | Clip | Code | Collection | ComicStory | Comment | Conversation | Course | CreativeWorkSeason | CreativeWorkSeries | Credential | DataCatalog | Dataset | DefinedTermSet | Diet | DigitalDocument | Drawing | Episode | ExercisePlan | Game | Guide | HowTo | HowToDirection | HowToSection | HowToStep | HowToTip | HyperToc | HyperTocEntry | LearningResource | Legislation | Manuscript | Map$1 | MathSolver | MediaObject | MediaReviewItem | Menu | MenuSection | Message | Movie | MusicComposition | MusicPlaylist | MusicRecording | Painting | Photograph | Play | Poster | PublicationIssue | PublicationVolume | Quotation | Review | Sculpture | Season | SheetMusic | ShortStory | SoftwareApplication | SoftwareSourceCode | SpecialAnnouncement | Statement | Thesis | TVSeason | TVSeries | VisualArtwork | WebContent | WebPage | WebPageElement | WebSite;
2306
2306
  interface CreativeWorkSeasonBase extends CreativeWorkBase {
2307
2307
  /** An actor (individual or a group), e.g. in TV, radio, movie, video games etc., or in an event. Actors can be associated with individual items or with a series, episode, clip. */
2308
2308
  "actor"?: SchemaValue<PerformingGroup | Person | IdReference, "actor">;
@@ -5467,7 +5467,7 @@ interface MapLeaf extends MapBase {
5467
5467
  "@type": "Map";
5468
5468
  }
5469
5469
  /** A map. */
5470
- type Map = MapLeaf;
5470
+ type Map$1 = MapLeaf;
5471
5471
  interface MapCategoryTypeLeaf extends EnumerationBase {
5472
5472
  "@type": "MapCategoryType";
5473
5473
  }
@@ -8176,7 +8176,7 @@ interface PlaceBase extends ThingBase {
8176
8176
  */
8177
8177
  "hasGS1DigitalLink"?: SchemaValue<URL, "hasGS1DigitalLink">;
8178
8178
  /** A URL to a map of the place. */
8179
- "hasMap"?: SchemaValue<Map | URL | IdReference, "hasMap">;
8179
+ "hasMap"?: SchemaValue<Map$1 | URL | IdReference, "hasMap">;
8180
8180
  /** A flag to signal that the item, event, or place is accessible for free. */
8181
8181
  "isAccessibleForFree"?: SchemaValue<Boolean, "isAccessibleForFree">;
8182
8182
  /** The International Standard of Industrial Classification of All Economic Activities (ISIC), Revision 4 code for a particular organization, business person, or place. */
@@ -11996,6 +11996,7 @@ type Zoo = ZooLeaf | string;
11996
11996
  * interface Register {
11997
11997
  * paths: FileRouteTypes["fullPaths"];
11998
11998
  * kinds: "page" | "article" | "hub";
11999
+ * facts: typeof facts;
11999
12000
  * }
12000
12001
  * }
12001
12002
  * ```
@@ -12120,8 +12121,118 @@ declare function applyTitleTemplate(template: string | undefined, title: string)
12120
12121
  declare module "@tanstack/react-router" {
12121
12122
  interface StaticDataRouteOption {
12122
12123
  seo?: RouteSeo | undefined;
12124
+ /** How this route supplies its authored document. */
12125
+ markdown?: "rendered" | "source" | undefined;
12126
+ /** Group label for this page in llms.txt. */
12127
+ llms?: string | undefined;
12123
12128
  }
12124
12129
  }
12125
12130
  //#endregion
12126
- export { Thing as _, SeoFaq as a, IdReference as b, SitemapPolicy as c, BreadcrumbList as d, FAQPage as f, Service as g, Organization as h, RouteSeo as i, applyTitleTemplate as l, ItemList as m, PublicPath as n, SeoKind as o, Graph as p, Register as r, SeoPageHead as s, JsonLdDocument as t, Article as u, WebSite as v, WithContext as y };
12127
- //# sourceMappingURL=declare-DVgdEdOL.d.ts.map
12131
+ //#region src/core/graph.d.ts
12132
+ /**
12133
+ * Where a node came from: `"route"` for a structural route declaration, or the
12134
+ * `source` label of the collection that produced the instance (e.g. "blog").
12135
+ */
12136
+ type SeoSource = string;
12137
+ interface SeoNode {
12138
+ /** Canonical path, no origin (e.g. "/pricing", "/blog/my-post"). */
12139
+ path: string;
12140
+ kind: SeoKind;
12141
+ source: SeoSource;
12142
+ /** Route-declared authored-document mode, inherited by collection instances. */
12143
+ markdown?: "rendered" | "source" | undefined;
12144
+ /** Group label for llms.txt, inherited by collection instances. */
12145
+ llms?: string | undefined;
12146
+ /** Route-declared policy, or synthesized (kind + inherited sitemap) for instances. */
12147
+ policy: RouteSeo;
12148
+ /**
12149
+ * The head the page ships: a static route's declared `head`, or an instance's
12150
+ * title (through its route's `titleTemplate`) and description. Absent when the
12151
+ * route computes its head at request time and no collection supplies it.
12152
+ */
12153
+ head?: SeoNodeHead | undefined;
12154
+ instance?: {
12155
+ title: string;
12156
+ description?: string | undefined;
12157
+ publishedAt?: string | undefined;
12158
+ modifiedAt?: string | undefined;
12159
+ } | undefined;
12160
+ }
12161
+ /**
12162
+ * A node's head as graph data: the full title, description, and FAQ text. An
12163
+ * instance without a description still carries its title.
12164
+ */
12165
+ interface SeoNodeHead {
12166
+ readonly title: string;
12167
+ readonly description?: string | undefined;
12168
+ readonly faqs?: ReadonlyArray<{
12169
+ readonly question: string;
12170
+ readonly answer: string;
12171
+ }> | undefined;
12172
+ }
12173
+ type SeoEdgeType = "crumb-parent" | "related" | "redirect" | "collection-member";
12174
+ interface SeoEdge {
12175
+ from: string;
12176
+ to: string;
12177
+ type: SeoEdgeType;
12178
+ }
12179
+ interface SeoGraph {
12180
+ nodes: Map<string, SeoNode>;
12181
+ edges: Array<SeoEdge>;
12182
+ /** Exact-path ownership conflicts encountered while assembling graph sources. */
12183
+ collisions?: ReadonlyArray<{
12184
+ path: string;
12185
+ sources: ReadonlyArray<SeoSource>;
12186
+ }> | undefined;
12187
+ }
12188
+ /** One concrete page produced by a collection. */
12189
+ interface SeoInstance {
12190
+ /** Canonical path of the page (e.g. "/blog/my-post"). */
12191
+ readonly path: string;
12192
+ readonly title: string;
12193
+ readonly description?: string | undefined;
12194
+ readonly publishedAt?: string | undefined;
12195
+ readonly modifiedAt?: string | undefined;
12196
+ }
12197
+ interface SeoCollection {
12198
+ /**
12199
+ * The param route these instances render through (e.g. "/blog/$slug"). Instances
12200
+ * inherit this route's declared policy (kind + sitemap) — the graph reads it from
12201
+ * the structural node, so declarations stay the single source of truth.
12202
+ */
12203
+ readonly route: PublicPath;
12204
+ /** The `source` stamped on every node this collection produces (e.g. "blog"). */
12205
+ readonly source: SeoSource;
12206
+ readonly instances: ReadonlyArray<SeoInstance>;
12207
+ /** Edges the collection declares between its instances and the rest of the graph. */
12208
+ readonly edges?: ReadonlyArray<SeoEdge> | undefined;
12209
+ }
12210
+ /**
12211
+ * The structural route shape the graph walks: a TanStack route tree has it
12212
+ * before `init()`, and an adapter can build it from parsed route files.
12213
+ */
12214
+ interface SeoRouteNode {
12215
+ readonly options: {
12216
+ readonly path?: string | undefined;
12217
+ readonly staticData?: {
12218
+ readonly seo?: RouteSeo | undefined;
12219
+ readonly markdown?: SeoNode["markdown"];
12220
+ readonly llms?: SeoNode["llms"];
12221
+ } | undefined;
12222
+ };
12223
+ readonly children?: ReadonlyArray<SeoRouteNode> | undefined;
12224
+ }
12225
+ interface BuildSeoGraphInput {
12226
+ readonly routeTree: AnyRoute | SeoRouteNode;
12227
+ readonly collections?: ReadonlyArray<SeoCollection> | undefined;
12228
+ }
12229
+ /**
12230
+ * Build the SEO graph from route declarations and content collections.
12231
+ *
12232
+ * Synchronous: the caller materializes its collections before calling, so there is
12233
+ * no async work here. Callers own the origin.
12234
+ */
12235
+ declare function buildSeoGraph(input: BuildSeoGraphInput): SeoGraph;
12236
+ //#endregion
12237
+ export { WithContext as A, FAQPage as C, Service as D, Organization as E, Thing as O, BreadcrumbList as S, ItemList as T, SeoKind as _, SeoGraph as a, applyTitleTemplate as b, SeoNodeHead as c, buildSeoGraph as d, JsonLdDocument as f, SeoFaq as g, RouteSeo as h, SeoEdgeType as i, IdReference as j, WebSite as k, SeoRouteNode as l, Register as m, SeoCollection as n, SeoInstance as o, PublicPath as p, SeoEdge as r, SeoNode as s, BuildSeoGraphInput as t, SeoSource as u, SeoPageHead as v, Graph as w, Article as x, SitemapPolicy as y };
12238
+ //# sourceMappingURL=graph-D1PIndsC.d.ts.map
@@ -47,22 +47,30 @@ function mergeSeo(base, override) {
47
47
  */
48
48
  function walkRoutes(route, parentPath, isRoot, nodes, edges, crumbStack) {
49
49
  const path = isRoot ? "/" : joinPath(parentPath, route.options.path);
50
- const seo = route.options.staticData?.seo;
51
- if (seo) {
50
+ const data = route.options.staticData;
51
+ const seo = data?.seo;
52
+ if (seo || data?.markdown !== void 0 || data?.llms !== void 0) {
52
53
  const existing = nodes.get(path);
53
54
  if (existing) {
54
- existing.policy = mergeSeo(existing.policy, seo);
55
+ if (seo) existing.policy = mergeSeo(existing.policy, seo);
55
56
  existing.kind = existing.policy.kind;
56
- existing.head = declaredHead(seo) ?? existing.head;
57
- } else nodes.set(path, {
58
- path,
59
- kind: seo.kind,
60
- source: "route",
61
- policy: { ...seo },
62
- head: declaredHead(seo)
63
- });
57
+ existing.head = (seo && declaredHead(seo)) ?? existing.head;
58
+ existing.markdown = data?.markdown ?? existing.markdown;
59
+ existing.llms = data?.llms ?? existing.llms;
60
+ } else {
61
+ const policy = seo ?? { kind: FALLBACK_KIND };
62
+ nodes.set(path, {
63
+ path,
64
+ kind: policy.kind,
65
+ source: "route",
66
+ policy: { ...policy },
67
+ head: seo && declaredHead(seo),
68
+ markdown: data?.markdown,
69
+ llms: data?.llms
70
+ });
71
+ }
64
72
  const nearestCrumbAncestor = crumbStack[crumbStack.length - 1];
65
- if (seo.crumb !== void 0 && nearestCrumbAncestor !== void 0) edges.push({
73
+ if (seo?.crumb !== void 0 && nearestCrumbAncestor !== void 0) edges.push({
66
74
  from: path,
67
75
  to: nearestCrumbAncestor,
68
76
  type: "crumb-parent"
@@ -104,6 +112,8 @@ function addCollection(nodes, edges, collisions, collection) {
104
112
  path: instance.path,
105
113
  kind,
106
114
  source: collection.source,
115
+ markdown: collectionNode?.markdown,
116
+ llms: collectionNode?.llms,
107
117
  policy: {
108
118
  kind,
109
119
  sitemap
@@ -159,4 +169,4 @@ function buildSeoGraph(input) {
159
169
  //#endregion
160
170
  export { buildSeoGraph as t };
161
171
 
162
- //# sourceMappingURL=graph-Bcsourlv.js.map
172
+ //# sourceMappingURL=graph-DMNOLXLu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-DMNOLXLu.js","names":[],"sources":["../src/core/graph.ts"],"sourcesContent":["/**\n * SEO graph — the derived model that projections (sitemap, robots), the CLI, and\n * the check engine all read from. Built from route declarations (`staticData.seo`)\n * plus consumer-supplied content collections. This module is pure: no React, no\n * env, no knowledge of where instances come from. Origins and env-derived values\n * are injected by the callers of the projections, never read here.\n *\n * A node is one of:\n * - a structural route (`source: \"route\"`), keyed by its normalized full path,\n * merging a layout route's declaration (crumb) with its index child's (kind,\n * sitemap policy) when both resolve to the same URL;\n * - a content instance (`source` = the collection's label), keyed by the page URL\n * and carrying page-level metadata.\n *\n * Edges: `crumb-parent` (breadcrumb ancestry), `related` (deliberate cross-links),\n * `collection-member` (membership in a curated set), and `redirect` (route aliases).\n * The route walk emits crumb/related/redirect edges from declarations; a collection\n * may declare any additional edges its instances need.\n */\n\nimport type { AnyRoute } from \"@tanstack/react-router\";\n\nimport { applyTitleTemplate, type PublicPath, type RouteSeo, type SeoKind } from \"./declare\";\n\n/**\n * Where a node came from: `\"route\"` for a structural route declaration, or the\n * `source` label of the collection that produced the instance (e.g. \"blog\").\n */\nexport type SeoSource = string;\n\nexport interface SeoNode {\n /** Canonical path, no origin (e.g. \"/pricing\", \"/blog/my-post\"). */\n path: string;\n kind: SeoKind;\n source: SeoSource;\n /** Route-declared authored-document mode, inherited by collection instances. */\n markdown?: \"rendered\" | \"source\" | undefined;\n /** Group label for llms.txt, inherited by collection instances. */\n llms?: string | undefined;\n /** Route-declared policy, or synthesized (kind + inherited sitemap) for instances. */\n policy: RouteSeo;\n /**\n * The head the page ships: a static route's declared `head`, or an instance's\n * title (through its route's `titleTemplate`) and description. Absent when the\n * route computes its head at request time and no collection supplies it.\n */\n head?: SeoNodeHead | undefined;\n instance?:\n | {\n title: string;\n description?: string | undefined;\n publishedAt?: string | undefined;\n modifiedAt?: string | undefined;\n }\n | undefined;\n}\n\n/**\n * A node's head as graph data: the full title, description, and FAQ text. An\n * instance without a description still carries its title.\n */\nexport interface SeoNodeHead {\n readonly title: string;\n readonly description?: string | undefined;\n readonly faqs?: ReadonlyArray<{ readonly question: string; readonly answer: string }> | undefined;\n}\n\nexport type SeoEdgeType = \"crumb-parent\" | \"related\" | \"redirect\" | \"collection-member\";\n\nexport interface SeoEdge {\n from: string;\n to: string;\n type: SeoEdgeType;\n}\n\nexport interface SeoGraph {\n nodes: Map<string, SeoNode>;\n edges: Array<SeoEdge>;\n /** Exact-path ownership conflicts encountered while assembling graph sources. */\n collisions?: ReadonlyArray<{ path: string; sources: ReadonlyArray<SeoSource> }> | undefined;\n}\n\n/** One concrete page produced by a collection. */\nexport interface SeoInstance {\n /** Canonical path of the page (e.g. \"/blog/my-post\"). */\n readonly path: string;\n readonly title: string;\n readonly description?: string | undefined;\n readonly publishedAt?: string | undefined;\n readonly modifiedAt?: string | undefined;\n}\n\nexport interface SeoCollection {\n /**\n * The param route these instances render through (e.g. \"/blog/$slug\"). Instances\n * inherit this route's declared policy (kind + sitemap) — the graph reads it from\n * the structural node, so declarations stay the single source of truth.\n */\n readonly route: PublicPath;\n /** The `source` stamped on every node this collection produces (e.g. \"blog\"). */\n readonly source: SeoSource;\n readonly instances: ReadonlyArray<SeoInstance>;\n /** Edges the collection declares between its instances and the rest of the graph. */\n readonly edges?: ReadonlyArray<SeoEdge> | undefined;\n}\n\n/**\n * The structural route shape the graph walks: a TanStack route tree has it\n * before `init()`, and an adapter can build it from parsed route files.\n */\nexport interface SeoRouteNode {\n readonly options: {\n readonly path?: string | undefined;\n readonly staticData?:\n | {\n readonly seo?: RouteSeo | undefined;\n readonly markdown?: SeoNode[\"markdown\"];\n readonly llms?: SeoNode[\"llms\"];\n }\n | undefined;\n };\n readonly children?: ReadonlyArray<SeoRouteNode> | undefined;\n}\n\nexport interface BuildSeoGraphInput {\n readonly routeTree: AnyRoute | SeoRouteNode;\n readonly collections?: ReadonlyArray<SeoCollection> | undefined;\n}\n\n/** Kind for instances whose collection route carries no declaration to inherit. */\nconst FALLBACK_KIND: SeoKind = \"page\";\n\n\n/**\n * Join a child's local path onto its parent's computed full path with the same\n * semantics as TanStack's route init (relative segments, index \"/\" inherits the\n * parent, pathless/group routes are transparent), then normalize trailing slashes.\n */\nfunction joinPath(parent: string, seg: string | undefined): string {\n if (seg === undefined) return parent; // pathless layout / route group\n if (seg === \"/\") return parent; // index route resolves to its parent's URL\n const trimmed = seg.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const joined = `${parent === \"/\" ? \"\" : parent}/${trimmed}`;\n return joined.replace(/\\/{2,}/g, \"/\");\n}\n\n/** A route's declared head as graph data: templated title, description, and plain FAQs. */\nfunction declaredHead(seo: RouteSeo): SeoNodeHead | undefined {\n if (seo.head === undefined) return undefined;\n return {\n title: applyTitleTemplate(seo.titleTemplate, seo.head.title),\n description: seo.head.description,\n faqs: seo.head.faqs?.map(({ question, answer }) => ({ question, answer })),\n };\n}\n\n/** Merge a route's declaration into an existing same-path node (deeper route wins). */\nfunction mergeSeo(base: RouteSeo, override: RouteSeo): RouteSeo {\n return {\n kind: override.kind,\n crumb: override.crumb ?? base.crumb,\n sitemap: override.sitemap ?? base.sitemap,\n robots: override.robots ?? base.robots,\n related: override.related ?? base.related,\n link: override.link ?? base.link,\n redirectTo: override.redirectTo ?? base.redirectTo,\n modifiedAt: override.modifiedAt ?? base.modifiedAt,\n head: override.head ?? base.head,\n titleTemplate: override.titleTemplate ?? base.titleTemplate,\n };\n}\n\n/**\n * Walk the route tree building structural nodes and crumb-parent edges. `crumbStack`\n * holds the paths of crumb-declaring ancestors so each crumb node links to its nearest\n * crumb ancestor down the real route-parent chain (matching render-time breadcrumbs).\n */\nfunction walkRoutes(\n route: SeoRouteNode,\n parentPath: string,\n isRoot: boolean,\n nodes: Map<string, SeoNode>,\n edges: Array<SeoEdge>,\n crumbStack: Array<string>,\n): void {\n const path = isRoot ? \"/\" : joinPath(parentPath, route.options.path);\n const data = route.options.staticData;\n const seo = data?.seo;\n\n if (seo || data?.markdown !== undefined || data?.llms !== undefined) {\n const existing = nodes.get(path);\n if (existing) {\n if (seo) existing.policy = mergeSeo(existing.policy, seo);\n existing.kind = existing.policy.kind;\n // A head renders with its own route's template, never one merged from a layout.\n existing.head = (seo && declaredHead(seo)) ?? existing.head;\n existing.markdown = data?.markdown ?? existing.markdown;\n existing.llms = data?.llms ?? existing.llms;\n } else {\n const policy = seo ?? { kind: FALLBACK_KIND };\n nodes.set(path, {\n path,\n kind: policy.kind,\n source: \"route\",\n policy: { ...policy },\n head: seo && declaredHead(seo),\n markdown: data?.markdown,\n llms: data?.llms,\n });\n }\n\n const nearestCrumbAncestor = crumbStack[crumbStack.length - 1];\n if (seo?.crumb !== undefined && nearestCrumbAncestor !== undefined) {\n edges.push({ from: path, to: nearestCrumbAncestor, type: \"crumb-parent\" });\n }\n }\n\n const pushedCrumb = seo?.crumb !== undefined;\n if (pushedCrumb) crumbStack.push(path);\n for (const child of route.children ?? []) {\n walkRoutes(child, path, false, nodes, edges, crumbStack);\n }\n if (pushedCrumb) crumbStack.pop();\n}\n\n/**\n * Add one collection's instance nodes, inheriting kind + sitemap policy from the\n * collection route's declaration, then append the edges it declares. A path already\n * owned by another node is a collision: the first owner keeps the path and the\n * conflict is reported (the `path-owner-collision` check turns it into a violation).\n */\nfunction addCollection(\n nodes: Map<string, SeoNode>,\n edges: Array<SeoEdge>,\n collisions: Array<{ path: string; sources: ReadonlyArray<SeoSource> }>,\n collection: SeoCollection,\n): void {\n const collectionNode = nodes.get(collection.route);\n const kind = collectionNode?.kind ?? FALLBACK_KIND;\n const sitemap = collectionNode?.policy.sitemap;\n const titleTemplate = collectionNode?.policy.titleTemplate;\n\n /**\n * Paths this collection lost to an earlier owner. Their instances never enter\n * the graph, so any edge declared out of them would dangle — and the dead-edge\n * check only validates an edge's `to`, so nothing downstream would catch it.\n */\n const rejected = new Set<string>();\n\n for (const instance of collection.instances) {\n const existing = nodes.get(instance.path);\n if (existing) {\n collisions.push({ path: instance.path, sources: [existing.source, collection.source] });\n rejected.add(instance.path);\n continue;\n }\n nodes.set(instance.path, {\n path: instance.path,\n kind,\n source: collection.source,\n markdown: collectionNode?.markdown,\n llms: collectionNode?.llms,\n policy: { kind, sitemap },\n head: { title: applyTitleTemplate(titleTemplate, instance.title), description: instance.description },\n instance: {\n title: instance.title,\n description: instance.description,\n publishedAt: instance.publishedAt,\n modifiedAt: instance.modifiedAt,\n },\n });\n }\n\n for (const edge of collection.edges ?? []) {\n if (rejected.has(edge.from)) continue;\n edges.push(edge);\n }\n}\n\n/**\n * Build the SEO graph from route declarations and content collections.\n *\n * Synchronous: the caller materializes its collections before calling, so there is\n * no async work here. Callers own the origin.\n */\nexport function buildSeoGraph(input: BuildSeoGraphInput): SeoGraph {\n const nodes = new Map<string, SeoNode>();\n const edges: Array<SeoEdge> = [];\n const collisions: Array<{ path: string; sources: ReadonlyArray<SeoSource> }> = [];\n\n walkRoutes(input.routeTree as unknown as SeoRouteNode, \"/\", true, nodes, edges, []);\n\n for (const collection of input.collections ?? []) {\n addCollection(nodes, edges, collisions, collection);\n }\n\n for (const node of nodes.values()) {\n if (node.source !== \"route\") continue;\n for (const to of node.policy.related ?? []) {\n edges.push({ from: node.path, to, type: \"related\" });\n }\n if (node.policy.redirectTo !== undefined) {\n edges.push({ from: node.path, to: node.policy.redirectTo, type: \"redirect\" });\n }\n }\n\n return { nodes, edges, collisions };\n}\n"],"mappings":"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"}
@@ -0,0 +1,204 @@
1
+ import { a as SeoGraph, h as RouteSeo, n as SeoCollection, o as SeoInstance, p as PublicPath, r as SeoEdge, s as SeoNode, u as SeoSource } from "./graph-D1PIndsC.js";
2
+ import { N as SimpleEdge, w as AnchorRegion } from "./content-DgNr7apS.js";
3
+ import { f as Message, m as SectionDocument, p as PageDocument } from "./page-heads-BfjWQvau.js";
4
+ import "./projections-Bk-qOOsR.js";
5
+ import { AnyRouter } from "@tanstack/react-router";
6
+ //#region src/markdown/hash.d.ts
7
+ declare const contentHash: (value: unknown) => string;
8
+ //#endregion
9
+ //#region src/markdown/markdown.d.ts
10
+ interface MarkdownWriter {
11
+ text(value: Message | string | undefined): string;
12
+ heading(level: number, value: Message | string | undefined): string;
13
+ table(rows: ReadonlyArray<ReadonlyArray<Message | string>>): string;
14
+ }
15
+ interface SectionKind {
16
+ readonly kind: string;
17
+ readonly markdown: (section: SectionDocument, md: MarkdownWriter) => string;
18
+ }
19
+ /** A kind can be passed directly to Section; importing its definition registers its serializer. */
20
+ declare function defineSectionKind<const K extends string>(definition: SectionKind & {
21
+ kind: K;
22
+ }): SectionKind & {
23
+ kind: K;
24
+ };
25
+ declare function sectionMarkdown(section: SectionDocument, options?: {
26
+ includeHumans?: boolean;
27
+ }): string;
28
+ /** One public twin, in authored section order. Human-only messages never enter it. */
29
+ declare function documentMarkdown(document: PageDocument, site: string): string;
30
+ //#endregion
31
+ //#region src/markdown/llms.d.ts
32
+ interface LlmsPage {
33
+ document: Pick<PageDocument, "path" | "title" | "description">;
34
+ llms?: string;
35
+ }
36
+ declare function llmsSection(path: string): string;
37
+ declare function llmsMarkdown(pages: ReadonlyArray<LlmsPage>, site: string): string;
38
+ //#endregion
39
+ //#region src/markdown/lock.d.ts
40
+ /** Stable, reviewable snapshot of document and message hashes with source locations. */
41
+ interface MarkdownLock {
42
+ readonly version: 1;
43
+ readonly pages: Readonly<Record<string, {
44
+ readonly document: string;
45
+ readonly messages: Readonly<Record<string, string>>;
46
+ }>>;
47
+ }
48
+ /** Build the v1 lock without changing document hashes, message hashes, or sources. */
49
+ declare function createMarkdownLock(documents: ReadonlyArray<PageDocument>): MarkdownLock;
50
+ //#endregion
51
+ //#region src/core/collections.d.ts
52
+ /** What a page contributes: its instance metadata plus the paths it links to deliberately. */
53
+ interface ContentEntry extends Omit<SeoInstance, "path"> {
54
+ /** Defaults to the page's `url`. */
55
+ readonly path?: string | undefined;
56
+ /** Deliberate cross-links, emitted as `related` edges from this page. */
57
+ readonly related?: ReadonlyArray<string> | undefined;
58
+ }
59
+ interface ContentCollectionOptions<Page> {
60
+ /** The param route these pages render through, e.g. `/blog/$slug`. */
61
+ readonly route: PublicPath;
62
+ /** The `source` label stamped on every node, e.g. `"blog"`. */
63
+ readonly source: SeoSource;
64
+ readonly pages: ReadonlyArray<Page>;
65
+ /** Map one page to its entry; decode frontmatter here. */
66
+ readonly entry: (page: Page) => ContentEntry;
67
+ /** Edges beyond `related` (crumb parents, collection membership). */
68
+ readonly edges?: ReadonlyArray<SeoEdge> | undefined;
69
+ }
70
+ declare function contentCollection<Page extends {
71
+ readonly url: string;
72
+ }>(options: ContentCollectionOptions<Page>): SeoCollection;
73
+ //#endregion
74
+ //#region src/core/wire.d.ts
75
+ interface SeoGraphJson {
76
+ readonly nodes: ReadonlyArray<SeoNode>;
77
+ readonly edges: ReadonlyArray<SeoEdge>;
78
+ readonly collisions?: ReadonlyArray<{
79
+ readonly path: string;
80
+ readonly sources: ReadonlyArray<SeoSource>;
81
+ }> | undefined;
82
+ }
83
+ declare function graphToJson(graph: SeoGraph): SeoGraphJson;
84
+ declare function graphFromJson(json: SeoGraphJson): SeoGraph;
85
+ //#endregion
86
+ //#region src/core/link-candidates.d.ts
87
+ /** One proposed contextual link, in the direction the link would be authored. */
88
+ interface LinkCandidatePair {
89
+ readonly source: string;
90
+ readonly destination: string;
91
+ /** The cluster the pair shares: a top-level section, or `kind:<kind>`. */
92
+ readonly cluster: string;
93
+ /** Why the pair is plausible, e.g. `same top-level section "/blog"`. */
94
+ readonly reason: string;
95
+ }
96
+ interface LinkCandidateOptions {
97
+ /** Maximum candidates to return after ordering (default 50). */
98
+ readonly limit?: number;
99
+ /** Restrict to these clusters; a filter may be a section or a bare kind. */
100
+ readonly clusters?: ReadonlyArray<string>;
101
+ /** Anchors already served in HTML. Only body-region edges exclude their own direction. */
102
+ readonly renderedEdges?: ReadonlyArray<SimpleEdge & {
103
+ readonly region?: AnchorRegion;
104
+ }>;
105
+ }
106
+ /** One cluster and how many candidate pairs it contributes (before `limit`). */
107
+ interface LinkClusterSummary {
108
+ readonly key: string;
109
+ readonly candidates: number;
110
+ }
111
+ interface LinkCandidateResult {
112
+ readonly candidates: ReadonlyArray<LinkCandidatePair>;
113
+ /** Candidate pairs before `limit` was applied (after cluster filters). */
114
+ readonly total: number;
115
+ readonly truncated: boolean;
116
+ readonly clusters: ReadonlyArray<LinkClusterSummary>;
117
+ }
118
+ /** Directionless pair key, so an edge in either direction means "connected". */
119
+ declare const undirectedEdgeKey: (from: string, to: string) => string;
120
+ /** A `--cluster` filter matches a section by name, or a kind via its bare label. */
121
+ declare const matchesClusterFilter: (cluster: string, filter: string) => boolean;
122
+ /** Human reason string for a candidate's source page, used when handing a plan to Jev. */
123
+ declare const candidateSourceText: (node: SeoNode) => string;
124
+ /**
125
+ * Enumerate reviewable contextual-link candidates from the declared graph.
126
+ *
127
+ * Excluded: self-pairs, pages that are not sitemap-eligible, pairs already
128
+ * declared as a `related` edge, and — when {@link LinkCandidateOptions.renderedEdges}
129
+ * is supplied — pairs already rendered as an anchor. Queries, hashes, and
130
+ * trailing slashes are normalized so both sides compare by the graph's path key.
131
+ */
132
+ declare const generateLinkCandidates: (graph: SeoGraph, options?: LinkCandidateOptions) => LinkCandidateResult;
133
+ /**
134
+ * Decode a rendered-edge dump supplied to the CLI: either a bare array of
135
+ * `{ from, to }` edges, or an object carrying `edges` (or `internalEdges`).
136
+ * Throws on any other shape — this is user input, not a provider payload.
137
+ */
138
+ declare const decodeRenderedEdges: (input: unknown) => ReadonlyArray<SimpleEdge & {
139
+ readonly region?: AnchorRegion;
140
+ }>;
141
+ //#endregion
142
+ //#region src/core/inspect-html.d.ts
143
+ /**
144
+ * Read a rendered `<head>` and validate it: title, meta
145
+ * (description/robots/og/twitter), canonical link, and `application/ld+json`
146
+ * blocks, with a minimal per-type JSON-LD check.
147
+ *
148
+ * This is the pure half of `pagegraph inspect --live` — the half worth having on its
149
+ * own. The CLI fetches a URL and hands the body here; a test suite can render a
150
+ * page and hand *that* here, asserting the head it actually ships. Both get the
151
+ * same verdict, because it is the same function.
152
+ *
153
+ * No HTML-parsing dependency, by design: a `<head>` is small and well-formed, so
154
+ * string/regex scanning is enough, and the package's core stays zero-dependency
155
+ * (which is also why the object guard below is hand-rolled — `effect/Predicate`
156
+ * is not reachable from this entry). It is honest about its limits: it does not
157
+ * build a DOM, so exotic markup (commented-out tags, CDATA, attributes spanning
158
+ * constructs) is out of scope. This validates *rendered output*, not arbitrary
159
+ * HTML.
160
+ *
161
+ * `issues` are blocking: a non-empty list makes `pagegraph inspect --live` exit 1.
162
+ * Required tags are `<title>`, `meta[name=description]`, and
163
+ * `link[rel=canonical]`; any JSON-LD that fails to parse or fails its minimal
164
+ * schema is also blocking.
165
+ */
166
+ interface JsonLdReport {
167
+ type: string;
168
+ valid: boolean;
169
+ errors: Array<string>;
170
+ }
171
+ interface LiveHeadReport {
172
+ url: string;
173
+ status: number;
174
+ title?: string | undefined;
175
+ description?: string | undefined;
176
+ canonical?: string | undefined;
177
+ robots?: string | undefined;
178
+ og: Record<string, string>;
179
+ twitter: Record<string, string>;
180
+ jsonLd: Array<JsonLdReport>;
181
+ issues: Array<string>;
182
+ }
183
+ /** Parse a rendered HTML document's `<head>` into a report. Pure. */
184
+ declare const inspectHtml: (url: string, status: number, html: string) => LiveHeadReport;
185
+ /** A non-empty `issues` list fails `pagegraph inspect --live` (exit 1). */
186
+ declare const hasBlockingIssues: (report: LiveHeadReport) => boolean;
187
+ //#endregion
188
+ //#region src/core/resolve-route-link.d.ts
189
+ /**
190
+ * Resolve a route's declared `seo.link` card (title + description) by its full
191
+ * path.
192
+ *
193
+ * `useRouter()` returns a Router typed to this app's exact route tree, so
194
+ * `routesByPath` is keyed by the literal `FileRouteTypes["fullPaths"]` union —
195
+ * but callers here (declared `related` targets) hold arbitrary runtime path
196
+ * strings, not that literal type. `routesByPath` and `routesById` are plain
197
+ * Records on every Router instance regardless of which route tree it's
198
+ * parameterized over, so this narrows to that structural shape once, at this
199
+ * boundary, instead of threading an `AnyRoute` cast through every call site.
200
+ */
201
+ declare function resolveRouteLink(router: AnyRouter, path: string): RouteSeo["link"];
202
+ //#endregion
203
+ export { sectionMarkdown as A, LlmsPage as C, SectionKind as D, MarkdownWriter as E, defineSectionKind as O, createMarkdownLock as S, llmsSection as T, graphToJson as _, inspectHtml as a, contentCollection as b, LinkCandidateResult as c, decodeRenderedEdges as d, generateLinkCandidates as f, graphFromJson as g, SeoGraphJson as h, hasBlockingIssues as i, contentHash as j, documentMarkdown as k, LinkClusterSummary as l, undirectedEdgeKey as m, JsonLdReport as n, LinkCandidateOptions as o, matchesClusterFilter as p, LiveHeadReport as r, LinkCandidatePair as s, resolveRouteLink as t, candidateSourceText as u, ContentCollectionOptions as v, llmsMarkdown as w, MarkdownLock as x, ContentEntry as y };
204
+ //# sourceMappingURL=index-C5w9ripz.d.ts.map