pagegraph 0.12.0 → 0.14.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 (160) hide show
  1. package/README.md +325 -66
  2. package/dist/anthropic-messages-TqKpKmfW.js +1067 -0
  3. package/dist/anthropic-messages-TqKpKmfW.js.map +1 -0
  4. package/dist/audit.d.ts +4 -2
  5. package/dist/audit.js +5 -5
  6. package/dist/audit.js.map +1 -1
  7. package/dist/azure-openai-responses-DJjBLwoI.js +231 -0
  8. package/dist/azure-openai-responses-DJjBLwoI.js.map +1 -0
  9. package/dist/build-only/audit.js +1 -1
  10. package/dist/build-only/claims.d.ts +1 -0
  11. package/dist/build-only/claims.js +6 -0
  12. package/dist/build-only/claims.js.map +1 -0
  13. package/dist/build-only/config.js +1 -1
  14. package/dist/build-only/oxlint.d.ts +1 -0
  15. package/dist/build-only/oxlint.js +6 -0
  16. package/dist/build-only/oxlint.js.map +1 -0
  17. package/dist/build-only/tanstack-start.js +1 -1
  18. package/dist/build-only/vite.js +1 -1
  19. package/dist/{checks-uZk5Em9A.js → checks-BS-aDXB_.js} +2 -2
  20. package/dist/checks-BS-aDXB_.js.map +1 -0
  21. package/dist/claims-Bch7Lxlo.d.ts +92 -0
  22. package/dist/claims-Bv7voeEV.js +431 -0
  23. package/dist/claims-Bv7voeEV.js.map +1 -0
  24. package/dist/claims.d.ts +94 -0
  25. package/dist/claims.js +2 -0
  26. package/dist/cli.js +17607 -43387
  27. package/dist/cli.js.map +1 -1
  28. package/dist/cloudflare-workers-ai-system-one-f3ZrvNYZ.js +41 -0
  29. package/dist/cloudflare-workers-ai-system-one-f3ZrvNYZ.js.map +1 -0
  30. package/dist/config.d.ts +9 -29
  31. package/dist/config.js +2 -2
  32. package/dist/config.js.map +1 -1
  33. package/dist/{content-kcEnQdfi.js → content-CHd0X4s6.js} +2 -2
  34. package/dist/{content-kcEnQdfi.js.map → content-CHd0X4s6.js.map} +1 -1
  35. package/dist/{content-B1cPtL8b.d.ts → content-DgNr7apS.d.ts} +4 -5
  36. package/dist/declare-D5o7hwmv.js.map +1 -1
  37. package/dist/diagnostics-Wt5faNDM.js +108 -0
  38. package/dist/diagnostics-Wt5faNDM.js.map +1 -0
  39. package/dist/dist-B6zg_Z3Z.js +4794 -0
  40. package/dist/dist-B6zg_Z3Z.js.map +1 -0
  41. package/dist/document-CztEu3Vd.d.ts +109 -0
  42. package/dist/document-Dvemcv7V.js +379 -0
  43. package/dist/document-Dvemcv7V.js.map +1 -0
  44. package/dist/env-api-keys-C-OA0yM-.js +124 -0
  45. package/dist/env-api-keys-C-OA0yM-.js.map +1 -0
  46. package/dist/error-body-CyiejW4b.js +103 -0
  47. package/dist/error-body-CyiejW4b.js.map +1 -0
  48. package/dist/facts-1OKclPOk.js +115 -0
  49. package/dist/facts-1OKclPOk.js.map +1 -0
  50. package/dist/github-copilot-headers-CEpMOpQF.js +24 -0
  51. package/dist/github-copilot-headers-CEpMOpQF.js.map +1 -0
  52. package/dist/google-generative-ai-BxABKIKO.js +330 -0
  53. package/dist/google-generative-ai-BxABKIKO.js.map +1 -0
  54. package/dist/google-shared-0gpM2jFi.js +19471 -0
  55. package/dist/google-shared-0gpM2jFi.js.map +1 -0
  56. package/dist/google-vertex-D8Eb5cbu.js +376 -0
  57. package/dist/google-vertex-D8Eb5cbu.js.map +1 -0
  58. package/dist/{declare-DVgdEdOL.d.ts → graph-D1PIndsC.d.ts} +117 -6
  59. package/dist/{graph-Bcsourlv.js → graph-DMNOLXLu.js} +23 -13
  60. package/dist/graph-DMNOLXLu.js.map +1 -0
  61. package/dist/hash-Dp7EzZUo.js +18 -0
  62. package/dist/hash-Dp7EzZUo.js.map +1 -0
  63. package/dist/headers-BRNMVpzu.js +19 -0
  64. package/dist/headers-BRNMVpzu.js.map +1 -0
  65. package/dist/index-C5w9ripz.d.ts +204 -0
  66. package/dist/index.d.ts +6 -160
  67. package/dist/index.js +37 -5
  68. package/dist/index.js.map +1 -1
  69. package/dist/inline-BbS93GAT.js +682 -0
  70. package/dist/inline-BbS93GAT.js.map +1 -0
  71. package/dist/json-parse-C10znB7l.js +383 -0
  72. package/dist/json-parse-C10znB7l.js.map +1 -0
  73. package/dist/llms-Dhr_5u2O.js +19 -0
  74. package/dist/llms-Dhr_5u2O.js.map +1 -0
  75. package/dist/markdown-C6dRE9zD.js +168 -0
  76. package/dist/markdown-C6dRE9zD.js.map +1 -0
  77. package/dist/markdown-path-lJB9BAVZ.js +6 -0
  78. package/dist/markdown-path-lJB9BAVZ.js.map +1 -0
  79. package/dist/{message-DT_CjUdt.js → message-CuBjgZ74.js} +2 -2
  80. package/dist/{message-DT_CjUdt.js.map → message-CuBjgZ74.js.map} +1 -1
  81. package/dist/mistral-conversations-CvaqBWjR.js +690 -0
  82. package/dist/mistral-conversations-CvaqBWjR.js.map +1 -0
  83. package/dist/models-DcT5qOEL.js +1203 -0
  84. package/dist/models-DcT5qOEL.js.map +1 -0
  85. package/dist/node-Ccvru9GE.js +26 -0
  86. package/dist/node-Ccvru9GE.js.map +1 -0
  87. package/dist/node-MEosBOF2.js +2403 -0
  88. package/dist/node-MEosBOF2.js.map +1 -0
  89. package/dist/openai-BCBEd2eO.js +17350 -0
  90. package/dist/openai-BCBEd2eO.js.map +1 -0
  91. package/dist/openai-codex-responses-vJqxeeRC.js +1268 -0
  92. package/dist/openai-codex-responses-vJqxeeRC.js.map +1 -0
  93. package/dist/openai-completions-Cb_fRAbI.js +989 -0
  94. package/dist/openai-completions-Cb_fRAbI.js.map +1 -0
  95. package/dist/openai-prompt-cache-Bc6qNWt0.js +10 -0
  96. package/dist/openai-prompt-cache-Bc6qNWt0.js.map +1 -0
  97. package/dist/openai-responses-sDuh82G5.js +268 -0
  98. package/dist/openai-responses-sDuh82G5.js.map +1 -0
  99. package/dist/openai-responses-shared-mhuo1Jov.js +599 -0
  100. package/dist/openai-responses-shared-mhuo1Jov.js.map +1 -0
  101. package/dist/openrouter-images-CRmBkzpX.js +127 -0
  102. package/dist/openrouter-images-CRmBkzpX.js.map +1 -0
  103. package/dist/oxlint.d.ts +58 -0
  104. package/dist/oxlint.js +202 -0
  105. package/dist/oxlint.js.map +1 -0
  106. package/dist/page-heads-BfjWQvau.d.ts +108 -0
  107. package/dist/{page-heads-6y-CARRQ.js → page-heads-BgbaIjj9.js} +3 -3
  108. package/dist/page-heads-BgbaIjj9.js.map +1 -0
  109. package/dist/pi-Bvw6J0gh.js +56795 -0
  110. package/dist/pi-Bvw6J0gh.js.map +1 -0
  111. package/dist/pi-messages-CejiVYvO.js +308 -0
  112. package/dist/pi-messages-CejiVYvO.js.map +1 -0
  113. package/dist/{projections-DeEFe7XT.d.ts → projections-Bk-qOOsR.d.ts} +2 -2
  114. package/dist/provider-env-t38GK5FF.js +39 -0
  115. package/dist/provider-env-t38GK5FF.js.map +1 -0
  116. package/dist/provider-retry-DzKoh1X7.js +80 -0
  117. package/dist/provider-retry-DzKoh1X7.js.map +1 -0
  118. package/dist/react.d.ts +3 -2
  119. package/dist/react.js +2 -1
  120. package/dist/react.js.map +1 -1
  121. package/dist/rolldown-runtime-B-lAHAz2.js +37 -0
  122. package/dist/sanitize-unicode-Ds14l9Om.js +28 -0
  123. package/dist/sanitize-unicode-Ds14l9Om.js.map +1 -0
  124. package/dist/sdk-C9_wiO6w.js +12503 -0
  125. package/dist/sdk-C9_wiO6w.js.map +1 -0
  126. package/dist/state-BuJP_3Lr.js +28102 -0
  127. package/dist/state-BuJP_3Lr.js.map +1 -0
  128. package/dist/system-one-shared-Dl10ZF7Z.js +167 -0
  129. package/dist/system-one-shared-Dl10ZF7Z.js.map +1 -0
  130. package/dist/tanstack-start/index.d.ts +29 -13
  131. package/dist/tanstack-start/index.js +391 -42
  132. package/dist/tanstack-start/index.js.map +1 -1
  133. package/dist/tanstack-start/markdown.d.ts +12 -0
  134. package/dist/tanstack-start/markdown.js +149 -0
  135. package/dist/tanstack-start/markdown.js.map +1 -0
  136. package/dist/tanstack-start/prerender-worker.d.ts +2 -0
  137. package/dist/tanstack-start/prerender-worker.js +7 -0
  138. package/dist/tanstack-start/prerender-worker.js.map +1 -0
  139. package/dist/tanstack-start/react.d.ts +7 -0
  140. package/dist/tanstack-start/react.js +12 -0
  141. package/dist/tanstack-start/react.js.map +1 -0
  142. package/dist/tanstack-start/server.d.ts +9 -3
  143. package/dist/tanstack-start/server.js +26 -2
  144. package/dist/tanstack-start/server.js.map +1 -1
  145. package/dist/transform-messages-BLJ8EfK6.js +468 -0
  146. package/dist/transform-messages-BLJ8EfK6.js.map +1 -0
  147. package/dist/typesafe-system-one-CwEWMV8P.js +25 -0
  148. package/dist/typesafe-system-one-CwEWMV8P.js.map +1 -0
  149. package/dist/{vite-graph-loader-01p1WxEq.js → vite-graph-loader-CsGiYNWE.js} +6 -5
  150. package/dist/vite-graph-loader-CsGiYNWE.js.map +1 -0
  151. package/dist/vite-graph-loader-DZCwejuJ.d.ts +133 -0
  152. package/package.json +47 -10
  153. package/dist/checks-uZk5Em9A.js.map +0 -1
  154. package/dist/graph-Bcsourlv.js.map +0 -1
  155. package/dist/graph-BxCY90vq.d.ts +0 -104
  156. package/dist/graph-CBIY71w6.d.ts +0 -57
  157. package/dist/page-heads-6y-CARRQ.js.map +0 -1
  158. package/dist/page-heads-DNkjMUEN.d.ts +0 -21
  159. package/dist/vite-graph-loader-01p1WxEq.js.map +0 -1
  160. package/dist/vite-graph-loader-Btgjz5RH.d.ts +0 -56
