@jant/core 0.3.36 → 0.3.37

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 (271) hide show
  1. package/bin/commands/export.js +1 -1
  2. package/bin/commands/import-site.js +529 -0
  3. package/bin/commands/reset-password.js +3 -2
  4. package/dist/client/assets/heic-to-DIRPI3VF.js +1 -0
  5. package/dist/client/assets/url-FWFqPJPb.js +1 -0
  6. package/dist/client/client.css +1 -1
  7. package/dist/client/client.js +4012 -3276
  8. package/dist/index.js +10285 -5809
  9. package/package.json +11 -3
  10. package/src/__tests__/helpers/app.ts +9 -9
  11. package/src/__tests__/helpers/db.ts +91 -93
  12. package/src/app.tsx +157 -27
  13. package/src/auth.ts +20 -2
  14. package/src/client/archive-nav.js +187 -0
  15. package/src/client/audio-player.ts +478 -0
  16. package/src/client/audio-processor.ts +84 -0
  17. package/src/client/avatar-upload.ts +3 -2
  18. package/src/client/components/__tests__/jant-compose-dialog.test.ts +645 -49
  19. package/src/client/components/__tests__/jant-compose-editor.test.ts +208 -16
  20. package/src/client/components/__tests__/jant-post-form.test.ts +19 -9
  21. package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
  22. package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
  23. package/src/client/components/collection-sidebar-types.ts +7 -9
  24. package/src/client/components/compose-types.ts +101 -4
  25. package/src/client/components/jant-collection-form.ts +43 -7
  26. package/src/client/components/jant-collection-sidebar.ts +88 -84
  27. package/src/client/components/jant-compose-dialog.ts +1655 -219
  28. package/src/client/components/jant-compose-editor.ts +732 -168
  29. package/src/client/components/jant-compose-fullscreen.ts +23 -78
  30. package/src/client/components/jant-media-lightbox.ts +2 -0
  31. package/src/client/components/jant-nav-manager.ts +24 -284
  32. package/src/client/components/jant-post-form.ts +89 -9
  33. package/src/client/components/jant-post-menu.ts +1019 -0
  34. package/src/client/components/jant-settings-avatar.ts +3 -3
  35. package/src/client/components/jant-settings-general.ts +38 -4
  36. package/src/client/components/jant-text-preview.ts +232 -0
  37. package/src/client/components/nav-manager-types.ts +4 -19
  38. package/src/client/components/post-form-template.ts +107 -12
  39. package/src/client/components/post-form-types.ts +11 -4
  40. package/src/client/compose-bridge.ts +410 -109
  41. package/src/client/image-processor.ts +26 -8
  42. package/src/client/media-metadata.ts +247 -0
  43. package/src/client/multipart-upload.ts +160 -0
  44. package/src/client/post-form-bridge.ts +52 -1
  45. package/src/client/settings-bridge.ts +0 -12
  46. package/src/client/thread-context.ts +140 -0
  47. package/src/client/tiptap/create-editor.ts +46 -0
  48. package/src/client/tiptap/extensions.ts +5 -0
  49. package/src/client/tiptap/image-node.ts +2 -8
  50. package/src/client/tiptap/paste-image.ts +2 -13
  51. package/src/client/tiptap/slash-commands.ts +173 -63
  52. package/src/client/toast.ts +101 -3
  53. package/src/client/types/sortablejs.d.ts +15 -0
  54. package/src/client/upload-with-metadata.ts +54 -0
  55. package/src/client/video-processor.ts +207 -0
  56. package/src/client.ts +5 -2
  57. package/src/db/__tests__/migrations.test.ts +118 -0
  58. package/src/db/index.ts +52 -0
  59. package/src/db/migrations/0000_baseline.sql +269 -0
  60. package/src/db/migrations/0001_fts_setup.sql +31 -0
  61. package/src/db/migrations/meta/0000_snapshot.json +703 -119
  62. package/src/db/migrations/meta/0001_snapshot.json +1337 -0
  63. package/src/db/migrations/meta/_journal.json +4 -39
  64. package/src/db/schema.ts +409 -145
  65. package/src/i18n/__tests__/detect.test.ts +115 -0
  66. package/src/i18n/context.tsx +2 -2
  67. package/src/i18n/detect.ts +85 -1
  68. package/src/i18n/i18n.ts +1 -1
  69. package/src/i18n/index.ts +2 -1
  70. package/src/i18n/locales/en.po +487 -1217
  71. package/src/i18n/locales/en.ts +1 -1
  72. package/src/i18n/locales/zh-Hans.po +613 -996
  73. package/src/i18n/locales/zh-Hans.ts +1 -1
  74. package/src/i18n/locales/zh-Hant.po +624 -1007
  75. package/src/i18n/locales/zh-Hant.ts +1 -1
  76. package/src/i18n/middleware.ts +6 -0
  77. package/src/index.ts +5 -7
  78. package/src/lib/__tests__/blurhash-placeholder.test.ts +75 -0
  79. package/src/lib/__tests__/constants.test.ts +0 -1
  80. package/src/lib/__tests__/markdown-to-tiptap.test.ts +358 -0
  81. package/src/lib/__tests__/nanoid.test.ts +26 -0
  82. package/src/lib/__tests__/schemas.test.ts +181 -63
  83. package/src/lib/__tests__/slug.test.ts +126 -0
  84. package/src/lib/__tests__/sse.test.ts +6 -6
  85. package/src/lib/__tests__/summary.test.ts +264 -0
  86. package/src/lib/__tests__/theme.test.ts +1 -1
  87. package/src/lib/__tests__/timeline.test.ts +33 -30
  88. package/src/lib/__tests__/tiptap-to-markdown.test.ts +346 -0
  89. package/src/lib/__tests__/view.test.ts +141 -66
  90. package/src/lib/blurhash-placeholder.ts +102 -0
  91. package/src/lib/constants.ts +3 -1
  92. package/src/lib/emoji-catalog.ts +885 -68
  93. package/src/lib/errors.ts +11 -8
  94. package/src/lib/feed.ts +78 -32
  95. package/src/lib/html.ts +2 -1
  96. package/src/lib/icon-catalog.ts +5033 -1
  97. package/src/lib/icons.ts +3 -2
  98. package/src/lib/index.ts +0 -1
  99. package/src/lib/markdown-to-tiptap.ts +286 -0
  100. package/src/lib/media-helpers.ts +12 -3
  101. package/src/lib/nanoid.ts +29 -0
  102. package/src/lib/navigation.ts +1 -1
  103. package/src/lib/render.tsx +20 -2
  104. package/src/lib/resolve-config.ts +6 -2
  105. package/src/lib/schemas.ts +224 -55
  106. package/src/lib/search-snippet.ts +34 -0
  107. package/src/lib/slug.ts +96 -0
  108. package/src/lib/sse.ts +6 -6
  109. package/src/lib/storage.ts +115 -7
  110. package/src/lib/summary.ts +66 -0
  111. package/src/lib/theme.ts +11 -8
  112. package/src/lib/timeline.ts +74 -34
  113. package/src/lib/tiptap-render.ts +5 -10
  114. package/src/lib/tiptap-to-markdown.ts +305 -0
  115. package/src/lib/upload.ts +190 -29
  116. package/src/lib/url.ts +31 -0
  117. package/src/lib/view.ts +204 -37
  118. package/src/middleware/__tests__/auth.test.ts +191 -11
  119. package/src/middleware/__tests__/onboarding.test.ts +12 -10
  120. package/src/middleware/auth.ts +63 -9
  121. package/src/middleware/onboarding.ts +1 -1
  122. package/src/middleware/secure-headers.ts +40 -0
  123. package/src/preset.css +45 -2
  124. package/src/routes/__tests__/compose.test.ts +17 -24
  125. package/src/routes/api/__tests__/collections.test.ts +109 -61
  126. package/src/routes/api/__tests__/nav-items.test.ts +46 -29
  127. package/src/routes/api/__tests__/posts.test.ts +132 -68
  128. package/src/routes/api/__tests__/search.test.ts +15 -2
  129. package/src/routes/api/__tests__/upload-multipart.test.ts +534 -0
  130. package/src/routes/api/collections.ts +51 -42
  131. package/src/routes/api/custom-urls.ts +80 -0
  132. package/src/routes/api/export.ts +31 -0
  133. package/src/routes/api/nav-items.ts +23 -19
  134. package/src/routes/api/posts.ts +43 -39
  135. package/src/routes/api/search.ts +3 -4
  136. package/src/routes/api/upload-multipart.ts +245 -0
  137. package/src/routes/api/upload.ts +85 -19
  138. package/src/routes/auth/__tests__/setup.test.ts +20 -60
  139. package/src/routes/auth/setup.tsx +26 -33
  140. package/src/routes/auth/signin.tsx +3 -7
  141. package/src/routes/compose.tsx +10 -55
  142. package/src/routes/dash/__tests__/settings-avatar.test.ts +1 -1
  143. package/src/routes/dash/custom-urls.tsx +414 -0
  144. package/src/routes/dash/settings.tsx +304 -232
  145. package/src/routes/feed/__tests__/rss.test.ts +27 -28
  146. package/src/routes/feed/rss.ts +6 -4
  147. package/src/routes/feed/sitemap.ts +2 -12
  148. package/src/routes/pages/__tests__/collections.test.ts +5 -6
  149. package/src/routes/pages/__tests__/featured.test.ts +41 -22
  150. package/src/routes/pages/archive.tsx +175 -39
  151. package/src/routes/pages/collection.tsx +22 -10
  152. package/src/routes/pages/collections.tsx +3 -3
  153. package/src/routes/pages/featured.tsx +28 -4
  154. package/src/routes/pages/home.tsx +16 -15
  155. package/src/routes/pages/latest.tsx +1 -11
  156. package/src/routes/pages/new.tsx +39 -0
  157. package/src/routes/pages/page.tsx +95 -49
  158. package/src/routes/pages/search.tsx +1 -1
  159. package/src/services/__tests__/api-token.test.ts +135 -0
  160. package/src/services/__tests__/collection.test.ts +275 -227
  161. package/src/services/__tests__/custom-url.test.ts +213 -0
  162. package/src/services/__tests__/media.test.ts +162 -22
  163. package/src/services/__tests__/navigation.test.ts +109 -68
  164. package/src/services/__tests__/post-timeline.test.ts +205 -32
  165. package/src/services/__tests__/post.test.ts +713 -234
  166. package/src/services/__tests__/search.test.ts +67 -10
  167. package/src/services/api-token.ts +166 -0
  168. package/src/services/auth.ts +17 -2
  169. package/src/services/collection.ts +397 -131
  170. package/src/services/custom-url.ts +188 -0
  171. package/src/services/export.ts +802 -0
  172. package/src/services/index.ts +26 -19
  173. package/src/services/media.ts +100 -22
  174. package/src/services/navigation.ts +158 -47
  175. package/src/services/path.ts +339 -0
  176. package/src/services/post.ts +687 -154
  177. package/src/services/search.ts +160 -75
  178. package/src/styles/components.css +58 -7
  179. package/src/styles/tokens.css +84 -6
  180. package/src/styles/ui.css +2964 -457
  181. package/src/types/bindings.ts +4 -1
  182. package/src/types/config.ts +12 -4
  183. package/src/types/constants.ts +15 -3
  184. package/src/types/entities.ts +74 -35
  185. package/src/types/operations.ts +11 -24
  186. package/src/types/props.ts +51 -16
  187. package/src/types/views.ts +45 -22
  188. package/src/ui/color-themes.ts +133 -23
  189. package/src/ui/compose/ComposeDialog.tsx +239 -17
  190. package/src/ui/compose/ComposePrompt.tsx +1 -1
  191. package/src/ui/dash/CrudPageHeader.tsx +1 -1
  192. package/src/ui/dash/ListItemRow.tsx +1 -1
  193. package/src/ui/dash/StatusBadge.tsx +3 -1
  194. package/src/ui/dash/appearance/AdvancedContent.tsx +22 -1
  195. package/src/ui/dash/appearance/ColorThemeContent.tsx +22 -2
  196. package/src/ui/dash/appearance/FontThemeContent.tsx +1 -1
  197. package/src/ui/dash/appearance/NavigationContent.tsx +5 -45
  198. package/src/ui/dash/index.ts +0 -3
  199. package/src/ui/dash/settings/AccountContent.tsx +3 -57
  200. package/src/ui/dash/settings/AccountMenuContent.tsx +147 -0
  201. package/src/ui/dash/settings/ApiTokensContent.tsx +232 -0
  202. package/src/ui/dash/settings/AvatarContent.tsx +8 -0
  203. package/src/ui/dash/settings/SessionsContent.tsx +159 -0
  204. package/src/ui/dash/settings/SettingsRootContent.tsx +45 -15
  205. package/src/ui/feed/LinkCard.tsx +89 -40
  206. package/src/ui/feed/NoteCard.tsx +39 -25
  207. package/src/ui/feed/PostStatusBadges.tsx +67 -0
  208. package/src/ui/feed/QuoteCard.tsx +38 -23
  209. package/src/ui/feed/ThreadPreview.tsx +90 -26
  210. package/src/ui/feed/TimelineFeed.tsx +3 -2
  211. package/src/ui/feed/TimelineItem.tsx +15 -6
  212. package/src/ui/feed/__tests__/thread-preview.test.ts +107 -0
  213. package/src/ui/feed/thread-preview-state.ts +61 -0
  214. package/src/ui/font-themes.ts +2 -2
  215. package/src/ui/layouts/BaseLayout.tsx +2 -2
  216. package/src/ui/layouts/SiteLayout.tsx +105 -92
  217. package/src/ui/pages/ArchivePage.tsx +923 -98
  218. package/src/ui/pages/ComposePage.tsx +54 -0
  219. package/src/ui/pages/PostPage.tsx +30 -45
  220. package/src/ui/pages/SearchPage.tsx +181 -37
  221. package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
  222. package/src/ui/shared/CollectionsSidebar.tsx +47 -37
  223. package/src/ui/shared/MediaGallery.tsx +445 -149
  224. package/src/ui/shared/PostFooter.tsx +204 -0
  225. package/src/ui/shared/StarRating.tsx +27 -0
  226. package/src/ui/shared/__tests__/format-chars.test.ts +35 -0
  227. package/src/ui/shared/index.ts +0 -1
  228. package/dist/client/assets/url-8Dj-5CLW.js +0 -1
  229. package/src/client/media-upload.ts +0 -161
  230. package/src/client/page-slug-bridge.ts +0 -42
  231. package/src/db/migrations/0000_square_wallflower.sql +0 -118
  232. package/src/db/migrations/0001_add_search_fts.sql +0 -34
  233. package/src/db/migrations/0002_add_media_attachments.sql +0 -3
  234. package/src/db/migrations/0003_add_navigation_links.sql +0 -8
  235. package/src/db/migrations/0004_add_storage_provider.sql +0 -3
  236. package/src/db/migrations/0005_v2_schema_migration.sql +0 -268
  237. package/src/db/migrations/0006_rename_slug_to_path.sql +0 -5
  238. package/src/db/migrations/0007_post_collections_m2m.sql +0 -94
  239. package/src/db/migrations/0008_add_collection_dividers.sql +0 -8
  240. package/src/db/migrations/0009_drop_collection_show_divider.sql +0 -2
  241. package/src/db/migrations/0010_add_performance_indexes.sql +0 -16
  242. package/src/db/migrations/0011_add_path_registry.sql +0 -23
  243. package/src/db/migrations/0012_add_tiptap_columns.sql +0 -2
  244. package/src/db/migrations/0013_replace_featured_with_visibility.sql +0 -8
  245. package/src/db/migrations/meta/0003_snapshot.json +0 -821
  246. package/src/lib/__tests__/sqid.test.ts +0 -65
  247. package/src/lib/sqid.ts +0 -79
  248. package/src/routes/api/__tests__/pages.test.ts +0 -218
  249. package/src/routes/api/pages.ts +0 -73
  250. package/src/routes/dash/__tests__/pages.test.ts +0 -226
  251. package/src/routes/dash/index.tsx +0 -109
  252. package/src/routes/dash/media.tsx +0 -135
  253. package/src/routes/dash/pages.tsx +0 -245
  254. package/src/routes/dash/posts.tsx +0 -338
  255. package/src/routes/dash/redirects.tsx +0 -263
  256. package/src/routes/pages/post.tsx +0 -59
  257. package/src/services/__tests__/page.test.ts +0 -298
  258. package/src/services/__tests__/path-registry.test.ts +0 -165
  259. package/src/services/__tests__/redirect.test.ts +0 -159
  260. package/src/services/page.ts +0 -216
  261. package/src/services/path-registry.ts +0 -160
  262. package/src/services/redirect.ts +0 -97
  263. package/src/ui/dash/PageForm.tsx +0 -187
  264. package/src/ui/dash/PostList.tsx +0 -95
  265. package/src/ui/dash/media/MediaListContent.tsx +0 -206
  266. package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
  267. package/src/ui/dash/pages/PagesContent.tsx +0 -75
  268. package/src/ui/dash/posts/PostForm.tsx +0 -260
  269. package/src/ui/layouts/DashLayout.tsx +0 -247
  270. package/src/ui/pages/SinglePage.tsx +0 -23
  271. package/src/ui/shared/ThreadView.tsx +0 -136
