@jant/core 0.6.14 → 0.6.15
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/import-site.js +81 -13
- package/bin/commands/migrate.js +55 -0
- package/bin/commands/posts/rebuild-html.js +184 -0
- package/bin/commands/site/snapshot/import.js +14 -10
- package/bin/lib/d1-query.js +23 -5
- package/bin/lib/migration-runner.js +29 -7
- package/bin/lib/node-database.js +32 -0
- package/bin/lib/site-snapshot.js +347 -46
- package/bin/lib/sql-export.js +1 -1
- package/bin/lib/thread-collection-migration.js +326 -0
- package/dist/app-BxCOR3Uc.js +6 -0
- package/dist/{app-2i7-WQHg.js → app-C-oi_t8W.js} +7408 -6343
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-B_eGKN5p.css +2 -0
- package/dist/client/_assets/{client-BvID5ucz.js → client-DxY5BFe8.js} +58 -37
- package/dist/client/_assets/client-auth-CakPESv9.js +5533 -0
- package/dist/{export-DWn149b7.js → export-CsFx6F_M.js} +1259 -125
- package/dist/{github-sync-Ckk0sJxK.js → github-sync-Cq1pzzOI.js} +1 -1
- package/dist/{github-sync-C0zyF6XS.js → github-sync-ppWXN3jb.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/node.js +5 -5
- package/package.json +11 -10
- package/src/__tests__/bin/posts-rebuild-html.test.ts +137 -0
- package/src/__tests__/dev-navigation-defaults.test.ts +22 -0
- package/src/__tests__/export-hugo-build.test.ts +63 -4
- package/src/__tests__/export-import-roundtrip.test.ts +6 -4
- package/src/__tests__/export-service.test.ts +142 -9
- package/src/__tests__/helpers/app.ts +3 -0
- package/src/__tests__/helpers/export-fixtures.ts +2 -0
- package/src/__tests__/import-site-command.test.ts +65 -0
- package/src/__tests__/vite-config.test.ts +45 -0
- package/src/app.tsx +16 -3
- package/src/client/__tests__/collection-created-notice.test.ts +36 -0
- package/src/client/__tests__/collection-form-bridge.test.ts +10 -1
- package/src/client/__tests__/collection-page-actions.test.ts +71 -2
- package/src/client/__tests__/compose-bridge.test.ts +64 -0
- package/src/client/__tests__/feed-video-player.test.ts +16 -1
- package/src/client/__tests__/footnote-rail.test.ts +472 -0
- package/src/client/__tests__/hydrate-partial.test.ts +3 -0
- package/src/client/__tests__/toast.test.ts +18 -1
- package/src/client/collection-created-notice.ts +66 -0
- package/src/client/collection-form-bridge.ts +17 -0
- package/src/client/collection-navigation.ts +44 -0
- package/src/client/collection-page-actions.ts +57 -1
- package/src/client/components/__tests__/jant-collection-directory.test.ts +155 -6
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +370 -5
- package/src/client/components/__tests__/jant-compose-editor.test.ts +97 -0
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +104 -0
- package/src/client/components/__tests__/jant-config-editor.test.ts +405 -0
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +121 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +538 -2
- package/src/client/components/__tests__/jant-post-form.test.ts +2 -2
- package/src/client/components/__tests__/jant-post-menu.test.ts +47 -5
- 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-manager-types.ts +9 -3
- package/src/client/components/compose-types.ts +8 -0
- package/src/client/components/jant-collection-directory.ts +165 -30
- package/src/client/components/jant-command-palette.ts +2 -1
- package/src/client/components/jant-compose-dialog.ts +85 -21
- package/src/client/components/jant-compose-editor.ts +13 -7
- package/src/client/components/jant-compose-fullscreen.ts +24 -4
- package/src/client/components/jant-config-editor.ts +722 -0
- package/src/client/components/jant-media-lightbox.ts +100 -18
- package/src/client/components/jant-nav-manager.ts +1283 -54
- package/src/client/components/jant-post-form.ts +3 -3
- package/src/client/components/jant-post-menu.ts +86 -74
- package/src/client/components/nav-manager-types.ts +51 -2
- package/src/client/components/post-form-types.ts +1 -1
- package/src/client/compose-bridge.ts +4 -1
- package/src/client/feed-video-player.ts +62 -2
- package/src/client/footnote-rail.ts +478 -0
- package/src/client/hydrate-partial.ts +2 -0
- package/src/client/media-lightbox-events.ts +63 -0
- package/src/client/site-header-fragment.ts +38 -0
- package/src/client/tiptap/__tests__/blockquote-editing.test.ts +201 -0
- package/src/client/tiptap/__tests__/clear-formatting.test.ts +168 -0
- package/src/client/tiptap/__tests__/list-editing.test.ts +447 -0
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +727 -4
- package/src/client/tiptap/__tests__/table-controls.test.ts +263 -0
- package/src/client/tiptap/__tests__/table-size-picker.test.ts +179 -0
- package/src/client/tiptap/bubble-menu.ts +88 -10
- package/src/client/tiptap/continuous-lists.ts +102 -0
- package/src/client/tiptap/create-editor.ts +3 -0
- package/src/client/tiptap/extensions.ts +14 -5
- package/src/client/tiptap/markdown-clipboard.ts +608 -11
- package/src/client/tiptap/slash-commands.ts +8 -1
- package/src/client/tiptap/structural-keymap.ts +211 -0
- package/src/client/tiptap/tab-indent.ts +8 -22
- package/src/client/tiptap/table-control-labels.ts +30 -0
- package/src/client/tiptap/table-controls.ts +537 -0
- package/src/client/tiptap/table-size-picker.ts +254 -0
- package/src/client/toast.ts +8 -6
- package/src/client-auth.ts +1 -0
- package/src/client.ts +1 -0
- package/src/db/__tests__/d1-query.test.ts +43 -1
- package/src/db/__tests__/migration-runner.test.ts +63 -0
- package/src/db/__tests__/migrations.test.ts +469 -0
- package/src/db/__tests__/thread-collection-migration.test.ts +364 -0
- package/src/db/migrations/0027_old_blue_blade.sql +39 -0
- package/src/db/migrations/0028_superb_post.sql +2 -0
- package/src/db/migrations/0029_boring_magma.sql +1 -0
- package/src/db/migrations/0030_typical_vivisector.sql +50 -0
- package/src/db/migrations/meta/0027_snapshot.json +2511 -0
- package/src/db/migrations/meta/0028_snapshot.json +2511 -0
- package/src/db/migrations/meta/0029_snapshot.json +2519 -0
- package/src/db/migrations/meta/0030_snapshot.json +2541 -0
- package/src/db/migrations/meta/_journal.json +28 -0
- package/src/db/migrations/pg/0025_moaning_epoch.sql +39 -0
- package/src/db/migrations/pg/0026_talented_killer_shrike.sql +2 -0
- package/src/db/migrations/pg/0027_short_cannonball.sql +1 -0
- package/src/db/migrations/pg/0028_perfect_sharon_carter.sql +27 -0
- package/src/db/migrations/pg/meta/0025_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0026_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0027_snapshot.json +3211 -0
- package/src/db/migrations/pg/meta/0028_snapshot.json +3248 -0
- package/src/db/migrations/pg/meta/_journal.json +28 -0
- package/src/db/pg/schema.ts +28 -20
- package/src/db/rehearsal-fixtures/demo-current.json +39 -4
- package/src/db/rehearsal-fixtures/demo-current.sql +121 -6
- package/src/db/schema.ts +28 -20
- package/src/i18n/__tests__/detect.test.ts +8 -2
- package/src/i18n/detect.ts +14 -5
- package/src/i18n/locales/public/en.po +133 -30
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +128 -25
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +128 -25
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +513 -10
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +515 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +515 -12
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/api-settings.test.ts +299 -0
- package/src/lib/__tests__/constants.test.ts +5 -0
- package/src/lib/__tests__/feed-policy.test.ts +25 -0
- package/src/lib/__tests__/footnotes-upgrade.test.ts +181 -0
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +61 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +32 -4
- package/src/lib/__tests__/markdown.test.ts +8 -6
- package/src/lib/__tests__/navigation.test.ts +59 -2
- package/src/lib/__tests__/post-body-html.test.ts +176 -0
- package/src/lib/__tests__/post-display.test.ts +64 -0
- package/src/lib/__tests__/resolve-config.test.ts +113 -1
- package/src/lib/__tests__/summary.test.ts +82 -0
- package/src/lib/__tests__/timeline.test.ts +109 -70
- package/src/lib/__tests__/timezones.test.ts +31 -3
- package/src/lib/__tests__/tiptap-render.test.ts +268 -9
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +6 -0
- package/src/lib/__tests__/view.test.ts +53 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +18 -1
- package/src/lib/api-settings.ts +239 -12
- package/src/lib/collection-sort.ts +7 -5
- package/src/lib/constants.ts +2 -0
- package/src/lib/feed-path.ts +25 -0
- package/src/lib/feed-policy.ts +64 -0
- package/src/lib/footnotes.ts +264 -0
- package/src/lib/github-sync-site-config.ts +2 -0
- package/src/lib/hugo-markdown.ts +10 -3
- package/src/lib/jant-branding.ts +1 -0
- package/src/lib/markdown-manager.ts +188 -5
- package/src/lib/markdown.ts +10 -3
- package/src/lib/navigation.ts +16 -3
- package/src/lib/post-body-html.ts +131 -0
- package/src/lib/post-display.ts +7 -1
- package/src/lib/render.tsx +9 -2
- package/src/lib/resolve-config.ts +100 -16
- package/src/lib/schemas.ts +133 -1
- package/src/lib/settings-paths.ts +5 -0
- package/src/lib/site-skill.ts +78 -0
- package/src/lib/summary.ts +16 -5
- package/src/lib/timeline.ts +98 -122
- package/src/lib/timezones.ts +54 -14
- package/src/lib/tiptap-render.ts +355 -81
- package/src/lib/view.ts +30 -20
- package/src/lib/worker-response-cache.ts +11 -18
- package/src/middleware/__tests__/public-content-access.test.ts +91 -0
- package/src/middleware/config.ts +2 -2
- package/src/middleware/public-content-access.ts +87 -0
- package/src/node/__tests__/cli-export.test.ts +14 -0
- package/src/node/__tests__/cli-migrate.test.ts +6 -0
- package/src/node/__tests__/cli-site-snapshot.test.ts +205 -2
- package/src/node/__tests__/cli-snapshot-meta.test.ts +100 -0
- package/src/node/index.ts +2 -1
- package/src/preset.css +16 -2
- package/src/routes/__tests__/compose.test.ts +51 -0
- package/src/routes/__tests__/site-skill.test.ts +112 -0
- package/src/routes/api/__tests__/collections.test.ts +91 -19
- package/src/routes/api/__tests__/mcp.test.ts +65 -1
- package/src/routes/api/__tests__/nav-items.test.ts +91 -0
- package/src/routes/api/__tests__/public-access.test.ts +81 -0
- package/src/routes/api/__tests__/settings.test.ts +217 -0
- package/src/routes/api/collections.ts +22 -26
- package/src/routes/api/export.ts +2 -0
- package/src/routes/api/internal/__tests__/post-body-html.test.ts +163 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +35 -2
- package/src/routes/api/internal/post-body-html.ts +43 -0
- package/src/routes/api/internal/sites.ts +24 -0
- package/src/routes/api/nav-items.ts +24 -1
- package/src/routes/api/posts.ts +2 -2
- package/src/routes/api/public/__tests__/archive.test.ts +35 -0
- package/src/routes/api/public/__tests__/posts.test.ts +208 -0
- package/src/routes/api/public/archive.ts +4 -0
- package/src/routes/api/public/posts.ts +41 -24
- package/src/routes/api/search.ts +3 -0
- package/src/routes/api/settings.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +7 -4
- package/src/routes/compose.tsx +2 -1
- package/src/routes/dash/settings.tsx +45 -2
- package/src/routes/feed/__tests__/feed.test.ts +210 -6
- package/src/routes/feed/feed.ts +23 -14
- package/src/routes/pages/__tests__/archive-params.test.ts +92 -3
- package/src/routes/pages/__tests__/collection-routing.test.ts +63 -5
- package/src/routes/pages/__tests__/collections.test.ts +9 -9
- package/src/routes/pages/__tests__/preview.test.ts +174 -0
- package/src/routes/pages/__tests__/search.test.ts +69 -0
- package/src/routes/pages/archive.tsx +27 -7
- package/src/routes/pages/collection.tsx +32 -19
- package/src/routes/pages/collections.tsx +6 -1
- package/src/routes/pages/page.tsx +83 -11
- package/src/routes/site-skill.ts +18 -0
- package/src/services/__tests__/auth.test.ts +7 -5
- package/src/services/__tests__/collection.test.ts +159 -44
- package/src/services/__tests__/navigation.test.ts +229 -21
- package/src/services/__tests__/post-timeline.test.ts +109 -30
- package/src/services/__tests__/post.test.ts +567 -5
- package/src/services/__tests__/search.test.ts +48 -1
- package/src/services/__tests__/settings.test.ts +8 -0
- package/src/services/__tests__/site-admin.test.ts +121 -1
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +3 -2
- package/src/services/collection.ts +226 -167
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +46 -25
- package/src/services/export-theme/layouts/_default/list.html +18 -4
- package/src/services/export-theme/layouts/_default/rss.xml +8 -12
- package/src/services/export-theme/layouts/featured/list.html +5 -4
- package/src/services/export-theme/layouts/partials/featured-thread.html +56 -0
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/media-gallery.html +1 -0
- package/src/services/export-theme/styles/main.css +30 -2
- package/src/services/export.ts +91 -80
- package/src/services/mcp.ts +37 -27
- package/src/services/media.ts +1 -1
- package/src/services/navigation.ts +185 -46
- package/src/services/post.ts +874 -271
- package/src/services/search.ts +8 -2
- package/src/services/settings.ts +2 -10
- package/src/services/site-admin.ts +63 -43
- package/src/style-cjk-jp.css +1 -1
- package/src/style-cjk-kr.css +1 -1
- package/src/style-cjk-tc.css +1 -1
- package/src/style-cjk.css +1 -1
- package/src/styles/components.css +540 -0
- package/src/styles/site-media.css +111 -19
- package/src/styles/tokens.css +14 -9
- package/src/styles/ui.css +829 -29
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +243 -12
- package/src/types/constants.ts +22 -1
- package/src/types/entities.ts +9 -8
- package/src/types/operations.ts +18 -10
- package/src/types/props.ts +8 -2
- package/src/types/raw-assets.d.ts +5 -0
- package/src/types/views.ts +16 -4
- package/src/ui/__tests__/font-themes.test.ts +42 -30
- package/src/ui/compose/ComposeDialog.tsx +92 -2
- package/src/ui/dash/appearance/NavigationContent.tsx +332 -4
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +23 -0
- package/src/ui/dash/settings/ConfigEditorContent.tsx +578 -0
- package/src/ui/dash/settings/GeneralContent.tsx +15 -5
- package/src/ui/dash/settings/SettingsRootContent.tsx +21 -1
- package/src/ui/dash/settings/__tests__/ConfigEditorContent.test.tsx +98 -0
- package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +15 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +16 -4
- package/src/ui/feed/LinkCard.tsx +2 -12
- package/src/ui/feed/NoteCard.tsx +10 -20
- package/src/ui/feed/QuoteCard.tsx +2 -12
- package/src/ui/feed/ThreadPreview.tsx +26 -34
- package/src/ui/feed/TimelineFeed.tsx +2 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +197 -56
- package/src/ui/feed/__tests__/timeline-cards.test.ts +178 -3
- package/src/ui/feed/thread-preview-state.ts +5 -7
- package/src/ui/font-themes.ts +74 -26
- package/src/ui/layouts/BaseLayout.tsx +22 -8
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +64 -1
- package/src/ui/pages/CollectionPage.tsx +101 -22
- package/src/ui/pages/CollectionsPage.tsx +3 -1
- package/src/ui/pages/PostPage.tsx +39 -12
- package/src/ui/pages/SearchPage.tsx +6 -14
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +7 -0
- package/src/ui/pages/__tests__/PostPage.test.tsx +88 -0
- package/src/ui/shared/CollectionDirectory.tsx +13 -9
- package/src/ui/shared/CollectionsManager.tsx +17 -7
- package/src/ui/shared/DraftPreviewBar.tsx +33 -0
- package/src/ui/shared/HomePageBranding.tsx +2 -2
- package/src/ui/shared/MediaGallery.tsx +1 -0
- package/src/ui/shared/PostFooter.tsx +3 -1
- package/src/ui/shared/__tests__/home-page-branding.test.tsx +1 -1
- package/src/ui/shared/__tests__/media-gallery.test.ts +1 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +2 -2
- package/src/ui/shared/__tests__/post-footer.test.ts +15 -0
- package/src/ui/shared/collection-management-labels.ts +33 -2
- package/src/ui/shared/navigation-labels.ts +2 -4
- package/src/ui/shared/post-article-attributes.ts +46 -0
- package/dist/app-DZDlmh7C.js +0 -6
- package/dist/client/_assets/client-Cevfpm8H.css +0 -2
- package/dist/client/_assets/client-auth-v3yvhbgZ.js +0 -4736
- package/src/client/tiptap/wrapping-input-rules.ts +0 -102
|
@@ -11,6 +11,8 @@ const TEST_COLLECTION_ID = "col_test00000000000000000000001";
|
|
|
11
11
|
const TEST_COLLECTION_ID_2 = "col_test00000000000000000000002";
|
|
12
12
|
const TEST_POST_ID = "pst_test00000000000000000000001";
|
|
13
13
|
const TEST_POST_ID_2 = "pst_test00000000000000000000002";
|
|
14
|
+
const TEST_POST_ID_3 = "pst_test00000000000000000000003";
|
|
15
|
+
const TEST_POST_ID_4 = "pst_test00000000000000000000004";
|
|
14
16
|
|
|
15
17
|
function insertTestPath(
|
|
16
18
|
sqlite: ReturnType<typeof createTestDatabase>["sqlite"],
|
|
@@ -76,6 +78,7 @@ function insertTestPost(
|
|
|
76
78
|
id: string;
|
|
77
79
|
slug: string;
|
|
78
80
|
title: string | null;
|
|
81
|
+
format?: "note" | "link" | "quote";
|
|
79
82
|
status?: "draft" | "published";
|
|
80
83
|
visibility?: "public" | "latest_hidden" | "private";
|
|
81
84
|
},
|
|
@@ -87,11 +90,12 @@ function insertTestPost(
|
|
|
87
90
|
id, site_id, format, status, visibility, title, thread_id,
|
|
88
91
|
published_at, last_activity_at, created_at, updated_at
|
|
89
92
|
)
|
|
90
|
-
VALUES (?, ?,
|
|
93
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
91
94
|
)
|
|
92
95
|
.run(
|
|
93
96
|
input.id,
|
|
94
97
|
DEFAULT_TEST_SITE_ID,
|
|
98
|
+
input.format ?? "note",
|
|
95
99
|
input.status ?? "published",
|
|
96
100
|
input.visibility ?? "latest_hidden",
|
|
97
101
|
input.title,
|
|
@@ -232,15 +236,71 @@ describe("NavItemService", () => {
|
|
|
232
236
|
}),
|
|
233
237
|
).rejects.toThrow("Built-in navigation item already exists");
|
|
234
238
|
});
|
|
239
|
+
|
|
240
|
+
it("creates a page nav item from an eligible titled note", async () => {
|
|
241
|
+
insertTestPost(sqlite, {
|
|
242
|
+
id: TEST_POST_ID,
|
|
243
|
+
slug: "about-me",
|
|
244
|
+
title: "About me",
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const item = await navItemService.create({
|
|
248
|
+
type: "page",
|
|
249
|
+
postId: TEST_POST_ID,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(item).toMatchObject({
|
|
253
|
+
type: "page",
|
|
254
|
+
postId: TEST_POST_ID,
|
|
255
|
+
label: "About me",
|
|
256
|
+
url: "/about-me",
|
|
257
|
+
placement: "header",
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("rejects duplicate and ineligible page nav items", async () => {
|
|
262
|
+
insertTestPost(sqlite, {
|
|
263
|
+
id: TEST_POST_ID,
|
|
264
|
+
slug: "about",
|
|
265
|
+
title: "About",
|
|
266
|
+
});
|
|
267
|
+
insertTestPost(sqlite, {
|
|
268
|
+
id: TEST_POST_ID_2,
|
|
269
|
+
slug: "private-page",
|
|
270
|
+
title: "Private page",
|
|
271
|
+
visibility: "private",
|
|
272
|
+
});
|
|
273
|
+
await navItemService.create({ type: "page", postId: TEST_POST_ID });
|
|
274
|
+
|
|
275
|
+
await expect(
|
|
276
|
+
navItemService.create({ type: "page", postId: TEST_POST_ID }),
|
|
277
|
+
).rejects.toThrow("Page already added to navigation");
|
|
278
|
+
await expect(
|
|
279
|
+
navItemService.create({ type: "page", postId: TEST_POST_ID_2 }),
|
|
280
|
+
).rejects.toThrow("Page must be a published, non-private titled note");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("truncates a page's default navigation label", async () => {
|
|
284
|
+
insertTestPost(sqlite, {
|
|
285
|
+
id: TEST_POST_ID,
|
|
286
|
+
slug: "long-title",
|
|
287
|
+
title: "A".repeat(150),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const item = await navItemService.create({
|
|
291
|
+
type: "page",
|
|
292
|
+
postId: TEST_POST_ID,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(item.label).toBe("A".repeat(100));
|
|
296
|
+
});
|
|
235
297
|
});
|
|
236
298
|
|
|
237
|
-
describe("
|
|
238
|
-
it("creates the
|
|
239
|
-
const created = await navItemService.
|
|
299
|
+
describe("materializeDefaultNavigation", () => {
|
|
300
|
+
it("creates the five-item default profile without Latest", async () => {
|
|
301
|
+
const created = await navItemService.materializeDefaultNavigation();
|
|
240
302
|
|
|
241
|
-
expect(created).toHaveLength(6);
|
|
242
303
|
expect(created.map((item) => item.systemKey)).toEqual([
|
|
243
|
-
"latest",
|
|
244
304
|
"featured",
|
|
245
305
|
"collections",
|
|
246
306
|
"archive",
|
|
@@ -251,39 +311,86 @@ describe("NavItemService", () => {
|
|
|
251
311
|
"header",
|
|
252
312
|
"header",
|
|
253
313
|
"header",
|
|
254
|
-
"header",
|
|
255
314
|
"more",
|
|
256
315
|
"more",
|
|
257
316
|
]);
|
|
317
|
+
expect(created.map((item) => item.systemKey)).not.toContain("latest");
|
|
258
318
|
});
|
|
259
319
|
|
|
260
|
-
it("
|
|
261
|
-
await navItemService.create({
|
|
320
|
+
it("preserves an existing optional Latest item", async () => {
|
|
321
|
+
const latest = await navItemService.create({
|
|
262
322
|
type: "system",
|
|
263
|
-
systemKey: "
|
|
323
|
+
systemKey: "latest",
|
|
324
|
+
});
|
|
325
|
+
const customizedLatest = await navItemService.update(latest.id, {
|
|
326
|
+
label: "Recent writing",
|
|
327
|
+
placement: "more",
|
|
328
|
+
position: "a3",
|
|
264
329
|
});
|
|
265
330
|
|
|
266
|
-
const created = await navItemService.
|
|
331
|
+
const created = await navItemService.materializeDefaultNavigation();
|
|
267
332
|
const items = await navItemService.list();
|
|
268
333
|
|
|
269
334
|
expect(created.map((item) => item.systemKey)).toEqual([
|
|
270
|
-
"latest",
|
|
271
335
|
"featured",
|
|
272
336
|
"collections",
|
|
337
|
+
"archive",
|
|
273
338
|
"rss",
|
|
274
339
|
"settings",
|
|
275
340
|
]);
|
|
341
|
+
expect(items.find((item) => item.id === latest.id)).toEqual(
|
|
342
|
+
customizedLatest,
|
|
343
|
+
);
|
|
276
344
|
expect(items.filter((item) => item.type === "system")).toHaveLength(6);
|
|
277
345
|
});
|
|
278
346
|
|
|
279
|
-
it("
|
|
280
|
-
await navItemService.
|
|
347
|
+
it("only appends missing profile items during recovery", async () => {
|
|
348
|
+
const archive = await navItemService.create({
|
|
349
|
+
type: "system",
|
|
350
|
+
systemKey: "archive",
|
|
351
|
+
});
|
|
352
|
+
const customizedArchive = await navItemService.update(archive.id, {
|
|
353
|
+
label: "All posts",
|
|
354
|
+
placement: "more",
|
|
355
|
+
position: "a2",
|
|
356
|
+
});
|
|
357
|
+
const customLink = await navItemService.create({
|
|
358
|
+
type: "link",
|
|
359
|
+
label: "Elsewhere",
|
|
360
|
+
url: "https://example.com",
|
|
361
|
+
position: "a3",
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const created = await navItemService.materializeDefaultNavigation();
|
|
365
|
+
const items = await navItemService.list();
|
|
366
|
+
|
|
367
|
+
expect(created.map((item) => item.systemKey)).toEqual([
|
|
368
|
+
"featured",
|
|
369
|
+
"collections",
|
|
370
|
+
"rss",
|
|
371
|
+
"settings",
|
|
372
|
+
]);
|
|
373
|
+
expect(items.slice(0, 2).map((item) => item.id)).toEqual([
|
|
374
|
+
archive.id,
|
|
375
|
+
customLink.id,
|
|
376
|
+
]);
|
|
377
|
+
expect(items.find((item) => item.id === archive.id)).toEqual(
|
|
378
|
+
customizedArchive,
|
|
379
|
+
);
|
|
380
|
+
expect(items.find((item) => item.id === customLink.id)).toEqual(
|
|
381
|
+
customLink,
|
|
382
|
+
);
|
|
383
|
+
expect(items.map((item) => item.systemKey)).not.toContain("latest");
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("is idempotent when the current profile already exists", async () => {
|
|
387
|
+
await navItemService.materializeDefaultNavigation();
|
|
281
388
|
|
|
282
|
-
const created = await navItemService.
|
|
389
|
+
const created = await navItemService.materializeDefaultNavigation();
|
|
283
390
|
const items = await navItemService.list();
|
|
284
391
|
|
|
285
392
|
expect(created).toEqual([]);
|
|
286
|
-
expect(items.filter((item) => item.type === "system")).toHaveLength(
|
|
393
|
+
expect(items.filter((item) => item.type === "system")).toHaveLength(5);
|
|
287
394
|
});
|
|
288
395
|
});
|
|
289
396
|
|
|
@@ -361,8 +468,53 @@ describe("NavItemService", () => {
|
|
|
361
468
|
});
|
|
362
469
|
});
|
|
363
470
|
|
|
471
|
+
describe("listPageCandidates", () => {
|
|
472
|
+
it("returns eligible notes by title and excludes added pages", async () => {
|
|
473
|
+
insertTestPost(sqlite, {
|
|
474
|
+
id: TEST_POST_ID,
|
|
475
|
+
slug: "about",
|
|
476
|
+
title: "About this site",
|
|
477
|
+
visibility: "latest_hidden",
|
|
478
|
+
});
|
|
479
|
+
insertTestPost(sqlite, {
|
|
480
|
+
id: TEST_POST_ID_2,
|
|
481
|
+
slug: "another-note",
|
|
482
|
+
title: "Another note",
|
|
483
|
+
visibility: "public",
|
|
484
|
+
});
|
|
485
|
+
insertTestPost(sqlite, {
|
|
486
|
+
id: TEST_POST_ID_3,
|
|
487
|
+
slug: "about-link",
|
|
488
|
+
title: "About link",
|
|
489
|
+
format: "link",
|
|
490
|
+
});
|
|
491
|
+
insertTestPost(sqlite, {
|
|
492
|
+
id: TEST_POST_ID_4,
|
|
493
|
+
slug: "about-draft",
|
|
494
|
+
title: "About draft",
|
|
495
|
+
status: "draft",
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
expect(
|
|
499
|
+
await navItemService.listPageCandidates({ query: "ABOUT" }),
|
|
500
|
+
).toEqual([
|
|
501
|
+
{
|
|
502
|
+
id: TEST_POST_ID,
|
|
503
|
+
title: "About this site",
|
|
504
|
+
slug: "about",
|
|
505
|
+
updatedAt: expect.any(Number),
|
|
506
|
+
},
|
|
507
|
+
]);
|
|
508
|
+
|
|
509
|
+
await navItemService.create({ type: "page", postId: TEST_POST_ID });
|
|
510
|
+
expect(
|
|
511
|
+
await navItemService.listPageCandidates({ query: "about" }),
|
|
512
|
+
).toEqual([]);
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
|
|
364
516
|
describe("listSuggestedLinks", () => {
|
|
365
|
-
it("suggests a published /about page as a
|
|
517
|
+
it("suggests a published /about page as a page nav item", async () => {
|
|
366
518
|
insertTestPost(sqlite, {
|
|
367
519
|
id: TEST_POST_ID,
|
|
368
520
|
slug: "about",
|
|
@@ -377,7 +529,8 @@ describe("NavItemService", () => {
|
|
|
377
529
|
label: "About me",
|
|
378
530
|
url: "/about",
|
|
379
531
|
targetType: "page",
|
|
380
|
-
navItemType: "
|
|
532
|
+
navItemType: "page",
|
|
533
|
+
postId: TEST_POST_ID,
|
|
381
534
|
},
|
|
382
535
|
]);
|
|
383
536
|
});
|
|
@@ -786,6 +939,32 @@ describe("NavItemService", () => {
|
|
|
786
939
|
});
|
|
787
940
|
});
|
|
788
941
|
|
|
942
|
+
describe("page nav items", () => {
|
|
943
|
+
beforeEach(() => {
|
|
944
|
+
insertTestPost(sqlite, {
|
|
945
|
+
id: TEST_POST_ID,
|
|
946
|
+
slug: "about",
|
|
947
|
+
title: "About",
|
|
948
|
+
});
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
it("allows label updates but keeps the page URL managed", async () => {
|
|
952
|
+
const item = await navItemService.create({
|
|
953
|
+
type: "page",
|
|
954
|
+
postId: TEST_POST_ID,
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
const updated = await navItemService.update(item.id, {
|
|
958
|
+
label: "Start here",
|
|
959
|
+
});
|
|
960
|
+
expect(updated?.label).toBe("Start here");
|
|
961
|
+
|
|
962
|
+
await expect(
|
|
963
|
+
navItemService.update(item.id, { url: "/other" }),
|
|
964
|
+
).rejects.toThrow("Page navigation URLs are managed automatically");
|
|
965
|
+
});
|
|
966
|
+
});
|
|
967
|
+
|
|
789
968
|
describe("getCollectionFreshness", () => {
|
|
790
969
|
beforeEach(() => {
|
|
791
970
|
insertTestCollection(sqlite, TEST_COLLECTION_ID, "design", "Design");
|
|
@@ -810,7 +989,7 @@ describe("NavItemService", () => {
|
|
|
810
989
|
// Add it to the collection recently
|
|
811
990
|
sqlite
|
|
812
991
|
.prepare(
|
|
813
|
-
`INSERT INTO
|
|
992
|
+
`INSERT INTO thread_collection (site_id, thread_id, collection_id, created_at, position)
|
|
814
993
|
VALUES (?, ?, ?, ?, 0)`,
|
|
815
994
|
)
|
|
816
995
|
.run(DEFAULT_TEST_SITE_ID, "pst_test001", TEST_COLLECTION_ID, ts);
|
|
@@ -838,7 +1017,7 @@ describe("NavItemService", () => {
|
|
|
838
1017
|
);
|
|
839
1018
|
sqlite
|
|
840
1019
|
.prepare(
|
|
841
|
-
`INSERT INTO
|
|
1020
|
+
`INSERT INTO thread_collection (site_id, thread_id, collection_id, created_at, position)
|
|
842
1021
|
VALUES (?, ?, ?, ?, 0)`,
|
|
843
1022
|
)
|
|
844
1023
|
.run(DEFAULT_TEST_SITE_ID, "pst_test002", TEST_COLLECTION_ID, oldTs);
|
|
@@ -849,6 +1028,35 @@ describe("NavItemService", () => {
|
|
|
849
1028
|
expect(result.has(TEST_COLLECTION_ID)).toBe(false);
|
|
850
1029
|
});
|
|
851
1030
|
|
|
1031
|
+
it("detects freshness from a recent root edit even when Thread activity is old", async () => {
|
|
1032
|
+
const oldTs = now() - 60 * 60 * 72;
|
|
1033
|
+
const recentTs = now();
|
|
1034
|
+
sqlite
|
|
1035
|
+
.prepare(
|
|
1036
|
+
`INSERT INTO post (id, site_id, thread_id, format, status, visibility, created_at, updated_at, last_activity_at)
|
|
1037
|
+
VALUES (?, ?, ?, 'note', 'published', 'public', ?, ?, ?)`,
|
|
1038
|
+
)
|
|
1039
|
+
.run(
|
|
1040
|
+
"pst_edited",
|
|
1041
|
+
DEFAULT_TEST_SITE_ID,
|
|
1042
|
+
"pst_edited",
|
|
1043
|
+
oldTs,
|
|
1044
|
+
recentTs,
|
|
1045
|
+
oldTs,
|
|
1046
|
+
);
|
|
1047
|
+
sqlite
|
|
1048
|
+
.prepare(
|
|
1049
|
+
`INSERT INTO thread_collection (site_id, thread_id, collection_id, created_at, position)
|
|
1050
|
+
VALUES (?, ?, ?, ?, 0)`,
|
|
1051
|
+
)
|
|
1052
|
+
.run(DEFAULT_TEST_SITE_ID, "pst_edited", TEST_COLLECTION_ID, oldTs);
|
|
1053
|
+
|
|
1054
|
+
const result = await navItemService.getCollectionFreshness([
|
|
1055
|
+
TEST_COLLECTION_ID,
|
|
1056
|
+
]);
|
|
1057
|
+
expect(result.get(TEST_COLLECTION_ID)).toBe(recentTs);
|
|
1058
|
+
});
|
|
1059
|
+
|
|
852
1060
|
it("detects freshness from recent thread replies", async () => {
|
|
853
1061
|
const oldTs = now() - 60 * 60 * 72; // 72 hours ago
|
|
854
1062
|
const recentTs = now();
|
|
@@ -871,7 +1079,7 @@ describe("NavItemService", () => {
|
|
|
871
1079
|
// Add the root to the collection (old)
|
|
872
1080
|
sqlite
|
|
873
1081
|
.prepare(
|
|
874
|
-
`INSERT INTO
|
|
1082
|
+
`INSERT INTO thread_collection (site_id, thread_id, collection_id, created_at, position)
|
|
875
1083
|
VALUES (?, ?, ?, ?, 0)`,
|
|
876
1084
|
)
|
|
877
1085
|
.run(DEFAULT_TEST_SITE_ID, "pst_root", TEST_COLLECTION_ID, oldTs);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
3
|
import {
|
|
3
4
|
createTestDatabase,
|
|
4
5
|
DEFAULT_TEST_SITE_ID,
|
|
5
6
|
} from "../../__tests__/helpers/db.js";
|
|
6
|
-
import {
|
|
7
|
+
import { posts, threadCollections } from "../../db/schema.js";
|
|
7
8
|
import { createPostService } from "../post.js";
|
|
8
9
|
import { createCollectionService } from "../collection.js";
|
|
9
10
|
import type { Database } from "../../db/index.js";
|
|
@@ -236,7 +237,7 @@ describe("PostService - Timeline features", () => {
|
|
|
236
237
|
expect(result.size).toBe(0);
|
|
237
238
|
});
|
|
238
239
|
|
|
239
|
-
it("returns
|
|
240
|
+
it("returns the only reply as leading and latest context", async () => {
|
|
240
241
|
const root = await postService.create({
|
|
241
242
|
format: "note",
|
|
242
243
|
bodyMarkdown: "root",
|
|
@@ -250,13 +251,13 @@ describe("PostService - Timeline features", () => {
|
|
|
250
251
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
251
252
|
const ctx = result.get(root.id);
|
|
252
253
|
expect(ctx).toBeDefined();
|
|
253
|
-
expect(ctx?.
|
|
254
|
-
expect(ctx?.
|
|
254
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([reply.id]);
|
|
255
|
+
expect(ctx?.trailingReplies).toEqual([]);
|
|
255
256
|
expect(ctx?.latestReply.id).toBe(reply.id);
|
|
256
257
|
expect(ctx?.totalReplyCount).toBe(1);
|
|
257
258
|
});
|
|
258
259
|
|
|
259
|
-
it("returns
|
|
260
|
+
it("returns overlapping leading and trailing context for short threads", async () => {
|
|
260
261
|
const root = await postService.create({
|
|
261
262
|
format: "note",
|
|
262
263
|
bodyMarkdown: "root",
|
|
@@ -275,13 +276,16 @@ describe("PostService - Timeline features", () => {
|
|
|
275
276
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
276
277
|
const ctx = result.get(root.id);
|
|
277
278
|
expect(ctx).toBeDefined();
|
|
278
|
-
expect(ctx?.
|
|
279
|
-
|
|
279
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([
|
|
280
|
+
reply1.id,
|
|
281
|
+
reply2.id,
|
|
282
|
+
]);
|
|
283
|
+
expect(ctx?.trailingReplies.map((post) => post.id)).toEqual([reply1.id]);
|
|
280
284
|
expect(ctx?.latestReply.id).toBe(reply2.id);
|
|
281
285
|
expect(ctx?.totalReplyCount).toBe(2);
|
|
282
286
|
});
|
|
283
287
|
|
|
284
|
-
it("returns
|
|
288
|
+
it("returns the first two and last three reply positions for longer threads", async () => {
|
|
285
289
|
const root = await postService.create({
|
|
286
290
|
format: "note",
|
|
287
291
|
bodyMarkdown: "root",
|
|
@@ -298,8 +302,14 @@ describe("PostService - Timeline features", () => {
|
|
|
298
302
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
299
303
|
const ctx = result.get(root.id);
|
|
300
304
|
expect(ctx).toBeDefined();
|
|
301
|
-
expect(ctx?.
|
|
302
|
-
|
|
305
|
+
expect(ctx?.leadingReplies.map((post) => post.bodyText)).toEqual([
|
|
306
|
+
"reply 0",
|
|
307
|
+
"reply 1",
|
|
308
|
+
]);
|
|
309
|
+
expect(ctx?.trailingReplies.map((post) => post.bodyText)).toEqual([
|
|
310
|
+
"reply 2",
|
|
311
|
+
"reply 3",
|
|
312
|
+
]);
|
|
303
313
|
expect(ctx?.latestReply.bodyText).toBe("reply 4");
|
|
304
314
|
expect(ctx?.totalReplyCount).toBe(5);
|
|
305
315
|
});
|
|
@@ -326,7 +336,7 @@ describe("PostService - Timeline features", () => {
|
|
|
326
336
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
327
337
|
const ctx = result.get(root.id);
|
|
328
338
|
expect(ctx).toBeDefined();
|
|
329
|
-
expect(ctx?.
|
|
339
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([reply1.id]);
|
|
330
340
|
expect(ctx?.latestReply.id).toBe(reply1.id);
|
|
331
341
|
expect(ctx?.totalReplyCount).toBe(1);
|
|
332
342
|
});
|
|
@@ -351,7 +361,9 @@ describe("PostService - Timeline features", () => {
|
|
|
351
361
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
352
362
|
const ctx = result.get(root.id);
|
|
353
363
|
expect(ctx).toBeDefined();
|
|
354
|
-
expect(ctx?.
|
|
364
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([
|
|
365
|
+
publishedReply.id,
|
|
366
|
+
]);
|
|
355
367
|
expect(ctx?.latestReply.id).toBe(publishedReply.id);
|
|
356
368
|
expect(ctx?.totalReplyCount).toBe(1);
|
|
357
369
|
});
|
|
@@ -381,8 +393,8 @@ describe("PostService - Timeline features", () => {
|
|
|
381
393
|
root2.id,
|
|
382
394
|
]);
|
|
383
395
|
expect(result.size).toBe(2);
|
|
384
|
-
expect(result.get(root1.id)?.
|
|
385
|
-
expect(result.get(root2.id)?.
|
|
396
|
+
expect(result.get(root1.id)?.leadingReplies[0]?.id).toBe(r1Reply.id);
|
|
397
|
+
expect(result.get(root2.id)?.leadingReplies[0]?.id).toBe(r2Reply.id);
|
|
386
398
|
expect(result.get(root1.id)?.latestReply.id).toBe(r1Reply.id);
|
|
387
399
|
expect(result.get(root2.id)?.latestReply.id).toBe(r2Reply.id);
|
|
388
400
|
});
|
|
@@ -464,6 +476,39 @@ describe("PostService - Timeline features", () => {
|
|
|
464
476
|
});
|
|
465
477
|
});
|
|
466
478
|
|
|
479
|
+
describe("countCollectionThreadRootsForCollections", () => {
|
|
480
|
+
it("counts rated Threads once even when multiple children are rated", async () => {
|
|
481
|
+
const collection = await collectionService.create({
|
|
482
|
+
slug: "rated-threads",
|
|
483
|
+
title: "Rated Threads",
|
|
484
|
+
});
|
|
485
|
+
const ratedRoot = await postService.create({
|
|
486
|
+
format: "note",
|
|
487
|
+
bodyMarkdown: "Rated root",
|
|
488
|
+
rating: 5,
|
|
489
|
+
});
|
|
490
|
+
await postService.create({
|
|
491
|
+
format: "note",
|
|
492
|
+
bodyMarkdown: "Rated child",
|
|
493
|
+
rating: 4,
|
|
494
|
+
replyToId: ratedRoot.id,
|
|
495
|
+
});
|
|
496
|
+
const unratedRoot = await postService.create({
|
|
497
|
+
format: "note",
|
|
498
|
+
bodyMarkdown: "Unrated root",
|
|
499
|
+
});
|
|
500
|
+
await collectionService.addThread(collection.id, ratedRoot.id);
|
|
501
|
+
await collectionService.addThread(collection.id, unratedRoot.id);
|
|
502
|
+
|
|
503
|
+
const count = await postService.countCollectionThreadRootsForCollections(
|
|
504
|
+
[collection.id],
|
|
505
|
+
{ status: "published", hasRating: true },
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
expect(count).toBe(1);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
467
512
|
describe("listCollectionThreadRootIds", () => {
|
|
468
513
|
it("bumps a hidden collection thread when a non-quiet reply is published", async () => {
|
|
469
514
|
const collection = await collectionService.create({
|
|
@@ -482,8 +527,8 @@ describe("PostService - Timeline features", () => {
|
|
|
482
527
|
publishedAt: 2000,
|
|
483
528
|
});
|
|
484
529
|
|
|
485
|
-
await collectionService.
|
|
486
|
-
await collectionService.
|
|
530
|
+
await collectionService.addThread(collection.id, hiddenRoot.id);
|
|
531
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
487
532
|
|
|
488
533
|
await postService.create({
|
|
489
534
|
format: "note",
|
|
@@ -519,8 +564,8 @@ describe("PostService - Timeline features", () => {
|
|
|
519
564
|
publishedAt: 2000,
|
|
520
565
|
});
|
|
521
566
|
|
|
522
|
-
await collectionService.
|
|
523
|
-
await collectionService.
|
|
567
|
+
await collectionService.addThread(collection.id, olderRoot.id);
|
|
568
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
524
569
|
|
|
525
570
|
await postService.create({
|
|
526
571
|
format: "note",
|
|
@@ -540,6 +585,40 @@ describe("PostService - Timeline features", () => {
|
|
|
540
585
|
|
|
541
586
|
expect(rootIds).toEqual([newerRoot.id, olderRoot.id]);
|
|
542
587
|
});
|
|
588
|
+
|
|
589
|
+
it("orders a Thread by a newer root edit", async () => {
|
|
590
|
+
const collection = await collectionService.create({
|
|
591
|
+
slug: "edited",
|
|
592
|
+
title: "Edited",
|
|
593
|
+
});
|
|
594
|
+
const editedRoot = await postService.create({
|
|
595
|
+
format: "note",
|
|
596
|
+
bodyMarkdown: "Edited root",
|
|
597
|
+
publishedAt: 1000,
|
|
598
|
+
});
|
|
599
|
+
const newerRoot = await postService.create({
|
|
600
|
+
format: "note",
|
|
601
|
+
bodyMarkdown: "Newer root",
|
|
602
|
+
publishedAt: 2000,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
await collectionService.addThread(collection.id, editedRoot.id);
|
|
606
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
607
|
+
await db
|
|
608
|
+
.update(posts)
|
|
609
|
+
.set({ createdAt: 500, updatedAt: 3000 })
|
|
610
|
+
.where(eq(posts.id, editedRoot.id));
|
|
611
|
+
|
|
612
|
+
const rootIds = await postService.listCollectionThreadRootIds(
|
|
613
|
+
collection.id,
|
|
614
|
+
{
|
|
615
|
+
status: "published",
|
|
616
|
+
sortOrder: "newest",
|
|
617
|
+
},
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
expect(rootIds).toEqual([editedRoot.id, newerRoot.id]);
|
|
621
|
+
});
|
|
543
622
|
});
|
|
544
623
|
|
|
545
624
|
describe("listCollectionFeedEntries", () => {
|
|
@@ -562,16 +641,16 @@ describe("PostService - Timeline features", () => {
|
|
|
562
641
|
bodyMarkdown: "Second root",
|
|
563
642
|
});
|
|
564
643
|
|
|
565
|
-
await db.insert(
|
|
644
|
+
await db.insert(threadCollections).values([
|
|
566
645
|
{
|
|
567
646
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
568
|
-
|
|
647
|
+
threadId: firstRoot.id,
|
|
569
648
|
collectionId: collection.id,
|
|
570
649
|
createdAt: 100,
|
|
571
650
|
},
|
|
572
651
|
{
|
|
573
652
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
574
|
-
|
|
653
|
+
threadId: secondRoot.id,
|
|
575
654
|
collectionId: collection.id,
|
|
576
655
|
createdAt: 200,
|
|
577
656
|
},
|
|
@@ -607,17 +686,17 @@ describe("PostService - Timeline features", () => {
|
|
|
607
686
|
publishedAt: 2000,
|
|
608
687
|
});
|
|
609
688
|
|
|
610
|
-
await db.insert(
|
|
689
|
+
await db.insert(threadCollections).values([
|
|
611
690
|
{
|
|
612
691
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
613
|
-
|
|
692
|
+
threadId: olderRoot.id,
|
|
614
693
|
collectionId: collection.id,
|
|
615
694
|
createdAt: 100,
|
|
616
695
|
pinnedAt: 5000,
|
|
617
696
|
},
|
|
618
697
|
{
|
|
619
698
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
620
|
-
|
|
699
|
+
threadId: newerRoot.id,
|
|
621
700
|
collectionId: collection.id,
|
|
622
701
|
createdAt: 200,
|
|
623
702
|
},
|
|
@@ -670,22 +749,22 @@ describe("PostService - Timeline features", () => {
|
|
|
670
749
|
bodyMarkdown: "Second root",
|
|
671
750
|
});
|
|
672
751
|
|
|
673
|
-
await db.insert(
|
|
752
|
+
await db.insert(threadCollections).values([
|
|
674
753
|
{
|
|
675
754
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
676
|
-
|
|
755
|
+
threadId: sharedRoot.id,
|
|
677
756
|
collectionId: smart.id,
|
|
678
757
|
createdAt: 100,
|
|
679
758
|
},
|
|
680
759
|
{
|
|
681
760
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
682
|
-
|
|
761
|
+
threadId: sharedRoot.id,
|
|
683
762
|
collectionId: movies.id,
|
|
684
763
|
createdAt: 300,
|
|
685
764
|
},
|
|
686
765
|
{
|
|
687
766
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
688
|
-
|
|
767
|
+
threadId: secondRoot.id,
|
|
689
768
|
collectionId: movies.id,
|
|
690
769
|
createdAt: 200,
|
|
691
770
|
},
|
|
@@ -722,8 +801,8 @@ describe("PostService - Timeline features", () => {
|
|
|
722
801
|
publishedAt: 2000,
|
|
723
802
|
});
|
|
724
803
|
|
|
725
|
-
await collectionService.
|
|
726
|
-
await collectionService.
|
|
804
|
+
await collectionService.addThread(collection.id, hiddenRoot.id);
|
|
805
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
727
806
|
|
|
728
807
|
await postService.create({
|
|
729
808
|
format: "note",
|