@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
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ npm run dev
|
|
|
21
21
|
For a traditional server deployment, use the official Docker image:
|
|
22
22
|
|
|
23
23
|
- [`owenyoung/jant`](https://hub.docker.com/r/owenyoung/jant)
|
|
24
|
-
- [Docker deployment guide](https://
|
|
24
|
+
- [Docker deployment guide](https://jant.me/docs/deployment-docker)
|
|
25
25
|
|
|
26
26
|
## Tech Stack
|
|
27
27
|
|
|
@@ -35,16 +35,16 @@ For a traditional server deployment, use the official Docker image:
|
|
|
35
35
|
|
|
36
36
|
## Documentation
|
|
37
37
|
|
|
38
|
-
- [Overview](https://
|
|
39
|
-
- [Getting Started](https://
|
|
40
|
-
- [Writing and Organizing Posts](https://
|
|
41
|
-
- [Deploy on Cloudflare](https://
|
|
42
|
-
- [Deploy with Docker](https://
|
|
43
|
-
- [Configuration](https://
|
|
44
|
-
- [Export and Import](https://
|
|
45
|
-
- [Backups and Recovery](https://
|
|
46
|
-
- [Theming](https://
|
|
47
|
-
- [API Reference](https://
|
|
38
|
+
- [Overview](https://jant.me/docs)
|
|
39
|
+
- [Getting Started](https://jant.me/docs/getting-started)
|
|
40
|
+
- [Writing and Organizing Posts](https://jant.me/docs/writing-and-organizing)
|
|
41
|
+
- [Deploy on Cloudflare](https://jant.me/docs/deployment)
|
|
42
|
+
- [Deploy with Docker](https://jant.me/docs/deployment-docker)
|
|
43
|
+
- [Configuration](https://jant.me/docs/configuration)
|
|
44
|
+
- [Export and Import](https://jant.me/docs/export-and-import)
|
|
45
|
+
- [Backups and Recovery](https://jant.me/docs/backups)
|
|
46
|
+
- [Theming](https://jant.me/docs/theming)
|
|
47
|
+
- [API Reference](https://jant.me/docs/api)
|
|
48
48
|
|
|
49
49
|
## Contributing
|
|
50
50
|
|
|
@@ -6,14 +6,25 @@
|
|
|
6
6
|
* Uses the same S3_* environment variables as media storage. Files with
|
|
7
7
|
* content-hashed names (JS, fonts) are skipped if already present in the
|
|
8
8
|
* bucket — they are immutable. Non-hashed files (e.g. CSS in older builds)
|
|
9
|
-
* are always uploaded.
|
|
9
|
+
* are always uploaded. An object stored without the compression it should
|
|
10
|
+
* have is re-uploaded even though its key exists, so a change to the
|
|
11
|
+
* compression policy reaches assets whose content never changed.
|
|
12
|
+
*
|
|
13
|
+
* Text assets are uploaded brotli-compressed at maximum quality, with
|
|
14
|
+
* `Content-Encoding: br`. The CDN in front of the bucket compresses at a fixed
|
|
15
|
+
* low quality — measured as brotli q4, which is worse than its own gzip — and
|
|
16
|
+
* offers no way to raise it, so the only route to q11 is to compress here and
|
|
17
|
+
* store the result. A client that cannot take brotli is unaffected: the edge
|
|
18
|
+
* decompresses and re-encodes for it (verified against R2 behind a Cloudflare
|
|
19
|
+
* custom domain: `br` is passed through, `gzip` is re-encoded, `identity` is
|
|
20
|
+
* decompressed, all three byte-identical after decoding).
|
|
10
21
|
*
|
|
11
22
|
* Intended CI order: build → upload-assets → deploy container
|
|
12
23
|
*/
|
|
13
24
|
|
|
14
25
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
15
26
|
import { join, relative, extname } from "node:path";
|
|
16
|
-
import {
|
|
27
|
+
import { brotliCompressSync, constants } from "node:zlib";
|
|
17
28
|
import { parseArgs } from "node:util";
|
|
18
29
|
import { fileURLToPath } from "node:url";
|
|
19
30
|
import { dirname, resolve } from "node:path";
|
|
@@ -41,6 +52,59 @@ function getContentType(filePath) {
|
|
|
41
52
|
);
|
|
42
53
|
}
|
|
43
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Extensions worth pre-compressing. Fonts (woff2), images and icons already
|
|
57
|
+
* carry their own compression, and running brotli over them costs build time
|
|
58
|
+
* for nothing — usually for a slightly larger file.
|
|
59
|
+
*/
|
|
60
|
+
const COMPRESSIBLE_EXTENSIONS = new Set([
|
|
61
|
+
".js",
|
|
62
|
+
".css",
|
|
63
|
+
".json",
|
|
64
|
+
".map",
|
|
65
|
+
".svg",
|
|
66
|
+
".txt",
|
|
67
|
+
".html",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Brotli-compress an asset for storage, at a quality only a build can afford.
|
|
72
|
+
*
|
|
73
|
+
* Quality 11 is far too slow to run per request, which is why CDNs compress
|
|
74
|
+
* lower. Here it runs once per file per release, and every visitor gets the
|
|
75
|
+
* result.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} filePath - Source path, used only for its extension.
|
|
78
|
+
* @param {Buffer} body - The file's bytes.
|
|
79
|
+
* @returns {Buffer | null} The compressed body, or `null` when the type is not
|
|
80
|
+
* worth compressing or compression failed to shrink it — upload `body` as-is
|
|
81
|
+
* and omit `Content-Encoding` in that case.
|
|
82
|
+
* @example
|
|
83
|
+
* const packed = compressForUpload("client.css", body);
|
|
84
|
+
* // packed.length < body.length, or null for a .woff2
|
|
85
|
+
*/
|
|
86
|
+
export function compressForUpload(filePath, body) {
|
|
87
|
+
if (!COMPRESSIBLE_EXTENSIONS.has(extname(filePath).toLowerCase())) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const compressed = brotliCompressSync(body, {
|
|
91
|
+
params: {
|
|
92
|
+
[constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY,
|
|
93
|
+
[constants.BROTLI_PARAM_SIZE_HINT]: body.length,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
// Tiny or already-dense files can come back bigger. Storing that would make
|
|
97
|
+
// every request pay for the encoding header and gain nothing.
|
|
98
|
+
return compressed.length < body.length ? compressed : null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Human-readable byte count for the upload log. */
|
|
102
|
+
function formatBytes(bytes) {
|
|
103
|
+
return bytes >= 1024 * 1024
|
|
104
|
+
? `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
|
105
|
+
: `${(bytes / 1024).toFixed(1)} KB`;
|
|
106
|
+
}
|
|
107
|
+
|
|
44
108
|
/**
|
|
45
109
|
* Resolve the default source directory: packages/core/dist/client/_assets
|
|
46
110
|
* relative to this file's location in packages/core/bin/commands/assets/.
|
|
@@ -64,8 +128,12 @@ async function walkDir(dir) {
|
|
|
64
128
|
}
|
|
65
129
|
|
|
66
130
|
async function loadS3(config) {
|
|
67
|
-
const {
|
|
68
|
-
|
|
131
|
+
const {
|
|
132
|
+
S3Client,
|
|
133
|
+
PutObjectCommand,
|
|
134
|
+
ListObjectsV2Command,
|
|
135
|
+
HeadObjectCommand,
|
|
136
|
+
} = await import("@aws-sdk/client-s3");
|
|
69
137
|
const forcePathStyle = !config.endpoint.includes("amazonaws.com");
|
|
70
138
|
const client = new S3Client({
|
|
71
139
|
endpoint: config.endpoint,
|
|
@@ -80,6 +148,7 @@ async function loadS3(config) {
|
|
|
80
148
|
client,
|
|
81
149
|
PutObjectCommand,
|
|
82
150
|
ListObjectsV2Command,
|
|
151
|
+
HeadObjectCommand,
|
|
83
152
|
bucket: config.bucket,
|
|
84
153
|
};
|
|
85
154
|
}
|
|
@@ -107,6 +176,53 @@ async function listExistingKeys(s3, prefix) {
|
|
|
107
176
|
return keys;
|
|
108
177
|
}
|
|
109
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Of the keys that already exist, the ones stored without the compression they
|
|
181
|
+
* should have.
|
|
182
|
+
*
|
|
183
|
+
* Objects are content-addressed, so a key that exists is assumed to hold the
|
|
184
|
+
* right bytes and is skipped. That assumption breaks when the *encoding*
|
|
185
|
+
* changes rather than the content: every asset uploaded before compression was
|
|
186
|
+
* introduced keeps its key, keeps being skipped, and never picks it up. The
|
|
187
|
+
* CDN then compresses those at its own low quality on every request, which is
|
|
188
|
+
* the whole thing compression was meant to avoid.
|
|
189
|
+
*
|
|
190
|
+
* Only worth asking about types we would compress, and only when compression
|
|
191
|
+
* is on. A file that brotli cannot shrink is stored raw on purpose, so its
|
|
192
|
+
* missing `Content-Encoding` is correct and must not read as stale — otherwise
|
|
193
|
+
* it would be re-uploaded on every run, forever.
|
|
194
|
+
*
|
|
195
|
+
* @param {object} s3 - The client bundle from `loadS3`.
|
|
196
|
+
* @param {Array<{filePath: string, key: string}>} candidates - Local files
|
|
197
|
+
* whose key already exists in the bucket.
|
|
198
|
+
* @returns {Promise<Set<string>>} Keys to upload again.
|
|
199
|
+
* @example
|
|
200
|
+
* const stale = await findStaleEncodingKeys(s3, existing);
|
|
201
|
+
* // Set { "_assets/client-cjk-B7Z0snDu.css" }
|
|
202
|
+
*/
|
|
203
|
+
async function findStaleEncodingKeys(s3, candidates) {
|
|
204
|
+
const { client, HeadObjectCommand, bucket } = s3;
|
|
205
|
+
const stale = new Set();
|
|
206
|
+
const CONCURRENCY = 25;
|
|
207
|
+
|
|
208
|
+
for (let i = 0; i < candidates.length; i += CONCURRENCY) {
|
|
209
|
+
await Promise.all(
|
|
210
|
+
candidates.slice(i, i + CONCURRENCY).map(async ({ filePath, key }) => {
|
|
211
|
+
const head = await client.send(
|
|
212
|
+
new HeadObjectCommand({ Bucket: bucket, Key: key }),
|
|
213
|
+
);
|
|
214
|
+
if (head.ContentEncoding === "br") return;
|
|
215
|
+
// Read it only now: the answer is only interesting for the few objects
|
|
216
|
+
// that are missing the encoding.
|
|
217
|
+
const body = await readFile(filePath);
|
|
218
|
+
if (compressForUpload(filePath, body) !== null) stale.add(key);
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return stale;
|
|
224
|
+
}
|
|
225
|
+
|
|
110
226
|
export async function run(argv) {
|
|
111
227
|
const { values } = parseArgs({
|
|
112
228
|
args: argv,
|
|
@@ -115,6 +231,8 @@ export async function run(argv) {
|
|
|
115
231
|
prefix: { type: "string", default: "_assets" },
|
|
116
232
|
"source-dir": { type: "string" },
|
|
117
233
|
"dry-run": { type: "boolean", default: false },
|
|
234
|
+
force: { type: "boolean", default: false },
|
|
235
|
+
"no-compress": { type: "boolean", default: false },
|
|
118
236
|
},
|
|
119
237
|
});
|
|
120
238
|
|
|
@@ -135,6 +253,12 @@ export async function run(argv) {
|
|
|
135
253
|
console.log(
|
|
136
254
|
" --dry-run Print what would be uploaded without uploading",
|
|
137
255
|
);
|
|
256
|
+
console.log(
|
|
257
|
+
" --force Re-upload every key, including ones already compressed",
|
|
258
|
+
);
|
|
259
|
+
console.log(
|
|
260
|
+
" --no-compress Upload raw bytes instead of brotli-compressed",
|
|
261
|
+
);
|
|
138
262
|
process.exit(0);
|
|
139
263
|
}
|
|
140
264
|
|
|
@@ -171,6 +295,8 @@ export async function run(argv) {
|
|
|
171
295
|
const sourceDir = values["source-dir"] ?? resolveDefaultSourceDir();
|
|
172
296
|
const prefix = values.prefix.replace(/^\/+|\/+$/g, "");
|
|
173
297
|
const dryRun = values["dry-run"];
|
|
298
|
+
const force = values.force;
|
|
299
|
+
const compress = !values["no-compress"];
|
|
174
300
|
|
|
175
301
|
// Verify source directory exists
|
|
176
302
|
try {
|
|
@@ -181,10 +307,12 @@ export async function run(argv) {
|
|
|
181
307
|
process.exit(1);
|
|
182
308
|
}
|
|
183
309
|
|
|
184
|
-
console.log(`Source:
|
|
185
|
-
console.log(`Bucket:
|
|
186
|
-
console.log(`Prefix:
|
|
187
|
-
|
|
310
|
+
console.log(`Source: ${sourceDir}`);
|
|
311
|
+
console.log(`Bucket: ${bucket}`);
|
|
312
|
+
console.log(`Prefix: ${prefix}`);
|
|
313
|
+
console.log(`Encoding: ${compress ? "brotli (quality 11)" : "raw"}`);
|
|
314
|
+
if (force) console.log("Force: re-uploading keys that already exist");
|
|
315
|
+
if (dryRun) console.log("Dry run: no files will be uploaded");
|
|
188
316
|
console.log("");
|
|
189
317
|
|
|
190
318
|
const s3 = await loadS3({
|
|
@@ -205,19 +333,57 @@ export async function run(argv) {
|
|
|
205
333
|
let skipped = 0;
|
|
206
334
|
|
|
207
335
|
const toUpload = [];
|
|
336
|
+
const alreadyThere = [];
|
|
208
337
|
for (const filePath of files) {
|
|
209
338
|
const relPath = relative(sourceDir, filePath).replace(/\\/g, "/");
|
|
210
339
|
const key = prefix ? `${prefix}/${relPath}` : relPath;
|
|
211
|
-
if (existingKeys.has(key)) {
|
|
212
|
-
|
|
340
|
+
if (existingKeys.has(key) && !force) {
|
|
341
|
+
alreadyThere.push({ filePath, key });
|
|
213
342
|
} else {
|
|
214
343
|
toUpload.push({ filePath, key });
|
|
215
344
|
}
|
|
216
345
|
}
|
|
217
346
|
|
|
347
|
+
// A key that exists is normally left alone, but that skips objects stored
|
|
348
|
+
// before compression was introduced — they would keep their old encoding
|
|
349
|
+
// forever. Re-upload those, and only those.
|
|
350
|
+
let restored = 0;
|
|
351
|
+
if (compress && alreadyThere.length > 0) {
|
|
352
|
+
process.stdout.write("Checking stored encodings... ");
|
|
353
|
+
const stale = await findStaleEncodingKeys(s3, alreadyThere);
|
|
354
|
+
for (const entry of alreadyThere) {
|
|
355
|
+
if (stale.has(entry.key)) toUpload.push(entry);
|
|
356
|
+
else skipped++;
|
|
357
|
+
}
|
|
358
|
+
restored = stale.size;
|
|
359
|
+
console.log(
|
|
360
|
+
stale.size === 0
|
|
361
|
+
? "all compressed"
|
|
362
|
+
: `${stale.size} stored uncompressed, re-uploading`,
|
|
363
|
+
);
|
|
364
|
+
} else {
|
|
365
|
+
skipped += alreadyThere.length;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Bytes as stored, against bytes as built — what the compression bought.
|
|
369
|
+
let rawTotal = 0;
|
|
370
|
+
let storedTotal = 0;
|
|
371
|
+
|
|
372
|
+
/** Read a file and decide how it should be stored. */
|
|
373
|
+
async function prepare(filePath) {
|
|
374
|
+
const body = await readFile(filePath);
|
|
375
|
+
const packed = compress ? compressForUpload(filePath, body) : null;
|
|
376
|
+
rawTotal += body.length;
|
|
377
|
+
storedTotal += (packed ?? body).length;
|
|
378
|
+
return { body: packed ?? body, encoded: packed !== null };
|
|
379
|
+
}
|
|
380
|
+
|
|
218
381
|
if (dryRun) {
|
|
219
|
-
for (const { key } of toUpload) {
|
|
220
|
-
|
|
382
|
+
for (const { filePath, key } of toUpload) {
|
|
383
|
+
const { body, encoded } = await prepare(filePath);
|
|
384
|
+
console.log(
|
|
385
|
+
` [dry-run] upload ${key} (${formatBytes(body.length)}${encoded ? ", br" : ""})`,
|
|
386
|
+
);
|
|
221
387
|
}
|
|
222
388
|
uploaded = toUpload.length;
|
|
223
389
|
} else {
|
|
@@ -226,16 +392,17 @@ export async function run(argv) {
|
|
|
226
392
|
const batch = toUpload.slice(i, i + CONCURRENCY);
|
|
227
393
|
await Promise.all(
|
|
228
394
|
batch.map(async ({ filePath, key }) => {
|
|
229
|
-
const body = await
|
|
395
|
+
const { body, encoded } = await prepare(filePath);
|
|
230
396
|
const command = new s3.PutObjectCommand({
|
|
231
397
|
Bucket: s3.bucket,
|
|
232
398
|
Key: key,
|
|
233
399
|
Body: body,
|
|
234
400
|
ContentType: getContentType(filePath),
|
|
235
401
|
CacheControl: "public, max-age=31536000, immutable",
|
|
402
|
+
...(encoded ? { ContentEncoding: "br" } : {}),
|
|
236
403
|
});
|
|
237
404
|
await s3.client.send(command);
|
|
238
|
-
process.stdout.write(` uploaded ${key}\n`);
|
|
405
|
+
process.stdout.write(` uploaded ${key}${encoded ? " (br)" : ""}\n`);
|
|
239
406
|
uploaded++;
|
|
240
407
|
}),
|
|
241
408
|
);
|
|
@@ -243,5 +410,15 @@ export async function run(argv) {
|
|
|
243
410
|
}
|
|
244
411
|
|
|
245
412
|
console.log("");
|
|
246
|
-
console.log(
|
|
413
|
+
console.log(
|
|
414
|
+
`Done. ${uploaded} uploaded, ${skipped} skipped` +
|
|
415
|
+
`${restored > 0 ? `, ${restored} re-uploaded for compression` : ""}.`,
|
|
416
|
+
);
|
|
417
|
+
if (compress && rawTotal > 0) {
|
|
418
|
+
const saved = rawTotal - storedTotal;
|
|
419
|
+
console.log(
|
|
420
|
+
`Stored ${formatBytes(storedTotal)} of ${formatBytes(rawTotal)} built ` +
|
|
421
|
+
`(${formatBytes(saved)} saved, ${((saved / rawTotal) * 100).toFixed(1)}%).`,
|
|
422
|
+
);
|
|
423
|
+
}
|
|
247
424
|
}
|
package/bin/commands/export.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
-
import { queryD1 } from "../lib/d1-query.js";
|
|
4
|
+
import { D1_DUMP_PAGE_SIZE, queryD1 } from "../lib/d1-query.js";
|
|
5
5
|
import { openNodeDatabase } from "../lib/node-database.js";
|
|
6
6
|
import { dumpDatabaseToSql } from "../lib/sql-export.js";
|
|
7
7
|
import {
|
|
@@ -108,6 +108,8 @@ export async function run(argv) {
|
|
|
108
108
|
{
|
|
109
109
|
source: runtime === "d1-remote" ? "remote" : "local",
|
|
110
110
|
dialect: "sqlite",
|
|
111
|
+
// D1 answers through Wrangler's buffered output; read it in pages.
|
|
112
|
+
pageSize: D1_DUMP_PAGE_SIZE,
|
|
111
113
|
},
|
|
112
114
|
);
|
|
113
115
|
}
|