@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.
- package/dist/app.d.ts +34 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +474 -0
- package/dist/assets/datastar.min.js +1775 -0
- package/dist/auth.d.ts +23 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +34 -0
- package/{src/client.ts → dist/client.d.ts} +1 -1
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +4 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +10 -0
- package/dist/db/schema.d.ts +1507 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +183 -0
- package/{src/i18n/Trans.tsx → dist/i18n/Trans.d.ts} +4 -10
- package/dist/i18n/Trans.d.ts.map +1 -0
- package/dist/i18n/Trans.js +24 -0
- package/dist/i18n/context.d.ts +69 -0
- package/dist/i18n/context.d.ts.map +1 -0
- package/dist/i18n/context.js +61 -0
- package/dist/i18n/detect.d.ts +31 -0
- package/dist/i18n/detect.d.ts.map +1 -0
- package/dist/i18n/detect.js +77 -0
- package/{src/i18n/i18n.ts → dist/i18n/i18n.d.ts} +5 -25
- package/dist/i18n/i18n.d.ts.map +1 -0
- package/dist/i18n/i18n.js +55 -0
- package/dist/i18n/index.d.ts +41 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/{src/i18n/index.ts → dist/i18n/index.js} +3 -24
- package/dist/i18n/locales/en.d.ts +3 -0
- package/dist/i18n/locales/en.d.ts.map +1 -0
- package/dist/i18n/locales/en.js +1 -0
- package/dist/i18n/locales/zh-Hans.d.ts +3 -0
- package/dist/i18n/locales/zh-Hans.d.ts.map +1 -0
- package/dist/i18n/locales/zh-Hans.js +1 -0
- package/dist/i18n/locales/zh-Hant.d.ts +3 -0
- package/dist/i18n/locales/zh-Hant.d.ts.map +1 -0
- package/dist/i18n/locales/zh-Hant.js +1 -0
- package/dist/i18n/locales.d.ts +11 -0
- package/dist/i18n/locales.d.ts.map +1 -0
- package/dist/i18n/locales.js +13 -0
- package/dist/i18n/middleware.d.ts +24 -0
- package/dist/i18n/middleware.d.ts.map +1 -0
- package/dist/i18n/middleware.js +41 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +1 -28
- package/dist/lib/assets.d.ts +19 -0
- package/dist/lib/assets.d.ts.map +1 -0
- package/dist/lib/assets.js +33 -0
- package/dist/lib/constants.d.ts +36 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +50 -0
- package/{src/lib/image.ts → dist/lib/image.d.ts} +13 -47
- package/dist/lib/image.d.ts.map +1 -0
- package/dist/lib/image.js +77 -0
- package/{src/lib/index.ts → dist/lib/index.d.ts} +1 -1
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +7 -0
- package/dist/lib/markdown.d.ts +60 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/{src/lib/markdown.ts → dist/lib/markdown.js} +16 -26
- package/dist/lib/schemas.d.ts +113 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +71 -0
- package/dist/lib/sqid.d.ts +60 -0
- package/dist/lib/sqid.d.ts.map +1 -0
- package/{src/lib/sqid.ts → dist/lib/sqid.js} +15 -22
- package/dist/lib/sse.d.ts +95 -0
- package/dist/lib/sse.d.ts.map +1 -0
- package/dist/lib/sse.js +81 -0
- package/dist/lib/time.d.ts +90 -0
- package/dist/lib/time.d.ts.map +1 -0
- package/{src/lib/time.ts → dist/lib/time.js} +20 -33
- package/{src/lib/url.ts → dist/lib/url.d.ts} +5 -30
- package/dist/lib/url.d.ts.map +1 -0
- package/dist/lib/url.js +89 -0
- package/dist/middleware/auth.d.ts +24 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +52 -0
- package/dist/routes/api/posts.d.ts +13 -0
- package/dist/routes/api/posts.d.ts.map +1 -0
- package/dist/routes/api/posts.js +124 -0
- package/dist/routes/api/search.d.ts +13 -0
- package/dist/routes/api/search.d.ts.map +1 -0
- package/dist/routes/api/search.js +49 -0
- package/dist/routes/api/upload.d.ts +16 -0
- package/dist/routes/api/upload.d.ts.map +1 -0
- package/dist/routes/api/upload.js +227 -0
- package/dist/routes/dash/collections.d.ts +13 -0
- package/dist/routes/dash/collections.d.ts.map +1 -0
- package/dist/routes/dash/collections.js +512 -0
- package/dist/routes/dash/index.d.ts +15 -0
- package/dist/routes/dash/index.d.ts.map +1 -0
- package/dist/routes/dash/index.js +117 -0
- package/dist/routes/dash/media.d.ts +16 -0
- package/dist/routes/dash/media.d.ts.map +1 -0
- package/dist/routes/dash/media.js +589 -0
- package/dist/routes/dash/pages.d.ts +15 -0
- package/dist/routes/dash/pages.d.ts.map +1 -0
- package/dist/routes/dash/pages.js +290 -0
- package/dist/routes/dash/posts.d.ts +13 -0
- package/dist/routes/dash/posts.d.ts.map +1 -0
- package/dist/routes/dash/posts.js +226 -0
- package/dist/routes/dash/redirects.d.ts +13 -0
- package/dist/routes/dash/redirects.d.ts.map +1 -0
- package/dist/routes/dash/redirects.js +237 -0
- package/dist/routes/dash/settings.d.ts +13 -0
- package/dist/routes/dash/settings.d.ts.map +1 -0
- package/dist/routes/dash/settings.js +154 -0
- package/dist/routes/feed/rss.d.ts +13 -0
- package/dist/routes/feed/rss.d.ts.map +1 -0
- package/dist/routes/feed/rss.js +95 -0
- package/dist/routes/feed/sitemap.d.ts +13 -0
- package/dist/routes/feed/sitemap.d.ts.map +1 -0
- package/dist/routes/feed/sitemap.js +59 -0
- package/dist/routes/pages/archive.d.ts +15 -0
- package/dist/routes/pages/archive.d.ts.map +1 -0
- package/dist/routes/pages/archive.js +255 -0
- package/dist/routes/pages/collection.d.ts +13 -0
- package/dist/routes/pages/collection.d.ts.map +1 -0
- package/dist/routes/pages/collection.js +93 -0
- package/dist/routes/pages/home.d.ts +13 -0
- package/dist/routes/pages/home.d.ts.map +1 -0
- package/dist/routes/pages/home.js +122 -0
- package/dist/routes/pages/page.d.ts +15 -0
- package/dist/routes/pages/page.d.ts.map +1 -0
- package/dist/routes/pages/page.js +69 -0
- package/dist/routes/pages/post.d.ts +13 -0
- package/dist/routes/pages/post.d.ts.map +1 -0
- package/dist/routes/pages/post.js +90 -0
- package/dist/routes/pages/search.d.ts +13 -0
- package/dist/routes/pages/search.d.ts.map +1 -0
- package/dist/routes/pages/search.js +180 -0
- package/dist/services/collection.d.ts +31 -0
- package/dist/services/collection.d.ts.map +1 -0
- package/dist/services/collection.js +108 -0
- package/dist/services/index.d.ts +28 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +20 -0
- package/dist/services/media.d.ts +27 -0
- package/dist/services/media.d.ts.map +1 -0
- package/dist/services/media.js +62 -0
- package/dist/services/post.d.ts +31 -0
- package/dist/services/post.d.ts.map +1 -0
- package/dist/services/post.js +191 -0
- package/dist/services/redirect.d.ts +15 -0
- package/dist/services/redirect.d.ts.map +1 -0
- package/dist/services/redirect.js +48 -0
- package/dist/services/search.d.ts +26 -0
- package/dist/services/search.d.ts.map +1 -0
- package/dist/services/search.js +61 -0
- package/dist/services/settings.d.ts +17 -0
- package/dist/services/settings.d.ts.map +1 -0
- package/dist/services/settings.js +65 -0
- package/dist/theme/components/ActionButtons.d.ts +43 -0
- package/dist/theme/components/ActionButtons.d.ts.map +1 -0
- package/dist/theme/components/ActionButtons.js +50 -0
- package/dist/theme/components/CrudPageHeader.d.ts +23 -0
- package/dist/theme/components/CrudPageHeader.d.ts.map +1 -0
- package/dist/theme/components/CrudPageHeader.js +22 -0
- package/dist/theme/components/DangerZone.d.ts +36 -0
- package/dist/theme/components/DangerZone.d.ts.map +1 -0
- package/dist/theme/components/DangerZone.js +39 -0
- package/dist/theme/components/EmptyState.d.ts +27 -0
- package/dist/theme/components/EmptyState.d.ts.map +1 -0
- package/dist/theme/components/EmptyState.js +27 -0
- package/dist/theme/components/ListItemRow.d.ts +15 -0
- package/dist/theme/components/ListItemRow.d.ts.map +1 -0
- package/dist/theme/components/ListItemRow.js +21 -0
- package/dist/theme/components/PageForm.d.ts +14 -0
- package/dist/theme/components/PageForm.d.ts.map +1 -0
- package/dist/theme/components/PageForm.js +173 -0
- package/dist/theme/components/Pagination.d.ts +46 -0
- package/dist/theme/components/Pagination.d.ts.map +1 -0
- package/dist/theme/components/Pagination.js +159 -0
- package/dist/theme/components/PostForm.d.ts +12 -0
- package/dist/theme/components/PostForm.d.ts.map +1 -0
- package/dist/theme/components/PostForm.js +230 -0
- package/dist/theme/components/PostList.d.ts +10 -0
- package/dist/theme/components/PostList.d.ts.map +1 -0
- package/dist/theme/components/PostList.js +73 -0
- package/dist/theme/components/ThreadView.d.ts +15 -0
- package/dist/theme/components/ThreadView.d.ts.map +1 -0
- package/dist/theme/components/ThreadView.js +111 -0
- package/dist/theme/components/TypeBadge.d.ts +12 -0
- package/dist/theme/components/TypeBadge.d.ts.map +1 -0
- package/dist/theme/components/TypeBadge.js +39 -0
- package/dist/theme/components/VisibilityBadge.d.ts +12 -0
- package/dist/theme/components/VisibilityBadge.d.ts.map +1 -0
- package/dist/theme/components/VisibilityBadge.js +37 -0
- package/{src/theme/components/index.ts → dist/theme/components/index.d.ts} +1 -0
- package/dist/theme/components/index.d.ts.map +1 -0
- package/dist/theme/components/index.js +12 -0
- package/dist/theme/index.d.ts +21 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/{src/theme/index.ts → dist/theme/index.js} +1 -4
- package/dist/theme/layouts/BaseLayout.d.ts +16 -0
- package/dist/theme/layouts/BaseLayout.d.ts.map +1 -0
- package/dist/theme/layouts/BaseLayout.js +58 -0
- package/dist/theme/layouts/DashLayout.d.ts +15 -0
- package/dist/theme/layouts/DashLayout.d.ts.map +1 -0
- package/dist/theme/layouts/DashLayout.js +139 -0
- package/{src/theme/layouts/index.ts → dist/theme/layouts/index.d.ts} +1 -0
- package/dist/theme/layouts/index.d.ts.map +1 -0
- package/dist/theme/layouts/index.js +2 -0
- package/dist/theme/styles/main.css +2 -0
- package/dist/types.d.ts +179 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/package.json +26 -26
- package/drizzle.config.ts +0 -10
- package/lingui.config.ts +0 -16
- package/src/app.tsx +0 -377
- package/src/assets/datastar.min.js +0 -8
- package/src/auth.ts +0 -38
- package/src/db/index.ts +0 -14
- package/src/db/migrations/0000_solid_moon_knight.sql +0 -118
- package/src/db/migrations/0001_add_search_fts.sql +0 -40
- package/src/db/migrations/0002_collection_path.sql +0 -2
- package/src/db/migrations/0003_collection_path_nullable.sql +0 -21
- package/src/db/migrations/0004_media_uuid.sql +0 -35
- package/src/db/migrations/meta/0000_snapshot.json +0 -784
- package/src/db/migrations/meta/_journal.json +0 -41
- package/src/db/schema.ts +0 -159
- package/src/i18n/EXAMPLES.md +0 -235
- package/src/i18n/README.md +0 -296
- package/src/i18n/context.tsx +0 -101
- package/src/i18n/detect.ts +0 -100
- package/src/i18n/locales/en.po +0 -875
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/zh-Hans.po +0 -875
- package/src/i18n/locales/zh-Hans.ts +0 -1
- package/src/i18n/locales/zh-Hant.po +0 -875
- package/src/i18n/locales/zh-Hant.ts +0 -1
- package/src/i18n/locales.ts +0 -14
- package/src/i18n/middleware.ts +0 -59
- package/src/lib/assets.ts +0 -47
- package/src/lib/constants.ts +0 -67
- package/src/lib/schemas.ts +0 -92
- package/src/lib/sse.ts +0 -152
- package/src/middleware/auth.ts +0 -59
- package/src/routes/api/posts.ts +0 -127
- package/src/routes/api/search.ts +0 -53
- package/src/routes/api/upload.ts +0 -240
- package/src/routes/dash/collections.tsx +0 -341
- package/src/routes/dash/index.tsx +0 -89
- package/src/routes/dash/media.tsx +0 -551
- package/src/routes/dash/pages.tsx +0 -245
- package/src/routes/dash/posts.tsx +0 -202
- package/src/routes/dash/redirects.tsx +0 -155
- package/src/routes/dash/settings.tsx +0 -93
- package/src/routes/feed/rss.ts +0 -119
- package/src/routes/feed/sitemap.ts +0 -75
- package/src/routes/pages/archive.tsx +0 -223
- package/src/routes/pages/collection.tsx +0 -79
- package/src/routes/pages/home.tsx +0 -93
- package/src/routes/pages/page.tsx +0 -64
- package/src/routes/pages/post.tsx +0 -81
- package/src/routes/pages/search.tsx +0 -162
- package/src/services/collection.ts +0 -180
- package/src/services/index.ts +0 -40
- package/src/services/media.ts +0 -97
- package/src/services/post.ts +0 -279
- package/src/services/redirect.ts +0 -74
- package/src/services/search.ts +0 -117
- package/src/services/settings.ts +0 -76
- package/src/theme/components/ActionButtons.tsx +0 -98
- package/src/theme/components/CrudPageHeader.tsx +0 -48
- package/src/theme/components/DangerZone.tsx +0 -77
- package/src/theme/components/EmptyState.tsx +0 -56
- package/src/theme/components/ListItemRow.tsx +0 -24
- package/src/theme/components/PageForm.tsx +0 -114
- package/src/theme/components/Pagination.tsx +0 -196
- package/src/theme/components/PostForm.tsx +0 -122
- package/src/theme/components/PostList.tsx +0 -68
- package/src/theme/components/ThreadView.tsx +0 -118
- package/src/theme/components/TypeBadge.tsx +0 -28
- package/src/theme/components/VisibilityBadge.tsx +0 -33
- package/src/theme/layouts/BaseLayout.tsx +0 -49
- package/src/theme/layouts/DashLayout.tsx +0 -108
- package/src/theme/styles/main.css +0 -52
- package/src/types.ts +0 -222
- package/tsconfig.json +0 -16
- package/vite.config.ts +0 -82
- 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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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"}
|
package/dist/lib/sse.js
ADDED
|
@@ -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
|
+
}
|