package/src/lib/errors.ts CHANGED
@@ -97,20 +97,23 @@ export function assertFound<T>(
97
97
  return value;
98
98
  }
99
99
 
100
+ const UUID_RE =
101
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
102
+
100
103
  /**
101
- * Parse a route parameter as a positive integer, throwing ValidationError if invalid.
104
+ * Validates a route parameter as a UUID.
105
+ * Throws ValidationError if the format is invalid.
102
106
  *
103
- * @param value - Raw string parameter from the route
104
- * @returns Parsed integer
107
+ * @param value - UUID string from the route
108
+ * @returns The validated UUID string
105
109
  * @example
106
110
  * ```ts
107
- * const id = parseIntParam(c.req.param("id"));
111
+ * const id = parseIdParam(c.req.param("id"));
108
112
  * ```
109
113
  */
110
- export function parseIntParam(value: string): number {
111
- const id = parseInt(value, 10);
112
- if (isNaN(id) || id < 1) {
114
+ export function parseIdParam(value: string): string {
115
+ if (!UUID_RE.test(value)) {
113
116
  throw new ValidationError("Invalid ID");
114
117
  }
115
- return id;
118
+ return value;
116
119
  }
package/src/lib/feed.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * ```
11
11
  */
12
12
 
13
- import type { FeedData, SitemapData } from "../types.js";
13
+ import type { FeedData, PostView, SitemapData } from "../types.js";
14
14
 
15
15
  /**
16
16
  * Escape special XML characters.
@@ -24,6 +24,67 @@ function escapeXml(str: string): string {
24
24
  .replace(/'/g, "&apos;");
25
25
  }
26
26
 
27
+ /**
28
+ * Escape content for safe embedding inside a CDATA section.
29
+ *
30
+ * CDATA sections end at the first `]]>` sequence. If the content contains
31
+ * `]]>`, we split it by closing the current CDATA section and opening a new
32
+ * one: `]]>` becomes `]]]]><![CDATA[>`.
33
+ *
34
+ * @param str - Raw string to embed in CDATA
35
+ * @returns String safe to place inside `<![CDATA[...]]>`
36
+ */
37
+ function escapeCdata(str: string): string {
38
+ return str.replaceAll("]]>", "]]]]><![CDATA[>");
39
+ }
40
+
41
+ /**
42
+ * Render a star rating as HTML for feed content.
43
+ */
44
+ function renderRatingHtml(rating: number): string {
45
+ const filled = "★".repeat(rating);
46
+ const empty = "☆".repeat(5 - rating);
47
+ return `<p>${filled}${empty} ${rating}/5</p>`;
48
+ }
49
+
50
+ /**
51
+ * Build the full HTML content for a feed item, combining format-specific
52
+ * fields (quote text, source URL) with body and rating.
53
+ */
54
+ function buildFeedContent(post: PostView): string {
55
+ const parts: string[] = [];
56
+
57
+ if (post.format === "quote" && post.quoteText) {
58
+ const attribution = post.title || post.url || "";
59
+ const cite = post.url ? ` cite="${escapeXml(post.url)}"` : "";
60
+ parts.push(
61
+ `<blockquote${cite}><p>${escapeXml(post.quoteText)}</p></blockquote>`,
62
+ );
63
+ if (attribution) {
64
+ const source = post.url
65
+ ? `<a href="${escapeXml(post.url)}">${escapeXml(post.title || "Source")}</a>`
66
+ : escapeXml(attribution);
67
+ parts.push(`<p>— ${source}</p>`);
68
+ }
69
+ }
70
+
71
+ if (post.format === "link" && post.url) {
72
+ parts.push(
73
+ `<p><a href="${escapeXml(post.url)}">${escapeXml(post.url)}</a></p>`,
74
+ );
75
+ }
76
+
77
+ if (post.bodyHtml) {
78
+ parts.push(post.bodyHtml);
79
+ }
80
+
81
+ if (post.rating && post.rating > 0) {
82
+ parts.push(renderRatingHtml(post.rating));
83
+ }
84
+
85
+ return parts.join("\n");
86
+ }
87
+
27
88
  /**
28
89
  * Default RSS 2.0 renderer.
29
90
  *
@@ -35,23 +96,23 @@ export function defaultRssRenderer(data: FeedData): string {
35
96
 
36
97
  const items = posts
37
98
  .map((post) => {
38
- const link = `${siteUrl}${post.permalink}`;
99
+ const link = escapeXml(`${siteUrl}${post.permalink}`);
39
100
  const title = post.title || `Post #${post.id}`;
40
101
  const pubDate = new Date(post.publishedAt).toUTCString();
41
102
 
42
103
  // Add enclosure for first media attachment
43
104
  const firstMedia = post.media[0];
44
105
  const enclosure = firstMedia
45
- ? `\n <enclosure url="${firstMedia.url}" type="${firstMedia.mimeType}"${firstMedia.size ? ` length="${firstMedia.size}"` : ""}/>`
106
+ ? `\n <enclosure url="${escapeXml(firstMedia.url)}" type="${escapeXml(firstMedia.mimeType)}"${firstMedia.size ? ` length="${firstMedia.size}"` : ""}/>`
46
107
  : "";
47
108
 
48
109
  return `
49
110
  <item>
50
- <title><![CDATA[${escapeXml(title)}]]></title>
111
+ <title><![CDATA[${escapeCdata(escapeXml(title))}]]></title>
51
112
  <link>${link}</link>
52
113
  <guid isPermaLink="true">${link}</guid>
53
114
  <pubDate>${pubDate}</pubDate>
54
- <description><![CDATA[${post.bodyHtml || ""}]]></description>${enclosure}
115
+ <description><![CDATA[${escapeCdata(buildFeedContent(post))}]]></description>${enclosure}
55
116
  </item>`;
56
117
  })