@@ -1,11 +1,20 @@
1
- import { t as buildSeoGraph } from "../graph-Bcsourlv.js";
1
+ import { t as buildSeoGraph } from "../graph-DMNOLXLu.js";
2
2
  import { n as graphToJson } from "../wire-B4RQMc4e.js";
3
- import { a as globToRegExp } from "../checks-uZk5Em9A.js";
3
+ import { a as globToRegExp } from "../checks-BS-aDXB_.js";
4
+ import { o as hashDocument } from "../inline-BbS93GAT.js";
5
+ import { t as decodeFacts } from "../facts-1OKclPOk.js";
6
+ import { n as documentMarkdown } from "../markdown-C6dRE9zD.js";
4
7
  import { i as seoRouteConfig, n as inMemoryWriteFs, r as resolveOptions, t as deriveRouteConfig } from "../route-config-35ZEzkNC.js";
5
- import { n as withCleanStdout } from "../vite-graph-loader-01p1WxEq.js";
6
- import { join, relative, resolve } from "node:path";
8
+ import { n as withCleanStdout } from "../vite-graph-loader-CsGiYNWE.js";
9
+ import { r as replayClaims } from "../claims-Bv7voeEV.js";
10
+ import { t as MARKDOWN_CAPTURE_PATH } from "../markdown-path-lJB9BAVZ.js";
11
+ import { existsSync } from "node:fs";
12
+ import { mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
13
+ import { dirname, join, relative, resolve, sep } from "node:path";
7
14
  import { Generator, getConfig } from "@tanstack/router-generator";
8
- import { createRunnableDevEnvironment, resolveConfig } from "vite";
15
+ import { createRunnableDevEnvironment, parseAst, resolveConfig } from "vite";
16
+ import * as Effect from "effect/Effect";
17
+ import * as Schema from "effect/Schema";
9
18
  import { pathToFileURL } from "node:url";
10
19
  //#region src/tanstack-start/routes.ts
11
20
  /**
@@ -60,7 +69,12 @@ const messageOf = (cause) => cause instanceof Error ? cause.message : String(cau
60
69
  const routeStaticData = (module, file) => {
61
70
  const route = module["Route"];
62
71
  if (route?.options === void 0) throw new Error(`${file} has no \`Route\` export`);
63
- return route.options.staticData;
72
+ const data = route.options.staticData;
73
+ return data === void 0 ? void 0 : {
74
+ seo: data.seo,
75
+ markdown: data.markdown,
76
+ llms: data.llms
77
+ };
64
78
  };
65
79
  const loadCollections = async (runner, root, fullPath) => {
66
80
  const path = relative(root, fullPath);
@@ -82,7 +96,8 @@ async function planGraph(root, options) {
82
96
  options,
83
97
  scan,
84
98
  routeFiles: [scan.root, ...scan.nodes].filter(participates).map((node) => node.fullPath),
85
- collectionsFile: options.collections === void 0 ? void 0 : resolve(root, options.collections)
99
+ collectionsFile: options.collections === void 0 ? void 0 : resolve(root, options.collections),
100
+ factsFile: options.facts === void 0 ? void 0 : resolve(root, options.facts)
86
101
  };
87
102
  }
88
103
  /**
@@ -121,18 +136,269 @@ async function evaluateGraph(runner, plan) {
121
136
  ...options.routeConfig,
122
137
  routesDirectory: options.routesDirectory
123
138
  })).robotsExclusions;
139
+ const site = {
140
+ origin: options.origin,
141
+ indexable: options.indexable ?? true,
142
+ robots: {
143
+ disallow: [.../* @__PURE__ */ new Set([...exclusions, ...options.robots?.disallow ?? []])].sort(),
144
+ contentSignal: options.robots?.contentSignal,
145
+ directives: options.robots?.directives
146
+ }
147
+ };
148
+ let facts;
149
+ if (plan.factsFile !== void 0) {
150
+ const path = relative(root, plan.factsFile);
151
+ try {
152
+ const module = await runner.import(plan.factsFile);
153
+ if (module["facts"] === void 0) throw new Error(`${path} has no \`facts\` export`);
154
+ facts = decodeFacts(module["facts"]);
155
+ } catch (cause) {
156
+ throw new Error(`pagegraph could not evaluate ${path}: ${messageOf(cause)}`, { cause });
157
+ }
158
+ }
124
159
  return {
125
160
  graph,
126
- site: {
127
- origin: options.origin,
128
- indexable: options.indexable ?? true,
129
- robots: {
130
- disallow: [.../* @__PURE__ */ new Set([...exclusions, ...options.robots?.disallow ?? []])].sort(),
131
- contentSignal: options.robots?.contentSignal,
132
- directives: options.robots?.directives
161
+ site,
162
+ markdown: options.markdown === void 0 ? null : { origin: options.markdown.origin },
163
+ facts
164
+ };
165
+ }
166
+ //#endregion
167
+ //#region src/markdown/schema.ts
168
+ const InlineNode = Schema.suspend(() => Schema.Union([
169
+ Schema.String,
170
+ Schema.Struct({
171
+ fact: Schema.String,
172
+ text: Schema.String
173
+ }),
174
+ Schema.Struct({
175
+ tag: Schema.Literals([
176
+ "em",
177
+ "strong",
178
+ "a",
179
+ "code",
180
+ "br"
181
+ ]),
182
+ href: Schema.optionalKey(Schema.String),
183
+ children: Schema.Array(InlineNode)
184
+ })
185
+ ]));
186
+ const Message = Schema.Struct({
187
+ hash: Schema.String,
188
+ tree: Schema.Array(InlineNode),
189
+ text: Schema.String,
190
+ source: Schema.String,
191
+ facts: Schema.Array(Schema.String),
192
+ audience: Schema.Literals([
193
+ "all",
194
+ "agents",
195
+ "humans"
196
+ ])
197
+ });
198
+ const BodyEntry = Schema.Union([Schema.Struct({
199
+ type: Schema.Literal("message"),
200
+ message: Message
201
+ }), Schema.Struct({
202
+ type: Schema.Literal("visual"),
203
+ text: Schema.String,
204
+ source: Schema.String,
205
+ audience: Schema.Literals([
206
+ "all",
207
+ "agents",
208
+ "humans"
209
+ ])
210
+ })]);
211
+ const PageDocument = Schema.Struct({
212
+ path: Schema.String,
213
+ hash: Schema.String,
214
+ title: Schema.String,
215
+ description: Schema.String,
216
+ sections: Schema.Array(Schema.Struct({
217
+ claimsMarkdown: Schema.optionalKey(Schema.String),
218
+ evidence: Schema.optionalKey(Schema.Boolean),
219
+ kind: Schema.String,
220
+ id: Schema.String,
221
+ title: Schema.optionalKey(Message),
222
+ language: Schema.optionalKey(Schema.String),
223
+ body: Schema.Array(BodyEntry),
224
+ items: Schema.Array(Schema.Struct({
225
+ title: Schema.optionalKey(Message),
226
+ href: Schema.optionalKey(Schema.String),
227
+ body: Schema.Array(BodyEntry),
228
+ cells: Schema.Array(Message),
229
+ source: Schema.String,
230
+ audience: Schema.Literals([
231
+ "all",
232
+ "agents",
233
+ "humans"
234
+ ])
235
+ })),
236
+ source: Schema.String,
237
+ audience: Schema.Literals([
238
+ "all",
239
+ "agents",
240
+ "humans"
241
+ ])
242
+ })),
243
+ messages: Schema.Array(Message),
244
+ facts: Schema.Array(Schema.String)
245
+ });
246
+ const CaptureBundle = Schema.Struct({
247
+ origin: Schema.String,
248
+ documents: Schema.Array(PageDocument),
249
+ heads: Schema.Array(Schema.Struct({
250
+ path: Schema.String,
251
+ title: Schema.String,
252
+ description: Schema.String
253
+ }))
254
+ });
255
+ //#endregion
256
+ //#region src/tanstack-start/persist-markdown.ts
257
+ /** File names must remain inside the package's capture and the client's output trees. */
258
+ function documentName(path) {
259
+ if (!path.startsWith("/") || path.startsWith("//") || /[?#\\$\u0000-\u001f\u007f]/.test(path) || path !== "/" && path.endsWith("/") || path.slice(1).split("/").some((part) => part === "." || part === ".." || part === "*" || part === "" && path !== "/")) throw new Error(`Invalid captured document path "${path}"`);
260
+ return path === "/" ? "index" : path.slice(1);
261
+ }
262
+ function verifyDocument(document) {
263
+ const name = documentName(document.path);
264
+ const { hash, ...content } = document;
265
+ if (!/^[0-9a-f]{64}$/.test(hash) || hashDocument(content) !== hash) throw new Error(`Invalid captured document hash for "${document.path}"`);
266
+ return name;
267
+ }
268
+ /** Only valid captures at their derived paths belong to pagegraph; preserve unrelated files. */
269
+ async function pruneDocuments(directory, paths) {
270
+ const visit = async (folder) => {
271
+ const entries = await readdir(folder, { withFileTypes: true });
272
+ for (const entry of entries) {
273
+ const file = resolve(folder, entry.name);
274
+ if (entry.isDirectory()) {
275
+ await visit(file);
276
+ continue;
133
277
  }
278
+ if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
279
+ const contents = await readFile(file, "utf8");
280
+ let document;
281
+ let name;
282
+ try {
283
+ document = Schema.decodeUnknownSync(Schema.fromJsonString(PageDocument))(contents, { onExcessProperty: "error" });
284
+ name = verifyDocument(document);
285
+ } catch {
286
+ continue;
287
+ }
288
+ if (file === resolve(directory, `${name}.json`) && !paths.has(document.path)) await unlink(file);
134
289
  }
135
290
  };
291
+ await visit(directory);
292
+ }
293
+ /** Documents, twins, and graph heads are persisted before prerender onSuccess returns. */
294
+ async function persistMarkdownCapture(html, root, clientOutDir, claims, facts = {}) {
295
+ const bundle = Schema.decodeUnknownSync(Schema.fromJsonString(CaptureBundle))(html, { onExcessProperty: "error" });
296
+ const origin = new URL(bundle.origin);
297
+ if (origin.protocol !== "https:" && origin.protocol !== "http:" || origin.origin !== bundle.origin) throw new Error(`Invalid captured markdown origin "${bundle.origin}"`);
298
+ const active = /* @__PURE__ */ new Set();
299
+ const destinations = /* @__PURE__ */ new Set();
300
+ const names = bundle.documents.map((document) => {
301
+ if (active.has(document.path)) throw new Error(`Duplicate captured document path "${document.path}"`);
302
+ active.add(document.path);
303
+ const name = verifyDocument(document);
304
+ if (destinations.has(name)) throw new Error(`Captured document paths share the output name "${name}"`);
305
+ destinations.add(name);
306
+ return name;
307
+ });
308
+ await unlink(resolve(clientOutDir, MARKDOWN_CAPTURE_PATH.slice(1)));
309
+ const directory = resolve(root, ".pagegraph/documents");
310
+ await mkdir(directory, { recursive: true });
311
+ for (const [index, document] of bundle.documents.entries()) {
312
+ const name = names[index];
313
+ const destination = resolve(directory, `${name}.json`);
314
+ const twin = resolve(clientOutDir, `${name}.md`);
315
+ await mkdir(dirname(destination), { recursive: true });
316
+ await writeFile(destination, JSON.stringify(document));
317
+ await mkdir(dirname(twin), { recursive: true });
318
+ await writeFile(twin, documentMarkdown(document, bundle.origin));
319
+ }
320
+ await pruneDocuments(directory, active);
321
+ await writeFile(resolve(root, ".pagegraph/heads.json"), `${JSON.stringify(bundle.heads)}\n`);
322
+ if (claims !== void 0) await Effect.runPromise(replayClaims(root, bundle.documents, bundle.heads, facts, claims));
323
+ }
324
+ //#endregion
325
+ //#region src/tanstack-start/source.ts
326
+ var DocumentSourceError = class extends Schema.TaggedError()("DocumentSourceError", { message: Schema.String }) {};
327
+ const SOURCE_COMPONENTS = /* @__PURE__ */ new Set([
328
+ "T",
329
+ "Title",
330
+ "Section",
331
+ "Visual",
332
+ "ForAgents",
333
+ "ForHumans"
334
+ ]);
335
+ const parse = Effect.fn("DocumentSource.parse")(function* (code, id) {
336
+ return yield* Effect.try({
337
+ try: () => parseAst(code, { lang: id.endsWith(".tsx") ? "tsx" : id.endsWith(".ts") ? "ts" : id.endsWith(".jsx") ? "jsx" : "js" }, id),
338
+ catch: (cause) => new DocumentSourceError({ message: `Cannot parse document source ${id}: ${cause instanceof Error ? cause.message : String(cause)}` })
339
+ });
340
+ });
341
+ function componentNames(program) {
342
+ const named = /* @__PURE__ */ new Set();
343
+ const sections = /* @__PURE__ */ new Set();
344
+ const namespaces = /* @__PURE__ */ new Set();
345
+ for (const node of program.body) {
346
+ if (node.type !== "ImportDeclaration" || node.source.value !== "pagegraph/react" || node.importKind === "type") continue;
347
+ for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier") {
348
+ if (specifier.importKind === "type") continue;
349
+ const imported = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
350
+ if (SOURCE_COMPONENTS.has(imported)) named.add(specifier.local.name);
351
+ if (imported === "Section") sections.add(specifier.local.name);
352
+ } else if (specifier.type === "ImportNamespaceSpecifier") namespaces.add(specifier.local.name);
353
+ }
354
+ return {
355
+ named,
356
+ sections,
357
+ namespaces
358
+ };
359
+ }
360
+ function memberParts(name) {
361
+ if (name.type === "JSXIdentifier") return [name.name];
362
+ if (name.type !== "JSXMemberExpression") return [];
363
+ return [...memberParts(name.object), name.property.name];
364
+ }
365
+ function isSourceComponent(name, names) {
366
+ const [base, ...members] = memberParts(name);
367
+ if (base === void 0) return false;
368
+ if (members.length === 0) return names.named.has(base);
369
+ if (names.namespaces.has(base)) return members.length === 1 && members[0] !== void 0 && SOURCE_COMPONENTS.has(members[0]) || members.length === 2 && members[0] === "Section" && members[1] === "Item";
370
+ return names.sections.has(base) && members.length === 1 && members[0] === "Item";
371
+ }
372
+ function walk(value, visit) {
373
+ if (Array.isArray(value)) for (const child of value) walk(child, visit);
374
+ else if (typeof value === "object" && value !== null) {
375
+ if (isOpeningElement(value)) visit(value);
376
+ for (const child of Object.values(value)) walk(child, visit);
377
+ }
378
+ }
379
+ function isOpeningElement(node) {
380
+ return "type" in node && node.type === "JSXOpeningElement";
381
+ }
382
+ function transformDocumentSource(code, id, root) {
383
+ const program = Effect.runSync(parse(code, id));
384
+ const names = componentNames(program);
385
+ const edits = [];
386
+ const file = relative(resolve(root), resolve(id)).split(sep).join("/");
387
+ walk(program, (node) => {
388
+ if (!isSourceComponent(node.name, names)) return;
389
+ if (node.attributes.some((attribute) => attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier" && attribute.name.name === "source")) return;
390
+ const line = code.slice(0, node.start).split("\n").length;
391
+ edits.push({
392
+ start: node.name.end,
393
+ end: node.name.end,
394
+ value: ` source=${JSON.stringify(`${file}:${line}`)}`
395
+ });
396
+ });
397
+ if (edits.length === 0) return void 0;
398
+ edits.sort((a, b) => b.start - a.start);
399
+ let transformed = code;
400
+ for (const edit of edits) transformed = transformed.slice(0, edit.start) + edit.value + transformed.slice(edit.end);
401
+ return { code: transformed };
136
402
  }
137
403
  //#endregion
138
404
  //#region src/tanstack-start/plugin.ts
@@ -164,7 +430,11 @@ const RESOLVED_RUNTIME_ID = `\0virtual:pagegraph/runtime`;
164
430
  const ENTRY_ID = "virtual:pagegraph/entry";
165
431
  const RESOLVED_ENTRY_ID = `\0${ENTRY_ID}`;
166
432
  /** The runtime entry imports the virtual module, so dep pre-bundling must leave it to this plugin. */
167
- const RUNTIME_ENTRY = "pagegraph/tanstack-start/server";
433
+ const RUNTIME_ENTRIES = [
434
+ "pagegraph/tanstack-start/server",
435
+ "pagegraph/tanstack-start/markdown",
436
+ "pagegraph/tanstack-start/react"
437
+ ];
168
438
  /** Where a build writes the graph environment's output, relative to the Vite root. */
169
439
  const BUILD_DIRECTORY = "node_modules/.cache/pagegraph/build";
170
440
  const runtimeModulesPlugin = {
@@ -179,10 +449,14 @@ const runtimeModulesPlugin = {
179
449
  })
180
450
  }
181
451
  };
182
- const runtimeModule = ({ graph, site }) => `export const graph = ${JSON.stringify(graphToJson(graph))};\nexport const site = ${JSON.stringify(site)};\n`;
452
+ const runtimeModule = ({ graph, site, markdown, facts }) => `export const graph = ${JSON.stringify(graphToJson(graph))};\nexport const site = ${JSON.stringify(site)};\nexport const markdown = ${JSON.stringify(markdown)};\nexport const facts = ${JSON.stringify(facts) ?? "undefined"};\n`;
183
453
  /** The graph environment's build entry: a loader per module the plan evaluates. */
184
454
  const entryModule = (plan) => {
185
- return `export const modules = {\n${[...plan.routeFiles, ...plan.collectionsFile === void 0 ? [] : [plan.collectionsFile]].map((file) => ` ${JSON.stringify(file)}: () => import(${JSON.stringify(file)}),`).join("\n")}\n};\n`;
455
+ return `export const modules = {\n${[
456
+ ...plan.routeFiles,
457
+ ...plan.collectionsFile === void 0 ? [] : [plan.collectionsFile],
458
+ ...plan.factsFile === void 0 ? [] : [plan.factsFile]
459
+ ].map((file) => ` ${JSON.stringify(file)}: () => import(${JSON.stringify(file)}),`).join("\n")}\n};\n`;
186
460
  };
187
461
  /** Build the graph environment with the build's config, import its output, and evaluate. */
188
462
  const buildGraph = async (builder, environment, plan) => {
@@ -196,8 +470,10 @@ const buildGraph = async (builder, environment, plan) => {
196
470
  } }, plan);
197
471
  };
