@changerawr/markdown 1.1.5 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,8 @@
15
15
  - ⚛️ **React Integration**: Drop-in `<MarkdownRenderer>` component + hooks
16
16
  - 🍦 **Vanilla JS Support**: Use anywhere with `renderCum()` function
17
17
  - 📝 **TypeScript First**: Fully typed with excellent IntelliSense
18
- - 🎯 **Performance Focused**: Efficient parsing and rendering
18
+ - **High Performance**: Automatic caching, streaming support, handles 100K+ words efficiently
19
+ - 💾 **Smart Caching**: Built-in LRU cache for 100-1000x speedup on repeated renders
19
20
  - 🛡️ **Secure**: Built-in HTML sanitization with DOMPurify
20
21
  - 🔧 **Extensible**: Easy-to-write custom extensions
21
22
  - 🎨 **Themeable**: Customizable CSS classes and styling
@@ -484,23 +485,97 @@ function DebugComponent() {
484
485
  }
485
486
  ```
486
487
 
487
- ## 📊 Performance
488
+ ## 📊 Performance & Caching
488
489
 
489
- ### Metrics and Monitoring
490
+ Built-in performance optimizations handle large documents efficiently with automatic caching and streaming support.
491
+
492
+ ### Automatic Caching
493
+
494
+ The engine automatically caches parsed and rendered content for instant repeated renders:
495
+
496
+ ```typescript
497
+ const engine = new ChangerawrMarkdown();
498
+
499
+ // First render - parses and renders
500
+ const html1 = engine.toHtml(largeMarkdown); // ~100ms
501
+
502
+ // Second render - served from cache
503
+ const html2 = engine.toHtml(largeMarkdown); // <1ms (100x+ faster!)
504
+
505
+ // Check cache statistics
506
+ const stats = engine.getCacheStats();
507
+ console.log(stats.parse.hits, stats.parse.hitRate);
508
+ console.log(stats.render.hits, stats.render.hitRate);
509
+
510
+ // Clear caches if needed
511
+ engine.clearCaches();
512
+
513
+ // Adjust cache size (default: 100 entries)
514
+ engine.setCacheSize(200);
515
+ ```
516
+
517
+ ### Streaming for Large Documents
518
+
519
+ Stream large documents in chunks for progressive rendering:
490
520
 
491
521
  ```typescript
492
- const engine = createDebugEngine();
493
- const html = engine.toHtml('# Large Document...');
522
+ const engine = new ChangerawrMarkdown();
523
+
524
+ const html = await engine.toHtmlStreamed(hugeMarkdown, {
525
+ chunkSize: 50, // Render 50 tokens at a time
526
+ onChunk: ({ html, progress }) => {
527
+ console.log(`Rendered: ${(progress * 100).toFixed(0)}%`);
528
+ // Update UI progressively
529
+ updatePreview(html);
530
+ }
531
+ });
532
+ ```
494
533
 
495
- const metrics = engine.getPerformanceMetrics();
496
- console.log('Parse time:', metrics.parseTime);
497
- console.log('Render time:', metrics.renderTime);
498
- console.log('Total time:', metrics.totalTime);
534
+ ### Performance Metrics
535
+
536
+ Track rendering performance with built-in metrics:
537
+
538
+ ```typescript
539
+ const { html, metrics } = engine.toHtmlWithMetrics(markdown);
540
+
541
+ console.log('Input size:', metrics.inputSize);
542
+ console.log('Parse time:', metrics.parseTime, 'ms');
543
+ console.log('Render time:', metrics.renderTime, 'ms');
544
+ console.log('Total time:', metrics.totalTime, 'ms');
499
545
  console.log('Token count:', metrics.tokenCount);