57
118
  .join("");
@@ -60,10 +121,10 @@ export function defaultRssRenderer(data: FeedData): string {
60
121
  <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
61
122
  <channel>
62
123
  <title>${escapeXml(siteName)}</title>
63
- <link>${siteUrl}</link>
124
+ <link>${escapeXml(siteUrl)}</link>
64
125
  <description>${escapeXml(siteDescription)}</description>
65
126
  <language>${siteLanguage}</language>
66
- <atom:link href="${siteUrl}/feed" rel="self" type="application/rss+xml"/>
127
+ <atom:link href="${escapeXml(siteUrl)}/feed" rel="self" type="application/rss+xml"/>
67
128
  ${items}
68
129
  </channel>
69
130
  </rss>`;
@@ -80,7 +141,7 @@ export function defaultAtomRenderer(data: FeedData): string {
80
141
 
81
142
  const entries = posts
82
143
  .map((post) => {
83
- const link = `${siteUrl}${post.permalink}`;
144
+ const link = escapeXml(`${siteUrl}${post.permalink}`);
84
145
  const title = post.title || `Post #${post.id}`;
85
146
 
86
147
  return `
@@ -90,7 +151,7 @@ export function defaultAtomRenderer(data: FeedData): string {
90
151
  <id>${link}</id>
91
152
  <published>${post.publishedAt}</published>
92
153
  <updated>${post.updatedAt}</updated>
93
- <content type="html"><![CDATA[${post.bodyHtml || ""}]]></content>
154
+ <content type="html"><![CDATA[${escapeCdata(buildFeedContent(post))}]]></content>
94
155
  </entry>`;
95
156
  })
