@sc4rfurryx/proteusjs 1.0.0

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.
Files changed (82) hide show
  1. package/API.md +438 -0
  2. package/FEATURES.md +286 -0
  3. package/LICENSE +21 -0
  4. package/README.md +645 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/proteus.cjs.js +16014 -0
  7. package/dist/proteus.cjs.js.map +1 -0
  8. package/dist/proteus.d.ts +3018 -0
  9. package/dist/proteus.esm.js +16005 -0
  10. package/dist/proteus.esm.js.map +1 -0
  11. package/dist/proteus.esm.min.js +8 -0
  12. package/dist/proteus.esm.min.js.map +1 -0
  13. package/dist/proteus.js +16020 -0
  14. package/dist/proteus.js.map +1 -0
  15. package/dist/proteus.min.js +8 -0
  16. package/dist/proteus.min.js.map +1 -0
  17. package/package.json +98 -0
  18. package/src/__tests__/mvp-integration.test.ts +518 -0
  19. package/src/accessibility/AccessibilityEngine.ts +2106 -0
  20. package/src/accessibility/ScreenReaderSupport.ts +444 -0
  21. package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
  22. package/src/animations/FLIPAnimationSystem.ts +491 -0
  23. package/src/compatibility/BrowserCompatibility.ts +1076 -0
  24. package/src/containers/BreakpointSystem.ts +347 -0
  25. package/src/containers/ContainerBreakpoints.ts +726 -0
  26. package/src/containers/ContainerManager.ts +370 -0
  27. package/src/containers/ContainerUnits.ts +336 -0
  28. package/src/containers/ContextIsolation.ts +394 -0
  29. package/src/containers/ElementQueries.ts +411 -0
  30. package/src/containers/SmartContainer.ts +536 -0
  31. package/src/containers/SmartContainers.ts +376 -0
  32. package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
  33. package/src/containers/__tests__/SmartContainers.test.ts +281 -0
  34. package/src/content/ResponsiveImages.ts +570 -0
  35. package/src/core/EventSystem.ts +147 -0
  36. package/src/core/MemoryManager.ts +321 -0
  37. package/src/core/PerformanceMonitor.ts +238 -0
  38. package/src/core/PluginSystem.ts +275 -0
  39. package/src/core/ProteusJS.test.ts +164 -0
  40. package/src/core/ProteusJS.ts +962 -0
  41. package/src/developer/PerformanceProfiler.ts +567 -0
  42. package/src/developer/VisualDebuggingTools.ts +656 -0
  43. package/src/developer/ZeroConfigSystem.ts +593 -0
  44. package/src/index.ts +35 -0
  45. package/src/integration.test.ts +227 -0
  46. package/src/layout/AdaptiveGrid.ts +429 -0
  47. package/src/layout/ContentReordering.ts +532 -0
  48. package/src/layout/FlexboxEnhancer.ts +406 -0
  49. package/src/layout/FlowLayout.ts +545 -0
  50. package/src/layout/SpacingSystem.ts +512 -0
  51. package/src/observers/IntersectionObserverPolyfill.ts +289 -0
  52. package/src/observers/ObserverManager.ts +299 -0
  53. package/src/observers/ResizeObserverPolyfill.ts +179 -0
  54. package/src/performance/BatchDOMOperations.ts +519 -0
  55. package/src/performance/CSSOptimizationEngine.ts +646 -0
  56. package/src/performance/CacheOptimizationSystem.ts +601 -0
  57. package/src/performance/EfficientEventHandler.ts +740 -0
  58. package/src/performance/LazyEvaluationSystem.ts +532 -0
  59. package/src/performance/MemoryManagementSystem.ts +497 -0
  60. package/src/performance/PerformanceMonitor.ts +931 -0
  61. package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
  62. package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
  63. package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
  64. package/src/polyfills/BrowserPolyfills.ts +586 -0
  65. package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
  66. package/src/test/setup.ts +115 -0
  67. package/src/theming/SmartThemeSystem.ts +591 -0
  68. package/src/types/index.ts +134 -0
  69. package/src/typography/ClampScaling.ts +356 -0
  70. package/src/typography/FluidTypography.ts +759 -0
  71. package/src/typography/LineHeightOptimization.ts +430 -0
  72. package/src/typography/LineHeightOptimizer.ts +326 -0
  73. package/src/typography/TextFitting.ts +355 -0
  74. package/src/typography/TypographicScale.ts +428 -0
  75. package/src/typography/VerticalRhythm.ts +369 -0
  76. package/src/typography/__tests__/FluidTypography.test.ts +432 -0
  77. package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
  78. package/src/utils/Logger.ts +173 -0
  79. package/src/utils/debounce.ts +259 -0
  80. package/src/utils/performance.ts +371 -0
  81. package/src/utils/support.ts +106 -0
  82. package/src/utils/version.ts +24 -0