198
472
  function pagegraph(options) {
473
+ if (options.claims !== void 0 && options.markdown === void 0) throw new Error("Claims require pagegraph({ markdown: { origin }, claims: { rules, model } }) so the build captures documents.");
199
474
  const api = { options };
200
- let root = "";
475
+ let root = process.cwd();
476
+ let clientOutDir = resolve(root, "dist/client");
201
477
  let server;
202
478
  let devEnvironment;
203
479
  /** The plan the graph environment's build entry is generated from. */
@@ -213,7 +489,7 @@ function pagegraph(options) {
213
489
  if (started === generation) return result;
214
490
  }
215
491
  };
216
- return [
492
+ return Object.assign([
217
493
  {
218
494
  name: PLUGIN_NAME,
219
495
  api,
@@ -244,14 +520,18 @@ function pagegraph(options) {
244
520
  return;
245
521
  }
246
522
  environmentConfig.optimizeDeps ??= {};
247
- environmentConfig.optimizeDeps.exclude = [...environmentConfig.optimizeDeps.exclude ?? [], RUNTIME_ENTRY];
523
+ environmentConfig.optimizeDeps.exclude = [...environmentConfig.optimizeDeps.exclude ?? [], ...RUNTIME_ENTRIES];
248
524
  },
249
525
  configResolved(resolved) {
250
526
  root = resolved.root;
527
+ clientOutDir = resolve(root, resolved.environments.client?.build.outDir ?? "dist/client");
251
528
  },
252
- configureServer(devServer) {
253
- server = devServer;
254
- devEnvironment = devServer.environments[GRAPH_ENVIRONMENT$1];
529
+ configureServer: {
530
+ order: "pre",
531
+ handler(devServer) {
532
+ server = devServer;
533
+ devEnvironment = devServer.environments[GRAPH_ENVIRONMENT$1];
534
+ }
255
535
  },
256
536
  hotUpdate({ file, modules }) {
257
537
  if (this.environment.name !== "pagegraph") return;
@@ -288,7 +568,7 @@ function pagegraph(options) {
288
568
  if (buildPlan === void 0) throw new Error(`[${PLUGIN_NAME}] ${ENTRY_ID} is only built by the pagegraph buildApp hook`);
289
569
  return entryModule(buildPlan);
290
570
  }
291
- if (this.environment.name === "pagegraph") return "export const graph = null;\nexport const site = null;\n";
571
+ if (this.environment.name === "pagegraph") return "export const graph = null;\nexport const site = null;\nexport const markdown = null;\nexport const facts = undefined;\n";
292
572
  if (current === void 0) {
293
573
  if (devEnvironment === void 0) throw new Error(`[${PLUGIN_NAME}] the graph was not built before ${this.environment.name} imported it; build with Vite's app builder (\`vite build\`).`);
294
574
  current = evaluateInDev(devEnvironment);
@@ -297,68 +577,137 @@ function pagegraph(options) {
297
577
  }
298
578
  }
299
579
  },
580
+ {
581
+ name: "pagegraph:markdown",
582
+ enforce: "pre",
583
+ applyToEnvironment: (environment) => environment.name !== GRAPH_ENVIRONMENT$1,
584
+ resolveId: {
585
+ filter: { id: /^virtual:pagegraph\/prerender-server$/ },
586
+ handler: (id) => `\0${id}`
587
+ },
588
+ load: {
589
+ filter: { id: /^\0virtual:pagegraph\/prerender-server$/ },
590
+ handler: () => `export { default } from ${JSON.stringify(resolve(root, options.markdown?.serverEntry ?? "dist/server/index.js"))};`
591
+ },
592
+ transform: {
593
+ filter: {
594
+ id: /\.[jt]sx?(\?|$)/,
595
+ code: /pagegraph\/react/
596
+ },
597
+ handler(code, id) {
598
+ const file = id.split("?")[0];
599
+ if (file === void 0 || file.includes("/node_modules/") || file.endsWith(".gen.ts") || !file.startsWith(`${root}/`)) return;
600
+ return transformDocumentSource(code, file, root);
601
+ }
602
+ },
603
+ configurePreviewServer(previewServer) {
604
+ previewServer.middlewares.use((_request, response, next) => {
605
+ response.on("finish", () => {
606
+ const error = response.getHeader("x-pagegraph-error");
607
+ if (typeof error === "string") previewServer.config.logger.error(decodeURIComponent(error));
608
+ });
609
+ next();
610
+ });
611
+ }
612
+ },
300
613
  runtimeModulesPlugin,
301
614
  ...options.routeConfig === void 0 ? [] : [seoRouteConfig({
302
615
  ...options.routeConfig,
303
616
  routesDirectory: options.routesDirectory
304
617
  })]
305
- ];
618
+ ], { prerenderPages: options.markdown === void 0 ? [] : [{
619
+ path: MARKDOWN_CAPTURE_PATH,
620
+ sitemap: { exclude: true },
621
+ prerender: { onSuccess: async ({ html }) => {
622
+ if (current === void 0) throw new Error("The pagegraph graph must be evaluated before Markdown persistence.");
623
+ const app = await current;
624
+ await persistMarkdownCapture(html, root, clientOutDir, options.claims, app.facts);
625
+ } }
626
+ }] });
306
627
  }
