2026-02-24 23:03:45 -03:00
# ! / u s r / b i n / e n v n p x t s - n o d e
2026-02-25 07:17:45 +05:30
/**
* Benchmark suite for @rocket.chat/message-parser
*
* Measures parsing performance (ops/sec) across various message categories.
* Run with: `yarn bench` from packages/message-parser/
*
2026-02-24 23:03:45 -03:00
* Uses a custom loader (pegjs-register.js) to compile .pegjs at runtime — no build needed.
2026-02-25 07:17:45 +05:30
*/
import { Bench , type Task } from 'tinybench' ;
import { parse } from '../src' ;
import type { Options } from '../src' ;
// ── Options presets ────────────────────────────────────────────────────────
const fullOptions : Options = {
colors : true ,
emoticons : true ,
katex : {
dollarSyntax : true ,
parenthesisSyntax : true ,
} ,
} ;
2026-02-24 23:03:45 -03:00
// ── Fixture type ───────────────────────────────────────────────────────────
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
type Fixture = {
name : string ;
input : string ;
options? : Options ;
} ;
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
type BenchCategory = {
name : string ;
time? : number ;
warmupTime? : number ;
fixtures : Fixture [ ] ;
} ;
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
// ── Categories ─────────────────────────────────────────────────────────────
const categories : BenchCategory [ ] = [
{
name : 'Plain Text' ,
fixtures : [
{ name : 'short' , input : 'Hello world' } ,
{
name : 'medium' ,
input : 'The quick brown fox jumps over the lazy dog. This is a typical message one might send in a chat application.' ,
} ,
{ name : 'long' , input : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' . repeat ( 20 ) . trim ( ) } ,
] ,
} ,
{
name : 'Emphasis / Formatting' ,
fixtures : [
{ name : 'bold' , input : '**Hello world**' } ,
{ name : 'italic' , input : '_Hello world_' } ,
{ name : 'strike' , input : '~~Hello world~~' } ,
{ name : 'nested' , input : '**bold _italic_ and ~~strike~~**' } ,
{ name : 'deep nesting' , input : '**bold _italic ~~strike _deep italic_~~_**' } ,
2026-04-17 16:20:36 -03:00
{ name : 'bold + italic mixed' , input : '*Bold text* and _italic text_ in a message' } ,
{ name : 'deeply nested' , input : '*bold _italic ~~strike~~ italic_ bold*' } ,
2026-02-24 23:03:45 -03:00
{ name : 'multiple' , input : '**bold** normal _italic_ normal ~~strike~~ **more bold** _more italic_' } ,
] ,
} ,
{
name : 'URLs & Links' ,
fixtures : [
{ name : 'single' , input : 'Check out https://rocket.chat for more info' } ,
{ name : 'multiple' , input : 'Visit https://rocket.chat or https://github.com/RocketChat/Rocket.Chat or https://open.rocket.chat' } ,
{ name : 'markdown link' , input : '[Rocket.Chat](https://rocket.chat)' } ,
{ name : 'autolinked domain' , input : 'Visit rocket.chat for more info' } ,
{ name : 'with path' , input : 'See https://github.com/RocketChat/Rocket.Chat/tree/develop/packages/message-parser for details' } ,
] ,
} ,
{
name : 'Emoji' ,
fixtures : [
2026-02-26 00:39:13 +05:30
{ name : 'single shortcode' , input : ':smile:' , options : fullOptions } ,
{ name : 'triple shortcode (BigEmoji)' , input : ':smile::heart::rocket:' , options : fullOptions } ,
{ name : 'single unicode' , input : '😀' , options : fullOptions } ,
{ name : 'triple unicode (BigEmoji)' , input : '😀🚀🌈' , options : fullOptions } ,
{ name : 'in text' , input : 'Hello :smile: world :heart: test :rocket: done' , options : fullOptions } ,
{ name : 'mixed' , input : 'Great job :thumbsup: 🎉 keep going :rocket:' , options : fullOptions } ,
2026-04-17 16:20:36 -03:00
{ name : 'emoji heavy' , input : ':smile: :wave: :rocket: :fire: :heart: :100:' , options : fullOptions } ,
2026-02-24 23:03:45 -03:00
] ,
} ,
{
name : 'Mentions' ,
fixtures : [
{ name : 'single user' , input : '@admin' } ,
{ name : 'multiple users' , input : '@admin @user1 @moderator' } ,
{ name : 'channel' , input : '#general' } ,
{ name : 'mixed' , input : 'Hey @admin check #general and @user1' } ,
2026-04-17 16:20:36 -03:00
{ name : 'mentions (suggested)' , input : 'Hey @john and @jane, check #general' } ,
2026-02-24 23:03:45 -03:00
] ,
} ,
{
name : 'Code' ,
fixtures : [
{ name : 'inline' , input : 'Use `console.log()` for debugging' } ,
{ name : 'block' , input : '```javascript\nconst x = 1;\nconsole.log(x);\n```' } ,
{ name : 'multi inline' , input : 'Use `Array.map()` and `Array.filter()` and `Array.reduce()`' } ,
] ,
} ,
{
name : 'Structured Blocks' ,
fixtures : [
{ name : 'ordered list' , input : '1. First item\n2. Second item\n3. Third item' } ,
{ name : 'unordered list' , input : '- First item\n- Second item\n- Third item' } ,
{ name : 'task list' , input : '- [x] Done task\n- [ ] Pending task\n- [x] Another done' } ,
{ name : 'blockquote' , input : '> This is a quoted message\n> with multiple lines' } ,
{ name : 'heading' , input : '# Hello World' } ,
{ name : 'heading multi-level' , input : '# H1\n## H2\n### H3\n#### H4' } ,
{ name : 'spoiler' , input : '||This is a spoiler||' } ,
{ name : 'spoiler with formatting' , input : '||**bold** and _italic_ spoiler||' } ,
] ,
} ,
{
name : 'KaTeX (Math)' ,
fixtures : [
{ name : 'inline' , input : 'The formula is $E = mc^2$ in physics' , options : fullOptions } ,
{ name : 'block' , input : '$$\\sum_{i=1}^{n} x_i = x_1 + x_2 + ... + x_n$$' , options : fullOptions } ,
] ,
} ,
{
name : 'Adversarial / Stress' ,
time : 2000 ,
warmupTime : 500 ,
fixtures : [
{
name : 'adversarial emphasis' ,
input : '**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__**_**__' ,
} ,
{
name : 'adversarial mixed' ,
input :
'This a message designed to stress test !!@#$%^&*()_+, overloading the symbols {}:"|<>?, some more text ,./;\'\\[], numbers too 1234567890-= let it call s o s ok~' ,
} ,
{ name : 'repeated specials' , input : '****____~~~~||||````####>>>>' } ,
{
name : 'long with formatting' ,
input : '**bold** _italic_ ~~strike~~ `code` @user #channel :smile: https://example.com ' . repeat ( 10 ) . trim ( ) ,
} ,
2026-04-17 16:20:36 -03:00
{
name : 'unmatched markers (pathological)' ,
input : '*_~*_~*_~*_~*_~ hello world absolutely no closing markers anywhere at all' ,
} ,
2026-02-24 23:03:45 -03:00
] ,
} ,
{
name : 'Real-World Messages' ,
fixtures : [
{ name : 'simple' , input : 'Hey team, the deploy is done ✅' } ,
{
name : 'medium' ,
input :
'@admin I pushed the fix to `develop` branch. Check https://github.com/RocketChat/Rocket.Chat/pull/12345 for details. :thumbsup:' ,
} ,
{
name : 'complex' ,
input :
'**Release Notes v7.0**\n- [x] Fix #12345\n- [ ] Update docs\n\n> Important: check https://docs.rocket.chat\n\ncc @admin @devlead #releases :rocket:' ,
options : fullOptions ,
} ,
2026-04-17 16:20:36 -03:00
{
name : 'realistic chat message' ,
input : 'Hello @team, please review the *important* update:\n\n1. Run `yarn build`\n2. Check #deployments\n\n*Thanks!* :rocket:' ,
options : fullOptions ,
} ,
2026-02-24 23:03:45 -03:00
] ,
} ,
{
name : 'Timestamps' ,
fixtures : [ { name : 'unix format' , input : '<t:1630360800:f>' } ] ,
} ,
] ;
// ── Helpers ────────────────────────────────────────────────────────────────
function formatResults ( tasks : Task [ ] ) {
return tasks . map ( ( task ) = > ( {
'Task' : task . name ,
'ops/sec' : Math . round ( task . result ? . hz ? ? 0 ) . toLocaleString ( ) ,
'Avg (ms)' : ( ( task . result ? . mean ? ? 0 ) * 1000 ) . toFixed ( 4 ) ,
'Min (ms)' : ( ( task . result ? . min ? ? 0 ) * 1000 ) . toFixed ( 4 ) ,
'Max (ms)' : ( ( task . result ? . max ? ? 0 ) * 1000 ) . toFixed ( 4 ) ,
'P99 (ms)' : ( ( task . result ? . p99 ? ? 0 ) * 1000 ) . toFixed ( 4 ) ,
'Samples' : task . result ? . samples ? . length ? ? 0 ,
} ) ) ;
}
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
// ── Runner ─────────────────────────────────────────────────────────────────
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
async function run() {
console . log ( '=' . repeat ( 72 ) ) ;
console . log ( ' @rocket.chat/message-parser — Performance Benchmark Suite' ) ;
console . log ( '=' . repeat ( 72 ) ) ;
console . log ( ) ;
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
// Benchmarks must run sequentially to avoid interference
2026-03-03 20:33:25 -03:00
2026-02-24 23:03:45 -03:00
for ( const category of categories ) {
const bench = new Bench ( {
time : category.time ? ? 1000 ,
warmupTime : category.warmupTime ? ? 200 ,
} ) ;
2026-02-25 07:17:45 +05:30
2026-02-24 23:03:45 -03:00
for ( const fixture of category . fixtures ) {
bench . add ( fixture . name , ( ) = > parse ( fixture . input , fixture . options ) ) ;
}
2026-02-25 07:17:45 +05:30
await bench . run ( ) ;
2026-02-24 23:03:45 -03:00
console . log ( ` ── ${ category . name } ${ '─' . repeat ( Math . max ( 0 , 56 - category . name . length ) ) } ` ) ;
console . table ( formatResults ( bench . tasks ) ) ;
2026-02-25 07:17:45 +05:30
console . log ( ) ;
}
console . log ( '=' . repeat ( 72 ) ) ;
2026-02-24 23:03:45 -03:00
console . log ( ' Done.' ) ;
2026-02-25 07:17:45 +05:30
console . log ( '=' . repeat ( 72 ) ) ;
}
2026-02-24 23:03:45 -03:00
run ( ) . catch ( ( err ) = > {
console . error ( err ) ;
process . exit ( 1 ) ;
} ) ;