@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
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
bootstrapCliRuntime,
|
|
8
8
|
getCliRuntimeLabel,
|
|
9
9
|
} from "../lib/runtime-target.js";
|
|
10
|
-
import { resolveCliSite } from "../lib/site-selection.js";
|
|
10
|
+
import { parseCliSiteSelector, resolveCliSite } from "../lib/site-selection.js";
|
|
11
11
|
|
|
12
12
|
export async function run(argv) {
|
|
13
13
|
const { values } = parseArgs({
|
|
@@ -29,7 +29,9 @@ export async function run(argv) {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
if (values.help) {
|
|
32
|
-
console.log(
|
|
32
|
+
console.log(
|
|
33
|
+
"Usage: jant reset-password [--local | --remote | --node] [--site <key|id> | --host <host> | --url <url>]",
|
|
34
|
+
);
|
|
33
35
|
console.log("");
|
|
34
36
|
console.log("Generate a password reset token (expires in 15 minutes).");
|
|
35
37
|
console.log("");
|
|
@@ -37,7 +39,7 @@ export async function run(argv) {
|
|
|
37
39
|
console.log(" --local Force local D1 instead of DATABASE_URL");
|
|
38
40
|
console.log(" --remote Run against remote D1 database (default: local)");
|
|
39
41
|
console.log(" --node Force Node runtime even if DATABASE_URL is unset");
|
|
40
|
-
console.log(" --site Target site id");
|
|
42
|
+
console.log(" --site Target site key or id");
|
|
41
43
|
console.log(" --host Target site host");
|
|
42
44
|
console.log(" --url Target site URL");
|
|
43
45
|
console.log(" --path-prefix Path prefix used with --host");
|
|
@@ -53,10 +55,20 @@ export async function run(argv) {
|
|
|
53
55
|
"If DATABASE_URL or DATA_DIR is then set and no runtime flag is passed,",
|
|
54
56
|
);
|
|
55
57
|
console.log("this command uses the Node database runtime.");
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log(
|
|
60
|
+
"With SITE_RESOLUTION_MODE=host-based, pass one of --site, --host, or --url.",
|
|
61
|
+
);
|
|
56
62
|
process.exit(0);
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
const { runtime } = bootstrapCliRuntime(values);
|
|
66
|
+
const siteFlags = {
|
|
67
|
+
host: values.host,
|
|
68
|
+
pathPrefix: values["path-prefix"],
|
|
69
|
+
site: values.site,
|
|
70
|
+
url: values.url,
|
|
71
|
+
};
|
|
60
72
|
|
|
61
73
|
const token = randomBytes(32).toString("hex");
|
|
62
74
|
const hash = createHash("sha256").update(token).digest("hex");
|
|
@@ -65,11 +77,15 @@ export async function run(argv) {
|
|
|
65
77
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
66
78
|
|
|
67
79
|
if (runtime === "node") {
|
|
80
|
+
const siteSelector = parseCliSiteSelector(siteFlags);
|
|
68
81
|
const { createNodeCliRuntime } = await loadNodeRuntime();
|
|
69
82
|
const nodeDatabase = await openNodeDatabase(process.env);
|
|
70
83
|
|
|
71
84
|
try {
|
|
72
|
-
const nodeRuntime = await createNodeCliRuntime(
|
|
85
|
+
const nodeRuntime = await createNodeCliRuntime(
|
|
86
|
+
nodeDatabase.bindings,
|
|
87
|
+
siteSelector,
|
|
88
|
+
);
|
|
73
89
|
await nodeRuntime.services.settings.set("PASSWORD_RESET_TOKEN", value);
|
|
74
90
|
} finally {
|
|
75
91
|
await nodeDatabase.close();
|
|
@@ -95,10 +111,7 @@ export async function run(argv) {
|
|
|
95
111
|
},
|
|
96
112
|
{
|
|
97
113
|
env: process.env,
|
|
98
|
-
|
|
99
|
-
pathPrefix: values["path-prefix"],
|
|
100
|
-
site: values.site,
|
|
101
|
-
url: values.url,
|
|
114
|
+
...siteFlags,
|
|
102
115
|
},
|
|
103
116
|
);
|
|
104
117
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { loadNodeRuntime } from "../lib/load-node-runtime.js";
|
|
3
|
+
import { openNodeDatabase } from "../lib/node-database.js";
|
|
4
|
+
import { bootstrapCliRuntime } from "../lib/runtime-target.js";
|
|
5
|
+
|
|
6
|
+
function printHelp() {
|
|
7
|
+
console.log(
|
|
8
|
+
"Usage: jant setup --email <address> --password-stdin [--site-name <name>] [--language <tag>] [--time-zone <zone>] [--site-id <id>]",
|
|
9
|
+
);
|
|
10
|
+
console.log("");
|
|
11
|
+
console.log(
|
|
12
|
+
"Set up a self-hosted install without the browser: the owner account, the site, and onboarding.",
|
|
13
|
+
);
|
|
14
|
+
console.log("Does the same as the two /setup screens.");
|
|
15
|
+
console.log("");
|
|
16
|
+
console.log("Options:");
|
|
17
|
+
console.log(" --email The owner's sign-in address");
|
|
18
|
+
console.log(
|
|
19
|
+
" --password-stdin Read the owner's password from standard input",
|
|
20
|
+
);
|
|
21
|
+
console.log(
|
|
22
|
+
" --site-name The site's name (default: the built-in fallback name)",
|
|
23
|
+
);
|
|
24
|
+
console.log(
|
|
25
|
+
" --language Language the site publishes in, as a BCP 47 tag (default: en)",
|
|
26
|
+
);
|
|
27
|
+
console.log(" --time-zone The site's IANA time zone (default: UTC)");
|
|
28
|
+
console.log(
|
|
29
|
+
" --site-id Create the site with this id instead of a random one",
|
|
30
|
+
);
|
|
31
|
+
console.log(
|
|
32
|
+
" --node Force Node runtime even if DATABASE_URL is unset",
|
|
33
|
+
);
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log("Safe to run on every start. An install that has finished setup");
|
|
36
|
+
console.log(
|
|
37
|
+
"is left unchanged, so a password changed since then is not reset.",
|
|
38
|
+
);
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log("Example:");
|
|
41
|
+
console.log(
|
|
42
|
+
` printf '%s' "$OWNER_PASSWORD" | jant setup --email owner@example.com --password-stdin`,
|
|
43
|
+
);
|
|
44
|
+
console.log("");
|
|
45
|
+
console.log(
|
|
46
|
+
"Node runtime and SITE_RESOLUTION_MODE=single-site only. `.env.node` next to",
|
|
47
|
+
);
|
|
48
|
+
console.log("your project (or in packages/core/) is auto-loaded.");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Read a secret piped to the command, the way `docker login --password-stdin`
|
|
53
|
+
* does: everything on the stream, minus the one line break `echo` adds.
|
|
54
|
+
*
|
|
55
|
+
* @param stream - Where the secret arrives
|
|
56
|
+
* @returns The secret
|
|
57
|
+
*/
|
|
58
|
+
async function readPasswordFromStdin(stream) {
|
|
59
|
+
if (stream.isTTY) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"--password-stdin reads the password from a pipe. Example: printf '%s' \"$OWNER_PASSWORD\" | jant setup --email <address> --password-stdin",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const chunks = [];
|
|
66
|
+
for await (const chunk of stream) {
|
|
67
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
68
|
+
}
|
|
69
|
+
return Buffer.concat(chunks)
|
|
70
|
+
.toString("utf8")
|
|
71
|
+
.replace(/\r?\n$/, "");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const OUTCOME_MESSAGES = {
|
|
75
|
+
created: (siteId) => `Set up site ${siteId}.`,
|
|
76
|
+
resumed: (siteId) =>
|
|
77
|
+
`Finished setting up site ${siteId}, which an earlier run had started.`,
|
|
78
|
+
"already-set-up": (siteId) =>
|
|
79
|
+
`Site ${siteId} was already set up. Nothing changed.`,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export async function run(argv, io = { stdin: process.stdin }) {
|
|
83
|
+
const { values } = parseArgs({
|
|
84
|
+
args: argv,
|
|
85
|
+
options: {
|
|
86
|
+
email: { type: "string" },
|
|
87
|
+
help: { type: "boolean", short: "h" },
|
|
88
|
+
language: { type: "string" },
|
|
89
|
+
node: { type: "boolean", default: false },
|
|
90
|
+
"password-stdin": { type: "boolean", default: false },
|
|
91
|
+
"site-id": { type: "string" },
|
|
92
|
+
"site-name": { type: "string" },
|
|
93
|
+
"time-zone": { type: "string" },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (values.help) {
|
|
98
|
+
printHelp();
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!values.email) {
|
|
103
|
+
throw new Error("Pass the owner's sign-in address with --email.");
|
|
104
|
+
}
|
|
105
|
+
if (!values["password-stdin"]) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
"Pass --password-stdin and pipe the owner's password in. The password is never read from an argument, where it would show up in the process list.",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const { runtime } = bootstrapCliRuntime(values);
|
|
112
|
+
if (runtime !== "node") {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"jant setup runs against a Node database. Set DATABASE_URL or DATA_DIR, or pass --node.",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const password = await readPasswordFromStdin(io.stdin);
|
|
119
|
+
const { setUpNodeInstance } = await loadNodeRuntime();
|
|
120
|
+
const nodeDatabase = await openNodeDatabase(process.env);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const result = await setUpNodeInstance(nodeDatabase.bindings, {
|
|
124
|
+
email: values.email,
|
|
125
|
+
password,
|
|
126
|
+
siteId: values["site-id"],
|
|
127
|
+
siteName: values["site-name"],
|
|
128
|
+
siteLanguage: values.language,
|
|
129
|
+
timeZone: values["time-zone"],
|
|
130
|
+
});
|
|
131
|
+
console.log(OUTCOME_MESSAGES[result.outcome](result.siteId));
|
|
132
|
+
return result;
|
|
133
|
+
} finally {
|
|
134
|
+
await nodeDatabase.close();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { mkdirSync, readdirSync
|
|
2
|
-
import {
|
|
1
|
+
import { createWriteStream, mkdirSync, readdirSync } from "node:fs";
|
|
2
|
+
import { mkdir, mkdtemp, rename, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
3
7
|
import { parseArgs } from "node:util";
|
|
4
|
-
import { unzipSync } from "fflate";
|
|
5
8
|
import {
|
|
6
9
|
CLI_API_TOKEN_ENV_VAR,
|
|
7
10
|
getCliApiToken,
|
|
8
11
|
} from "../../lib/cli-api-token.js";
|
|
9
|
-
import {
|
|
12
|
+
import { pullSiteExportDirectory } from "../../lib/site-pull-media.js";
|
|
13
|
+
import { extractZipFile, writeDirectoryToZip } from "../../lib/zip-archive.js";
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Stream the site's export archive to a file. The site streams it too, and a
|
|
17
|
+
* site that bundles its media answers with gigabytes.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} url - Site URL
|
|
20
|
+
* @param {string} token - API token
|
|
21
|
+
* @param {string} destination - File to write
|
|
22
|
+
*/
|
|
23
|
+
async function downloadSiteArchive(url, token, destination) {
|
|
12
24
|
const response = await fetch(`${url.replace(/\/$/, "")}/api/export/hugo`, {
|
|
13
25
|
method: "POST",
|
|
14
26
|
headers: {
|
|
@@ -16,12 +28,16 @@ async function exportRemoteSite(url, token) {
|
|
|
16
28
|
},
|
|
17
29
|
});
|
|
18
30
|
|
|
19
|
-
if (!response.ok) {
|
|
31
|
+
if (!response.ok || !response.body) {
|
|
20
32
|
const text = await response.text();
|
|
21
33
|
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
22
34
|
}
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
37
|
+
await pipeline(
|
|
38
|
+
Readable.fromWeb(response.body),
|
|
39
|
+
createWriteStream(destination),
|
|
40
|
+
);
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
function describeProgressUrl(value) {
|
|
@@ -161,22 +177,8 @@ export async function run(argv) {
|
|
|
161
177
|
process.exit(1);
|
|
162
178
|
}
|
|
163
179
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const zipBytes = await exportRemoteSite(url, token);
|
|
167
|
-
let zip = zipBytes;
|
|
168
|
-
let pullStats = null;
|
|
169
|
-
|
|
170
|
-
if (pullMedia) {
|
|
171
|
-
console.log("Preparing pull-media export ZIP...");
|
|
172
|
-
const pulled = await pullSiteExportZipBytes(zip, {
|
|
173
|
-
assetLoader: null,
|
|
174
|
-
logger: logPullProgress,
|
|
175
|
-
});
|
|
176
|
-
zip = pulled.zipBytes;
|
|
177
|
-
pullStats = pulled.stats;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
+
// Fail before the download, not after it: pulling a real site's media
|
|
181
|
+
// takes minutes.
|
|
180
182
|
if (outputDirectory) {
|
|
181
183
|
let existingEntries = [];
|
|
182
184
|
try {
|
|
@@ -194,20 +196,55 @@ export async function run(argv) {
|
|
|
194
196
|
);
|
|
195
197
|
process.exit(1);
|
|
196
198
|
}
|
|
199
|
+
}
|
|
197
200
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
mkdirSync(dirname(fullPath), { recursive: true });
|
|
203
|
-
writeFileSync(fullPath, Buffer.from(bytes));
|
|
204
|
-
}
|
|
205
|
-
console.log(`Exported site from ${url} to ${values.directory}`);
|
|
206
|
-
console.log(`Preview with: cd ${values.directory} && hugo serve`);
|
|
207
|
-
} else {
|
|
201
|
+
console.log(`Exporting site from ${url}...`);
|
|
202
|
+
|
|
203
|
+
// Nothing to add: the site's archive is the export.
|
|
204
|
+
if (!pullMedia && !outputDirectory) {
|
|
208
205
|
console.log(`Writing ${values.output}...`);
|
|
209
|
-
|
|
206
|
+
const partial = `${output}.partial`;
|
|
207
|
+
try {
|
|
208
|
+
await downloadSiteArchive(url, token, partial);
|
|
209
|
+
await rename(partial, output);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
await rm(partial, { force: true });
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
210
214
|
console.log(`Exported site from ${url} to ${values.output}`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Unpacked to disk and media downloaded next to it, so the export never
|
|
219
|
+
// holds more than one media file in memory.
|
|
220
|
+
const tempDir = await mkdtemp(join(tmpdir(), "jant-site-export-"));
|
|
221
|
+
const workDir = outputDirectory ?? join(tempDir, "site");
|
|
222
|
+
let pullStats = null;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const archivePath = join(tempDir, "site.zip");
|
|
226
|
+
await downloadSiteArchive(url, token, archivePath);
|
|
227
|
+
await extractZipFile(archivePath, workDir);
|
|
228
|
+
await rm(archivePath, { force: true });
|
|
229
|
+
|
|
230
|
+
if (pullMedia) {
|
|
231
|
+
console.log("Pulling media...");
|
|
232
|
+
pullStats = await pullSiteExportDirectory(workDir, {
|
|
233
|
+
assetLoader: null,
|
|
234
|
+
logger: logPullProgress,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (outputDirectory) {
|
|
239
|
+
console.log(`Exported site from ${url} to ${values.directory}`);
|
|
240
|
+
console.log(`Preview with: cd ${values.directory} && hugo serve`);
|
|
241
|
+
} else {
|
|
242
|
+
console.log(`Writing ${values.output}...`);
|
|
243
|
+
await writeDirectoryToZip(workDir, output);
|
|
244
|
+
console.log(`Exported site from ${url} to ${values.output}`);
|
|
245
|
+
}
|
|
246
|
+
} finally {
|
|
247
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
211
248
|
}
|
|
212
249
|
|
|
213
250
|
if (pullStats) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
1
|
import { stat } from "node:fs/promises";
|
|
3
2
|
import { resolve } from "node:path";
|
|
4
3
|
import { parseArgs } from "node:util";
|
|
5
4
|
import {
|
|
6
5
|
pullSiteExportDirectory,
|
|
7
|
-
|
|
6
|
+
pullSiteExportZipFile,
|
|
8
7
|
} from "../../lib/site-pull-media.js";
|
|
9
8
|
|
|
10
9
|
function describeProgressUrl(value) {
|
|
@@ -102,13 +101,10 @@ export async function run(argv) {
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
const outputPath = resolve(process.cwd(), values.output || values.path);
|
|
105
|
-
const inputBytes = new Uint8Array(readFileSync(inputPath));
|
|
106
104
|
console.log(`Pulling media in ${values.path}...`);
|
|
107
|
-
const
|
|
105
|
+
const stats = await pullSiteExportZipFile(inputPath, outputPath, {
|
|
108
106
|
logger: logPullProgress,
|
|
109
107
|
});
|
|
110
|
-
console.log(`Writing ${values.output || values.path}...`);
|
|
111
|
-
writeFileSync(outputPath, Buffer.from(zipBytes));
|
|
112
108
|
console.log(
|
|
113
109
|
`Pulled media in ${values.path} -> ${values.output || values.path}`,
|
|
114
110
|
);
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
readFile,
|
|
6
|
-
readdir,
|
|
7
|
-
rm,
|
|
8
|
-
writeFile,
|
|
9
|
-
} from "node:fs/promises";
|
|
1
|
+
import { createWriteStream, existsSync } from "node:fs";
|
|
2
|
+
import { Readable } from "node:stream";
|
|
3
|
+
import { pipeline } from "node:stream/promises";
|
|
4
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
10
5
|
import { tmpdir } from "node:os";
|
|
11
|
-
import { dirname, join,
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
12
7
|
import { parseArgs } from "node:util";
|
|
13
|
-
import {
|
|
14
|
-
import { queryD1 } from "../../../lib/d1-query.js";
|
|
8
|
+
import { writeDirectoryToZip } from "../../../lib/zip-archive.js";
|
|
9
|
+
import { D1_DUMP_PAGE_SIZE, queryD1 } from "../../../lib/d1-query.js";
|
|
15
10
|
import { loadNodeRuntime } from "../../../lib/load-node-runtime.js";
|
|
16
11
|
import { openNodeDatabase } from "../../../lib/node-database.js";
|
|
17
12
|
import {
|
|
@@ -23,6 +18,7 @@ import {
|
|
|
23
18
|
buildSnapshotStorageQuery,
|
|
24
19
|
collectSnapshotObjects,
|
|
25
20
|
getSnapshotSelectSql,
|
|
21
|
+
orderSnapshotPostRows,
|
|
26
22
|
SNAPSHOT_TABLES,
|
|
27
23
|
snapshotObjectPath,
|
|
28
24
|
} from "../../../lib/site-snapshot.js";
|
|
@@ -38,49 +34,6 @@ function isZipPath(filePath) {
|
|
|
38
34
|
return filePath.toLowerCase().endsWith(".zip");
|
|
39
35
|
}
|
|
40
36
|
|
|
41
|
-
async function readStorageBody(body) {
|
|
42
|
-
const reader = body.getReader();
|
|
43
|
-
const chunks = [];
|
|
44
|
-
let totalLength = 0;
|
|
45
|
-
|
|
46
|
-
for (;;) {
|
|
47
|
-
const { done, value } = await reader.read();
|
|
48
|
-
if (done) break;
|
|
49
|
-
chunks.push(value);
|
|
50
|
-
totalLength += value.length;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const bytes = new Uint8Array(totalLength);
|
|
54
|
-
let offset = 0;
|
|
55
|
-
for (const chunk of chunks) {
|
|
56
|
-
bytes.set(chunk, offset);
|
|
57
|
-
offset += chunk.length;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return bytes;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function readDirectoryEntries(rootDir) {
|
|
64
|
-
const entries = {};
|
|
65
|
-
|
|
66
|
-
async function walk(dir) {
|
|
67
|
-
const items = await readdir(dir, { withFileTypes: true });
|
|
68
|
-
for (const item of items) {
|
|
69
|
-
const fullPath = join(dir, item.name);
|
|
70
|
-
if (item.isDirectory()) {
|
|
71
|
-
await walk(fullPath);
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const relativePath = relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
76
|
-
entries[relativePath] = new Uint8Array(await readFile(fullPath));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
await walk(rootDir);
|
|
81
|
-
return entries;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
37
|
async function assertWritableOutput(outputPath, force) {
|
|
85
38
|
if (!existsSync(outputPath)) {
|
|
86
39
|
return;
|
|
@@ -123,7 +76,12 @@ async function createNodeExportContext() {
|
|
|
123
76
|
}
|
|
124
77
|
|
|
125
78
|
await mkdir(dirname(filePath), { recursive: true });
|
|
126
|
-
|
|
79
|
+
// Straight to disk: a snapshot holds every media file, and collecting
|
|
80
|
+
// each one in memory first ran a 3 GB export to a 2 GB peak.
|
|
81
|
+
await pipeline(
|
|
82
|
+
Readable.fromWeb(object.body),
|
|
83
|
+
createWriteStream(filePath),
|
|
84
|
+
);
|
|
127
85
|
},
|
|
128
86
|
};
|
|
129
87
|
}
|
|
@@ -215,7 +173,7 @@ export async function run(argv) {
|
|
|
215
173
|
console.log(
|
|
216
174
|
" --output, -o Output directory or .zip file (default: jant-site-snapshot)",
|
|
217
175
|
);
|
|
218
|
-
console.log(" --site Target site id");
|
|
176
|
+
console.log(" --site Target site key or id");
|
|
219
177
|
console.log(" --host Target site host");
|
|
220
178
|
console.log(" --url Target site URL");
|
|
221
179
|
console.log(" --path-prefix Path prefix used with --host");
|
|
@@ -295,6 +253,9 @@ export async function run(argv) {
|
|
|
295
253
|
{
|
|
296
254
|
dialect: context.dialect,
|
|
297
255
|
source: runtime,
|
|
256
|
+
// D1 answers through Wrangler's buffered output; read it in pages.
|
|
257
|
+
pageSize: runtime === "node" ? undefined : D1_DUMP_PAGE_SIZE,
|
|
258
|
+
orderRowsByTable: { post: orderSnapshotPostRows },
|
|
298
259
|
tables: SNAPSHOT_TABLES,
|
|
299
260
|
selectSqlByTable: Object.fromEntries(
|
|
300
261
|
SNAPSHOT_TABLES.map((tableName) => [
|
|
@@ -344,10 +305,7 @@ export async function run(argv) {
|
|
|
344
305
|
|
|
345
306
|
if (shouldZip) {
|
|
346
307
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
347
|
-
|
|
348
|
-
level: 6,
|
|
349
|
-
});
|
|
350
|
-
await writeFile(outputPath, zipped);
|
|
308
|
+
await writeDirectoryToZip(scratchDir, outputPath);
|
|
351
309
|
if (process.env.SNAPSHOT_SUPPRESS_SUCCESS_LOG !== "true") {
|
|
352
310
|
console.log(
|
|
353
311
|
`Exported ${getCliRuntimeLabel(runtime)} snapshot to ${values.output}`,
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
mkdtemp,
|
|
5
|
-
readFile,
|
|
6
|
-
rm,
|
|
7
|
-
stat,
|
|
8
|
-
writeFile,
|
|
9
|
-
} from "node:fs/promises";
|
|
1
|
+
import { createReadStream, existsSync } from "node:fs";
|
|
2
|
+
import { Readable } from "node:stream";
|
|
3
|
+
import { mkdtemp, readFile, rm, stat } from "node:fs/promises";
|
|
10
4
|
import { tmpdir } from "node:os";
|
|
11
|
-
import {
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
12
6
|
import { parseArgs } from "node:util";
|
|
13
|
-
import {
|
|
7
|
+
import { extractZipFile } from "../../../lib/zip-archive.js";
|
|
14
8
|
import { executeD1, queryD1 } from "../../../lib/d1-query.js";
|
|
15
9
|
import { loadNodeRuntime } from "../../../lib/load-node-runtime.js";
|
|
16
10
|
import { openNodeDatabase } from "../../../lib/node-database.js";
|
|
@@ -18,6 +12,8 @@ import { deleteR2Object, uploadR2Object } from "../../../lib/r2-query.js";
|
|
|
18
12
|
import {
|
|
19
13
|
assertSnapshotDialectMatches,
|
|
20
14
|
assertSnapshotMeta,
|
|
15
|
+
buildMediaProviderSql,
|
|
16
|
+
DEFER_FOREIGN_KEYS_SQL,
|
|
21
17
|
buildReplaceSql,
|
|
22
18
|
buildSnapshotStorageQuery,
|
|
23
19
|
collectSnapshotObjects,
|
|
@@ -58,11 +54,13 @@ async function createNodeImportContext() {
|
|
|
58
54
|
// the current site, which (a) is redundant with the bin-level resolveCliSite
|
|
59
55
|
// call below and (b) prints a generic "/setup first" error when the
|
|
60
56
|
// snapshot's own error path is more informative.
|
|
61
|
-
const { createStorageDriver } =
|
|
57
|
+
const { createStorageDriver, getConfiguredStorageDriver } =
|
|
58
|
+
await loadNodeRuntime();
|
|
62
59
|
const storage = createStorageDriver(nodeDatabase.bindings);
|
|
63
60
|
|
|
64
61
|
return {
|
|
65
62
|
dialect: nodeDatabase.database.dialect,
|
|
63
|
+
storageProvider: getConfiguredStorageDriver(nodeDatabase.bindings),
|
|
66
64
|
async close() {
|
|
67
65
|
await nodeDatabase.close();
|
|
68
66
|
},
|
|
@@ -77,8 +75,7 @@ async function createNodeImportContext() {
|
|
|
77
75
|
throw new Error("Snapshot import requires configured storage.");
|
|
78
76
|
}
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
await storage.put(key, bytes, {
|
|
78
|
+
await storage.put(key, Readable.toWeb(createReadStream(filePath)), {
|
|
82
79
|
contentType: contentType || undefined,
|
|
83
80
|
});
|
|
84
81
|
},
|
|
@@ -96,6 +93,8 @@ function createD1ImportContext(runtime, values) {
|
|
|
96
93
|
|
|
97
94
|
return {
|
|
98
95
|
dialect: "sqlite",
|
|
96
|
+
// Objects go up through the wrangler R2 binding below.
|
|
97
|
+
storageProvider: "r2",
|
|
99
98
|
async close() {},
|
|
100
99
|
async query(sql) {
|
|
101
100
|
return queryD1(sql, runtime, wranglerOptions);
|
|
@@ -141,14 +140,7 @@ async function materializeSnapshotInput(inputPath) {
|
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
const outputDir = await mkdtemp(join(tmpdir(), "jant-site-snapshot-import-"));
|
|
144
|
-
|
|
145
|
-
const files = unzipSync(bytes);
|
|
146
|
-
|
|
147
|
-
for (const [relativePath, data] of Object.entries(files)) {
|
|
148
|
-
const absolutePath = join(outputDir, relativePath);
|
|
149
|
-
await mkdir(dirname(absolutePath), { recursive: true });
|
|
150
|
-
await writeFile(absolutePath, data);
|
|
151
|
-
}
|
|
143
|
+
await extractZipFile(inputPath, outputDir);
|
|
152
144
|
|
|
153
145
|
return {
|
|
154
146
|
cleanup: async () => {
|
|
@@ -204,7 +196,7 @@ export async function run(argv) {
|
|
|
204
196
|
console.log(
|
|
205
197
|
" --replace Replace the current content scope before importing",
|
|
206
198
|
);
|
|
207
|
-
console.log(" --site Target site id");
|
|
199
|
+
console.log(" --site Target site key or id");
|
|
208
200
|
console.log(" --host Target site host");
|
|
209
201
|
console.log(" --url Target site URL");
|
|
210
202
|
console.log(" --path-prefix Path prefix used with --host");
|
|
@@ -402,7 +394,14 @@ export async function run(argv) {
|
|
|
402
394
|
);
|
|
403
395
|
}
|
|
404
396
|
|
|
405
|
-
await context.execute(
|
|
397
|
+
await context.execute(
|
|
398
|
+
[
|
|
399
|
+
...(context.dialect === "postgres" ? [] : [DEFER_FOREIGN_KEYS_SQL]),
|
|
400
|
+
buildReplaceSql(targetSite.id),
|
|
401
|
+
dbSql,
|
|
402
|
+
buildMediaProviderSql(targetSite.id, context.storageProvider),
|
|
403
|
+
].join("\n"),
|
|
404
|
+
);
|
|
406
405
|
|
|
407
406
|
const keysToDelete = [...currentKeys].filter(
|
|
408
407
|
(key) => !snapshotKeys.has(key),
|