@pagesmith/core 0.2.0 → 0.4.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 (83) hide show
  1. package/README.md +9 -4
  2. package/REFERENCE.md +284 -0
  3. package/dist/ai/index.d.mts +7 -4
  4. package/dist/ai/index.d.mts.map +1 -1
  5. package/dist/ai/index.mjs +605 -136
  6. package/dist/ai/index.mjs.map +1 -1
  7. package/dist/assets/index.d.mts +10 -1
  8. package/dist/assets/index.d.mts.map +1 -1
  9. package/dist/assets/index.mjs +2 -2
  10. package/dist/{assets-bX08zEJm.mjs → assets-CAPOqQ_P.mjs} +42 -5
  11. package/dist/assets-CAPOqQ_P.mjs.map +1 -0
  12. package/dist/{content-config-wW-3r5gG.d.mts → content-config-DJXUOcNG.d.mts} +47 -15
  13. package/dist/{content-config-wW-3r5gG.d.mts.map → content-config-DJXUOcNG.d.mts.map} +1 -1
  14. package/dist/{content-layer-DWdgdBeI.mjs → content-layer-B5enqWeJ.mjs} +195 -63
  15. package/dist/content-layer-B5enqWeJ.mjs.map +1 -0
  16. package/dist/content-layer-CpHYUYNN.d.mts +121 -0
  17. package/dist/content-layer-CpHYUYNN.d.mts.map +1 -0
  18. package/dist/create/index.d.mts.map +1 -1
  19. package/dist/create/index.mjs +26 -28
  20. package/dist/create/index.mjs.map +1 -1
  21. package/dist/css/index.d.mts +1 -1
  22. package/dist/css/index.mjs +1 -1
  23. package/dist/{css-ekIt2Fdb.mjs → css-BneO430t.mjs} +5 -4
  24. package/dist/css-BneO430t.mjs.map +1 -0
  25. package/dist/index-B7NRZAxd.d.mts +13 -0
  26. package/dist/index-B7NRZAxd.d.mts.map +1 -0
  27. package/dist/{index-D79hUFbK.d.mts → index-C0QFHYwb.d.mts} +1 -1
  28. package/dist/{index-D79hUFbK.d.mts.map → index-C0QFHYwb.d.mts.map} +1 -1
  29. package/dist/{index-DpRBzO8Q.d.mts → index-CJkBs8YQ.d.mts} +2 -2
  30. package/dist/index-CJkBs8YQ.d.mts.map +1 -0
  31. package/dist/{index-Dbsw1QON.d.mts → index-DCznbvaV.d.mts} +4 -2
  32. package/dist/{index-Dbsw1QON.d.mts.map → index-DCznbvaV.d.mts.map} +1 -1
  33. package/dist/index.d.mts +13 -91
  34. package/dist/index.d.mts.map +1 -1
  35. package/dist/index.mjs +16 -13
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/loaders/index.d.mts +2 -2
  38. package/dist/loaders/index.mjs +2 -2
  39. package/dist/{loaders-Bla48ZN9.mjs → loaders-Cf-BXf2L.mjs} +10 -2
  40. package/dist/{loaders-Bla48ZN9.mjs.map → loaders-Cf-BXf2L.mjs.map} +1 -1
  41. package/dist/markdown/index.d.mts +1 -1
  42. package/dist/markdown/index.mjs +1 -1
  43. package/dist/{markdown-Cj5X26FL.mjs → markdown-BmDJgYeB.mjs} +59 -9
  44. package/dist/markdown-BmDJgYeB.mjs.map +1 -0
  45. package/dist/mcp/index.d.mts +23 -0
  46. package/dist/mcp/index.d.mts.map +1 -0
  47. package/dist/mcp/index.mjs +2 -0
  48. package/dist/mcp/server.d.mts +13 -0
  49. package/dist/mcp/server.d.mts.map +1 -0
  50. package/dist/mcp/server.mjs +2 -0
  51. package/dist/runtime/index.mjs +1 -1
  52. package/dist/schemas/index.d.mts +2 -2
  53. package/dist/schemas/index.mjs +1 -1
  54. package/dist/{schemas-BZEPTGWs.mjs → schemas-UL4ynWsA.mjs} +1 -1
  55. package/dist/{schemas-BZEPTGWs.mjs.map → schemas-UL4ynWsA.mjs.map} +1 -1
  56. package/dist/server-D3DHoh5f.mjs +202 -0
  57. package/dist/server-D3DHoh5f.mjs.map +1 -0
  58. package/dist/ssg-utils/index.d.mts +61 -0
  59. package/dist/ssg-utils/index.d.mts.map +1 -0
  60. package/dist/ssg-utils/index.mjs +118 -0
  61. package/dist/ssg-utils/index.mjs.map +1 -0
  62. package/dist/vite/index.d.mts +68 -33
  63. package/dist/vite/index.d.mts.map +1 -1
  64. package/dist/vite/index.mjs +302 -227
  65. package/dist/vite/index.mjs.map +1 -1
  66. package/docs/agents/AGENTS.md.template +9 -0
  67. package/docs/agents/changelog-notes.md +15 -0
  68. package/docs/agents/errors.md +96 -0
  69. package/docs/agents/migration.md +25 -0
  70. package/docs/agents/recipes.md +26 -0
  71. package/docs/agents/usage.md +58 -0
  72. package/docs/llms-full.txt +53 -0
  73. package/docs/llms.txt +29 -0
  74. package/package.json +58 -6
  75. package/dist/assets-bX08zEJm.mjs.map +0 -1
  76. package/dist/content-layer-DWdgdBeI.mjs.map +0 -1
  77. package/dist/convert-XdGgNqH0.mjs +0 -27
  78. package/dist/convert-XdGgNqH0.mjs.map +0 -1
  79. package/dist/css-ekIt2Fdb.mjs.map +0 -1
  80. package/dist/index-CeNDTM-y.d.mts +0 -7
  81. package/dist/index-CeNDTM-y.d.mts.map +0 -1
  82. package/dist/index-DpRBzO8Q.d.mts.map +0 -1
  83. package/dist/markdown-Cj5X26FL.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"loaders-Bla48ZN9.mjs","names":["parseYaml","parse"],"sources":["../src/loaders/errors.ts","../src/loaders/json.ts","../src/loaders/jsonc.ts","../src/loaders/markdown.ts","../src/loaders/toml.ts","../src/loaders/yaml.ts","../src/loaders/index.ts"],"sourcesContent":["export class LoaderError extends Error {\n readonly filePath: string\n readonly format: string\n readonly line?: number\n readonly column?: number\n\n constructor(message: string, filePath: string, format: string, line?: number, column?: number) {\n super(`${format} parse error in ${filePath}${line ? `:${line}` : ''}: ${message}`)\n this.name = 'LoaderError'\n this.filePath = filePath\n this.format = format\n this.line = line\n this.column = column\n }\n}\n","/**\n * JSON / JSON5 / JSONC loader.\n *\n * Detects format from file extension and parses accordingly.\n */\n\nimport { readFile } from 'fs/promises'\nimport JSON5 from 'json5'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class JsonLoader implements Loader {\n name = 'json'\n kind = 'data' as const\n extensions = ['.json', '.json5']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n const isJson = filePath.endsWith('.json')\n\n try {\n const data = isJson ? JSON.parse(raw) : JSON5.parse(raw)\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, isJson ? 'JSON' : 'JSON5')\n }\n }\n}\n","/**\n * JSONC loader — JSON with Comments.\n *\n * Strips single-line (//) and multi-line comments before parsing.\n * Delegates to JSON.parse after stripping (no JSON5 superset features).\n */\n\nimport { readFile } from 'fs/promises'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\n/** Strip // and block comments, preserving strings. */\nfunction stripComments(raw: string): string {\n let result = ''\n let i = 0\n const len = raw.length\n\n while (i < len) {\n // String literal — copy verbatim to preserve contents\n if (raw[i] === '\"') {\n let end = i + 1\n while (end < len && raw[end] !== '\"') {\n if (raw[end] === '\\\\') end++ // skip escaped char\n end++\n }\n result += raw.slice(i, end + 1)\n i = end + 1\n continue\n }\n\n // Single-line comment\n if (raw[i] === '/' && raw[i + 1] === '/') {\n i += 2\n while (i < len && raw[i] !== '\\n') i++\n continue\n }\n\n // Block comment\n if (raw[i] === '/' && raw[i + 1] === '*') {\n i += 2\n while (i < len && !(raw[i] === '*' && raw[i + 1] === '/')) i++\n i += 2 // skip closing */\n continue\n }\n\n result += raw[i]\n i++\n }\n\n return result\n}\n\nexport class JsoncLoader implements Loader {\n name = 'jsonc'\n kind = 'data' as const\n extensions = ['.jsonc']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const stripped = stripComments(raw)\n const data = JSON.parse(stripped)\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, 'JSONC')\n }\n }\n}\n","/**\n * Markdown loader.\n *\n * Uses gray-matter to extract YAML frontmatter and body content.\n * Rendering is NOT done here — it's deferred to ContentEntry.render().\n */\n\nimport { readFile } from 'fs/promises'\nimport matter from 'gray-matter'\nimport { parse as parseYaml } from 'yaml'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class MarkdownLoader implements Loader {\n name = 'markdown'\n kind = 'markdown' as const\n extensions = ['.md']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const { data, content } = matter(raw, { engines: { yaml: parseYaml } })\n return { data, content }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, 'Markdown')\n }\n }\n}\n","/**\n * TOML loader.\n */\n\nimport { readFile } from 'fs/promises'\nimport { parse } from 'smol-toml'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class TomlLoader implements Loader {\n name = 'toml'\n kind = 'data' as const\n extensions = ['.toml']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const data = parse(raw)\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, 'TOML')\n }\n }\n}\n","/**\n * YAML loader.\n */\n\nimport { readFile } from 'fs/promises'\nimport { parse } from 'yaml'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class YamlLoader implements Loader {\n name = 'yaml'\n kind = 'data' as const\n extensions = ['.yaml', '.yml']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const data = parse(raw) ?? {}\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const line =\n err != null && typeof err === 'object' && 'linePos' in err\n ? (err as any).linePos?.[0]?.line\n : undefined\n throw new LoaderError(message, filePath, 'YAML', line)\n }\n }\n}\n","/**\n * Loader registry — resolves loader type strings to Loader instances.\n */\n\nexport { JsonLoader } from './json'\nexport { JsoncLoader } from './jsonc'\nexport { LoaderError } from './errors'\nexport { MarkdownLoader } from './markdown'\nexport { TomlLoader } from './toml'\nexport type { Loader, LoaderResult, LoaderType } from './types'\nexport { YamlLoader } from './yaml'\n\nimport { JsonLoader } from './json'\nimport { JsoncLoader } from './jsonc'\nimport { MarkdownLoader } from './markdown'\nimport { TomlLoader } from './toml'\nimport type { Loader, LoaderType } from './types'\nimport { YamlLoader } from './yaml'\n\n/** Singleton loader instances (loaders are stateless). */\nconst markdownLoader = new MarkdownLoader()\nconst jsonLoader = new JsonLoader()\nconst jsoncLoader = new JsoncLoader()\nconst yamlLoader = new YamlLoader()\nconst tomlLoader = new TomlLoader()\n\n/** Resolve a loader type string or custom Loader instance. */\nexport function resolveLoader(loaderOrType: LoaderType | Loader): Loader {\n if (typeof loaderOrType === 'object') return loaderOrType\n\n switch (loaderOrType) {\n case 'markdown':\n return markdownLoader\n case 'json':\n case 'json5':\n return jsonLoader\n case 'jsonc':\n return jsoncLoader\n case 'yaml':\n return yamlLoader\n case 'toml':\n return tomlLoader\n default:\n throw new Error(`Unknown loader type: ${loaderOrType as string}`)\n }\n}\n\n/** Get default include glob patterns for a loader. */\nexport function defaultIncludePatterns(loader: Loader): string[] {\n return loader.extensions.map((ext) => `**/*${ext}`)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"loaders-Cf-BXf2L.mjs","names":["parseYaml","parse"],"sources":["../src/loaders/errors.ts","../src/loaders/json.ts","../src/loaders/jsonc.ts","../src/loaders/markdown.ts","../src/loaders/toml.ts","../src/loaders/yaml.ts","../src/loaders/index.ts"],"sourcesContent":["export class LoaderError extends Error {\n readonly filePath: string\n readonly format: string\n readonly line?: number\n readonly column?: number\n\n constructor(message: string, filePath: string, format: string, line?: number, column?: number) {\n super(`${format} parse error in ${filePath}${line ? `:${line}` : ''}: ${message}`)\n this.name = 'LoaderError'\n this.filePath = filePath\n this.format = format\n this.line = line\n this.column = column\n }\n}\n","/**\n * JSON / JSON5 / JSONC loader.\n *\n * Detects format from file extension and parses accordingly.\n */\n\nimport { readFile } from 'fs/promises'\nimport JSON5 from 'json5'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class JsonLoader implements Loader {\n name = 'json'\n kind = 'data' as const\n extensions = ['.json', '.json5']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n const isJson = filePath.endsWith('.json')\n\n try {\n const data = isJson ? JSON.parse(raw) : JSON5.parse(raw)\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, isJson ? 'JSON' : 'JSON5')\n }\n }\n}\n","/**\n * JSONC loader — JSON with Comments.\n *\n * Strips single-line (//) and multi-line comments before parsing.\n * Delegates to JSON.parse after stripping (no JSON5 superset features).\n */\n\nimport { readFile } from 'fs/promises'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\n/** Strip // and block comments, preserving strings. */\nfunction stripComments(raw: string): string {\n let result = ''\n let i = 0\n const len = raw.length\n\n while (i < len) {\n // String literal — copy verbatim to preserve contents\n if (raw[i] === '\"') {\n let end = i + 1\n while (end < len && raw[end] !== '\"') {\n if (raw[end] === '\\\\') end++ // skip escaped char\n end++\n }\n result += raw.slice(i, end + 1)\n i = end + 1\n continue\n }\n\n // Single-line comment\n if (raw[i] === '/' && raw[i + 1] === '/') {\n i += 2\n while (i < len && raw[i] !== '\\n') i++\n continue\n }\n\n // Block comment\n if (raw[i] === '/' && raw[i + 1] === '*') {\n i += 2\n while (i < len && !(raw[i] === '*' && raw[i + 1] === '/')) i++\n i += 2 // skip closing */\n continue\n }\n\n result += raw[i]\n i++\n }\n\n return result\n}\n\nexport class JsoncLoader implements Loader {\n name = 'jsonc'\n kind = 'data' as const\n extensions = ['.jsonc']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const stripped = stripComments(raw)\n const data = JSON.parse(stripped)\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, 'JSONC')\n }\n }\n}\n","/**\n * Markdown loader.\n *\n * Uses gray-matter to extract YAML frontmatter and body content.\n * Rendering is NOT done here — it's deferred to ContentEntry.render().\n */\n\nimport { readFile } from 'fs/promises'\nimport matter from 'gray-matter'\nimport { parse as parseYaml } from 'yaml'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class MarkdownLoader implements Loader {\n name = 'markdown'\n kind = 'markdown' as const\n extensions = ['.md']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const { data, content } = matter(raw, { engines: { yaml: parseYaml } })\n return { data, content }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, 'Markdown')\n }\n }\n}\n","/**\n * TOML loader.\n */\n\nimport { readFile } from 'fs/promises'\nimport { parse } from 'smol-toml'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class TomlLoader implements Loader {\n name = 'toml'\n kind = 'data' as const\n extensions = ['.toml']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const data = parse(raw)\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new LoaderError(message, filePath, 'TOML')\n }\n }\n}\n","/**\n * YAML loader.\n */\n\nimport { readFile } from 'fs/promises'\nimport { parse } from 'yaml'\nimport { LoaderError } from './errors'\nimport type { Loader, LoaderResult } from './types'\n\nexport class YamlLoader implements Loader {\n name = 'yaml'\n kind = 'data' as const\n extensions = ['.yaml', '.yml']\n\n async load(filePath: string): Promise<LoaderResult> {\n const raw = await readFile(filePath, 'utf-8')\n try {\n const data = parse(raw) ?? {}\n return { data }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const line =\n err != null && typeof err === 'object' && 'linePos' in err\n ? (err as any).linePos?.[0]?.line\n : undefined\n throw new LoaderError(message, filePath, 'YAML', line)\n }\n }\n}\n","/**\n * Loader registry — resolves loader type strings to Loader instances.\n */\n\nexport { JsonLoader } from './json'\nexport { JsoncLoader } from './jsonc'\nexport { LoaderError } from './errors'\nexport { MarkdownLoader } from './markdown'\nexport { TomlLoader } from './toml'\nexport type { Loader, LoaderResult, LoaderType } from './types'\nexport { YamlLoader } from './yaml'\n\nimport { JsonLoader } from './json'\nimport { JsoncLoader } from './jsonc'\nimport { MarkdownLoader } from './markdown'\nimport { TomlLoader } from './toml'\nimport type { Loader, LoaderType } from './types'\nimport { YamlLoader } from './yaml'\n\n/** Singleton loader instances (loaders are stateless). */\nconst markdownLoader = new MarkdownLoader()\nconst jsonLoader = new JsonLoader()\nconst jsoncLoader = new JsoncLoader()\nconst yamlLoader = new YamlLoader()\nconst tomlLoader = new TomlLoader()\n\n/** Custom loader registry for user-registered loaders. */\nconst customLoaders = new Map<string, Loader>()\n\n/** Register a custom loader by name. */\nexport function registerLoader(name: string, loader: Loader): void {\n customLoaders.set(name, loader)\n}\n\n/** Resolve a loader type string or custom Loader instance. */\nexport function resolveLoader(loaderOrType: LoaderType | Loader): Loader {\n if (typeof loaderOrType === 'object') return loaderOrType\n\n const custom = customLoaders.get(loaderOrType)\n if (custom) return custom\n\n switch (loaderOrType) {\n case 'markdown':\n return markdownLoader\n case 'json':\n case 'json5':\n return jsonLoader\n case 'jsonc':\n return jsoncLoader\n case 'yaml':\n return yamlLoader\n case 'toml':\n return tomlLoader\n default:\n throw new Error(`Unknown loader type: ${loaderOrType as string}`)\n }\n}\n\n/** Get default include glob patterns for a loader. */\nexport function defaultIncludePatterns(loader: Loader): string[] {\n return loader.extensions.map((ext) => `**/*${ext}`)\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { r as MarkdownConfig } from "../heading-Dhvzlay-.mjs";
2
- import { n as processMarkdown, t as MarkdownResult } from "../index-DpRBzO8Q.mjs";
2
+ import { n as processMarkdown, t as MarkdownResult } from "../index-CJkBs8YQ.mjs";
3
3
  export { MarkdownConfig, MarkdownResult, processMarkdown };
@@ -1,2 +1,2 @@
1
- import { t as processMarkdown } from "../markdown-Cj5X26FL.mjs";
1
+ import { t as processMarkdown } from "../markdown-BmDJgYeB.mjs";
2
2
  export { processMarkdown };
@@ -17,6 +17,17 @@ import remarkSmartypants from "remark-smartypants";
17
17
  import { unified } from "unified";
18
18
  //#region src/markdown/pipeline.ts
19
19
  const DEFAULT_MARKDOWN_CONFIG = {};
20
+ /** Default language aliases for fenced code blocks that Shiki doesn't recognize natively. */
21
+ const DEFAULT_LANG_ALIASES = {
22
+ dot: "text",
23
+ mermaid: "text",
24
+ plantuml: "text",
25
+ excalidraw: "json",
26
+ drawio: "xml",
27
+ proto: "protobuf",
28
+ ejs: "html",
29
+ hbs: "handlebars"
30
+ };
20
31
  function getTextContent(node) {
21
32
  if (node.type === "text") return node.value || "";
22
33
  if (node.children) return node.children.map(getTextContent).join("");
@@ -34,7 +45,19 @@ function createProcessor(config) {
34
45
  const processor = unified().use(remarkParse).use(remarkGfm).use(remarkMath).use(remarkFrontmatter, ["yaml"]).use(remarkGithubAlerts).use(remarkSmartypants);
35
46
  if (config.remarkPlugins) for (const plugin of config.remarkPlugins) if (Array.isArray(plugin)) processor.use(plugin[0], plugin[1]);
36
47
  else processor.use(plugin);
48
+ const langAlias = {
49
+ ...DEFAULT_LANG_ALIASES,
50
+ ...config.shiki?.langAlias
51
+ };
52
+ processor.use(() => (tree) => {
53
+ const visit = (node) => {
54
+ if (node?.type === "code" && typeof node.lang === "string" && langAlias[node.lang]) node.lang = langAlias[node.lang];
55
+ if (Array.isArray(node?.children)) for (const child of node.children) visit(child);
56
+ };
57
+ visit(tree);
58
+ });
37
59
  processor.use(remarkRehype, { allowDangerousHtml: true });
60
+ processor.use(rehypeMathjax);
38
61
  const lightTheme = config.shiki?.themes?.light || "github-light";
39
62
  const darkTheme = config.shiki?.themes?.dark || "github-dark";
40
63
  processor.use(rehypeExpressiveCode, {
@@ -49,7 +72,7 @@ function createProcessor(config) {
49
72
  borderColor: "var(--ps-color-border-subtle, var(--color-border-subtle, #e5e7eb))"
50
73
  }
51
74
  });
52
- processor.use(rehypeMathjax).use(rehypeSlug).use(rehypeAutolinkHeadings, { behavior: "wrap" }).use(rehypeExternalLinks, {
75
+ processor.use(rehypeSlug).use(rehypeAutolinkHeadings, { behavior: "wrap" }).use(rehypeExternalLinks, {
53
76
  target: "_blank",
54
77
  rel: ["noopener", "noreferrer"]
55
78
  }).use(rehypeAccessibleEmojis);
@@ -63,6 +86,28 @@ function createProcessor(config) {
63
86
  processor.use(rehypeStringify, { allowDangerousHtml: true });
64
87
  return processor;
65
88
  }
89
+ /**
90
+ * Processor cache keyed by MarkdownConfig object reference.
91
+ *
92
+ * **Why a WeakMap keyed by object reference?**
93
+ * Building a unified processor chain is expensive — it loads Shiki grammars,
94
+ * theme JSON, and instantiates every remark/rehype plugin. Caching the
95
+ * processor by config reference lets callers that reuse the same config object
96
+ * (the common case) skip all of that setup on subsequent calls. The WeakMap
97
+ * also ensures that if a config object is garbage-collected, its processor is
98
+ * too, so long-running processes don't leak memory.
99
+ *
100
+ * **Why is the config frozen?**
101
+ * The cache assumes the config does not change after the processor is built.
102
+ * If a caller mutated a config object after the processor was created, later
103
+ * calls would still receive the stale processor (keyed by the same reference),
104
+ * producing silently wrong output. Freezing the config at first use turns that
105
+ * silent bug into a loud TypeError on any attempted mutation.
106
+ *
107
+ * **What if a consumer needs different settings?**
108
+ * Pass a new config object — a fresh reference gets its own cache entry.
109
+ * For example: `processMarkdown(md, { ...existingConfig, remarkPlugins: [...] })`.
110
+ */
66
111
  const processorCache = /* @__PURE__ */ new WeakMap();
67
112
  async function processMarkdown(raw, config, preExtracted) {
68
113
  let frontmatter;
@@ -76,20 +121,25 @@ async function processMarkdown(raw, config, preExtracted) {
76
121
  content = parsed.content;
77
122
  }
78
123
  const resolvedConfig = config && Object.keys(config).length > 0 ? config : DEFAULT_MARKDOWN_CONFIG;
124
+ if (Object.isFrozen(resolvedConfig) === false) Object.freeze(resolvedConfig);
79
125
  let processor = processorCache.get(resolvedConfig);
80
126
  if (!processor) {
81
127
  processor = createProcessor(resolvedConfig);
82
128
  processorCache.set(resolvedConfig, processor);
83
129
  }
84
- const result = await processor.process(content);
85
- const headings = Array.isArray(result.data.headings) ? result.data.headings : [];
86
- return {
87
- html: String(result),
88
- headings,
89
- frontmatter
90
- };
130
+ try {
131
+ const result = await processor.process(content);
132
+ const headings = Array.isArray(result.data.headings) ? result.data.headings : [];
133
+ return {
134
+ html: String(result),
135
+ headings,
136
+ frontmatter
137
+ };
138
+ } catch (err) {
139
+ throw new Error(`Markdown processing failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
140
+ }
91
141
  }
92
142
  //#endregion
93
143
  export { processMarkdown as t };
94
144
 
95
- //# sourceMappingURL=markdown-Cj5X26FL.mjs.map
145
+ //# sourceMappingURL=markdown-BmDJgYeB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-BmDJgYeB.mjs","names":["parseYaml"],"sources":["../src/markdown/pipeline.ts"],"sourcesContent":["import matter from 'gray-matter'\nimport { parse as parseYaml } from 'yaml'\nimport { rehypeAccessibleEmojis } from 'rehype-accessible-emojis'\nimport rehypeAutolinkHeadings from 'rehype-autolink-headings'\nimport rehypeExpressiveCode, {\n type BundledShikiTheme,\n type RehypeExpressiveCodeOptions,\n} from 'rehype-expressive-code'\nimport rehypeExternalLinks from 'rehype-external-links'\nimport rehypeMathjax from 'rehype-mathjax/svg'\nimport rehypeSlug from 'rehype-slug'\nimport rehypeStringify from 'rehype-stringify'\nimport remarkFrontmatter from 'remark-frontmatter'\nimport remarkGfm from 'remark-gfm'\nimport remarkGithubAlerts from 'remark-github-alerts'\nimport remarkMath from 'remark-math'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport remarkSmartypants from 'remark-smartypants'\nimport { unified } from 'unified'\nimport type { Heading } from '../schemas/heading'\nimport type { MarkdownConfig } from '../schemas/markdown-config'\n\nexport type MarkdownResult = {\n html: string\n headings: Heading[]\n frontmatter: Record<string, unknown>\n}\n\nexport type { MarkdownConfig }\n\nconst DEFAULT_MARKDOWN_CONFIG: MarkdownConfig = {}\n\n/** Default language aliases for fenced code blocks that Shiki doesn't recognize natively. */\nconst DEFAULT_LANG_ALIASES: Record<string, string> = {\n dot: 'text',\n mermaid: 'text',\n plantuml: 'text',\n excalidraw: 'json',\n drawio: 'xml',\n proto: 'protobuf',\n ejs: 'html',\n hbs: 'handlebars',\n}\n\nfunction getTextContent(node: any): string {\n if (node.type === 'text') return node.value || ''\n if (node.children) return node.children.map(getTextContent).join('')\n return ''\n}\n\nfunction extractHeadings(tree: any, headings: Heading[]): void {\n if (tree.type === 'element' && /^h[1-6]$/.test(tree.tagName)) {\n headings.push({\n depth: parseInt(tree.tagName[1]),\n text: getTextContent(tree),\n slug: tree.properties?.id || '',\n })\n }\n if (tree.children) {\n for (const child of tree.children) {\n extractHeadings(child, headings)\n }\n }\n}\n\nfunction createProcessor(config: MarkdownConfig) {\n const processor = unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkFrontmatter, ['yaml'])\n // GitHub-flavored alerts: > [!NOTE], > [!TIP], > [!IMPORTANT], > [!WARNING], > [!CAUTION]\n .use(remarkGithubAlerts)\n // Smart typography: \"smart quotes\", em—dashes, el…lipses\n .use(remarkSmartypants)\n\n if (config.remarkPlugins) {\n for (const plugin of config.remarkPlugins) {\n if (Array.isArray(plugin)) processor.use(plugin[0], plugin[1])\n else processor.use(plugin)\n }\n }\n\n // Apply language aliases to fenced code blocks before Expressive Code processes them.\n // Merge defaults with user-provided aliases (user overrides take precedence).\n const langAlias = { ...DEFAULT_LANG_ALIASES, ...config.shiki?.langAlias }\n processor.use(() => (tree: any) => {\n const visit = (node: any): void => {\n if (node?.type === 'code' && typeof node.lang === 'string' && langAlias[node.lang]) {\n node.lang = langAlias[node.lang]\n }\n if (Array.isArray(node?.children)) {\n for (const child of node.children) visit(child)\n }\n }\n visit(tree)\n })\n\n processor.use(remarkRehype, { allowDangerousHtml: true })\n\n // MathJax must run before Expressive Code so that math elements (from remark-math)\n // are rendered to SVG before Expressive Code tries to highlight them as code blocks.\n processor.use(rehypeMathjax)\n\n // Expressive Code — syntax highlighting, code frames, tabs, copy button\n const lightTheme = (config.shiki?.themes?.light || 'github-light') as BundledShikiTheme\n const darkTheme = (config.shiki?.themes?.dark || 'github-dark') as BundledShikiTheme\n\n processor.use(rehypeExpressiveCode, {\n themes: [darkTheme, lightTheme],\n useDarkModeMediaQuery: true,\n styleOverrides: {\n uiFontFamily: 'var(--ps-font-sans, var(--font-family, system-ui, sans-serif))',\n codeFontFamily: 'var(--ps-font-mono, var(--font-mono, ui-monospace, monospace))',\n codeFontSize: 'var(--ps-font-size-sm, 0.875rem)',\n codeLineHeight: '1.7',\n borderRadius: 'var(--ps-radius-lg, 0.5rem)',\n borderColor: 'var(--ps-color-border-subtle, var(--color-border-subtle, #e5e7eb))',\n },\n } satisfies RehypeExpressiveCodeOptions)\n\n processor\n .use(rehypeSlug)\n .use(rehypeAutolinkHeadings, { behavior: 'wrap' })\n // External links: add target=\"_blank\" rel=\"noopener noreferrer\" to absolute URLs\n .use(rehypeExternalLinks, {\n target: '_blank',\n rel: ['noopener', 'noreferrer'],\n })\n // Accessible emojis: wrap emoji characters in <span role=\"img\" aria-label=\"...\">\n .use(rehypeAccessibleEmojis)\n\n processor.use(() => (tree: any, file: any) => {\n const headings: Heading[] = []\n extractHeadings(tree, headings)\n file.data.headings = headings\n })\n\n if (config.rehypePlugins) {\n for (const plugin of config.rehypePlugins) {\n if (Array.isArray(plugin)) processor.use(plugin[0], plugin[1])\n else processor.use(plugin)\n }\n }\n\n processor.use(rehypeStringify, { allowDangerousHtml: true })\n return processor\n}\n\n/**\n * Processor cache keyed by MarkdownConfig object reference.\n *\n * **Why a WeakMap keyed by object reference?**\n * Building a unified processor chain is expensive — it loads Shiki grammars,\n * theme JSON, and instantiates every remark/rehype plugin. Caching the\n * processor by config reference lets callers that reuse the same config object\n * (the common case) skip all of that setup on subsequent calls. The WeakMap\n * also ensures that if a config object is garbage-collected, its processor is\n * too, so long-running processes don't leak memory.\n *\n * **Why is the config frozen?**\n * The cache assumes the config does not change after the processor is built.\n * If a caller mutated a config object after the processor was created, later\n * calls would still receive the stale processor (keyed by the same reference),\n * producing silently wrong output. Freezing the config at first use turns that\n * silent bug into a loud TypeError on any attempted mutation.\n *\n * **What if a consumer needs different settings?**\n * Pass a new config object — a fresh reference gets its own cache entry.\n * For example: `processMarkdown(md, { ...existingConfig, remarkPlugins: [...] })`.\n */\nconst processorCache = new WeakMap<MarkdownConfig, ReturnType<typeof createProcessor>>()\n\nexport async function processMarkdown(\n raw: string,\n config?: MarkdownConfig,\n preExtracted?: { content: string; frontmatter: Record<string, unknown> },\n): Promise<MarkdownResult> {\n let frontmatter: Record<string, unknown>\n let content: string\n if (preExtracted) {\n frontmatter = preExtracted.frontmatter\n content = preExtracted.content\n } else {\n const parsed = matter(raw, { engines: { yaml: parseYaml } })\n frontmatter = parsed.data\n content = parsed.content\n }\n const resolvedConfig = config && Object.keys(config).length > 0 ? config : DEFAULT_MARKDOWN_CONFIG\n // Freeze to prevent mutation after caching — see processorCache JSDoc above.\n if (Object.isFrozen(resolvedConfig) === false) Object.freeze(resolvedConfig)\n let processor = processorCache.get(resolvedConfig)\n if (!processor) {\n processor = createProcessor(resolvedConfig)\n processorCache.set(resolvedConfig, processor)\n }\n try {\n const result = await processor.process(content)\n const headings = Array.isArray(result.data.headings) ? (result.data.headings as Heading[]) : []\n return { html: String(result), headings, frontmatter }\n } catch (err) {\n throw new Error(\n `Markdown processing failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n )\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { CoreMcpServerOptions, createCoreMcpServer, startCoreMcpServer } from "./server.mjs";
2
+
3
+ //#region src/mcp/shared.d.ts
4
+ /**
5
+ * Shared MCP server utilities.
6
+ *
7
+ * Common helpers used by both @pagesmith/core and @pagesmith/docs MCP servers.
8
+ */
9
+ /** Read the package version from a package.json relative to the calling module. */
10
+ declare function getPackageVersion(moduleDir: string): string;
11
+ /** Resolve a doc file path relative to the package root. */
12
+ declare function resolvePackageDocPath(moduleDir: string, relativePath: string): string;
13
+ /** Load a file as an MCP text resource response. Throws if the file doesn't exist. */
14
+ declare function asTextResource(uri: string, path: string): {
15
+ contents: {
16
+ uri: string;
17
+ mimeType: string;
18
+ text: string;
19
+ }[];
20
+ };
21
+ //#endregion
22
+ export { type CoreMcpServerOptions, asTextResource, createCoreMcpServer, getPackageVersion, resolvePackageDocPath, startCoreMcpServer };
23
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/mcp/shared.ts"],"mappings":";;;;;;;;;iBAUgB,iBAAA,CAAkB,SAAA;;iBAOlB,qBAAA,CAAsB,SAAA,UAAmB,YAAA;;iBAKzC,cAAA,CAAe,GAAA,UAAa,IAAA"}
@@ -0,0 +1,2 @@
1
+ import { a as resolvePackageDocPath, i as getPackageVersion, n as startCoreMcpServer, r as asTextResource, t as createCoreMcpServer } from "../server-D3DHoh5f.mjs";
2
+ export { asTextResource, createCoreMcpServer, getPackageVersion, resolvePackageDocPath, startCoreMcpServer };
@@ -0,0 +1,13 @@
1
+ import { t as ContentLayer } from "../content-layer-CpHYUYNN.mjs";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+
4
+ //#region src/mcp/server.d.ts
5
+ type CoreMcpServerOptions = {
6
+ layer: ContentLayer;
7
+ rootDir?: string;
8
+ };
9
+ declare function createCoreMcpServer(options: CoreMcpServerOptions): McpServer;
10
+ declare function startCoreMcpServer(options: CoreMcpServerOptions): Promise<void>;
11
+ //#endregion
12
+ export { CoreMcpServerOptions, createCoreMcpServer, startCoreMcpServer };
13
+ //# sourceMappingURL=server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../src/mcp/server.ts"],"mappings":";;;;KAQY,oBAAA;EACV,KAAA,EAAO,YAAA;EACP,OAAA;AAAA;AAAA,iBAkBc,mBAAA,CAAoB,OAAA,EAAS,oBAAA,GAAuB,SAAA;AAAA,iBA8S9C,kBAAA,CAAmB,OAAA,EAAS,oBAAA,GAAuB,OAAA"}
@@ -0,0 +1,2 @@
1
+ import { n as startCoreMcpServer, t as createCoreMcpServer } from "../server-D3DHoh5f.mjs";
2
+ export { createCoreMcpServer, startCoreMcpServer };
@@ -1,5 +1,5 @@
1
- import { dirname, join } from "path";
2
1
  import { readFileSync } from "fs";
2
+ import { dirname, join } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  //#region src/runtime/index.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { c as RawEntry, i as CollectionDef, n as ContentPlugin, t as ContentLayerConfig } from "../content-config-wW-3r5gG.mjs";
1
+ import { c as RawEntry, i as CollectionDef, n as ContentPlugin, t as ContentLayerConfig } from "../content-config-DJXUOcNG.mjs";
2
2
  import { i as MarkdownConfigSchema, n as HeadingSchema, r as MarkdownConfig, t as Heading } from "../heading-Dhvzlay-.mjs";
3
- import { a as ProjectFrontmatter, i as BlogFrontmatterSchema, n as BaseFrontmatterSchema, o as ProjectFrontmatterSchema, r as BlogFrontmatter, t as BaseFrontmatter } from "../index-D79hUFbK.mjs";
3
+ import { a as ProjectFrontmatter, i as BlogFrontmatterSchema, n as BaseFrontmatterSchema, o as ProjectFrontmatterSchema, r as BlogFrontmatter, t as BaseFrontmatter } from "../index-C0QFHYwb.mjs";
4
4
  export { BaseFrontmatter, BaseFrontmatterSchema, BlogFrontmatter, BlogFrontmatterSchema, CollectionDef, ContentLayerConfig, ContentPlugin, Heading, HeadingSchema, MarkdownConfig, MarkdownConfigSchema, ProjectFrontmatter, ProjectFrontmatterSchema, RawEntry };
@@ -1,2 +1,2 @@
1
- import { a as ProjectFrontmatterSchema, i as BlogFrontmatterSchema, n as HeadingSchema, r as BaseFrontmatterSchema, t as MarkdownConfigSchema } from "../schemas-BZEPTGWs.mjs";
1
+ import { a as ProjectFrontmatterSchema, i as BlogFrontmatterSchema, n as HeadingSchema, r as BaseFrontmatterSchema, t as MarkdownConfigSchema } from "../schemas-UL4ynWsA.mjs";
2
2
  export { BaseFrontmatterSchema, BlogFrontmatterSchema, HeadingSchema, MarkdownConfigSchema, ProjectFrontmatterSchema };
@@ -44,4 +44,4 @@ const MarkdownConfigSchema = z.object({
44
44
  //#endregion
45
45
  export { ProjectFrontmatterSchema as a, BlogFrontmatterSchema as i, HeadingSchema as n, BaseFrontmatterSchema as r, MarkdownConfigSchema as t };
46
46
 
47
- //# sourceMappingURL=schemas-BZEPTGWs.mjs.map
47
+ //# sourceMappingURL=schemas-UL4ynWsA.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas-BZEPTGWs.mjs","names":[],"sources":["../src/schemas/frontmatter.ts","../src/schemas/heading.ts","../src/schemas/markdown-config.ts"],"sourcesContent":["import { z } from 'zod'\n\n// ── Base frontmatter (required for all content items) ──\n\nexport const BaseFrontmatterSchema = z\n .object({\n title: z.string(),\n description: z.string(),\n publishedDate: z.coerce.date(),\n lastUpdatedOn: z.coerce.date().optional(),\n tags: z.array(z.string()).default([]),\n draft: z.boolean().optional().default(false),\n })\n .passthrough()\n\nexport type BaseFrontmatter = z.infer<typeof BaseFrontmatterSchema>\n\n// ── Project frontmatter (extends base with project-specific fields) ──\n\nexport const ProjectFrontmatterSchema = BaseFrontmatterSchema.extend({\n gitRepo: z.string().url().optional(),\n links: z\n .array(\n z.object({\n url: z.string().url(),\n text: z.string(),\n }),\n )\n .optional(),\n})\n\nexport type ProjectFrontmatter = z.infer<typeof ProjectFrontmatterSchema>\n\n// ── Blog frontmatter (extends base with blog-specific fields) ──\n\nexport const BlogFrontmatterSchema = BaseFrontmatterSchema.extend({\n category: z.string().optional(),\n featured: z.boolean().optional(),\n coverImage: z.string().optional(),\n})\n\nexport type BlogFrontmatter = z.infer<typeof BlogFrontmatterSchema>\n","import { z } from 'zod'\n\n// ── Heading (extracted from markdown) ──\n\nexport const HeadingSchema = z.object({\n depth: z.number(),\n text: z.string(),\n slug: z.string(),\n})\n\nexport type Heading = z.infer<typeof HeadingSchema>\n","import { z } from 'zod'\n\n// ── Markdown config ──\n\nexport const MarkdownConfigSchema = z.object({\n remarkPlugins: z.array(z.any()).optional(),\n rehypePlugins: z.array(z.any()).optional(),\n shiki: z\n .object({\n themes: z.object({\n light: z.string(),\n dark: z.string(),\n }),\n langAlias: z.record(z.string(), z.string()).optional(),\n defaultShowLineNumbers: z.boolean().optional(),\n })\n .optional(),\n})\n\nexport type MarkdownConfig = z.infer<typeof MarkdownConfigSchema>\n"],"mappings":";;AAIA,MAAa,wBAAwB,EAClC,OAAO;CACN,OAAO,EAAE,QAAQ;CACjB,aAAa,EAAE,QAAQ;CACvB,eAAe,EAAE,OAAO,MAAM;CAC9B,eAAe,EAAE,OAAO,MAAM,CAAC,UAAU;CACzC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;CACrC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,QAAQ,MAAM;CAC7C,CAAC,CACD,aAAa;AAMhB,MAAa,2BAA2B,sBAAsB,OAAO;CACnE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACpC,OAAO,EACJ,MACC,EAAE,OAAO;EACP,KAAK,EAAE,QAAQ,CAAC,KAAK;EACrB,MAAM,EAAE,QAAQ;EACjB,CAAC,CACH,CACA,UAAU;CACd,CAAC;AAMF,MAAa,wBAAwB,sBAAsB,OAAO;CAChE,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,UAAU,EAAE,SAAS,CAAC,UAAU;CAChC,YAAY,EAAE,QAAQ,CAAC,UAAU;CAClC,CAAC;;;ACnCF,MAAa,gBAAgB,EAAE,OAAO;CACpC,OAAO,EAAE,QAAQ;CACjB,MAAM,EAAE,QAAQ;CAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;;;ACJF,MAAa,uBAAuB,EAAE,OAAO;CAC3C,eAAe,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,UAAU;CAC1C,eAAe,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,UAAU;CAC1C,OAAO,EACJ,OAAO;EACN,QAAQ,EAAE,OAAO;GACf,OAAO,EAAE,QAAQ;GACjB,MAAM,EAAE,QAAQ;GACjB,CAAC;EACF,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,UAAU;EACtD,wBAAwB,EAAE,SAAS,CAAC,UAAU;EAC/C,CAAC,CACD,UAAU;CACd,CAAC"}
1
+ {"version":3,"file":"schemas-UL4ynWsA.mjs","names":[],"sources":["../src/schemas/frontmatter.ts","../src/schemas/heading.ts","../src/schemas/markdown-config.ts"],"sourcesContent":["import { z } from 'zod'\n\n// ── Base frontmatter (required for all content items) ──\n\nexport const BaseFrontmatterSchema = z\n .object({\n title: z.string(),\n description: z.string(),\n publishedDate: z.coerce.date(),\n lastUpdatedOn: z.coerce.date().optional(),\n tags: z.array(z.string()).default([]),\n draft: z.boolean().optional().default(false),\n })\n .passthrough()\n\nexport type BaseFrontmatter = z.infer<typeof BaseFrontmatterSchema>\n\n// ── Project frontmatter (extends base with project-specific fields) ──\n\nexport const ProjectFrontmatterSchema = BaseFrontmatterSchema.extend({\n gitRepo: z.string().url().optional(),\n links: z\n .array(\n z.object({\n url: z.string().url(),\n text: z.string(),\n }),\n )\n .optional(),\n})\n\nexport type ProjectFrontmatter = z.infer<typeof ProjectFrontmatterSchema>\n\n// ── Blog frontmatter (extends base with blog-specific fields) ──\n\nexport const BlogFrontmatterSchema = BaseFrontmatterSchema.extend({\n category: z.string().optional(),\n featured: z.boolean().optional(),\n coverImage: z.string().optional(),\n})\n\nexport type BlogFrontmatter = z.infer<typeof BlogFrontmatterSchema>\n","import { z } from 'zod'\n\n// ── Heading (extracted from markdown) ──\n\nexport const HeadingSchema = z.object({\n depth: z.number(),\n text: z.string(),\n slug: z.string(),\n})\n\nexport type Heading = z.infer<typeof HeadingSchema>\n","import { z } from 'zod'\n\n// ── Markdown config ──\n\nexport const MarkdownConfigSchema = z.object({\n remarkPlugins: z.array(z.any()).optional(),\n rehypePlugins: z.array(z.any()).optional(),\n shiki: z\n .object({\n themes: z.object({\n light: z.string(),\n dark: z.string(),\n }),\n langAlias: z.record(z.string(), z.string()).optional(),\n defaultShowLineNumbers: z.boolean().optional(),\n })\n .optional(),\n})\n\nexport type MarkdownConfig = z.infer<typeof MarkdownConfigSchema>\n"],"mappings":";;AAIA,MAAa,wBAAwB,EAClC,OAAO;CACN,OAAO,EAAE,QAAQ;CACjB,aAAa,EAAE,QAAQ;CACvB,eAAe,EAAE,OAAO,MAAM;CAC9B,eAAe,EAAE,OAAO,MAAM,CAAC,UAAU;CACzC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;CACrC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,QAAQ,MAAM;CAC7C,CAAC,CACD,aAAa;AAMhB,MAAa,2BAA2B,sBAAsB,OAAO;CACnE,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACpC,OAAO,EACJ,MACC,EAAE,OAAO;EACP,KAAK,EAAE,QAAQ,CAAC,KAAK;EACrB,MAAM,EAAE,QAAQ;EACjB,CAAC,CACH,CACA,UAAU;CACd,CAAC;AAMF,MAAa,wBAAwB,sBAAsB,OAAO;CAChE,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,UAAU,EAAE,SAAS,CAAC,UAAU;CAChC,YAAY,EAAE,QAAQ,CAAC,UAAU;CAClC,CAAC;;;ACnCF,MAAa,gBAAgB,EAAE,OAAO;CACpC,OAAO,EAAE,QAAQ;CACjB,MAAM,EAAE,QAAQ;CAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;;;ACJF,MAAa,uBAAuB,EAAE,OAAO;CAC3C,eAAe,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,UAAU;CAC1C,eAAe,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,UAAU;CAC1C,OAAO,EACJ,OAAO;EACN,QAAQ,EAAE,OAAO;GACf,OAAO,EAAE,QAAQ;GACjB,MAAM,EAAE,QAAQ;GACjB,CAAC;EACF,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,UAAU;EACtD,wBAAwB,EAAE,SAAS,CAAC,UAAU;EAC/C,CAAC,CACD,UAAU;CACd,CAAC"}
@@ -0,0 +1,202 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { basename, resolve } from "path";
3
+ import { z } from "zod";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ //#region src/mcp/shared.ts
7
+ /**
8
+ * Shared MCP server utilities.
9
+ *
10
+ * Common helpers used by both @pagesmith/core and @pagesmith/docs MCP servers.
11
+ */
12
+ /** Read the package version from a package.json relative to the calling module. */
13
+ function getPackageVersion(moduleDir) {
14
+ const pkgPath = resolve(moduleDir, "..", "..", "package.json");
15
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "0.0.0";
16
+ }
17
+ /** Resolve a doc file path relative to the package root. */
18
+ function resolvePackageDocPath(moduleDir, relativePath) {
19
+ return resolve(moduleDir, "..", "..", relativePath);
20
+ }
21
+ /** Load a file as an MCP text resource response. Throws if the file doesn't exist. */
22
+ function asTextResource(uri, path) {
23
+ if (!existsSync(path)) throw new Error(`Resource file not found: ${path}`);
24
+ return { contents: [{
25
+ uri,
26
+ mimeType: "text/markdown",
27
+ text: readFileSync(path, "utf-8")
28
+ }] };
29
+ }
30
+ //#endregion
31
+ //#region src/mcp/server.ts
32
+ function getLoaderType(loader) {
33
+ if (typeof loader === "string") return loader;
34
+ return loader.name ?? "custom";
35
+ }
36
+ function getSchemaFieldNames(schema) {
37
+ if (schema instanceof z.ZodObject) return Object.keys(schema.shape);
38
+ if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) return getSchemaFieldNames(schema.unwrap());
39
+ }
40
+ function createCoreMcpServer(options) {
41
+ const { layer } = options;
42
+ const baseRoot = resolve(options.rootDir ?? process.cwd());
43
+ const server = new McpServer({
44
+ name: "@pagesmith/core-mcp",
45
+ version: getPackageVersion(import.meta.dirname)
46
+ }, { instructions: ["Use core_* tools to inspect @pagesmith/core content layers.", `Root directory: ${baseRoot}`].join("\n") });
47
+ server.registerTool("core_list_collections", {
48
+ description: "List all configured collections with their loader type, directory, and schema field names.",
49
+ inputSchema: {}
50
+ }, async () => {
51
+ const collections = layer.getCollectionNames().map((name) => {
52
+ const def = layer.getCollectionDef(name);
53
+ return {
54
+ name,
55
+ loader: def ? getLoaderType(def.loader) : "unknown",
56
+ directory: def?.directory,
57
+ schemaFields: def ? getSchemaFieldNames(def.schema) : void 0
58
+ };
59
+ });
60
+ return { content: [{
61
+ type: "text",
62
+ text: JSON.stringify(collections, null, 2)
63
+ }] };
64
+ });
65
+ server.registerTool("core_list_entries", {
66
+ description: "List entries in a collection with slug, title, description, and file path. Does not render content. Supports pagination via limit/offset.",
67
+ inputSchema: {
68
+ collection: z.string().describe("Collection name"),
69
+ limit: z.number().int().min(1).max(200).default(50).describe("Maximum number of entries to return (default 50)"),
70
+ offset: z.number().int().min(0).default(0).describe("Number of entries to skip (default 0)")
71
+ }
72
+ }, async ({ collection, limit, offset }) => {
73
+ const entries = await layer.getCollection(collection);
74
+ const total = entries.length;
75
+ const items = entries.slice(offset, offset + limit).map((entry) => {
76
+ const data = entry.data ?? {};
77
+ return {
78
+ slug: entry.slug,
79
+ title: typeof data.title === "string" ? data.title : void 0,
80
+ description: typeof data.description === "string" ? data.description : void 0,
81
+ filePath: entry.filePath
82
+ };
83
+ });
84
+ return { content: [{
85
+ type: "text",
86
+ text: JSON.stringify({
87
+ collection,
88
+ total,
89
+ offset,
90
+ limit,
91
+ count: items.length,
92
+ entries: items
93
+ }, null, 2)
94
+ }] };
95
+ });
96
+ server.registerTool("core_get_entry", {
97
+ description: "Load a single content entry by collection name and slug. Returns slug, validated data, rendered HTML, headings, and read time.",
98
+ inputSchema: {
99
+ collection: z.string().describe("Collection name"),
100
+ slug: z.string().describe("Entry slug")
101
+ }
102
+ }, async ({ collection, slug }) => {
103
+ const entry = await layer.getEntry(collection, slug);
104
+ if (!entry) throw new Error(`Entry not found: ${collection}/${slug}`);
105
+ const rendered = await entry.render();
106
+ return { content: [{
107
+ type: "text",
108
+ text: JSON.stringify({
109
+ slug: entry.slug,
110
+ collection: entry.collection,
111
+ filePath: entry.filePath,
112
+ data: entry.data,
113
+ html: rendered.html,
114
+ headings: rendered.headings,
115
+ readTime: rendered.readTime
116
+ }, null, 2)
117
+ }] };
118
+ });
119
+ server.registerTool("core_validate", {
120
+ description: "Run validation on a specific collection or all collections. Returns structured validation results with error and warning counts.",
121
+ inputSchema: { collection: z.string().optional().describe("Collection name (omit to validate all)") }
122
+ }, async ({ collection }) => {
123
+ const results = await layer.validate(collection);
124
+ return { content: [{
125
+ type: "text",
126
+ text: JSON.stringify(results, null, 2)
127
+ }] };
128
+ });
129
+ server.registerTool("core_search_entries", {
130
+ description: "Search entries across collections by matching a query string against titles, descriptions, tags, and slugs. Case-insensitive. Returns up to 20 matches.",
131
+ inputSchema: {
132
+ query: z.string().describe("Search query string"),
133
+ collection: z.string().optional().describe("Limit search to a specific collection")
134
+ }
135
+ }, async ({ query, collection }) => {
136
+ if (!query.trim()) return { content: [{
137
+ type: "text",
138
+ text: JSON.stringify({
139
+ query,
140
+ matches: [],
141
+ count: 0
142
+ }, null, 2)
143
+ }] };
144
+ const queryLower = query.toLowerCase();
145
+ const maxResults = 20;
146
+ const collectionNames = collection ? [collection] : layer.getCollectionNames();
147
+ const matches = [];
148
+ for (const name of collectionNames) {
149
+ if (matches.length >= maxResults) break;
150
+ const entries = await layer.getCollection(name);
151
+ for (const entry of entries) {
152
+ if (matches.length >= maxResults) break;
153
+ const data = entry.data ?? {};
154
+ const title = typeof data.title === "string" ? data.title : "";
155
+ const description = typeof data.description === "string" ? data.description : "";
156
+ const tags = Array.isArray(data.tags) ? data.tags.join(" ") : "";
157
+ if (`${entry.slug} ${title} ${description} ${tags}`.toLowerCase().includes(queryLower)) matches.push({
158
+ collection: name,
159
+ slug: entry.slug,
160
+ title: title || void 0,
161
+ description: description || void 0,
162
+ filePath: entry.filePath
163
+ });
164
+ }
165
+ }
166
+ return { content: [{
167
+ type: "text",
168
+ text: JSON.stringify({
169
+ query,
170
+ collection: collection ?? null,
171
+ count: matches.length,
172
+ matches
173
+ }, null, 2)
174
+ }] };
175
+ });
176
+ server.registerResource("core-agent-usage", "pagesmith://core/agents/usage", {
177
+ title: "@pagesmith/core agent usage",
178
+ description: "Version-matched AI usage guide for @pagesmith/core.",
179
+ mimeType: "text/markdown"
180
+ }, async () => asTextResource("pagesmith://core/agents/usage", resolvePackageDocPath(import.meta.dirname, "docs/agents/usage.md")));
181
+ server.registerResource("core-llms-full", "pagesmith://core/llms-full", {
182
+ title: "@pagesmith/core llms-full",
183
+ description: "Version-matched full AI reference for @pagesmith/core.",
184
+ mimeType: "text/markdown"
185
+ }, async () => asTextResource("pagesmith://core/llms-full", resolvePackageDocPath(import.meta.dirname, "docs/llms-full.txt")));
186
+ server.registerResource("core-reference", "pagesmith://core/reference", {
187
+ title: "@pagesmith/core reference",
188
+ description: "Core package reference for content layer, schemas, loaders, and markdown pipeline.",
189
+ mimeType: "text/markdown"
190
+ }, async () => asTextResource("pagesmith://core/reference", resolvePackageDocPath(import.meta.dirname, "REFERENCE.md")));
191
+ return server;
192
+ }
193
+ async function startCoreMcpServer(options) {
194
+ const server = createCoreMcpServer(options);
195
+ const transport = new StdioServerTransport();
196
+ await server.connect(transport);
197
+ console.error(`[pagesmith:mcp] @pagesmith/core MCP server started (root=${basename(resolve(options.rootDir ?? process.cwd()))})`);
198
+ }
199
+ //#endregion
200
+ export { resolvePackageDocPath as a, getPackageVersion as i, startCoreMcpServer as n, asTextResource as r, createCoreMcpServer as t };
201
+
202
+ //# sourceMappingURL=server-D3DHoh5f.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-D3DHoh5f.mjs","names":[],"sources":["../src/mcp/shared.ts","../src/mcp/server.ts"],"sourcesContent":["/**\n * Shared MCP server utilities.\n *\n * Common helpers used by both @pagesmith/core and @pagesmith/docs MCP servers.\n */\n\nimport { existsSync, readFileSync } from 'fs'\nimport { resolve } from 'path'\n\n/** Read the package version from a package.json relative to the calling module. */\nexport function getPackageVersion(moduleDir: string): string {\n const pkgPath = resolve(moduleDir, '..', '..', 'package.json')\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version?: string }\n return pkg.version ?? '0.0.0'\n}\n\n/** Resolve a doc file path relative to the package root. */\nexport function resolvePackageDocPath(moduleDir: string, relativePath: string): string {\n return resolve(moduleDir, '..', '..', relativePath)\n}\n\n/** Load a file as an MCP text resource response. Throws if the file doesn't exist. */\nexport function asTextResource(uri: string, path: string) {\n if (!existsSync(path)) {\n throw new Error(`Resource file not found: ${path}`)\n }\n\n return {\n contents: [\n {\n uri,\n mimeType: 'text/markdown',\n text: readFileSync(path, 'utf-8'),\n },\n ],\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { basename, resolve } from 'path'\nimport { z } from 'zod'\nimport type { ContentLayer } from '../content-layer.js'\nimport type { Loader } from '../loaders/types.js'\nimport { asTextResource, getPackageVersion, resolvePackageDocPath } from './shared.js'\n\nexport type CoreMcpServerOptions = {\n layer: ContentLayer\n rootDir?: string\n}\n\nfunction getLoaderType(loader: string | Loader): string {\n if (typeof loader === 'string') return loader\n return loader.name ?? 'custom'\n}\n\nfunction getSchemaFieldNames(schema: z.ZodType): string[] | undefined {\n if (schema instanceof z.ZodObject) {\n return Object.keys(schema.shape)\n }\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n return getSchemaFieldNames(schema.unwrap() as z.ZodType)\n }\n return undefined\n}\n\nexport function createCoreMcpServer(options: CoreMcpServerOptions): McpServer {\n const { layer } = options\n const baseRoot = resolve(options.rootDir ?? process.cwd())\n\n const server = new McpServer(\n {\n name: '@pagesmith/core-mcp',\n version: getPackageVersion(import.meta.dirname),\n },\n {\n instructions: [\n 'Use core_* tools to inspect @pagesmith/core content layers.',\n `Root directory: ${baseRoot}`,\n ].join('\\n'),\n },\n )\n\n // ── Tools ──\n\n server.registerTool(\n 'core_list_collections',\n {\n description:\n 'List all configured collections with their loader type, directory, and schema field names.',\n inputSchema: {},\n },\n async () => {\n const names = layer.getCollectionNames()\n const collections = names.map((name) => {\n const def = layer.getCollectionDef(name)\n return {\n name,\n loader: def ? getLoaderType(def.loader) : 'unknown',\n directory: def?.directory,\n schemaFields: def ? getSchemaFieldNames(def.schema) : undefined,\n }\n })\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(collections, null, 2),\n },\n ],\n }\n },\n )\n\n server.registerTool(\n 'core_list_entries',\n {\n description:\n 'List entries in a collection with slug, title, description, and file path. Does not render content. Supports pagination via limit/offset.',\n inputSchema: {\n collection: z.string().describe('Collection name'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(200)\n .default(50)\n .describe('Maximum number of entries to return (default 50)'),\n offset: z\n .number()\n .int()\n .min(0)\n .default(0)\n .describe('Number of entries to skip (default 0)'),\n },\n },\n async ({\n collection,\n limit,\n offset,\n }: {\n collection: string\n limit: number\n offset: number\n }) => {\n const entries = await layer.getCollection(collection)\n const total = entries.length\n const page = entries.slice(offset, offset + limit)\n\n const items = page.map((entry) => {\n const data = (entry.data ?? {}) as Record<string, unknown>\n return {\n slug: entry.slug,\n title: typeof data.title === 'string' ? data.title : undefined,\n description: typeof data.description === 'string' ? data.description : undefined,\n filePath: entry.filePath,\n }\n })\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n collection,\n total,\n offset,\n limit,\n count: items.length,\n entries: items,\n },\n null,\n 2,\n ),\n },\n ],\n }\n },\n )\n\n server.registerTool(\n 'core_get_entry',\n {\n description:\n 'Load a single content entry by collection name and slug. Returns slug, validated data, rendered HTML, headings, and read time.',\n inputSchema: {\n collection: z.string().describe('Collection name'),\n slug: z.string().describe('Entry slug'),\n },\n },\n async ({ collection, slug }: { collection: string; slug: string }) => {\n const entry = await layer.getEntry(collection, slug)\n if (!entry) {\n throw new Error(`Entry not found: ${collection}/${slug}`)\n }\n\n const rendered = await entry.render()\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n slug: entry.slug,\n collection: entry.collection,\n filePath: entry.filePath,\n data: entry.data,\n html: rendered.html,\n headings: rendered.headings,\n readTime: rendered.readTime,\n },\n null,\n 2,\n ),\n },\n ],\n }\n },\n )\n\n server.registerTool(\n 'core_validate',\n {\n description:\n 'Run validation on a specific collection or all collections. Returns structured validation results with error and warning counts.',\n inputSchema: {\n collection: z.string().optional().describe('Collection name (omit to validate all)'),\n },\n },\n async ({ collection }: { collection?: string }) => {\n const results = await layer.validate(collection)\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(results, null, 2),\n },\n ],\n }\n },\n )\n\n server.registerTool(\n 'core_search_entries',\n {\n description:\n 'Search entries across collections by matching a query string against titles, descriptions, tags, and slugs. Case-insensitive. Returns up to 20 matches.',\n inputSchema: {\n query: z.string().describe('Search query string'),\n collection: z.string().optional().describe('Limit search to a specific collection'),\n },\n },\n async ({ query, collection }: { query: string; collection?: string }) => {\n if (!query.trim()) {\n return {\n content: [\n { type: 'text', text: JSON.stringify({ query, matches: [], count: 0 }, null, 2) },\n ],\n }\n }\n\n const queryLower = query.toLowerCase()\n const maxResults = 20\n const collectionNames = collection ? [collection] : layer.getCollectionNames()\n\n type SearchMatch = {\n collection: string\n slug: string\n title?: string\n description?: string\n filePath: string\n }\n\n const matches: SearchMatch[] = []\n\n for (const name of collectionNames) {\n if (matches.length >= maxResults) break\n const entries = await layer.getCollection(name)\n for (const entry of entries) {\n if (matches.length >= maxResults) break\n const data = (entry.data ?? {}) as Record<string, unknown>\n const title = typeof data.title === 'string' ? data.title : ''\n const description = typeof data.description === 'string' ? data.description : ''\n const tags = Array.isArray(data.tags) ? data.tags.join(' ') : ''\n const searchText = `${entry.slug} ${title} ${description} ${tags}`.toLowerCase()\n\n if (searchText.includes(queryLower)) {\n matches.push({\n collection: name,\n slug: entry.slug,\n title: title || undefined,\n description: description || undefined,\n filePath: entry.filePath,\n })\n }\n }\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n { query, collection: collection ?? null, count: matches.length, matches },\n null,\n 2,\n ),\n },\n ],\n }\n },\n )\n\n // ── Resources ──\n\n server.registerResource(\n 'core-agent-usage',\n 'pagesmith://core/agents/usage',\n {\n title: '@pagesmith/core agent usage',\n description: 'Version-matched AI usage guide for @pagesmith/core.',\n mimeType: 'text/markdown',\n },\n async () =>\n asTextResource(\n 'pagesmith://core/agents/usage',\n resolvePackageDocPath(import.meta.dirname, 'docs/agents/usage.md'),\n ),\n )\n\n server.registerResource(\n 'core-llms-full',\n 'pagesmith://core/llms-full',\n {\n title: '@pagesmith/core llms-full',\n description: 'Version-matched full AI reference for @pagesmith/core.',\n mimeType: 'text/markdown',\n },\n async () =>\n asTextResource(\n 'pagesmith://core/llms-full',\n resolvePackageDocPath(import.meta.dirname, 'docs/llms-full.txt'),\n ),\n )\n\n server.registerResource(\n 'core-reference',\n 'pagesmith://core/reference',\n {\n title: '@pagesmith/core reference',\n description:\n 'Core package reference for content layer, schemas, loaders, and markdown pipeline.',\n mimeType: 'text/markdown',\n },\n async () =>\n asTextResource(\n 'pagesmith://core/reference',\n resolvePackageDocPath(import.meta.dirname, 'REFERENCE.md'),\n ),\n )\n\n return server\n}\n\nexport async function startCoreMcpServer(options: CoreMcpServerOptions): Promise<void> {\n const server = createCoreMcpServer(options)\n const transport = new StdioServerTransport()\n await server.connect(transport)\n // Keep logs on stderr; stdout is reserved for MCP protocol messages.\n console.error(\n `[pagesmith:mcp] @pagesmith/core MCP server started (root=${basename(resolve(options.rootDir ?? process.cwd()))})`,\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ //#region src/ssg-utils/index.d.ts
2
+ /**
3
+ * Shared SSG utilities for framework example sites.
4
+ *
5
+ * Provides types that match virtual module output, icon SVGs,
6
+ * route helpers, date formatting, and HTML document rendering
7
+ * used across all framework examples.
8
+ */
9
+ type Heading = {
10
+ depth: number;
11
+ slug: string;
12
+ text: string;
13
+ };
14
+ type MarkdownEntry<T extends Record<string, unknown> = Record<string, unknown>> = {
15
+ contentSlug: string;
16
+ html: string;
17
+ headings: Heading[];
18
+ frontmatter: T;
19
+ };
20
+ type NavEntry = {
21
+ slug: string;
22
+ title: string;
23
+ description?: string;
24
+ url: string;
25
+ date?: string;
26
+ tags?: string[];
27
+ };
28
+ type NavGroup = {
29
+ series: string;
30
+ items: NavEntry[];
31
+ };
32
+ declare const menuIcon = "<svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"><path d=\"M3 5h14M3 10h14M3 15h14\"/></svg>";
33
+ declare const closeIcon = "<svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"><path d=\"m5 5 10 10M15 5 5 15\"/></svg>";
34
+ declare const searchIcon = "<svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"><circle cx=\"8.5\" cy=\"8.5\" r=\"5.5\"/><path d=\"m13 13 4 4\"/></svg>";
35
+ declare function normalizeRoute(url: string, base: string): string;
36
+ declare function leafSlug(contentSlug: string, collection: string): string;
37
+ declare function routeFor(entry: MarkdownEntry, collection: string, options?: {
38
+ topLevel?: string[];
39
+ }): string;
40
+ declare function getTime(date: string | Date | undefined): number;
41
+ declare function toIso(date: string | Date | undefined): string | undefined;
42
+ declare function formatDate(iso: string): string;
43
+ declare function estimateReadTime(html: string): number;
44
+ declare function escapeHtml(value: string): string;
45
+ declare function buildNavEntries(entries: MarkdownEntry[], base: string, section: string): NavEntry[];
46
+ declare function groupByField(entries: MarkdownEntry[], base: string, section: string, field: string, fallback?: string): NavGroup[];
47
+ type DocumentShellOptions = {
48
+ title: string;
49
+ description?: string;
50
+ basePath: string;
51
+ cssPath: string;
52
+ jsPath?: string;
53
+ searchEnabled?: boolean;
54
+ bodyHtml: string;
55
+ sidebarHtml?: string;
56
+ headHtml?: string;
57
+ };
58
+ declare function renderDocumentShell(options: DocumentShellOptions): string;
59
+ //#endregion
60
+ export { DocumentShellOptions, Heading, MarkdownEntry, NavEntry, NavGroup, buildNavEntries, closeIcon, escapeHtml, estimateReadTime, formatDate, getTime, groupByField, leafSlug, menuIcon, normalizeRoute, renderDocumentShell, routeFor, searchIcon, toIso };
61
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ssg-utils/index.ts"],"mappings":";;AAUA;;;;;;KAAY,OAAA;EACV,KAAA;EACA,IAAA;EACA,IAAA;AAAA;AAAA,KAGU,aAAA,WAAwB,MAAA,oBAA0B,MAAA;EAC5D,WAAA;EACA,IAAA;EACA,QAAA,EAAU,OAAA;EACV,WAAA,EAAa,CAAA;AAAA;AAAA,KAGH,QAAA;EACV,IAAA;EACA,KAAA;EACA,WAAA;EACA,GAAA;EACA,IAAA;EACA,IAAA;AAAA;AAAA,KAGU,QAAA;EACV,MAAA;EACA,KAAA,EAAO,QAAA;AAAA;AAAA,cAKI,QAAA;AAAA,cAEA,SAAA;AAAA,cAEA,UAAA;AAAA,iBAKG,cAAA,CAAe,GAAA,UAAa,IAAA;AAAA,iBAO5B,QAAA,CAAS,WAAA,UAAqB,UAAA;AAAA,iBAI9B,QAAA,CACd,KAAA,EAAO,aAAA,EACP,UAAA,UACA,OAAA;EAAY,QAAA;AAAA;AAAA,iBASE,OAAA,CAAQ,IAAA,WAAe,IAAA;AAAA,iBAKvB,KAAA,CAAM,IAAA,WAAe,IAAA;AAAA,iBAKrB,UAAA,CAAW,GAAA;AAAA,iBAWX,gBAAA,CAAiB,IAAA;AAAA,iBAKjB,UAAA,CAAW,KAAA;AAAA,iBASX,eAAA,CACd,OAAA,EAAS,aAAA,IACT,IAAA,UACA,OAAA,WACC,QAAA;AAAA,iBAWa,YAAA,CACd,OAAA,EAAS,aAAA,IACT,IAAA,UACA,OAAA,UACA,KAAA,UACA,QAAA,YACC,QAAA;AAAA,KAuBS,oBAAA;EACV,KAAA;EACA,WAAA;EACA,QAAA;EACA,OAAA;EACA,MAAA;EACA,aAAA;EACA,QAAA;EACA,WAAA;EACA,QAAA;AAAA;AAAA,iBAGc,mBAAA,CAAoB,OAAA,EAAS,oBAAA"}