/* ===================================================================== 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. */ /* ES SIND ZWEI MANIFESTE (10.09.2026). Seit die Modi-App unter ihrer eigenen Adresse laeuft, gibt es neben app.webmanifest auch crew.webmanifest -- dasselbe Haus, anderer Name, andere Symbole. ALS LISTE UND NICHT ALS ZWEITER BLOCK: Ein kopierter Block waere beim dritten Manifest ein dritter, und irgendwann bleibt einer stehen. Ein fehlendes Manifest ist hier ausserdem ein FEHLER und kein Ueberspringen -- sonst faellt das Ausbleiben nicht auf. */ const MANIFESTE = ["app.webmanifest", "crew.webmanifest"]; 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?");