307
628
  //#endregion
308
629
  //#region src/tanstack-start/load.ts
309
630
  /**
310
631
  * Evaluate an app's graph outside a running dev server — for a build, the
311
- * CLI (`seo.config.ts`), and build-time head loading — through the same
632
+ * CLI (`pagegraph.config.ts`), and build-time head loading — through the same
312
633
  * `pagegraph` environment the dev server uses.
313
634
  */
314
635
  const GRAPH_ENVIRONMENT = "pagegraph";
636
+ const cliEnvironmentOverride = (command) => command === "build" ? { [GRAPH_ENVIRONMENT]: { isBundled: false } } : void 0;
637
+ /** Locate the config without overriding its authored root with an inline root. */
638
+ const appConfig = (options) => ({
639
+ configFile: options.configFile ?? [
640
+ "js",
641
+ "mjs",
642
+ "ts",
643
+ "cjs",
644
+ "mts",
645
+ "cts"
646
+ ].map((extension) => resolve(options.root, `vite.config.${extension}`)).find(existsSync) ?? false,
647
+ logLevel: "error",
648
+ plugins: [{
649
+ name: "pagegraph:loader-root",
650
+ enforce: "pre",
651
+ config(config) {
652
+ config.root ??= options.root;
653
+ }
654
+ }]
655
+ });
315
656
  const pluginApi = (plugins, configFile) => {
316
657
  const plugin = plugins.find((candidate) => candidate.name === "pagegraph");
317
658
  if (plugin?.api === void 0) throw new Error(`${configFile ?? "The Vite config"} does not register pagegraph() from "pagegraph/tanstack-start".`);
318
659
  return plugin.api;
319
660
  };
