svedocs 0.1.0-beta.10

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 (286) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +85 -0
  3. package/dist/ai/openai-compatible.d.ts +13 -0
  4. package/dist/ai/openai-compatible.d.ts.map +1 -0
  5. package/dist/ai/openai-compatible.js +92 -0
  6. package/dist/ai/providers.d.ts +20 -0
  7. package/dist/ai/providers.d.ts.map +1 -0
  8. package/dist/ai/providers.js +185 -0
  9. package/dist/ai/rate-limit.d.ts +18 -0
  10. package/dist/ai/rate-limit.d.ts.map +1 -0
  11. package/dist/ai/rate-limit.js +52 -0
  12. package/dist/ai/response.d.ts +4 -0
  13. package/dist/ai/response.d.ts.map +1 -0
  14. package/dist/ai/response.js +151 -0
  15. package/dist/ai/runtime.d.ts +32 -0
  16. package/dist/ai/runtime.d.ts.map +1 -0
  17. package/dist/ai/runtime.js +111 -0
  18. package/dist/ai/types.d.ts +93 -0
  19. package/dist/ai/types.d.ts.map +1 -0
  20. package/dist/ai/types.js +1 -0
  21. package/dist/ai.d.ts +7 -0
  22. package/dist/ai.d.ts.map +1 -0
  23. package/dist/ai.js +6 -0
  24. package/dist/ambient.d.ts +5 -0
  25. package/dist/cloudflare.d.ts +29 -0
  26. package/dist/cloudflare.d.ts.map +1 -0
  27. package/dist/cloudflare.js +149 -0
  28. package/dist/config.d.ts +373 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +299 -0
  31. package/dist/core/checks.d.ts +4 -0
  32. package/dist/core/checks.d.ts.map +1 -0
  33. package/dist/core/checks.js +267 -0
  34. package/dist/core/config.d.ts +6 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +178 -0
  37. package/dist/core/content.d.ts +7 -0
  38. package/dist/core/content.d.ts.map +1 -0
  39. package/dist/core/content.js +340 -0
  40. package/dist/core/i18n.d.ts +5 -0
  41. package/dist/core/i18n.d.ts.map +1 -0
  42. package/dist/core/i18n.js +117 -0
  43. package/dist/core/links.d.ts +5 -0
  44. package/dist/core/links.d.ts.map +1 -0
  45. package/dist/core/links.js +60 -0
  46. package/dist/core/messages.d.ts +149 -0
  47. package/dist/core/messages.d.ts.map +1 -0
  48. package/dist/core/messages.js +163 -0
  49. package/dist/core/navigation.d.ts +5 -0
  50. package/dist/core/navigation.d.ts.map +1 -0
  51. package/dist/core/navigation.js +144 -0
  52. package/dist/core/routes.d.ts +36 -0
  53. package/dist/core/routes.d.ts.map +1 -0
  54. package/dist/core/routes.js +152 -0
  55. package/dist/core/search.d.ts +5 -0
  56. package/dist/core/search.d.ts.map +1 -0
  57. package/dist/core/search.js +41 -0
  58. package/dist/core/types.d.ts +468 -0
  59. package/dist/core/types.d.ts.map +1 -0
  60. package/dist/core/types.js +4 -0
  61. package/dist/core/utils.d.ts +13 -0
  62. package/dist/core/utils.d.ts.map +1 -0
  63. package/dist/core/utils.js +60 -0
  64. package/dist/core.d.ts +9 -0
  65. package/dist/core.d.ts.map +1 -0
  66. package/dist/core.js +8 -0
  67. package/dist/index.d.ts +6 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +3 -0
  70. package/dist/mdx/ast.d.ts +32 -0
  71. package/dist/mdx/ast.d.ts.map +1 -0
  72. package/dist/mdx/ast.js +148 -0
  73. package/dist/mdx/code.d.ts +17 -0
  74. package/dist/mdx/code.d.ts.map +1 -0
  75. package/dist/mdx/code.js +327 -0
  76. package/dist/mdx/compile.d.ts +29 -0
  77. package/dist/mdx/compile.d.ts.map +1 -0
  78. package/dist/mdx/compile.js +59 -0
  79. package/dist/mdx/diff.d.ts +9 -0
  80. package/dist/mdx/diff.d.ts.map +1 -0
  81. package/dist/mdx/diff.js +171 -0
  82. package/dist/mdx/headings.d.ts +4 -0
  83. package/dist/mdx/headings.d.ts.map +1 -0
  84. package/dist/mdx/headings.js +42 -0
  85. package/dist/mdx/links.d.ts +4 -0
  86. package/dist/mdx/links.d.ts.map +1 -0
  87. package/dist/mdx/links.js +144 -0
  88. package/dist/mdx/utils.d.ts +10 -0
  89. package/dist/mdx/utils.d.ts.map +1 -0
  90. package/dist/mdx/utils.js +64 -0
  91. package/dist/og/image.d.ts +25 -0
  92. package/dist/og/image.d.ts.map +1 -0
  93. package/dist/og/image.js +224 -0
  94. package/dist/og/metadata.d.ts +7 -0
  95. package/dist/og/metadata.d.ts.map +1 -0
  96. package/dist/og/metadata.js +198 -0
  97. package/dist/og/response.d.ts +4 -0
  98. package/dist/og/response.d.ts.map +1 -0
  99. package/dist/og/response.js +49 -0
  100. package/dist/og/rss.d.ts +4 -0
  101. package/dist/og/rss.d.ts.map +1 -0
  102. package/dist/og/rss.js +96 -0
  103. package/dist/og/sitemap.d.ts +7 -0
  104. package/dist/og/sitemap.d.ts.map +1 -0
  105. package/dist/og/sitemap.js +90 -0
  106. package/dist/og/types.d.ts +65 -0
  107. package/dist/og/types.d.ts.map +1 -0
  108. package/dist/og/types.js +1 -0
  109. package/dist/og.d.ts +6 -0
  110. package/dist/og.d.ts.map +1 -0
  111. package/dist/og.js +5 -0
  112. package/dist/routes.d.ts +2 -0
  113. package/dist/routes.d.ts.map +1 -0
  114. package/dist/routes.js +1 -0
  115. package/dist/search/algolia.d.ts +10 -0
  116. package/dist/search/algolia.d.ts.map +1 -0
  117. package/dist/search/algolia.js +82 -0
  118. package/dist/search/cloudflare-sync.d.ts +5 -0
  119. package/dist/search/cloudflare-sync.d.ts.map +1 -0
  120. package/dist/search/cloudflare-sync.js +169 -0
  121. package/dist/search/cloudflare.d.ts +11 -0
  122. package/dist/search/cloudflare.d.ts.map +1 -0
  123. package/dist/search/cloudflare.js +126 -0
  124. package/dist/search/local.d.ts +9 -0
  125. package/dist/search/local.d.ts.map +1 -0
  126. package/dist/search/local.js +165 -0
  127. package/dist/search/runtime.d.ts +41 -0
  128. package/dist/search/runtime.d.ts.map +1 -0
  129. package/dist/search/runtime.js +152 -0
  130. package/dist/search/types.d.ts +172 -0
  131. package/dist/search/types.d.ts.map +1 -0
  132. package/dist/search/types.js +1 -0
  133. package/dist/search/typesense.d.ts +11 -0
  134. package/dist/search/typesense.d.ts.map +1 -0
  135. package/dist/search/typesense.js +91 -0
  136. package/dist/search/utils.d.ts +11 -0
  137. package/dist/search/utils.d.ts.map +1 -0
  138. package/dist/search/utils.js +93 -0
  139. package/dist/search.d.ts +8 -0
  140. package/dist/search.d.ts.map +1 -0
  141. package/dist/search.js +7 -0
  142. package/dist/svelte.d.ts +27 -0
  143. package/dist/svelte.d.ts.map +1 -0
  144. package/dist/svelte.js +45 -0
  145. package/dist/testing.d.ts +3 -0
  146. package/dist/testing.d.ts.map +1 -0
  147. package/dist/testing.js +23 -0
  148. package/dist/theme/Article.svelte +145 -0
  149. package/dist/theme/Article.svelte.d.ts +36 -0
  150. package/dist/theme/Article.svelte.d.ts.map +1 -0
  151. package/dist/theme/AskAiPanel.svelte +241 -0
  152. package/dist/theme/AskAiPanel.svelte.d.ts +31 -0
  153. package/dist/theme/AskAiPanel.svelte.d.ts.map +1 -0
  154. package/dist/theme/Brand.svelte +19 -0
  155. package/dist/theme/Brand.svelte.d.ts +22 -0
  156. package/dist/theme/Brand.svelte.d.ts.map +1 -0
  157. package/dist/theme/Button.svelte +34 -0
  158. package/dist/theme/Button.svelte.d.ts +41 -0
  159. package/dist/theme/Button.svelte.d.ts.map +1 -0
  160. package/dist/theme/Checkbox.svelte +63 -0
  161. package/dist/theme/Checkbox.svelte.d.ts +45 -0
  162. package/dist/theme/Checkbox.svelte.d.ts.map +1 -0
  163. package/dist/theme/CodeBlock.svelte +6 -0
  164. package/dist/theme/CodeBlock.svelte.d.ts +22 -0
  165. package/dist/theme/CodeBlock.svelte.d.ts.map +1 -0
  166. package/dist/theme/DocPage.svelte +15 -0
  167. package/dist/theme/DocPage.svelte.d.ts +33 -0
  168. package/dist/theme/DocPage.svelte.d.ts.map +1 -0
  169. package/dist/theme/DocsApp.svelte +194 -0
  170. package/dist/theme/DocsApp.svelte.d.ts +60 -0
  171. package/dist/theme/DocsApp.svelte.d.ts.map +1 -0
  172. package/dist/theme/DocsLayout.svelte +99 -0
  173. package/dist/theme/DocsLayout.svelte.d.ts +39 -0
  174. package/dist/theme/DocsLayout.svelte.d.ts.map +1 -0
  175. package/dist/theme/DocsShell.svelte +91 -0
  176. package/dist/theme/DocsShell.svelte.d.ts +34 -0
  177. package/dist/theme/DocsShell.svelte.d.ts.map +1 -0
  178. package/dist/theme/ErrorPage.svelte +113 -0
  179. package/dist/theme/ErrorPage.svelte.d.ts +34 -0
  180. package/dist/theme/ErrorPage.svelte.d.ts.map +1 -0
  181. package/dist/theme/FloatingToolbar.svelte +8 -0
  182. package/dist/theme/FloatingToolbar.svelte.d.ts +22 -0
  183. package/dist/theme/FloatingToolbar.svelte.d.ts.map +1 -0
  184. package/dist/theme/Footer.svelte +18 -0
  185. package/dist/theme/Footer.svelte.d.ts +24 -0
  186. package/dist/theme/Footer.svelte.d.ts.map +1 -0
  187. package/dist/theme/FooterLinks.svelte +37 -0
  188. package/dist/theme/FooterLinks.svelte.d.ts +22 -0
  189. package/dist/theme/FooterLinks.svelte.d.ts.map +1 -0
  190. package/dist/theme/FormField.svelte +41 -0
  191. package/dist/theme/FormField.svelte.d.ts +35 -0
  192. package/dist/theme/FormField.svelte.d.ts.map +1 -0
  193. package/dist/theme/HomePage.svelte +284 -0
  194. package/dist/theme/HomePage.svelte.d.ts +65 -0
  195. package/dist/theme/HomePage.svelte.d.ts.map +1 -0
  196. package/dist/theme/Input.svelte +36 -0
  197. package/dist/theme/Input.svelte.d.ts +34 -0
  198. package/dist/theme/Input.svelte.d.ts.map +1 -0
  199. package/dist/theme/LayoutShell.svelte +120 -0
  200. package/dist/theme/LayoutShell.svelte.d.ts +43 -0
  201. package/dist/theme/LayoutShell.svelte.d.ts.map +1 -0
  202. package/dist/theme/LocalizedLink.svelte +21 -0
  203. package/dist/theme/LocalizedLink.svelte.d.ts +38 -0
  204. package/dist/theme/LocalizedLink.svelte.d.ts.map +1 -0
  205. package/dist/theme/MobileNav.svelte +20 -0
  206. package/dist/theme/MobileNav.svelte.d.ts +26 -0
  207. package/dist/theme/MobileNav.svelte.d.ts.map +1 -0
  208. package/dist/theme/Navbar.svelte +69 -0
  209. package/dist/theme/Navbar.svelte.d.ts +29 -0
  210. package/dist/theme/Navbar.svelte.d.ts.map +1 -0
  211. package/dist/theme/PageLayout.svelte +49 -0
  212. package/dist/theme/PageLayout.svelte.d.ts +34 -0
  213. package/dist/theme/PageLayout.svelte.d.ts.map +1 -0
  214. package/dist/theme/PageShell.svelte +98 -0
  215. package/dist/theme/PageShell.svelte.d.ts +35 -0
  216. package/dist/theme/PageShell.svelte.d.ts.map +1 -0
  217. package/dist/theme/PageTools.svelte +87 -0
  218. package/dist/theme/PageTools.svelte.d.ts +25 -0
  219. package/dist/theme/PageTools.svelte.d.ts.map +1 -0
  220. package/dist/theme/RenderError.svelte +62 -0
  221. package/dist/theme/RenderError.svelte.d.ts +31 -0
  222. package/dist/theme/RenderError.svelte.d.ts.map +1 -0
  223. package/dist/theme/RootLayout.svelte +274 -0
  224. package/dist/theme/RootLayout.svelte.d.ts +47 -0
  225. package/dist/theme/RootLayout.svelte.d.ts.map +1 -0
  226. package/dist/theme/RouteRenderError.svelte +53 -0
  227. package/dist/theme/RouteRenderError.svelte.d.ts +34 -0
  228. package/dist/theme/RouteRenderError.svelte.d.ts.map +1 -0
  229. package/dist/theme/SafeRenderError.svelte +49 -0
  230. package/dist/theme/SafeRenderError.svelte.d.ts +33 -0
  231. package/dist/theme/SafeRenderError.svelte.d.ts.map +1 -0
  232. package/dist/theme/ScopeSwitcher.svelte +107 -0
  233. package/dist/theme/ScopeSwitcher.svelte.d.ts +26 -0
  234. package/dist/theme/ScopeSwitcher.svelte.d.ts.map +1 -0
  235. package/dist/theme/SearchDialog.svelte +200 -0
  236. package/dist/theme/SearchDialog.svelte.d.ts +31 -0
  237. package/dist/theme/SearchDialog.svelte.d.ts.map +1 -0
  238. package/dist/theme/Select.svelte +37 -0
  239. package/dist/theme/Select.svelte.d.ts +42 -0
  240. package/dist/theme/Select.svelte.d.ts.map +1 -0
  241. package/dist/theme/SidebarTree.svelte +148 -0
  242. package/dist/theme/SidebarTree.svelte.d.ts +24 -0
  243. package/dist/theme/SidebarTree.svelte.d.ts.map +1 -0
  244. package/dist/theme/SocialNav.svelte +17 -0
  245. package/dist/theme/SocialNav.svelte.d.ts +22 -0
  246. package/dist/theme/SocialNav.svelte.d.ts.map +1 -0
  247. package/dist/theme/TableOfContents.svelte +73 -0
  248. package/dist/theme/TableOfContents.svelte.d.ts +25 -0
  249. package/dist/theme/TableOfContents.svelte.d.ts.map +1 -0
  250. package/dist/theme/Textarea.svelte +33 -0
  251. package/dist/theme/Textarea.svelte.d.ts +33 -0
  252. package/dist/theme/Textarea.svelte.d.ts.map +1 -0
  253. package/dist/theme/ThemeInit.svelte +16 -0
  254. package/dist/theme/ThemeInit.svelte.d.ts +23 -0
  255. package/dist/theme/ThemeInit.svelte.d.ts.map +1 -0
  256. package/dist/theme/ThemeToggle.svelte +37 -0
  257. package/dist/theme/ThemeToggle.svelte.d.ts +23 -0
  258. package/dist/theme/ThemeToggle.svelte.d.ts.map +1 -0
  259. package/dist/theme/TopNav.svelte +23 -0
  260. package/dist/theme/TopNav.svelte.d.ts +22 -0
  261. package/dist/theme/TopNav.svelte.d.ts.map +1 -0
  262. package/dist/theme/base.css +240 -0
  263. package/dist/theme/defaults.d.ts +4 -0
  264. package/dist/theme/defaults.d.ts.map +1 -0
  265. package/dist/theme/defaults.js +55 -0
  266. package/dist/theme/forms.d.ts +4 -0
  267. package/dist/theme/forms.d.ts.map +1 -0
  268. package/dist/theme/forms.js +3 -0
  269. package/dist/theme/headless.d.ts +62 -0
  270. package/dist/theme/headless.d.ts.map +1 -0
  271. package/dist/theme/headless.js +1064 -0
  272. package/dist/theme/index.d.ts +41 -0
  273. package/dist/theme/index.d.ts.map +1 -0
  274. package/dist/theme/index.js +38 -0
  275. package/dist/theme/portal.d.ts +6 -0
  276. package/dist/theme/portal.d.ts.map +1 -0
  277. package/dist/theme/portal.js +59 -0
  278. package/dist/theme/styles.css +4232 -0
  279. package/dist/theme/types.d.ts +363 -0
  280. package/dist/theme/types.d.ts.map +1 -0
  281. package/dist/theme/types.js +1 -0
  282. package/dist/virtual.d.ts +65 -0
  283. package/dist/vite.d.ts +16 -0
  284. package/dist/vite.d.ts.map +1 -0
  285. package/dist/vite.js +386 -0
  286. package/package.json +132 -0