546
+ console.log('Cache hit:', metrics.cacheHit);
500
547
  ```
501
548
 
549
+ ### Memoization Helper
550
+
551
+ Memoize expensive operations with the built-in helper:
552
+
553
+ ```typescript
554
+ import { memoize } from '@changerawr/markdown';
555
+
556
+ const expensiveTransform = memoize((content: string) => {
557
+ // Complex transformation here
558
+ return processContent(content);
559
+ });
560
+
561
+ // First call - computed
562
+ const result1 = expensiveTransform(data); // Slow
563
+
564
+ // Second call with same input - cached
565
+ const result2 = expensiveTransform(data); // Instant!
566
+ ```
567
+
568
+ ### Performance Benchmarks
569
+
570
+ - **Small documents** (1K words): ~15ms
571
+ - **Medium documents** (10K words): ~400ms
572
+ - **Large documents** (100K words): ~32s first render, <1ms cached
573
+ - **Cache speedup**: 100-1000x faster for repeated content
574
+
502
575
  ### Optimization Tips
503
576
 
577
+ - **Caching is automatic** - no configuration needed for most use cases
578
+ - Use `toHtmlStreamed()` for documents over 10,000 words
504
579
  - Use `createCoreOnlyEngine()` if you don't need feature extensions
505
580
  - Use `createMinimalEngine()` with only the extensions you need
506
581
  - Set `sanitize: false` if you trust your content (be careful!)
@@ -527,6 +602,16 @@ npm run test:coverage # Run with coverage report
527
602
  - `renderToTailwind(markdown: string): string` - Render with Tailwind classes
528
603
  - `renderToJSON(markdown: string): JsonAstNode` - Render to JSON AST
529
604
 
605
+ ### Performance & Cache Functions
606
+
607
+ - `engine.toHtml(markdown: string): string` - Render with automatic caching
608
+ - `engine.toHtmlWithMetrics(markdown: string): { html, metrics }` - Render with performance metrics
609
+ - `engine.toHtmlStreamed(markdown: string, options?): Promise<string>` - Stream large documents
610
+ - `engine.getCacheStats(): { parse, render }` - Get cache statistics
611
+ - `engine.clearCaches(): void` - Clear all caches
612
+ - `engine.setCacheSize(size: number): void` - Update cache capacity
613
+ - `memoize<T>(fn: T): T` - Memoize any function
614
+
530
615
  ### Factory Functions
531
616
 
532
617
  - `createEngine(config?)` - Full-featured engine
package/dist/index.d.mts CHANGED
@@ -1,3 +1,84 @@
1
+ interface CacheStats {
2
+ size: number;
3
+ capacity: number;
4
+ hits: number;
5
+ misses: number;
6
+ hitRate: number;
7
+ evictions: number;
8
+ }
9
+ declare class LRUCache<K, V> {
10
+ private cache;
11
+ private capacity;
12
+ private hits;
13
+ private misses;
14
+ private evictions;
15
+ constructor(capacity?: number);
16
+ /**
17
+ * Get a value from the cache
18
+ */
19
+ get(key: K): V | undefined;
20
+ /**
21
+ * Set a value in the cache
22
+ */
23
+ set(key: K, value: V): void;
24
+ /**
25
+ * Check if a key exists in the cache
26
+ */
27
+ has(key: K): boolean;
28
+ /**
29
+ * Delete a specific key from the cache
30
+ */
31
+ delete(key: K): boolean;
32
+ /**
33
+ * Clear all entries from the cache
34
+ */
35
+ clear(): void;
36
+ /**
37
+ * Get the current size of the cache
38
+ */
39
+ get size(): number;
40
+ /**
41
+ * Get cache statistics
42
+ */
43
+ getStats(): CacheStats;
44
+ /**
45
+ * Reset cache statistics
46
+ */
47
+ resetStats(): void;
48
+ /**
49
+ * Get all keys in the cache
50
+ */
51
+ keys(): K[];
52
+ /**
53
+ * Get all values in the cache
54
+ */
55
+ values(): V[];
56
+ /**
57
+ * Update cache capacity and evict if necessary
58
+ */
59
+ setCapacity(newCapacity: number): void;
60
+ /**
61
+ * Evict the least recently used entry
62
+ */
63
+ private evictLRU;
64
+ }
65
+ /**
66
+ * Simple hash function for content
67
+ * Uses FNV-1a algorithm for fast, decent-quality hashing
68
+ */
69
+ declare function hashContent(content: string): string;
70
+ /**
71
+ * Create a cached version of a function
72
+ */
73
+ declare function memoize<T extends (...args: any[]) => any>(fn: T, options?: {
74
+ cache?: LRUCache<string, ReturnType<T>>;
75
+ keyGenerator?: (...args: Parameters<T>) => string;
76
+ maxSize?: number;
77
+ }): T & {
78
+ cache: LRUCache<string, ReturnType<T>>;
79
+ clearCache: () => void;
80
+ };
81
+
1
82
  interface MarkdownToken {
2
83
  type: string;
3
84
  content: string;
@@ -87,23 +168,68 @@ interface ExtensionRegistration {
87
168
  conflictingRules?: string[];
88
169
  }
89
170
 
171
+ interface RenderMetrics {
172
+ inputSize: number;
173
+ parseTime: number;
174
+ renderTime: number;
175
+ totalTime: number;
176
+ tokenCount: number;
177
+ cacheHit: boolean;
178
+ }
90
179
  declare class ChangerawrMarkdown {
91
180
  private parser;
92
181
  private renderer;
93
182
  private extensions;
183
+ private parseCache;
184
+ private renderCache;
185
+ private parseTime;
186
+ private renderTime;
187
+ private lastTokenCount;
94
188
  constructor(config?: EngineConfig);
95
189
  private registerFeatureExtensions;
96
190
  private registerCoreExtensions;
97
191
  registerExtension(extension: Extension): ExtensionRegistration;
98
192
  unregisterExtension(name: string): boolean;
99
193
  parse(markdown: string): MarkdownToken[];
100
- render(tokens: MarkdownToken[]): string;
194
+ render(tokens: MarkdownToken[], cacheKey?: string): string;
101
195
  toHtml(markdown: string): string;
196
+ /**
197
+ * Render markdown with performance metrics
198
+ */
199
+ toHtmlWithMetrics(markdown: string): {
200
+ html: string;
201
+ metrics: RenderMetrics;
202
+ };
203
+ /**
204
+ * Stream-render large documents in chunks for better performance
205
+ */
206
+ toHtmlStreamed(markdown: string, options?: {
207
+ chunkSize?: number;
208
+ onChunk?: (chunk: {
209
+ html: string;
210
+ progress: number;
211
+ }) => void;
212
+ }): Promise<string>;
102
213
  getExtensions(): string[];
103
214
  hasExtension(name: string): boolean;
104
215
  getWarnings(): string[];
105
216
  getDebugInfo(): DebugInfo | null;
106
217
  getPerformanceMetrics(): PerformanceMetrics | null;
218
+ /**
219
+ * Get cache statistics
220
+ */
221
+ getCacheStats(): {
222
+ parse: CacheStats;
223
+ render: CacheStats;
224
+ };
225
+ /**
226
+ * Clear all caches
227
+ */
228
+ clearCaches(): void;
229
+ /**
230
+ * Update cache capacity
231
+ */
232
+ setCacheSize(size: number): void;
107
233
  private rebuildParserAndRenderer;
108
234
  }
109
235
  declare function parseMarkdown(content: string): MarkdownToken[];
@@ -373,6 +499,7 @@ declare class MarkdownParser {
373
499
  private rules;
374
500
  private warnings;
375
501
  private config;
502
+ private compiledPatterns;
376
503
  constructor(config?: ParserConfig);
377
504
  addRule(rule: ParseRule): void;
378
505
  hasRule(name: string): boolean;
@@ -600,4 +727,4 @@ declare const presets: {
600
727
  */
601
728
  declare function createEngineWithPreset(presetName: keyof typeof presets, additionalConfig?: EngineConfig): ChangerawrMarkdown;
602
729
 
603
- export { type ASTStatistics, AlertExtension, BlockquoteExtension, BoldExtension, ButtonExtension, ChangerawrMarkdown, CodeBlockExtension, CoreExtensions, type DebugInfo, EmbedExtension, type EngineConfig, type EngineEvents, type Extension, ExtensionError, type Extension as ExtensionInterface, type ExtensionRegistration, HeadingExtension, HorizontalRuleExtension, ImageExtension, InlineCodeExtension, ItalicExtension, type JsonAstNode, LineBreakExtension, LinkExtension, ListExtension, Logger, MarkdownParseError, MarkdownParser, MarkdownRenderError, MarkdownRenderer, type MarkdownToken, type OutputFormat, ParagraphExtension, type ParseRule, type ParserConfig, type PerformanceMetrics, PerformanceTimer, type RenderRule, type RendererConfig, TaskListExtension, TextExtension, type TokenComparison, type TokenDifference, type TokenStatistics, type TokenType, astToJSONString, astToTokens, basicSanitize, compareTokens, createCoreOnlyEngine, createCumEngine, createCustomEngine, createDebugEngine, createEngine, createEngineWithPreset, createHTMLEngine, createMinimalEngine, createTailwindEngine, debounce, deepMerge, markdown as default, defaultTailwindClasses, escapeHtml, extractDomain, generateId, getASTStats, getTokenStats, isBrowser, isNode, isValidUrl, markdown, minimalClasses, parseASTFromJSON, parseCum, parseMarkdown, parseOptions, parseTokensFromJSON, presets, proseClasses, renderCum, renderCumToHtml, renderCumToJson, renderCumToTailwind, renderMarkdown, renderToAST, renderToHTML, renderToHTMLUnsafe, renderToHTMLWithConfig, renderToJSON, renderToTailwind, renderToTailwindWithClasses, renderToTailwindWithConfig, sanitizeHtml, tokensToAST, tokensToJSONString };
730
+ export { type ASTStatistics, AlertExtension, BlockquoteExtension, BoldExtension, ButtonExtension, type CacheStats, ChangerawrMarkdown, CodeBlockExtension, CoreExtensions, type DebugInfo, EmbedExtension, type EngineConfig, type EngineEvents, type Extension, ExtensionError, type Extension as ExtensionInterface, type ExtensionRegistration, HeadingExtension, HorizontalRuleExtension, ImageExtension, InlineCodeExtension, ItalicExtension, type JsonAstNode, LRUCache, LineBreakExtension, LinkExtension, ListExtension, Logger, MarkdownParseError, MarkdownParser, MarkdownRenderError, MarkdownRenderer, type MarkdownToken, type OutputFormat, ParagraphExtension, type ParseRule, type ParserConfig, type PerformanceMetrics, PerformanceTimer, type RenderMetrics, type RenderRule, type RendererConfig, TaskListExtension, TextExtension, type TokenComparison, type TokenDifference, type TokenStatistics, type TokenType, astToJSONString, astToTokens, basicSanitize, compareTokens, createCoreOnlyEngine, createCumEngine, createCustomEngine, createDebugEngine, createEngine, createEngineWithPreset, createHTMLEngine, createMinimalEngine, createTailwindEngine, debounce, deepMerge, markdown as default, defaultTailwindClasses, escapeHtml, extractDomain, generateId, getASTStats, getTokenStats, hashContent, isBrowser, isNode, isValidUrl, markdown, memoize, minimalClasses, parseASTFromJSON, parseCum, parseMarkdown, parseOptions, parseTokensFromJSON, presets, proseClasses, renderCum, renderCumToHtml, renderCumToJson, renderCumToTailwind, renderMarkdown, renderToAST, renderToHTML, renderToHTMLUnsafe, renderToHTMLWithConfig, renderToJSON, renderToTailwind, renderToTailwindWithClasses, renderToTailwindWithConfig, sanitizeHtml, tokensToAST, tokensToJSONString };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,84 @@
1
+ interface CacheStats {
2
+ size: number;
3
+ capacity: number;
4
+ hits: number;
5
+ misses: number;
6
+ hitRate: number;
7
+ evictions: number;
8
+ }
9
+ declare class LRUCache<K, V> {
10
+ private cache;
11
+ private capacity;
12
+ private hits;
13
+ private misses;
14
+ private evictions;
15
+ constructor(capacity?: number);
16
+ /**
17
+ * Get a value from the cache
18
+ */
19
+ get(key: K): V | undefined;
20
+ /**
21
+ * Set a value in the cache
22
+ */
23
+ set(key: K, value: V): void;
24
+ /**
25
+ * Check if a key exists in the cache
26
+ */
27
+ has(key: K): boolean;
28
+ /**
29
+ * Delete a specific key from the cache
30
+ */
31
+ delete(key: K): boolean;
32
+ /**
33
+ * Clear all entries from the cache
34
+ */
35
+ clear(): void;
36
+ /**
37
+ * Get the current size of the cache
38
+ */
39
+ get size(): number;
40
+ /**
41
+ * Get cache statistics
42
+ */
43
+ getStats(): CacheStats;
44
+ /**
45
+ * Reset cache statistics
46
+ */
47
+ resetStats(): void;
48
+ /**
49
+ * Get all keys in the cache
50
+ */
51
+ keys(): K[];
52
+ /**
53
+ * Get all values in the cache
54
+ */
55
+ values(): V[];
56
+ /**
57
+ * Update cache capacity and evict if necessary
58
+ */
59
+ setCapacity(newCapacity: number): void;
60
+ /**
61
+ * Evict the least recently used entry
62
+ */
63
+ private evictLRU;
64
+ }
65
+ /**
66
+ * Simple hash function for content
67
+ * Uses FNV-1a algorithm for fast, decent-quality hashing
68
+ */
69
+ declare function hashContent(content: string): string;
70
+ /**
71
+ * Create a cached version of a function
72
+ */
73
+ declare function memoize<T extends (...args: any[]) => any>(fn: T, options?: {
74
+ cache?: LRUCache<string, ReturnType<T>>;
75
+ keyGenerator?: (...args: Parameters<T>) => string;
76
+ maxSize?: number;
77
+ }): T & {
78
+ cache: LRUCache<string, ReturnType<T>>;
79
+ clearCache: () => void;
80
+ };
81
+
1
82
  interface MarkdownToken {
2
83
  type: string;
3
84
  content: string;
@@ -87,23 +168,68 @@ interface ExtensionRegistration {
87
168
  conflictingRules?: string[];
88
169
  }
89
170
 
171
+ interface RenderMetrics {
172
+ inputSize: number;
173
+ parseTime: number;
174
+ renderTime: number;
175
+ totalTime: number;
176
+ tokenCount: number;
177
+ cacheHit: boolean;
178
+ }
90
179
  declare class ChangerawrMarkdown {
91
180
  private parser;
92
181
  private renderer;
93
182
  private extensions;
183
+ private parseCache;
184
+ private renderCache;
185
+ private parseTime;
186
+ private renderTime;
187
+ private lastTokenCount;
94
188
  constructor(config?: EngineConfig);
95
189
  private registerFeatureExtensions;
96
190
  private registerCoreExtensions;
97
191
  registerExtension(extension: Extension): ExtensionRegistration;
98
192
  unregisterExtension(name: string): boolean;
99
193
  parse(markdown: string): MarkdownToken[];
100
- render(tokens: MarkdownToken[]): string;
194
+ render(tokens: MarkdownToken[], cacheKey?: string): string;
101
195
  toHtml(markdown: string): string;
196
+ /**
197
+ * Render markdown with performance metrics
198
+ */
199
+ toHtmlWithMetrics(markdown: string): {
200
+ html: string;
201
+ metrics: RenderMetrics;
202
+ };
203
+ /**
204
+ * Stream-render large documents in chunks for better performance
205
+ */
206
+ toHtmlStreamed(markdown: string, options?: {
207
+ chunkSize?: number;
208
+ onChunk?: (chunk: {
209
+ html: string;
210
+ progress: number;
211
+ }) => void;
212
+ }): Promise<string>;
102
213
  getExtensions(): string[];
103
214
  hasExtension(name: string): boolean;
104
215
  getWarnings(): string[];
105
216
  getDebugInfo(): DebugInfo | null;
106
217
  getPerformanceMetrics(): PerformanceMetrics | null;
218
+ /**
219
+ * Get cache statistics
220
+ */
221
+ getCacheStats(): {
222
+ parse: CacheStats;
223
+ render: CacheStats;
224
+ };
225
+ /**
226
+ * Clear all caches
227
+ */
228
+ clearCaches(): void;
229
+ /**
230
+ * Update cache capacity
231
+ */
232
+ setCacheSize(size: number): void;
107
233
  private rebuildParserAndRenderer;
108
234
  }
109
235
  declare function parseMarkdown(content: string): MarkdownToken[];
@@ -373,6 +499,7 @@ declare class MarkdownParser {
373
499
  private rules;
374
500
  private warnings;
375
501
  private config;
502
+ private compiledPatterns;
376
503
  constructor(config?: ParserConfig);
377
504
  addRule(rule: ParseRule): void;
378
505
  hasRule(name: string): boolean;
@@ -600,4 +727,4 @@ declare const presets: {
600
727
  */
601
728
  declare function createEngineWithPreset(presetName: keyof typeof presets, additionalConfig?: EngineConfig): ChangerawrMarkdown;
602
729
 
603
- export { type ASTStatistics, AlertExtension, BlockquoteExtension, BoldExtension, ButtonExtension, ChangerawrMarkdown, CodeBlockExtension, CoreExtensions, type DebugInfo, EmbedExtension, type EngineConfig, type EngineEvents, type Extension, ExtensionError, type Extension as ExtensionInterface, type ExtensionRegistration, HeadingExtension, HorizontalRuleExtension, ImageExtension, InlineCodeExtension, ItalicExtension, type JsonAstNode, LineBreakExtension, LinkExtension, ListExtension, Logger, MarkdownParseError, MarkdownParser, MarkdownRenderError, MarkdownRenderer, type MarkdownToken, type OutputFormat, ParagraphExtension, type ParseRule, type ParserConfig, type PerformanceMetrics, PerformanceTimer, type RenderRule, type RendererConfig, TaskListExtension, TextExtension, type TokenComparison, type TokenDifference, type TokenStatistics, type TokenType, astToJSONString, astToTokens, basicSanitize, compareTokens, createCoreOnlyEngine, createCumEngine, createCustomEngine, createDebugEngine, createEngine, createEngineWithPreset, createHTMLEngine, createMinimalEngine, createTailwindEngine, debounce, deepMerge, markdown as default, defaultTailwindClasses, escapeHtml, extractDomain, generateId, getASTStats, getTokenStats, isBrowser, isNode, isValidUrl, markdown, minimalClasses, parseASTFromJSON, parseCum, parseMarkdown, parseOptions, parseTokensFromJSON, presets, proseClasses, renderCum, renderCumToHtml, renderCumToJson, renderCumToTailwind, renderMarkdown, renderToAST, renderToHTML, renderToHTMLUnsafe, renderToHTMLWithConfig, renderToJSON, renderToTailwind, renderToTailwindWithClasses, renderToTailwindWithConfig, sanitizeHtml, tokensToAST, tokensToJSONString };
730
+ export { type ASTStatistics, AlertExtension, BlockquoteExtension, BoldExtension, ButtonExtension, type CacheStats, ChangerawrMarkdown, CodeBlockExtension, CoreExtensions, type DebugInfo, EmbedExtension, type EngineConfig, type EngineEvents, type Extension, ExtensionError, type Extension as ExtensionInterface, type ExtensionRegistration, HeadingExtension, HorizontalRuleExtension, ImageExtension, InlineCodeExtension, ItalicExtension, type JsonAstNode, LRUCache, LineBreakExtension, LinkExtension, ListExtension, Logger, MarkdownParseError, MarkdownParser, MarkdownRenderError, MarkdownRenderer, type MarkdownToken, type OutputFormat, ParagraphExtension, type ParseRule, type ParserConfig, type PerformanceMetrics, PerformanceTimer, type RenderMetrics, type RenderRule, type RendererConfig, TaskListExtension, TextExtension, type TokenComparison, type TokenDifference, type TokenStatistics, type TokenType, astToJSONString, astToTokens, basicSanitize, compareTokens, createCoreOnlyEngine, createCumEngine, createCustomEngine, createDebugEngine, createEngine, createEngineWithPreset, createHTMLEngine, createMinimalEngine, createTailwindEngine, debounce, deepMerge, markdown as default, defaultTailwindClasses, escapeHtml, extractDomain, generateId, getASTStats, getTokenStats, hashContent, isBrowser, isNode, isValidUrl, markdown, memoize, minimalClasses, parseASTFromJSON, parseCum, parseMarkdown, parseOptions, parseTokensFromJSON, presets, proseClasses, renderCum, renderCumToHtml, renderCumToJson, renderCumToTailwind, renderMarkdown, renderToAST, renderToHTML, renderToHTMLUnsafe, renderToHTMLWithConfig, renderToJSON, renderToTailwind, renderToTailwindWithClasses, renderToTailwindWithConfig, sanitizeHtml, tokensToAST, tokensToJSONString };