@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.
- package/bin/commands/export.js +1 -1
- package/bin/commands/import-site.js +529 -0
- package/bin/commands/reset-password.js +3 -2
- package/dist/client/assets/heic-to-DIRPI3VF.js +1 -0
- package/dist/client/assets/url-FWFqPJPb.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +4012 -3276
- package/dist/index.js +10285 -5809
- package/package.json +11 -3
- package/src/__tests__/helpers/app.ts +9 -9
- package/src/__tests__/helpers/db.ts +91 -93
- package/src/app.tsx +157 -27
- package/src/auth.ts +20 -2
- package/src/client/archive-nav.js +187 -0
- package/src/client/audio-player.ts +478 -0
- package/src/client/audio-processor.ts +84 -0
- package/src/client/avatar-upload.ts +3 -2
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +645 -49
- package/src/client/components/__tests__/jant-compose-editor.test.ts +208 -16
- package/src/client/components/__tests__/jant-post-form.test.ts +19 -9
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +7 -9
- package/src/client/components/compose-types.ts +101 -4
- package/src/client/components/jant-collection-form.ts +43 -7
- package/src/client/components/jant-collection-sidebar.ts +88 -84
- package/src/client/components/jant-compose-dialog.ts +1655 -219
- package/src/client/components/jant-compose-editor.ts +732 -168
- package/src/client/components/jant-compose-fullscreen.ts +23 -78
- package/src/client/components/jant-media-lightbox.ts +2 -0
- package/src/client/components/jant-nav-manager.ts +24 -284
- package/src/client/components/jant-post-form.ts +89 -9
- package/src/client/components/jant-post-menu.ts +1019 -0
- package/src/client/components/jant-settings-avatar.ts +3 -3
- package/src/client/components/jant-settings-general.ts +38 -4
- package/src/client/components/jant-text-preview.ts +232 -0
- package/src/client/components/nav-manager-types.ts +4 -19
- package/src/client/components/post-form-template.ts +107 -12
- package/src/client/components/post-form-types.ts +11 -4
- package/src/client/compose-bridge.ts +410 -109
- package/src/client/image-processor.ts +26 -8
- package/src/client/media-metadata.ts +247 -0
- package/src/client/multipart-upload.ts +160 -0
- package/src/client/post-form-bridge.ts +52 -1
- package/src/client/settings-bridge.ts +0 -12
- package/src/client/thread-context.ts +140 -0
- package/src/client/tiptap/create-editor.ts +46 -0
- package/src/client/tiptap/extensions.ts +5 -0
- package/src/client/tiptap/image-node.ts +2 -8
- package/src/client/tiptap/paste-image.ts +2 -13
- package/src/client/tiptap/slash-commands.ts +173 -63
- package/src/client/toast.ts +101 -3
- package/src/client/types/sortablejs.d.ts +15 -0
- package/src/client/upload-with-metadata.ts +54 -0
- package/src/client/video-processor.ts +207 -0
- package/src/client.ts +5 -2
- package/src/db/__tests__/migrations.test.ts +118 -0
- package/src/db/index.ts +52 -0
- package/src/db/migrations/0000_baseline.sql +269 -0
- package/src/db/migrations/0001_fts_setup.sql +31 -0
- package/src/db/migrations/meta/0000_snapshot.json +703 -119
- package/src/db/migrations/meta/0001_snapshot.json +1337 -0
- package/src/db/migrations/meta/_journal.json +4 -39
- package/src/db/schema.ts +409 -145
- package/src/i18n/__tests__/detect.test.ts +115 -0
- package/src/i18n/context.tsx +2 -2
- package/src/i18n/detect.ts +85 -1
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/index.ts +2 -1
- package/src/i18n/locales/en.po +487 -1217
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +613 -996
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +624 -1007
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +6 -0
- package/src/index.ts +5 -7
- package/src/lib/__tests__/blurhash-placeholder.test.ts +75 -0
- package/src/lib/__tests__/constants.test.ts +0 -1
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +358 -0
- package/src/lib/__tests__/nanoid.test.ts +26 -0
- package/src/lib/__tests__/schemas.test.ts +181 -63
- package/src/lib/__tests__/slug.test.ts +126 -0
- package/src/lib/__tests__/sse.test.ts +6 -6
- package/src/lib/__tests__/summary.test.ts +264 -0
- package/src/lib/__tests__/theme.test.ts +1 -1
- package/src/lib/__tests__/timeline.test.ts +33 -30
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +346 -0
- package/src/lib/__tests__/view.test.ts +141 -66
- package/src/lib/blurhash-placeholder.ts +102 -0
- package/src/lib/constants.ts +3 -1
- package/src/lib/emoji-catalog.ts +885 -68
- package/src/lib/errors.ts +11 -8
- package/src/lib/feed.ts +78 -32
- package/src/lib/html.ts +2 -1
- package/src/lib/icon-catalog.ts +5033 -1
- package/src/lib/icons.ts +3 -2
- package/src/lib/index.ts +0 -1
- package/src/lib/markdown-to-tiptap.ts +286 -0
- package/src/lib/media-helpers.ts +12 -3
- package/src/lib/nanoid.ts +29 -0
- package/src/lib/navigation.ts +1 -1
- package/src/lib/render.tsx +20 -2
- package/src/lib/resolve-config.ts +6 -2
- package/src/lib/schemas.ts +224 -55
- package/src/lib/search-snippet.ts +34 -0
- package/src/lib/slug.ts +96 -0
- package/src/lib/sse.ts +6 -6
- package/src/lib/storage.ts +115 -7
- package/src/lib/summary.ts +66 -0
- package/src/lib/theme.ts +11 -8
- package/src/lib/timeline.ts +74 -34
- package/src/lib/tiptap-render.ts +5 -10
- package/src/lib/tiptap-to-markdown.ts +305 -0
- package/src/lib/upload.ts +190 -29
- package/src/lib/url.ts +31 -0
- package/src/lib/view.ts +204 -37
- package/src/middleware/__tests__/auth.test.ts +191 -11
- package/src/middleware/__tests__/onboarding.test.ts +12 -10
- package/src/middleware/auth.ts +63 -9
- package/src/middleware/onboarding.ts +1 -1
- package/src/middleware/secure-headers.ts +40 -0
- package/src/preset.css +45 -2
- package/src/routes/__tests__/compose.test.ts +17 -24
- package/src/routes/api/__tests__/collections.test.ts +109 -61
- package/src/routes/api/__tests__/nav-items.test.ts +46 -29
- package/src/routes/api/__tests__/posts.test.ts +132 -68
- package/src/routes/api/__tests__/search.test.ts +15 -2
- package/src/routes/api/__tests__/upload-multipart.test.ts +534 -0
- package/src/routes/api/collections.ts +51 -42
- package/src/routes/api/custom-urls.ts +80 -0
- package/src/routes/api/export.ts +31 -0
- package/src/routes/api/nav-items.ts +23 -19
- package/src/routes/api/posts.ts +43 -39
- package/src/routes/api/search.ts +3 -4
- package/src/routes/api/upload-multipart.ts +245 -0
- package/src/routes/api/upload.ts +85 -19
- package/src/routes/auth/__tests__/setup.test.ts +20 -60
- package/src/routes/auth/setup.tsx +26 -33
- package/src/routes/auth/signin.tsx +3 -7
- package/src/routes/compose.tsx +10 -55
- package/src/routes/dash/__tests__/settings-avatar.test.ts +1 -1
- package/src/routes/dash/custom-urls.tsx +414 -0
- package/src/routes/dash/settings.tsx +304 -232
- package/src/routes/feed/__tests__/rss.test.ts +27 -28
- package/src/routes/feed/rss.ts +6 -4
- package/src/routes/feed/sitemap.ts +2 -12
- package/src/routes/pages/__tests__/collections.test.ts +5 -6
- package/src/routes/pages/__tests__/featured.test.ts +41 -22
- package/src/routes/pages/archive.tsx +175 -39
- package/src/routes/pages/collection.tsx +22 -10
- package/src/routes/pages/collections.tsx +3 -3
- package/src/routes/pages/featured.tsx +28 -4
- package/src/routes/pages/home.tsx +16 -15
- package/src/routes/pages/latest.tsx +1 -11
- package/src/routes/pages/new.tsx +39 -0
- package/src/routes/pages/page.tsx +95 -49
- package/src/routes/pages/search.tsx +1 -1
- package/src/services/__tests__/api-token.test.ts +135 -0
- package/src/services/__tests__/collection.test.ts +275 -227
- package/src/services/__tests__/custom-url.test.ts +213 -0
- package/src/services/__tests__/media.test.ts +162 -22
- package/src/services/__tests__/navigation.test.ts +109 -68
- package/src/services/__tests__/post-timeline.test.ts +205 -32
- package/src/services/__tests__/post.test.ts +713 -234
- package/src/services/__tests__/search.test.ts +67 -10
- package/src/services/api-token.ts +166 -0
- package/src/services/auth.ts +17 -2
- package/src/services/collection.ts +397 -131
- package/src/services/custom-url.ts +188 -0
- package/src/services/export.ts +802 -0
- package/src/services/index.ts +26 -19
- package/src/services/media.ts +100 -22
- package/src/services/navigation.ts +158 -47
- package/src/services/path.ts +339 -0
- package/src/services/post.ts +687 -154
- package/src/services/search.ts +160 -75
- package/src/styles/components.css +58 -7
- package/src/styles/tokens.css +84 -6
- package/src/styles/ui.css +2964 -457
- package/src/types/bindings.ts +4 -1
- package/src/types/config.ts +12 -4
- package/src/types/constants.ts +15 -3
- package/src/types/entities.ts +74 -35
- package/src/types/operations.ts +11 -24
- package/src/types/props.ts +51 -16
- package/src/types/views.ts +45 -22
- package/src/ui/color-themes.ts +133 -23
- package/src/ui/compose/ComposeDialog.tsx +239 -17
- package/src/ui/compose/ComposePrompt.tsx +1 -1
- package/src/ui/dash/CrudPageHeader.tsx +1 -1
- package/src/ui/dash/ListItemRow.tsx +1 -1
- package/src/ui/dash/StatusBadge.tsx +3 -1
- package/src/ui/dash/appearance/AdvancedContent.tsx +22 -1
- package/src/ui/dash/appearance/ColorThemeContent.tsx +22 -2
- package/src/ui/dash/appearance/FontThemeContent.tsx +1 -1
- package/src/ui/dash/appearance/NavigationContent.tsx +5 -45
- package/src/ui/dash/index.ts +0 -3
- package/src/ui/dash/settings/AccountContent.tsx +3 -57
- package/src/ui/dash/settings/AccountMenuContent.tsx +147 -0
- package/src/ui/dash/settings/ApiTokensContent.tsx +232 -0
- package/src/ui/dash/settings/AvatarContent.tsx +8 -0
- package/src/ui/dash/settings/SessionsContent.tsx +159 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +45 -15
- package/src/ui/feed/LinkCard.tsx +89 -40
- package/src/ui/feed/NoteCard.tsx +39 -25
- package/src/ui/feed/PostStatusBadges.tsx +67 -0
- package/src/ui/feed/QuoteCard.tsx +38 -23
- package/src/ui/feed/ThreadPreview.tsx +90 -26
- package/src/ui/feed/TimelineFeed.tsx +3 -2
- package/src/ui/feed/TimelineItem.tsx +15 -6
- package/src/ui/feed/__tests__/thread-preview.test.ts +107 -0
- package/src/ui/feed/thread-preview-state.ts +61 -0
- package/src/ui/font-themes.ts +2 -2
- package/src/ui/layouts/BaseLayout.tsx +2 -2
- package/src/ui/layouts/SiteLayout.tsx +105 -92
- package/src/ui/pages/ArchivePage.tsx +923 -98
- package/src/ui/pages/ComposePage.tsx +54 -0
- package/src/ui/pages/PostPage.tsx +30 -45
- package/src/ui/pages/SearchPage.tsx +181 -37
- package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
- package/src/ui/shared/CollectionsSidebar.tsx +47 -37
- package/src/ui/shared/MediaGallery.tsx +445 -149
- package/src/ui/shared/PostFooter.tsx +204 -0
- package/src/ui/shared/StarRating.tsx +27 -0
- package/src/ui/shared/__tests__/format-chars.test.ts +35 -0
- package/src/ui/shared/index.ts +0 -1
- package/dist/client/assets/url-8Dj-5CLW.js +0 -1
- package/src/client/media-upload.ts +0 -161
- package/src/client/page-slug-bridge.ts +0 -42
- package/src/db/migrations/0000_square_wallflower.sql +0 -118
- package/src/db/migrations/0001_add_search_fts.sql +0 -34
- package/src/db/migrations/0002_add_media_attachments.sql +0 -3
- package/src/db/migrations/0003_add_navigation_links.sql +0 -8
- package/src/db/migrations/0004_add_storage_provider.sql +0 -3
- package/src/db/migrations/0005_v2_schema_migration.sql +0 -268
- package/src/db/migrations/0006_rename_slug_to_path.sql +0 -5
- package/src/db/migrations/0007_post_collections_m2m.sql +0 -94
- package/src/db/migrations/0008_add_collection_dividers.sql +0 -8
- package/src/db/migrations/0009_drop_collection_show_divider.sql +0 -2
- package/src/db/migrations/0010_add_performance_indexes.sql +0 -16
- package/src/db/migrations/0011_add_path_registry.sql +0 -23
- package/src/db/migrations/0012_add_tiptap_columns.sql +0 -2
- package/src/db/migrations/0013_replace_featured_with_visibility.sql +0 -8
- package/src/db/migrations/meta/0003_snapshot.json +0 -821
- package/src/lib/__tests__/sqid.test.ts +0 -65
- package/src/lib/sqid.ts +0 -79
- package/src/routes/api/__tests__/pages.test.ts +0 -218
- package/src/routes/api/pages.ts +0 -73
- package/src/routes/dash/__tests__/pages.test.ts +0 -226
- package/src/routes/dash/index.tsx +0 -109
- package/src/routes/dash/media.tsx +0 -135
- package/src/routes/dash/pages.tsx +0 -245
- package/src/routes/dash/posts.tsx +0 -338
- package/src/routes/dash/redirects.tsx +0 -263
- package/src/routes/pages/post.tsx +0 -59
- package/src/services/__tests__/page.test.ts +0 -298
- package/src/services/__tests__/path-registry.test.ts +0 -165
- package/src/services/__tests__/redirect.test.ts +0 -159
- package/src/services/page.ts +0 -216
- package/src/services/path-registry.ts +0 -160
- package/src/services/redirect.ts +0 -97
- package/src/ui/dash/PageForm.tsx +0 -187
- package/src/ui/dash/PostList.tsx +0 -95
- package/src/ui/dash/media/MediaListContent.tsx +0 -206
- package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
- package/src/ui/dash/pages/PagesContent.tsx +0 -75
- package/src/ui/dash/posts/PostForm.tsx +0 -260
- package/src/ui/layouts/DashLayout.tsx +0 -247
- package/src/ui/pages/SinglePage.tsx +0 -23
- package/src/ui/shared/ThreadView.tsx +0 -136
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { createTestApp } from "../../../__tests__/helpers/app.js";
|
|
3
3
|
import { postsApiRoutes } from "../posts.js";
|
|
4
|
-
import * as sqid from "../../../lib/sqid.js";
|
|
5
4
|
|
|
6
5
|
describe("Posts API Routes", () => {
|
|
7
6
|
describe("GET /api/posts", () => {
|
|
7
|
+
it("returns 401 when not authenticated", async () => {
|
|
8
|
+
const { app } = createTestApp({ authenticated: false });
|
|
9
|
+
app.route("/api/posts", postsApiRoutes);
|
|
10
|
+
|
|
11
|
+
const res = await app.request("/api/posts");
|
|
12
|
+
expect(res.status).toBe(401);
|
|
13
|
+
});
|
|
14
|
+
|
|
8
15
|
it("returns empty list when no posts exist", async () => {
|
|
9
|
-
const { app } = createTestApp();
|
|
16
|
+
const { app } = createTestApp({ authenticated: true });
|
|
10
17
|
app.route("/api/posts", postsApiRoutes);
|
|
11
18
|
|
|
12
19
|
const res = await app.request("/api/posts");
|
|
@@ -17,30 +24,30 @@ describe("Posts API Routes", () => {
|
|
|
17
24
|
expect(body.nextCursor).toBeNull();
|
|
18
25
|
});
|
|
19
26
|
|
|
20
|
-
it("returns posts with
|
|
21
|
-
const { app, services } = createTestApp();
|
|
27
|
+
it("returns posts with IDs", async () => {
|
|
28
|
+
const { app, services } = createTestApp({ authenticated: true });
|
|
22
29
|
app.route("/api/posts", postsApiRoutes);
|
|
23
30
|
|
|
24
31
|
await services.posts.create({
|
|
25
32
|
format: "note",
|
|
26
|
-
|
|
33
|
+
bodyMarkdown: "Hello world",
|
|
27
34
|
});
|
|
28
35
|
|
|
29
36
|
const res = await app.request("/api/posts");
|
|
30
37
|
const body = await res.json();
|
|
31
38
|
|
|
32
39
|
expect(body.posts).toHaveLength(1);
|
|
33
|
-
expect(body.posts[0].
|
|
34
|
-
expect(body.posts[0].
|
|
40
|
+
expect(body.posts[0].bodyText).toBe("Hello world");
|
|
41
|
+
expect(body.posts[0].id).toBeTruthy();
|
|
35
42
|
});
|
|
36
43
|
|
|
37
44
|
it("includes mediaAttachments in list response", async () => {
|
|
38
|
-
const { app, services } = createTestApp();
|
|
45
|
+
const { app, services } = createTestApp({ authenticated: true });
|
|
39
46
|
app.route("/api/posts", postsApiRoutes);
|
|
40
47
|
|
|
41
48
|
const post = await services.posts.create({
|
|
42
49
|
format: "note",
|
|
43
|
-
|
|
50
|
+
bodyMarkdown: "with media",
|
|
44
51
|
});
|
|
45
52
|
|
|
46
53
|
const media = await services.media.create({
|
|
@@ -63,20 +70,20 @@ describe("Posts API Routes", () => {
|
|
|
63
70
|
expect(body.posts[0].mediaAttachments[0].mimeType).toBe("image/jpeg");
|
|
64
71
|
expect(body.posts[0].mediaAttachments[0].url).toBeTruthy();
|
|
65
72
|
expect(body.posts[0].mediaAttachments[0].previewUrl).toBeTruthy();
|
|
66
|
-
expect(body.posts[0].mediaAttachments[0].position).toBe(
|
|
73
|
+
expect(body.posts[0].mediaAttachments[0].position).toBe("a0");
|
|
67
74
|
});
|
|
68
75
|
|
|
69
76
|
it("filters by status", async () => {
|
|
70
|
-
const { app, services } = createTestApp();
|
|
77
|
+
const { app, services } = createTestApp({ authenticated: true });
|
|
71
78
|
app.route("/api/posts", postsApiRoutes);
|
|
72
79
|
|
|
73
80
|
await services.posts.create({
|
|
74
81
|
format: "note",
|
|
75
|
-
|
|
82
|
+
bodyMarkdown: "published post",
|
|
76
83
|
});
|
|
77
84
|
await services.posts.create({
|
|
78
85
|
format: "note",
|
|
79
|
-
|
|
86
|
+
bodyMarkdown: "draft post",
|
|
80
87
|
status: "draft",
|
|
81
88
|
});
|
|
82
89
|
|
|
@@ -88,13 +95,13 @@ describe("Posts API Routes", () => {
|
|
|
88
95
|
});
|
|
89
96
|
|
|
90
97
|
it("supports limit parameter", async () => {
|
|
91
|
-
const { app, services } = createTestApp();
|
|
98
|
+
const { app, services } = createTestApp({ authenticated: true });
|
|
92
99
|
app.route("/api/posts", postsApiRoutes);
|
|
93
100
|
|
|
94
101
|
for (let i = 0; i < 5; i++) {
|
|
95
102
|
await services.posts.create({
|
|
96
103
|
format: "note",
|
|
97
|
-
|
|
104
|
+
bodyMarkdown: `post ${i}`,
|
|
98
105
|
});
|
|
99
106
|
}
|
|
100
107
|
|
|
@@ -107,31 +114,41 @@ describe("Posts API Routes", () => {
|
|
|
107
114
|
});
|
|
108
115
|
|
|
109
116
|
describe("GET /api/posts/:id", () => {
|
|
110
|
-
it("returns
|
|
111
|
-
const { app, services } = createTestApp();
|
|
117
|
+
it("returns 401 when not authenticated", async () => {
|
|
118
|
+
const { app, services } = createTestApp({ authenticated: false });
|
|
112
119
|
app.route("/api/posts", postsApiRoutes);
|
|
113
120
|
|
|
114
121
|
const post = await services.posts.create({
|
|
115
122
|
format: "note",
|
|
116
|
-
|
|
123
|
+
bodyMarkdown: "test post",
|
|
117
124
|
});
|
|
118
|
-
const
|
|
125
|
+
const res = await app.request(`/api/posts/${post.id}`);
|
|
126
|
+
expect(res.status).toBe(401);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("returns a post by ID", async () => {
|
|
130
|
+
const { app, services } = createTestApp({ authenticated: true });
|
|
131
|
+
app.route("/api/posts", postsApiRoutes);
|
|
119
132
|
|
|
120
|
-
const
|
|
133
|
+
const post = await services.posts.create({
|
|
134
|
+
format: "note",
|
|
135
|
+
bodyMarkdown: "test post",
|
|
136
|
+
});
|
|
137
|
+
const res = await app.request(`/api/posts/${post.id}`);
|
|
121
138
|
expect(res.status).toBe(200);
|
|
122
139
|
|
|
123
140
|
const body = await res.json();
|
|
124
|
-
expect(body.
|
|
125
|
-
expect(body.
|
|
141
|
+
expect(body.bodyText).toBe("test post");
|
|
142
|
+
expect(body.id).toBe(post.id);
|
|
126
143
|
});
|
|
127
144
|
|
|
128
145
|
it("includes mediaAttachments in single post response", async () => {
|
|
129
|
-
const { app, services } = createTestApp();
|
|
146
|
+
const { app, services } = createTestApp({ authenticated: true });
|
|
130
147
|
app.route("/api/posts", postsApiRoutes);
|
|
131
148
|
|
|
132
149
|
const post = await services.posts.create({
|
|
133
150
|
format: "note",
|
|
134
|
-
|
|
151
|
+
bodyMarkdown: "with media",
|
|
135
152
|
});
|
|
136
153
|
|
|
137
154
|
const media = await services.media.create({
|
|
@@ -144,15 +161,15 @@ describe("Posts API Routes", () => {
|
|
|
144
161
|
|
|
145
162
|
await services.media.attachToPost(post.id, [media.id]);
|
|
146
163
|
|
|
147
|
-
const res = await app.request(`/api/posts/${
|
|
164
|
+
const res = await app.request(`/api/posts/${post.id}`);
|
|
148
165
|
const body = await res.json();
|
|
149
166
|
|
|
150
167
|
expect(body.mediaAttachments).toHaveLength(1);
|
|
151
168
|
expect(body.mediaAttachments[0].id).toBe(media.id);
|
|
152
169
|
});
|
|
153
170
|
|
|
154
|
-
it("returns 400 for invalid
|
|
155
|
-
const { app } = createTestApp();
|
|
171
|
+
it("returns 400 for invalid ID", async () => {
|
|
172
|
+
const { app } = createTestApp({ authenticated: true });
|
|
156
173
|
app.route("/api/posts", postsApiRoutes);
|
|
157
174
|
|
|
158
175
|
const res = await app.request("/api/posts/!!invalid!!");
|
|
@@ -160,10 +177,12 @@ describe("Posts API Routes", () => {
|
|
|
160
177
|
});
|
|
161
178
|
|
|
162
179
|
it("returns 404 for non-existent post", async () => {
|
|
163
|
-
const { app } = createTestApp();
|
|
180
|
+
const { app } = createTestApp({ authenticated: true });
|
|
164
181
|
app.route("/api/posts", postsApiRoutes);
|
|
165
182
|
|
|
166
|
-
const res = await app.request(
|
|
183
|
+
const res = await app.request(
|
|
184
|
+
"/api/posts/00000000-0000-0000-0000-000000009999",
|
|
185
|
+
);
|
|
167
186
|
expect(res.status).toBe(404);
|
|
168
187
|
});
|
|
169
188
|
});
|
|
@@ -178,7 +197,7 @@ describe("Posts API Routes", () => {
|
|
|
178
197
|
headers: { "Content-Type": "application/json" },
|
|
179
198
|
body: JSON.stringify({
|
|
180
199
|
format: "note",
|
|
181
|
-
|
|
200
|
+
bodyMarkdown: "test",
|
|
182
201
|
}),
|
|
183
202
|
});
|
|
184
203
|
|
|
@@ -194,18 +213,57 @@ describe("Posts API Routes", () => {
|
|
|
194
213
|
headers: { "Content-Type": "application/json" },
|
|
195
214
|
body: JSON.stringify({
|
|
196
215
|
format: "note",
|
|
197
|
-
|
|
216
|
+
bodyMarkdown: "Hello from API",
|
|
198
217
|
}),
|
|
199
218
|
});
|
|
200
219
|
|
|
201
220
|
expect(res.status).toBe(201);
|
|
202
221
|
|
|
203
222
|
const body = await res.json();
|
|
204
|
-
expect(body.
|
|
205
|
-
expect(body.
|
|
223
|
+
expect(body.bodyText).toBe("Hello from API");
|
|
224
|
+
expect(body.id).toBeTruthy();
|
|
206
225
|
expect(body.mediaAttachments).toEqual([]);
|
|
207
226
|
});
|
|
208
227
|
|
|
228
|
+
it("creates a post with bodyMarkdown", async () => {
|
|
229
|
+
const { app } = createTestApp({ authenticated: true });
|
|
230
|
+
app.route("/api/posts", postsApiRoutes);
|
|
231
|
+
|
|
232
|
+
const res = await app.request("/api/posts", {
|
|
233
|
+
method: "POST",
|
|
234
|
+
headers: { "Content-Type": "application/json" },
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
format: "note",
|
|
237
|
+
bodyMarkdown: "Hello **bold** world",
|
|
238
|
+
}),
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(res.status).toBe(201);
|
|
242
|
+
|
|
243
|
+
const body = await res.json();
|
|
244
|
+
expect(body.bodyText).toContain("Hello");
|
|
245
|
+
expect(body.bodyHtml).toContain("<strong>bold</strong>");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("returns 400 when both body and bodyMarkdown are provided", async () => {
|
|
249
|
+
const { app } = createTestApp({ authenticated: true });
|
|
250
|
+
app.route("/api/posts", postsApiRoutes);
|
|
251
|
+
|
|
252
|
+
const res = await app.request("/api/posts", {
|
|
253
|
+
method: "POST",
|
|
254
|
+
headers: { "Content-Type": "application/json" },
|
|
255
|
+
body: JSON.stringify({
|
|
256
|
+
format: "note",
|
|
257
|
+
body: '{"type":"doc","content":[]}',
|
|
258
|
+
bodyMarkdown: "Hello",
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
expect(res.status).toBe(400);
|
|
263
|
+
const body = await res.json();
|
|
264
|
+
expect(body.error).toContain("Provide either body or bodyMarkdown");
|
|
265
|
+
});
|
|
266
|
+
|
|
209
267
|
it("creates a post with mediaIds and attaches them", async () => {
|
|
210
268
|
const { app, services } = createTestApp({ authenticated: true });
|
|
211
269
|
app.route("/api/posts", postsApiRoutes);
|
|
@@ -230,7 +288,7 @@ describe("Posts API Routes", () => {
|
|
|
230
288
|
headers: { "Content-Type": "application/json" },
|
|
231
289
|
body: JSON.stringify({
|
|
232
290
|
format: "note",
|
|
233
|
-
|
|
291
|
+
bodyMarkdown: "with images",
|
|
234
292
|
mediaIds: [m1.id, m2.id],
|
|
235
293
|
}),
|
|
236
294
|
});
|
|
@@ -239,9 +297,9 @@ describe("Posts API Routes", () => {
|
|
|
239
297
|
const body = await res.json();
|
|
240
298
|
expect(body.mediaAttachments).toHaveLength(2);
|
|
241
299
|
expect(body.mediaAttachments[0].id).toBe(m1.id);
|
|
242
|
-
expect(body.mediaAttachments[0].position).toBe(
|
|
300
|
+
expect(body.mediaAttachments[0].position).toBe("a0");
|
|
243
301
|
expect(body.mediaAttachments[1].id).toBe(m2.id);
|
|
244
|
-
expect(body.mediaAttachments[1].position).toBe(
|
|
302
|
+
expect(body.mediaAttachments[1].position).toBe("a1");
|
|
245
303
|
});
|
|
246
304
|
|
|
247
305
|
it("returns 400 for invalid media IDs", async () => {
|
|
@@ -253,7 +311,7 @@ describe("Posts API Routes", () => {
|
|
|
253
311
|
headers: { "Content-Type": "application/json" },
|
|
254
312
|
body: JSON.stringify({
|
|
255
313
|
format: "note",
|
|
256
|
-
|
|
314
|
+
bodyMarkdown: "test",
|
|
257
315
|
mediaIds: ["nonexistent-id"],
|
|
258
316
|
}),
|
|
259
317
|
});
|
|
@@ -300,13 +358,13 @@ describe("Posts API Routes", () => {
|
|
|
300
358
|
|
|
301
359
|
const post = await services.posts.create({
|
|
302
360
|
format: "note",
|
|
303
|
-
|
|
361
|
+
bodyMarkdown: "original",
|
|
304
362
|
});
|
|
305
363
|
|
|
306
|
-
const res = await app.request(`/api/posts/${
|
|
364
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
307
365
|
method: "PUT",
|
|
308
366
|
headers: { "Content-Type": "application/json" },
|
|
309
|
-
body: JSON.stringify({
|
|
367
|
+
body: JSON.stringify({ bodyMarkdown: "updated" }),
|
|
310
368
|
});
|
|
311
369
|
|
|
312
370
|
expect(res.status).toBe(401);
|
|
@@ -318,18 +376,18 @@ describe("Posts API Routes", () => {
|
|
|
318
376
|
|
|
319
377
|
const post = await services.posts.create({
|
|
320
378
|
format: "note",
|
|
321
|
-
|
|
379
|
+
bodyMarkdown: "original",
|
|
322
380
|
});
|
|
323
381
|
|
|
324
|
-
const res = await app.request(`/api/posts/${
|
|
382
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
325
383
|
method: "PUT",
|
|
326
384
|
headers: { "Content-Type": "application/json" },
|
|
327
|
-
body: JSON.stringify({
|
|
385
|
+
body: JSON.stringify({ bodyMarkdown: "updated" }),
|
|
328
386
|
});
|
|
329
387
|
|
|
330
388
|
expect(res.status).toBe(200);
|
|
331
389
|
const body = await res.json();
|
|
332
|
-
expect(body.
|
|
390
|
+
expect(body.bodyText).toBe("updated");
|
|
333
391
|
expect(body.mediaAttachments).toEqual([]);
|
|
334
392
|
});
|
|
335
393
|
|
|
@@ -339,7 +397,7 @@ describe("Posts API Routes", () => {
|
|
|
339
397
|
|
|
340
398
|
const post = await services.posts.create({
|
|
341
399
|
format: "note",
|
|
342
|
-
|
|
400
|
+
bodyMarkdown: "test",
|
|
343
401
|
});
|
|
344
402
|
|
|
345
403
|
const m1 = await services.media.create({
|
|
@@ -360,7 +418,7 @@ describe("Posts API Routes", () => {
|
|
|
360
418
|
storageKey: "media/2025/01/b.jpg",
|
|
361
419
|
});
|
|
362
420
|
|
|
363
|
-
const res = await app.request(`/api/posts/${
|
|
421
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
364
422
|
method: "PUT",
|
|
365
423
|
headers: { "Content-Type": "application/json" },
|
|
366
424
|
body: JSON.stringify({ mediaIds: [m2.id] }),
|
|
@@ -378,7 +436,7 @@ describe("Posts API Routes", () => {
|
|
|
378
436
|
|
|
379
437
|
const post = await services.posts.create({
|
|
380
438
|
format: "note",
|
|
381
|
-
|
|
439
|
+
bodyMarkdown: "test",
|
|
382
440
|
});
|
|
383
441
|
|
|
384
442
|
const m1 = await services.media.create({
|
|
@@ -391,10 +449,10 @@ describe("Posts API Routes", () => {
|
|
|
391
449
|
|
|
392
450
|
await services.media.attachToPost(post.id, [m1.id]);
|
|
393
451
|
|
|
394
|
-
const res = await app.request(`/api/posts/${
|
|
452
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
395
453
|
method: "PUT",
|
|
396
454
|
headers: { "Content-Type": "application/json" },
|
|
397
|
-
body: JSON.stringify({
|
|
455
|
+
body: JSON.stringify({ bodyMarkdown: "updated content" }),
|
|
398
456
|
});
|
|
399
457
|
|
|
400
458
|
expect(res.status).toBe(200);
|
|
@@ -407,11 +465,14 @@ describe("Posts API Routes", () => {
|
|
|
407
465
|
const { app } = createTestApp({ authenticated: true });
|
|
408
466
|
app.route("/api/posts", postsApiRoutes);
|
|
409
467
|
|
|
410
|
-
const res = await app.request(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
468
|
+
const res = await app.request(
|
|
469
|
+
"/api/posts/00000000-0000-0000-0000-000000009999",
|
|
470
|
+
{
|
|
471
|
+
method: "PUT",
|
|
472
|
+
headers: { "Content-Type": "application/json" },
|
|
473
|
+
body: JSON.stringify({ bodyMarkdown: "test" }),
|
|
474
|
+
},
|
|
475
|
+
);
|
|
415
476
|
|
|
416
477
|
expect(res.status).toBe(404);
|
|
417
478
|
});
|
|
@@ -422,10 +483,10 @@ describe("Posts API Routes", () => {
|
|
|
422
483
|
|
|
423
484
|
const post = await services.posts.create({
|
|
424
485
|
format: "note",
|
|
425
|
-
|
|
486
|
+
bodyMarkdown: "test",
|
|
426
487
|
});
|
|
427
488
|
|
|
428
|
-
const res = await app.request(`/api/posts/${
|
|
489
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
429
490
|
method: "PUT",
|
|
430
491
|
headers: { "Content-Type": "application/json" },
|
|
431
492
|
body: JSON.stringify({ format: "invalid-type" }),
|
|
@@ -442,10 +503,10 @@ describe("Posts API Routes", () => {
|
|
|
442
503
|
|
|
443
504
|
const post = await services.posts.create({
|
|
444
505
|
format: "note",
|
|
445
|
-
|
|
506
|
+
bodyMarkdown: "test",
|
|
446
507
|
});
|
|
447
508
|
|
|
448
|
-
const res = await app.request(`/api/posts/${
|
|
509
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
449
510
|
method: "DELETE",
|
|
450
511
|
});
|
|
451
512
|
|
|
@@ -458,10 +519,10 @@ describe("Posts API Routes", () => {
|
|
|
458
519
|
|
|
459
520
|
const post = await services.posts.create({
|
|
460
521
|
format: "note",
|
|
461
|
-
|
|
522
|
+
bodyMarkdown: "to be deleted",
|
|
462
523
|
});
|
|
463
524
|
|
|
464
|
-
const res = await app.request(`/api/posts/${
|
|
525
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
465
526
|
method: "DELETE",
|
|
466
527
|
});
|
|
467
528
|
|
|
@@ -478,9 +539,12 @@ describe("Posts API Routes", () => {
|
|
|
478
539
|
const { app } = createTestApp({ authenticated: true });
|
|
479
540
|
app.route("/api/posts", postsApiRoutes);
|
|
480
541
|
|
|
481
|
-
const res = await app.request(
|
|
482
|
-
|
|
483
|
-
|
|
542
|
+
const res = await app.request(
|
|
543
|
+
"/api/posts/00000000-0000-0000-0000-000000009999",
|
|
544
|
+
{
|
|
545
|
+
method: "DELETE",
|
|
546
|
+
},
|
|
547
|
+
);
|
|
484
548
|
|
|
485
549
|
expect(res.status).toBe(404);
|
|
486
550
|
});
|
|
@@ -491,7 +555,7 @@ describe("Posts API Routes", () => {
|
|
|
491
555
|
|
|
492
556
|
const post = await services.posts.create({
|
|
493
557
|
format: "note",
|
|
494
|
-
|
|
558
|
+
bodyMarkdown: "with media",
|
|
495
559
|
});
|
|
496
560
|
|
|
497
561
|
const m1 = await services.media.create({
|
|
@@ -511,7 +575,7 @@ describe("Posts API Routes", () => {
|
|
|
511
575
|
|
|
512
576
|
await services.media.attachToPost(post.id, [m1.id, m2.id]);
|
|
513
577
|
|
|
514
|
-
const res = await app.request(`/api/posts/${
|
|
578
|
+
const res = await app.request(`/api/posts/${post.id}`, {
|
|
515
579
|
method: "DELETE",
|
|
516
580
|
});
|
|
517
581
|
|
|
@@ -528,11 +592,11 @@ describe("Posts API Routes", () => {
|
|
|
528
592
|
|
|
529
593
|
const root = await services.posts.create({
|
|
530
594
|
format: "note",
|
|
531
|
-
|
|
595
|
+
bodyMarkdown: "thread root",
|
|
532
596
|
});
|
|
533
597
|
const reply = await services.posts.create({
|
|
534
598
|
format: "note",
|
|
535
|
-
|
|
599
|
+
bodyMarkdown: "reply",
|
|
536
600
|
replyToId: root.id,
|
|
537
601
|
});
|
|
538
602
|
|
|
@@ -554,7 +618,7 @@ describe("Posts API Routes", () => {
|
|
|
554
618
|
await services.media.attachToPost(root.id, [rootMedia.id]);
|
|
555
619
|
await services.media.attachToPost(reply.id, [replyMedia.id]);
|
|
556
620
|
|
|
557
|
-
const res = await app.request(`/api/posts/${
|
|
621
|
+
const res = await app.request(`/api/posts/${root.id}`, {
|
|
558
622
|
method: "DELETE",
|
|
559
623
|
});
|
|
560
624
|
|
|
@@ -2,6 +2,19 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import { createTestApp } from "../../../__tests__/helpers/app.js";
|
|
3
3
|
import { searchApiRoutes } from "../search.js";
|
|
4
4
|
|
|
5
|
+
/** Wraps plain text in a minimal valid TipTap JSON document. */
|
|
6
|
+
function tiptapDoc(text: string): string {
|
|
7
|
+
return JSON.stringify({
|
|
8
|
+
type: "doc",
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: "paragraph",
|
|
12
|
+
content: [{ type: "text", text }],
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
describe("Search API Routes", () => {
|
|
6
19
|
it("returns 400 when query is missing", async () => {
|
|
7
20
|
const { app } = createTestApp({ fts: true });
|
|
@@ -40,7 +53,7 @@ describe("Search API Routes", () => {
|
|
|
40
53
|
|
|
41
54
|
await services.posts.create({
|
|
42
55
|
format: "note",
|
|
43
|
-
body: "Testing search functionality in jant",
|
|
56
|
+
body: tiptapDoc("Testing search functionality in jant"),
|
|
44
57
|
});
|
|
45
58
|
|
|
46
59
|
const res = await app.request("/api/search?q=jant");
|
|
@@ -50,7 +63,7 @@ describe("Search API Routes", () => {
|
|
|
50
63
|
expect(body.query).toBe("jant");
|
|
51
64
|
expect(body.results.length).toBeGreaterThanOrEqual(1);
|
|
52
65
|
expect(body.count).toBeGreaterThanOrEqual(1);
|
|
53
|
-
expect(body.results[0].url).toMatch(/^\/
|
|
66
|
+
expect(body.results[0].url).toMatch(/^\/[a-z0-9]/);
|
|
54
67
|
});
|
|
55
68
|
|
|
56
69
|
it("returns empty results for non-matching query", async () => {
|