@sorane/core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +42 -0
  2. package/src/ai-disclosure.ts +181 -0
  3. package/src/asset-provenance.ts +141 -0
  4. package/src/associated-media.ts +93 -0
  5. package/src/blog-pages.ts +175 -0
  6. package/src/build.ts +1109 -0
  7. package/src/c2pa-pass.ts +116 -0
  8. package/src/catalog.ts +61 -0
  9. package/src/config.ts +255 -0
  10. package/src/diagrams/compile-d2.ts +70 -0
  11. package/src/diagrams/compile-graphviz.ts +71 -0
  12. package/src/diagrams/compile-mermaid.ts +102 -0
  13. package/src/diagrams/diagram-hash.ts +5 -0
  14. package/src/diagrams/diagram-meta.ts +74 -0
  15. package/src/diagrams/emit-diagram-assets.ts +135 -0
  16. package/src/diagrams/mermaid-head.ts +6 -0
  17. package/src/diagrams/needs-async-compile.ts +12 -0
  18. package/src/diagrams/parse-diagram-fence.ts +109 -0
  19. package/src/diagrams/rehype-diagram-pre.ts +39 -0
  20. package/src/diagrams/remark-inject-built-figures.ts +32 -0
  21. package/src/diagrams/render-async.ts +241 -0
  22. package/src/diagrams/render-body-section.ts +52 -0
  23. package/src/diagrams/validate-diagram-alt.ts +56 -0
  24. package/src/docs.ts +257 -0
  25. package/src/emit-page.ts +87 -0
  26. package/src/index.ts +49 -0
  27. package/src/iptc-xmp-pass.ts +94 -0
  28. package/src/markdown-image-refs.ts +135 -0
  29. package/src/migrate.ts +60 -0
  30. package/src/not-found.ts +64 -0
  31. package/src/og-meta.ts +18 -0
  32. package/src/render.ts +233 -0
  33. package/src/site-labels.ts +97 -0
  34. package/src/site-meta.ts +138 -0
  35. package/src/ssg.ts +676 -0
  36. package/src/static-assets.ts +198 -0
  37. package/src/theme-assets.ts +16 -0
  38. package/src/validate-heading-structure.ts +51 -0
