diff --git a/docs/publish-logs/2026-04-18-force-all-migration.json b/docs/publish-logs/2026-04-18-force-all-migration.json new file mode 100644 index 0000000..1078d81 --- /dev/null +++ b/docs/publish-logs/2026-04-18-force-all-migration.json @@ -0,0 +1,372 @@ +{ + "run_id": "6356c2c9-37c6-4927-b906-7943bb59d3c0", + "started_at": "2026-04-18T04:44:43.558Z", + "ended_at": "2026-04-18T04:47:34.238Z", + "mode": "force-all", + "posts": [ + { + "slug": "premium-freemium-mium-mium-mium", + "status": "success", + "action": "new", + "event_id": "7f18e5fbc825f16d118281e4c56ce8a989d2647fedcd3d4e45a8df0e897a395c", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 1, + "duration_ms": 4294 + }, + { + "slug": "erlebnispadagogik-im-handbuch-jugend-evangelische-perspektive", + "status": "success", + "action": "new", + "event_id": "ee5ecc397b4bfbff268ec3c53187f0b2b4a03958e14df2728c27145a72b85941", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [], + "images_uploaded": 0, + "duration_ms": 3111 + }, + { + "slug": "telegram-octopi", + "status": "success", + "action": "new", + "event_id": "bee5a9150ce2055e17d729772b4a998afbd3333e6224beb72fd2152ee2d0c05e", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 4, + "duration_ms": 5169 + }, + { + "slug": "lutherkuerbis", + "status": "success", + "action": "new", + "event_id": "7715c4359da95c0459a6e65fd25c17c3c8c169e83fe68acb9f98ed34dd44e4e6", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 6, + "duration_ms": 7921 + }, + { + "slug": "pflanzenschild-qr-code", + "status": "success", + "action": "new", + "event_id": "f66520b363e568bf2714b6d6bd63543dd4f79f8e834d40566656ea35d37ecdf2", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 2, + "duration_ms": 4436 + }, + { + "slug": "virtual-reality", + "status": "success", + "action": "new", + "event_id": "28b75d85774056e6e59097ebf58c44c9e8badadbd25084d0eebc3f95a9a90439", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [ + "wss://relay.damus.io/" + ], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 7, + "duration_ms": 14102 + }, + { + "slug": "wordpress-werkstatt", + "status": "success", + "action": "new", + "event_id": "5ff5ca9dcc4098e042c7bfe0808f05aea6d7ea871dbb69696594a7d7682b3115", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 7, + "duration_ms": 9300 + }, + { + "slug": "bibelfussball", + "status": "success", + "action": "new", + "event_id": "ee019e772f2c8e52a3d77041495508f5d65ab34bb46ccaff3a74f34173cc194f", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [ + "wss://relay.damus.io/" + ], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 1, + "duration_ms": 8627 + }, + { + "slug": "moodle-iomad-linux", + "status": "success", + "action": "new", + "event_id": "707e98d43993778d8e5ffb87a29262d32decc6d0518399991ed7e6c7b4dedf1d", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [ + "wss://relay.damus.io/" + ], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 4, + "duration_ms": 10653 + }, + { + "slug": "ob-virtualcam", + "status": "success", + "action": "new", + "event_id": "1e62cf1f6375fc3988024a3f3d02c041a3065ad69f53b32cb085858fff3c8ed0", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 31, + "duration_ms": 32549 + }, + { + "slug": "jojos-schoko-zimt-schnecken", + "status": "success", + "action": "new", + "event_id": "8561994ee97ebec8775e60106cd8c58b3b24df27c8c0f442e03c2ad384003df2", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 6, + "duration_ms": 9240 + }, + { + "slug": "gleichnis-vom-saemann", + "status": "success", + "action": "new", + "event_id": "a9b49cbf601b7dba4cb7b63e26c281308fee2f722b4c2c0bb7485d339cd9364e", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 9, + "duration_ms": 14335 + }, + { + "slug": "dampfnudeln", + "status": "success", + "action": "new", + "event_id": "51e032c62bc228ace874321f4bcb4f872fe4841258aba3d6d7208ce476664b43", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 6, + "duration_ms": 8128 + }, + { + "slug": "wordpress-statt-padlet-oder-taskcards", + "status": "success", + "action": "update", + "event_id": "8bd17088cb93d4b9868ac4764057f1963c9878a88abc528152649ff2d7b425ef", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 3, + "duration_ms": 7449 + }, + { + "slug": "offenheit-das-wesentliche", + "status": "success", + "action": "update", + "event_id": "45472a71074ed5fdb2c654c8500a9fb581bcf48221be3d565fb6b9ea088c9ab0", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [ + "wss://relay.damus.io/" + ], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 1, + "duration_ms": 8235 + }, + { + "slug": "bottomup-markdown", + "status": "success", + "action": "update", + "event_id": "a8030ba0f9c62a5787a84c607b11f3eaf9c0c694adcc245b9969522cff9f0e30", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [ + "wss://relay.damus.io/" + ], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 1, + "duration_ms": 7143 + }, + { + "slug": "kibedenken-bewusstsein", + "status": "success", + "action": "update", + "event_id": "a05458fc79f4192fef6902e8c55c465f3d9c26cede5772259d5f2d5879734dd2", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 1, + "duration_ms": 5147 + }, + { + "slug": "dezentrale-oep-oer", + "status": "success", + "action": "update", + "event_id": "4db003fd8c144fe1b0528c8cfbfb075ff6a8f203fd327c10c4c46e42fcac2a40", + "relays_ok": [ + "wss://nos.lol/", + "wss://relay.primal.net/", + "wss://relay.tchncs.de/", + "wss://relay.edufeed.org/" + ], + "relays_failed": [ + "wss://relay.damus.io/" + ], + "blossom_servers_ok": [ + "https://blossom.edufeed.org", + "https://blossom.primal.net" + ], + "images_uploaded": 1, + "duration_ms": 8489 + } + ], + "exit_code": 0 +} \ No newline at end of file diff --git a/publish/src/core/validation.ts b/publish/src/core/validation.ts index d51a299..17d1184 100644 --- a/publish/src/core/validation.ts +++ b/publish/src/core/validation.ts @@ -1,6 +1,7 @@ import type { Frontmatter } from './frontmatter.ts' const SLUG_RE = /^[a-z0-9][a-z0-9-]*$/ +const DATE_STRING_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$/ export function validateSlug(slug: string): void { if (!SLUG_RE.test(slug)) { @@ -16,7 +17,14 @@ export function validatePost(fm: Frontmatter): void { throw new Error('missing/invalid slug') } validateSlug(fm.slug) + // Coerce string-dates (YAML `date: "2023-02-26"`) in-place zu Date. + // Native YAML-Dates (`date: 2023-02-26` ohne quotes) kommen bereits als + // Date-instanz aus dem yaml-parser. + if (typeof fm.date === 'string' && DATE_STRING_RE.test(fm.date)) { + const coerced = new Date(fm.date) + if (!isNaN(coerced.getTime())) fm.date = coerced + } if (!(fm.date instanceof Date) || isNaN(fm.date.getTime())) { - throw new Error('missing/invalid date (expected YAML date)') + throw new Error('missing/invalid date (expected YAML date or ISO-string)') } } diff --git a/publish/tests/validation_test.ts b/publish/tests/validation_test.ts index 34c46d1..c38061d 100644 --- a/publish/tests/validation_test.ts +++ b/publish/tests/validation_test.ts @@ -1,4 +1,4 @@ -import { assertThrows } from '@std/assert' +import { assertEquals, assertThrows } from '@std/assert' import { validatePost, validateSlug } from '../src/core/validation.ts' import type { Frontmatter } from '../src/core/frontmatter.ts' @@ -35,7 +35,24 @@ Deno.test('validatePost: fehlt title', () => { assertThrows(() => validatePost(fm), Error, 'title') }) -Deno.test('validatePost: date muss Date sein', () => { +Deno.test('validatePost: lehnt beliebige strings als date ab', () => { const fm = { title: 'T', slug: 'ok', date: 'not-a-date' } as unknown as Frontmatter assertThrows(() => validatePost(fm), Error, 'date') }) + +Deno.test('validatePost: akzeptiert YYYY-MM-DD string-date (coerce zu Date)', () => { + const fm = { title: 'T', slug: 'ok', date: '2023-02-26' } as unknown as Frontmatter + validatePost(fm) + assertEquals(fm.date instanceof Date, true) + assertEquals((fm.date as Date).toISOString().startsWith('2023-02-26'), true) +}) + +Deno.test('validatePost: akzeptiert ISO-string-date', () => { + const fm = { + title: 'T', + slug: 'ok', + date: '2024-01-15T10:30:00Z', + } as unknown as Frontmatter + validatePost(fm) + assertEquals(fm.date instanceof Date, true) +})