Bausteine: slugify mit Umlauten, deutsche Zeit, Sperren

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 10:21:34 +00:00
1 parent a3faa1aff1
commit 679f996597
6 files changed
+144

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
/**
* Datum und Uhrzeit in der Berliner Zeitzone (beruecksichtigt MESZ/MEZ automatisch),
* fuer Anzeige und Dateinamen. date: 'tt.mm.jjjj', time: 'hhmm' (24h, '0030' statt '2430').
*/
export function berlinDateTime(date = new Date()) {
const parts = new Intl.DateTimeFormat('de-DE', {
timeZone: 'Europe/Berlin',
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hourCycle: 'h23',
}).formatToParts(date)
const value = (type) => parts.find((part) => part.type === type)?.value
return {
date: `${value('day')}.${value('month')}.${value('year')}`,
time: `${value('hour')}${value('minute')}`,
}
}
+36
View File
@@ -0,0 +1,36 @@
/**
* Sperren je Schluessel (z. B. Kundennummer, Ticket-Id), damit parallele Anfragen fuer
* dieselbe Ressource nacheinander laufen, waehrend Anfragen zu anderen Schluesseln parallel
* bleiben. Ein Aufruf kann mehrere Schluessel auf einmal sperren (alle muessen frei werden).
* waitMs begrenzt, wie lange auf die aktuellen Inhaber gewartet wird (Default: unbegrenzt);
* nach Ablauf laeuft fn trotzdem los, ohne weiter zu warten.
*
* Umsetzung: je Schluessel eine Promise-Kette (das "tail") in einer modulweiten Map.
*/
const tailsByKey = new Map()
// setTimeout(fn, Infinity) wuerde Node zu 1 ms clampen (samt Warnung) - hier bewusst
// eine Promise, die nie aufloest, damit "unbegrenzt warten" auch wirklich unbegrenzt ist.
const sleep = (ms) =>
ms === Infinity ? new Promise(() => {}) : new Promise((resolve) => setTimeout(resolve, ms))
export async function withLocks(keys, fn, { waitMs = Infinity } = {}) {
const previousTails = keys.map((key) => tailsByKey.get(key)).filter(Boolean)
let releaseTail
const tail = new Promise((resolve) => {
releaseTail = resolve
})
for (const key of keys) tailsByKey.set(key, tail)
await Promise.race([Promise.allSettled(previousTails), sleep(waitMs)])
try {
return await fn()
} finally {
releaseTail()
for (const key of keys) {
if (tailsByKey.get(key) === tail) tailsByKey.delete(key)
}
}
}
+22
View File
@@ -0,0 +1,22 @@
/**
* URL-/Dateiname-taugliches Kuerzel aus beliebigem Text: klein geschrieben, deutsche
* Umlaute ausgeschrieben (ä→ae, ö→oe, ü→ue, ß→ss), Akzente entfernt, Apostrophe entfernt,
* alles ausser a-z/0-9/- wird zu einem einzelnen '-', Rand-Bindestriche entfernt, auf
* 100 Zeichen gekuerzt. Kein Text (z. B. null/undefined) ergibt ''.
*/
export function slugify(value) {
const input = value === null || value === undefined ? '' : String(value)
return input
.toLowerCase()
.replace(/ä/g, 'ae')
.replace(/ö/g, 'oe')
.replace(/ü/g, 'ue')
.replace(/ß/g, 'ss')
.normalize('NFD')
.replace(/\p{M}/gu, '')
.replace(/['’‘`]/g, '')
.replace(/[^a-z0-9-]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 100)
}