320
661
  /**
321
- * Resolve the app's Vite config in serve mode, evaluate the graph in its
322
- * `pagegraph` environment, and release it — the dev server's evaluation path
323
- * without a server. A config that branches on `command` sees `serve`; pass
324
- * `mode` for the deployment the graph should describe.
662
+ * Resolve the app's Vite config, evaluate the graph in its `pagegraph`
663
+ * environment, and release it. The command defaults to `serve`; callers can
664
+ * request `build` to match the deployed site configuration.
325
665
  */
326
666
  async function evaluateAppGraph(options) {
667
+ const command = options.command ?? "serve";
327
668
  const config = await resolveConfig({
328
- root: options.root,
329
- configFile: options.configFile,
669
+ ...appConfig(options),
330
670
  mode: options.mode,
331
- logLevel: "error"
332
- }, "serve");
671
+ environments: cliEnvironmentOverride(command)
672
+ }, command);
333
673
  const api = pluginApi(config.plugins, config.configFile);
334
674
  const environment = createRunnableDevEnvironment(GRAPH_ENVIRONMENT, config, { hot: false });
335
675
  await environment.init();
336
676
  try {
337
677
  await environment.pluginContainer.buildStart({});
338
- return await evaluateGraph(environment.runner, await planGraph(config.root, api.options));
678
+ const app = await evaluateGraph(environment.runner, await planGraph(config.root, api.options));
679
+ return {
680
+ ...app,
681
+ pagegraph: {
682
+ root: config.root,
683
+ options: api.options,
684
+ facts: app.facts ?? {}
685
+ }
686
+ };
339
687
  } finally {
340
688
  await environment.close();
341
689
  }
