EmDash 1.1 wyszedł 1 października 2026, kilka dni po wersji 1.0 — pierwszej, po której breaking changes mają trafiać już tylko do nowych wersji głównych. Poniżej przegląd nowości (na podstawie oficjalnej zapowiedzi i changeloga), a w drugiej części to, czego nauczyłem się, aktualizując emdashcms.pl z wersji 0.0.3 prosto do 1.1.0 — czyli przeskakując około 45 wydań naraz.
Co nowego w EmDash 1.1
Kalendarz publikacji
W panelu admina pojawił się Kalendarz (w menu bocznym, palecie poleceń i na dashboardzie). Pokazuje opublikowane wpisy w dniu publikacji oraz zaplanowane wpisy i zaplanowane aktualizacje w dniu, na który są ustawione — we wszystkich kolekcjach i językach, do których masz dostęp. Są dwa widoki: miesiąc i agenda, daty w strefie czasowej strony. Po kliknięciu wpisu otwiera się panel boczny, z którego można zmienić termin, usunąć harmonogram albo od razu opublikować wpis, którego termin już minął.
Bloki HTML z CSS i JavaScriptem
Blok HTML ma teraz zakładki HTML, CSS i JS oraz podgląd. Ważna zmiana zachowania: nowe bloki HTML renderują się na stronie w izolowanym iframie (sandbox). Kod z bloku nie ma dostępu do ciasteczek, storage'u ani reszty strony, a style strony nie działają w jego wnętrzu. Kto chce starego zachowania, wybiera dla bloku tryb Inline.
Warto o tym pamiętać: od wersji 1.1 każdy, kto może edytować treść (od roli Contributor wzwyż), może dodać JavaScript, który wykona się u odwiedzających po publikacji wpisu — właśnie dlatego trafia on do sandboxa.
Blok iframe
Nowy blok /iframe do osadzania stron z innych serwisów: wklejasz kod embed albo link, a linki do YouTube i Vimeo same zamieniają się w odtwarzacze. Jeśli używasz CSP w Astro, musisz dopuścić hosty, z których osadzasz treści.
Logowanie przez Microsoft Entra ID
Nowy provider microsoft() pozwala redaktorom logować się do admina kontem służbowym lub szkolnym Microsoft, obok passkeyów i innych metod.
WebMcpSearch (eksperymentalnie)
Komponent emdash/ui/webmcp-search rejestruje w przeglądarkach obsługujących WebMCP narzędzie search_site, dzięki któremu agent AI działający w przeglądarce odwiedzającego może przeszukiwać opublikowane treści (tylko odczyt, przez publiczne API wyszukiwania). W pozostałych przeglądarkach nic nie robi.
Wydajność i poprawki
- Menu ładuje się z pozycjami w jednym zapytaniu zamiast kilku.
- Przekierowania są sprawdzane bez czytania całej tabeli co 30 sekund w każdym izolacie Workera.
- Paginacja przez
nextCursornie gubi już wpisów przy sortowaniu po polu, które może być puste (np. data publikacji) — dotyczyło to teżgetEmDashCollection. - Endpoint obrazków (
/_image) nie czeka na start runtime'u, więc pierwsze obrazki na świeżym izolacie ładują się szybciej. - Stara strona po publikacji: powracający odwiedzający nie dostają już nieaktualnej wersji strony do następnego deployu.
- Turnstile w komentarzach — o tym niżej, bo to poprawka bezpieczeństwa.
Nasza aktualizacja: z 0.0.3 do 1.1.0
emdashcms.pl powstał na wersji 0.0.3 i od tamtej pory stał. Powodem aktualizacji był zepsuty search. Oto co wyszło po drodze.
1. ^0.0.3 to nie „aktualizuj automatycznie”
W package.json miałem "emdash": "^0.0.3" i zakładałem, że caret pozwoli na drobne aktualizacje. Nie pozwala: w semverze dla wersji 0.0.x caret blokuje dokładnie tę wersję. ^1.2.3 oznacza „cokolwiek z 1.x”, ale ^0.0.3 oznacza „tylko 0.0.3”. Projekt mógł stać na pierwszej wersji w nieskończoność, a npm update nic by nie zmienił.
2. Wyszukiwarka w nagłówku nie działała od początku
Komponent LiveSearch zawsze pokazywał „No results found”. Przyczyna: w 0.0.3 middleware autoryzacji blokował publiczne /_emdash/api/search i zwracał 401 każdemu niezalogowanemu. Poprawiono to dawno temu w 0.1.x. Wniosek: przy stronie, która „działa”, warto co jakiś czas sprawdzić w DevTools, co faktycznie wracają zapytania do API.
3. Drugi błąd searcha był nasz, nie EmDasha
Strona /search?q=... pokazywała każdemu wyniki pierwszego zapytania — wpisywałeś „wordpress”, dostawałeś wyniki dla „astro”. Winny okazał się mój własny edge cache z benchmarku : Worker zapisywał strony w Cache API z kluczem new URL(url.pathname, url.origin), czyli bez query stringu. Dla /posts/jakis-wpis to nie problem, ale dla wyszukiwarki cała treść strony zależy właśnie od ?q=.
Poprawka: klucz cache zawiera teraz parametry zapytania (posortowane, bez utm_*, fbclid, gclid), żeby nie rozbijać cache na tysiąc wariantów przez linki z kampanii. Przy okazji wyszło, że kopie z cache dostawały domyślny Browser TTL strefy (4 godziny), więc przeglądarki trzymały stare strony znacznie dłużej, niż zakładałem.
4. Co faktycznie się zepsuło przy 1.0
Mniej, niż się bałem:
@emdash-cms/plugin-webhook-notifiernie eksportuje już funkcjiwebhookNotifierPlugin(), tylko gotowy deskryptor jako default export:plugins: [formsPlugin(), webhookNotifier].- Komenda
npx emdash devzostała usunięta — teraz po prostunpm run dev(czyliastro dev). - Nowy wrangler wymaga
@cloudflare/workers-typesw wersji 5. - Najnowszy npm domyślnie blokuje skrypty instalacyjne pakietów.
workerd,esbuildisharptrzeba zatwierdzić (npm approve-scripts), inaczej build nie ruszy.
Reszta breaking changes z 1.0 (przeniesione ścieżki emdash/internal/*, usunięte Comments z emdash/ui, opcja experimental.registry) nas nie dotyczyła. Warto jednak przejrzeć oficjalny przewodnik upgrade'u przed aktualizacją.
5. wrangler d1 export nie zrobi backupu bazy EmDasha
Przed deployem chciałem zrzucić bazę D1 do pliku SQL i dostałem:
D1 Export error: cannot export databases with virtual tables (like FTS5) EmDash trzyma indeks wyszukiwania w tabelach FTS5, więc eksport nie zadziała. Rozwiązanie to Time Travel: wrangler d1 time-travel info <baza> zwraca zakładkę aktualnego stanu, a wrangler d1 time-travel restore <baza> --bookmark=... cofa do niej bazę. Zakładka to nie kopia, tylko pozycja w dzienniku zmian, który D1 i tak prowadzi (30 dni wstecz na płatnym planie, 7 na darmowym). Do wycofania nieudanego deployu w zupełności wystarczy.
Kolejność przy wycofywaniu ma znaczenie: najpierw wrangler rollback kodu, potem restore bazy — stary kod nie zna tabel z nowych migracji.
6. Pierwsze wejście po deployu trwało 33 sekundy
Migracje bazy z ~45 wydań wykonują się przy pierwszym requeście po deployu, a nie w trakcie samego deployu. U mnie strona główna odpowiedziała po 33 sekundach, a każde następne wejście już po około 0,5 s. Rada: zaraz po wrangler deploy wejdź na stronę sam, zanim zrobi to Googlebot albo czytelnik.
7. Sprawdź, z czego jest zbudowana produkcja
Deployuję ręcznie wranglerem z lokalnego repozytorium i okazało się, że produkcja chodziła z gałęzi z optymalizacjami z benchmarku, której nie było w main. Gdybym wdrożył aktualizację zrobioną na main, po cichu wyłączyłbym edge cache i opóźnione ładowanie widgetów. Przy deployach bez CI dobrze mieć w odpowiedzi jakiś znacznik — u mnie był to nagłówek X-Cache — po którym widać, jaki kod faktycznie działa.
8. Turnstile: sekret ustawiony w runtime był ignorowany
Changelog 1.1 (#3622): weryfikacja Turnstile w komentarzach ignorowała TURNSTILE_SECRET_KEY ustawiony w runtime, np. przez wrangler secret put. Jeśli klucz nie był dostępny już podczas builda, komentarze przechodziły bez sprawdzenia Turnstile. Jeśli masz na stronie komentarze z Turnstile i trzymasz sekret w wrangler secret, to jest dobry powód, żeby zaktualizować.
Podsumowanie
Skok z 0.0.3 do 1.1 okazał się łagodniejszy, niż wskazywałoby 45 wydań różnicy: jedna zmiana importu w configu, jedna komenda w dokumentacji i aktualizacja typów Cloudflare. Najwięcej czasu zajęły rzeczy wokół samej aktualizacji: backup bazy, ustalenie, co naprawdę działa na produkcji, i błąd we własnym cache'u. Jeśli siedzisz na wersji sprzed 1.0, teraz jest dobry moment — od 1.0 breaking changes mają pojawiać się tylko w nowych wersjach głównych.
Brak komentarzy