@jant/core 0.6.16 → 0.7.1
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/README.md +11 -11
- package/bin/commands/assets/upload.js +192 -15
- package/bin/commands/export.js +3 -1
- package/bin/commands/import-site.js +749 -220
- package/bin/commands/reset-password.js +21 -8
- package/bin/commands/setup.js +136 -0
- package/bin/commands/site/export.js +71 -34
- package/bin/commands/site/pull-media.js +2 -6
- package/bin/commands/site/snapshot/export.js +19 -61
- package/bin/commands/site/snapshot/import.js +23 -24
- package/bin/lib/d1-query.js +92 -18
- package/bin/lib/hugo-markdown.js +4 -0
- package/bin/lib/node-env.js +40 -4
- package/bin/lib/site-pull-media.js +21 -28
- package/bin/lib/site-selection.js +94 -29
- package/bin/lib/site-snapshot.js +338 -3
- package/bin/lib/sql-export.js +68 -5
- package/bin/lib/wrangler-cli.js +37 -2
- package/bin/lib/wrangler-json.js +35 -0
- package/bin/lib/zip-archive.js +187 -0
- package/dist/{app-C7_4oab9.js → app-ZcaI1kPN.js} +53190 -13529
- package/dist/client/.vite/manifest.json +255 -90
- package/dist/client/_assets/chunks/confirm-D7GrwsCN.js +45 -0
- package/dist/client/_assets/chunks/create-editor-B-m7X7S5.js +261 -0
- package/dist/client/_assets/chunks/{native-DpcrFAPh.js → emoji-mart-C4RpRKcU.js} +716 -1
- package/dist/client/_assets/chunks/footnote-rail-CmeoVtnU.js +1 -0
- package/dist/client/_assets/chunks/heic-to-CuP2Qlsw.js +1 -0
- package/dist/client/_assets/chunks/jant-locale-picker-DL6mIOT5.js +69 -0
- package/dist/client/_assets/chunks/lit-DW8VmJAT.js +2 -0
- package/dist/client/_assets/chunks/mediabunny-DRaCQ1r-.js +31 -0
- package/dist/client/_assets/chunks/rolldown-runtime-hePW80VL.js +1 -0
- package/dist/client/_assets/chunks/settings-paths-BcdySNx6.js +84 -0
- package/dist/client/_assets/chunks/site-header-nav-DzXsFk05.js +1 -0
- package/dist/client/_assets/chunks/slug-format-CRXO1AL5.js +1 -0
- package/dist/client/_assets/chunks/slugify-373citbs.js +1 -0
- package/dist/client/_assets/chunks/sortable-list-BJyd-LXE.js +2 -0
- package/dist/client/_assets/chunks/types-D5iF3H1a.js +1 -0
- package/dist/client/_assets/chunks/unsafe-html-B5ZEXaxg.js +1 -0
- package/dist/client/_assets/chunks/unsafe-svg-BkscJx69.js +2 -0
- package/dist/client/_assets/client-DAhoqPdr.css +2 -0
- package/dist/client/_assets/client-auth-CAMfTrKW.js +828 -0
- package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
- package/dist/client/_assets/client-compose-D6rExKVK.js +2004 -0
- package/dist/client/_assets/client-knSEyUJO.js +295 -0
- package/dist/client/_assets/client-manage-sVOkqVbH.js +2170 -0
- package/dist/client/_assets/client-settings-CGR_nZ78.js +1112 -0
- package/dist/github-sync-Gw4orAHk.js +8398 -0
- package/dist/index.js +2 -5
- package/dist/node.js +11 -9
- package/package.json +15 -5
- package/src/__tests__/auth-session-freshness.test.ts +57 -0
- package/src/__tests__/basecoat-variants.test.ts +121 -0
- package/src/__tests__/bin/assets-upload.test.ts +68 -0
- package/src/__tests__/dev-scripts.test.ts +203 -0
- package/src/__tests__/export-collection-order.test.ts +276 -0
- package/src/__tests__/export-feed-ids.test.ts +184 -0
- package/src/__tests__/export-feed-order.test.ts +294 -0
- package/src/__tests__/export-hugo-build.test.ts +390 -0
- package/src/__tests__/export-import-roundtrip.test.ts +94 -0
- package/src/__tests__/export-service.test.ts +842 -28
- package/src/__tests__/export-smart-collection.test.ts +329 -0
- package/src/__tests__/helpers/component-css.ts +91 -0
- package/src/__tests__/helpers/db.ts +33 -0
- package/src/__tests__/helpers/export-fixtures.ts +3 -0
- package/src/__tests__/helpers/hugo-site.ts +146 -0
- package/src/__tests__/import-site-command.test.ts +487 -1
- package/src/__tests__/mise-config.test.ts +75 -4
- package/src/__tests__/node-dev-tasks.test.ts +264 -0
- package/src/__tests__/site-export-canonical-import.test.ts +149 -0
- package/src/__tests__/snapshot-canonical-replay.test.ts +179 -0
- package/src/__tests__/snapshot-settings.test.ts +97 -0
- package/src/__tests__/snapshot-tables.test.ts +219 -0
- package/src/__tests__/sql-export.test.ts +173 -0
- package/src/__tests__/stylesheet-audience.test.ts +159 -0
- package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
- package/src/__tests__/zip-archive.test.ts +106 -0
- package/src/app.tsx +86 -22
- package/src/auth.ts +18 -1
- package/src/client/__tests__/collection-page-actions.test.ts +51 -2
- package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
- package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
- package/src/client/__tests__/compose-bridge.test.ts +272 -0
- package/src/client/__tests__/compose-launch.test.ts +62 -1
- package/src/client/__tests__/compose-shortcuts.test.ts +38 -9
- package/src/client/__tests__/compose-triggers.test.ts +90 -0
- package/src/client/__tests__/copy-field.test.ts +162 -0
- package/src/client/__tests__/custom-url-menu.test.ts +127 -6
- package/src/client/__tests__/image-processor.test.ts +324 -2
- package/src/client/__tests__/lazy-entries.test.ts +92 -0
- package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
- package/src/client/__tests__/runtime-paths.test.ts +84 -0
- package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
- package/src/client/__tests__/toast.test.ts +23 -0
- package/src/client/__tests__/video-processor-probe.test.ts +175 -0
- package/src/client/__tests__/video-processor.test.ts +181 -0
- package/src/client/audio-processor.ts +24 -15
- package/src/client/audio-waveform.ts +49 -0
- package/src/client/collection-dialog-host.ts +72 -0
- package/src/client/collection-navigation.ts +51 -16
- package/src/client/collection-page-actions.ts +26 -0
- package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
- package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
- package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
- package/src/client/components/__tests__/jant-command-palette.test.ts +28 -0
- package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1250 -53
- package/src/client/components/__tests__/jant-compose-editor.test.ts +544 -40
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +0 -1
- package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
- package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
- package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
- package/src/client/components/__tests__/jant-settings-general.test.ts +561 -172
- package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
- package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
- package/src/client/components/collection-dialog-types.ts +49 -0
- package/src/client/components/collection-manager-types.ts +32 -1
- package/src/client/components/collection-types.ts +1 -15
- package/src/client/components/compose-types.ts +75 -1
- package/src/client/components/jant-collection-dialog.ts +722 -0
- package/src/client/components/jant-collection-directory.ts +567 -182
- package/src/client/components/jant-collection-form.ts +53 -232
- package/src/client/components/jant-command-palette.ts +100 -49
- package/src/client/components/jant-compose-dialog.ts +1486 -707
- package/src/client/components/jant-compose-editor.ts +673 -336
- package/src/client/components/jant-compose-fullscreen.ts +51 -43
- package/src/client/components/jant-config-editor.ts +65 -53
- package/src/client/components/jant-confirm-dialog.ts +13 -11
- package/src/client/components/jant-locale-picker.ts +418 -0
- package/src/client/components/jant-media-lightbox.ts +146 -136
- package/src/client/components/jant-nav-manager.ts +690 -366
- package/src/client/components/jant-post-menu.ts +1086 -282
- package/src/client/components/jant-post-picker.ts +324 -0
- package/src/client/components/jant-repo-picker-types.ts +6 -1
- package/src/client/components/jant-repo-picker.ts +137 -108
- package/src/client/components/jant-settings-avatar.ts +31 -27
- package/src/client/components/jant-settings-general.ts +622 -543
- package/src/client/components/jant-settings-language.ts +1087 -0
- package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
- package/src/client/components/jant-text-preview.ts +94 -86
- package/src/client/components/nav-manager-types.ts +62 -1
- package/src/client/components/settings-types.ts +14 -29
- package/src/client/components/smart-collection-conditions.ts +285 -0
- package/src/client/components/smart-collection-dialog-types.ts +68 -0
- package/src/client/compose-bridge.ts +214 -141
- package/src/client/compose-launch.ts +76 -2
- package/src/client/compose-shortcuts.ts +6 -8
- package/src/client/compose-triggers.ts +50 -0
- package/src/client/copy-field.ts +108 -0
- package/src/client/custom-url-menu.ts +60 -4
- package/src/client/icons.ts +14 -0
- package/src/client/image-plan.ts +165 -0
- package/src/client/image-processor.ts +195 -117
- package/src/client/image-worker-client.ts +45 -0
- package/src/client/image-worker.ts +135 -0
- package/src/client/lazy-entries.ts +95 -0
- package/src/client/lazy-slugify.ts +2 -2
- package/src/client/media-scroll-hint.ts +43 -2
- package/src/client/mediabunny.ts +29 -0
- package/src/client/post-picker.ts +41 -0
- package/src/client/runtime-paths.ts +74 -1
- package/src/client/settings-bridge.ts +4 -10
- package/src/client/site-header-nav.js +18 -6
- package/src/client/smart-collection-dialog-host.ts +116 -0
- package/src/client/smart-collection-page-actions.ts +208 -0
- package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
- package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
- package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
- package/src/client/tiptap/__tests__/list-editing.test.ts +224 -86
- package/src/client/tiptap/__tests__/mark-exit.test.ts +1 -2
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +26 -0
- package/src/client/tiptap/bubble-menu.ts +47 -16
- package/src/client/tiptap/extensions.ts +0 -3
- package/src/client/tiptap/floating-position.ts +216 -2
- package/src/client/tiptap/inline-image-upload.ts +7 -3
- package/src/client/tiptap/link-toolbar.ts +22 -9
- package/src/client/tiptap/structural-keymap.ts +158 -47
- package/src/client/toast.ts +13 -0
- package/src/client/types/vite-worker.d.ts +12 -0
- package/src/client/upload-with-metadata.ts +3 -13
- package/src/client/video-processor.ts +351 -107
- package/src/client-auth.ts +14 -31
- package/src/client-compose.ts +19 -0
- package/src/client-manage.ts +13 -0
- package/src/client-settings.ts +14 -0
- package/src/client-site.ts +10 -3
- package/src/client.ts +4 -0
- package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
- package/src/db/__tests__/d1-query.test.ts +56 -1
- package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
- package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
- package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
- package/src/db/migrations/0032_married_tomas.sql +3 -0
- package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
- package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
- package/src/db/migrations/meta/0032_snapshot.json +2582 -0
- package/src/db/migrations/meta/0033_snapshot.json +2816 -0
- package/src/db/migrations/meta/0034_snapshot.json +2816 -0
- package/src/db/migrations/meta/_journal.json +21 -0
- package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
- package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
- package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
- package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
- package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
- package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
- package/src/db/migrations/pg/meta/_journal.json +21 -0
- package/src/db/pg/schema.ts +195 -27
- package/src/db/post-visibility.ts +117 -0
- package/src/db/schema.ts +222 -27
- package/src/i18n/__tests__/detect.test.ts +26 -81
- package/src/i18n/__tests__/supported-locales.test.ts +83 -0
- package/src/i18n/context.tsx +12 -2
- package/src/i18n/detect.ts +7 -66
- package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
- package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
- package/src/i18n/locales/public/en.po +490 -158
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +487 -155
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +487 -155
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +555 -73
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +601 -119
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +614 -132
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/i18n/locales.ts +94 -0
- package/src/i18n/middleware.ts +12 -2
- package/src/i18n/supported-locales.ts +80 -0
- package/src/lib/__tests__/api-settings.test.ts +11 -2
- package/src/lib/__tests__/asset-path.test.ts +92 -0
- package/src/lib/__tests__/deferred.test.ts +90 -0
- package/src/lib/__tests__/discover-ping.test.ts +89 -0
- package/src/lib/__tests__/discover.test.ts +410 -0
- package/src/lib/__tests__/feed-policy.test.ts +26 -0
- package/src/lib/__tests__/feed.test.ts +1526 -15
- package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
- package/src/lib/__tests__/github-sync-repo-name.test.ts +40 -0
- package/src/lib/__tests__/hosted-signin.test.ts +1 -1
- package/src/lib/__tests__/http-cache.test.ts +162 -0
- package/src/lib/__tests__/image.test.ts +27 -1
- package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
- package/src/lib/__tests__/jant-docs.test.ts +23 -0
- package/src/lib/__tests__/lang-detect.test.ts +231 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +105 -0
- package/src/lib/__tests__/markdown.test.ts +10 -0
- package/src/lib/__tests__/media-helpers.test.ts +81 -0
- package/src/lib/__tests__/navigation.test.ts +135 -1
- package/src/lib/__tests__/resolve-config.test.ts +131 -3
- package/src/lib/__tests__/schemas.test.ts +69 -5
- package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
- package/src/lib/__tests__/slug.test.ts +70 -0
- package/src/lib/__tests__/slugify.test.ts +85 -0
- package/src/lib/__tests__/storage.test.ts +32 -0
- package/src/lib/__tests__/thread-fold.test.ts +113 -0
- package/src/lib/__tests__/timeline.test.ts +87 -0
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +172 -4
- package/src/lib/__tests__/upload-sideload.test.ts +14 -0
- package/src/lib/__tests__/url-fetch.test.ts +1 -2
- package/src/lib/__tests__/url.test.ts +78 -72
- package/src/lib/__tests__/view-language.test.ts +413 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
- package/src/lib/address.ts +57 -0
- package/src/lib/api-posts.ts +12 -0
- package/src/lib/asset-path.ts +98 -1
- package/src/lib/build-env.ts +16 -0
- package/src/lib/collection-groups.ts +5 -1
- package/src/lib/collection-paths.ts +3 -10
- package/src/lib/constants.ts +28 -3
- package/src/lib/copy-field.ts +36 -0
- package/src/lib/crypto.ts +40 -0
- package/src/lib/csp-builder.ts +2 -1
- package/src/lib/deferred.ts +79 -0
- package/src/lib/discover-ping.ts +141 -0
- package/src/lib/discover.ts +439 -0
- package/src/lib/env.ts +134 -0
- package/src/lib/errors.ts +33 -0
- package/src/lib/feed-policy.ts +236 -2
- package/src/lib/feed.ts +926 -73
- package/src/lib/filter-dimensions.ts +1349 -0
- package/src/lib/github-sync-repo-name.ts +45 -0
- package/src/lib/github-sync-site-config.ts +3 -0
- package/src/lib/hosted-signin.ts +1 -1
- package/src/lib/http-cache.ts +148 -0
- package/src/lib/hugo-markdown.ts +62 -0
- package/src/lib/icons.ts +9 -2
- package/src/lib/ids.ts +3 -0
- package/src/lib/image.ts +17 -4
- package/src/lib/jant-docs.ts +42 -0
- package/src/lib/lang-detect.ts +308 -0
- package/src/lib/markdown-manager.ts +392 -2
- package/src/lib/media-helpers.ts +13 -1
- package/src/lib/navigation.ts +78 -11
- package/src/lib/pagination.ts +28 -24
- package/src/lib/per-language-surfaces.ts +67 -0
- package/src/lib/post-body-html.ts +11 -4
- package/src/lib/post-meta.ts +19 -0
- package/src/lib/render.tsx +55 -7
- package/src/lib/resolve-config.ts +111 -6
- package/src/lib/schemas.ts +251 -15
- package/src/lib/site-header-fragment.tsx +2 -1
- package/src/lib/site-unavailable-page.ts +47 -0
- package/src/lib/slug.ts +51 -7
- package/src/lib/slugify.ts +113 -0
- package/src/lib/startup-config.ts +4 -6
- package/src/lib/storage.ts +7 -0
- package/src/lib/summary.ts +50 -0
- package/src/lib/thread-fold.ts +161 -0
- package/src/lib/time.ts +32 -0
- package/src/lib/timeline.ts +43 -2
- package/src/lib/tiptap-render.ts +1 -2
- package/src/lib/tiptap-to-markdown.ts +13 -7
- package/src/lib/upload.ts +16 -13
- package/src/lib/url.ts +159 -86
- package/src/lib/version.ts +10 -8
- package/src/lib/view-language.ts +436 -0
- package/src/lib/view.ts +53 -25
- package/src/lib/viewer-context.ts +62 -0
- package/src/middleware/__tests__/auth.test.ts +70 -2
- package/src/middleware/__tests__/cache-control.test.ts +71 -3
- package/src/middleware/__tests__/onboarding.test.ts +76 -5
- package/src/middleware/__tests__/secure-headers.test.ts +1 -1
- package/src/middleware/__tests__/session.test.ts +181 -33
- package/src/middleware/auth.ts +50 -31
- package/src/middleware/cache-control.ts +75 -10
- package/src/middleware/config.ts +6 -6
- package/src/middleware/error-handler.ts +12 -0
- package/src/middleware/onboarding.ts +32 -10
- package/src/middleware/secure-headers.ts +1 -1
- package/src/middleware/session.ts +96 -8
- package/src/node/__tests__/cli-migrate.test.ts +21 -19
- package/src/node/__tests__/cli-reset-password.test.ts +81 -8
- package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
- package/src/node/__tests__/cli-setup.test.ts +163 -0
- package/src/node/__tests__/cli-site-selection.test.ts +128 -38
- package/src/node/__tests__/cli-site-snapshot.test.ts +67 -0
- package/src/node/__tests__/cli-snapshot-meta.test.ts +20 -0
- package/src/node/__tests__/runtime.test.ts +75 -0
- package/src/node/index.ts +3 -0
- package/src/node/request-handler.ts +3 -1
- package/src/preset.css +76 -22
- package/src/routes/__tests__/feed-validator.test.ts +156 -0
- package/src/routes/api/__tests__/collections.test.ts +114 -0
- package/src/routes/api/__tests__/discover.test.ts +222 -0
- package/src/routes/api/__tests__/nav-items.test.ts +212 -2
- package/src/routes/api/__tests__/post-translations.test.ts +829 -0
- package/src/routes/api/__tests__/posts.test.ts +24 -0
- package/src/routes/api/__tests__/settings.test.ts +24 -3
- package/src/routes/api/__tests__/telegram.test.ts +6 -5
- package/src/routes/api/__tests__/upload.test.ts +34 -0
- package/src/routes/api/collections.ts +49 -6
- package/src/routes/api/custom-urls.ts +0 -1
- package/src/routes/api/discover.ts +95 -0
- package/src/routes/api/export.ts +6 -3
- package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
- package/src/routes/api/internal/sites.ts +0 -1
- package/src/routes/api/nav-items.ts +55 -8
- package/src/routes/api/posts.ts +161 -0
- package/src/routes/api/public/__tests__/archive.test.ts +111 -0
- package/src/routes/api/public/archive.ts +77 -81
- package/src/routes/api/public/posts.ts +44 -7
- package/src/routes/api/smart-collections.ts +132 -0
- package/src/routes/api/telegram.ts +2 -40
- package/src/routes/api/upload.ts +10 -3
- package/src/routes/api/uploads.ts +1 -2
- package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
- package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
- package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
- package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
- package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
- package/src/routes/auth/__tests__/setup.test.ts +278 -26
- package/src/routes/auth/__tests__/signin.test.ts +135 -0
- package/src/routes/auth/setup.tsx +934 -186
- package/src/routes/auth/signin.tsx +10 -8
- package/src/routes/compose.tsx +9 -0
- package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
- package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
- package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
- package/src/routes/dash/__tests__/github-sync-app.test.ts +365 -0
- package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
- package/src/routes/dash/custom-urls.tsx +1 -37
- package/src/routes/dash/settings.tsx +714 -111
- package/src/routes/discover-announce.ts +96 -0
- package/src/routes/feed/__tests__/feed.test.ts +165 -2
- package/src/routes/feed/feed.ts +69 -40
- package/src/routes/feed/sitemap.ts +95 -11
- package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
- package/src/routes/pages/__tests__/archive-params.test.ts +370 -6
- package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
- package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
- package/src/routes/pages/__tests__/collections.test.ts +9 -3
- package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
- package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
- package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
- package/src/routes/pages/__tests__/post-page-round-trips.test.ts +211 -0
- package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
- package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
- package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
- package/src/routes/pages/__tests__/thread-order.test.ts +179 -0
- package/src/routes/pages/archive.tsx +487 -379
- package/src/routes/pages/collection.tsx +94 -96
- package/src/routes/pages/collections.tsx +38 -50
- package/src/routes/pages/featured.tsx +43 -18
- package/src/routes/pages/home.tsx +25 -6
- package/src/routes/pages/language.tsx +113 -0
- package/src/routes/pages/latest.tsx +26 -16
- package/src/routes/pages/new.tsx +3 -0
- package/src/routes/pages/page.tsx +228 -45
- package/src/routes/pages/partials.tsx +41 -0
- package/src/routes/pages/search.tsx +22 -5
- package/src/routes/pages/smart-collection.tsx +340 -0
- package/src/routes/pages/subscribe.tsx +182 -0
- package/src/runtime/__tests__/index.test.ts +19 -0
- package/src/runtime/__tests__/node.test.ts +129 -4
- package/src/runtime/__tests__/readiness.test.ts +23 -0
- package/src/runtime/cloudflare.ts +21 -2
- package/src/runtime/index.ts +27 -1
- package/src/runtime/node.ts +67 -1
- package/src/runtime/readiness.ts +15 -0
- package/src/runtime/site.ts +95 -24
- package/src/services/__tests__/auth.test.ts +116 -7
- package/src/services/__tests__/bootstrap-setup-instance.test.ts +230 -0
- package/src/services/__tests__/collection.test.ts +108 -4
- package/src/services/__tests__/custom-url.test.ts +34 -0
- package/src/services/__tests__/github-app-installations.test.ts +153 -0
- package/src/services/__tests__/github-sync-push.test.ts +46 -0
- package/src/services/__tests__/language.test.ts +495 -0
- package/src/services/__tests__/media.test.ts +31 -0
- package/src/services/__tests__/navigation.test.ts +213 -10
- package/src/services/__tests__/path.test.ts +236 -0
- package/src/services/__tests__/post-language.test.ts +830 -0
- package/src/services/__tests__/post-timeline.test.ts +127 -0
- package/src/services/__tests__/post.test.ts +120 -9
- package/src/services/__tests__/settings.test.ts +228 -17
- package/src/services/__tests__/site-admin.test.ts +5 -5
- package/src/services/__tests__/smart-collection.test.ts +651 -0
- package/src/services/about-page.ts +22 -1
- package/src/services/auth.ts +51 -49
- package/src/services/bootstrap.ts +408 -39
- package/src/services/collection-directory-position.ts +76 -0
- package/src/services/collection.ts +331 -46
- package/src/services/custom-url.ts +29 -24
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +123 -123
- package/src/services/export-theme/layouts/_default/alias.html +27 -1
- package/src/services/export-theme/layouts/_default/list.html +2 -63
- package/src/services/export-theme/layouts/_default/rss.xml +103 -38
- package/src/services/export-theme/layouts/collections/list.html +3 -3
- package/src/services/export-theme/layouts/featured/list.html +1 -4
- package/src/services/export-theme/layouts/index.html +40 -26
- package/src/services/export-theme/layouts/partials/collection-members.html +100 -0
- package/src/services/export-theme/layouts/partials/collection-threads.html +33 -0
- package/src/services/export-theme/layouts/partials/featured-members.html +35 -0
- package/src/services/export-theme/layouts/partials/featured-thread.html +1 -1
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/head.html +1 -1
- package/src/services/export-theme/layouts/partials/header.html +5 -3
- package/src/services/export-theme/layouts/partials/jant-data.html +23 -0
- package/src/services/export-theme/layouts/partials/latest-members.html +48 -0
- package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
- package/src/services/export-theme/layouts/partials/pagination.html +14 -13
- package/src/services/export-theme/layouts/partials/post-card.html +8 -4
- package/src/services/export-theme/layouts/partials/reply.html +2 -1
- package/src/services/export-theme/layouts/partials/smart-collection-members.html +129 -0
- package/src/services/export-theme/layouts/partials/thread-preview.html +41 -31
- package/src/services/export-theme/layouts/post/list.html +1 -1
- package/src/services/export-theme/layouts/smart_collection/list.html +24 -0
- package/src/services/export-theme/styles/main.css +13 -2
- package/src/services/export-theme/theme.toml +1 -1
- package/src/services/export.ts +880 -72
- package/src/services/github-app-installations.ts +171 -4
- package/src/services/github-sync.ts +69 -15
- package/src/services/index.ts +34 -3
- package/src/services/language.ts +412 -0
- package/src/services/mcp.ts +22 -2
- package/src/services/media.ts +22 -7
- package/src/services/navigation.ts +429 -67
- package/src/services/path.ts +254 -6
- package/src/services/post.ts +1234 -190
- package/src/services/search.ts +39 -2
- package/src/services/settings.ts +238 -43
- package/src/services/site-admin.ts +27 -14
- package/src/services/site.ts +160 -32
- package/src/services/smart-collection.ts +636 -0
- package/src/services/telegram.ts +20 -22
- package/src/services/upload-session.ts +1 -3
- package/src/style-author.css +27 -0
- package/src/styles/components-author.css +1622 -0
- package/src/styles/components.css +24 -1571
- package/src/styles/site-media.css +16 -2
- package/src/styles/tokens.css +82 -12
- package/src/styles/ui-author.css +7602 -0
- package/src/styles/ui.css +1090 -7216
- package/src/types/app-context.ts +23 -0
- package/src/types/bindings.ts +8 -0
- package/src/types/config.ts +94 -15
- package/src/types/constants.ts +188 -16
- package/src/types/entities.ts +87 -1
- package/src/types/operations.ts +74 -2
- package/src/types/props.ts +157 -31
- package/src/types/views.ts +55 -1
- package/src/ui/__tests__/font-themes.test.ts +52 -12
- package/src/ui/compose/ComposeDialog.tsx +201 -7
- package/src/ui/compose/ComposePrompt.tsx +1 -5
- package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
- package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
- package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
- package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
- package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
- package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
- package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
- package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
- package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
- package/src/ui/dash/settings/GeneralContent.tsx +277 -113
- package/src/ui/dash/settings/LanguageContent.tsx +409 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
- package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
- package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
- package/src/ui/dash/settings/discover-copy.ts +116 -0
- package/src/ui/feed/LinkCard.tsx +55 -45
- package/src/ui/feed/NoteCard.tsx +9 -4
- package/src/ui/feed/PostStatusBadges.tsx +90 -43
- package/src/ui/feed/QuoteCard.tsx +55 -30
- package/src/ui/feed/ThreadPreview.tsx +6 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
- package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
- package/src/ui/feed/thread-preview-state.ts +20 -6
- package/src/ui/font-themes.ts +39 -29
- package/src/ui/layouts/BaseLayout.tsx +155 -10
- package/src/ui/layouts/SiteLayout.tsx +192 -34
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
- package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
- package/src/ui/pages/ArchivePage.tsx +282 -289
- package/src/ui/pages/CollectionPage.tsx +86 -76
- package/src/ui/pages/CollectionsPage.tsx +5 -0
- package/src/ui/pages/ComposePage.tsx +8 -0
- package/src/ui/pages/PostPage.tsx +47 -0
- package/src/ui/pages/SearchPage.tsx +3 -3
- package/src/ui/pages/SmartCollectionPage.tsx +434 -0
- package/src/ui/pages/SubscribePage.tsx +236 -0
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +65 -9
- package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
- package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
- package/src/ui/shared/CollectionDirectory.tsx +178 -3
- package/src/ui/shared/CollectionsManager.tsx +70 -17
- package/src/ui/shared/CopyField.tsx +127 -0
- package/src/ui/shared/EmptyPostContent.tsx +23 -0
- package/src/ui/shared/MediaGallery.tsx +125 -46
- package/src/ui/shared/Pagination.tsx +20 -5
- package/src/ui/shared/PostFooter.tsx +20 -11
- package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
- package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
- package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
- package/src/ui/shared/__tests__/pagination.test.ts +38 -24
- package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
- package/src/ui/shared/archive-labels.ts +98 -0
- package/src/ui/shared/collection-management-labels.ts +94 -16
- package/src/ui/shared/navigation-labels.ts +45 -71
- package/src/ui/shared/post-article-attributes.ts +2 -0
- package/src/ui/shared/smart-collection-labels.ts +343 -0
- package/dist/app-C75Zc8dD.js +0 -6
- package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
- package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
- package/dist/client/_assets/chunks/url-CU9pEbk5.js +0 -1
- package/dist/client/_assets/client-Bq0vre8Y.js +0 -296
- package/dist/client/_assets/client-D3VZyG4L.css +0 -2
- package/dist/client/_assets/client-auth-F2rb9WfF.js +0 -5571
- package/dist/env-BPYViDJJ.js +0 -281
- package/dist/export-Dl5KCiEs.js +0 -5562
- package/dist/github-api-BNF35Lkx.js +0 -176
- package/dist/github-app-DdiD-bC4.js +0 -275
- package/dist/github-sync-Bqg62IvV.js +0 -4
- package/dist/github-sync-HdK2EgvJ.js +0 -431
- package/dist/url-CbLAtlZe.js +0 -900
- package/src/client/__tests__/collection-created-notice.test.ts +0 -36
- package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
- package/src/client/__tests__/post-form-bridge.test.ts +0 -82
- package/src/client/collection-created-notice.ts +0 -66
- package/src/client/collection-form-bridge.ts +0 -191
- package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
- package/src/client/components/jant-post-form.ts +0 -422
- package/src/client/components/post-form-template.ts +0 -352
- package/src/client/components/post-form-types.ts +0 -96
- package/src/client/media-metadata.ts +0 -247
- package/src/client/post-form-bridge.ts +0 -193
- package/src/client/tiptap/exitable-marks.ts +0 -73
- package/src/ui/pages/CollectionEditorPage.tsx +0 -175
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
* Routes call these methods instead of touching the settings table to
|
|
10
10
|
* keep the model relational (one entry per (installation_id, site_id)
|
|
11
11
|
* pair) instead of serialising a JSON list into a single settings row.
|
|
12
|
+
*
|
|
13
|
+
* Two scopes matter and they are not interchangeable. A *site* scope
|
|
14
|
+
* answers "which accounts is this blog syncing through". A *user* scope
|
|
15
|
+
* answers "which accounts has this person already authorized anywhere",
|
|
16
|
+
* which is what the connect flow needs: a GitHub App installs once per
|
|
17
|
+
* GitHub account, so someone who connected their first site can never
|
|
18
|
+
* install it a second time — GitHub sends them to the installation's
|
|
19
|
+
* Configure page and never calls back. Their other sites have to be able
|
|
20
|
+
* to reuse that installation without leaving Jant.
|
|
12
21
|
*/
|
|
13
22
|
|
|
14
23
|
import { and, eq, inArray } from "drizzle-orm";
|
|
@@ -35,11 +44,56 @@ export interface StoredGitHubAppInstallation {
|
|
|
35
44
|
addedAt: number;
|
|
36
45
|
}
|
|
37
46
|
|
|
47
|
+
/**
|
|
48
|
+
* An installation as seen by one person rather than by one site: the
|
|
49
|
+
* same installation may back several of their sites, so the site id
|
|
50
|
+
* drops out and `addedAt` carries the most recent binding.
|
|
51
|
+
*/
|
|
52
|
+
export interface VisibleGitHubAppInstallation {
|
|
53
|
+
installationId: string;
|
|
54
|
+
account: GitHubInstallationAccount;
|
|
55
|
+
/** Unix seconds — most recent binding of this installation to any of the user's sites. */
|
|
56
|
+
addedAt: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
export interface GitHubAppInstallationsService {
|
|
39
60
|
/** Installations this site has authorized, newest-first by `addedAt`. */
|
|
40
61
|
listInstallationsForSite(
|
|
41
62
|
siteId: string,
|
|
42
63
|
): Promise<StoredGitHubAppInstallation[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Installations authorized on any site this user is a member of,
|
|
66
|
+
* deduplicated by installation and newest-first by `addedAt`.
|
|
67
|
+
*
|
|
68
|
+
* This is the set the connect flow may offer and act on: membership is
|
|
69
|
+
* the tenancy boundary, so one person's sites share their GitHub
|
|
70
|
+
* accounts while other tenants' installations stay invisible.
|
|
71
|
+
*/
|
|
72
|
+
listInstallationsForUser(
|
|
73
|
+
userId: string,
|
|
74
|
+
): Promise<VisibleGitHubAppInstallation[]>;
|
|
75
|
+
/**
|
|
76
|
+
* One installation from `listInstallationsForUser`, or `null` when the
|
|
77
|
+
* user has no site bound to it. Routes use this to authorize an
|
|
78
|
+
* `installationId` that arrived in a request.
|
|
79
|
+
*/
|
|
80
|
+
findInstallationForUser(
|
|
81
|
+
installationId: string,
|
|
82
|
+
userId: string,
|
|
83
|
+
): Promise<VisibleGitHubAppInstallation | null>;
|
|
84
|
+
/**
|
|
85
|
+
* Installations the user's sites are *actively syncing through*, read
|
|
86
|
+
* from each site's settings rather than from the junction table.
|
|
87
|
+
*
|
|
88
|
+
* The two can disagree: settings are written when a site connects,
|
|
89
|
+
* the binding when it was installed, and a site that connected while
|
|
90
|
+
* the binding write failed keeps syncing with nothing to show for it.
|
|
91
|
+
* Callers use this to rebuild the missing row — they have to fetch the
|
|
92
|
+
* account from GitHub, which is why this returns ids and not accounts.
|
|
93
|
+
*/
|
|
94
|
+
listSyncingInstallationsForUser(
|
|
95
|
+
userId: string,
|
|
96
|
+
): Promise<Array<{ siteId: string; installationId: string }>>;
|
|
43
97
|
/** Site ids that share the given installation. */
|
|
44
98
|
listSitesForInstallation(installationId: string): Promise<string[]>;
|
|
45
99
|
/**
|
|
@@ -56,6 +110,19 @@ export interface GitHubAppInstallationsService {
|
|
|
56
110
|
): Promise<void>;
|
|
57
111
|
/** Remove a single (installation, site) binding. */
|
|
58
112
|
removeInstallation(installationId: string, siteId: string): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Remove the installation from every site this user is a member of.
|
|
115
|
+
*
|
|
116
|
+
* Used when GitHub reports the installation as gone (401/404): the
|
|
117
|
+
* binding that made it visible may live on another of the user's
|
|
118
|
+
* sites, so clearing only the current site would leave a dead account
|
|
119
|
+
* in their picker forever. Other tenants' bindings are left to the
|
|
120
|
+
* App-level webhook, which sees the authoritative `installation.deleted`.
|
|
121
|
+
*/
|
|
122
|
+
removeInstallationForUser(
|
|
123
|
+
installationId: string,
|
|
124
|
+
userId: string,
|
|
125
|
+
): Promise<void>;
|
|
59
126
|
/**
|
|
60
127
|
* Remove the installation from every site it's bound to and return
|
|
61
128
|
* the previously-bound site ids so callers can fan out side effects
|
|
@@ -94,7 +161,63 @@ export function createGitHubAppInstallationsService(
|
|
|
94
161
|
db: Database,
|
|
95
162
|
databaseSchema: DatabaseSchema = sqliteSchemaBundle,
|
|
96
163
|
): GitHubAppInstallationsService {
|
|
97
|
-
const { githubAppInstallation, settings } = databaseSchema;
|
|
164
|
+
const { githubAppInstallation, settings, siteMembers } = databaseSchema;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Rows for every binding on a site the user belongs to. One join, not
|
|
168
|
+
* a per-site fan-out — a user has few sites but the query shape is
|
|
169
|
+
* what gets copied.
|
|
170
|
+
*/
|
|
171
|
+
function selectUserBindings(userId: string, installationId?: string) {
|
|
172
|
+
const scope = eq(siteMembers.userId, userId);
|
|
173
|
+
return db
|
|
174
|
+
.select({
|
|
175
|
+
installationId: githubAppInstallation.installationId,
|
|
176
|
+
accountLogin: githubAppInstallation.accountLogin,
|
|
177
|
+
accountType: githubAppInstallation.accountType,
|
|
178
|
+
accountAvatarUrl: githubAppInstallation.accountAvatarUrl,
|
|
179
|
+
addedAt: githubAppInstallation.addedAt,
|
|
180
|
+
})
|
|
181
|
+
.from(githubAppInstallation)
|
|
182
|
+
.innerJoin(
|
|
183
|
+
siteMembers,
|
|
184
|
+
eq(siteMembers.siteId, githubAppInstallation.siteId),
|
|
185
|
+
)
|
|
186
|
+
.where(
|
|
187
|
+
installationId
|
|
188
|
+
? and(scope, eq(githubAppInstallation.installationId, installationId))
|
|
189
|
+
: scope,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Collapse per-site bindings into one entry per installation. */
|
|
194
|
+
function toVisible(
|
|
195
|
+
rows: Array<{
|
|
196
|
+
installationId: string;
|
|
197
|
+
accountLogin: string;
|
|
198
|
+
accountType: string;
|
|
199
|
+
accountAvatarUrl: string;
|
|
200
|
+
addedAt: number;
|
|
201
|
+
}>,
|
|
202
|
+
): VisibleGitHubAppInstallation[] {
|
|
203
|
+
const byInstallation = new Map<string, VisibleGitHubAppInstallation>();
|
|
204
|
+
for (const row of rows) {
|
|
205
|
+
const entry = {
|
|
206
|
+
installationId: row.installationId,
|
|
207
|
+
account: {
|
|
208
|
+
login: row.accountLogin,
|
|
209
|
+
type: toAccountType(row.accountType),
|
|
210
|
+
avatarUrl: row.accountAvatarUrl,
|
|
211
|
+
},
|
|
212
|
+
addedAt: row.addedAt,
|
|
213
|
+
};
|
|
214
|
+
const existing = byInstallation.get(row.installationId);
|
|
215
|
+
if (!existing || existing.addedAt < entry.addedAt) {
|
|
216
|
+
byInstallation.set(row.installationId, entry);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return [...byInstallation.values()].sort((a, b) => b.addedAt - a.addedAt);
|
|
220
|
+
}
|
|
98
221
|
|
|
99
222
|
// Per-site settings mutation helpers. These sit alongside the
|
|
100
223
|
// junction-table operations because the webhook fan-out inherently
|
|
@@ -152,6 +275,32 @@ export function createGitHubAppInstallationsService(
|
|
|
152
275
|
return rows.map(toStored).sort((a, b) => b.addedAt - a.addedAt);
|
|
153
276
|
},
|
|
154
277
|
|
|
278
|
+
async listInstallationsForUser(userId) {
|
|
279
|
+
return toVisible(await selectUserBindings(userId));
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
async findInstallationForUser(installationId, userId) {
|
|
283
|
+
const rows = await selectUserBindings(userId, installationId);
|
|
284
|
+
return toVisible(rows)[0] ?? null;
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
async listSyncingInstallationsForUser(userId) {
|
|
288
|
+
const rows = await db
|
|
289
|
+
.select({
|
|
290
|
+
siteId: settings.siteId,
|
|
291
|
+
installationId: settings.value,
|
|
292
|
+
})
|
|
293
|
+
.from(settings)
|
|
294
|
+
.innerJoin(siteMembers, eq(siteMembers.siteId, settings.siteId))
|
|
295
|
+
.where(
|
|
296
|
+
and(
|
|
297
|
+
eq(siteMembers.userId, userId),
|
|
298
|
+
eq(settings.key, "GITHUB_SYNC_APP_INSTALLATION_ID"),
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
return rows.filter((row) => row.installationId.trim().length > 0);
|
|
302
|
+
},
|
|
303
|
+
|
|
155
304
|
async listSitesForInstallation(installationId) {
|
|
156
305
|
const rows = await db
|
|
157
306
|
.select({ siteId: githubAppInstallation.siteId })
|
|
@@ -198,6 +347,21 @@ export function createGitHubAppInstallationsService(
|
|
|
198
347
|
);
|
|
199
348
|
},
|
|
200
349
|
|
|
350
|
+
async removeInstallationForUser(installationId, userId) {
|
|
351
|
+
const memberSites = db
|
|
352
|
+
.select({ siteId: siteMembers.siteId })
|
|
353
|
+
.from(siteMembers)
|
|
354
|
+
.where(eq(siteMembers.userId, userId));
|
|
355
|
+
await db
|
|
356
|
+
.delete(githubAppInstallation)
|
|
357
|
+
.where(
|
|
358
|
+
and(
|
|
359
|
+
eq(githubAppInstallation.installationId, installationId),
|
|
360
|
+
inArray(githubAppInstallation.siteId, memberSites),
|
|
361
|
+
),
|
|
362
|
+
);
|
|
363
|
+
},
|
|
364
|
+
|
|
201
365
|
async removeInstallationEverywhere(installationId) {
|
|
202
366
|
const rows = await db
|
|
203
367
|
.select({ siteId: githubAppInstallation.siteId })
|
|
@@ -277,6 +441,11 @@ export function createGitHubAppInstallationsService(
|
|
|
277
441
|
};
|
|
278
442
|
}
|
|
279
443
|
|
|
444
|
+
/** The column is a checked enum; narrow it without trusting the read. */
|
|
445
|
+
function toAccountType(value: string): GitHubAccountType {
|
|
446
|
+
return value === "Organization" ? "Organization" : "User";
|
|
447
|
+
}
|
|
448
|
+
|
|
280
449
|
function toStored(row: {
|
|
281
450
|
installationId: string;
|
|
282
451
|
siteId: string;
|
|
@@ -285,14 +454,12 @@ function toStored(row: {
|
|
|
285
454
|
accountAvatarUrl: string;
|
|
286
455
|
addedAt: number;
|
|
287
456
|
}): StoredGitHubAppInstallation {
|
|
288
|
-
const type: GitHubAccountType =
|
|
289
|
-
row.accountType === "Organization" ? "Organization" : "User";
|
|
290
457
|
return {
|
|
291
458
|
installationId: row.installationId,
|
|
292
459
|
siteId: row.siteId,
|
|
293
460
|
account: {
|
|
294
461
|
login: row.accountLogin,
|
|
295
|
-
type,
|
|
462
|
+
type: toAccountType(row.accountType),
|
|
296
463
|
avatarUrl: row.accountAvatarUrl,
|
|
297
464
|
},
|
|
298
465
|
addedAt: row.addedAt,
|
|
@@ -32,7 +32,13 @@ import { getInstallationToken } from "../lib/github-app.js";
|
|
|
32
32
|
import type { GitHubAppEnvConfig } from "../lib/env.js";
|
|
33
33
|
import { parseFrontMatter } from "../lib/hugo-markdown.js";
|
|
34
34
|
import { markdownToTiptapJson } from "../lib/markdown-to-tiptap.js";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
createExportService,
|
|
37
|
+
isStoredExportFile,
|
|
38
|
+
readStoredExportFile,
|
|
39
|
+
type ExportFile,
|
|
40
|
+
type SiteConfig,
|
|
41
|
+
} from "./export.js";
|
|
36
42
|
import type { PostService } from "./post.js";
|
|
37
43
|
import type { PathService } from "./path.js";
|
|
38
44
|
import type { CollectionService } from "./collection.js";
|
|
@@ -116,6 +122,33 @@ export function isManagedPath(path: string): boolean {
|
|
|
116
122
|
return JANT_MANAGED_GLOBS.some((g) => pathMatchesManagedGlob(path, g));
|
|
117
123
|
}
|
|
118
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Pick the export files this push should write.
|
|
127
|
+
*
|
|
128
|
+
* Everything is written every push except scaffolding the repo already has.
|
|
129
|
+
* `wrangler.jsonc` is the case that needs this: its Worker name defaults to
|
|
130
|
+
* the repository name, and a user whose Worker is named differently has to
|
|
131
|
+
* correct it by hand. Rewriting the file every push would revert that, and
|
|
132
|
+
* every build after it would fail on the name mismatch.
|
|
133
|
+
*
|
|
134
|
+
* @param exportFiles - Everything the export generated.
|
|
135
|
+
* @param existingPaths - Repo-relative paths already present on the remote.
|
|
136
|
+
* @returns The subset to include in the commit.
|
|
137
|
+
* @example
|
|
138
|
+
* selectFilesToWrite(files, new Set(["wrangler.jsonc"]));
|
|
139
|
+
*/
|
|
140
|
+
export function selectFilesToWrite(
|
|
141
|
+
exportFiles: readonly ExportFile[],
|
|
142
|
+
existingPaths: ReadonlySet<string>,
|
|
143
|
+
): ExportFile[] {
|
|
144
|
+
return exportFiles.filter(
|
|
145
|
+
(file) =>
|
|
146
|
+
isStoredExportFile(file) ||
|
|
147
|
+
!file.scaffoldOnce ||
|
|
148
|
+
!existingPaths.has(file.path),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
119
152
|
/**
|
|
120
153
|
* Compute the tree items that should null-out files on the remote HEAD
|
|
121
154
|
* which Jant claims ownership of (matches `JANT_MANAGED_GLOBS`) but is
|
|
@@ -458,6 +491,9 @@ export function createGitHubSyncService(
|
|
|
458
491
|
const exportService = createExportService(services, siteConfig, {
|
|
459
492
|
storage: deps.storage,
|
|
460
493
|
bundleMedia: false,
|
|
494
|
+
// Cloudflare names a Worker imported from this repository after it,
|
|
495
|
+
// so the deploy config's Worker name matches with no editing.
|
|
496
|
+
repoName: repo,
|
|
461
497
|
});
|
|
462
498
|
const exportFiles = await exportService.generateHugoFiles();
|
|
463
499
|
|
|
@@ -488,6 +524,26 @@ export function createGitHubSyncService(
|
|
|
488
524
|
marker,
|
|
489
525
|
);
|
|
490
526
|
|
|
527
|
+
// The HEAD tree is needed twice: to decide which scaffold files the
|
|
528
|
+
// repo already has, and (below) to delete managed files this push no
|
|
529
|
+
// longer generates. Reading it once, before the tree is assembled,
|
|
530
|
+
// covers both.
|
|
531
|
+
const headCommit = await client.getCommit(owner, repo, headSha);
|
|
532
|
+
const headTree = await client.getTree(owner, repo, headCommit.treeSha, {
|
|
533
|
+
recursive: true,
|
|
534
|
+
});
|
|
535
|
+
if (headTree.truncated) {
|
|
536
|
+
throw new Error(
|
|
537
|
+
"GitHub tree exceeds API limits (>100k entries or >7MB); " +
|
|
538
|
+
"incremental deletion cannot run safely against this repo.",
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
const existingPaths = new Set<string>(
|
|
542
|
+
headTree.tree
|
|
543
|
+
.filter((item) => item.type === "blob")
|
|
544
|
+
.map((item) => item.path),
|
|
545
|
+
);
|
|
546
|
+
|
|
491
547
|
// Convert to Git tree items. The ownership marker is always emitted;
|
|
492
548
|
// `created_at` is preserved across pushes (Git dedupes identical
|
|
493
549
|
// blobs by SHA, so a no-op marker push is still cheap).
|
|
@@ -500,20 +556,28 @@ export function createGitHubSyncService(
|
|
|
500
556
|
},
|
|
501
557
|
];
|
|
502
558
|
|
|
503
|
-
for (const file of exportFiles) {
|
|
504
|
-
|
|
559
|
+
for (const file of selectFilesToWrite(exportFiles, existingPaths)) {
|
|
560
|
+
// Sync links media by URL, so a stored file only turns up when the
|
|
561
|
+
// site URL is unknown and the export falls back to bundling.
|
|
562
|
+
const content = isStoredExportFile(file)
|
|
563
|
+
? deps.storage
|
|
564
|
+
? await readStoredExportFile(file, deps.storage)
|
|
565
|
+
: null
|
|
566
|
+
: file.content;
|
|
567
|
+
if (content === null) continue;
|
|
568
|
+
if (typeof content === "string") {
|
|
505
569
|
treeItems.push({
|
|
506
570
|
path: file.path,
|
|
507
571
|
mode: "100644",
|
|
508
572
|
type: "blob",
|
|
509
|
-
content
|
|
573
|
+
content,
|
|
510
574
|
});
|
|
511
575
|
} else {
|
|
512
576
|
// Binary files need to be created as blobs first
|
|
513
577
|
const blob = await client.createBlob(
|
|
514
578
|
owner,
|
|
515
579
|
repo,
|
|
516
|
-
uint8ArrayToBase64(
|
|
580
|
+
uint8ArrayToBase64(content),
|
|
517
581
|
"base64",
|
|
518
582
|
);
|
|
519
583
|
treeItems.push({
|
|
@@ -530,16 +594,6 @@ export function createGitHubSyncService(
|
|
|
530
594
|
// removed. Without this, deleting a post in Jant would leave the
|
|
531
595
|
// old file behind on GitHub, because base_tree preserves everything
|
|
532
596
|
// we don't explicitly overwrite.
|
|
533
|
-
const headCommit = await client.getCommit(owner, repo, headSha);
|
|
534
|
-
const headTree = await client.getTree(owner, repo, headCommit.treeSha, {
|
|
535
|
-
recursive: true,
|
|
536
|
-
});
|
|
537
|
-
if (headTree.truncated) {
|
|
538
|
-
throw new Error(
|
|
539
|
-
"GitHub tree exceeds API limits (>100k entries or >7MB); " +
|
|
540
|
-
"incremental deletion cannot run safely against this repo.",
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
597
|
const writtenPaths = new Set<string>(treeItems.map((item) => item.path));
|
|
544
598
|
treeItems.push(...computeManagedDeletions(headTree.tree, writtenPaths));
|
|
545
599
|
|
package/src/services/index.ts
CHANGED
|
@@ -21,6 +21,10 @@ import {
|
|
|
21
21
|
createCollectionService,
|
|
22
22
|
type CollectionService,
|
|
23
23
|
} from "./collection.js";
|
|
24
|
+
import {
|
|
25
|
+
createSmartCollectionService,
|
|
26
|
+
type SmartCollectionService,
|
|
27
|
+
} from "./smart-collection.js";
|
|
24
28
|
import { createSearchService, type SearchService } from "./search.js";
|
|
25
29
|
import { createNavItemService, type NavItemService } from "./navigation.js";
|
|
26
30
|
import { createAuthService, type AuthService } from "./auth.js";
|
|
@@ -36,6 +40,7 @@ import {
|
|
|
36
40
|
type SiteProfileService,
|
|
37
41
|
} from "./site-profile.js";
|
|
38
42
|
import { createAboutPageService, type AboutPageService } from "./about-page.js";
|
|
43
|
+
import { createLanguageService, type LanguageService } from "./language.js";
|
|
39
44
|
import {
|
|
40
45
|
createUploadSessionService,
|
|
41
46
|
type UploadSessionService,
|
|
@@ -57,6 +62,7 @@ export interface Services {
|
|
|
57
62
|
media: MediaService;
|
|
58
63
|
uploads: UploadSessionService;
|
|
59
64
|
collections: CollectionService;
|
|
65
|
+
smartCollections: SmartCollectionService;
|
|
60
66
|
search: SearchService;
|
|
61
67
|
navItems: NavItemService;
|
|
62
68
|
auth: AuthService;
|
|
@@ -66,6 +72,7 @@ export interface Services {
|
|
|
66
72
|
siteMembers: SiteMemberService;
|
|
67
73
|
siteProfile: SiteProfileService;
|
|
68
74
|
aboutPage: AboutPageService;
|
|
75
|
+
language: LanguageService;
|
|
69
76
|
githubAppInstallations: GitHubAppInstallationsService;
|
|
70
77
|
telegram: TelegramService;
|
|
71
78
|
}
|
|
@@ -82,6 +89,8 @@ export function createServices(
|
|
|
82
89
|
siteResolutionMode?: "single-site" | "host-based";
|
|
83
90
|
enforceHostedMediaQuota?: boolean;
|
|
84
91
|
hostedControlPlane?: HostedControlPlaneClient | null;
|
|
92
|
+
/** Signing key for derived tokens, e.g. the account-deletion CSRF token. */
|
|
93
|
+
authSecret?: string;
|
|
85
94
|
},
|
|
86
95
|
): Services {
|
|
87
96
|
const databaseSchema = config?.schema ?? sqliteSchemaBundle;
|
|
@@ -89,7 +98,7 @@ export function createServices(
|
|
|
89
98
|
const site = createSiteService(db, databaseSchema);
|
|
90
99
|
const settings = createSettingsService(db, siteId, databaseSchema, dialect);
|
|
91
100
|
const paths = createPathService(db, siteId, databaseSchema);
|
|
92
|
-
const navItems = createNavItemService(db, siteId, databaseSchema);
|
|
101
|
+
const navItems = createNavItemService(db, siteId, databaseSchema, paths);
|
|
93
102
|
const posts = createPostService(
|
|
94
103
|
db,
|
|
95
104
|
{
|
|
@@ -100,12 +109,21 @@ export function createServices(
|
|
|
100
109
|
paths,
|
|
101
110
|
databaseSchema,
|
|
102
111
|
);
|
|
112
|
+
const smartCollections = createSmartCollectionService(
|
|
113
|
+
db,
|
|
114
|
+
siteId,
|
|
115
|
+
paths,
|
|
116
|
+
posts,
|
|
117
|
+
databaseSchema,
|
|
118
|
+
config?.slugIdLength ?? 5,
|
|
119
|
+
);
|
|
103
120
|
const collections = createCollectionService(
|
|
104
121
|
db,
|
|
105
122
|
siteId,
|
|
106
123
|
paths,
|
|
107
124
|
databaseSchema,
|
|
108
125
|
dialect,
|
|
126
|
+
smartCollections,
|
|
109
127
|
);
|
|
110
128
|
const media = createMediaService(db, siteId, databaseSchema, dialect, {
|
|
111
129
|
enforceHostedQuota: config?.enforceHostedMediaQuota ?? false,
|
|
@@ -121,6 +139,7 @@ export function createServices(
|
|
|
121
139
|
media,
|
|
122
140
|
uploads: createUploadSessionService(db, siteId, media, databaseSchema),
|
|
123
141
|
collections,
|
|
142
|
+
smartCollections,
|
|
124
143
|
search: createSearchService(rawQuery, siteId, dialect),
|
|
125
144
|
navItems,
|
|
126
145
|
auth: createAuthService(
|
|
@@ -128,13 +147,16 @@ export function createServices(
|
|
|
128
147
|
settings,
|
|
129
148
|
{
|
|
130
149
|
databaseDialect: dialect,
|
|
150
|
+
authSecret: config?.authSecret,
|
|
131
151
|
},
|
|
132
152
|
databaseSchema,
|
|
133
153
|
),
|
|
134
154
|
apiTokens: createApiTokenService(db, siteId, databaseSchema),
|
|
135
|
-
bootstrap: createBootstrapService(db, {
|
|
155
|
+
bootstrap: createBootstrapService(db, siteId, {
|
|
136
156
|
schema: databaseSchema,
|
|
157
|
+
databaseDialect: dialect,
|
|
137
158
|
bootstrapSite: config?.bootstrapSite,
|
|
159
|
+
siteResolutionMode: config?.siteResolutionMode,
|
|
138
160
|
}),
|
|
139
161
|
siteAdmin: createSiteAdminService(db, databaseSchema, dialect, {
|
|
140
162
|
siteResolutionMode: config?.siteResolutionMode,
|
|
@@ -156,7 +178,9 @@ export function createServices(
|
|
|
156
178
|
paths,
|
|
157
179
|
posts,
|
|
158
180
|
collections,
|
|
181
|
+
smartCollections,
|
|
159
182
|
}),
|
|
183
|
+
language: createLanguageService({ settings, posts, paths }),
|
|
160
184
|
githubAppInstallations: createGitHubAppInstallationsService(
|
|
161
185
|
db,
|
|
162
186
|
databaseSchema,
|
|
@@ -170,17 +194,24 @@ export type { SiteService } from "./site.js";
|
|
|
170
194
|
export type { PathService } from "./path.js";
|
|
171
195
|
export type { PostService, PostFilters, PostDeleteDeps } from "./post.js";
|
|
172
196
|
export type { CustomUrlService } from "./custom-url.js";
|
|
197
|
+
export type {
|
|
198
|
+
SmartCollectionService,
|
|
199
|
+
SmartCollectionViewer,
|
|
200
|
+
} from "./smart-collection.js";
|
|
173
201
|
export type { MediaService, MediaFilters } from "./media.js";
|
|
174
202
|
export type { UploadSessionService } from "./upload-session.js";
|
|
175
203
|
export type { CollectionService } from "./collection.js";
|
|
176
204
|
export type { SearchService, SearchResult, SearchOptions } from "./search.js";
|
|
177
205
|
export type { NavItemService } from "./navigation.js";
|
|
178
206
|
export type { AboutPageService, AboutPageStatus } from "./about-page.js";
|
|
207
|
+
export type { LanguageService, LanguageState } from "./language.js";
|
|
179
208
|
export type { AuthService, DeleteAccountDeps } from "./auth.js";
|
|
180
209
|
export type { ApiTokenService } from "./api-token.js";
|
|
181
210
|
export type {
|
|
182
211
|
BootstrapService,
|
|
183
|
-
|
|
212
|
+
CompleteSiteSetupData,
|
|
213
|
+
CompleteSiteSetupDeps,
|
|
214
|
+
ProvisionOwnerAccountData,
|
|
184
215
|
} from "./bootstrap.js";
|
|
185
216
|
export type { SiteMemberService } from "./site-member.js";
|
|
186
217
|
export type { SiteProfileService } from "./site-profile.js";
|