/* ===================================================================== workspace-stempel.mjs — Versionsstempel an die Workspace-Dateien. WARUM ES DAS GIBT: Cloudflare speichert JS und CSS vier Stunden im Browser -- es ueberschreibt damit das "no-cache", das der Server korrekt schickt. Nach einem Deploy sah man deshalb bis zu vier Stunden lang die ALTE Oberflaeche, obwohl der Server laengst die neue auslieferte. Genau so ist es passiert: eine neue Kachel war live, aber nicht sichtbar. Die HTML-Seiten kommen dagegen immer frisch an (no-cache, DYNAMIC). Deshalb genuegt es, in der HTML einen Stempel an die Adressen zu haengen: Neue Seite -> neue Adresse -> Cloudflare kennt sie nicht -> frische Datei. Ohne Cloudflare-Einstellung, ohne Zutun von aussen. Der Stempel ist die Minute des Aufrufs. Er wird bei jedem Lauf neu gesetzt, auch wenn schon einer dasteht. AUFRUF: node tools/workspace-stempel.mjs Gehoert vor jeden Deploy, der JS oder CSS im Workspace anfasst. ===================================================================== */ import { readdirSync, readFileSync, writeFileSync, existsSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; const HIER = dirname(fileURLToPath(import.meta.url)); const ORDNER = join(HIER, "..", "workspace"); const d = new Date(); const p = (n) => String(n).padStart(2, "0"); const STEMPEL = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}`; /* Trifft href="assets/..." und src="assets/..." -- mit oder ohne vorhandenen Stempel, damit wiederholte Laeufe nicht anhaengen. */ const MUSTER = /(href|src)="(assets\/(?:css|js)\/[A-Za-z0-9._-]+)(?:\?v=\d+)?"/g; /* ZWEITES MUSTER: DIE APP-SYMBOLE (09.09.2026). Sie stehen mit absolutem Pfad in der Seite (`/assets/img/app-symbole/…`) und fielen deshalb durch das Muster oben durch. Der Server gibt Bildern einen Tag Haltbarkeit -- ein neu gezeichnetes Symbol war damit bis zu 24 Stunden lang unsichtbar, obwohl es ausgeliefert wurde. Genau die Luecke, die der Stempel eigentlich schliessen soll, nur fuer Bilder statt fuer Stilvorlagen. Die Reiter-Kennung und das Apple-Symbol bekommen deshalb denselben Stempel wie alles andere. Dass es DERSELBE ist, ist wichtig: Die Pruefung `pruef-zwischenspeicher` verlangt genau einen Stempel im ganzen Haus, und eine zweite Zahl waere sofort rot. */ const MUSTER_SYMBOL = /(href)="(\/assets\/img\/app-symbole\/[A-Za-z0-9._-]+)(?:\?v=\d+)?"/g; let dateien = 0; let stellen = 0; for (const name of readdirSync(ORDNER)) { if (!name.endsWith(".html")) continue; const pfad = join(ORDNER, name); const alt = readFileSync(pfad, "utf8"); let n = 0; const neu = alt .replace(MUSTER, (_, was, datei) => { n += 1; return `${was}="${datei}?v=${STEMPEL}"`; }) .replace(MUSTER_SYMBOL, (_, was, datei) => { n += 1; return `${was}="${datei}?v=${STEMPEL}"`; }); if (neu !== alt) { writeFileSync(pfad, neu); dateien += 1; stellen += n; console.log(` ${name.padEnd(20)} ${n} Verweise`); } } /* DAS MANIFEST GEHOERT DAZU (09.09.2026). Aus ihm holt sich das Betriebssystem das Symbol der installierten App. Ohne Stempel zeigen Android und Windows nach einer Neuzeichnung weiter das alte Bild -- die Adresse hat sich ja nicht geaendert, und ein einmal installiertes Symbol wird nicht von sich aus nachgeladen. Das Manifest selbst wird ohne Haltbarkeit ausgeliefert, kommt also frisch an; nur die Adressen darin waren starr. Derselbe Stempel wie in den Seiten -- `pruef-zwischenspeicher` verlangt im ganzen Haus genau eine Zahl und wuerde eine zweite sofort melden. */ /* DIE MANIFESTE WERDEN GEFUNDEN, NICHT AUFGEZAEHLT (11.09.2026). Hier stand eine Liste mit zwei Namen und daneben der Satz, warum eine Liste besser sei als ein kopierter Block. Beim DRITTEN Manifest (treff.webmanifest, fuer die Community) hat sich das genau so bewahrheitet, wie es vorausgesagt war -- nur eine Stufe weiter oben: Der kopierte Block kam nicht, aber der neue Name fehlte in der Liste. Das Manifest bekam keinen Stempel, und die Symbole der neuen App waeren beim naechsten Wechsel bis zu vier Stunden lang die alten geblieben. Kein Fehler, keine Warnung -- nur eine App mit dem falschen Zeichen. Jetzt wird gesucht. Wer eine vierte Tuer baut, muss hier nichts mehr nachtragen; das Werkzeug findet sie. NULL MANIFESTE SIND EIN FEHLER und kein Ueberspringen: Faende die Suche nichts (falscher Ordner, umbenannte Endung), liefe der Stempel gruen durch und haette nichts getan -- der gruene Haken ueber der Null, gegen den in diesem Haus die halbe Dokumentation steht. */ const MANIFESTE = readdirSync(ORDNER) .filter((n) => n.endsWith(".webmanifest")) .sort(); if (!MANIFESTE.length) { console.error(`FEHLER: kein einziges Manifest in ${ORDNER} gefunden.`); process.exit(1); } const MUSTER_MANIFEST = /("src":\s*")(\/assets\/img\/app-symbole\/[A-Za-z0-9._-]+)(?:\?v=\d+)?(")/g; for (const name of MANIFESTE) { const pfad = join(ORDNER, name); if (!existsSync(pfad)) { console.error(`FEHLER: ${name} fehlt -- erwartet in ${ORDNER}.`); process.exit(1); } const alt = readFileSync(pfad, "utf8"); let n = 0; const neu = alt.replace(MUSTER_MANIFEST, (_, vor, datei, nach) => { n += 1; return `${vor}${datei}?v=${STEMPEL}${nach}`; }); /* Findet das Muster nichts, ist das ein FEHLER und kein Achselzucken: Dann hat jemand das Manifest umgebaut, und ab da laeuft das Symbol still auf dem alten Stand weiter. */ if (n === 0) { console.error(`FEHLER: in ${name} keine Symboladresse gefunden -- Muster pruefen.`); process.exit(1); } if (neu !== alt) { writeFileSync(pfad, neu); dateien += 1; stellen += n; } console.log(` ${name.padEnd(20)} ${n} Verweise`); } console.log(`\nStempel ${STEMPEL} auf ${stellen} Verweise in ${dateien} Dateien gesetzt.`); if (!dateien) console.log("Nichts geaendert -- stimmt das Muster noch?");