@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/scripts/built-site.mjs
CHANGED
|
@@ -1,99 +1,134 @@
|
|
|
1
|
-
// The built site served on this computer, with what the editor asks of GitHub (through the site's Worker) answered from
|
|
2
|
-
// the files in this repository, for the scripts that open the editor in a
|
|
3
|
-
// app-screenshots.mjs): no sign-in, and nothing changes.
|
|
4
|
-
import { createServer } from 'node:http';
|
|
5
|
-
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
6
|
-
import { extname, join, posix } from 'node:path';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'.
|
|
14
|
-
'.
|
|
15
|
-
'.
|
|
16
|
-
'.
|
|
17
|
-
'.
|
|
18
|
-
'.
|
|
19
|
-
'.
|
|
20
|
-
'.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
else
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (route === '
|
|
67
|
-
if (route === '
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (route === '
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
1
|
+
// The built site served on this computer, with what the editor asks of GitHub (through the site's Worker) answered from
|
|
2
|
+
// the files in this repository, and the app store from a made-up catalog, for the scripts that open the editor in a
|
|
3
|
+
// browser (editor-screens.mjs, app-screenshots.mjs): no sign-in, and nothing changes.
|
|
4
|
+
import { createServer } from 'node:http';
|
|
5
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
6
|
+
import { extname, join, posix } from 'node:path';
|
|
7
|
+
import { APP_API, THEME_API } from '../lib/contracts.mjs';
|
|
8
|
+
|
|
9
|
+
const ROOT = `${process.cwd()}/`;
|
|
10
|
+
|
|
11
|
+
// The built site, as the host serves it: a folder is its index.html.
|
|
12
|
+
const types = {
|
|
13
|
+
'.html': 'text/html',
|
|
14
|
+
'.js': 'text/javascript',
|
|
15
|
+
'.css': 'text/css',
|
|
16
|
+
'.json': 'application/json',
|
|
17
|
+
'.svg': 'image/svg+xml',
|
|
18
|
+
'.jpg': 'image/jpeg',
|
|
19
|
+
'.png': 'image/png',
|
|
20
|
+
'.webp': 'image/webp',
|
|
21
|
+
'.webmanifest': 'application/manifest+json',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Serves the built site in `dist`; gives the server and its address. */
|
|
25
|
+
export async function serveBuilt(dist) {
|
|
26
|
+
const server = createServer((req, res) => {
|
|
27
|
+
let path = join(dist, decodeURIComponent(new URL(req.url, 'http://x').pathname));
|
|
28
|
+
if (existsSync(path) && statSync(path).isDirectory()) path = join(path, 'index.html');
|
|
29
|
+
if (!existsSync(path)) return res.writeHead(404).end();
|
|
30
|
+
res.writeHead(200, { 'content-type': types[extname(path)] ?? 'application/octet-stream' }).end(readFileSync(path));
|
|
31
|
+
});
|
|
32
|
+
await new Promise((done) => server.listen(0, '127.0.0.1', done));
|
|
33
|
+
return { server, site: `http://127.0.0.1:${server.address().port}` };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// GitHub, as the site's Worker answers the editor (packages/editor/worker/editor-github.js), answered from the files on disk: who is
|
|
37
|
+
// signed in (with write access), the commit the branch is at, the files in the folders and at the paths asked for, and
|
|
38
|
+
// their texts (a file's blob id is "blob:<path>"). A question about anything else (the versions of a file, what was
|
|
39
|
+
// deleted) gets no data, as for a file without history. `texts`: files with other texts than on disk, by their paths.
|
|
40
|
+
const TEXT = /\.(md|mdx|jsonc?|ya?ml|txt|ts|js|mjs|css|astro|svg)$/;
|
|
41
|
+
const listed = (path, texts) => ({ path, oid: `blob:${path}`, binary: !(path in texts) && !TEXT.test(path) });
|
|
42
|
+
function filesIn(dir, depth, texts) {
|
|
43
|
+
if (!existsSync(join(ROOT, dir)) || !statSync(join(ROOT, dir)).isDirectory()) return [];
|
|
44
|
+
const files = readdirSync(join(ROOT, dir), { withFileTypes: true }).flatMap((d) => {
|
|
45
|
+
const path = posix.join(dir, d.name);
|
|
46
|
+
if (d.isDirectory()) return depth > 1 ? filesIn(path, depth - 1, texts) : [];
|
|
47
|
+
return [listed(path, texts)];
|
|
48
|
+
});
|
|
49
|
+
// The files given that are not on disk, in their folder.
|
|
50
|
+
const more = Object.keys(texts)
|
|
51
|
+
.filter((path) => posix.dirname(path) === dir && !files.some((f) => f.path === path))
|
|
52
|
+
.map((path) => listed(path, texts));
|
|
53
|
+
return [...files, ...more];
|
|
54
|
+
}
|
|
55
|
+
function answer(query, texts) {
|
|
56
|
+
const repository = { ref: { target: { oid: 'head' } }, viewerPermission: 'ADMIN' };
|
|
57
|
+
for (const [, key, path] of query.matchAll(/(f\d+): object\(expression: "[^:"]+:([^"]+)"\)/g)) {
|
|
58
|
+
const full = join(ROOT, path);
|
|
59
|
+
if (path in texts) repository[key] = { oid: `blob:${path}`, text: texts[path] };
|
|
60
|
+
else if (!existsSync(full) || statSync(full).isDirectory()) repository[key] = null;
|
|
61
|
+
else repository[key] = { oid: `blob:${path}`, text: TEXT.test(path) ? readFileSync(full, 'utf8') : null };
|
|
62
|
+
}
|
|
63
|
+
return { data: { viewer: { login: 'editor-screens', name: 'Anna Jensen' }, repository } };
|
|
64
|
+
}
|
|
65
|
+
function workerAnswer(route, body, texts) {
|
|
66
|
+
if (route === 'graphql') return answer(body.query ?? '', texts);
|
|
67
|
+
if (route === 'head') return { commit: 'head', tree: 'root' };
|
|
68
|
+
if (route === 'files')
|
|
69
|
+
return {
|
|
70
|
+
commit: 'head',
|
|
71
|
+
tree: 'root',
|
|
72
|
+
folders: (body.folders ?? []).map((dir) => filesIn(dir, body.depth ?? 3, texts)),
|
|
73
|
+
paths: (body.paths ?? []).map((path) =>
|
|
74
|
+
path in texts || (existsSync(join(ROOT, path)) && statSync(join(ROOT, path)).isFile()) ? listed(path, texts) : null,
|
|
75
|
+
),
|
|
76
|
+
};
|
|
77
|
+
if (route === 'trees') return { trees: (body.at ?? []).map(() => null) };
|
|
78
|
+
if (route === 'texts') {
|
|
79
|
+
const paths = (body.oids ?? []).map((oid) => oid.replace(/^blob:/, ''));
|
|
80
|
+
return { texts: Object.fromEntries(paths.map((path) => [`blob:${path}`, texts[path] ?? readFileSync(join(ROOT, path), 'utf8')])) };
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The app store, as the site's Worker passes it on (packages/editor/worker/store.js), made up: one app and one theme in
|
|
86
|
+
// the catalog's shape, and no file by any id.
|
|
87
|
+
const STORE_CATALOG = {
|
|
88
|
+
apps: [
|
|
89
|
+
{
|
|
90
|
+
id: 'sample-app',
|
|
91
|
+
version: '1.0.0',
|
|
92
|
+
api: APP_API,
|
|
93
|
+
label: 'Sample app',
|
|
94
|
+
publisher: 'Example Studio',
|
|
95
|
+
icon: 'star',
|
|
96
|
+
hint: 'A made-up app for the checks.',
|
|
97
|
+
adds: { blocks: [], lists: [], kinds: [] },
|
|
98
|
+
files: [{ path: 'index.ts', oid: '0'.repeat(40) }],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
themes: [
|
|
102
|
+
{
|
|
103
|
+
id: 'sample-theme',
|
|
104
|
+
version: '1.0.0',
|
|
105
|
+
api: THEME_API,
|
|
106
|
+
name: 'Sample theme',
|
|
107
|
+
author: 'Example Studio',
|
|
108
|
+
icon: 'palette',
|
|
109
|
+
files: [{ path: 'theme.json', oid: '1'.repeat(40) }],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** Opens the editor (/editor/) of the site at `site` in `page`, signed in, with GitHub answered from the files on disk
|
|
115
|
+
* (`texts`: some with other texts), and waits until it has loaded the site. */
|
|
116
|
+
export async function openEditor(page, site, texts = {}) {
|
|
117
|
+
// R2 caching under "Avanceret": Cloudflare has the bucket (packages/editor/worker/r2-status.js).
|
|
118
|
+
await page.route(`${site}/api/r2?**`, (r) =>
|
|
119
|
+
r.fulfill({ contentType: 'application/json', body: JSON.stringify({ bound: false, bucket: 'ready' }) }),
|
|
120
|
+
);
|
|
121
|
+
await page.route(`${site}/api/store/**`, (r) =>
|
|
122
|
+
new URL(r.request().url()).pathname === '/api/store/catalog'
|
|
123
|
+
? r.fulfill({ contentType: 'application/json', body: JSON.stringify(STORE_CATALOG) })
|
|
124
|
+
: r.fulfill({ status: 404 }),
|
|
125
|
+
);
|
|
126
|
+
await page.route(`${site}/api/github/**`, (r) => {
|
|
127
|
+
const route = new URL(r.request().url()).pathname.slice('/api/github/'.length);
|
|
128
|
+
const found = workerAnswer(route, JSON.parse(r.request().postData() || '{}'), texts);
|
|
129
|
+
return found ? r.fulfill({ contentType: 'application/json', body: JSON.stringify(found) }) : r.fulfill({ status: 404 });
|
|
130
|
+
});
|
|
131
|
+
await page.goto(`${site}/editor/`);
|
|
132
|
+
// The editor has loaded the site when the start screen has its links.
|
|
133
|
+
await page.waitForFunction(() => document.querySelectorAll('a[href^="#"]').length > 3, null, { timeout: 60_000 });
|
|
134
|
+
}
|
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
// Checks the consent log (packages/editor/worker/consent.js): the Worker's own code with D1 played by Node's SQLite (node:sqlite) and
|
|
2
|
-
// GitHub's answer about a sign-in played here. An answer from the site's own page is kept with the build's commit;
|
|
3
|
-
// one from another site, with a wrong consent id or with a category the banner does not have is not; only someone
|
|
4
|
-
// who can save the site reads, saves as CSV or deletes them; the hourly clean-up deletes those older than two years,
|
|
5
|
-
// and without the binding nothing is kept. Nothing goes to GitHub or Cloudflare. Run: npm run check:worker
|
|
6
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
7
|
-
import { workerModules } from './worker-modules.mjs';
|
|
8
|
-
|
|
9
|
-
const [{ forgetOldConsents, handleConsent, KEEP_DAYS }] = await workerModules('consent.js');
|
|
10
|
-
|
|
11
|
-
const problems = [];
|
|
12
|
-
const expect = (ok, what) => ok || problems.push(what);
|
|
13
|
-
|
|
14
|
-
/** D1, as much of it as the Worker uses, on an SQLite database in memory. */
|
|
15
|
-
function d1() {
|
|
16
|
-
const db = new DatabaseSync(':memory:');
|
|
17
|
-
const statement = (sql, args = []) => ({
|
|
18
|
-
bind: (...values) => statement(sql, values),
|
|
19
|
-
run: async () => ({ meta: { changes: Number(db.prepare(sql).run(...args).changes) } }),
|
|
20
|
-
all: async () => ({ results: db.prepare(sql).all(...args) }),
|
|
21
|
-
first: async (column) => db.prepare(sql).get(...args)?.[column] ?? null,
|
|
22
|
-
});
|
|
23
|
-
return { db, prepare: (sql) => statement(sql), batch: async (list) => Promise.all(list.map((s) => s.run())) };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// GitHub: the token "editor" may save the site, "guest" may only read it.
|
|
27
|
-
globalThis.fetch = async (url, init) => {
|
|
28
|
-
const token = init?.headers?.authorization?.replace('Bearer ', '');
|
|
29
|
-
if (!String(url).startsWith('https://api.github.com/')) return new Response(null, { status: 404 });
|
|
30
|
-
if (token === 'editor') return Response.json({ permissions: { pull: true, push: true } });
|
|
31
|
-
if (token === 'guest') return Response.json({ permissions: { pull: true, push: false } });
|
|
32
|
-
return new Response(null, { status: 401 });
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const SITE = 'https://site.example';
|
|
36
|
-
const COMMIT = 'abc123';
|
|
37
|
-
const ASSETS = { fetch: async () => Response.json({ commit: COMMIT }) };
|
|
38
|
-
const ID = '0f8fad5b-d9cb-469f-a165-70867728950e';
|
|
39
|
-
const OTHER = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
40
|
-
|
|
41
|
-
const visitor = (body, origin = SITE) =>
|
|
42
|
-
new Request(`${SITE}/api/consent`, { method: 'POST', headers: { origin, 'content-type': 'text/plain' }, body: JSON.stringify(body) });
|
|
43
|
-
const editor = (token, path = '', init = {}) =>
|
|
44
|
-
new Request(`${SITE}/api/consent${path}`, {
|
|
45
|
-
...init,
|
|
46
|
-
headers: { cookie: `editor_session=${token}`, 'sec-fetch-site': 'same-origin', ...init.headers },
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const store = d1();
|
|
50
|
-
const env = { CONSENT: store, ASSETS };
|
|
51
|
-
|
|
52
|
-
// What a visitor sends.
|
|
53
|
-
const sent = async (body, origin) => (await handleConsent(visitor(body, origin), env)).status;
|
|
54
|
-
expect((await sent({ id: ID, choices: { statistics: true, marketing: false } })) === 204, 'et svar fra sitet blev ikke gemt');
|
|
55
|
-
expect((await sent({ id: OTHER, choices: { statistics: false } })) === 204, 'et andet svar fra sitet blev ikke gemt');
|
|
56
|
-
expect(
|
|
57
|
-
(await sent({ id: ID, choices: { statistics: true } }, 'https://other.example')) === 403,
|
|
58
|
-
'et svar fra en anden side blev taget imod',
|
|
59
|
-
);
|
|
60
|
-
expect((await sent({ id: 'not-an-id', choices: { statistics: true } })) === 400, 'et svar med et forkert id blev taget imod');
|
|
61
|
-
expect((await sent({ id: ID, choices: { necessary: true } })) === 400, 'et svar om nødvendige cookies blev taget imod');
|
|
62
|
-
expect((await sent({ id: ID, choices: { statistics: 'yes' } })) === 400, 'et svar, der ikke er ja eller nej, blev taget imod');
|
|
63
|
-
expect((await sent({ id: ID, choices: {} })) === 400, 'et svar uden kategorier blev taget imod');
|
|
64
|
-
const kept = store.db.prepare('SELECT * FROM consents ORDER BY row').all();
|
|
65
|
-
expect(kept.length === 2, `der blev gemt ${kept.length} svar, ikke 2`);
|
|
66
|
-
expect(kept[0]?.version === COMMIT, `svaret blev gemt med versionen ${kept[0]?.version}, ikke ${COMMIT}`);
|
|
67
|
-
expect(
|
|
68
|
-
!JSON.stringify(kept).includes('origin') && Object.keys(kept[0] ?? {}).join() === 'row,id,at,choices,version',
|
|
69
|
-
'der blev gemt mere end svaret',
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// Who reads them.
|
|
73
|
-
expect((await handleConsent(editor('guest'), env)).status === 403, 'en, der ikke kan gemme sitet, kunne læse samtykkerne');
|
|
74
|
-
expect((await handleConsent(editor('nobody'), env)).status === 403, 'en uden login kunne læse samtykkerne');
|
|
75
|
-
const listed = await (await handleConsent(editor('editor'), env)).json();
|
|
76
|
-
expect(listed.total === 2 && listed.entries.length === 2, `listen havde ${listed.entries?.length} af ${listed.total} svar, ikke 2`);
|
|
77
|
-
expect(listed.entries.find((e) => e.id === ID)?.choices.statistics === true, 'listen gav ikke svaret, som det blev givet');
|
|
78
|
-
const found = await (await handleConsent(editor('editor', `?q=${ID.slice(0, 8)}`), env)).json();
|
|
79
|
-
expect(found.entries.length === 1 && found.entries[0].id === ID, 'søgningen på et id fandt ikke kun det id');
|
|
80
|
-
|
|
81
|
-
// As a file.
|
|
82
|
-
const file = await handleConsent(editor('editor', '?format=csv'), env);
|
|
83
|
-
const lines = (await file.text()).trim().split('\r\n');
|
|
84
|
-
expect(file.headers.get('content-disposition')?.startsWith('attachment'), 'CSV-filen hentes ikke som en fil');
|
|
85
|
-
expect(lines[0] === '"id","at","statistics","marketing","version"', `CSV-filens første linje er ${lines[0]}`);
|
|
86
|
-
expect(lines.length === 3, `CSV-filen har ${lines.length - 1} svar, ikke 2`);
|
|
87
|
-
|
|
88
|
-
// Deleted, when a visitor asks.
|
|
89
|
-
const removed = await handleConsent(editor('editor', '', { method: 'DELETE', body: JSON.stringify({ id: OTHER }) }), env);
|
|
90
|
-
expect((await removed.json()).deleted === 1, 'svaret blev ikke slettet');
|
|
91
|
-
expect(
|
|
92
|
-
(await handleConsent(editor('guest', '', { method: 'DELETE', body: JSON.stringify({ id: ID }) }), env)).status === 403,
|
|
93
|
-
'en, der ikke kan gemme sitet, kunne slette et svar',
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
// Those older than two years are deleted each hour; the others stay.
|
|
97
|
-
const old = new Date(Date.now() - (KEEP_DAYS + 1) * 24 * 60 * 60 * 1000).toISOString();
|
|
98
|
-
store.db.prepare('INSERT INTO consents (id, at, choices, version) VALUES (?, ?, ?, ?)').run(OTHER, old, '{"statistics":true}', COMMIT);
|
|
99
|
-
await forgetOldConsents(env);
|
|
100
|
-
const left = store.db.prepare('SELECT id FROM consents').all();
|
|
101
|
-
expect(left.length === 1 && left[0].id === ID, `efter oprydningen er der ${left.length} svar, ikke kun det nye`);
|
|
102
|
-
|
|
103
|
-
// Without the binding (a preview of a branch) nothing is kept, and the editor is told so.
|
|
104
|
-
const none = { ASSETS };
|
|
105
|
-
expect((await handleConsent(visitor({ id: ID, choices: { statistics: true } }), none)).status === 204, 'et svar uden D1 gav en fejl');
|
|
106
|
-
expect((await handleConsent(editor('editor'), none)).status === 503, 'editoren fik ikke at vide, at der ikke gemmes samtykker');
|
|
107
|
-
await forgetOldConsents(none);
|
|
108
|
-
|
|
109
|
-
if (problems.length) {
|
|
110
|
-
console.error(`${problems.length} fejl i samtykkeloggen:\n${problems.join('\n')}`);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
console.log('Samtykkeloggen virker: svarene gemmes med sitets version, kun editoren kan læse, hente og slette dem, og gamle slettes.');
|
|
1
|
+
// Checks the consent log (packages/editor/worker/consent.js): the Worker's own code with D1 played by Node's SQLite (node:sqlite) and
|
|
2
|
+
// GitHub's answer about a sign-in played here. An answer from the site's own page is kept with the build's commit;
|
|
3
|
+
// one from another site, with a wrong consent id or with a category the banner does not have is not; only someone
|
|
4
|
+
// who can save the site reads, saves as CSV or deletes them; the hourly clean-up deletes those older than two years,
|
|
5
|
+
// and without the binding nothing is kept. Nothing goes to GitHub or Cloudflare. Run: npm run check:worker
|
|
6
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
7
|
+
import { workerModules } from './worker-modules.mjs';
|
|
8
|
+
|
|
9
|
+
const [{ forgetOldConsents, handleConsent, KEEP_DAYS }] = await workerModules('consent.js');
|
|
10
|
+
|
|
11
|
+
const problems = [];
|
|
12
|
+
const expect = (ok, what) => ok || problems.push(what);
|
|
13
|
+
|
|
14
|
+
/** D1, as much of it as the Worker uses, on an SQLite database in memory. */
|
|
15
|
+
function d1() {
|
|
16
|
+
const db = new DatabaseSync(':memory:');
|
|
17
|
+
const statement = (sql, args = []) => ({
|
|
18
|
+
bind: (...values) => statement(sql, values),
|
|
19
|
+
run: async () => ({ meta: { changes: Number(db.prepare(sql).run(...args).changes) } }),
|
|
20
|
+
all: async () => ({ results: db.prepare(sql).all(...args) }),
|
|
21
|
+
first: async (column) => db.prepare(sql).get(...args)?.[column] ?? null,
|
|
22
|
+
});
|
|
23
|
+
return { db, prepare: (sql) => statement(sql), batch: async (list) => Promise.all(list.map((s) => s.run())) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// GitHub: the token "editor" may save the site, "guest" may only read it.
|
|
27
|
+
globalThis.fetch = async (url, init) => {
|
|
28
|
+
const token = init?.headers?.authorization?.replace('Bearer ', '');
|
|
29
|
+
if (!String(url).startsWith('https://api.github.com/')) return new Response(null, { status: 404 });
|
|
30
|
+
if (token === 'editor') return Response.json({ permissions: { pull: true, push: true } });
|
|
31
|
+
if (token === 'guest') return Response.json({ permissions: { pull: true, push: false } });
|
|
32
|
+
return new Response(null, { status: 401 });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const SITE = 'https://site.example';
|
|
36
|
+
const COMMIT = 'abc123';
|
|
37
|
+
const ASSETS = { fetch: async () => Response.json({ commit: COMMIT }) };
|
|
38
|
+
const ID = '0f8fad5b-d9cb-469f-a165-70867728950e';
|
|
39
|
+
const OTHER = '7c9e6679-7425-40de-944b-e07fc1f90ae7';
|
|
40
|
+
|
|
41
|
+
const visitor = (body, origin = SITE) =>
|
|
42
|
+
new Request(`${SITE}/api/consent`, { method: 'POST', headers: { origin, 'content-type': 'text/plain' }, body: JSON.stringify(body) });
|
|
43
|
+
const editor = (token, path = '', init = {}) =>
|
|
44
|
+
new Request(`${SITE}/api/consent${path}`, {
|
|
45
|
+
...init,
|
|
46
|
+
headers: { cookie: `editor_session=${token}`, 'sec-fetch-site': 'same-origin', ...init.headers },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const store = d1();
|
|
50
|
+
const env = { CONSENT: store, ASSETS };
|
|
51
|
+
|
|
52
|
+
// What a visitor sends.
|
|
53
|
+
const sent = async (body, origin) => (await handleConsent(visitor(body, origin), env)).status;
|
|
54
|
+
expect((await sent({ id: ID, choices: { statistics: true, marketing: false } })) === 204, 'et svar fra sitet blev ikke gemt');
|
|
55
|
+
expect((await sent({ id: OTHER, choices: { statistics: false } })) === 204, 'et andet svar fra sitet blev ikke gemt');
|
|
56
|
+
expect(
|
|
57
|
+
(await sent({ id: ID, choices: { statistics: true } }, 'https://other.example')) === 403,
|
|
58
|
+
'et svar fra en anden side blev taget imod',
|
|
59
|
+
);
|
|
60
|
+
expect((await sent({ id: 'not-an-id', choices: { statistics: true } })) === 400, 'et svar med et forkert id blev taget imod');
|
|
61
|
+
expect((await sent({ id: ID, choices: { necessary: true } })) === 400, 'et svar om nødvendige cookies blev taget imod');
|
|
62
|
+
expect((await sent({ id: ID, choices: { statistics: 'yes' } })) === 400, 'et svar, der ikke er ja eller nej, blev taget imod');
|
|
63
|
+
expect((await sent({ id: ID, choices: {} })) === 400, 'et svar uden kategorier blev taget imod');
|
|
64
|
+
const kept = store.db.prepare('SELECT * FROM consents ORDER BY row').all();
|
|
65
|
+
expect(kept.length === 2, `der blev gemt ${kept.length} svar, ikke 2`);
|
|
66
|
+
expect(kept[0]?.version === COMMIT, `svaret blev gemt med versionen ${kept[0]?.version}, ikke ${COMMIT}`);
|
|
67
|
+
expect(
|
|
68
|
+
!JSON.stringify(kept).includes('origin') && Object.keys(kept[0] ?? {}).join() === 'row,id,at,choices,version',
|
|
69
|
+
'der blev gemt mere end svaret',
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Who reads them.
|
|
73
|
+
expect((await handleConsent(editor('guest'), env)).status === 403, 'en, der ikke kan gemme sitet, kunne læse samtykkerne');
|
|
74
|
+
expect((await handleConsent(editor('nobody'), env)).status === 403, 'en uden login kunne læse samtykkerne');
|
|
75
|
+
const listed = await (await handleConsent(editor('editor'), env)).json();
|
|
76
|
+
expect(listed.total === 2 && listed.entries.length === 2, `listen havde ${listed.entries?.length} af ${listed.total} svar, ikke 2`);
|
|
77
|
+
expect(listed.entries.find((e) => e.id === ID)?.choices.statistics === true, 'listen gav ikke svaret, som det blev givet');
|
|
78
|
+
const found = await (await handleConsent(editor('editor', `?q=${ID.slice(0, 8)}`), env)).json();
|
|
79
|
+
expect(found.entries.length === 1 && found.entries[0].id === ID, 'søgningen på et id fandt ikke kun det id');
|
|
80
|
+
|
|
81
|
+
// As a file.
|
|
82
|
+
const file = await handleConsent(editor('editor', '?format=csv'), env);
|
|
83
|
+
const lines = (await file.text()).trim().split('\r\n');
|
|
84
|
+
expect(file.headers.get('content-disposition')?.startsWith('attachment'), 'CSV-filen hentes ikke som en fil');
|
|
85
|
+
expect(lines[0] === '"id","at","statistics","marketing","version"', `CSV-filens første linje er ${lines[0]}`);
|
|
86
|
+
expect(lines.length === 3, `CSV-filen har ${lines.length - 1} svar, ikke 2`);
|
|
87
|
+
|
|
88
|
+
// Deleted, when a visitor asks.
|
|
89
|
+
const removed = await handleConsent(editor('editor', '', { method: 'DELETE', body: JSON.stringify({ id: OTHER }) }), env);
|
|
90
|
+
expect((await removed.json()).deleted === 1, 'svaret blev ikke slettet');
|
|
91
|
+
expect(
|
|
92
|
+
(await handleConsent(editor('guest', '', { method: 'DELETE', body: JSON.stringify({ id: ID }) }), env)).status === 403,
|
|
93
|
+
'en, der ikke kan gemme sitet, kunne slette et svar',
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Those older than two years are deleted each hour; the others stay.
|
|
97
|
+
const old = new Date(Date.now() - (KEEP_DAYS + 1) * 24 * 60 * 60 * 1000).toISOString();
|
|
98
|
+
store.db.prepare('INSERT INTO consents (id, at, choices, version) VALUES (?, ?, ?, ?)').run(OTHER, old, '{"statistics":true}', COMMIT);
|
|
99
|
+
await forgetOldConsents(env);
|
|
100
|
+
const left = store.db.prepare('SELECT id FROM consents').all();
|
|
101
|
+
expect(left.length === 1 && left[0].id === ID, `efter oprydningen er der ${left.length} svar, ikke kun det nye`);
|
|
102
|
+
|
|
103
|
+
// Without the binding (a preview of a branch) nothing is kept, and the editor is told so.
|
|
104
|
+
const none = { ASSETS };
|
|
105
|
+
expect((await handleConsent(visitor({ id: ID, choices: { statistics: true } }), none)).status === 204, 'et svar uden D1 gav en fejl');
|
|
106
|
+
expect((await handleConsent(editor('editor'), none)).status === 503, 'editoren fik ikke at vide, at der ikke gemmes samtykker');
|
|
107
|
+
await forgetOldConsents(none);
|
|
108
|
+
|
|
109
|
+
if (problems.length) {
|
|
110
|
+
console.error(`${problems.length} fejl i samtykkeloggen:\n${problems.join('\n')}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
console.log('Samtykkeloggen virker: svarene gemmes med sitets version, kun editoren kan læse, hente og slette dem, og gamle slettes.');
|