@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/bin/lib/d1-query.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { WRANGLER_MAX_BUFFER, runLocalWrangler } from "./wrangler-cli.js";
|
|
5
|
+
import { extractWranglerJson } from "./wrangler-json.js";
|
|
2
6
|
|
|
3
7
|
const DEFAULT_RETRY_ATTEMPTS = 4;
|
|
4
8
|
const DEFAULT_RETRY_DELAY_MS = 500;
|
|
5
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Byte ceiling for a statement batch sent as one `--command=` argument.
|
|
12
|
+
*
|
|
13
|
+
* Linux caps a single argv entry at 128 KiB (`MAX_ARG_STRLEN`) and fails the
|
|
14
|
+
* spawn with `E2BIG`; macOS has no per-argument cap, so an oversized batch
|
|
15
|
+
* works on a developer's Mac and dies in CI and in the demo rebuild, which is
|
|
16
|
+
* exactly how it first surfaced — a snapshot import whose `db.sql` had grown
|
|
17
|
+
* past the limit. Anything at or above this goes through `--file` instead.
|
|
18
|
+
*/
|
|
19
|
+
const MAX_INLINE_COMMAND_BYTES = 96 * 1024;
|
|
20
|
+
|
|
6
21
|
function getD1Flag(runtime) {
|
|
7
22
|
return runtime === "d1-remote" ? "--remote" : "--local";
|
|
8
23
|
}
|
|
@@ -35,7 +50,7 @@ export function parseWranglerError(output) {
|
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
try {
|
|
38
|
-
const parsed = JSON.parse(output.trim());
|
|
53
|
+
const parsed = JSON.parse(extractWranglerJson(output).trim());
|
|
39
54
|
const error = Array.isArray(parsed) ? parsed[0]?.error : parsed?.error;
|
|
40
55
|
if (!error?.text) {
|
|
41
56
|
return undefined;
|
|
@@ -61,6 +76,46 @@ function sleepSync(ms) {
|
|
|
61
76
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
62
77
|
}
|
|
63
78
|
|
|
79
|
+
const WRANGLER_OUTPUT_EXCERPT_LENGTH = 2000;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Rows per read when dumping a D1 table: small enough that a page of long
|
|
83
|
+
* posts stays well inside {@link WRANGLER_MAX_BUFFER}.
|
|
84
|
+
*/
|
|
85
|
+
export const D1_DUMP_PAGE_SIZE = 200;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Say why a Wrangler call failed without echoing its whole output: on a
|
|
89
|
+
* failed `d1 execute` that output can be a site's rows, hundreds of KB of it.
|
|
90
|
+
*
|
|
91
|
+
* @param {string[]} args - The Wrangler arguments
|
|
92
|
+
* @param {Error & { code?: string, stdout?: string, stderr?: string }} error
|
|
93
|
+
* @returns {string} A message naming the command and the cause
|
|
94
|
+
* @example
|
|
95
|
+
* describeWranglerFailure(["d1", "execute", "DB"], enobufsError);
|
|
96
|
+
* // "`wrangler d1 execute DB` printed more than 67108864 bytes, …"
|
|
97
|
+
*/
|
|
98
|
+
export function describeWranglerFailure(args, error) {
|
|
99
|
+
const command = `wrangler ${args.slice(0, 3).join(" ")}`;
|
|
100
|
+
if (error?.code === "ENOBUFS") {
|
|
101
|
+
return `\`${command}\` printed more than ${WRANGLER_MAX_BUFFER} bytes, the most the CLI buffers.`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const stderr = String(error?.stderr ?? "").trim();
|
|
105
|
+
if (stderr) return `\`${command}\` failed: ${stderr}`;
|
|
106
|
+
|
|
107
|
+
const stdout = String(error?.stdout ?? "").trim();
|
|
108
|
+
if (stdout) {
|
|
109
|
+
const excerpt =
|
|
110
|
+
stdout.length > WRANGLER_OUTPUT_EXCERPT_LENGTH
|
|
111
|
+
? `${stdout.slice(0, WRANGLER_OUTPUT_EXCERPT_LENGTH)}… (${stdout.length} characters)`
|
|
112
|
+
: stdout;
|
|
113
|
+
return `\`${command}\` failed: ${excerpt}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return `\`${command}\` failed: ${error?.message ?? "unknown error"}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
64
119
|
export function isRetryableWranglerD1Failure(output, error) {
|
|
65
120
|
const combined = `${output ?? ""}\n${error?.message ?? ""}`.toLowerCase();
|
|
66
121
|
return [
|
|
@@ -75,18 +130,6 @@ export function isRetryableWranglerD1Failure(output, error) {
|
|
|
75
130
|
].some((fragment) => combined.includes(fragment));
|
|
76
131
|
}
|
|
77
132
|
|
|
78
|
-
/**
|
|
79
|
-
* Strip non-JSON preamble that Wrangler sometimes writes to stdout
|
|
80
|
-
* (e.g. "Proxy environment variables detected…") before the actual JSON payload.
|
|
81
|
-
*/
|
|
82
|
-
function extractJson(raw) {
|
|
83
|
-
const idx = raw.indexOf("[");
|
|
84
|
-
const idx2 = raw.indexOf("{");
|
|
85
|
-
if (idx === -1 && idx2 === -1) return raw;
|
|
86
|
-
const start = idx === -1 ? idx2 : idx2 === -1 ? idx : Math.min(idx, idx2);
|
|
87
|
-
return raw.slice(start);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
133
|
function getWranglerExecutionOptions(options) {
|
|
91
134
|
const executionOptions = { ...options };
|
|
92
135
|
for (const key of [
|
|
@@ -134,12 +177,43 @@ function runWrangler(args, options = {}) {
|
|
|
134
177
|
throw new Error(`Wrangler error: ${wranglerError}`);
|
|
135
178
|
}
|
|
136
179
|
|
|
137
|
-
throw new Error(
|
|
180
|
+
throw new Error(describeWranglerFailure(args, error), { cause: error });
|
|
138
181
|
}
|
|
139
182
|
}
|
|
140
183
|
}
|
|
141
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Whether a statement batch is too large to travel as one CLI argument.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} sql - SQL batch about to be sent to Wrangler
|
|
189
|
+
* @returns {boolean} true when it has to go through a file instead
|
|
190
|
+
* @example
|
|
191
|
+
* ```js
|
|
192
|
+
* exceedsInlineCommandLimit("SELECT 1;"); // false
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export function exceedsInlineCommandLimit(sql) {
|
|
196
|
+
return Buffer.byteLength(sql, "utf8") >= MAX_INLINE_COMMAND_BYTES;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function withTemporarySqlFile(sql, run) {
|
|
200
|
+
const directory = mkdtempSync(join(tmpdir(), "jant-d1-"));
|
|
201
|
+
try {
|
|
202
|
+
const filePath = join(directory, "batch.sql");
|
|
203
|
+
writeFileSync(filePath, sql);
|
|
204
|
+
return run(filePath);
|
|
205
|
+
} finally {
|
|
206
|
+
rmSync(directory, { recursive: true, force: true });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
142
210
|
export function executeD1(sql, runtime, options = {}) {
|
|
211
|
+
if (exceedsInlineCommandLimit(sql)) {
|
|
212
|
+
return withTemporarySqlFile(sql, (filePath) =>
|
|
213
|
+
executeD1File(filePath, runtime, options),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
143
217
|
const args = appendWranglerContext(
|
|
144
218
|
[
|
|
145
219
|
"d1",
|
|
@@ -153,7 +227,7 @@ export function executeD1(sql, runtime, options = {}) {
|
|
|
153
227
|
|
|
154
228
|
if (options.quiet) {
|
|
155
229
|
const output = runWrangler([...args, "--json"], options);
|
|
156
|
-
const parsed = JSON.parse(
|
|
230
|
+
const parsed = JSON.parse(extractWranglerJson(output));
|
|
157
231
|
const statements = Array.isArray(parsed) ? parsed : [parsed];
|
|
158
232
|
|
|
159
233
|
for (const statement of statements) {
|
|
@@ -183,7 +257,7 @@ export function queryD1(sql, runtime, options = {}) {
|
|
|
183
257
|
),
|
|
184
258
|
options,
|
|
185
259
|
);
|
|
186
|
-
const parsed = JSON.parse(
|
|
260
|
+
const parsed = JSON.parse(extractWranglerJson(output));
|
|
187
261
|
const statement = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
188
262
|
|
|
189
263
|
if (statement?.error?.text) {
|
|
@@ -208,7 +282,7 @@ export function executeD1File(filePath, runtime, options = {}) {
|
|
|
208
282
|
|
|
209
283
|
if (options.quiet) {
|
|
210
284
|
const output = runWrangler([...args, "--json"], options);
|
|
211
|
-
const parsed = JSON.parse(
|
|
285
|
+
const parsed = JSON.parse(extractWranglerJson(output));
|
|
212
286
|
const statements = Array.isArray(parsed) ? parsed : [parsed];
|
|
213
287
|
|
|
214
288
|
for (const statement of statements) {
|
package/bin/lib/hugo-markdown.js
CHANGED
|
@@ -20,6 +20,7 @@ const FRONT_MATTER_KEY_ORDER = [
|
|
|
20
20
|
"type",
|
|
21
21
|
"draft",
|
|
22
22
|
"aliases",
|
|
23
|
+
"feed_id",
|
|
23
24
|
"build",
|
|
24
25
|
|
|
25
26
|
// Post payload
|
|
@@ -27,12 +28,15 @@ const FRONT_MATTER_KEY_ORDER = [
|
|
|
27
28
|
"status",
|
|
28
29
|
"visibility",
|
|
29
30
|
"summary_text",
|
|
31
|
+
"truncated",
|
|
30
32
|
"link_url",
|
|
31
33
|
"source_name",
|
|
32
34
|
"source_url",
|
|
33
35
|
"quote_text",
|
|
34
36
|
"rating",
|
|
35
37
|
"featured_at",
|
|
38
|
+
"featured_post_ids",
|
|
39
|
+
"featured_sort_at",
|
|
36
40
|
"pinned_at",
|
|
37
41
|
|
|
38
42
|
// Bookkeeping / attachments
|
package/bin/lib/node-env.js
CHANGED
|
@@ -22,13 +22,46 @@ function fileExists(envPath) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Locate `.env.node` for CLI auto-load.
|
|
25
|
+
* Locate `.env.node` for CLI auto-load.
|
|
26
|
+
*
|
|
27
|
+
* `JANT_ENV_FILE` overrides the search whenever it is set: a path names the
|
|
28
|
+
* file to load, and an empty value means "load nothing". The empty form is
|
|
29
|
+
* what keeps a developer's `packages/core/.env.node` out of the test suite —
|
|
30
|
+
* see the `env` block in `vitest.config.ts`.
|
|
31
|
+
*
|
|
32
|
+
* Without the override, the search order is:
|
|
26
33
|
* 1. `<cwd>/.env.node` — user's site directory
|
|
27
34
|
* 2. `<bin>/../../.env.node` — `packages/core/.env.node` (in-repo dev)
|
|
28
35
|
*
|
|
29
|
-
*
|
|
36
|
+
* @param {string} [cwd] - Directory searched first, and the base a relative
|
|
37
|
+
* `JANT_ENV_FILE` resolves against. Defaults to `process.cwd()`.
|
|
38
|
+
* @param {Record<string, string | undefined>} [env] - Environment to read
|
|
39
|
+
* `JANT_ENV_FILE` from. Defaults to `process.env`.
|
|
40
|
+
* @returns {string | null} The env file to load, or `null` when there is none.
|
|
41
|
+
* @throws {Error} When `JANT_ENV_FILE` names a file that does not exist.
|
|
42
|
+
* @example
|
|
43
|
+
* findNodeEnvPath(process.cwd(), { JANT_ENV_FILE: "" }); // null
|
|
44
|
+
* findNodeEnvPath(process.cwd(), { JANT_ENV_FILE: "./staging.env" });
|
|
30
45
|
*/
|
|
31
|
-
export function findNodeEnvPath(cwd = process.cwd()) {
|
|
46
|
+
export function findNodeEnvPath(cwd = process.cwd(), env = process.env) {
|
|
47
|
+
const override = env.JANT_ENV_FILE;
|
|
48
|
+
if (override !== undefined) {
|
|
49
|
+
const trimmed = override.trim();
|
|
50
|
+
if (trimmed === "") {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const overridePath = resolve(cwd, trimmed);
|
|
55
|
+
if (!fileExists(overridePath)) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`JANT_ENV_FILE points at "${override}", which does not exist. ` +
|
|
58
|
+
"Set it to a readable env file, or to an empty value to skip the env file.",
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return overridePath;
|
|
63
|
+
}
|
|
64
|
+
|
|
32
65
|
const candidates = [
|
|
33
66
|
resolve(cwd, ".env.node"),
|
|
34
67
|
resolve(dirname(fileURLToPath(import.meta.url)), "../../.env.node"),
|
|
@@ -90,9 +123,12 @@ export function loadNodeEnvFile(envPath, env = process.env) {
|
|
|
90
123
|
* Always called before `resolveCliRuntime()`, so DATABASE_URL / DATA_DIR
|
|
91
124
|
* defined in `.env.node` make `--node` (or auto-detect) work without
|
|
92
125
|
* requiring the user to source the file manually.
|
|
126
|
+
*
|
|
127
|
+
* Honours `JANT_ENV_FILE` through {@link findNodeEnvPath}, so a caller that
|
|
128
|
+
* needs a fully controlled environment can opt out of the file entirely.
|
|
93
129
|
*/
|
|
94
130
|
export function autoloadNodeEnv(env = process.env) {
|
|
95
|
-
const envPath = findNodeEnvPath();
|
|
131
|
+
const envPath = findNodeEnvPath(process.cwd(), env);
|
|
96
132
|
if (!envPath) {
|
|
97
133
|
return { envPath: null, found: false, assignedKeys: [], skippedKeys: [] };
|
|
98
134
|
}
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "node:fs/promises";
|
|
13
13
|
import { tmpdir } from "node:os";
|
|
14
14
|
import { basename, dirname, extname, join, relative } from "node:path";
|
|
15
|
-
import { unzipSync, zipSync } from "fflate";
|
|
16
15
|
import { parse, stringify } from "smol-toml";
|
|
17
16
|
import {
|
|
18
17
|
collectMediaReferences as collectParsedMediaReferences,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
rewriteMediaReferences,
|
|
21
20
|
} from "./site-media-parser.js";
|
|
22
21
|
import { formatFrontMatter, parseFrontMatter } from "./hugo-markdown.js";
|
|
22
|
+
import { extractZipFile, writeDirectoryToZip } from "./zip-archive.js";
|
|
23
23
|
|
|
24
24
|
export function getSitePathPrefix(baseUrl) {
|
|
25
25
|
if (typeof baseUrl !== "string" || baseUrl.trim() === "") {
|
|
@@ -477,29 +477,6 @@ async function resolveExistingPulledPath(
|
|
|
477
477
|
return toPulledPublicPath(pathname, sitePathPrefix);
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
async function packDirectoryToZip(rootDir) {
|
|
481
|
-
const files = {};
|
|
482
|
-
const allFiles = await walkFiles(rootDir);
|
|
483
|
-
|
|
484
|
-
for (const fullPath of allFiles) {
|
|
485
|
-
const relPath = relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
486
|
-
files[relPath] = new Uint8Array(await readFile(fullPath));
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return zipSync(files);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
async function unpackZipToDirectory(zipBytes, rootDir) {
|
|
493
|
-
const files = unzipSync(zipBytes);
|
|
494
|
-
await Promise.all(
|
|
495
|
-
Object.entries(files).map(async ([relPath, bytes]) => {
|
|
496
|
-
const fullPath = join(rootDir, relPath);
|
|
497
|
-
await mkdir(dirname(fullPath), { recursive: true });
|
|
498
|
-
await writeFile(fullPath, bytes);
|
|
499
|
-
}),
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
480
|
export async function pullSiteExportDirectory(rootDir, options = {}) {
|
|
504
481
|
const logger =
|
|
505
482
|
typeof options.logger === "function" ? options.logger : () => {};
|
|
@@ -681,14 +658,30 @@ export async function pullSiteExportDirectory(rootDir, options = {}) {
|
|
|
681
658
|
return stats;
|
|
682
659
|
}
|
|
683
660
|
|
|
684
|
-
|
|
661
|
+
/**
|
|
662
|
+
* Pull media into an export ZIP: extract it to a temporary directory, pull
|
|
663
|
+
* there, and write the result as a new archive. Nothing is held in memory
|
|
664
|
+
* beyond one media file at a time, so the archive can be any size.
|
|
665
|
+
*
|
|
666
|
+
* @param {string} inputZipPath - Export archive to read
|
|
667
|
+
* @param {string} outputZipPath - Archive to write; may be the input
|
|
668
|
+
* @param {Parameters<typeof pullSiteExportDirectory>[1]} [options]
|
|
669
|
+
* @returns {Promise<Awaited<ReturnType<typeof pullSiteExportDirectory>>>} Pull stats
|
|
670
|
+
* @example
|
|
671
|
+
* await pullSiteExportZipFile("export.zip", "export.zip", { logger });
|
|
672
|
+
*/
|
|
673
|
+
export async function pullSiteExportZipFile(
|
|
674
|
+
inputZipPath,
|
|
675
|
+
outputZipPath,
|
|
676
|
+
options = {},
|
|
677
|
+
) {
|
|
685
678
|
const tempDir = await mkdtemp(join(tmpdir(), "jant-site-pull-"));
|
|
686
679
|
|
|
687
680
|
try {
|
|
688
|
-
await
|
|
681
|
+
await extractZipFile(inputZipPath, tempDir);
|
|
689
682
|
const stats = await pullSiteExportDirectory(tempDir, options);
|
|
690
|
-
|
|
691
|
-
return
|
|
683
|
+
await writeDirectoryToZip(tempDir, outputZipPath);
|
|
684
|
+
return stats;
|
|
692
685
|
} finally {
|
|
693
686
|
await rm(tempDir, { recursive: true, force: true });
|
|
694
687
|
}
|
|
@@ -39,16 +39,45 @@ function normalizePathPrefix(value) {
|
|
|
39
39
|
return normalized || null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Printed when a host-based command names no site. Kept word for word with
|
|
44
|
+
* `resolveCliSite` in `src/runtime/site.ts`, which the Node path uses.
|
|
45
|
+
*/
|
|
46
|
+
const HOST_BASED_SITE_REQUIRED_MESSAGE =
|
|
47
|
+
"host-based mode needs a target site. Pass --site <key|id>, --host <host>, or --url <url>.";
|
|
48
|
+
|
|
49
|
+
function getTrimmedOption(value) {
|
|
50
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Turn the site flags a command parsed into the selector both resolvers take:
|
|
55
|
+
* this file's raw-SQL `resolveCliSite` and `createNodeCliRuntime`.
|
|
56
|
+
*
|
|
57
|
+
* @param {{ site?: string, host?: string, pathPrefix?: string, url?: string }} [options]
|
|
58
|
+
* The `--site`, `--host`, `--path-prefix` and `--url` values
|
|
59
|
+
* @returns {{ kind: "site", idOrKey: string } | { kind: "host", host: string, pathPrefix: string | null } | null}
|
|
60
|
+
* The selector, or null when no site flag was passed
|
|
61
|
+
* @throws {Error} When more than one of `--site`, `--host` and `--url` is set
|
|
62
|
+
* @example
|
|
63
|
+
* parseCliSiteSelector({ url: "https://example.com/blog/" });
|
|
64
|
+
* // { kind: "host", host: "example.com", pathPrefix: "/blog" }
|
|
65
|
+
*/
|
|
66
|
+
export function parseCliSiteSelector(options = {}) {
|
|
67
|
+
const site = getTrimmedOption(options.site);
|
|
68
|
+
const host = getTrimmedOption(options.host);
|
|
69
|
+
const url = getTrimmedOption(options.url);
|
|
70
|
+
|
|
71
|
+
if ([site, host, url].filter(Boolean).length > 1) {
|
|
72
|
+
throw new Error("Choose only one of --site, --host, or --url.");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (site) {
|
|
76
|
+
return { kind: "site", idOrKey: site };
|
|
48
77
|
}
|
|
49
78
|
|
|
50
|
-
if (
|
|
51
|
-
const parsed = new URL(
|
|
79
|
+
if (url) {
|
|
80
|
+
const parsed = new URL(url);
|
|
52
81
|
return {
|
|
53
82
|
kind: "host",
|
|
54
83
|
host: parsed.host,
|
|
@@ -56,10 +85,10 @@ function resolveSelector(options = {}) {
|
|
|
56
85
|
};
|
|
57
86
|
}
|
|
58
87
|
|
|
59
|
-
if (
|
|
88
|
+
if (host) {
|
|
60
89
|
return {
|
|
61
90
|
kind: "host",
|
|
62
|
-
host
|
|
91
|
+
host,
|
|
63
92
|
pathPrefix: normalizePathPrefix(options.pathPrefix),
|
|
64
93
|
};
|
|
65
94
|
}
|
|
@@ -67,21 +96,26 @@ function resolveSelector(options = {}) {
|
|
|
67
96
|
return null;
|
|
68
97
|
}
|
|
69
98
|
|
|
70
|
-
|
|
99
|
+
// Mirrors `SiteService.getByIdOrKey`: an id match wins over a key match.
|
|
100
|
+
async function resolveSiteByIdOrKey(queryRunner, idOrKey) {
|
|
101
|
+
const escaped = escapeSqlString(idOrKey);
|
|
71
102
|
const rows = await queryRunner.query(`
|
|
72
103
|
SELECT "id", "key", "status", "created_at", "updated_at"
|
|
73
104
|
FROM "site"
|
|
74
|
-
WHERE "id" = '${
|
|
75
|
-
LIMIT
|
|
105
|
+
WHERE "id" = '${escaped}' OR "key" = '${escaped}'
|
|
106
|
+
LIMIT 2
|
|
76
107
|
`);
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
109
|
+
const row =
|
|
110
|
+
rows.find((candidate) => getOptionalString(candidate, "id") === idOrKey) ??
|
|
111
|
+
rows[0];
|
|
112
|
+
if (!row) {
|
|
113
|
+
throw new Error(`No site found for --site ${idOrKey}.`);
|
|
80
114
|
}
|
|
81
115
|
|
|
82
116
|
return {
|
|
83
117
|
created: false,
|
|
84
|
-
site: toSite(
|
|
118
|
+
site: toSite(row),
|
|
85
119
|
};
|
|
86
120
|
}
|
|
87
121
|
|
|
@@ -142,24 +176,63 @@ function formatSiteSummary(rows) {
|
|
|
142
176
|
.join(", ");
|
|
143
177
|
}
|
|
144
178
|
|
|
179
|
+
/**
|
|
180
|
+
* How the target instance maps requests to sites.
|
|
181
|
+
*
|
|
182
|
+
* @param {{ SITE_RESOLUTION_MODE?: unknown }} [env] The process environment,
|
|
183
|
+
* or runtime bindings built from it; defaults to `process.env`
|
|
184
|
+
* @returns {"host-based" | "single-site"} `host-based` only when set to it
|
|
185
|
+
* @example
|
|
186
|
+
* if (getCliSiteResolutionMode(process.env) === "host-based") { ... }
|
|
187
|
+
*/
|
|
145
188
|
export function getCliSiteResolutionMode(env = process.env) {
|
|
146
189
|
return env.SITE_RESOLUTION_MODE === "host-based"
|
|
147
190
|
? "host-based"
|
|
148
191
|
: "single-site";
|
|
149
192
|
}
|
|
150
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Resolve the site a command acts on with raw SQL, for runtimes that cannot
|
|
196
|
+
* run the site service (D1 through Wrangler, snapshot tooling).
|
|
197
|
+
*
|
|
198
|
+
* A site flag wins in either mode. Without one, single-site mode uses the
|
|
199
|
+
* instance's one site, and host-based mode fails before querying: a hosted
|
|
200
|
+
* database holds every tenant, so there is no default to fall back to.
|
|
201
|
+
*
|
|
202
|
+
* @param {{ query(sql: string): Promise<Record<string, unknown>[]>, execute?(sql: string): Promise<void> }} queryRunner
|
|
203
|
+
* Runs SQL against the target database
|
|
204
|
+
* @param {{ env?: { SITE_RESOLUTION_MODE?: unknown }, site?: string, host?: string, pathPrefix?: string, url?: string, createIfMissing?: boolean, bootstrapSite?: { id?: string, key?: string } }} [options]
|
|
205
|
+
* The environment, the command's site flags, and whether single-site mode
|
|
206
|
+
* may create the site shell when none exists
|
|
207
|
+
* @returns {Promise<{ created: boolean, site: { id: string, key: string, status: string, createdAt: number, updatedAt: number } }>}
|
|
208
|
+
* The site, and whether this call created it
|
|
209
|
+
* @throws {Error} When the flags match no site, when host-based mode has none,
|
|
210
|
+
* or when single-site mode finds zero sites it may not create or several
|
|
211
|
+
* @example
|
|
212
|
+
* const { site } = await resolveCliSite(context, {
|
|
213
|
+
* env: process.env,
|
|
214
|
+
* site: values.site,
|
|
215
|
+
* host: values.host,
|
|
216
|
+
* pathPrefix: values["path-prefix"],
|
|
217
|
+
* url: values.url,
|
|
218
|
+
* });
|
|
219
|
+
*/
|
|
151
220
|
export async function resolveCliSite(queryRunner, options = {}) {
|
|
152
221
|
const resolutionMode = getCliSiteResolutionMode(options.env);
|
|
153
|
-
const selector =
|
|
222
|
+
const selector = parseCliSiteSelector(options);
|
|
154
223
|
|
|
155
224
|
if (selector?.kind === "site") {
|
|
156
|
-
return
|
|
225
|
+
return resolveSiteByIdOrKey(queryRunner, selector.idOrKey);
|
|
157
226
|
}
|
|
158
227
|
|
|
159
228
|
if (selector?.kind === "host") {
|
|
160
229
|
return resolveSiteByHost(queryRunner, selector.host, selector.pathPrefix);
|
|
161
230
|
}
|
|
162
231
|
|
|
232
|
+
if (resolutionMode === "host-based") {
|
|
233
|
+
throw new Error(HOST_BASED_SITE_REQUIRED_MESSAGE);
|
|
234
|
+
}
|
|
235
|
+
|
|
163
236
|
const rows = await queryRunner.query(`
|
|
164
237
|
SELECT "id", "key", "status", "created_at", "updated_at"
|
|
165
238
|
FROM "site"
|
|
@@ -168,12 +241,6 @@ export async function resolveCliSite(queryRunner, options = {}) {
|
|
|
168
241
|
`);
|
|
169
242
|
|
|
170
243
|
if (rows.length === 0) {
|
|
171
|
-
if (resolutionMode !== "single-site") {
|
|
172
|
-
throw new Error(
|
|
173
|
-
"No site configured for this instance. Create a site before using host-based mode.",
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
244
|
if (!options.createIfMissing || typeof queryRunner.execute !== "function") {
|
|
178
245
|
throw new Error(
|
|
179
246
|
"single-site mode requires an initialized site. Complete /setup first or run a command that can bootstrap the site shell.",
|
|
@@ -208,11 +275,9 @@ export async function resolveCliSite(queryRunner, options = {}) {
|
|
|
208
275
|
}
|
|
209
276
|
|
|
210
277
|
if (rows.length > 1) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
: "host-based mode requires --site, --host, or --url when the database contains multiple sites.";
|
|
215
|
-
throw new Error(message);
|
|
278
|
+
throw new Error(
|
|
279
|
+
`single-site mode found multiple sites in the database: ${formatSiteSummary(rows)}. Restore SITE_RESOLUTION_MODE=host-based for this database, or remove the extra sites before restarting in single-site mode.`,
|
|
280
|
+
);
|
|
216
281
|
}
|
|
217
282
|
|
|
218
283
|
return {
|