342
690
  }
343
691
  /**
344
- * A `seo.config.ts` graph loader for a TanStack Start app: the same graph the
345
- * `pagegraph()` plugin ships to the runtime, with its robots policy.
692
+ * A `pagegraph.config.ts` graph loader for a TanStack Start app. It uses the
693
+ * built site's identity by default; pass `command: "serve"` for a dev host.
346
694
  *
347
695
  * ```ts
348
696
  * export default defineSeoConfig({
349
- * origin: "https://example.com",
350
697
  * loadGraph: tanstackStartGraph({ root: import.meta.dirname }),
351
698
  * });
352
699
  * ```
353
700
  */
354
- const tanstackStartGraph = (options) => async () => {
355
- const { graph, site } = await withCleanStdout(() => evaluateAppGraph({
701
+ const tanstackStartGraph = (options) => async (_input, environment) => {
702
+ const { graph, site, pagegraph } = await withCleanStdout(() => evaluateAppGraph({
356
703
  ...options,
357
- mode: options.mode ?? "production"
704
+ command: environment?.command ?? options.command ?? "build",
705
+ mode: environment?.mode ?? options.mode ?? "production"
358
706
  }));
359
707
  return {
360
708
  graph,
361
- robots: site.robots,
709
+ site,
710
+ pagegraph,
362
711
  dispose: async () => {}
363
712
  };
364
713
  };