@@ -0,0 +1,179 @@
1
+ /**
2
+ * ResizeObserver Polyfill for ProteusJS
3
+ * Provides ResizeObserver functionality for browsers that don't support it
4
+ */
5
+
6
+ export interface ResizeObserverEntry {
7
+ target: Element;
8
+ contentRect: DOMRectReadOnly;
9
+ borderBoxSize?: ResizeObserverSize[];
10
+ contentBoxSize?: ResizeObserverSize[];
11
+ devicePixelContentBoxSize?: ResizeObserverSize[];
12
+ }
13
+
14
+ export interface ResizeObserverSize {
15
+ inlineSize: number;
16
+ blockSize: number;
17
+ }
18
+
19
+ export type ResizeObserverCallback = (entries: ResizeObserverEntry[]) => void;
20
+
21
+ export class ResizeObserverPolyfill {
22
+ private callback: ResizeObserverCallback;
23
+ private observedElements: Map<Element, { lastWidth: number; lastHeight: number }> = new Map();
24
+ private rafId: number | null = null;
25
+ private isObserving: boolean = false;
26
+
27
+ constructor(callback: ResizeObserverCallback) {
28
+ this.callback = callback;
29
+ }
30
+
31
+ /**
32
+ * Start observing an element for resize changes
33
+ */
34
+ public observe(element: Element, options?: ResizeObserverOptions): void {
35
+ if (this.observedElements.has(element)) return;
36
+
37
+ const rect = element.getBoundingClientRect();
38
+ this.observedElements.set(element, {
39
+ lastWidth: rect.width,
40
+ lastHeight: rect.height
41
+ });
42
+
43
+ if (!this.isObserving) {
44
+ this.startObserving();
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Stop observing an element
50
+ */
51
+ public unobserve(element: Element): void {
52
+ this.observedElements.delete(element);
53
+
54
+ if (this.observedElements.size === 0) {
55
+ this.stopObserving();
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Disconnect all observations
61
+ */
62
+ public disconnect(): void {
63
+ this.observedElements.clear();
64
+ this.stopObserving();
65
+ }
66
+
67
+ /**
68
+ * Start the polling mechanism
69
+ */
70
+ private startObserving(): void {
71
+ if (this.isObserving) return;
72
+
73
+ this.isObserving = true;
74
+ this.checkForChanges();
75
+ }
76
+
77
+ /**
78
+ * Stop the polling mechanism
79
+ */
80
+ private stopObserving(): void {
81
+ if (!this.isObserving) return;
82
+
83
+ this.isObserving = false;
84
+ if (this.rafId) {
85
+ cancelAnimationFrame(this.rafId);
86
+ this.rafId = null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Check for size changes in observed elements
92
+ */
93
+ private checkForChanges(): void {
94
+ if (!this.isObserving) return;
95
+
96
+ const changedEntries: ResizeObserverEntry[] = [];
97
+
98
+ this.observedElements.forEach((lastSize, element) => {
99
+ // Check if element is still in DOM
100
+ if (!document.contains(element)) {
101
+ this.observedElements.delete(element);
102
+ return;
103
+ }
104
+
105
+ const rect = element.getBoundingClientRect();
106
+ const currentWidth = rect.width;
107
+ const currentHeight = rect.height;
108
+
109
+ if (currentWidth !== lastSize.lastWidth || currentHeight !== lastSize.lastHeight) {
110
+ // Update stored size
111
+ this.observedElements.set(element, {
112
+ lastWidth: currentWidth,
113
+ lastHeight: currentHeight
114
+ });
115
+
116
+ // Create entry
117
+ const entry: ResizeObserverEntry = {
118
+ target: element,
119
+ contentRect: this.createDOMRectReadOnly(rect),
120
+ contentBoxSize: [{
121
+ inlineSize: currentWidth,
122
+ blockSize: currentHeight
123
+ }],
124
+ borderBoxSize: [{
125
+ inlineSize: currentWidth,
126
+ blockSize: currentHeight
127
+ }]
128
+ };
129
+
130
+ changedEntries.push(entry);
131
+ }
132
+ });
133
+
134
+ // Call callback if there are changes
135
+ if (changedEntries.length > 0) {
136
+ try {
137
+ this.callback(changedEntries);
138
+ } catch (error) {
139
+ console.error('ResizeObserver callback error:', error);
140
+ }
141
+ }
142
+
143
+ // Schedule next check
144
+ if (this.isObserving) {
145
+ this.rafId = requestAnimationFrame(() => this.checkForChanges());
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Create a DOMRectReadOnly-like object
151
+ */
152
+ private createDOMRectReadOnly(rect: DOMRect): DOMRectReadOnly {
153
+ return {
154
+ x: rect.x,
155
+ y: rect.y,
156
+ width: rect.width,
157
+ height: rect.height,
158
+ top: rect.top,
159
+ right: rect.right,
160
+ bottom: rect.bottom,
161
+ left: rect.left,
162
+ toJSON: () => ({
163
+ x: rect.x,
164
+ y: rect.y,
165
+ width: rect.width,
166
+ height: rect.height,
167
+ top: rect.top,
168
+ right: rect.right,
169
+ bottom: rect.bottom,
170
+ left: rect.left
171
+ })
172
+ };
173
+ }
174
+ }
175
+
176
+ // Add static method for feature detection
177
+ (ResizeObserverPolyfill as any).isSupported = (): boolean => {
178
+ return typeof ResizeObserver !== 'undefined';
179
+ };
@@ -0,0 +1,519 @@
1
+ /**
2
+ * Batch DOM Operations for ProteusJS
3
+ * Efficient DOM manipulation with read/write separation and layout thrashing prevention
4
+ */
5
+
6
+ export interface DOMOperation {
7
+ id: string;
8
+ type: 'read' | 'write';
9
+ element: Element;
10
+ operation: () => any;
11
+ priority: 'high' | 'normal' | 'low';
12
+ timestamp: number;
13
+ dependencies?: string[];
14
+ }
15
+
16
+ export interface BatchConfig {
17
+ maxBatchSize: number;
18
+ frameTimeLimit: number;
19
+ separateReadWrite: boolean;
20
+ measurePerformance: boolean;
21
+ autoFlush: boolean;
22
+ flushInterval: number;
23
+ }
24
+
25
+ export interface BatchMetrics {
26
+ totalOperations: number;
27
+ readOperations: number;
28
+ writeOperations: number;
29
+ batchesProcessed: number;
30
+ averageBatchTime: number;
31
+ layoutThrashes: number;
32
+ preventedThrashes: number;
33
+ }
34
+
35
+ export class BatchDOMOperations {
36
+ private config: Required<BatchConfig>;
37
+ private readQueue: DOMOperation[] = [];
38
+ private writeQueue: DOMOperation[] = [];
39
+ private processingQueue: DOMOperation[] = [];
40
+ private metrics: BatchMetrics;
41
+ private rafId: number | null = null;
42
+ private flushTimer: number | null = null;
43
+ private isProcessing: boolean = false;
44
+ private operationResults: Map<string, any> = new Map();
45
+
46
+ constructor(config: Partial<BatchConfig> = {}) {
47
+ this.config = {
48
+ maxBatchSize: 50,
49
+ frameTimeLimit: 16, // 60fps target
50
+ separateReadWrite: true,
51
+ measurePerformance: true,
52
+ autoFlush: true,
53
+ flushInterval: 100,
54
+ ...config
55
+ };
56
+
57
+ this.metrics = this.createInitialMetrics();
58
+
59
+ if (this.config.autoFlush) {
60
+ this.startAutoFlush();
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Queue a DOM read operation
66
+ */
67
+ public queueRead<T>(
68
+ element: Element,
69
+ operation: () => T,
70
+ priority: 'high' | 'normal' | 'low' = 'normal',
71
+ dependencies: string[] = []
72
+ ): Promise<T> {
73
+ const id = this.generateOperationId('read');
74
+
75
+ return new Promise((resolve, reject) => {
76
+ const domOperation: DOMOperation = {
77
+ id,
78
+ type: 'read',
79
+ element,
80
+ operation: () => {
81
+ try {
82
+ const result = operation();
83
+ this.operationResults.set(id, result);
84
+ resolve(result);
85
+ return result;
86
+ } catch (error) {
87
+ reject(error);
88
+ throw error;
89
+ }
90
+ },
91
+ priority,
92
+ timestamp: performance.now(),
93
+ dependencies
94
+ };
95
+
96
+ this.readQueue.push(domOperation);
97
+ this.scheduleProcessing();
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Queue a DOM write operation
103
+ */
104
+ public queueWrite(
105
+ element: Element,
106
+ operation: () => void,
107
+ priority: 'high' | 'normal' | 'low' = 'normal',
108
+ dependencies: string[] = []
109
+ ): Promise<void> {
110
+ const id = this.generateOperationId('write');
111
+
112
+ return new Promise((resolve, reject) => {
113
+ const domOperation: DOMOperation = {
114
+ id,
115
+ type: 'write',
116
+ element,
117
+ operation: () => {
118
+ try {
119
+ operation();
120
+ resolve();
121
+ } catch (error) {
122
+ reject(error);
123
+ throw error;
124
+ }
125
+ },
126
+ priority,
127
+ timestamp: performance.now(),
128
+ dependencies
129
+ };
130
+
131
+ this.writeQueue.push(domOperation);
132
+ this.scheduleProcessing();
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Batch multiple style changes
138
+ */
139
+ public batchStyles(
140
+ element: Element,
141
+ styles: Record<string, string>,
142
+ priority: 'high' | 'normal' | 'low' = 'normal'
143
+ ): Promise<void> {
144
+ return this.queueWrite(
145
+ element,
146
+ () => {
147
+ const htmlElement = element as HTMLElement;
148
+ Object.entries(styles).forEach(([property, value]) => {
149
+ htmlElement.style.setProperty(property, value);
150
+ });
151
+ },
152
+ priority
153
+ );
154
+ }
155
+
156
+ /**
157
+ * Batch multiple class changes
158
+ */
159
+ public batchClasses(
160
+ element: Element,
161
+ changes: { add?: string[]; remove?: string[]; toggle?: string[] },
162
+ priority: 'high' | 'normal' | 'low' = 'normal'
163
+ ): Promise<void> {
164
+ return this.queueWrite(
165
+ element,
166
+ () => {
167
+ if (changes.add) {
168
+ element.classList.add(...changes.add);
169
+ }
170
+ if (changes.remove) {
171
+ element.classList.remove(...changes.remove);
172
+ }
173
+ if (changes.toggle) {
174
+ changes.toggle.forEach(className => {
175
+ element.classList.toggle(className);
176
+ });
177
+ }
178
+ },
179
+ priority
180
+ );
181
+ }
182
+
183
+ /**
184
+ * Batch multiple attribute changes
185
+ */
186
+ public batchAttributes(
187
+ element: Element,
188
+ attributes: Record<string, string | null>,
189
+ priority: 'high' | 'normal' | 'low' = 'normal'
190
+ ): Promise<void> {
191
+ return this.queueWrite(
192
+ element,
193
+ () => {
194
+ Object.entries(attributes).forEach(([name, value]) => {
195
+ if (value === null) {
196
+ element.removeAttribute(name);
197
+ } else {
198
+ element.setAttribute(name, value);
199
+ }
200
+ });
201
+ },
202
+ priority
203
+ );
204
+ }
205
+
206
+ /**
207
+ * Read multiple properties efficiently
208
+ */
209
+ public batchReads<T extends Record<string, () => any>>(
210
+ element: Element,
211
+ readers: T,
212
+ priority: 'high' | 'normal' | 'low' = 'normal'
213
+ ): Promise<{ [K in keyof T]: ReturnType<T[K]> }> {
214
+ return this.queueRead(
215
+ element,
216
+ () => {
217
+ const results = {} as { [K in keyof T]: ReturnType<T[K]> };
218
+ Object.entries(readers).forEach(([key, reader]) => {
219
+ results[key as keyof T] = (reader as () => any)();
220
+ });
221
+ return results;
222
+ },
223
+ priority
224
+ );
225
+ }
226
+
227
+ /**
228
+ * Measure element dimensions efficiently
229
+ */
230
+ public measureElement(
231
+ element: Element,
232
+ measurements: ('width' | 'height' | 'top' | 'left' | 'right' | 'bottom')[] = ['width', 'height'],
233
+ priority: 'high' | 'normal' | 'low' = 'normal'
234
+ ): Promise<Partial<DOMRect>> {
235
+ return this.queueRead(
236
+ element,
237
+ () => {
238
+ const rect = element.getBoundingClientRect();
239
+ const result: Partial<DOMRect> = {};
240
+
241
+ measurements.forEach(measurement => {
242
+ (result as any)[measurement] = rect[measurement as keyof DOMRect];
243
+ });
244
+
245
+ return result;
246
+ },
247
+ priority
248
+ );
249
+ }
250
+
251
+ /**
252
+ * Force flush all queued operations
253
+ */
254
+ public flush(): Promise<void> {
255
+ return new Promise((resolve) => {
256
+ this.processOperations(true).then(() => {
257
+ resolve();
258
+ });
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Get current batch metrics
264
+ */
265
+ public getMetrics(): BatchMetrics {
266
+ return { ...this.metrics };
267
+ }
268
+
269
+ /**
270
+ * Clear all queues
271
+ */
272
+ public clear(): void {
273
+ this.readQueue = [];
274
+ this.writeQueue = [];
275
+ this.processingQueue = [];
276
+ this.operationResults.clear();
277
+ }
278
+
279
+ /**
280
+ * Destroy the batch processor
281
+ */
282
+ public destroy(): void {
283
+ this.stopAutoFlush();
284
+ this.stopProcessing();
285
+ this.clear();
286
+ }
287
+
288
+ /**
289
+ * Schedule processing of queued operations
290
+ */
291
+ private scheduleProcessing(): void {
292
+ if (this.isProcessing || this.rafId) {
293
+ return;
294
+ }
295
+
296
+ this.rafId = requestAnimationFrame(() => {
297
+ this.processOperations();
298
+ this.rafId = null;
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Process queued operations with read/write separation
304
+ */
305
+ private async processOperations(forceFlush: boolean = false): Promise<void> {
306
+ if (this.isProcessing) {
307
+ return;
308
+ }
309
+
310
+ this.isProcessing = true;
311
+ const startTime = performance.now();
312
+
313
+ try {
314
+ if (this.config.separateReadWrite) {
315
+ // Process all reads first to avoid layout thrashing
316
+ await this.processQueue(this.readQueue, 'read', forceFlush);
317
+
318
+ // Then process all writes
319
+ await this.processQueue(this.writeQueue, 'write', forceFlush);
320
+ } else {
321
+ // Process mixed operations (less efficient but simpler)
322
+ const allOperations = [...this.readQueue, ...this.writeQueue];
323
+ this.readQueue = [];
324
+ this.writeQueue = [];
325
+
326
+ await this.processQueue(allOperations, 'mixed', forceFlush);
327
+ }
328
+
329
+ // Update metrics
330
+ const processingTime = performance.now() - startTime;
331
+ this.updateMetrics(processingTime);
332
+
333
+ } finally {
334
+ this.isProcessing = false;
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Process a specific queue of operations
340
+ */
341
+ private async processQueue(
342
+ queue: DOMOperation[],
343
+ queueType: 'read' | 'write' | 'mixed',
344
+ forceFlush: boolean
345
+ ): Promise<void> {
346
+ if (queue.length === 0) {
347
+ return;
348
+ }
349
+
350
+ // Sort by priority and dependencies
351
+ const sortedOperations = this.sortOperations(queue);
352
+ const budget = forceFlush ? Infinity : this.config.frameTimeLimit;
353
+ const startTime = performance.now();
354
+ let processedCount = 0;
355
+
356
+ for (const operation of sortedOperations) {
357
+ // Check time budget
358
+ const elapsed = performance.now() - startTime;
359
+ if (!forceFlush && elapsed > budget) {
360
+ break;
361
+ }
362
+
363
+ // Check dependencies
364
+ if (!this.areDependenciesSatisfied(operation)) {
365
+ continue;
366
+ }
367
+
368
+ // Check batch size limit
369
+ if (!forceFlush && processedCount >= this.config.maxBatchSize) {
370
+ break;
371
+ }
372
+
373
+ try {
374
+ // Detect potential layout thrashing
375
+ if (this.config.measurePerformance && this.wouldCauseLayoutThrash(operation, queueType)) {
376
+ this.metrics.layoutThrashes++;
377
+ }
378
+
379
+ // Execute operation
380
+ operation.operation();
381
+ processedCount++;
382
+
383
+ // Update metrics
384
+ if (operation.type === 'read') {
385
+ this.metrics.readOperations++;
386
+ } else {
387
+ this.metrics.writeOperations++;
388
+ }
389
+
390
+ } catch (error) {
391
+ console.error('Error processing DOM operation:', error);
392
+ }
393
+
394
+ // Remove from queue
395
+ const index = queue.indexOf(operation);
396
+ if (index > -1) {
397
+ queue.splice(index, 1);
398
+ }
399
+ }
400
+
401
+ this.metrics.totalOperations += processedCount;
402
+ }
403
+
404
+ /**
405
+ * Sort operations by priority and dependencies
406
+ */
407
+ private sortOperations(operations: DOMOperation[]): DOMOperation[] {
408
+ return operations.sort((a, b) => {
409
+ // Priority first
410
+ const priorityOrder = { high: 0, normal: 1, low: 2 };
411
+ const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
412
+
413
+ if (priorityDiff !== 0) {
414
+ return priorityDiff;
415
+ }
416
+
417
+ // Then by timestamp (older first)
418
+ return a.timestamp - b.timestamp;
419
+ });
420
+ }
421
+
422
+ /**
423
+ * Check if operation dependencies are satisfied
424
+ */
425
+ private areDependenciesSatisfied(operation: DOMOperation): boolean {
426
+ if (!operation.dependencies || operation.dependencies.length === 0) {
427
+ return true;
428
+ }
429
+
430
+ return operation.dependencies.every(depId =>
431
+ this.operationResults.has(depId)
432
+ );
433
+ }
434
+
435
+ /**
436
+ * Detect potential layout thrashing
437
+ */
438
+ private wouldCauseLayoutThrash(operation: DOMOperation, queueType: string): boolean {
439
+ // Simplified heuristic: write after read in same frame might cause thrashing
440
+ if (queueType === 'mixed' && operation.type === 'write') {
441
+ const recentReads = this.processingQueue.filter(op =>
442
+ op.type === 'read' &&
443
+ performance.now() - op.timestamp < 16
444
+ );
445
+
446
+ return recentReads.length > 0;
447
+ }
448
+
449
+ return false;
450
+ }
451
+
452
+ /**
453
+ * Generate unique operation ID
454
+ */
455
+ private generateOperationId(type: string): string {
456
+ return `${type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
457
+ }
458
+
459
+ /**
460
+ * Start auto-flush timer
461
+ */
462
+ private startAutoFlush(): void {
463
+ this.flushTimer = window.setInterval(() => {
464
+ if (this.readQueue.length > 0 || this.writeQueue.length > 0) {
465
+ this.scheduleProcessing();
466
+ }
467
+ }, this.config.flushInterval);
468
+ }
469
+
470
+ /**
471
+ * Stop auto-flush timer
472
+ */
473
+ private stopAutoFlush(): void {
474
+ if (this.flushTimer) {
475
+ clearInterval(this.flushTimer);
476
+ this.flushTimer = null;
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Stop processing
482
+ */
483
+ private stopProcessing(): void {
484
+ if (this.rafId) {
485
+ cancelAnimationFrame(this.rafId);
486
+ this.rafId = null;
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Update performance metrics
492
+ */
493
+ private updateMetrics(processingTime: number): void {
494
+ this.metrics.batchesProcessed++;
495
+ this.metrics.averageBatchTime =
496
+ (this.metrics.averageBatchTime + processingTime) / 2;
497
+
498
+ // Calculate prevented thrashes (simplified)
499
+ if (this.config.separateReadWrite) {
500
+ const potentialThrashes = Math.min(this.readQueue.length, this.writeQueue.length);
501
+ this.metrics.preventedThrashes += potentialThrashes;
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Create initial metrics
507
+ */
508
+ private createInitialMetrics(): BatchMetrics {
509
+ return {
510
+ totalOperations: 0,
511
+ readOperations: 0,
512
+ writeOperations: 0,
513
+ batchesProcessed: 0,
514
+ averageBatchTime: 0,
515
+ layoutThrashes: 0,
516
+ preventedThrashes: 0
517
+ };
518
+ }
519
+ }