@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
|
@@ -281,7 +281,7 @@ describe("CollectionService", () => {
|
|
|
281
281
|
format: "note",
|
|
282
282
|
bodyMarkdown: "Book note",
|
|
283
283
|
});
|
|
284
|
-
await collectionService.
|
|
284
|
+
await collectionService.addThread(reading.id, post.id);
|
|
285
285
|
|
|
286
286
|
const directory = await collectionService.listDirectoryData();
|
|
287
287
|
|
|
@@ -297,7 +297,7 @@ describe("CollectionService", () => {
|
|
|
297
297
|
type: "collection",
|
|
298
298
|
collection: expect.objectContaining({
|
|
299
299
|
id: reading.id,
|
|
300
|
-
|
|
300
|
+
threadCount: 1,
|
|
301
301
|
recentActivityAt: post.lastActivityAt,
|
|
302
302
|
}),
|
|
303
303
|
}),
|
|
@@ -309,6 +309,30 @@ describe("CollectionService", () => {
|
|
|
309
309
|
]);
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
+
it("uses a later Root edit as the Thread's recent activity", async () => {
|
|
313
|
+
vi.useFakeTimers();
|
|
314
|
+
try {
|
|
315
|
+
vi.setSystemTime(new Date("2024-01-01T00:00:00Z"));
|
|
316
|
+
const collection = await collectionService.create({
|
|
317
|
+
slug: "edited-thread",
|
|
318
|
+
title: "Edited Thread",
|
|
319
|
+
});
|
|
320
|
+
const root = await postService.create({
|
|
321
|
+
format: "note",
|
|
322
|
+
bodyMarkdown: "before",
|
|
323
|
+
});
|
|
324
|
+
await collectionService.addThread(collection.id, root.id);
|
|
325
|
+
|
|
326
|
+
vi.setSystemTime(new Date("2024-01-01T00:01:00Z"));
|
|
327
|
+
await postService.update(root.id, { bodyMarkdown: "after" });
|
|
328
|
+
|
|
329
|
+
const directory = await collectionService.listDirectoryData();
|
|
330
|
+
expect(directory.collections[0]?.recentActivityAt).toBe(1704067260);
|
|
331
|
+
} finally {
|
|
332
|
+
vi.useRealTimers();
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
312
336
|
it("includes custom links in directory items", async () => {
|
|
313
337
|
const link = await collectionService.createDirectoryItem({
|
|
314
338
|
type: "link",
|
|
@@ -451,7 +475,7 @@ describe("CollectionService", () => {
|
|
|
451
475
|
bodyMarkdown: "test post",
|
|
452
476
|
});
|
|
453
477
|
|
|
454
|
-
await collectionService.
|
|
478
|
+
await collectionService.addThread(collection.id, post.id);
|
|
455
479
|
|
|
456
480
|
// Verify association exists
|
|
457
481
|
const before = await collectionService.getCollectionsByPostId(post.id);
|
|
@@ -736,15 +760,15 @@ describe("CollectionService", () => {
|
|
|
736
760
|
});
|
|
737
761
|
});
|
|
738
762
|
|
|
739
|
-
describe("
|
|
763
|
+
describe("getThreadCounts", () => {
|
|
740
764
|
it("returns empty map when no posts exist", async () => {
|
|
741
765
|
await collectionService.create({ slug: "empty", title: "Empty" });
|
|
742
766
|
|
|
743
|
-
const counts = await collectionService.
|
|
767
|
+
const counts = await collectionService.getThreadCounts();
|
|
744
768
|
expect(counts.size).toBe(0);
|
|
745
769
|
});
|
|
746
770
|
|
|
747
|
-
it("returns correct counts for collections with
|
|
771
|
+
it("returns correct counts for collections with Threads", async () => {
|
|
748
772
|
const col1 = await collectionService.create({
|
|
749
773
|
slug: "col1",
|
|
750
774
|
title: "Col 1",
|
|
@@ -767,16 +791,16 @@ describe("CollectionService", () => {
|
|
|
767
791
|
bodyMarkdown: "post 3",
|
|
768
792
|
});
|
|
769
793
|
|
|
770
|
-
await collectionService.
|
|
771
|
-
await collectionService.
|
|
772
|
-
await collectionService.
|
|
794
|
+
await collectionService.addThread(col1.id, p1.id);
|
|
795
|
+
await collectionService.addThread(col1.id, p2.id);
|
|
796
|
+
await collectionService.addThread(col2.id, p3.id);
|
|
773
797
|
|
|
774
|
-
const counts = await collectionService.
|
|
798
|
+
const counts = await collectionService.getThreadCounts();
|
|
775
799
|
expect(counts.get(col1.id)).toBe(2);
|
|
776
800
|
expect(counts.get(col2.id)).toBe(1);
|
|
777
801
|
});
|
|
778
802
|
|
|
779
|
-
it("does not count
|
|
803
|
+
it("does not count Threads without a collection", async () => {
|
|
780
804
|
const col = await collectionService.create({
|
|
781
805
|
slug: "col",
|
|
782
806
|
title: "Col",
|
|
@@ -791,14 +815,14 @@ describe("CollectionService", () => {
|
|
|
791
815
|
bodyMarkdown: "no collection",
|
|
792
816
|
});
|
|
793
817
|
|
|
794
|
-
await collectionService.
|
|
818
|
+
await collectionService.addThread(col.id, p1.id);
|
|
795
819
|
|
|
796
|
-
const counts = await collectionService.
|
|
820
|
+
const counts = await collectionService.getThreadCounts();
|
|
797
821
|
expect(counts.get(col.id)).toBe(1);
|
|
798
822
|
expect(counts.size).toBe(1);
|
|
799
823
|
});
|
|
800
824
|
|
|
801
|
-
it("does not count
|
|
825
|
+
it("does not count deleted Threads", async () => {
|
|
802
826
|
const col = await collectionService.create({
|
|
803
827
|
slug: "col",
|
|
804
828
|
title: "Col",
|
|
@@ -813,19 +837,18 @@ describe("CollectionService", () => {
|
|
|
813
837
|
bodyMarkdown: "still alive",
|
|
814
838
|
});
|
|
815
839
|
|
|
816
|
-
await collectionService.
|
|
817
|
-
await collectionService.
|
|
840
|
+
await collectionService.addThread(col.id, post.id);
|
|
841
|
+
await collectionService.addThread(col.id, post2.id);
|
|
818
842
|
|
|
819
|
-
// Soft-delete one post
|
|
820
843
|
await postService.delete(post.id);
|
|
821
844
|
|
|
822
|
-
const counts = await collectionService.
|
|
845
|
+
const counts = await collectionService.getThreadCounts();
|
|
823
846
|
expect(counts.get(col.id)).toBe(1);
|
|
824
847
|
});
|
|
825
848
|
});
|
|
826
849
|
|
|
827
|
-
describe("
|
|
828
|
-
it("adds a
|
|
850
|
+
describe("addThread / removeThread", () => {
|
|
851
|
+
it("adds a Thread to a collection", async () => {
|
|
829
852
|
const col = await collectionService.create({
|
|
830
853
|
slug: "test",
|
|
831
854
|
title: "Test",
|
|
@@ -835,7 +858,7 @@ describe("CollectionService", () => {
|
|
|
835
858
|
bodyMarkdown: "test",
|
|
836
859
|
});
|
|
837
860
|
|
|
838
|
-
await collectionService.
|
|
861
|
+
await collectionService.addThread(col.id, post.id);
|
|
839
862
|
|
|
840
863
|
const collections = await collectionService.getCollectionsByPostId(
|
|
841
864
|
post.id,
|
|
@@ -854,14 +877,14 @@ describe("CollectionService", () => {
|
|
|
854
877
|
bodyMarkdown: "test",
|
|
855
878
|
});
|
|
856
879
|
|
|
857
|
-
await collectionService.
|
|
858
|
-
await collectionService.
|
|
880
|
+
await collectionService.addThread(col.id, post.id);
|
|
881
|
+
await collectionService.addThread(col.id, post.id); // duplicate
|
|
859
882
|
|
|
860
|
-
const
|
|
861
|
-
expect(
|
|
883
|
+
const threadIds = await collectionService.getThreadIds(col.id);
|
|
884
|
+
expect(threadIds).toHaveLength(1);
|
|
862
885
|
});
|
|
863
886
|
|
|
864
|
-
it("removes a
|
|
887
|
+
it("removes a Thread from a collection", async () => {
|
|
865
888
|
const col = await collectionService.create({
|
|
866
889
|
slug: "test",
|
|
867
890
|
title: "Test",
|
|
@@ -871,14 +894,43 @@ describe("CollectionService", () => {
|
|
|
871
894
|
bodyMarkdown: "test",
|
|
872
895
|
});
|
|
873
896
|
|
|
874
|
-
await collectionService.
|
|
875
|
-
await collectionService.
|
|
897
|
+
await collectionService.addThread(col.id, post.id);
|
|
898
|
+
await collectionService.removeThread(col.id, post.id);
|
|
876
899
|
|
|
877
900
|
const collections = await collectionService.getCollectionsByPostId(
|
|
878
901
|
post.id,
|
|
879
902
|
);
|
|
880
903
|
expect(collections).toHaveLength(0);
|
|
881
904
|
});
|
|
905
|
+
|
|
906
|
+
it("normalizes Child mutations to the shared Thread root", async () => {
|
|
907
|
+
const collection = await collectionService.create({
|
|
908
|
+
slug: "child-mutation",
|
|
909
|
+
title: "Child Mutation",
|
|
910
|
+
});
|
|
911
|
+
const root = await postService.create({
|
|
912
|
+
format: "note",
|
|
913
|
+
bodyMarkdown: "root",
|
|
914
|
+
});
|
|
915
|
+
const child = await postService.create({
|
|
916
|
+
format: "note",
|
|
917
|
+
bodyMarkdown: "child",
|
|
918
|
+
replyToId: root.id,
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
await collectionService.addThread(collection.id, child.id);
|
|
922
|
+
expect(await collectionService.getThreadIds(collection.id)).toEqual([
|
|
923
|
+
root.id,
|
|
924
|
+
]);
|
|
925
|
+
expect(
|
|
926
|
+
(await collectionService.getCollectionsByPostId(root.id)).map(
|
|
927
|
+
(item) => item.id,
|
|
928
|
+
),
|
|
929
|
+
).toEqual([collection.id]);
|
|
930
|
+
|
|
931
|
+
await collectionService.removeThread(collection.id, child.id);
|
|
932
|
+
expect(await collectionService.getThreadIds(collection.id)).toEqual([]);
|
|
933
|
+
});
|
|
882
934
|
});
|
|
883
935
|
|
|
884
936
|
describe("getCollectionsByPostId", () => {
|
|
@@ -899,8 +951,8 @@ describe("CollectionService", () => {
|
|
|
899
951
|
bodyMarkdown: "test",
|
|
900
952
|
});
|
|
901
953
|
|
|
902
|
-
await collectionService.
|
|
903
|
-
await collectionService.
|
|
954
|
+
await collectionService.addThread(cols[0]?.id ?? "", post.id);
|
|
955
|
+
await collectionService.addThread(cols[1]?.id ?? "", post.id);
|
|
904
956
|
|
|
905
957
|
const collections = await collectionService.getCollectionsByPostId(
|
|
906
958
|
post.id,
|
|
@@ -919,10 +971,70 @@ describe("CollectionService", () => {
|
|
|
919
971
|
);
|
|
920
972
|
expect(collections).toHaveLength(0);
|
|
921
973
|
});
|
|
974
|
+
|
|
975
|
+
it("projects one shared membership set onto the root and every child", async () => {
|
|
976
|
+
const collection = await collectionService.create({
|
|
977
|
+
slug: "shared-thread",
|
|
978
|
+
title: "Shared Thread",
|
|
979
|
+
});
|
|
980
|
+
const root = await postService.create({
|
|
981
|
+
format: "note",
|
|
982
|
+
bodyMarkdown: "root",
|
|
983
|
+
});
|
|
984
|
+
const child = await postService.create({
|
|
985
|
+
format: "note",
|
|
986
|
+
bodyMarkdown: "child",
|
|
987
|
+
replyToId: root.id,
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
await collectionService.addThread(collection.id, root.id);
|
|
991
|
+
|
|
992
|
+
const [rootCollections, childCollections, batch] = await Promise.all([
|
|
993
|
+
collectionService.getCollectionsByPostId(root.id),
|
|
994
|
+
collectionService.getCollectionsByPostId(child.id),
|
|
995
|
+
collectionService.getCollectionsByPostIds([root.id, child.id]),
|
|
996
|
+
]);
|
|
997
|
+
|
|
998
|
+
expect(rootCollections.map((item) => item.id)).toEqual([collection.id]);
|
|
999
|
+
expect(childCollections.map((item) => item.id)).toEqual([collection.id]);
|
|
1000
|
+
expect(batch.get(root.id)?.map((item) => item.id)).toEqual([
|
|
1001
|
+
collection.id,
|
|
1002
|
+
]);
|
|
1003
|
+
expect(batch.get(child.id)?.map((item) => item.id)).toEqual([
|
|
1004
|
+
collection.id,
|
|
1005
|
+
]);
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
it("keeps shared membership when a Child is deleted and removes it with the Root", async () => {
|
|
1009
|
+
const collection = await collectionService.create({
|
|
1010
|
+
slug: "thread-lifecycle",
|
|
1011
|
+
title: "Thread Lifecycle",
|
|
1012
|
+
});
|
|
1013
|
+
const root = await postService.create({
|
|
1014
|
+
format: "note",
|
|
1015
|
+
bodyMarkdown: "root",
|
|
1016
|
+
});
|
|
1017
|
+
const child = await postService.create({
|
|
1018
|
+
format: "note",
|
|
1019
|
+
bodyMarkdown: "child",
|
|
1020
|
+
replyToId: root.id,
|
|
1021
|
+
});
|
|
1022
|
+
await collectionService.addThread(collection.id, root.id);
|
|
1023
|
+
|
|
1024
|
+
await postService.delete(child.id);
|
|
1025
|
+
expect(
|
|
1026
|
+
(await collectionService.getCollectionsByPostId(root.id)).map(
|
|
1027
|
+
(item) => item.id,
|
|
1028
|
+
),
|
|
1029
|
+
).toEqual([collection.id]);
|
|
1030
|
+
|
|
1031
|
+
await postService.delete(root.id);
|
|
1032
|
+
expect(await collectionService.getThreadIds(collection.id)).toEqual([]);
|
|
1033
|
+
});
|
|
922
1034
|
});
|
|
923
1035
|
|
|
924
|
-
describe("
|
|
925
|
-
it("returns all
|
|
1036
|
+
describe("getThreadIds", () => {
|
|
1037
|
+
it("returns all Thread root IDs in a collection", async () => {
|
|
926
1038
|
const col = await collectionService.create({
|
|
927
1039
|
slug: "test",
|
|
928
1040
|
title: "Test",
|
|
@@ -936,18 +1048,18 @@ describe("CollectionService", () => {
|
|
|
936
1048
|
bodyMarkdown: "two",
|
|
937
1049
|
});
|
|
938
1050
|
|
|
939
|
-
await collectionService.
|
|
940
|
-
await collectionService.
|
|
1051
|
+
await collectionService.addThread(col.id, p1.id);
|
|
1052
|
+
await collectionService.addThread(col.id, p2.id);
|
|
941
1053
|
|
|
942
|
-
const ids = await collectionService.
|
|
1054
|
+
const ids = await collectionService.getThreadIds(col.id);
|
|
943
1055
|
expect(ids).toHaveLength(2);
|
|
944
1056
|
expect(ids).toContain(p1.id);
|
|
945
1057
|
expect(ids).toContain(p2.id);
|
|
946
1058
|
});
|
|
947
1059
|
});
|
|
948
1060
|
|
|
949
|
-
describe("
|
|
950
|
-
it("replaces all collection memberships for a
|
|
1061
|
+
describe("syncThreadCollections", () => {
|
|
1062
|
+
it("replaces all collection memberships for a Thread", async () => {
|
|
951
1063
|
const col1 = await collectionService.create({
|
|
952
1064
|
slug: "col1",
|
|
953
1065
|
title: "Col 1",
|
|
@@ -967,11 +1079,14 @@ describe("CollectionService", () => {
|
|
|
967
1079
|
});
|
|
968
1080
|
|
|
969
1081
|
// Initially in col1 and col2
|
|
970
|
-
await collectionService.
|
|
971
|
-
await collectionService.
|
|
1082
|
+
await collectionService.addThread(col1.id, post.id);
|
|
1083
|
+
await collectionService.addThread(col2.id, post.id);
|
|
972
1084
|
|
|
973
1085
|
// Sync to col2 and col3
|
|
974
|
-
await collectionService.
|
|
1086
|
+
await collectionService.syncThreadCollections(post.id, [
|
|
1087
|
+
col2.id,
|
|
1088
|
+
col3.id,
|
|
1089
|
+
]);
|
|
975
1090
|
|
|
976
1091
|
const collections = await collectionService.getCollectionsByPostId(
|
|
977
1092
|
post.id,
|
|
@@ -993,8 +1108,8 @@ describe("CollectionService", () => {
|
|
|
993
1108
|
bodyMarkdown: "test",
|
|
994
1109
|
});
|
|
995
1110
|
|
|
996
|
-
await collectionService.
|
|
997
|
-
await collectionService.
|
|
1111
|
+
await collectionService.addThread(col.id, post.id);
|
|
1112
|
+
await collectionService.syncThreadCollections(post.id, []);
|
|
998
1113
|
|
|
999
1114
|
const collections = await collectionService.getCollectionsByPostId(
|
|
1000
1115
|
post.id,
|
|
@@ -1018,13 +1133,13 @@ describe("CollectionService", () => {
|
|
|
1018
1133
|
const originalTransaction = dbWithoutTransaction.transaction.bind(db);
|
|
1019
1134
|
dbWithoutTransaction.transaction = async () => {
|
|
1020
1135
|
throw new Error(
|
|
1021
|
-
"sqlite
|
|
1136
|
+
"sqlite syncThreadCollections() should not call transaction()",
|
|
1022
1137
|
);
|
|
1023
1138
|
};
|
|
1024
1139
|
|
|
1025
1140
|
try {
|
|
1026
1141
|
await expect(
|
|
1027
|
-
collectionService.
|
|
1142
|
+
collectionService.syncThreadCollections(post.id, [col.id]),
|
|
1028
1143
|
).resolves.toBeUndefined();
|
|
1029
1144
|
} finally {
|
|
1030
1145
|
dbWithoutTransaction.transaction = originalTransaction;
|