@jant/core 0.1.2 → 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 (288) hide show
  1. package/dist/app.d.ts +34 -0
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +474 -0
  4. package/dist/assets/datastar.min.js +1775 -0
  5. package/dist/auth.d.ts +23 -0
  6. package/dist/auth.d.ts.map +1 -0
  7. package/dist/auth.js +34 -0
  8. package/{src/client.ts → dist/client.d.ts} +1 -1
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +4 -0
  11. package/dist/db/index.d.ts +10 -0
  12. package/dist/db/index.d.ts.map +1 -0
  13. package/dist/db/index.js +10 -0
  14. package/dist/db/schema.d.ts +1507 -0
  15. package/dist/db/schema.d.ts.map +1 -0
  16. package/dist/db/schema.js +183 -0
  17. package/{src/i18n/Trans.tsx → dist/i18n/Trans.d.ts} +4 -10
  18. package/dist/i18n/Trans.d.ts.map +1 -0
  19. package/dist/i18n/Trans.js +24 -0
  20. package/dist/i18n/context.d.ts +69 -0
  21. package/dist/i18n/context.d.ts.map +1 -0
  22. package/dist/i18n/context.js +61 -0
  23. package/dist/i18n/detect.d.ts +31 -0
  24. package/dist/i18n/detect.d.ts.map +1 -0
  25. package/dist/i18n/detect.js +77 -0
  26. package/{src/i18n/i18n.ts → dist/i18n/i18n.d.ts} +5 -25
  27. package/dist/i18n/i18n.d.ts.map +1 -0
  28. package/dist/i18n/i18n.js +55 -0
  29. package/dist/i18n/index.d.ts +41 -0
  30. package/dist/i18n/index.d.ts.map +1 -0
  31. package/{src/i18n/index.ts → dist/i18n/index.js} +3 -24
  32. package/dist/i18n/locales/en.d.ts +3 -0
  33. package/dist/i18n/locales/en.d.ts.map +1 -0
  34. package/dist/i18n/locales/en.js +1 -0
  35. package/dist/i18n/locales/zh-Hans.d.ts +3 -0
  36. package/dist/i18n/locales/zh-Hans.d.ts.map +1 -0
  37. package/dist/i18n/locales/zh-Hans.js +1 -0
  38. package/dist/i18n/locales/zh-Hant.d.ts +3 -0
  39. package/dist/i18n/locales/zh-Hant.d.ts.map +1 -0
  40. package/dist/i18n/locales/zh-Hant.js +1 -0
  41. package/dist/i18n/locales.d.ts +11 -0
  42. package/dist/i18n/locales.d.ts.map +1 -0
  43. package/dist/i18n/locales.js +13 -0
  44. package/dist/i18n/middleware.d.ts +24 -0
  45. package/dist/i18n/middleware.d.ts.map +1 -0
  46. package/dist/i18n/middleware.js +41 -0
  47. package/dist/index.d.ts +16 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/{src/index.ts → dist/index.js} +1 -28
  50. package/dist/lib/assets.d.ts +19 -0
  51. package/dist/lib/assets.d.ts.map +1 -0
  52. package/dist/lib/assets.js +33 -0
  53. package/dist/lib/constants.d.ts +36 -0
  54. package/dist/lib/constants.d.ts.map +1 -0
  55. package/dist/lib/constants.js +50 -0
  56. package/{src/lib/image.ts → dist/lib/image.d.ts} +13 -47
  57. package/dist/lib/image.d.ts.map +1 -0
  58. package/dist/lib/image.js +77 -0
  59. package/{src/lib/index.ts → dist/lib/index.d.ts} +1 -1
  60. package/dist/lib/index.d.ts.map +1 -0
  61. package/dist/lib/index.js +7 -0
  62. package/dist/lib/markdown.d.ts +60 -0
  63. package/dist/lib/markdown.d.ts.map +1 -0
  64. package/{src/lib/markdown.ts → dist/lib/markdown.js} +16 -26
  65. package/dist/lib/schemas.d.ts +113 -0
  66. package/dist/lib/schemas.d.ts.map +1 -0
  67. package/dist/lib/schemas.js +71 -0
  68. package/dist/lib/sqid.d.ts +60 -0
  69. package/dist/lib/sqid.d.ts.map +1 -0
  70. package/{src/lib/sqid.ts → dist/lib/sqid.js} +15 -22
  71. package/dist/lib/sse.d.ts +95 -0
  72. package/dist/lib/sse.d.ts.map +1 -0
  73. package/dist/lib/sse.js +81 -0
  74. package/dist/lib/time.d.ts +90 -0
  75. package/dist/lib/time.d.ts.map +1 -0
  76. package/{src/lib/time.ts → dist/lib/time.js} +20 -33
  77. package/{src/lib/url.ts → dist/lib/url.d.ts} +5 -30
  78. package/dist/lib/url.d.ts.map +1 -0
  79. package/dist/lib/url.js +89 -0
  80. package/dist/middleware/auth.d.ts +24 -0
  81. package/dist/middleware/auth.d.ts.map +1 -0
  82. package/dist/middleware/auth.js +52 -0
  83. package/dist/routes/api/posts.d.ts +13 -0
  84. package/dist/routes/api/posts.d.ts.map +1 -0
  85. package/dist/routes/api/posts.js +124 -0
  86. package/dist/routes/api/search.d.ts +13 -0
  87. package/dist/routes/api/search.d.ts.map +1 -0
  88. package/dist/routes/api/search.js +49 -0
  89. package/dist/routes/api/upload.d.ts +16 -0
  90. package/dist/routes/api/upload.d.ts.map +1 -0
  91. package/dist/routes/api/upload.js +227 -0
  92. package/dist/routes/dash/collections.d.ts +13 -0
  93. package/dist/routes/dash/collections.d.ts.map +1 -0
  94. package/dist/routes/dash/collections.js +512 -0
  95. package/dist/routes/dash/index.d.ts +15 -0
  96. package/dist/routes/dash/index.d.ts.map +1 -0
  97. package/dist/routes/dash/index.js +117 -0
  98. package/dist/routes/dash/media.d.ts +16 -0
  99. package/dist/routes/dash/media.d.ts.map +1 -0
  100. package/dist/routes/dash/media.js +589 -0
  101. package/dist/routes/dash/pages.d.ts +15 -0
  102. package/dist/routes/dash/pages.d.ts.map +1 -0
  103. package/dist/routes/dash/pages.js +290 -0
  104. package/dist/routes/dash/posts.d.ts +13 -0
  105. package/dist/routes/dash/posts.d.ts.map +1 -0
  106. package/dist/routes/dash/posts.js +226 -0
  107. package/dist/routes/dash/redirects.d.ts +13 -0
  108. package/dist/routes/dash/redirects.d.ts.map +1 -0
  109. package/dist/routes/dash/redirects.js +237 -0
  110. package/dist/routes/dash/settings.d.ts +13 -0
  111. package/dist/routes/dash/settings.d.ts.map +1 -0
  112. package/dist/routes/dash/settings.js +154 -0
  113. package/dist/routes/feed/rss.d.ts +13 -0
  114. package/dist/routes/feed/rss.d.ts.map +1 -0
  115. package/dist/routes/feed/rss.js +95 -0
  116. package/dist/routes/feed/sitemap.d.ts +13 -0
  117. package/dist/routes/feed/sitemap.d.ts.map +1 -0
  118. package/dist/routes/feed/sitemap.js +59 -0
  119. package/dist/routes/pages/archive.d.ts +15 -0
  120. package/dist/routes/pages/archive.d.ts.map +1 -0
  121. package/dist/routes/pages/archive.js +255 -0
  122. package/dist/routes/pages/collection.d.ts +13 -0
  123. package/dist/routes/pages/collection.d.ts.map +1 -0
  124. package/dist/routes/pages/collection.js +93 -0
  125. package/dist/routes/pages/home.d.ts +13 -0
  126. package/dist/routes/pages/home.d.ts.map +1 -0
  127. package/dist/routes/pages/home.js +122 -0
  128. package/dist/routes/pages/page.d.ts +15 -0
  129. package/dist/routes/pages/page.d.ts.map +1 -0
  130. package/dist/routes/pages/page.js +69 -0
  131. package/dist/routes/pages/post.d.ts +13 -0
  132. package/dist/routes/pages/post.d.ts.map +1 -0
  133. package/dist/routes/pages/post.js +90 -0
  134. package/dist/routes/pages/search.d.ts +13 -0
  135. package/dist/routes/pages/search.d.ts.map +1 -0
  136. package/dist/routes/pages/search.js +180 -0
  137. package/dist/services/collection.d.ts +31 -0
  138. package/dist/services/collection.d.ts.map +1 -0
  139. package/dist/services/collection.js +108 -0
  140. package/dist/services/index.d.ts +28 -0
  141. package/dist/services/index.d.ts.map +1 -0
  142. package/dist/services/index.js +20 -0
  143. package/dist/services/media.d.ts +27 -0
  144. package/dist/services/media.d.ts.map +1 -0
  145. package/dist/services/media.js +62 -0
  146. package/dist/services/post.d.ts +31 -0
  147. package/dist/services/post.d.ts.map +1 -0
  148. package/dist/services/post.js +191 -0
  149. package/dist/services/redirect.d.ts +15 -0
  150. package/dist/services/redirect.d.ts.map +1 -0
  151. package/dist/services/redirect.js +48 -0
  152. package/dist/services/search.d.ts +26 -0
  153. package/dist/services/search.d.ts.map +1 -0
  154. package/dist/services/search.js +61 -0
  155. package/dist/services/settings.d.ts +17 -0
  156. package/dist/services/settings.d.ts.map +1 -0
  157. package/dist/services/settings.js +65 -0
  158. package/dist/theme/components/ActionButtons.d.ts +43 -0
  159. package/dist/theme/components/ActionButtons.d.ts.map +1 -0
  160. package/dist/theme/components/ActionButtons.js +50 -0
  161. package/dist/theme/components/CrudPageHeader.d.ts +23 -0
  162. package/dist/theme/components/CrudPageHeader.d.ts.map +1 -0
  163. package/dist/theme/components/CrudPageHeader.js +22 -0
  164. package/dist/theme/components/DangerZone.d.ts +36 -0
  165. package/dist/theme/components/DangerZone.d.ts.map +1 -0
  166. package/dist/theme/components/DangerZone.js +39 -0
  167. package/dist/theme/components/EmptyState.d.ts +27 -0
  168. package/dist/theme/components/EmptyState.d.ts.map +1 -0
  169. package/dist/theme/components/EmptyState.js +27 -0
  170. package/dist/theme/components/ListItemRow.d.ts +15 -0
  171. package/dist/theme/components/ListItemRow.d.ts.map +1 -0
  172. package/dist/theme/components/ListItemRow.js +21 -0
  173. package/dist/theme/components/PageForm.d.ts +14 -0
  174. package/dist/theme/components/PageForm.d.ts.map +1 -0
  175. package/dist/theme/components/PageForm.js +173 -0
  176. package/dist/theme/components/Pagination.d.ts +46 -0
  177. package/dist/theme/components/Pagination.d.ts.map +1 -0
  178. package/dist/theme/components/Pagination.js +159 -0
  179. package/dist/theme/components/PostForm.d.ts +12 -0
  180. package/dist/theme/components/PostForm.d.ts.map +1 -0
  181. package/dist/theme/components/PostForm.js +230 -0
  182. package/dist/theme/components/PostList.d.ts +10 -0
  183. package/dist/theme/components/PostList.d.ts.map +1 -0
  184. package/dist/theme/components/PostList.js +73 -0
  185. package/dist/theme/components/ThreadView.d.ts +15 -0
  186. package/dist/theme/components/ThreadView.d.ts.map +1 -0
  187. package/dist/theme/components/ThreadView.js +111 -0
  188. package/dist/theme/components/TypeBadge.d.ts +12 -0
  189. package/dist/theme/components/TypeBadge.d.ts.map +1 -0
  190. package/dist/theme/components/TypeBadge.js +39 -0
  191. package/dist/theme/components/VisibilityBadge.d.ts +12 -0
  192. package/dist/theme/components/VisibilityBadge.d.ts.map +1 -0
  193. package/dist/theme/components/VisibilityBadge.js +37 -0
  194. package/{src/theme/components/index.ts → dist/theme/components/index.d.ts} +1 -0
  195. package/dist/theme/components/index.d.ts.map +1 -0
  196. package/dist/theme/components/index.js +12 -0
  197. package/dist/theme/index.d.ts +21 -0
  198. package/dist/theme/index.d.ts.map +1 -0
  199. package/{src/theme/index.ts → dist/theme/index.js} +1 -4
  200. package/dist/theme/layouts/BaseLayout.d.ts +16 -0
  201. package/dist/theme/layouts/BaseLayout.d.ts.map +1 -0
  202. package/dist/theme/layouts/BaseLayout.js +58 -0
  203. package/dist/theme/layouts/DashLayout.d.ts +15 -0
  204. package/dist/theme/layouts/DashLayout.d.ts.map +1 -0
  205. package/dist/theme/layouts/DashLayout.js +139 -0
  206. package/{src/theme/layouts/index.ts → dist/theme/layouts/index.d.ts} +1 -0
  207. package/dist/theme/layouts/index.d.ts.map +1 -0
  208. package/dist/theme/layouts/index.js +2 -0
  209. package/dist/theme/styles/main.css +2 -0
  210. package/dist/types.d.ts +179 -0
  211. package/dist/types.d.ts.map +1 -0
  212. package/dist/types.js +19 -0
  213. package/package.json +26 -26
  214. package/drizzle.config.ts +0 -10
  215. package/lingui.config.ts +0 -16
  216. package/src/app.tsx +0 -377
  217. package/src/assets/datastar.min.js +0 -8
  218. package/src/auth.ts +0 -38
  219. package/src/db/index.ts +0 -14
  220. package/src/db/migrations/0000_solid_moon_knight.sql +0 -118
  221. package/src/db/migrations/0001_add_search_fts.sql +0 -40
  222. package/src/db/migrations/0002_collection_path.sql +0 -2
  223. package/src/db/migrations/0003_collection_path_nullable.sql +0 -21
  224. package/src/db/migrations/0004_media_uuid.sql +0 -35
  225. package/src/db/migrations/meta/0000_snapshot.json +0 -784
  226. package/src/db/migrations/meta/_journal.json +0 -41
  227. package/src/db/schema.ts +0 -159
  228. package/src/i18n/EXAMPLES.md +0 -235
  229. package/src/i18n/README.md +0 -296
  230. package/src/i18n/context.tsx +0 -101
  231. package/src/i18n/detect.ts +0 -100
  232. package/src/i18n/locales/en.po +0 -875
  233. package/src/i18n/locales/en.ts +0 -1
  234. package/src/i18n/locales/zh-Hans.po +0 -875
  235. package/src/i18n/locales/zh-Hans.ts +0 -1
  236. package/src/i18n/locales/zh-Hant.po +0 -875
  237. package/src/i18n/locales/zh-Hant.ts +0 -1
  238. package/src/i18n/locales.ts +0 -14
  239. package/src/i18n/middleware.ts +0 -59
  240. package/src/lib/assets.ts +0 -47
  241. package/src/lib/constants.ts +0 -67
  242. package/src/lib/schemas.ts +0 -92
  243. package/src/lib/sse.ts +0 -152
  244. package/src/middleware/auth.ts +0 -59
  245. package/src/routes/api/posts.ts +0 -127
  246. package/src/routes/api/search.ts +0 -53
  247. package/src/routes/api/upload.ts +0 -240
  248. package/src/routes/dash/collections.tsx +0 -341
  249. package/src/routes/dash/index.tsx +0 -89
  250. package/src/routes/dash/media.tsx +0 -551
  251. package/src/routes/dash/pages.tsx +0 -245
  252. package/src/routes/dash/posts.tsx +0 -202
  253. package/src/routes/dash/redirects.tsx +0 -155
  254. package/src/routes/dash/settings.tsx +0 -93
  255. package/src/routes/feed/rss.ts +0 -119
  256. package/src/routes/feed/sitemap.ts +0 -75
  257. package/src/routes/pages/archive.tsx +0 -223
  258. package/src/routes/pages/collection.tsx +0 -79
  259. package/src/routes/pages/home.tsx +0 -93
  260. package/src/routes/pages/page.tsx +0 -64
  261. package/src/routes/pages/post.tsx +0 -81
  262. package/src/routes/pages/search.tsx +0 -162
  263. package/src/services/collection.ts +0 -180
  264. package/src/services/index.ts +0 -40
  265. package/src/services/media.ts +0 -97
  266. package/src/services/post.ts +0 -279
  267. package/src/services/redirect.ts +0 -74
  268. package/src/services/search.ts +0 -117
  269. package/src/services/settings.ts +0 -76
  270. package/src/theme/components/ActionButtons.tsx +0 -98
  271. package/src/theme/components/CrudPageHeader.tsx +0 -48
  272. package/src/theme/components/DangerZone.tsx +0 -77
  273. package/src/theme/components/EmptyState.tsx +0 -56
  274. package/src/theme/components/ListItemRow.tsx +0 -24
  275. package/src/theme/components/PageForm.tsx +0 -114
  276. package/src/theme/components/Pagination.tsx +0 -196
  277. package/src/theme/components/PostForm.tsx +0 -122
  278. package/src/theme/components/PostList.tsx +0 -68
  279. package/src/theme/components/ThreadView.tsx +0 -118
  280. package/src/theme/components/TypeBadge.tsx +0 -28
  281. package/src/theme/components/VisibilityBadge.tsx +0 -33
  282. package/src/theme/layouts/BaseLayout.tsx +0 -49
  283. package/src/theme/layouts/DashLayout.tsx +0 -108
  284. package/src/theme/styles/main.css +0 -52
  285. package/src/types.ts +0 -222
  286. package/tsconfig.json +0 -16
  287. package/vite.config.ts +0 -82
  288. package/wrangler.toml +0 -21
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Markdown Rendering
3
+ *
4
+ * Uses marked with minimal configuration
5
+ */
6
+ /**
7
+ * Renders Markdown content to HTML using the marked library.
8
+ *
9
+ * Configured with GitHub Flavored Markdown (GFM) support and line breaks enabled.
10
+ * Uses synchronous parsing for simplicity and consistency in server-side rendering.
11
+ *
12
+ * @param markdown - The Markdown string to convert to HTML
13
+ * @returns The rendered HTML string
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const html = render("# Hello\n\nThis is **bold** text.");
18
+ * // Returns: "<h1>Hello</h1>\n<p>This is <strong>bold</strong> text.</p>"
19
+ * ```
20
+ */
21
+ export declare function render(markdown: string): string;
22
+ /**
23
+ * Converts Markdown to plain text by stripping all formatting syntax.
24
+ *
25
+ * Removes Markdown syntax including headers, bold, italic, links, images, code blocks,
26
+ * blockquotes, lists, and converts newlines to spaces. Useful for generating text excerpts,
27
+ * meta descriptions, or search indexes.
28
+ *
29
+ * @param markdown - The Markdown string to convert to plain text
30
+ * @returns The plain text string with all Markdown syntax removed
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const plain = toPlainText("## Hello\n\nThis is **bold** and [a link](url).");
35
+ * // Returns: "Hello This is bold and a link."
36
+ * ```
37
+ */
38
+ export declare function toPlainText(markdown: string): string;
39
+ /**
40
+ * Extracts a title from Markdown content by taking the first sentence or line.
41
+ *
42
+ * Converts Markdown to plain text first, then takes the first sentence (split by `.!?`)
43
+ * or truncates to the specified maximum length. Useful for generating automatic titles
44
+ * from post content when no explicit title is provided.
45
+ *
46
+ * @param markdown - The Markdown string to extract a title from
47
+ * @param maxLength - Maximum length of the extracted title (default: 120)
48
+ * @returns The extracted title string, with "..." appended if truncated
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const title = extractTitle("This is the first sentence. And another one.", 50);
53
+ * // Returns: "This is the first sentence"
54
+ *
55
+ * const title = extractTitle("A very long sentence that exceeds the maximum length...", 30);
56
+ * // Returns: "A very long sentence that ex..."
57
+ * ```
58
+ */
59
+ export declare function extractTitle(markdown: string, maxLength?: number): string;
60
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYpD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,MAAM,CAStE"}
@@ -2,16 +2,12 @@
2
2
  * Markdown Rendering