@@ -0,0 +1,138 @@
1
+ export interface SiteEntry {
2
+ readonly url: string;
3
+ readonly lastmod?: string;
4
+ readonly isIndex: boolean;
5
+ }
6
+
7
+ export function buildRobotsTxt(baseUrl: string): string {
8
+ const lines = ["User-agent: *", "Allow: /"];
9
+ if (baseUrl.length > 0) lines.push(`Sitemap: ${baseUrl}/sitemap.xml`);
10
+ return lines.join("\n") + "\n";
11
+ }
12
+
13
+ function escapeXml(s: string): string {
14
+ return s
15
+ .replace(/&/g, "&")
16
+ .replace(/</g, "&lt;")
17
+ .replace(/>/g, "&gt;")
18
+ .replace(/"/g, "&quot;");
19
+ }
20
+
21
+ export function buildSitemapXml(entries: readonly SiteEntry[], baseUrl: string): string {
22
+ const abs = (u: string) => (baseUrl.length > 0 ? `${baseUrl}/${u}` : u);
23
+ const urls = entries
24
+ .map((e) => {
25
+ const parts = [` <loc>${escapeXml(abs(e.url))}</loc>`];
26
+ if (e.lastmod) parts.push(` <lastmod>${escapeXml(e.lastmod)}</lastmod>`);
27
+ parts.push(` <priority>${e.isIndex ? "0.8" : "0.5"}</priority>`);
28
+ return ` <url>\n${parts.join("\n")}\n </url>`;
29
+ })
30
+ .join("\n");
31
+ return (
32
+ '<?xml version="1.0" encoding="UTF-8"?>\n' +
33
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n' +
34
+ urls +
35
+ "\n</urlset>\n"
36
+ );
37
+ }
38
+
39
+ export interface LlmsTxtOptions {
40
+ readonly siteTitle: string;
41
+ readonly siteDescription: string;
42
+ readonly baseUrl: string;
43
+ readonly aiLabeledCount?: number;
44
+ readonly diagramsEnabled?: boolean;
45
+ }
46
+
47
+ export interface FeedEntry {
48
+ readonly title: string;
49
+ readonly url: string;
50
+ readonly id: string;
51
+ readonly updated: string;
52
+ readonly summary?: string;
53
+ readonly digitalSourceCode?: string;
54
+ }
55
+
56
+ export function buildAtomFeed(
57
+ entries: readonly FeedEntry[],
58
+ opts: { siteTitle: string; siteDescription: string; baseUrl: string; feedPath?: string },
59
+ ): string {
60
+ const feedUrl =
61
+ opts.baseUrl.length > 0
62
+ ? `${opts.baseUrl}/${opts.feedPath ?? "feed.xml"}`
63
+ : opts.feedPath ?? "feed.xml";
64
+ const updated =
65
+ entries.length > 0
66
+ ? entries[0]!.updated
67
+ : new Date().toISOString();
68
+ const items = entries
69
+ .map((e) => {
70
+ const summary = e.summary
71
+ ? `<summary>${escapeXml(e.summary)}</summary>`
72
+ : "";
73
+ const category = e.digitalSourceCode
74
+ ? ` <category term="ai-disclosure:${escapeXml(e.digitalSourceCode)}" scheme="http://cv.iptc.org/newscodes/digitalsourcetype" />\n`
75
+ : "";
76
+ return (
77
+ ` <entry>\n` +
78
+ ` <title>${escapeXml(e.title)}</title>\n` +
79
+ ` <link href="${escapeXml(e.url)}" />\n` +
80
+ ` <id>${escapeXml(e.id)}</id>\n` +
81
+ ` <updated>${escapeXml(e.updated)}</updated>\n` +
82
+ `${category}` +
83
+ `${summary}\n` +
84
+ ` </entry>`
85
+ );
86
+ })
87
+ .join("\n");
88
+ return (
89
+ '<?xml version="1.0" encoding="utf-8"?>\n' +
90
+ '<feed xmlns="http://www.w3.org/2005/Atom">\n' +
91
+ ` <title>${escapeXml(opts.siteTitle)}</title>\n` +
92
+ ` <subtitle>${escapeXml(opts.siteDescription)}</subtitle>\n` +
93
+ ` <link href="${escapeXml(feedUrl)}" rel="self" />\n` +
94
+ ` <link href="${escapeXml(opts.baseUrl.length > 0 ? opts.baseUrl + "/" : "")}" />\n` +
95
+ ` <updated>${escapeXml(updated)}</updated>\n` +
96
+ ` <id>${escapeXml(feedUrl)}</id>\n` +
97
+ `${items}\n` +
98
+ `</feed>\n`
99
+ );
100
+ }
101
+
102
+ export function buildLlmsTxt(opts: LlmsTxtOptions): string {
103
+ const abs = (u: string) =>
104
+ /^https?:/.test(u) || opts.baseUrl.length === 0 ? u : `${opts.baseUrl}/${u}`;
105
+ const lines = [
106
+ `# ${opts.siteTitle}`,
107
+ "",
108
+ `> ${opts.siteDescription}`,
109
+ "",
110
+ "## Machine-readable",
111
+ "",
112
+ `- [OKF bundle](${abs("okf/bundle.tar.gz")}): all concepts as {type}/{slug}.md`,
113
+ `- [DCAT catalog](${abs("catalog.jsonld")})`,
114
+ `- [Sitemap](${abs("sitemap.xml")})`,
115
+ ];
116
+ if (opts.diagramsEnabled) {
117
+ lines.push(
118
+ "",
119
+ "Diagram fences (` ```mermaid `, ` ```d2 `) in page bodies are preserved verbatim in sibling `.md` alternates and the OKF bundle.",
120
+ "HTML pages may load client-side Mermaid (`assets/diagrams/sorane-mermaid-loader.mjs`) when fences are present.",
121
+ );
122
+ }
123
+ if (opts.aiLabeledCount !== undefined && opts.aiLabeledCount > 0) {
124
+ lines.push(
125
+ "",
126
+ "## AI content disclosure",
127
+ "",
128
+ "Articles may declare `digitalSourceType` (IPTC NewsCodes / schema.org) in OKF frontmatter.",
129
+ "Published HTML includes JSON-LD `digitalSourceType` and optional EU transparency badges.",
130
+ "Search index (`assets/search-index.json`) exposes `digital_source_type` per chunk when set.",
131
+ "Atom feed (`feed.xml`) includes `category term` when disclosure is present.",
132
+ "",
133
+ `Labeled articles: ${opts.aiLabeledCount}`,
134
+ );
135
+ }
136
+ lines.push("");
137
+ return lines.join("\n");
138
+ }