96
157
  .join("");
@@ -101,9 +162,9 @@ export function defaultAtomRenderer(data: FeedData): string {
101
162
  <feed xmlns="http://www.w3.org/2005/Atom">
102
163
  <title>${escapeXml(siteName)}</title>
103
164
  <subtitle>${escapeXml(siteDescription)}</subtitle>
104
- <link href="${siteUrl}" rel="alternate"/>
105
- <link href="${siteUrl}/feed/atom.xml" rel="self"/>
106
- <id>${siteUrl}/</id>
165
+ <link href="${escapeXml(siteUrl)}" rel="alternate"/>
166
+ <link href="${escapeXml(siteUrl)}/feed/atom.xml" rel="self"/>
167
+ <id>${escapeXml(siteUrl)}/</id>
107
168
  <updated>${now}</updated>
108
169
  ${entries}
109
170
  </feed>`;
@@ -112,17 +173,17 @@ export function defaultAtomRenderer(data: FeedData): string {
112
173
  /**
113
174
  * Default Sitemap renderer.
114
175
  *
115
- * @param data - Sitemap data with PostView[] and PageView[]
176
+ * @param data - Sitemap data with PostView[]
116
177
  * @returns Sitemap XML string
117
178
  */
118
179
  export function defaultSitemapRenderer(data: SitemapData): string {
119
- const { siteUrl, posts, pages } = data;
180
+ const { siteUrl, posts } = data;
120
181
 
121
182
  const postUrls = posts
122
183
  .map((post) => {
123
- const loc = `${siteUrl}${post.permalink}`;
184
+ const loc = escapeXml(`${siteUrl}${post.permalink}`);
124
185
  const lastmod = post.updatedAt.split("T")[0];
125
- const priority = post.visibility === "featured" ? "0.8" : "0.6";
186
+ const priority = post.featured ? "0.8" : "0.6";
126
187
 
127
188
  return `
128
189
  <url>
@@ -133,23 +194,9 @@ export function defaultSitemapRenderer(data: SitemapData): string {
133
194
  })