3
3
  *
4
4
  * Uses marked with minimal configuration
5
- */
6
-
7
- import { marked } from "marked";
8
-
5
+ */ import { marked } from "marked";
9
6
  // Configure marked for security and simplicity
10
7
  marked.setOptions({
11
- gfm: true,
12
- breaks: true,
8
+ gfm: true,
9
+ breaks: true
13
10
  });
14
-
15
11
  /**
16
12
  * Renders Markdown content to HTML using the marked library.
17
13
  *
@@ -26,11 +22,11 @@ marked.setOptions({
26
22
  * const html = render("# Hello\n\nThis is **bold** text.");
27
23
  * // Returns: "<h1>Hello</h1>\n<p>This is <strong>bold</strong> text.</p>"
28
24
  * ```
29
- */
30
- export function render(markdown: string): string {
31
- return marked.parse(markdown, { async: false }) as string;
25
+ */ export function render(markdown) {
26
+ return marked.parse(markdown, {
27
+ async: false
28
+ });
32
29
  }
33
-
34
30
  /**
35
31
  * Converts Markdown to plain text by stripping all formatting syntax.
36
32
  *
@@ -46,10 +42,8 @@ export function render(markdown: string): string {
46
42
  * const plain = toPlainText("## Hello\n\nThis is **bold** and [a link](url).");
47
43
  * // Returns: "Hello This is bold and a link."
48
44
  * ```
49
- */
50
- export function toPlainText(markdown: string): string {
51
- return markdown
52
- .replace(/#{1,6}\s+/g, "") // Remove headers
45
+ */ export function toPlainText(markdown) {
46
+ return markdown.replace(/#{1,6}\s+/g, "") // Remove headers
53
47
  .replace(/\*\*(.+?)\*\*/g, "$1") // Bold
54
48
  .replace(/\*(.+?)\*/g, "$1") // Italic
55
49
  .replace(/\[(.+?)\]\(.+?\)/g, "$1") // Links
@@ -60,7 +54,6 @@ export function toPlainText(markdown: string): string {
60
54
  .replace(/\n+/g, " ") // Newlines
61
55
  .trim();
62
56
  }
63
-
64
57
  /**
65
58
  * Extracts a title from Markdown content by taking the first sentence or line.
66
59
  *
@@ -80,14 +73,11 @@ export function toPlainText(markdown: string): string {
80
73
  * const title = extractTitle("A very long sentence that exceeds the maximum length...", 30);
81
74
  * // Returns: "A very long sentence that ex..."
82
75
  * ```
83
- */
84
- export function extractTitle(markdown: string, maxLength = 120): string {
85
- const plain = toPlainText(markdown);
86
- const firstLine = plain.split(/[.!?]/)[0] ?? plain;
87
-
88
- if (firstLine.length <= maxLength) {
89
- return firstLine;
90
- }
91
-
92
- return plain.slice(0, maxLength).trim() + "...";
76
+ */ export function extractTitle(markdown, maxLength = 120) {
77
+ const plain = toPlainText(markdown);
78
+ const firstLine = plain.split(/[.!?]/)[0] ?? plain;
79
+ if (firstLine.length <= maxLength) {
80
+ return firstLine;
81
+ }
82
+ return plain.slice(0, maxLength).trim() + "...";
93
83
  }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Shared Zod schemas for validation
3
+ *
4
+ * These schemas ensure type-safe validation of user input
5
+ * from forms, API requests, and other external sources.
6
+ *
7
+ * IMPORTANT: Types are defined in types.ts as the single source of truth.
8
+ * This file only defines Zod validation schemas based on those types.
9
+ */
10
+ import { z } from "zod";
11
+ /**
12
+ * Post type enum schema
13
+ * Based on POST_TYPES from types.ts
14
+ */
15
+ export declare const PostTypeSchema: z.ZodEnum<{
16
+ note: "note";
17
+ article: "article";
18
+ link: "link";
19
+ quote: "quote";
20
+ image: "image";
21
+ page: "page";
22
+ }>;
23
+ /**
24
+ * Visibility enum schema
25
+ * Based on VISIBILITY_LEVELS from types.ts
26
+ */
27
+ export declare const VisibilitySchema: z.ZodEnum<{
28
+ featured: "featured";
29
+ quiet: "quiet";
30
+ unlisted: "unlisted";
31
+ draft: "draft";
32
+ }>;
33
+ /**
34
+ * Redirect type enum schema
35
+ * Form input validation for redirect type (stored as number in DB)
36
+ */
37
+ export declare const RedirectTypeSchema: z.ZodEnum<{
38
+ 301: "301";
39
+ 302: "302";
40
+ }>;
41
+ /**
42
+ * API request body schema for creating a post
43
+ */
44
+ export declare const CreatePostSchema: z.ZodObject<{
45
+ type: z.ZodEnum<{
46
+ note: "note";
47
+ article: "article";
48
+ link: "link";
49
+ quote: "quote";
50
+ image: "image";
51
+ page: "page";
52
+ }>;
53
+ title: z.ZodOptional<z.ZodString>;
54
+ content: z.ZodString;
55
+ visibility: z.ZodEnum<{
56
+ featured: "featured";
57
+ quiet: "quiet";
58
+ unlisted: "unlisted";
59
+ draft: "draft";
60
+ }>;
61
+ sourceUrl: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
62
+ sourceName: z.ZodOptional<z.ZodString>;
63
+ path: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
64
+ replyToId: z.ZodOptional<z.ZodString>;
65
+ publishedAt: z.ZodOptional<z.ZodNumber>;
66
+ }, z.core.$strip>;
67
+ /**
68
+ * API request body schema for updating a post
69
+ */
70
+ export declare const UpdatePostSchema: z.ZodObject<{
71
+ type: z.ZodOptional<z.ZodEnum<{
72
+ note: "note";
73
+ article: "article";
74
+ link: "link";
75
+ quote: "quote";
76
+ image: "image";
77
+ page: "page";
78
+ }>>;
79
+ title: z.ZodOptional<z.ZodOptional<z.ZodString>>;
80
+ content: z.ZodOptional<z.ZodString>;
81
+ visibility: z.ZodOptional<z.ZodEnum<{
82
+ featured: "featured";
83
+ quiet: "quiet";
84
+ unlisted: "unlisted";
85
+ draft: "draft";
86
+ }>>;
87
+ sourceUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
88
+ sourceName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
89
+ path: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
90
+ replyToId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
91
+ publishedAt: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
92
+ }, z.core.$strip>;
93
+ /**
94
+ * Form data helper: safely parse a FormData value with a schema
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const type = parseFormData(formData, "type", PostTypeSchema);
99
+ * // type is PostType, throws if invalid
100
+ * ```
101
+ */
102
+ export declare function parseFormData<T>(formData: FormData, key: string, schema: z.ZodSchema<T>): T;
103
+ /**
104
+ * Form data helper: safely parse optional FormData value with a schema
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * const slug = parseFormDataOptional(formData, "slug", z.string());
109
+ * // slug is string | undefined
110
+ * ```
111
+ */
112
+ export declare function parseFormDataOptional<T>(formData: FormData, key: string, schema: z.ZodSchema<T>): T | undefined;
113
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;EAAqB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;;EAA4B,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;EAAyB,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;iBAU3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;iBAA6B,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GACrB,CAAC,CAMH;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GACrB,CAAC,GAAG,SAAS,CAMf"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Shared Zod schemas for validation
3
+ *
4
+ * These schemas ensure type-safe validation of user input
5
+ * from forms, API requests, and other external sources.
6
+ *
7
+ * IMPORTANT: Types are defined in types.ts as the single source of truth.
8
+ * This file only defines Zod validation schemas based on those types.
9
+ */ import { z } from "zod";
10
+ import { POST_TYPES, VISIBILITY_LEVELS } from "../types.js";
11
+ /**
12
+ * Post type enum schema
13
+ * Based on POST_TYPES from types.ts
14
+ */ export const PostTypeSchema = z.enum(POST_TYPES);
15
+ /**
16
+ * Visibility enum schema
17
+ * Based on VISIBILITY_LEVELS from types.ts
18
+ */ export const VisibilitySchema = z.enum(VISIBILITY_LEVELS);
19
+ /**
20
+ * Redirect type enum schema
21
+ * Form input validation for redirect type (stored as number in DB)
22
+ */ export const RedirectTypeSchema = z.enum([
23
+ "301",
24
+ "302"
25
+ ]);
26
+ /**
27
+ * API request body schema for creating a post
28
+ */ export const CreatePostSchema = z.object({
29
+ type: PostTypeSchema,
30
+ title: z.string().optional(),
31
+ content: z.string(),
32
+ visibility: VisibilitySchema,
33
+ sourceUrl: z.string().url().optional().or(z.literal("")),
34
+ sourceName: z.string().optional(),
35
+ path: z.string().regex(/^[a-z0-9-]*$/).optional().or(z.literal("")),
36
+ replyToId: z.string().optional(),
37
+ publishedAt: z.number().int().positive().optional()
38
+ });
39
+ /**
40
+ * API request body schema for updating a post
41
+ */ export const UpdatePostSchema = CreatePostSchema.partial();
42
+ /**
43
+ * Form data helper: safely parse a FormData value with a schema
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const type = parseFormData(formData, "type", PostTypeSchema);
48
+ * // type is PostType, throws if invalid
49
+ * ```
50
+ */ export function parseFormData(formData, key, schema) {
51
+ const value = formData.get(key);
52
+ if (value === null) {
53
+ throw new Error(`Missing required field: ${key}`);
54
+ }
55
+ return schema.parse(value);
56
+ }
57
+ /**
58
+ * Form data helper: safely parse optional FormData value with a schema
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const slug = parseFormDataOptional(formData, "slug", z.string());
63
+ * // slug is string | undefined
64
+ * ```
65
+ */ export function parseFormDataOptional(formData, key, schema) {
66
+ const value = formData.get(key);
67
+ if (value === null || value === "") {
68
+ return undefined;
69
+ }
70
+ return schema.parse(value);
71
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Sqids - Short unique IDs for URLs
3
+ *
4
+ * Encodes numeric IDs to short strings like "jR3k"
5
+ */
6
+ /**
7
+ * Encodes a numeric database ID to a short, URL-friendly string.
8
+ *
9
+ * Uses the Sqids library to generate short unique identifiers with a minimum length of 4 characters.
10
+ * These are used in URLs (e.g., `/p/jR3k`) to obscure sequential integer IDs while maintaining
11
+ * uniqueness and reversibility.
12
+ *
13
+ * @param id - The numeric database ID to encode
14
+ * @returns A short string representation of the ID (minimum 4 characters)
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const sqid = encode(123);
19
+ * // Returns: "jR3k" (or similar short string)
20
+ * ```
21
+ */
22
+ export declare function encode(id: number): string;
23
+ /**
24
+ * Decodes a sqid string back to the original numeric database ID.
25
+ *
26
+ * Attempts to decode a sqid string generated by the `encode()` function. Returns the original
27
+ * numeric ID if valid, or `null` if the string is not a valid sqid. This is used to extract
28
+ * database IDs from URL parameters.
29
+ *
30
+ * @param str - The sqid string to decode
31
+ * @returns The original numeric ID if valid, or `null` if decoding fails
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const id = decode("jR3k");
36
+ * // Returns: 123
37
+ *
38
+ * const invalid = decode("invalid");
39
+ * // Returns: null
40
+ * ```
41
+ */
42
+ export declare function decode(str: string): number | null;
43
+ /**
44
+ * Checks if a string is a valid sqid that can be decoded.
45
+ *
46
+ * Validates whether a string can be successfully decoded to a numeric ID.
47
+ * Useful for route validation and input sanitization.
48
+ *
49
+ * @param str - The string to validate
50
+ * @returns `true` if the string is a valid sqid, `false` otherwise
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * if (isValidSqid("jR3k")) {
55
+ * // Process the valid sqid
56
+ * }
57
+ * ```
58
+ */
59
+ export declare function isValidSqid(str: string): boolean;
60
+ //# sourceMappingURL=sqid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqid.d.ts","sourceRoot":"","sources":["../../src/lib/sqid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOjD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD"}
@@ -2,14 +2,10 @@
2
2
  * Sqids - Short unique IDs for URLs
3
3
  *
4
4
  * Encodes numeric IDs to short strings like "jR3k"
5
- */
6
-
7
- import Sqids from "sqids";
8
-
5
+ */ import Sqids from "sqids";
9
6
  const sqids = new Sqids({
10
- minLength: 4,
7
+ minLength: 4
11
8
  });
12
-
13
9
  /**
14
10
  * Encodes a numeric database ID to a short, URL-friendly string.
15
11
  *
@@ -25,11 +21,11 @@ const sqids = new Sqids({
25
21
  * const sqid = encode(123);
26
22
  * // Returns: "jR3k" (or similar short string)
27
23
  * ```
28
- */
29
- export function encode(id: number): string {
30
- return sqids.encode([id]);
24
+ */ export function encode(id) {
25
+ return sqids.encode([
26
+ id
27
+ ]);
31
28
  }
32
-
33
29
  /**
34
30
  * Decodes a sqid string back to the original numeric database ID.
35
31
  *
@@ -48,16 +44,14 @@ export function encode(id: number): string {
48
44
  * const invalid = decode("invalid");
49
45
  * // Returns: null
50
46
  * ```
51
- */
52
- export function decode(str: string): number | null {
53
- try {
54
- const ids = sqids.decode(str);
55
- return ids[0] ?? null;
56
- } catch {
57
- return null;
58
- }
47
+ */ export function decode(str) {
48
+ try {
49
+ const ids = sqids.decode(str);
50
+ return ids[0] ?? null;
51
+ } catch {
52
+ return null;
53
+ }
59
54
  }
60
-
61
55
  /**
62
56
  * Checks if a string is a valid sqid that can be decoded.
63
57
  *
@@ -73,7 +67,6 @@ export function decode(str: string): number | null {
73
67
  * // Process the valid sqid
74
68
  * }
75
69
  * ```
76
- */
77
- export function isValidSqid(str: string): boolean {
78
- return decode(str) !== null;
70
+ */ export function isValidSqid(str) {
71
+ return decode(str) !== null;
79
72
  }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Server-Sent Events (SSE) utilities for Datastar
3
+ *
4
+ * Provides helpers for streaming SSE responses that Datastar can consume.
5
+ * Datastar uses SSE for real-time UI updates without page reloads.
6
+ *
7
+ * @see https://data-star.dev/
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * app.post("/api/example", (c) => {
12
+ * return sse(c, async (stream) => {
13
+ * await stream.patchSignals({ loading: false });
14
+ * await stream.patchElements("#result", "<div>Done!</div>");
15
+ * });
16
+ * });
17
+ * ```
18
+ */
19
+ import type { Context } from "hono";
20
+ /**
21
+ * Patch modes for DOM updates
22
+ */
23
+ export type PatchMode = "morph" | "inner" | "outer" | "append" | "prepend" | "remove";
24
+ /**
25
+ * SSE stream writer for Datastar events
26
+ */
27
+ export interface SSEStream {
28
+ /**
29
+ * Update reactive signals on the client
30
+ *
31
+ * @param signals - Object containing signal values to update
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * await stream.patchSignals({ count: 42, loading: false });
36
+ * ```
37
+ */
38
+ patchSignals(signals: Record<string, unknown>): Promise<void>;
39
+ /**
40
+ * Update DOM elements
41
+ *
42
+ * @param html - HTML content (must include element with id for targeting)
43
+ * @param options - Optional mode and selector
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * // Replace element with matching id (default: morph)
48
+ * await stream.patchElements('<div id="content">New content</div>');
49
+ *
50
+ * // Append to a container
51
+ * await stream.patchElements('<div>New item</div>', {
52
+ * mode: 'append',
53
+ * selector: '#list'
54
+ * });
55
+ * ```
56
+ */
57
+ patchElements(html: string, options?: {
58
+ mode?: PatchMode;
59
+ selector?: string;
60
+ }): Promise<void>;
61
+ /**
62
+ * Execute JavaScript on the client
63
+ *
64
+ * @param script - JavaScript code to execute
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * await stream.executeScript('console.log("Hello from server")');
69
+ * ```
70
+ */
71
+ executeScript(script: string): Promise<void>;
72
+ }
73
+ /**
74
+ * Create an SSE response for Datastar
75
+ *
76
+ * @param c - Hono context
77
+ * @param handler - Async function that writes to the SSE stream
78
+ * @returns Response with SSE content-type
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * app.post("/api/upload", (c) => {
83
+ * return sse(c, async (stream) => {
84
+ * // Process upload...
85
+ * await stream.patchSignals({ uploading: false });
86
+ * await stream.patchElements('<div id="new-item">...</div>', {
87
+ * mode: 'append',
88
+ * selector: '#items'
89
+ * });
90
+ * });
91
+ * });
92
+ * ```
93
+ */
94
+ export declare function sse(c: Context, handler: (stream: SSEStream) => Promise<void>): Response;
95
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/lib/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;;;;OASG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;;;;;;;;;;;;;;;OAiBG;IACH,aAAa,CACX,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAChD,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;;OASG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,GAAG,CACjB,CAAC,EAAE,OAAO,EACV,OAAO,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,GAC5C,QAAQ,CAiDV"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Server-Sent Events (SSE) utilities for Datastar
3
+ *
4
+ * Provides helpers for streaming SSE responses that Datastar can consume.
5
+ * Datastar uses SSE for real-time UI updates without page reloads.
6
+ *
7
+ * @see https://data-star.dev/
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * app.post("/api/example", (c) => {
12
+ * return sse(c, async (stream) => {
13
+ * await stream.patchSignals({ loading: false });
14
+ * await stream.patchElements("#result", "<div>Done!</div>");
15
+ * });
16
+ * });
17
+ * ```
18
+ */ /**
19
+ * Create an SSE response for Datastar
20
+ *
21
+ * @param c - Hono context
22
+ * @param handler - Async function that writes to the SSE stream
23
+ * @returns Response with SSE content-type
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * app.post("/api/upload", (c) => {
28
+ * return sse(c, async (stream) => {
29
+ * // Process upload...
30
+ * await stream.patchSignals({ uploading: false });
31
+ * await stream.patchElements('<div id="new-item">...</div>', {
32
+ * mode: 'append',
33
+ * selector: '#items'
34
+ * });
35
+ * });
36
+ * });
37
+ * ```
38
+ */ export function sse(c, handler) {
39
+ const encoder = new TextEncoder();
40
+ const stream = new ReadableStream({
41
+ async start (controller) {
42
+ const write = (data)=>{
43
+ controller.enqueue(encoder.encode(data));
44
+ };
45
+ const sseStream = {
46
+ async patchSignals (signals) {
47
+ write(`event: datastar-patch-signals\n`);
48
+ write(`data: signals ${JSON.stringify(signals)}\n\n`);
49
+ },
50
+ async patchElements (html, options = {}) {
51
+ write(`event: datastar-patch-elements\n`);
52
+ if (options.mode) {
53
+ write(`data: mode ${options.mode}\n`);
54
+ }
55
+ if (options.selector) {
56
+ write(`data: selector ${options.selector}\n`);
57
+ }
58
+ // Escape newlines in HTML for SSE format
59
+ const escapedHtml = html.replace(/\n/g, "\ndata: ");
60
+ write(`data: elements ${escapedHtml}\n\n`);
61
+ },
62
+ async executeScript (script) {
63
+ write(`event: datastar-execute-script\n`);
64
+ write(`data: script ${script}\n\n`);
65
+ }
66
+ };
67
+ try {
68
+ await handler(sseStream);
69
+ } finally{
70
+ controller.close();
71
+ }
72
+ }
73
+ });
74
+ return new Response(stream, {
75
+ headers: {
76
+ "Content-Type": "text/event-stream",
77
+ "Cache-Control": "no-cache",
78
+ Connection: "keep-alive"
79
+ }
80
+ });
81
+ }