@@ -0,0 +1,224 @@
1
+ export const defaultOgTemplate = {
2
+ name: 'default',
3
+ render: createDefaultOgTemplateNode
4
+ };
5
+ export function createPageOgImagePath(page, format = 'svg') {
6
+ const name = page.routePath === '/' ? 'index' : page.routePath.replace(/^\/+/, '').replace(/[^a-zA-Z0-9]+/g, '-');
7
+ return `/og/${name}.${format}`;
8
+ }
9
+ export function createConfiguredOgImageFormat(config) {
10
+ return config.seo.ogImage === false ? 'svg' : config.seo.ogImage.format;
11
+ }
12
+ export function createConfiguredOgImageRenderer(config) {
13
+ return config.seo.ogImage === false ? 'svg' : config.seo.ogImage.renderer;
14
+ }
15
+ export function createConfiguredOgImageTemplate(config) {
16
+ return config.seo.ogImage !== false && typeof config.seo.ogImage.template === 'function'
17
+ ? config.seo.ogImage.template
18
+ : undefined;
19
+ }
20
+ export function isOgImageEnabled(config) {
21
+ return config.seo.ogImage !== false;
22
+ }
23
+ export function createPageOgImageEntries(pages, format = 'svg') {
24
+ return pages
25
+ .filter((page) => !page.hidden)
26
+ .map((page) => ({ path: createPageOgImagePath(page, format).replace(/^\/og\//, '') }));
27
+ }
28
+ export function createConfiguredPageOgImageEntries(config, pages) {
29
+ if (!isOgImageEnabled(config))
30
+ return [];
31
+ return createPageOgImageEntries(pages, createConfiguredOgImageFormat(config));
32
+ }
33
+ export async function createPageOgImageResponse(config, page, options = {}) {
34
+ const format = options.format ?? createConfiguredOgImageFormat(config);
35
+ const template = options.template ?? createConfiguredOgImageTemplate(config);
36
+ const asset = await createOgImage(createOgImageInput(config, page), {
37
+ ...options,
38
+ format,
39
+ renderer: options.renderer ?? createConfiguredOgImageRenderer(config),
40
+ ...(template ? { template } : {})
41
+ });
42
+ const body = typeof asset === 'string' ? asset : new Blob([toArrayBuffer(asset)], { type: 'image/png' });
43
+ return new Response(body, {
44
+ headers: {
45
+ 'content-type': format === 'png' ? 'image/png' : 'image/svg+xml; charset=utf-8',
46
+ 'cache-control': 'public, max-age=31536000, immutable'
47
+ }
48
+ });
49
+ }
50
+ export function createOgImageInput(config, page) {
51
+ return {
52
+ title: page.seo.title,
53
+ description: page.seo.description ?? config.site.description,
54
+ siteName: config.site.name
55
+ };
56
+ }
57
+ export function createOgSvg(input) {
58
+ const title = escapeHtml(input.title);
59
+ const description = escapeHtml(input.description ?? input.siteName ?? 'svedocs');
60
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
61
+ <rect width="1200" height="630" fill="#11130f"/>
62
+ <path d="M0 84h1200M0 168h1200M0 252h1200M0 336h1200M0 420h1200M0 504h1200M120 0v630M240 0v630M360 0v630M480 0v630M600 0v630M720 0v630M840 0v630M960 0v630M1080 0v630" stroke="#2f332d" stroke-width="2"/>
63
+ <rect x="84" y="84" width="96" height="96" fill="#50d6b3"/>
64
+ <rect x="180" y="180" width="96" height="96" fill="#ff8a66"/>
65
+ <rect x="84" y="276" width="96" height="96" fill="#f4f1e8"/>
66
+ <text x="300" y="270" fill="#f4f1e8" font-family="Arial, sans-serif" font-size="78" font-weight="800">${title}</text>
67
+ <text x="306" y="352" fill="#aaa698" font-family="Arial, sans-serif" font-size="34">${description}</text>
68
+ </svg>`;
69
+ }
70
+ export function createDefaultOgTemplateNode(input) {
71
+ return {
72
+ type: 'div',
73
+ props: {
74
+ style: {
75
+ display: 'flex',
76
+ width: '1200px',
77
+ height: '630px',
78
+ background: '#11130f',
79
+ color: '#f4f1e8',
80
+ position: 'relative',
81
+ fontFamily: 'Inter',
82
+ overflow: 'hidden'
83
+ },
84
+ children: [
85
+ gridLayer(),
86
+ pixelMark(),
87
+ {
88
+ type: 'div',
89
+ props: {
90
+ style: {
91
+ display: 'flex',
92
+ position: 'absolute',
93
+ left: '300px',
94
+ top: '200px',
95
+ width: '780px',
96
+ flexDirection: 'column'
97
+ },
98
+ children: [
99
+ {
100
+ type: 'div',
101
+ props: {
102
+ style: {
103
+ fontSize: 78,
104
+ fontWeight: 800,
105
+ lineHeight: 1,
106
+ color: '#f4f1e8'
107
+ },
108
+ children: input.title
109
+ }
110
+ },
111
+ {
112
+ type: 'div',
113
+ props: {
114
+ style: {
115
+ marginTop: 28,
116
+ fontSize: 34,
117
+ lineHeight: 1.25,
118
+ color: '#aaa698'
119
+ },
120
+ children: input.description ?? input.siteName ?? 'svedocs'
121
+ }
122
+ }
123
+ ]
124
+ }
125
+ }
126
+ ]
127
+ }
128
+ };
129
+ }
130
+ export async function createSatoriOgSvg(input, options) {
131
+ if (!options.fonts?.length) {
132
+ throw new Error('Satori OG rendering requires at least one font.');
133
+ }
134
+ const { default: satori } = await importOptionalOgModule('satori');
135
+ return satori((options.template ?? createDefaultOgTemplateNode)(input), {
136
+ width: 1200,
137
+ height: 630,
138
+ fonts: options.fonts
139
+ });
140
+ }
141
+ export async function createOgImage(input, options = {}) {
142
+ return options.format === 'png' ? createOgPng(input, options) : renderOgSvg(input, options);
143
+ }
144
+ export async function createOgPng(input, options = {}) {
145
+ const { Resvg } = await importOptionalOgModule('@resvg/resvg-js');
146
+ const renderer = new Resvg(await renderOgSvg(input, options), {
147
+ fitTo: {
148
+ mode: 'width',
149
+ value: 1200
150
+ }
151
+ });
152
+ return renderer.render().asPng();
153
+ }
154
+ function gridLayer() {
155
+ return {
156
+ type: 'div',
157
+ props: {
158
+ style: {
159
+ display: 'flex',
160
+ position: 'absolute',
161
+ inset: 0,
162
+ backgroundImage: 'linear-gradient(#2f332d 2px, transparent 2px), linear-gradient(90deg, #2f332d 2px, transparent 2px)',
163
+ backgroundSize: '120px 84px'
164
+ }
165
+ }
166
+ };
167
+ }
168
+ function pixelMark() {
169
+ return {
170
+ type: 'div',
171
+ props: {
172
+ style: {
173
+ display: 'flex',
174
+ position: 'absolute',
175
+ left: '84px',
176
+ top: '84px',
177
+ width: '192px',
178
+ height: '288px',
179
+ flexWrap: 'wrap'
180
+ },
181
+ children: [
182
+ colorBlock('#50d6b3'),
183
+ colorBlock('transparent'),
184
+ colorBlock('transparent'),
185
+ colorBlock('#ff8a66'),
186
+ colorBlock('#f4f1e8'),
187
+ colorBlock('transparent')
188
+ ]
189
+ }
190
+ };
191
+ }
192
+ function colorBlock(color) {
193
+ return {
194
+ type: 'div',
195
+ props: {
196
+ style: {
197
+ width: '96px',
198
+ height: '96px',
199
+ background: color
200
+ }
201
+ }
202
+ };
203
+ }
204
+ async function renderOgSvg(input, options) {
205
+ return options.renderer === 'satori' ? createSatoriOgSvg(input, options) : createOgSvg(input);
206
+ }
207
+ function escapeHtml(value) {
208
+ return value
209
+ .replace(/&/g, '&amp;')
210
+ .replace(/</g, '&lt;')
211
+ .replace(/>/g, '&gt;')
212
+ .replace(/"/g, '&quot;');
213
+ }
214
+ function toArrayBuffer(value) {
215
+ return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
216
+ }
217
+ function importOptionalOgModule(specifier) {
218
+ if (typeof process !== 'undefined' && process.versions?.node) {
219
+ return import(specifier);
220
+ }
221
+ // Keep optional Node-only OG renderers out of edge bundles unless the caller actually uses them.
222
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
223
+ return dynamicImport(specifier);
224
+ }
@@ -0,0 +1,7 @@
1
+ import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
2
+ import type { SvedocsPageAlternate, SvedocsPageMetadata } from './types.js';
3
+ export declare function createPageMetadata(config: SvedocsResolvedConfig, page: SvedocsPage, pages?: SvedocsPage[]): SvedocsPageMetadata;
4
+ export declare function createPageAlternates(config: SvedocsResolvedConfig, page: SvedocsPage, pages: SvedocsPage[]): SvedocsPageAlternate[];
5
+ export declare function serializeJsonLd(value: unknown): string;
6
+ export declare function createJsonLdScript(value: unknown): string;
7
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/og/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAA0C,MAAM,YAAY,CAAC;AAG7G,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,WAAW,EACjB,KAAK,GAAE,WAAW,EAAO,GACxB,mBAAmB,CAuDrB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAAE,GACnB,oBAAoB,EAAE,CAuCxB;AA4FD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOtD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,198 @@
1
+ import { formatRoutePathForBuildMode } from '../core/utils.js';
2
+ import { createConfiguredOgImageFormat, createPageOgImagePath } from './image.js';
3
+ export function createPageMetadata(config, page, pages = []) {
4
+ const title = page.routePath === '/' ? page.seo.title : `${page.seo.title} | ${config.site.name}`;
5
+ const description = page.seo.description ?? config.site.description;
6
+ const canonical = page.seo.canonical
7
+ ? createAbsoluteUrl(config, page.seo.canonical) ?? page.seo.canonical
8
+ : createAbsoluteRouteUrl(config, page.routePath);
9
+ const keywords = page.seo.keywords ?? [];
10
+ const robots = page.seo.robots;
11
+ const head = withRssAlternate(config, mergeSeoHead(config.seo.head, page.seo.head));
12
+ const generatedImage = config.seo.ogImage === false
13
+ ? undefined
14
+ : createAbsoluteUrl(config, createPageOgImagePath(page, createConfiguredOgImageFormat(config)));
15
+ const image = page.seo.image ? createAbsoluteUrl(config, page.seo.image) : generatedImage;
16
+ const type = page.seo.type ?? (page.kind === 'doc' ? 'article' : 'website');
17
+ const author = page.seo.author ?? config.seo.defaultAuthor;
18
+ const updatedTime = page.seo.updatedTime ?? page.lastUpdated;
19
+ const pageLanguage = getPageLanguage(config, page);
20
+ const ogLocale = toOpenGraphLocale(pageLanguage);
21
+ const alternateOgLocales = createPageAlternates(config, page, pages)
22
+ .filter((alternate) => alternate.lang !== 'x-default' && alternate.locale !== page.locale)
23
+ .map((alternate) => toOpenGraphLocale(alternate.lang));
24
+ return {
25
+ title,
26
+ description,
27
+ keywords,
28
+ head,
29
+ ...(canonical ? { canonical } : {}),
30
+ ...(image ? { image } : {}),
31
+ ...(robots ? { robots } : {}),
32
+ openGraph: {
33
+ title,
34
+ description,
35
+ type,
36
+ ...(canonical ? { url: canonical } : {}),
37
+ ...(image ? { image } : {}),
38
+ siteName: config.site.name,
39
+ locale: ogLocale,
40
+ ...(alternateOgLocales.length > 0 ? { alternateLocales: [...new Set(alternateOgLocales)] } : {}),
41
+ ...(author ? { author } : {}),
42
+ ...(page.seo.publishedTime ? { publishedTime: page.seo.publishedTime } : {}),
43
+ ...(updatedTime ? { updatedTime } : {})
44
+ },
45
+ twitter: {
46
+ card: image ? 'summary_large_image' : 'summary',
47
+ title,
48
+ description,
49
+ ...(image ? { image } : {})
50
+ },
51
+ jsonLd: createPageJsonLd(config, page, {
52
+ title,
53
+ description,
54
+ ...(canonical ? { canonical } : {}),
55
+ ...(image ? { image } : {})
56
+ })
57
+ };
58
+ }
59
+ export function createPageAlternates(config, page, pages) {
60
+ if (config.i18n.locales.length === 0)
61
+ return [];
62
+ const candidates = pages
63
+ .filter((candidate) => !candidate.hidden)
64
+ .filter((candidate) => candidate.kind === page.kind)
65
+ .filter((candidate) => candidate.scopePath === page.scopePath);
66
+ const alternates = [];
67
+ for (const candidate of candidates) {
68
+ if (!candidate.locale)
69
+ continue;
70
+ const href = candidate.seo.canonical
71
+ ? createAbsoluteUrl(config, candidate.seo.canonical) ?? candidate.seo.canonical
72
+ : createAbsoluteRouteUrl(config, candidate.routePath);
73
+ if (!href)
74
+ continue;
75
+ const locale = config.i18n.locales.find((item) => item.code === candidate.locale);
76
+ alternates.push({
77
+ lang: locale?.hreflang ?? candidate.locale,
78
+ href,
79
+ locale: candidate.locale
80
+ });
81
+ }
82
+ const defaultLocale = config.i18n.defaultLocale;
83
+ const defaultPage = defaultLocale
84
+ ? candidates.find((candidate) => candidate.locale === defaultLocale)
85
+ : undefined;
86
+ const defaultHref = defaultPage?.seo.canonical
87
+ ? createAbsoluteUrl(config, defaultPage.seo.canonical) ?? defaultPage.seo.canonical
88
+ : defaultPage
89
+ ? createAbsoluteRouteUrl(config, defaultPage.routePath)
90
+ : undefined;
91
+ const defaultAlternate = defaultHref
92
+ ? [
93
+ {
94
+ lang: 'x-default',
95
+ href: defaultHref,
96
+ ...(defaultLocale ? { locale: defaultLocale } : {})
97
+ }
98
+ ]
99
+ : [];
100
+ return uniqueAlternates([...alternates, ...defaultAlternate]);
101
+ }
102
+ function mergeSeoHead(globalHead, pageHead) {
103
+ return {
104
+ meta: [...globalHead.meta, ...(pageHead?.meta ?? [])],
105
+ links: [...globalHead.links, ...(pageHead?.links ?? [])],
106
+ jsonLd: [...globalHead.jsonLd, ...(pageHead?.jsonLd ?? pageHead?.jsonld ?? pageHead?.['json-ld'] ?? [])]
107
+ };
108
+ }
109
+ function withRssAlternate(config, head) {
110
+ if (!config.seo.rss || head.links.some((link) => link.type === 'application/rss+xml'))
111
+ return head;
112
+ return {
113
+ ...head,
114
+ links: [
115
+ ...head.links,
116
+ {
117
+ rel: 'alternate',
118
+ type: 'application/rss+xml',
119
+ href: createAbsoluteUrl(config, '/feed.xml') ?? '/feed.xml',
120
+ title: config.seo.rss.title
121
+ }
122
+ ]
123
+ };
124
+ }
125
+ function uniqueAlternates(alternates) {
126
+ const seen = new Set();
127
+ return alternates.filter((alternate) => {
128
+ const key = `${alternate.lang}:${alternate.href}`;
129
+ if (seen.has(key))
130
+ return false;
131
+ seen.add(key);
132
+ return true;
133
+ });
134
+ }
135
+ function createPageJsonLd(config, page, metadata) {
136
+ const graph = {
137
+ '@context': 'https://schema.org',
138
+ '@type': page.kind === 'doc' ? 'TechArticle' : 'WebPage',
139
+ headline: metadata.title,
140
+ description: metadata.description,
141
+ inLanguage: getPageLanguage(config, page),
142
+ isPartOf: {
143
+ '@type': 'WebSite',
144
+ name: config.site.name,
145
+ ...(config.site.url ? { url: config.site.url } : {})
146
+ }
147
+ };
148
+ if (metadata.canonical)
149
+ graph.url = metadata.canonical;
150
+ if (metadata.image)
151
+ graph.image = metadata.image;
152
+ const author = page.seo.author ?? config.seo.defaultAuthor;
153
+ const updatedTime = page.seo.updatedTime ?? page.lastUpdated;
154
+ if (author) {
155
+ graph.author = {
156
+ '@type': 'Person',
157
+ name: author
158
+ };
159
+ }
160
+ if (page.seo.publishedTime)
161
+ graph.datePublished = page.seo.publishedTime;
162
+ if (updatedTime)
163
+ graph.dateModified = updatedTime;
164
+ if (page.kind === 'doc') {
165
+ graph.position = page.order;
166
+ graph.about = page.headings.map((heading) => heading.text);
167
+ }
168
+ return graph;
169
+ }
170
+ function getPageLanguage(config, page) {
171
+ const code = page.locale ?? config.i18n.defaultLocale ?? 'en';
172
+ const locale = config.i18n.locales.find((candidate) => candidate.code === code);
173
+ return locale?.hreflang ?? code;
174
+ }
175
+ function toOpenGraphLocale(language) {
176
+ return language.replaceAll('-', '_');
177
+ }
178
+ function createAbsoluteUrl(config, value) {
179
+ if (/^https?:\/\//.test(value))
180
+ return value;
181
+ if (!config.site.url)
182
+ return undefined;
183
+ return new URL(value, config.site.url).href;
184
+ }
185
+ function createAbsoluteRouteUrl(config, routePath) {
186
+ return createAbsoluteUrl(config, formatRoutePathForBuildMode(routePath, config.build.mode));
187
+ }
188
+ export function serializeJsonLd(value) {
189
+ return (JSON.stringify(value) ?? 'null')
190
+ .replace(/</g, '\\u003c')
191
+ .replace(/>/g, '\\u003e')
192
+ .replace(/&/g, '\\u0026')
193
+ .replace(/\u2028/g, '\\u2028')
194
+ .replace(/\u2029/g, '\\u2029');
195
+ }
196
+ export function createJsonLdScript(value) {
197
+ return `<script type="application/ld+json">${serializeJsonLd(value)}<${'/script'}>`;
198
+ }
@@ -0,0 +1,4 @@
1
+ export declare function createDiscoveryResponse(body: string, contentType: string, request?: Request): Response;
2
+ export declare function createDisabledDiscoveryResponse(label: string): Response;
3
+ export declare function escapeXml(value: string): string;
4
+ //# sourceMappingURL=response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/og/response.ts"],"names":[],"mappings":"AAEA,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,CAWV;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAQvE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C"}
@@ -0,0 +1,49 @@
1
+ const discoveryCacheControl = 'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400';
2
+ export function createDiscoveryResponse(body, contentType, request) {
3
+ const etag = createWeakEtag(body);
4
+ const headers = {
5
+ 'cache-control': discoveryCacheControl,
6
+ 'content-type': contentType,
7
+ etag
8
+ };
9
+ if (request && matchesEtag(request.headers.get('if-none-match'), etag)) {
10
+ return new Response(null, { status: 304, headers });
11
+ }
12
+ return new Response(body, { headers });
13
+ }
14
+ export function createDisabledDiscoveryResponse(label) {
15
+ return new Response(`${label} is disabled.`, {
16
+ status: 404,
17
+ headers: {
18
+ 'cache-control': 'no-store',
19
+ 'content-type': 'text/plain; charset=utf-8'
20
+ }
21
+ });
22
+ }
23
+ export function escapeXml(value) {
24
+ return value
25
+ .replace(/&/g, '&amp;')
26
+ .replace(/</g, '&lt;')
27
+ .replace(/>/g, '&gt;')
28
+ .replace(/"/g, '&quot;')
29
+ .replace(/'/g, '&apos;');
30
+ }
31
+ function createWeakEtag(value) {
32
+ let hash = 0x811c9dc5;
33
+ for (let index = 0; index < value.length; index += 1) {
34
+ hash = Math.imul(hash ^ value.charCodeAt(index), 0x01000193);
35
+ }
36
+ return `W/"${value.length.toString(16)}-${(hash >>> 0).toString(16)}"`;
37
+ }
38
+ function matchesEtag(header, etag) {
39
+ if (!header)
40
+ return false;
41
+ const expected = stripWeakPrefix(etag);
42
+ return header.split(',').some((candidate) => {
43
+ const value = candidate.trim();
44
+ return value === '*' || stripWeakPrefix(value) === expected;
45
+ });
46
+ }
47
+ function stripWeakPrefix(etag) {
48
+ return etag.startsWith('W/') ? etag.slice(2) : etag;
49
+ }
@@ -0,0 +1,4 @@
1
+ import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
2
+ export declare function createRssXml(config: SvedocsResolvedConfig, pages: SvedocsPage[]): string;
3
+ export declare function createRssResponse(config: SvedocsResolvedConfig, pages: SvedocsPage[], request?: Request): Response;
4
+ //# sourceMappingURL=rss.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../src/og/rss.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAWrE,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CA+CxF;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,CAGV"}
package/dist/og/rss.js ADDED
@@ -0,0 +1,96 @@
1
+ import { formatRoutePathForBuildMode } from '../core/utils.js';
2
+ import { createDisabledDiscoveryResponse, createDiscoveryResponse, escapeXml } from './response.js';
3
+ import { isDiscoverablePage } from './sitemap.js';
4
+ export function createRssXml(config, pages) {
5
+ const rss = config.seo.rss;
6
+ if (!rss)
7
+ return '';
8
+ const locale = rss.locale ?? config.i18n.defaultLocale;
9
+ const entries = pages
10
+ .filter(isDiscoverablePage)
11
+ .filter((page) => isPageInLocale(config, page, locale))
12
+ .map((page) => {
13
+ const link = createPageUrl(config, page);
14
+ if (!link)
15
+ return undefined;
16
+ const date = parseDate(page.seo.updatedTime ?? page.seo.publishedTime ?? page.lastUpdated);
17
+ return { page, link, ...(date ? { date } : {}) };
18
+ })
19
+ .filter((entry) => Boolean(entry))
20
+ .sort((left, right) => (right.date?.getTime() ?? 0) - (left.date?.getTime() ?? 0))
21
+ .slice(0, rss.limit);
22
+ const channelUrl = config.site.url ?? '/';
23
+ const feedUrl = createAbsoluteUrl(config, '/feed.xml') ?? '/feed.xml';
24
+ const language = resolveFeedLanguage(config, locale);
25
+ const lastBuildDate = entries.find((entry) => entry.date)?.date;
26
+ const items = entries.map(({ page, date, link }) => [
27
+ ' <item>',
28
+ ` <title>${escapeXml(page.title)}</title>`,
29
+ ` <link>${escapeXml(link)}</link>`,
30
+ ` <guid isPermaLink="true">${escapeXml(link)}</guid>`,
31
+ ...(page.seo.description ?? page.description
32
+ ? [` <description>${escapeXml(page.seo.description ?? page.description ?? '')}</description>`]
33
+ : []),
34
+ ...(date ? [` <pubDate>${date.toUTCString()}</pubDate>`] : []),
35
+ ...(page.seo.keywords ?? []).map((keyword) => ` <category>${escapeXml(keyword)}</category>`),
36
+ ' </item>'
37
+ ].join('\n')).join('\n');
38
+ return [
39
+ '<?xml version="1.0" encoding="UTF-8"?>',
40
+ '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">',
41
+ ' <channel>',
42
+ ` <title>${escapeXml(rss.title)}</title>`,
43
+ ` <link>${escapeXml(channelUrl)}</link>`,
44
+ ` <description>${escapeXml(rss.description)}</description>`,
45
+ ` <language>${escapeXml(language)}</language>`,
46
+ ` <atom:link href="${escapeXml(feedUrl)}" rel="self" type="application/rss+xml" />`,
47
+ ...(lastBuildDate ? [` <lastBuildDate>${lastBuildDate.toUTCString()}</lastBuildDate>`] : []),
48
+ items,
49
+ ' </channel>',
50
+ '</rss>',
51
+ ''
52
+ ].join('\n');
53
+ }
54
+ export function createRssResponse(config, pages, request) {
55
+ if (!config.seo.rss)
56
+ return createDisabledDiscoveryResponse('RSS');
57
+ return createDiscoveryResponse(createRssXml(config, pages), 'application/rss+xml; charset=utf-8', request);
58
+ }
59
+ function createPageUrl(config, page) {
60
+ const routePath = formatRoutePathForBuildMode(page.routePath, config.build.mode);
61
+ const link = page.seo.canonical
62
+ ? createAbsoluteUrl(config, page.seo.canonical) ?? page.seo.canonical
63
+ : createAbsoluteUrl(config, routePath) ?? routePath;
64
+ if (!config.site.url || !/^https?:\/\//.test(link))
65
+ return link;
66
+ try {
67
+ return new URL(link).origin === new URL(config.site.url).origin ? link : undefined;
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ }
73
+ function isPageInLocale(config, page, locale) {
74
+ if (!locale)
75
+ return true;
76
+ const pageLocale = page.locale ?? config.i18n.defaultLocale;
77
+ return pageLocale ? pageLocale === locale : config.i18n.locales.length === 0;
78
+ }
79
+ function createAbsoluteUrl(config, value) {
80
+ if (/^https?:\/\//.test(value))
81
+ return value;
82
+ if (!config.site.url)
83
+ return undefined;
84
+ return new URL(value, config.site.url).href;
85
+ }
86
+ function resolveFeedLanguage(config, locale) {
87
+ if (!locale)
88
+ return 'en';
89
+ return config.i18n.locales.find((candidate) => candidate.code === locale)?.hreflang ?? locale;
90
+ }
91
+ function parseDate(value) {
92
+ if (!value)
93
+ return undefined;
94
+ const date = new Date(value);
95
+ return Number.isNaN(date.getTime()) ? undefined : date;
96
+ }
@@ -0,0 +1,7 @@
1
+ import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
2
+ export declare function createSitemapXml(config: SvedocsResolvedConfig, pages: SvedocsPage[]): string;
3
+ export declare function createRobotsTxt(config: SvedocsResolvedConfig): string;
4
+ export declare function createSitemapResponse(config: SvedocsResolvedConfig, pages: SvedocsPage[], request?: Request): Response;
5
+ export declare function createRobotsResponse(config: SvedocsResolvedConfig, request?: Request): Response;
6
+ export declare function isDiscoverablePage(page: SvedocsPage): boolean;
7
+ //# sourceMappingURL=sitemap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/og/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAMrE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAqB5F;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAQrE;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,CAGV;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,qBAAqB,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAG/F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE7D"}