134
195
  .join("");
135
196
 
136
- const pageUrls = pages
137
- .map((page) => {
138
- const loc = `${siteUrl}/${page.slug}`;
139
- const lastmod = page.updatedAt.split("T")[0];
140
-
141
- return `
142
- <url>
143
- <loc>${loc}</loc>
144
- <lastmod>${lastmod}</lastmod>
145
- <priority>0.7</priority>
146
- </url>`;
147
- })
148
- .join("");
149
-
150
197
  const homepageUrl = `
151
198
  <url>
152
- <loc>${siteUrl}/</loc>
199
+ <loc>${escapeXml(siteUrl)}/</loc>
153
200
  <priority>1.0</priority>
154
201
  <changefreq>daily</changefreq>
155
202
  </url>`;
@@ -158,6 +205,5 @@ export function defaultSitemapRenderer(data: SitemapData): string {
158
205
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
159
206
  ${homepageUrl}
160
207
  ${postUrls}
161
- ${pageUrls}
162
208
  </urlset>`;
163
209
  }
package/src/lib/html.ts CHANGED
@@ -18,5 +18,6 @@ export function escapeHtml(str: string): string {
18
18
  .replace(/&/g, "&amp;")
19
19
  .replace(/</g, "&lt;")
20
20
  .replace(/>/g, "&gt;")
21
- .replace(/"/g, "&quot;");
21
+ .replace(/"/g, "&quot;")
22
+ .replace(/'/g, "&#39;");
22
23
  }