Validation: the dump validators refuse the nulls their shared types don't allow (a null title or note on creation, a null URL on edit); a playlist title of spaces is refused and titles are stored trimmed; usernames and emails are checked by the same USERNAME_RE and EMAIL_RE in shared/validation.ts on sign-up, on PATCH /api/users/me (which checked other limits) and in the sign-up form. Mentions are read by shared/mention.ts's mentionedUsernames, which skips code and URLs as Markdown shows them: https://mastodon.social/@bob or a `@bob` in code notified bob; the editor's suggestions derive their trigger from the same pattern instead of a copy. Following an unknown user is a 404 rather than a foreign-key 500, and following a private playlist a 404 rather than a 403 that confirmed it exists. `ws` and `emojibase` are reserved category slugs, since the server answers them before the app. GET /api/users/me no longer turns every error into a 500. Dead code goes: adminOnlyMiddleware, verifyEmailTransport, listUsers, deleteUser with disconnectUser and the force_logout message only it sent, PATCH /api/notifications/:id/read (with markOneRead), isSlugAvailable, the unreachable 500 in fetchComment, and the `if (!ctx.state.user)` checks behind authMiddleware. 214 backend and 627 frontend tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
/**
|
|
* Reads the Markdown of chat messages, to find mentions and to flatten it into
|
|
* reply previews.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import { mentionedUsernames } from "../../../shared/mention.ts";
|
|
|
|
/**
|
|
* Checks whether `body` @mentions `username`, ignoring case, with the pattern
|
|
* the server uses for mention notifications. `WSProvider` uses it to pick the
|
|
* chime, and `ChatModal` to highlight the message.
|
|
*/
|
|
export function mentionsUser(body: string, username: string): boolean {
|
|
return mentionedUsernames(body).includes(username.toLowerCase());
|
|
}
|
|
|
|
/**
|
|
* Returns the Markdown `md` as one line of plain text. Images become their alt
|
|
* text, or `imageLabel` without one, and links their text. It takes a
|
|
* `replyToBody`, which the server cuts short, possibly inside a link: the
|
|
* unfinished link then shows as its text, without half a URL. `snippet` calls
|
|
* it.
|
|
*/
|
|
export function markdownToPlainText(md: string, imageLabel: string): string {
|
|
return md
|
|
// An image or link cut short at the end loses its unfinished target.
|
|
.replace(/!\[([^\]]*)\]\([^)]*$/, (_, alt: string) => alt || imageLabel)
|
|
.replace(/(^|[^!])\[([^\]]*)\]\([^)]*$/, "$1$2")
|
|
.replace(/!\[[^\]]*$/, "")
|
|
.replace(/!\[([^\]]*)\]\([^)]*\)/g, (_, alt: string) => alt || imageLabel)
|
|
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
.replace(/^\s{0,3}(#{1,6}|>|[-*+]|\d+\.)\s+/gm, "")
|
|
.replace(/```\w*|\*\*|__|~~|[*`]/g, "")
|
|
.replace(/\s+/g, " ")
|
|
.trim();
|
|
}
|