Stage 17: cap display-name special characters at 5 (ui + backend)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 35s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m3s

display_name validation gains a rule: at most 5 special characters — the '.' / '_'
punctuation (spaces, which separate words, don't count) — so a still-well-formed name
can't be mostly punctuation. Mirrored in the Go ValidateDisplayName and the UI
validDisplayName; both unit-tested (5 ok, 6 rejected, 'J. R. R. Tolkien' ok). Docs:
FUNCTIONAL (+ _ru).
This commit is contained in:
Ilia Denisov
2026-06-09 07:42:47 +02:00
parent 84ecc85f51
commit d87c0fb10b
6 changed files with 32 additions and 3 deletions
+3
View File
@@ -19,6 +19,9 @@ describe('validDisplayName', () => {
['Name2', false],
['', false],
['a'.repeat(33), false],
['a.a.a.a.a.a', true], // 5 dots — at the special-char limit
['a.a.a.a.a.a.a', false], // 6 dots — over the limit
['J. R. R. Tolkien', true], // 3 dots; spaces are not special
])('%s -> %s', (name, ok) => {
expect(validDisplayName(name)).toBe(ok);
});
+8 -1
View File
@@ -5,6 +5,10 @@
/** maxDisplayName caps the editable display name in runes. */
export const maxDisplayName = 32;
/** maxDisplayNameSpecials caps the total special characters (the "." / "_" separators — every
* rune that is neither a letter nor a space) a display name may carry. Mirrors the Go rule. */
export const maxDisplayNameSpecials = 5;
/** maxAwayMinutes bounds the daily away window's length (12 h). */
export const maxAwayMinutes = 12 * 60;
@@ -17,7 +21,10 @@ const displayNameRe = /^\p{L}+(?:(?:[._] ?| )\p{L}+)*\.?$/u;
/** displayNameError returns true when the trimmed name is a valid display name. */
export function validDisplayName(raw: string): boolean {
const name = raw.trim();
return name.length > 0 && [...name].length <= maxDisplayName && displayNameRe.test(name);
const chars = [...name];
if (name.length === 0 || chars.length > maxDisplayName || !displayNameRe.test(name)) return false;
const specials = chars.filter((c) => c !== ' ' && !/\p{L}/u.test(c)).length;
return specials <= maxDisplayNameSpecials;
}
// A pragmatic email check (the backend re-validates with net/mail). Rejects spaces