@morphemecms/cms 0.1.0 → 0.2.0-next-20261104150211
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/CHANGELOG.md +12 -0
- package/LICENSE +21 -21
- package/README.md +51 -51
- package/astro.mjs +162 -162
- package/bin/morpheme.mjs +38 -38
- package/client.d.ts +10 -10
- package/defaults/blockTexts.ts +33 -33
- package/defaults/collections.ts +774 -774
- package/defaults/colors.ts +125 -125
- package/defaults/company.ts +20 -20
- package/defaults/editor.ts +178 -178
- package/defaults/internalLinks.ts +30 -30
- package/defaults/opens.ts +18 -18
- package/defaults/popup.tsx +93 -93
- package/defaults/privacy.ts +79 -79
- package/defaults/rules.ts +450 -450
- package/defaults/schema.ts +16 -16
- package/defaults/sitedata.ts +129 -129
- package/defaults/standard.ts +106 -106
- package/defaults/standardPages.ts +87 -87
- package/defaults/texts.ts +481 -481
- package/demo/README.md +26 -26
- package/demo/src/content/blog/build-a-page-from-blocks.md +19 -19
- package/demo/src/content/blog/colors-everyone-can-read.md +19 -19
- package/demo/src/content/blog/edit-your-website-from-your-phone.md +21 -21
- package/demo/src/content/blog/every-save-is-a-version.md +21 -21
- package/demo/src/content/blog/schedule-a-post.md +17 -17
- package/demo/src/content/blog/secure-from-day-one.md +17 -17
- package/demo/src/content/blog/seo-without-the-guesswork.md +23 -23
- package/demo/src/content/blog/switch-themes-without-losing-anything.md +23 -23
- package/demo/src/content/blog/the-app-store.md +21 -21
- package/demo/src/content/pages/about.md +22 -22
- package/demo/src/content/pages/app-store.md +30 -30
- package/demo/src/content/pages/blocks.md +98 -98
- package/demo/src/content/pages/blog-page.md +11 -11
- package/demo/src/content/pages/contact.md +16 -16
- package/demo/src/content/pages/get-started.md +20 -20
- package/demo/src/content/pages/home.md +26 -26
- package/demo/src/content/pages/page-not-found.md +11 -11
- package/demo/src/content/pages/privacy-policy.md +24 -24
- package/demo/src/data/blocks.json +25 -25
- package/demo/src/data/booking.json +6 -6
- package/demo/src/data/categories/apps.json +6 -6
- package/demo/src/data/categories/design.json +6 -6
- package/demo/src/data/categories/editing.json +6 -6
- package/demo/src/data/categories/security.json +6 -6
- package/demo/src/data/categories/seo.json +6 -6
- package/demo/src/data/colors.json +75 -75
- package/demo/src/data/contact.json +8 -8
- package/demo/src/data/cookies.json +21 -21
- package/demo/src/data/footer.json +27 -27
- package/demo/src/data/menu.json +35 -35
- package/demo/src/data/mobile-menu.json +25 -25
- package/demo/src/data/popups/booking-bar.json +13 -13
- package/demo/src/data/popups/try-the-editor.json +16 -16
- package/demo/src/data/standard-pages.json +8 -8
- package/demo/src/data/testimonials/sample-bakery.json +10 -10
- package/demo/src/data/testimonials/sample-clinic.json +10 -10
- package/demo/src/data/testimonials/sample-studio.json +10 -10
- package/demo/src/site/colors.ts +11 -11
- package/demo/src/site/config.ts +19 -19
- package/icons/lucide/LICENSE +43 -43
- package/icons/lucide/address-book.svg +1 -1
- package/icons/lucide/app-window.svg +1 -1
- package/icons/lucide/arrow-counter-clockwise.svg +1 -1
- package/icons/lucide/arrow-down.svg +1 -1
- package/icons/lucide/arrow-left.svg +1 -1
- package/icons/lucide/arrow-right.svg +1 -1
- package/icons/lucide/arrow-square-out.svg +1 -1
- package/icons/lucide/arrow-u-up-left.svg +1 -1
- package/icons/lucide/arrow-u-up-right.svg +1 -1
- package/icons/lucide/arrow-up-right.svg +1 -1
- package/icons/lucide/arrow-up.svg +1 -1
- package/icons/lucide/arrows-down-up.svg +1 -1
- package/icons/lucide/arrows-in-line-horizontal.svg +1 -1
- package/icons/lucide/arrows-out-line-horizontal.svg +1 -1
- package/icons/lucide/article.svg +1 -1
- package/icons/lucide/bank.svg +1 -1
- package/icons/lucide/bell.svg +1 -1
- package/icons/lucide/bicycle.svg +1 -1
- package/icons/lucide/binoculars.svg +1 -1
- package/icons/lucide/book-open-text.svg +1 -1
- package/icons/lucide/book-open.svg +1 -1
- package/icons/lucide/browser.svg +1 -1
- package/icons/lucide/buildings.svg +1 -1
- package/icons/lucide/bus.svg +1 -1
- package/icons/lucide/calendar-blank.svg +1 -1
- package/icons/lucide/calendar-check.svg +1 -1
- package/icons/lucide/car.svg +1 -1
- package/icons/lucide/cards.svg +1 -1
- package/icons/lucide/caret-down.svg +1 -1
- package/icons/lucide/caret-left.svg +1 -1
- package/icons/lucide/caret-right.svg +1 -1
- package/icons/lucide/caret-up.svg +1 -1
- package/icons/lucide/certificate.svg +1 -1
- package/icons/lucide/chart-line-up.svg +1 -1
- package/icons/lucide/chats.svg +1 -1
- package/icons/lucide/check-circle.svg +1 -1
- package/icons/lucide/check.svg +1 -1
- package/icons/lucide/circle-half.svg +1 -1
- package/icons/lucide/circle.svg +1 -1
- package/icons/lucide/clock-counter-clockwise.svg +1 -1
- package/icons/lucide/clock.svg +1 -1
- package/icons/lucide/code.svg +1 -1
- package/icons/lucide/coffee.svg +1 -1
- package/icons/lucide/columns.svg +1 -1
- package/icons/lucide/cookie.svg +1 -1
- package/icons/lucide/copy.svg +1 -1
- package/icons/lucide/credit-card.svg +1 -1
- package/icons/lucide/currency-circle-dollar.svg +1 -1
- package/icons/lucide/cursor-click.svg +1 -1
- package/icons/lucide/database.svg +1 -1
- package/icons/lucide/desktop.svg +1 -1
- package/icons/lucide/device-mobile.svg +1 -1
- package/icons/lucide/door-open.svg +1 -1
- package/icons/lucide/download-simple.svg +1 -1
- package/icons/lucide/envelope-simple.svg +1 -1
- package/icons/lucide/eye-slash.svg +1 -1
- package/icons/lucide/eye.svg +1 -1
- package/icons/lucide/file-pdf.svg +1 -1
- package/icons/lucide/file-text.svg +1 -1
- package/icons/lucide/file-video.svg +1 -1
- package/icons/lucide/file-zip.svg +1 -1
- package/icons/lucide/flower-lotus.svg +1 -1
- package/icons/lucide/folder-simple.svg +1 -1
- package/icons/lucide/funnel.svg +1 -1
- package/icons/lucide/gauge.svg +1 -1
- package/icons/lucide/gear.svg +1 -1
- package/icons/lucide/github-logo.svg +1 -1
- package/icons/lucide/globe.svg +1 -1
- package/icons/lucide/google-logo.svg +1 -1
- package/icons/lucide/graduation-cap.svg +1 -1
- package/icons/lucide/hammer.svg +1 -1
- package/icons/lucide/hand-heart.svg +1 -1
- package/icons/lucide/handshake.svg +1 -1
- package/icons/lucide/hard-drives.svg +1 -1
- package/icons/lucide/heart.svg +1 -1
- package/icons/lucide/house.svg +1 -1
- package/icons/lucide/image-square.svg +1 -1
- package/icons/lucide/image.svg +1 -1
- package/icons/lucide/info.svg +1 -1
- package/icons/lucide/keyboard.svg +1 -1
- package/icons/lucide/leaf.svg +1 -1
- package/icons/lucide/lifebuoy.svg +1 -1
- package/icons/lucide/lightbulb.svg +1 -1
- package/icons/lucide/link-break.svg +1 -1
- package/icons/lucide/link-simple.svg +1 -1
- package/icons/lucide/list-bullets.svg +1 -1
- package/icons/lucide/list-checks.svg +1 -1
- package/icons/lucide/list-numbers.svg +1 -1
- package/icons/lucide/list.svg +1 -1
- package/icons/lucide/lock-simple-open.svg +1 -1
- package/icons/lucide/lock-simple.svg +1 -1
- package/icons/lucide/magnifying-glass.svg +1 -1
- package/icons/lucide/map-pin.svg +1 -1
- package/icons/lucide/map-trifold.svg +1 -1
- package/icons/lucide/medal.svg +1 -1
- package/icons/lucide/megaphone.svg +1 -1
- package/icons/lucide/minus.svg +1 -1
- package/icons/lucide/moon.svg +1 -1
- package/icons/lucide/newspaper.svg +1 -1
- package/icons/lucide/pack.json +23 -23
- package/icons/lucide/paint-brush-broad.svg +1 -1
- package/icons/lucide/palette.svg +1 -1
- package/icons/lucide/paper-plane-tilt.svg +1 -1
- package/icons/lucide/pencil-simple.svg +1 -1
- package/icons/lucide/phone.svg +1 -1
- package/icons/lucide/play-fill.svg +1 -1
- package/icons/lucide/plus.svg +1 -1
- package/icons/lucide/prohibit.svg +1 -1
- package/icons/lucide/puzzle-piece.svg +1 -1
- package/icons/lucide/question.svg +1 -1
- package/icons/lucide/quotes.svg +1 -1
- package/icons/lucide/rows.svg +1 -1
- package/icons/lucide/ruler.svg +1 -1
- package/icons/lucide/scan.svg +1 -1
- package/icons/lucide/shapes.svg +1 -1
- package/icons/lucide/share-network.svg +1 -1
- package/icons/lucide/shield-check.svg +1 -1
- package/icons/lucide/sidebar-simple.svg +1 -1
- package/icons/lucide/sign-out.svg +1 -1
- package/icons/lucide/signpost.svg +1 -1
- package/icons/lucide/sliders-horizontal.svg +1 -1
- package/icons/lucide/smiley.svg +1 -1
- package/icons/lucide/sparkle.svg +1 -1
- package/icons/lucide/square-half-bottom.svg +1 -1
- package/icons/lucide/square.svg +1 -1
- package/icons/lucide/squares-four.svg +1 -1
- package/icons/lucide/sticker.svg +1 -1
- package/icons/lucide/storefront.svg +1 -1
- package/icons/lucide/sun.svg +1 -1
- package/icons/lucide/swatches.svg +1 -1
- package/icons/lucide/tag.svg +1 -1
- package/icons/lucide/text-aa.svg +1 -1
- package/icons/lucide/text-align-left.svg +1 -1
- package/icons/lucide/text-b.svg +1 -1
- package/icons/lucide/text-h-one.svg +1 -1
- package/icons/lucide/text-h-three.svg +1 -1
- package/icons/lucide/text-h-two.svg +1 -1
- package/icons/lucide/text-italic.svg +1 -1
- package/icons/lucide/text-t.svg +1 -1
- package/icons/lucide/ticket.svg +1 -1
- package/icons/lucide/toggle-right.svg +1 -1
- package/icons/lucide/train.svg +1 -1
- package/icons/lucide/trash.svg +1 -1
- package/icons/lucide/upload-simple.svg +1 -1
- package/icons/lucide/user-circle.svg +1 -1
- package/icons/lucide/user.svg +1 -1
- package/icons/lucide/users-three.svg +1 -1
- package/icons/lucide/users.svg +1 -1
- package/icons/lucide/video-camera.svg +1 -1
- package/icons/lucide/warning-circle.svg +1 -1
- package/icons/lucide/warning.svg +1 -1
- package/icons/lucide/wave-sine.svg +1 -1
- package/icons/lucide/wheelchair.svg +1 -1
- package/icons/lucide/wrench.svg +1 -1
- package/icons/lucide/x.svg +1 -1
- package/icons/material-symbols/LICENSE +201 -201
- package/icons/material-symbols/address-book.svg +1 -1
- package/icons/material-symbols/app-window.svg +1 -1
- package/icons/material-symbols/arrow-counter-clockwise.svg +1 -1
- package/icons/material-symbols/arrow-down.svg +1 -1
- package/icons/material-symbols/arrow-left.svg +1 -1
- package/icons/material-symbols/arrow-right.svg +1 -1
- package/icons/material-symbols/arrow-square-out.svg +1 -1
- package/icons/material-symbols/arrow-u-up-left.svg +1 -1
- package/icons/material-symbols/arrow-u-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up.svg +1 -1
- package/icons/material-symbols/arrows-down-up.svg +1 -1
- package/icons/material-symbols/arrows-in-line-horizontal.svg +1 -1
- package/icons/material-symbols/arrows-out-line-horizontal.svg +1 -1
- package/icons/material-symbols/article.svg +1 -1
- package/icons/material-symbols/bank.svg +1 -1
- package/icons/material-symbols/bell.svg +1 -1
- package/icons/material-symbols/bicycle.svg +1 -1
- package/icons/material-symbols/binoculars.svg +1 -1
- package/icons/material-symbols/book-open-text.svg +1 -1
- package/icons/material-symbols/book-open.svg +1 -1
- package/icons/material-symbols/browser.svg +1 -1
- package/icons/material-symbols/buildings.svg +1 -1
- package/icons/material-symbols/bus.svg +1 -1
- package/icons/material-symbols/calendar-blank.svg +1 -1
- package/icons/material-symbols/calendar-check.svg +1 -1
- package/icons/material-symbols/car.svg +1 -1
- package/icons/material-symbols/cards.svg +1 -1
- package/icons/material-symbols/caret-down.svg +1 -1
- package/icons/material-symbols/caret-left.svg +1 -1
- package/icons/material-symbols/caret-right.svg +1 -1
- package/icons/material-symbols/caret-up.svg +1 -1
- package/icons/material-symbols/certificate.svg +1 -1
- package/icons/material-symbols/chart-line-up.svg +1 -1
- package/icons/material-symbols/chats.svg +1 -1
- package/icons/material-symbols/check-circle.svg +1 -1
- package/icons/material-symbols/check.svg +1 -1
- package/icons/material-symbols/circle-half.svg +1 -1
- package/icons/material-symbols/circle.svg +1 -1
- package/icons/material-symbols/clock-counter-clockwise.svg +1 -1
- package/icons/material-symbols/clock.svg +1 -1
- package/icons/material-symbols/code.svg +1 -1
- package/icons/material-symbols/coffee.svg +1 -1
- package/icons/material-symbols/columns.svg +1 -1
- package/icons/material-symbols/cookie.svg +1 -1
- package/icons/material-symbols/copy.svg +1 -1
- package/icons/material-symbols/credit-card.svg +1 -1
- package/icons/material-symbols/currency-circle-dollar.svg +1 -1
- package/icons/material-symbols/cursor-click.svg +1 -1
- package/icons/material-symbols/database.svg +1 -1
- package/icons/material-symbols/desktop.svg +1 -1
- package/icons/material-symbols/device-mobile.svg +1 -1
- package/icons/material-symbols/door-open.svg +1 -1
- package/icons/material-symbols/download-simple.svg +1 -1
- package/icons/material-symbols/envelope-simple.svg +1 -1
- package/icons/material-symbols/eye-slash.svg +1 -1
- package/icons/material-symbols/eye.svg +1 -1
- package/icons/material-symbols/file-pdf.svg +1 -1
- package/icons/material-symbols/file-text.svg +1 -1
- package/icons/material-symbols/file-video.svg +1 -1
- package/icons/material-symbols/file-zip.svg +1 -1
- package/icons/material-symbols/flower-lotus.svg +1 -1
- package/icons/material-symbols/folder-simple.svg +1 -1
- package/icons/material-symbols/funnel.svg +1 -1
- package/icons/material-symbols/gauge.svg +1 -1
- package/icons/material-symbols/gear.svg +1 -1
- package/icons/material-symbols/github-logo.svg +1 -1
- package/icons/material-symbols/globe.svg +1 -1
- package/icons/material-symbols/google-logo.svg +1 -1
- package/icons/material-symbols/graduation-cap.svg +1 -1
- package/icons/material-symbols/hammer.svg +1 -1
- package/icons/material-symbols/hand-heart.svg +1 -1
- package/icons/material-symbols/handshake.svg +1 -1
- package/icons/material-symbols/hard-drives.svg +1 -1
- package/icons/material-symbols/heart.svg +1 -1
- package/icons/material-symbols/house.svg +1 -1
- package/icons/material-symbols/image-square.svg +1 -1
- package/icons/material-symbols/image.svg +1 -1
- package/icons/material-symbols/info.svg +1 -1
- package/icons/material-symbols/keyboard.svg +1 -1
- package/icons/material-symbols/leaf.svg +1 -1
- package/icons/material-symbols/lifebuoy.svg +1 -1
- package/icons/material-symbols/lightbulb.svg +1 -1
- package/icons/material-symbols/link-break.svg +1 -1
- package/icons/material-symbols/link-simple.svg +1 -1
- package/icons/material-symbols/list-bullets.svg +1 -1
- package/icons/material-symbols/list-checks.svg +1 -1
- package/icons/material-symbols/list-numbers.svg +1 -1
- package/icons/material-symbols/list.svg +1 -1
- package/icons/material-symbols/lock-simple-open.svg +1 -1
- package/icons/material-symbols/lock-simple.svg +1 -1
- package/icons/material-symbols/magnifying-glass.svg +1 -1
- package/icons/material-symbols/map-pin.svg +1 -1
- package/icons/material-symbols/map-trifold.svg +1 -1
- package/icons/material-symbols/medal.svg +1 -1
- package/icons/material-symbols/megaphone.svg +1 -1
- package/icons/material-symbols/minus.svg +1 -1
- package/icons/material-symbols/moon.svg +1 -1
- package/icons/material-symbols/newspaper.svg +1 -1
- package/icons/material-symbols/pack.json +29 -29
- package/icons/material-symbols/paint-brush-broad.svg +1 -1
- package/icons/material-symbols/palette.svg +1 -1
- package/icons/material-symbols/paper-plane-tilt.svg +1 -1
- package/icons/material-symbols/pencil-simple.svg +1 -1
- package/icons/material-symbols/phone.svg +1 -1
- package/icons/material-symbols/play-fill.svg +1 -1
- package/icons/material-symbols/plus.svg +1 -1
- package/icons/material-symbols/prohibit.svg +1 -1
- package/icons/material-symbols/puzzle-piece.svg +1 -1
- package/icons/material-symbols/question.svg +1 -1
- package/icons/material-symbols/quotes.svg +1 -1
- package/icons/material-symbols/rows.svg +1 -1
- package/icons/material-symbols/ruler.svg +1 -1
- package/icons/material-symbols/scan.svg +1 -1
- package/icons/material-symbols/shapes.svg +1 -1
- package/icons/material-symbols/share-network.svg +1 -1
- package/icons/material-symbols/shield-check.svg +1 -1
- package/icons/material-symbols/sidebar-simple.svg +1 -1
- package/icons/material-symbols/sign-out.svg +1 -1
- package/icons/material-symbols/signpost.svg +1 -1
- package/icons/material-symbols/sliders-horizontal.svg +1 -1
- package/icons/material-symbols/smiley.svg +1 -1
- package/icons/material-symbols/sparkle.svg +1 -1
- package/icons/material-symbols/square-half-bottom.svg +1 -1
- package/icons/material-symbols/square.svg +1 -1
- package/icons/material-symbols/squares-four.svg +1 -1
- package/icons/material-symbols/sticker.svg +1 -1
- package/icons/material-symbols/storefront.svg +1 -1
- package/icons/material-symbols/sun.svg +1 -1
- package/icons/material-symbols/swatches.svg +1 -1
- package/icons/material-symbols/tag.svg +1 -1
- package/icons/material-symbols/text-aa.svg +1 -1
- package/icons/material-symbols/text-align-left.svg +1 -1
- package/icons/material-symbols/text-b.svg +1 -1
- package/icons/material-symbols/text-h-one.svg +1 -1
- package/icons/material-symbols/text-h-three.svg +1 -1
- package/icons/material-symbols/text-h-two.svg +1 -1
- package/icons/material-symbols/text-italic.svg +1 -1
- package/icons/material-symbols/text-t.svg +1 -1
- package/icons/material-symbols/ticket.svg +1 -1
- package/icons/material-symbols/toggle-right.svg +1 -1
- package/icons/material-symbols/train.svg +1 -1
- package/icons/material-symbols/trash.svg +1 -1
- package/icons/material-symbols/upload-simple.svg +1 -1
- package/icons/material-symbols/user-circle.svg +1 -1
- package/icons/material-symbols/user.svg +1 -1
- package/icons/material-symbols/users-three.svg +1 -1
- package/icons/material-symbols/users.svg +1 -1
- package/icons/material-symbols/video-camera.svg +1 -1
- package/icons/material-symbols/warning-circle.svg +1 -1
- package/icons/material-symbols/warning.svg +1 -1
- package/icons/material-symbols/wave-sine.svg +1 -1
- package/icons/material-symbols/wheelchair.svg +1 -1
- package/icons/material-symbols/wrench.svg +1 -1
- package/icons/material-symbols/x.svg +1 -1
- package/icons/phosphor/pack.json +8 -8
- package/lib/apps.mjs +142 -139
- package/lib/apps.test.d.mts +1 -0
- package/lib/apps.test.mjs +50 -0
- package/lib/blocks.mjs +90 -90
- package/lib/caseCollisions.d.mts +4 -0
- package/lib/caseCollisions.mjs +62 -0
- package/lib/caseCollisions.test.d.mts +1 -0
- package/lib/caseCollisions.test.mjs +74 -0
- package/lib/colors.ts +86 -86
- package/lib/contactBar.ts +18 -18
- package/lib/contracts.d.mts +15 -0
- package/lib/contracts.mjs +17 -0
- package/lib/cookieCategories.mjs +28 -28
- package/lib/crop.mjs +83 -83
- package/lib/csp.mjs +130 -130
- package/lib/cspSources.mjs +60 -60
- package/lib/dynamic-colors.ts +426 -426
- package/lib/git.mjs +64 -64
- package/lib/hooks.ts +51 -51
- package/lib/inline.ts +20 -20
- package/lib/license-texts/Apache-2.0.txt +190 -190
- package/lib/license-texts/BSD-2-Clause.txt +24 -24
- package/lib/license-texts/BSD-3-Clause.txt +28 -28
- package/lib/license-texts/ISC.txt +15 -15
- package/lib/license-texts/MIT.txt +21 -21
- package/lib/licenses.d.mts +1 -1
- package/lib/licenses.mjs +683 -683
- package/lib/markdown.ts +42 -42
- package/lib/motion-schemes.mjs +5 -5
- package/lib/motion.ts +131 -131
- package/lib/notice.mjs +34 -34
- package/lib/scheduled.mjs +37 -37
- package/lib/snackbar.ts +149 -149
- package/lib/status.mjs +18 -18
- package/lib/storeEntries.d.mts +18 -0
- package/lib/storeEntries.mjs +64 -0
- package/lib/storeEntries.test.d.mts +1 -0
- package/lib/storeEntries.test.mjs +71 -0
- package/lib/svg.mjs +231 -231
- package/lib/text-colors.mjs +43 -43
- package/lib/video-id.mjs +72 -72
- package/lib/video.mjs +91 -91
- package/package.json +136 -131
- package/pages/404.astro +10 -10
- package/pages/[...slug].astro +51 -51
- package/pages/_headers.txt +31 -31
- package/pages/blog/index.astro +10 -10
- package/pages/blog/kategori/[category].astro +18 -18
- package/pages/editor/code-images.json.js +25 -25
- package/pages/editor/index.astro +75 -75
- package/pages/editor/manifest.webmanifest.js +23 -23
- package/pages/editor/sw.js +33 -33
- package/pages/editor/sw.js.js +4 -4
- package/pages/editor/thumbnails.json.js +21 -21
- package/pages/editor/version.json.js +22 -22
- package/pages/headers.js +4 -4
- package/pages/index.astro +10 -10
- package/pages/ny.astro +6 -6
- package/pages/robots.txt.js +3 -3
- package/pages/rss.xml.js +20 -20
- package/scripts/app-screenshots.mjs +164 -164
- package/scripts/app-workers.mjs +6 -6
- package/scripts/bin.mjs +12 -12
- package/scripts/built-site.mjs +134 -99
- package/scripts/check-consent.mjs +113 -113
- package/scripts/check-content.mjs +151 -149
- package/scripts/check-demo.mjs +24 -24
- package/scripts/check-store.mjs +160 -0
- package/scripts/check-themes.mjs +45 -45
- package/scripts/check-worker.mjs +569 -569
- package/scripts/compare-html.mjs +81 -81
- package/scripts/declarations.mjs +10 -0
- package/scripts/declarations.test.mjs +32 -0
- package/scripts/demo-site.mjs +42 -42
- package/scripts/editor-icons.mjs +56 -56
- package/scripts/editor-roundtrip.mjs +134 -134
- package/scripts/editor-screens.mjs +101 -101
- package/scripts/git-env.mjs +9 -9
- package/scripts/icon-packs.mjs +151 -151
- package/scripts/link-counts.mjs +22 -22
- package/scripts/page-colors.mjs +42 -42
- package/scripts/releases.mjs +80 -80
- package/scripts/responsive-images.mjs +94 -94
- package/scripts/scheduled-list.mjs +15 -15
- package/scripts/theme-screenshots.mjs +88 -88
- package/scripts/types.mjs +17 -21
- package/scripts/video-thumbnails.mjs +89 -89
- package/scripts/worker-modules.mjs +34 -34
- package/site/appCookies.mjs +74 -74
- package/site/apps.ts +21 -21
- package/site/autolinks.mjs +200 -200
- package/site/blockScripts.ts +31 -31
- package/site/categories.ts +16 -16
- package/site/consent.ts +26 -26
- package/site/content.config.ts +84 -84
- package/site/content.ts +104 -104
- package/site/cookieList.ts +17 -17
- package/site/defineSite.d.mts +2 -14
- package/site/defineSite.mjs +131 -136
- package/site/directives/interact.mjs +31 -31
- package/site/editor-version.mjs +26 -26
- package/site/fill.ts +13 -13
- package/site/format.ts +11 -11
- package/site/home.ts +66 -66
- package/site/iconPacks.mjs +43 -43
- package/site/icons.mjs +605 -605
- package/site/images.mjs +157 -157
- package/site/motion.ts +13 -13
- package/site/noindex.mjs +21 -21
- package/site/pageColors.ts +110 -110
- package/site/popups.ts +98 -98
- package/site/schema.ts +189 -189
- package/site/sections.ts +3 -3
- package/site/seo.ts +28 -28
- package/site/site.ts +76 -76
- package/site/siteConfig.mjs +8 -8
- package/site/siteCsp.mjs +75 -75
- package/site/siteModules.mjs +27 -27
- package/site/standardPages.ts +59 -59
- package/site/storeTheme.d.mts +6 -0
- package/site/storeTheme.mjs +29 -0
- package/site/storeTheme.test.d.mts +1 -0
- package/site/storeTheme.test.mjs +53 -0
- package/site/testimonials.ts +48 -48
- package/site/text.ts +43 -43
- package/site/theme.mjs +483 -472
- package/site/tracking.client.ts +225 -225
- package/site/tracking.ts +9 -9
- package/src/ActionParts.tsx +205 -205
- package/src/Advanced.tsx +152 -152
- package/src/App.tsx +901 -904
- package/src/AppList.tsx +1362 -1345
- package/src/BlockDialog.tsx +360 -360
- package/src/BlockParts.tsx +652 -652
- package/src/BlockSettings.tsx +533 -533
- package/src/BrandForm.tsx +164 -164
- package/src/Colors.tsx +476 -476
- package/src/CommunicationParts.tsx +199 -199
- package/src/CompanyForm.tsx +209 -209
- package/src/Consents.tsx +175 -175
- package/src/ContainmentParts.tsx +185 -185
- package/src/Crop.tsx +152 -152
- package/src/DataForm.tsx +434 -434
- package/src/Deleted.tsx +177 -177
- package/src/EntryForm.tsx +740 -740
- package/src/FileView.tsx +30 -30
- package/src/FormFrame.tsx +237 -237
- package/src/Keywords.tsx +225 -225
- package/src/Library.tsx +1563 -1563
- package/src/Licenses.tsx +336 -336
- package/src/LinkSheet.tsx +138 -138
- package/src/MarkdownEditor.tsx +1075 -1075
- package/src/Media.tsx +677 -677
- package/src/NavigationParts.tsx +350 -350
- package/src/NavigationRestParts.tsx +247 -247
- package/src/Notifications.tsx +59 -59
- package/src/PagePicker.tsx +136 -136
- package/src/Parts.tsx +991 -991
- package/src/PartsShown.tsx +168 -168
- package/src/PickerParts.tsx +183 -183
- package/src/Redirects.tsx +450 -450
- package/src/Screen.tsx +184 -184
- package/src/SectionsField.tsx +65 -65
- package/src/SelectionParts.tsx +134 -134
- package/src/SeoScores.tsx +184 -184
- package/src/Sheet.tsx +35 -35
- package/src/ThemeStore.tsx +376 -376
- package/src/Tokens.tsx +346 -346
- package/src/Versions.tsx +214 -214
- package/src/addresses.ts +75 -75
- package/src/allIcons.ts +7 -7
- package/src/appFiles.ts +29 -29
- package/src/apps.ts +281 -281
- package/src/ask.tsx +123 -123
- package/src/blocks.ts +251 -251
- package/src/brand.ts +153 -153
- package/src/cache.ts +42 -42
- package/src/check.ts +285 -285
- package/src/checkSite.ts +256 -256
- package/src/{consents.ts → consentData.ts} +26 -26
- package/src/cookieList.ts +70 -70
- package/src/countries.ts +59 -59
- package/src/deploy.ts +155 -155
- package/src/editor.css +602 -602
- package/src/examples.ts +85 -85
- package/src/fields.tsx +942 -942
- package/src/frontmatter.mjs +42 -42
- package/src/github.ts +376 -376
- package/src/i18n/da-DK/advanced.ts +28 -28
- package/src/i18n/da-DK/app.ts +71 -71
- package/src/i18n/da-DK/apps.ts +90 -90
- package/src/i18n/da-DK/blockSettings.ts +38 -38
- package/src/i18n/da-DK/blocks.ts +408 -408
- package/src/i18n/da-DK/check.ts +25 -25
- package/src/i18n/da-DK/colors.ts +64 -64
- package/src/i18n/da-DK/common.ts +118 -118
- package/src/i18n/da-DK/company.ts +24 -24
- package/src/i18n/da-DK/consents.ts +23 -23
- package/src/i18n/da-DK/forms.ts +116 -116
- package/src/i18n/da-DK/history.ts +35 -35
- package/src/i18n/da-DK/index.ts +49 -49
- package/src/i18n/da-DK/keywords.ts +19 -19
- package/src/i18n/da-DK/library.ts +135 -135
- package/src/i18n/da-DK/licenses.ts +25 -25
- package/src/i18n/da-DK/media.ts +79 -79
- package/src/i18n/da-DK/parts.ts +696 -696
- package/src/i18n/da-DK/preview.ts +13 -13
- package/src/i18n/da-DK/redirects.ts +51 -51
- package/src/i18n/da-DK/seo.ts +20 -20
- package/src/i18n/da-DK/text.ts +35 -35
- package/src/i18n/da-DK/theme.ts +41 -41
- package/src/i18n/en-US/advanced.ts +32 -32
- package/src/i18n/en-US/app.ts +84 -84
- package/src/i18n/en-US/apps.ts +134 -134
- package/src/i18n/en-US/blockSettings.ts +48 -48
- package/src/i18n/en-US/blocks.ts +441 -441
- package/src/i18n/en-US/check.ts +31 -31
- package/src/i18n/en-US/colors.ts +82 -82
- package/src/i18n/en-US/common.ts +144 -144
- package/src/i18n/en-US/company.ts +25 -25
- package/src/i18n/en-US/consents.ts +25 -25
- package/src/i18n/en-US/forms.ts +145 -145
- package/src/i18n/en-US/history.ts +43 -43
- package/src/i18n/en-US/index.ts +51 -51
- package/src/i18n/en-US/keywords.ts +24 -24
- package/src/i18n/en-US/library.ts +176 -176
- package/src/i18n/en-US/licenses.ts +36 -36
- package/src/i18n/en-US/media.ts +80 -80
- package/src/i18n/en-US/parts.ts +718 -718
- package/src/i18n/en-US/preview.ts +15 -15
- package/src/i18n/en-US/redirects.ts +66 -66
- package/src/i18n/en-US/seo.ts +26 -26
- package/src/i18n/en-US/text.ts +41 -41
- package/src/i18n/en-US/theme.ts +41 -41
- package/src/i18n/index.ts +124 -124
- package/src/icon.tsx +160 -160
- package/src/iconPacks.ts +89 -89
- package/src/images.ts +163 -163
- package/src/{licenses.ts → licenseData.ts} +79 -79
- package/src/markdown.mjs +204 -204
- package/src/nav.ts +68 -68
- package/src/notFound.ts +28 -28
- package/src/{notifications.ts → notificationData.ts} +223 -223
- package/src/pagePreview.tsx +204 -204
- package/src/r2Caching.ts +55 -55
- package/src/{redirects.ts → redirectData.ts} +149 -149
- package/src/registry.tsx +188 -188
- package/src/schema.ts +417 -417
- package/src/sections.mjs +303 -303
- package/src/seo.ts +333 -333
- package/src/shared.ts +44 -44
- package/src/sidebar.ts +31 -31
- package/src/site.ts +317 -317
- package/src/standard/Block.astro +19 -19
- package/src/standard/BlockSite.astro +18 -18
- package/src/standard/accordion/index.tsx +47 -47
- package/src/standard/accordion/load.ts +23 -23
- package/src/standard/accordion/shared.ts +15 -15
- package/src/standard/accordion/view.ts +32 -32
- package/src/standard/buttons/fields.ts +98 -98
- package/src/standard/buttons/index.tsx +23 -23
- package/src/standard/buttons/load.ts +34 -34
- package/src/standard/buttons/shared.ts +12 -12
- package/src/standard/buttons/view.ts +30 -30
- package/src/standard/cards/index.tsx +71 -71
- package/src/standard/cards/load.ts +17 -17
- package/src/standard/cards/shared.ts +27 -27
- package/src/standard/cards/view.ts +29 -29
- package/src/standard/checklist/index.tsx +27 -27
- package/src/standard/checklist/load.ts +5 -5
- package/src/standard/checklist/view.ts +13 -13
- package/src/standard/cookies/index.tsx +17 -17
- package/src/standard/cookies/list.ts +56 -56
- package/src/standard/cookies/load.ts +11 -11
- package/src/standard/cookies/shared.ts +20 -20
- package/src/standard/cookies/view.ts +28 -28
- package/src/standard/faq/index.tsx +29 -29
- package/src/standard/faq/load.ts +14 -14
- package/src/standard/faq/view.ts +21 -21
- package/src/standard/head.ts +23 -23
- package/src/standard/hero/fields.ts +48 -48
- package/src/standard/hero/index.tsx +57 -57
- package/src/standard/hero/load.ts +23 -23
- package/src/standard/hero/view.ts +38 -38
- package/src/standard/links/index.tsx +41 -41
- package/src/standard/links/load.ts +17 -17
- package/src/standard/links/view.ts +25 -25
- package/src/standard/list/index.tsx +50 -50
- package/src/standard/list/load.ts +5 -5
- package/src/standard/list/view.ts +18 -18
- package/src/standard/load.ts +7 -7
- package/src/standard/notice/index.tsx +30 -30
- package/src/standard/notice/load.ts +16 -16
- package/src/standard/notice/view.ts +10 -10
- package/src/standard/points/index.tsx +42 -42
- package/src/standard/points/load.ts +5 -5
- package/src/standard/points/view.ts +12 -12
- package/src/standard/posts/index.tsx +132 -132
- package/src/standard/posts/load.ts +36 -36
- package/src/standard/posts/shared.ts +21 -21
- package/src/standard/posts/view.ts +63 -63
- package/src/standard/pricecards/index.tsx +57 -57
- package/src/standard/pricecards/load.ts +11 -11
- package/src/standard/pricecards/view.ts +50 -50
- package/src/standard/sharedTexts.ts +6 -6
- package/src/standard/site.ts +124 -124
- package/src/standard/siteLooks.ts +19 -19
- package/src/standard/sites.ts +13 -13
- package/src/standard/steps/index.tsx +28 -28
- package/src/standard/steps/load.ts +5 -5
- package/src/standard/steps/view.ts +11 -11
- package/src/standard/testimonials/index.tsx +87 -87
- package/src/standard/testimonials/list.ts +43 -43
- package/src/standard/testimonials/load.ts +58 -58
- package/src/standard/testimonials/shared.ts +13 -13
- package/src/standard/testimonials/view.ts +76 -76
- package/src/standard/themeBlocks.ts +13 -13
- package/src/standard/video/index.tsx +23 -23
- package/src/standard/video/load.ts +10 -10
- package/src/standard/video/view.ts +42 -42
- package/src/standard/view.tsx +105 -105
- package/src/standard/views.ts +37 -37
- package/src/store.ts +479 -499
- package/src/theme.ts +90 -90
- package/src/themeUi.ts +97 -97
- package/src/themes.ts +91 -91
- package/src/ui.tsx +3402 -3402
- package/src/virtual.d.ts +5 -5
- package/src/when.ts +22 -22
- package/starter/.githooks/pre-push +4 -4
- package/starter/.nvmrc +1 -1
- package/starter/.prettierignore +11 -11
- package/starter/.prettierrc.json +7 -7
- package/starter/README.md +27 -26
- package/starter/astro.config.mjs +5 -5
- package/starter/package.json +67 -67
- package/starter/public/_redirects +1 -1
- package/starter/public/favicon.svg +1 -1
- package/starter/public/logo.svg +1 -1
- package/starter/src/content/pages/blog.md +8 -8
- package/starter/src/content/pages/forside.md +9 -9
- package/starter/src/content/pages/kontakt.md +7 -7
- package/starter/src/content/pages/privatlivspolitik.md +9 -9
- package/starter/src/content/pages/siden-findes-ikke.md +7 -7
- package/starter/src/content.config.ts +2 -2
- package/starter/src/data/apps.json +1 -1
- package/starter/src/data/block-settings.json +3 -3
- package/starter/src/data/blocks.json +1 -1
- package/starter/src/data/booking.json +6 -6
- package/starter/src/data/colors.json +70 -70
- package/starter/src/data/contact.json +8 -8
- package/starter/src/data/cookies.json +21 -21
- package/starter/src/data/csp.json +1 -1
- package/starter/src/data/footer.json +15 -15
- package/starter/src/data/internal-links.json +4 -4
- package/starter/src/data/menu.json +4 -4
- package/starter/src/data/mobile-menu.json +4 -4
- package/starter/src/data/old-addresses.json +1 -1
- package/starter/src/data/redirects.json +1 -1
- package/starter/src/data/standard-pages.json +8 -8
- package/starter/src/data/theme.json +1 -1
- package/starter/src/env.d.ts +2 -2
- package/starter/src/site/config.ts +13 -13
- package/starter/src/site/worker.js +14 -14
- package/starter/tsconfig.json +17 -17
- package/starter/worker/apps.js +6 -6
- package/starter/wrangler.jsonc +39 -39
- package/theme/LICENSE +21 -21
- package/theme/blocks/ArrowLink.tsx +14 -14
- package/theme/blocks/BlockHead.tsx +26 -26
- package/theme/blocks/Panel.tsx +32 -32
- package/theme/blocks/Person.tsx +26 -26
- package/theme/blocks/accordion/PageRow.tsx +25 -25
- package/theme/blocks/accordion/View.tsx +17 -17
- package/theme/blocks/buttons/Buttons.tsx +28 -28
- package/theme/blocks/buttons/View.tsx +12 -12
- package/theme/blocks/cards/LinkCards.tsx +72 -72
- package/theme/blocks/cards/View.tsx +20 -20
- package/theme/blocks/checklist/Checks.tsx +15 -15
- package/theme/blocks/checklist/View.tsx +14 -14
- package/theme/blocks/cookies/View.tsx +36 -36
- package/theme/blocks/faq/Accordion.tsx +34 -34
- package/theme/blocks/faq/View.tsx +16 -16
- package/theme/blocks/faq/script.ts +56 -56
- package/theme/blocks/hero/View.tsx +44 -44
- package/theme/blocks/links/LinkGroups.tsx +24 -24
- package/theme/blocks/links/View.tsx +17 -17
- package/theme/blocks/list/Rows.tsx +24 -24
- package/theme/blocks/list/View.tsx +14 -14
- package/theme/blocks/notice/View.tsx +13 -13
- package/theme/blocks/points/PointList.tsx +34 -34
- package/theme/blocks/points/View.tsx +14 -14
- package/theme/blocks/posts/View.tsx +26 -26
- package/theme/blocks/pricecards/View.tsx +40 -40
- package/theme/blocks/steps/StepList.tsx +23 -23
- package/theme/blocks/steps/View.tsx +14 -14
- package/theme/blocks/testimonials/List.tsx +46 -46
- package/theme/blocks/testimonials/Slider.tsx +44 -44
- package/theme/blocks/testimonials/Testimonial.tsx +124 -124
- package/theme/blocks/testimonials/View.tsx +21 -21
- package/theme/blocks/testimonials/script.ts +52 -52
- package/theme/blocks/video/View.tsx +41 -41
- package/theme/components/ActionBar.astro +63 -63
- package/theme/components/AppSlot.astro +21 -21
- package/theme/components/ArrowLink.tsx +21 -21
- package/theme/components/BlogIndex.astro +48 -48
- package/theme/components/BlogPosts.astro +18 -18
- package/theme/components/BlogPosts.tsx +45 -45
- package/theme/components/BookButton.tsx +22 -22
- package/theme/components/Container.astro +11 -11
- package/theme/components/CookieConsent.astro +121 -121
- package/theme/components/EndBlock.astro +21 -21
- package/theme/components/FontPreload.astro +6 -6
- package/theme/components/Footer.astro +85 -85
- package/theme/components/Header.astro +186 -186
- package/theme/components/Icon.astro +16 -16
- package/theme/components/Img.astro +29 -29
- package/theme/components/Img.tsx +33 -33
- package/theme/components/Inline.astro +11 -11
- package/theme/components/Logo.astro +23 -23
- package/theme/components/LogoMark.astro +10 -10
- package/theme/components/Markdown.astro +17 -17
- package/theme/components/MobileMenu.tsx +224 -224
- package/theme/components/MobileNav.astro +33 -33
- package/theme/components/PageHeader.astro +22 -22
- package/theme/components/PageSection.astro +24 -24
- package/theme/components/PhoneButton.tsx +11 -11
- package/theme/components/Popup.astro +145 -145
- package/theme/components/PopupButton.tsx +24 -24
- package/theme/components/PopupView.tsx +48 -48
- package/theme/components/PostList.tsx +58 -58
- package/theme/components/PostMeta.tsx +17 -17
- package/theme/components/RichText.astro +28 -28
- package/theme/components/Section.astro +29 -29
- package/theme/components/SectionHeading.tsx +8 -8
- package/theme/components/TextSection.astro +16 -16
- package/theme/components/home/PageBlock.astro +19 -19
- package/theme/components/home/Text.astro +13 -13
- package/theme/components/loadPopup.ts +22 -22
- package/theme/components/loadPosts.ts +16 -16
- package/theme/components/posts.ts +15 -15
- package/theme/editor/looks.mjs +7 -7
- package/theme/editor/previews.tsx +56 -56
- package/theme/layouts/Base.astro +273 -273
- package/theme/layouts/PageLayout.astro +126 -126
- package/theme/layouts/PostLayout.astro +137 -137
- package/theme/scripts/motion.ts +29 -29
- package/theme/styles/base.css +320 -320
- package/theme/styles/blocks.css +1808 -1808
- package/theme/styles/components.css +206 -206
- package/theme/styles/global.css +1302 -1302
- package/theme/styles/ui.css +6544 -6544
- package/theme/templates/Blog.astro +38 -38
- package/theme/templates/Category.astro +19 -19
- package/theme/templates/Home.astro +36 -36
- package/theme/templates/New.astro +68 -68
- package/theme/templates/NotFound.astro +117 -117
- package/theme/theme.json +28 -28
- package/theme/ui/Badge.tsx +45 -45
- package/theme/ui/BottomSheet.tsx +148 -148
- package/theme/ui/Button.tsx +79 -79
- package/theme/ui/ButtonGroup.tsx +31 -31
- package/theme/ui/Card.tsx +45 -45
- package/theme/ui/Carousel.tsx +124 -124
- package/theme/ui/Checkbox.tsx +59 -59
- package/theme/ui/Chip.tsx +101 -101
- package/theme/ui/ChipSet.tsx +27 -27
- package/theme/ui/DatePicker.tsx +149 -149
- package/theme/ui/DatePickerContainer.tsx +316 -316
- package/theme/ui/Dialog.tsx +100 -100
- package/theme/ui/Divider.tsx +27 -27
- package/theme/ui/ExposedDropdownMenu.tsx +77 -77
- package/theme/ui/Fab.tsx +88 -88
- package/theme/ui/FabMenu.tsx +67 -67
- package/theme/ui/IconButton.tsx +78 -78
- package/theme/ui/List.tsx +287 -287
- package/theme/ui/LoadingIndicator.tsx +29 -29
- package/theme/ui/Menu.tsx +278 -278
- package/theme/ui/NavigationBar.tsx +62 -62
- package/theme/ui/NavigationDrawer.tsx +80 -80
- package/theme/ui/NavigationRail.tsx +154 -154
- package/theme/ui/ProgressIndicator.tsx +162 -162
- package/theme/ui/RadioButton.tsx +50 -50
- package/theme/ui/Search.tsx +129 -129
- package/theme/ui/SegmentedButton.tsx +49 -49
- package/theme/ui/SideSheet.tsx +54 -54
- package/theme/ui/Slider.tsx +155 -155
- package/theme/ui/Snackbar.tsx +67 -67
- package/theme/ui/SplitButton.tsx +67 -67
- package/theme/ui/Switch.tsx +60 -60
- package/theme/ui/Tabs.tsx +114 -114
- package/theme/ui/TextField.tsx +278 -278
- package/theme/ui/TimePicker.tsx +128 -128
- package/theme/ui/TimePickerContainer.tsx +234 -234
- package/theme/ui/Toolbar.tsx +67 -67
- package/theme/ui/Tooltip.tsx +58 -58
- package/theme/ui/TopAppBar.tsx +77 -77
- package/theme/ui/bottomSheet.script.ts +8 -8
- package/theme/ui/{bottomSheet.ts → bottomSheetControl.ts} +211 -211
- package/theme/ui/carousel.script.ts +28 -28
- package/theme/ui/carouselLayout.ts +229 -229
- package/theme/ui/checkbox.script.ts +8 -8
- package/theme/ui/datePicker.script.ts +302 -302
- package/theme/ui/dates.ts +185 -185
- package/theme/ui/{dialog.ts → dialogControl.ts} +48 -48
- package/theme/ui/dropdownMenu.script.ts +166 -166
- package/theme/ui/fabMenu.script.ts +57 -57
- package/theme/ui/navigationRail.script.ts +47 -47
- package/theme/ui/ripple.script.ts +159 -159
- package/theme/ui/search.script.ts +92 -92
- package/theme/ui/{sideSheet.ts → sideSheetControl.ts} +48 -48
- package/theme/ui/slider.script.ts +12 -12
- package/theme/ui/sliderUpdate.ts +52 -52
- package/theme/ui/snackbar.script.ts +16 -16
- package/theme/ui/splitButton.script.ts +63 -63
- package/theme/ui/tabs.script.ts +48 -48
- package/theme/ui/textField.script.ts +17 -17
- package/theme/ui/textFieldUpdate.ts +44 -44
- package/theme/ui/timePicker.script.ts +248 -248
- package/theme/ui/toolbar.script.ts +43 -43
- package/theme/ui/tooltip.script.ts +68 -68
- package/worker/consent.js +141 -141
- package/worker/editor-github.js +167 -167
- package/worker/editor-host.js +74 -74
- package/worker/files.js +401 -401
- package/worker/github.js +36 -36
- package/worker/index.js +188 -184
- package/worker/not-found.js +97 -97
- package/worker/oauth.js +95 -95
- package/worker/publish.js +52 -52
- package/worker/push.js +270 -270
- package/worker/r2-status.js +50 -50
- package/worker/session.js +58 -58
- package/worker/store.js +46 -0
package/worker/editor-github.js
CHANGED
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
// What the editor asks of GitHub, through the Worker (packages/editor/src/github.ts): the browser never has the token
|
|
2
|
-
// (packages/editor/worker/session.js), so every request to GitHub goes from here with it. The site's files come through the cache in
|
|
3
|
-
// R2 (packages/editor/worker/files.js), and only to a sign-in GitHub says may read the repository (`access`, ten minutes).
|
|
4
|
-
// GET /api/github/head the branch's commit and root tree (always from GitHub)
|
|
5
|
-
// POST /api/github/files { tree?, folders?, depth?, paths? }: the files in folders (`depth` levels down) and at
|
|
6
|
-
// paths, at a root tree, or at the branch's head
|
|
7
|
-
// POST /api/github/trees { at: [{ root, path }] }: the folders at paths under root trees, with their entries
|
|
8
|
-
// POST /api/github/texts { oids }: files' texts by their blob ids (at most 100)
|
|
9
|
-
// GET /api/github/blob/<oid> a file's bytes; ?repo=<owner>/<name> from another repository (the store), not cached
|
|
10
|
-
// POST /api/github/commit { message, changes: [{ path, content (base64) | null, oid | null }] }: one commit,
|
|
11
|
-
// refused (409) when a file was changed or created elsewhere since the editor loaded it
|
|
12
|
-
// POST /api/github/graphql any other question to GitHub's GraphQL API (who is signed in, the history, the
|
|
13
|
-
// store): only questions, never a change
|
|
14
|
-
// POST /api/github/logout "Log ud": the cookie goes, and GitHub forgets the token
|
|
15
|
-
import { config } from '@site/config';
|
|
16
|
-
import { access, forget, noSession, sessionToken } from './session.js';
|
|
17
|
-
import { filesOf, GitHubError } from './files.js';
|
|
18
|
-
import { json } from './not-found.js';
|
|
19
|
-
|
|
20
|
-
const OID = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
|
|
21
|
-
const isOid = (s) => typeof s === 'string' && OID.test(s);
|
|
22
|
-
const isPath = (s) => typeof s === 'string' && s.length > 0 && s.length <= 1000 && !s.startsWith('/') && !s.split('/').includes('..');
|
|
23
|
-
|
|
24
|
-
export async function handleGitHub(request, env, ctx) {
|
|
25
|
-
const route = new URL(request.url).pathname.slice('/api/github/'.length);
|
|
26
|
-
const method = request.method;
|
|
27
|
-
if (route === 'logout' && method === 'POST') return logout(request, env, ctx);
|
|
28
|
-
const token = sessionToken(request);
|
|
29
|
-
if (!token) return json({ error: 'signed out' }, 401);
|
|
30
|
-
const files = filesOf(env, token, ctx);
|
|
31
|
-
try {
|
|
32
|
-
if (route === 'graphql' && method === 'POST') return await question(request, files);
|
|
33
|
-
// The site's own files: only to someone GitHub says may read the repository.
|
|
34
|
-
const may = await access(request, files.ask);
|
|
35
|
-
if (!may) return json({ error: 'signed out' }, 401);
|
|
36
|
-
if (!may.read) return json({ error: 'forbidden' }, 403);
|
|
37
|
-
if (route === 'head' && method === 'GET') return json(await files.head());
|
|
38
|
-
if (route.startsWith('blob/') && method === 'GET') return await blob(request, route.slice('blob/'.length), files);
|
|
39
|
-
if (method !== 'POST') return json({ error: 'method' }, 405);
|
|
40
|
-
const body = await request.json().catch(() => ({}));
|
|
41
|
-
if (route === 'files') return await listed(body, files);
|
|
42
|
-
if (route === 'trees') return await folders(body, files);
|
|
43
|
-
if (route === 'texts') return await texts(body, files);
|
|
44
|
-
if (route === 'commit') return await commit(body, files);
|
|
45
|
-
return json({ error: 'not found' }, 404);
|
|
46
|
-
} catch (e) {
|
|
47
|
-
if (e instanceof GitHubError) return json({ error: e.message || undefined, status: e.status }, e.status === 401 ? 401 : 502);
|
|
48
|
-
throw e;
|
|
49
|
-
} finally {
|
|
50
|
-
await files.finish(route.replace(/\/.*/, ''));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// A question to GitHub's GraphQL API as it is, and GitHub's answer. A change (a mutation) only goes through `commit`.
|
|
55
|
-
async function question(request, files) {
|
|
56
|
-
const body = await request.text();
|
|
57
|
-
let query;
|
|
58
|
-
try {
|
|
59
|
-
({ query } = JSON.parse(body));
|
|
60
|
-
} catch {}
|
|
61
|
-
if (typeof query !== 'string' || /\b(mutation|subscription)\b/.test(withoutStrings(query))) return json({ error: 'only questions' }, 400);
|
|
62
|
-
const res = await files.ask('https://api.github.com/graphql', { method: 'POST', headers: { 'content-type': 'application/json' }, body });
|
|
63
|
-
return new Response(res.body, { status: res.status, headers: { 'content-type': 'application/json', 'cache-control': 'no-store' } });
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// GraphQL without its strings and comments, where a file's name could say anything.
|
|
67
|
-
const withoutStrings = (query) => query.replace(/"""[\s\S]*?"""|"(?:\\.|[^"\\])*"|#[^\n]*/g, '""');
|
|
68
|
-
|
|
69
|
-
async function listed({ tree, folders = [], depth = 3, paths = [] }, files) {
|
|
70
|
-
if (
|
|
71
|
-
(tree !== undefined && !isOid(tree)) ||
|
|
72
|
-
!Array.isArray(folders) ||
|
|
73
|
-
!folders.every(isPath) ||
|
|
74
|
-
!Array.isArray(paths) ||
|
|
75
|
-
!paths.every(isPath) ||
|
|
76
|
-
!Number.isInteger(depth) ||
|
|
77
|
-
depth < 1 ||
|
|
78
|
-
depth > 20
|
|
79
|
-
)
|
|
80
|
-
return json({ error: 'request' }, 400);
|
|
81
|
-
const at = tree ? { tree } : await files.head();
|
|
82
|
-
const [lists, entries] = await Promise.all([files.filesIn(at.tree, folders, depth), files.entriesAt(at.tree, paths)]);
|
|
83
|
-
return json({
|
|
84
|
-
...at,
|
|
85
|
-
folders: lists,
|
|
86
|
-
// At a path: a file, or anything else git has there (a folder), whose text is never asked.
|
|
87
|
-
paths: entries.map((e, i) => e && { path: paths[i], oid: e.oid, binary: e.type !== 'blob' || e.binary === true }),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function folders({ at }, files) {
|
|
92
|
-
if (!Array.isArray(at) || at.length > 200 || !at.every((a) => isOid(a?.root) && (a.path === '' || isPath(a.path))))
|
|
93
|
-
return json({ error: 'request' }, 400);
|
|
94
|
-
const oids = await files.foldersAt(at);
|
|
95
|
-
const trees = await files.treesOf(oids.filter(Boolean));
|
|
96
|
-
return json({
|
|
97
|
-
trees: oids.map((oid) => {
|
|
98
|
-
const entries = oid && trees.get(oid);
|
|
99
|
-
return entries ? { oid, entries: entries.map((e) => ({ name: e.name, oid: e.oid, type: e.type, binary: e.binary === true })) } : null;
|
|
100
|
-
}),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function texts({ oids }, files) {
|
|
105
|
-
if (!Array.isArray(oids) || oids.length > 100 || !oids.every(isOid)) return json({ error: 'request' }, 400);
|
|
106
|
-
const found = await files.texts(oids);
|
|
107
|
-
return json({ texts: Object.fromEntries([...found].filter(([, text]) => text !== undefined)) });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function blob(request, oid, files) {
|
|
111
|
-
const repo = new URL(request.url).searchParams.get('repo')?.split('/');
|
|
112
|
-
if (!isOid(oid) || (repo && (repo.length !== 2 || !repo.every((p) => /^[\w.-]+$/.test(p))))) return json({ error: 'request' }, 400);
|
|
113
|
-
const bytes = await files.bytes(oid, repo ? { owner: repo[0], name: repo[1] } : config.repo);
|
|
114
|
-
if (!bytes) return json({ error: 'not found' }, 404);
|
|
115
|
-
// A blob id's bytes never change.
|
|
116
|
-
return new Response(bytes, {
|
|
117
|
-
headers: { 'content-type': 'application/octet-stream', 'cache-control': 'private, max-age=31536000, immutable' },
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function commit({ message, changes }, files) {
|
|
122
|
-
if (
|
|
123
|
-
typeof message !== 'string' ||
|
|
124
|
-
!message ||
|
|
125
|
-
!Array.isArray(changes) ||
|
|
126
|
-
!changes.length ||
|
|
127
|
-
!changes.every((c) => isPath(c?.path) && (c.content === null || typeof c.content === 'string') && (c.oid === null || isOid(c.oid)))
|
|
128
|
-
)
|
|
129
|
-
return json({ error: 'request' }, 400);
|
|
130
|
-
// The branch's commit, then the files in that same commit, so a push in between cannot slip past the check (the
|
|
131
|
-
// commit is refused unless the branch is still at this commit).
|
|
132
|
-
const at = await files.head();
|
|
133
|
-
const now = await files.entriesAt(
|
|
134
|
-
at.tree,
|
|
135
|
-
changes.map((c) => c.path),
|
|
136
|
-
);
|
|
137
|
-
const conflict = changes.findIndex((c, i) => (now[i]?.oid ?? null) !== c.oid);
|
|
138
|
-
if (conflict >= 0) return json({ conflict: changes[conflict].oid ? 'changed' : 'exists' }, 409);
|
|
139
|
-
return json(
|
|
140
|
-
await files.save(
|
|
141
|
-
at,
|
|
142
|
-
message,
|
|
143
|
-
changes.map((c) => ({ path: c.path, contents: c.content })),
|
|
144
|
-
),
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function logout(request, env, ctx) {
|
|
149
|
-
const token = sessionToken(request);
|
|
150
|
-
if (token) {
|
|
151
|
-
await forget(token);
|
|
152
|
-
// GitHub forgets the token too, so it is no good to anyone after "Log ud".
|
|
153
|
-
if (env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
|
|
154
|
-
ctx.waitUntil(
|
|
155
|
-
fetch(`https://api.github.com/applications/${env.GITHUB_CLIENT_ID}/token`, {
|
|
156
|
-
method: 'DELETE',
|
|
157
|
-
headers: {
|
|
158
|
-
authorization: `Basic ${btoa(`${env.GITHUB_CLIENT_ID}:${env.GITHUB_CLIENT_SECRET}`)}`,
|
|
159
|
-
accept: 'application/vnd.github+json',
|
|
160
|
-
'user-agent': `${config.repo.name}-worker`,
|
|
161
|
-
},
|
|
162
|
-
body: JSON.stringify({ access_token: token }),
|
|
163
|
-
}).catch(() => {}),
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
return new Response(null, { status: 204, headers: { 'set-cookie': noSession(), 'cache-control': 'no-store' } });
|
|
167
|
-
}
|
|
1
|
+
// What the editor asks of GitHub, through the Worker (packages/editor/src/github.ts): the browser never has the token
|
|
2
|
+
// (packages/editor/worker/session.js), so every request to GitHub goes from here with it. The site's files come through the cache in
|
|
3
|
+
// R2 (packages/editor/worker/files.js), and only to a sign-in GitHub says may read the repository (`access`, ten minutes).
|
|
4
|
+
// GET /api/github/head the branch's commit and root tree (always from GitHub)
|
|
5
|
+
// POST /api/github/files { tree?, folders?, depth?, paths? }: the files in folders (`depth` levels down) and at
|
|
6
|
+
// paths, at a root tree, or at the branch's head
|
|
7
|
+
// POST /api/github/trees { at: [{ root, path }] }: the folders at paths under root trees, with their entries
|
|
8
|
+
// POST /api/github/texts { oids }: files' texts by their blob ids (at most 100)
|
|
9
|
+
// GET /api/github/blob/<oid> a file's bytes; ?repo=<owner>/<name> from another repository (the store), not cached
|
|
10
|
+
// POST /api/github/commit { message, changes: [{ path, content (base64) | null, oid | null }] }: one commit,
|
|
11
|
+
// refused (409) when a file was changed or created elsewhere since the editor loaded it
|
|
12
|
+
// POST /api/github/graphql any other question to GitHub's GraphQL API (who is signed in, the history, the
|
|
13
|
+
// store): only questions, never a change
|
|
14
|
+
// POST /api/github/logout "Log ud": the cookie goes, and GitHub forgets the token
|
|
15
|
+
import { config } from '@site/config';
|
|
16
|
+
import { access, forget, noSession, sessionToken } from './session.js';
|
|
17
|
+
import { filesOf, GitHubError } from './files.js';
|
|
18
|
+
import { json } from './not-found.js';
|
|
19
|
+
|
|
20
|
+
const OID = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
|
|
21
|
+
const isOid = (s) => typeof s === 'string' && OID.test(s);
|
|
22
|
+
const isPath = (s) => typeof s === 'string' && s.length > 0 && s.length <= 1000 && !s.startsWith('/') && !s.split('/').includes('..');
|
|
23
|
+
|
|
24
|
+
export async function handleGitHub(request, env, ctx) {
|
|
25
|
+
const route = new URL(request.url).pathname.slice('/api/github/'.length);
|
|
26
|
+
const method = request.method;
|
|
27
|
+
if (route === 'logout' && method === 'POST') return logout(request, env, ctx);
|
|
28
|
+
const token = sessionToken(request);
|
|
29
|
+
if (!token) return json({ error: 'signed out' }, 401);
|
|
30
|
+
const files = filesOf(env, token, ctx);
|
|
31
|
+
try {
|
|
32
|
+
if (route === 'graphql' && method === 'POST') return await question(request, files);
|
|
33
|
+
// The site's own files: only to someone GitHub says may read the repository.
|
|
34
|
+
const may = await access(request, files.ask);
|
|
35
|
+
if (!may) return json({ error: 'signed out' }, 401);
|
|
36
|
+
if (!may.read) return json({ error: 'forbidden' }, 403);
|
|
37
|
+
if (route === 'head' && method === 'GET') return json(await files.head());
|
|
38
|
+
if (route.startsWith('blob/') && method === 'GET') return await blob(request, route.slice('blob/'.length), files);
|
|
39
|
+
if (method !== 'POST') return json({ error: 'method' }, 405);
|
|
40
|
+
const body = await request.json().catch(() => ({}));
|
|
41
|
+
if (route === 'files') return await listed(body, files);
|
|
42
|
+
if (route === 'trees') return await folders(body, files);
|
|
43
|
+
if (route === 'texts') return await texts(body, files);
|
|
44
|
+
if (route === 'commit') return await commit(body, files);
|
|
45
|
+
return json({ error: 'not found' }, 404);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e instanceof GitHubError) return json({ error: e.message || undefined, status: e.status }, e.status === 401 ? 401 : 502);
|
|
48
|
+
throw e;
|
|
49
|
+
} finally {
|
|
50
|
+
await files.finish(route.replace(/\/.*/, ''));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// A question to GitHub's GraphQL API as it is, and GitHub's answer. A change (a mutation) only goes through `commit`.
|
|
55
|
+
async function question(request, files) {
|
|
56
|
+
const body = await request.text();
|
|
57
|
+
let query;
|
|
58
|
+
try {
|
|
59
|
+
({ query } = JSON.parse(body));
|
|
60
|
+
} catch {}
|
|
61
|
+
if (typeof query !== 'string' || /\b(mutation|subscription)\b/.test(withoutStrings(query))) return json({ error: 'only questions' }, 400);
|
|
62
|
+
const res = await files.ask('https://api.github.com/graphql', { method: 'POST', headers: { 'content-type': 'application/json' }, body });
|
|
63
|
+
return new Response(res.body, { status: res.status, headers: { 'content-type': 'application/json', 'cache-control': 'no-store' } });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// GraphQL without its strings and comments, where a file's name could say anything.
|
|
67
|
+
const withoutStrings = (query) => query.replace(/"""[\s\S]*?"""|"(?:\\.|[^"\\])*"|#[^\n]*/g, '""');
|
|
68
|
+
|
|
69
|
+
async function listed({ tree, folders = [], depth = 3, paths = [] }, files) {
|
|
70
|
+
if (
|
|
71
|
+
(tree !== undefined && !isOid(tree)) ||
|
|
72
|
+
!Array.isArray(folders) ||
|
|
73
|
+
!folders.every(isPath) ||
|
|
74
|
+
!Array.isArray(paths) ||
|
|
75
|
+
!paths.every(isPath) ||
|
|
76
|
+
!Number.isInteger(depth) ||
|
|
77
|
+
depth < 1 ||
|
|
78
|
+
depth > 20
|
|
79
|
+
)
|
|
80
|
+
return json({ error: 'request' }, 400);
|
|
81
|
+
const at = tree ? { tree } : await files.head();
|
|
82
|
+
const [lists, entries] = await Promise.all([files.filesIn(at.tree, folders, depth), files.entriesAt(at.tree, paths)]);
|
|
83
|
+
return json({
|
|
84
|
+
...at,
|
|
85
|
+
folders: lists,
|
|
86
|
+
// At a path: a file, or anything else git has there (a folder), whose text is never asked.
|
|
87
|
+
paths: entries.map((e, i) => e && { path: paths[i], oid: e.oid, binary: e.type !== 'blob' || e.binary === true }),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function folders({ at }, files) {
|
|
92
|
+
if (!Array.isArray(at) || at.length > 200 || !at.every((a) => isOid(a?.root) && (a.path === '' || isPath(a.path))))
|
|
93
|
+
return json({ error: 'request' }, 400);
|
|
94
|
+
const oids = await files.foldersAt(at);
|
|
95
|
+
const trees = await files.treesOf(oids.filter(Boolean));
|
|
96
|
+
return json({
|
|
97
|
+
trees: oids.map((oid) => {
|
|
98
|
+
const entries = oid && trees.get(oid);
|
|
99
|
+
return entries ? { oid, entries: entries.map((e) => ({ name: e.name, oid: e.oid, type: e.type, binary: e.binary === true })) } : null;
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function texts({ oids }, files) {
|
|
105
|
+
if (!Array.isArray(oids) || oids.length > 100 || !oids.every(isOid)) return json({ error: 'request' }, 400);
|
|
106
|
+
const found = await files.texts(oids);
|
|
107
|
+
return json({ texts: Object.fromEntries([...found].filter(([, text]) => text !== undefined)) });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function blob(request, oid, files) {
|
|
111
|
+
const repo = new URL(request.url).searchParams.get('repo')?.split('/');
|
|
112
|
+
if (!isOid(oid) || (repo && (repo.length !== 2 || !repo.every((p) => /^[\w.-]+$/.test(p))))) return json({ error: 'request' }, 400);
|
|
113
|
+
const bytes = await files.bytes(oid, repo ? { owner: repo[0], name: repo[1] } : config.repo);
|
|
114
|
+
if (!bytes) return json({ error: 'not found' }, 404);
|
|
115
|
+
// A blob id's bytes never change.
|
|
116
|
+
return new Response(bytes, {
|
|
117
|
+
headers: { 'content-type': 'application/octet-stream', 'cache-control': 'private, max-age=31536000, immutable' },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function commit({ message, changes }, files) {
|
|
122
|
+
if (
|
|
123
|
+
typeof message !== 'string' ||
|
|
124
|
+
!message ||
|
|
125
|
+
!Array.isArray(changes) ||
|
|
126
|
+
!changes.length ||
|
|
127
|
+
!changes.every((c) => isPath(c?.path) && (c.content === null || typeof c.content === 'string') && (c.oid === null || isOid(c.oid)))
|
|
128
|
+
)
|
|
129
|
+
return json({ error: 'request' }, 400);
|
|
130
|
+
// The branch's commit, then the files in that same commit, so a push in between cannot slip past the check (the
|
|
131
|
+
// commit is refused unless the branch is still at this commit).
|
|
132
|
+
const at = await files.head();
|
|
133
|
+
const now = await files.entriesAt(
|
|
134
|
+
at.tree,
|
|
135
|
+
changes.map((c) => c.path),
|
|
136
|
+
);
|
|
137
|
+
const conflict = changes.findIndex((c, i) => (now[i]?.oid ?? null) !== c.oid);
|
|
138
|
+
if (conflict >= 0) return json({ conflict: changes[conflict].oid ? 'changed' : 'exists' }, 409);
|
|
139
|
+
return json(
|
|
140
|
+
await files.save(
|
|
141
|
+
at,
|
|
142
|
+
message,
|
|
143
|
+
changes.map((c) => ({ path: c.path, contents: c.content })),
|
|
144
|
+
),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function logout(request, env, ctx) {
|
|
149
|
+
const token = sessionToken(request);
|
|
150
|
+
if (token) {
|
|
151
|
+
await forget(token);
|
|
152
|
+
// GitHub forgets the token too, so it is no good to anyone after "Log ud".
|
|
153
|
+
if (env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
|
|
154
|
+
ctx.waitUntil(
|
|
155
|
+
fetch(`https://api.github.com/applications/${env.GITHUB_CLIENT_ID}/token`, {
|
|
156
|
+
method: 'DELETE',
|
|
157
|
+
headers: {
|
|
158
|
+
authorization: `Basic ${btoa(`${env.GITHUB_CLIENT_ID}:${env.GITHUB_CLIENT_SECRET}`)}`,
|
|
159
|
+
accept: 'application/vnd.github+json',
|
|
160
|
+
'user-agent': `${config.repo.name}-worker`,
|
|
161
|
+
},
|
|
162
|
+
body: JSON.stringify({ access_token: token }),
|
|
163
|
+
}).catch(() => {}),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return new Response(null, { status: 204, headers: { 'set-cookie': noSession(), 'cache-control': 'no-store' } });
|
|
167
|
+
}
|
package/worker/editor-host.js
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
// The editor on its own address (EDITOR_HOST, e.g. editor.example.dk), apart from the public
|
|
2
|
-
// site: the editor's GitHub sign-in is a cookie no script can read (packages/editor/worker/session.js), but a script on the
|
|
3
|
-
// same address (the site's third-party scripts, Google after consent, Calendly) could ask the Worker in the
|
|
4
|
-
// editor's name, and read what the editor keeps in the browser. See docs/editor.md.
|
|
5
|
-
// Without EDITOR_HOST (a variable on the Worker) the editor is at /editor/ on every address, as before.
|
|
6
|
-
// On EDITOR_HOST: the editor (/editor/), the sign-in (/oauth/), what the editor asks the Worker
|
|
7
|
-
// (/api/, packages/editor/worker/not-found.js) and files (scripts, styles, images)
|
|
8
|
-
// are served; the address alone opens the editor; any other page goes to the public site
|
|
9
|
-
// (PUBLIC_HOST, else EDITOR_HOST without "editor."), so no page of the site runs there.
|
|
10
|
-
// On any other address: the editor, the sign-in and /api/ go to EDITOR_HOST. The editor's page first
|
|
11
|
-
// removes what the editor kept on that address (the sign-in's cookie above all).
|
|
12
|
-
// A page the editor fetches as text for its preview ("Forhåndsvis", packages/editor/src/pagePreview.tsx,
|
|
13
|
-
// with the header X-Editor-Preview) is served on EDITOR_HOST too; the editor shows it without the
|
|
14
|
-
// site's scripts.
|
|
15
|
-
|
|
16
|
-
import { noSession } from './session.js';
|
|
17
|
-
|
|
18
|
-
// The site's pages and feeds: no file ending, or one of these.
|
|
19
|
-
const PAGE = /(^|\/)[^./]*$|\.(html?|xml)$/i;
|
|
20
|
-
|
|
21
|
-
/** A response for a request that belongs on the other address, or undefined to serve it here. */
|
|
22
|
-
export function editorHost(url, env, request) {
|
|
23
|
-
const editor = env.EDITOR_HOST?.trim().toLowerCase();
|
|
24
|
-
if (!editor) return undefined;
|
|
25
|
-
const own =
|
|
26
|
-
url.pathname.startsWith('/editor/') ||
|
|
27
|
-
url.pathname === '/editor' ||
|
|
28
|
-
url.pathname.startsWith('/oauth/') ||
|
|
29
|
-
url.pathname.startsWith('/api/');
|
|
30
|
-
|
|
31
|
-
if (url.hostname === editor) {
|
|
32
|
-
if (url.pathname === '/' && !url.search) return redirect(`https://${editor}/editor/`, 302);
|
|
33
|
-
if (own || !PAGE.test(url.pathname) || request?.headers.get('x-editor-preview')) return undefined;
|
|
34
|
-
const site = env.PUBLIC_HOST?.trim() || editor.replace(/^editor\./, '');
|
|
35
|
-
return redirect(`https://${site}${url.pathname}${url.search}`, 302);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!own) return undefined;
|
|
39
|
-
const to = `https://${editor}${url.pathname}${url.search}`;
|
|
40
|
-
if (url.pathname === '/editor/' || url.pathname === '/editor') return cleanUp(to);
|
|
41
|
-
return redirect(to, 301);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const redirect = (location, status) => new Response(null, { status, headers: { location, 'cache-control': 'no-store' } });
|
|
45
|
-
|
|
46
|
-
// The editor's page on the old address: removes the sign-in (its cookie), the editor's settings and its copy of the
|
|
47
|
-
// files (IndexedDB "editor") and its service worker there, then opens the editor on its own address,
|
|
48
|
-
// with what came after "#" (the open screen).
|
|
49
|
-
function cleanUp(to) {
|
|
50
|
-
const js = JSON.stringify(to).replace(/</g, '\\u003c');
|
|
51
|
-
const html = `<!doctype html><html lang="da"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="robots" content="noindex"><title>Editoren er flyttet</title></head>
|
|
52
|
-
<body style="font-family: system-ui, sans-serif; padding: 2rem"><p>Editoren er flyttet til <a href=${JSON.stringify(to)}>${to.replace(/^https:\/\//, '').replace(/[?#].*$/, '')}</a> …</p><script>
|
|
53
|
-
(async () => {
|
|
54
|
-
try {
|
|
55
|
-
for (const key of Object.keys(localStorage)) if (key.startsWith('editor-')) localStorage.removeItem(key);
|
|
56
|
-
} catch {}
|
|
57
|
-
try {
|
|
58
|
-
indexedDB.deleteDatabase('editor');
|
|
59
|
-
} catch {}
|
|
60
|
-
try {
|
|
61
|
-
for (const r of (await navigator.serviceWorker?.getRegistrations()) ?? []) if (new URL(r.scope).pathname.startsWith('/editor/')) await r.unregister();
|
|
62
|
-
} catch {}
|
|
63
|
-
location.replace(${js} + location.hash);
|
|
64
|
-
})();
|
|
65
|
-
</script></body></html>`;
|
|
66
|
-
return new Response(html, {
|
|
67
|
-
headers: {
|
|
68
|
-
'content-type': 'text/html; charset=utf-8',
|
|
69
|
-
'cache-control': 'no-store',
|
|
70
|
-
'x-robots-tag': 'noindex',
|
|
71
|
-
'set-cookie': noSession(),
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
}
|
|
1
|
+
// The editor on its own address (EDITOR_HOST, e.g. editor.example.dk), apart from the public
|
|
2
|
+
// site: the editor's GitHub sign-in is a cookie no script can read (packages/editor/worker/session.js), but a script on the
|
|
3
|
+
// same address (the site's third-party scripts, Google after consent, Calendly) could ask the Worker in the
|
|
4
|
+
// editor's name, and read what the editor keeps in the browser. See docs/editor.md.
|
|
5
|
+
// Without EDITOR_HOST (a variable on the Worker) the editor is at /editor/ on every address, as before.
|
|
6
|
+
// On EDITOR_HOST: the editor (/editor/), the sign-in (/oauth/), what the editor asks the Worker
|
|
7
|
+
// (/api/, packages/editor/worker/not-found.js) and files (scripts, styles, images)
|
|
8
|
+
// are served; the address alone opens the editor; any other page goes to the public site
|
|
9
|
+
// (PUBLIC_HOST, else EDITOR_HOST without "editor."), so no page of the site runs there.
|
|
10
|
+
// On any other address: the editor, the sign-in and /api/ go to EDITOR_HOST. The editor's page first
|
|
11
|
+
// removes what the editor kept on that address (the sign-in's cookie above all).
|
|
12
|
+
// A page the editor fetches as text for its preview ("Forhåndsvis", packages/editor/src/pagePreview.tsx,
|
|
13
|
+
// with the header X-Editor-Preview) is served on EDITOR_HOST too; the editor shows it without the
|
|
14
|
+
// site's scripts.
|
|
15
|
+
|
|
16
|
+
import { noSession } from './session.js';
|
|
17
|
+
|
|
18
|
+
// The site's pages and feeds: no file ending, or one of these.
|
|
19
|
+
const PAGE = /(^|\/)[^./]*$|\.(html?|xml)$/i;
|
|
20
|
+
|
|
21
|
+
/** A response for a request that belongs on the other address, or undefined to serve it here. */
|
|
22
|
+
export function editorHost(url, env, request) {
|
|
23
|
+
const editor = env.EDITOR_HOST?.trim().toLowerCase();
|
|
24
|
+
if (!editor) return undefined;
|
|
25
|
+
const own =
|
|
26
|
+
url.pathname.startsWith('/editor/') ||
|
|
27
|
+
url.pathname === '/editor' ||
|
|
28
|
+
url.pathname.startsWith('/oauth/') ||
|
|
29
|
+
url.pathname.startsWith('/api/');
|
|
30
|
+
|
|
31
|
+
if (url.hostname === editor) {
|
|
32
|
+
if (url.pathname === '/' && !url.search) return redirect(`https://${editor}/editor/`, 302);
|
|
33
|
+
if (own || !PAGE.test(url.pathname) || request?.headers.get('x-editor-preview')) return undefined;
|
|
34
|
+
const site = env.PUBLIC_HOST?.trim() || editor.replace(/^editor\./, '');
|
|
35
|
+
return redirect(`https://${site}${url.pathname}${url.search}`, 302);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!own) return undefined;
|
|
39
|
+
const to = `https://${editor}${url.pathname}${url.search}`;
|
|
40
|
+
if (url.pathname === '/editor/' || url.pathname === '/editor') return cleanUp(to);
|
|
41
|
+
return redirect(to, 301);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const redirect = (location, status) => new Response(null, { status, headers: { location, 'cache-control': 'no-store' } });
|
|
45
|
+
|
|
46
|
+
// The editor's page on the old address: removes the sign-in (its cookie), the editor's settings and its copy of the
|
|
47
|
+
// files (IndexedDB "editor") and its service worker there, then opens the editor on its own address,
|
|
48
|
+
// with what came after "#" (the open screen).
|
|
49
|
+
function cleanUp(to) {
|
|
50
|
+
const js = JSON.stringify(to).replace(/</g, '\\u003c');
|
|
51
|
+
const html = `<!doctype html><html lang="da"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="robots" content="noindex"><title>Editoren er flyttet</title></head>
|
|
52
|
+
<body style="font-family: system-ui, sans-serif; padding: 2rem"><p>Editoren er flyttet til <a href=${JSON.stringify(to)}>${to.replace(/^https:\/\//, '').replace(/[?#].*$/, '')}</a> …</p><script>
|
|
53
|
+
(async () => {
|
|
54
|
+
try {
|
|
55
|
+
for (const key of Object.keys(localStorage)) if (key.startsWith('editor-')) localStorage.removeItem(key);
|
|
56
|
+
} catch {}
|
|
57
|
+
try {
|
|
58
|
+
indexedDB.deleteDatabase('editor');
|
|
59
|
+
} catch {}
|
|
60
|
+
try {
|
|
61
|
+
for (const r of (await navigator.serviceWorker?.getRegistrations()) ?? []) if (new URL(r.scope).pathname.startsWith('/editor/')) await r.unregister();
|
|
62
|
+
} catch {}
|
|
63
|
+
location.replace(${js} + location.hash);
|
|
64
|
+
})();
|
|
65
|
+
</script></body></html>`;
|
|
66
|
+
return new Response(html, {
|
|
67
|
+
headers: {
|
|
68
|
+
'content-type': 'text/html; charset=utf-8',
|
|
69
|
+
'cache-control': 'no-store',
|
|
70
|
+
'x-robots-tag': 'noindex',
|
|
71
|
+
'set-cookie': noSession(),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|