@smartnet360/svelte-components 0.0.123 → 0.0.125
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/dist/apps/antenna-tools/components/AntennaControls.svelte +71 -9
- package/dist/apps/antenna-tools/components/AntennaControls.svelte.d.ts +2 -0
- package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +4 -174
- package/dist/apps/antenna-tools/components/AntennaTools.svelte +48 -82
- package/dist/apps/antenna-tools/components/DatabaseViewer.svelte +5 -8
- package/dist/apps/antenna-tools/components/MSIConverter.svelte +377 -52
- package/dist/apps/antenna-tools/db.js +4 -0
- package/dist/apps/antenna-tools/utils/db-utils.d.ts +19 -0
- package/dist/apps/antenna-tools/utils/db-utils.js +108 -0
- package/dist/apps/antenna-tools/utils/msi-parser.d.ts +35 -1
- package/dist/apps/antenna-tools/utils/msi-parser.js +105 -35
- package/dist/core/Auth/LoginForm.svelte +397 -0
- package/dist/core/Auth/LoginForm.svelte.d.ts +16 -0
- package/dist/core/Auth/auth.svelte.d.ts +22 -0
- package/dist/core/Auth/auth.svelte.js +184 -0
- package/dist/core/Auth/config.d.ts +25 -0
- package/dist/core/Auth/config.js +256 -0
- package/dist/core/Auth/index.d.ts +4 -0
- package/dist/core/Auth/index.js +5 -0
- package/dist/core/Auth/types.d.ts +140 -0
- package/dist/core/Auth/types.js +2 -0
- package/dist/core/Benchmark/Benchmark.svelte +662 -0
- package/dist/core/Benchmark/Benchmark.svelte.d.ts +3 -0
- package/dist/core/Benchmark/benchmark-utils.d.ts +48 -0
- package/dist/core/Benchmark/benchmark-utils.js +80 -0
- package/dist/core/Benchmark/index.d.ts +2 -0
- package/dist/core/Benchmark/index.js +3 -0
- package/dist/core/LandingPage/App.svelte +102 -0
- package/dist/core/LandingPage/App.svelte.d.ts +20 -0
- package/dist/core/LandingPage/LandingPage.svelte +480 -0
- package/dist/core/LandingPage/LandingPage.svelte.d.ts +21 -0
- package/dist/core/LandingPage/index.d.ts +2 -0
- package/dist/core/LandingPage/index.js +3 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
<svelte:options runes={true} />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { tick } from 'svelte';
|
|
5
|
+
import {
|
|
6
|
+
generateTestData,
|
|
7
|
+
runBenchmark,
|
|
8
|
+
formatDuration,
|
|
9
|
+
type BenchmarkResult,
|
|
10
|
+
type TestItem
|
|
11
|
+
} from './benchmark-utils';
|
|
12
|
+
|
|
13
|
+
// Test configuration
|
|
14
|
+
const DATA_SIZES = [100, 1000, 5000, 10000];
|
|
15
|
+
|
|
16
|
+
// Reactive state
|
|
17
|
+
let selectedSize = $state(1000);
|
|
18
|
+
let items = $state<TestItem[]>([]);
|
|
19
|
+
let results = $state<BenchmarkResult[]>([]);
|
|
20
|
+
let isRunning = $state(false);
|
|
21
|
+
let currentTest = $state('');
|
|
22
|
+
let filterValue = $state(500);
|
|
23
|
+
|
|
24
|
+
// Derived state - this is what we want to benchmark
|
|
25
|
+
let filteredItems = $derived(items.filter(item => item.value > filterValue));
|
|
26
|
+
let activeItems = $derived(items.filter(item => item.active));
|
|
27
|
+
let totalValue = $derived(items.reduce((sum, item) => sum + item.value, 0));
|
|
28
|
+
let groupedByCategory = $derived(() => {
|
|
29
|
+
const groups = new Map<string, TestItem[]>();
|
|
30
|
+
for (const item of items) {
|
|
31
|
+
const existing = groups.get(item.category) || [];
|
|
32
|
+
existing.push(item);
|
|
33
|
+
groups.set(item.category, existing);
|
|
34
|
+
}
|
|
35
|
+
return groups;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function log(message: string, data?: any) {
|
|
39
|
+
const timestamp = new Date().toISOString().split('T')[1].slice(0, -1);
|
|
40
|
+
if (data !== undefined) {
|
|
41
|
+
console.log(`%c[${timestamp}] ${message}`, 'color: #2196F3;', data);
|
|
42
|
+
} else {
|
|
43
|
+
console.log(`%c[${timestamp}] ${message}`, 'color: #2196F3;');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Wait for Svelte to finish updating the DOM */
|
|
48
|
+
async function waitForRender(): Promise<void> {
|
|
49
|
+
await tick(); // Wait for Svelte's microtask queue
|
|
50
|
+
return new Promise(resolve => {
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
requestAnimationFrame(() => {
|
|
53
|
+
resolve(); // Double rAF ensures paint is complete
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Measure REAL time including DOM updates */
|
|
60
|
+
async function measureReal<T>(name: string, fn: () => T, itemCount: number): Promise<BenchmarkResult> {
|
|
61
|
+
const start = performance.now();
|
|
62
|
+
|
|
63
|
+
log(`[REAL] Starting: ${name}`);
|
|
64
|
+
|
|
65
|
+
// Execute the function
|
|
66
|
+
const result = fn();
|
|
67
|
+
const jsTime = performance.now() - start;
|
|
68
|
+
|
|
69
|
+
log(`[REAL] JS execution: ${formatDuration(jsTime)}`);
|
|
70
|
+
|
|
71
|
+
// Wait for Svelte + DOM + Paint
|
|
72
|
+
await waitForRender();
|
|
73
|
+
|
|
74
|
+
const totalTime = performance.now() - start;
|
|
75
|
+
|
|
76
|
+
log(`[REAL] Total time (including render): ${formatDuration(totalTime)}`);
|
|
77
|
+
log(`[REAL] Render overhead: ${formatDuration(totalTime - jsTime)}`);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
name: `${name} (REAL)`,
|
|
81
|
+
duration: totalTime,
|
|
82
|
+
itemCount,
|
|
83
|
+
opsPerSecond: (itemCount / totalTime) * 1000,
|
|
84
|
+
timestamp: new Date()
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clearResults() {
|
|
89
|
+
results = [];
|
|
90
|
+
items = [];
|
|
91
|
+
log('Results cleared');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Test 1: Initial data generation
|
|
95
|
+
function testDataGeneration() {
|
|
96
|
+
currentTest = 'Data Generation';
|
|
97
|
+
log(`Starting: ${currentTest} with ${selectedSize} items`);
|
|
98
|
+
|
|
99
|
+
const result = runBenchmark(
|
|
100
|
+
`Generate ${selectedSize} items`,
|
|
101
|
+
() => {
|
|
102
|
+
items = generateTestData(selectedSize);
|
|
103
|
+
},
|
|
104
|
+
selectedSize
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
results = [...results, result];
|
|
108
|
+
log(`Completed: ${currentTest}`, { itemCount: items.length });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Test 2: Filter operation (triggers $derived)
|
|
112
|
+
function testFilterOperation() {
|
|
113
|
+
if (items.length === 0) {
|
|
114
|
+
log('ERROR: No items loaded. Run data generation first.');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
currentTest = 'Filter Operation';
|
|
119
|
+
log(`Starting: ${currentTest}`);
|
|
120
|
+
|
|
121
|
+
const start = performance.now();
|
|
122
|
+
|
|
123
|
+
// Change filter value to trigger $derived recalculation
|
|
124
|
+
filterValue = Math.random() * 1000;
|
|
125
|
+
|
|
126
|
+
// Access the derived value to force computation
|
|
127
|
+
const count = filteredItems.length;
|
|
128
|
+
|
|
129
|
+
const duration = performance.now() - start;
|
|
130
|
+
|
|
131
|
+
const result: BenchmarkResult = {
|
|
132
|
+
name: `Filter items (threshold: ${filterValue.toFixed(0)})`,
|
|
133
|
+
duration,
|
|
134
|
+
itemCount: items.length,
|
|
135
|
+
opsPerSecond: (items.length / duration) * 1000,
|
|
136
|
+
timestamp: new Date()
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
results = [...results, result];
|
|
140
|
+
log(`Completed: ${currentTest}`, { filtered: count, total: items.length });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Test 3: Update single item
|
|
144
|
+
function testSingleUpdate() {
|
|
145
|
+
if (items.length === 0) {
|
|
146
|
+
log('ERROR: No items loaded. Run data generation first.');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
currentTest = 'Single Item Update';
|
|
151
|
+
log(`Starting: ${currentTest}`);
|
|
152
|
+
|
|
153
|
+
const result = runBenchmark(
|
|
154
|
+
'Update single item',
|
|
155
|
+
() => {
|
|
156
|
+
const index = Math.floor(Math.random() * items.length);
|
|
157
|
+
items[index] = { ...items[index], value: Math.random() * 1000 };
|
|
158
|
+
},
|
|
159
|
+
1
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
results = [...results, result];
|
|
163
|
+
log(`Completed: ${currentTest}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Test 4: Bulk update (update all items)
|
|
167
|
+
function testBulkUpdate() {
|
|
168
|
+
if (items.length === 0) {
|
|
169
|
+
log('ERROR: No items loaded. Run data generation first.');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
currentTest = 'Bulk Update';
|
|
174
|
+
log(`Starting: ${currentTest} for ${items.length} items`);
|
|
175
|
+
|
|
176
|
+
const result = runBenchmark(
|
|
177
|
+
`Bulk update ${items.length} items`,
|
|
178
|
+
() => {
|
|
179
|
+
items = items.map(item => ({
|
|
180
|
+
...item,
|
|
181
|
+
value: item.value * 1.1,
|
|
182
|
+
active: !item.active
|
|
183
|
+
}));
|
|
184
|
+
},
|
|
185
|
+
items.length
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
results = [...results, result];
|
|
189
|
+
log(`Completed: ${currentTest}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Test 5: Array push operations
|
|
193
|
+
function testArrayPush() {
|
|
194
|
+
currentTest = 'Array Push';
|
|
195
|
+
log(`Starting: ${currentTest}`);
|
|
196
|
+
|
|
197
|
+
const pushCount = 1000;
|
|
198
|
+
const result = runBenchmark(
|
|
199
|
+
`Push ${pushCount} items`,
|
|
200
|
+
() => {
|
|
201
|
+
for (let i = 0; i < pushCount; i++) {
|
|
202
|
+
items = [...items, {
|
|
203
|
+
id: items.length,
|
|
204
|
+
name: `New Item ${items.length}`,
|
|
205
|
+
value: Math.random() * 1000,
|
|
206
|
+
category: 'NEW',
|
|
207
|
+
active: true,
|
|
208
|
+
data: [1, 2, 3]
|
|
209
|
+
}];
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
pushCount
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
results = [...results, result];
|
|
216
|
+
log(`Completed: ${currentTest}`, { totalItems: items.length });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Test 6: Derived with grouping (heavy operation)
|
|
220
|
+
function testGroupingDerived() {
|
|
221
|
+
if (items.length === 0) {
|
|
222
|
+
log('ERROR: No items loaded. Run data generation first.');
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
currentTest = 'Grouping Derived';
|
|
227
|
+
log(`Starting: ${currentTest}`);
|
|
228
|
+
|
|
229
|
+
const start = performance.now();
|
|
230
|
+
|
|
231
|
+
// Access the grouped derived to force computation
|
|
232
|
+
const groups = groupedByCategory();
|
|
233
|
+
const groupCount = groups.size;
|
|
234
|
+
|
|
235
|
+
const duration = performance.now() - start;
|
|
236
|
+
|
|
237
|
+
const result: BenchmarkResult = {
|
|
238
|
+
name: `Group ${items.length} items into categories`,
|
|
239
|
+
duration,
|
|
240
|
+
itemCount: items.length,
|
|
241
|
+
opsPerSecond: (items.length / duration) * 1000,
|
|
242
|
+
timestamp: new Date()
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
results = [...results, result];
|
|
246
|
+
log(`Completed: ${currentTest}`, { groups: groupCount });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Test 7: Multiple derived access (simulates real component)
|
|
250
|
+
function testMultipleDerivedAccess() {
|
|
251
|
+
if (items.length === 0) {
|
|
252
|
+
log('ERROR: No items loaded. Run data generation first.');
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
currentTest = 'Multiple Derived Access';
|
|
257
|
+
log(`Starting: ${currentTest}`);
|
|
258
|
+
|
|
259
|
+
const iterations = 100;
|
|
260
|
+
const start = performance.now();
|
|
261
|
+
|
|
262
|
+
for (let i = 0; i < iterations; i++) {
|
|
263
|
+
// Access multiple derived values (like a component would during render)
|
|
264
|
+
const _f = filteredItems.length;
|
|
265
|
+
const _a = activeItems.length;
|
|
266
|
+
const _t = totalValue;
|
|
267
|
+
const _g = groupedByCategory().size;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const duration = performance.now() - start;
|
|
271
|
+
|
|
272
|
+
const result: BenchmarkResult = {
|
|
273
|
+
name: `Access 4 derived values ${iterations}x`,
|
|
274
|
+
duration,
|
|
275
|
+
itemCount: iterations * 4,
|
|
276
|
+
opsPerSecond: ((iterations * 4) / duration) * 1000,
|
|
277
|
+
timestamp: new Date()
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
results = [...results, result];
|
|
281
|
+
log(`Completed: ${currentTest}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ============================================
|
|
285
|
+
// REAL TIME TESTS (including DOM updates)
|
|
286
|
+
// ============================================
|
|
287
|
+
|
|
288
|
+
// Test R1: Generate data and wait for render
|
|
289
|
+
async function testRealDataGeneration() {
|
|
290
|
+
currentTest = 'REAL: Data Generation + Render';
|
|
291
|
+
const result = await measureReal(
|
|
292
|
+
`Generate ${selectedSize} items + render`,
|
|
293
|
+
() => {
|
|
294
|
+
items = generateTestData(selectedSize);
|
|
295
|
+
},
|
|
296
|
+
selectedSize
|
|
297
|
+
);
|
|
298
|
+
results = [...results, result];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Test R2: Bulk update and wait for render
|
|
302
|
+
async function testRealBulkUpdate() {
|
|
303
|
+
if (items.length === 0) {
|
|
304
|
+
log('ERROR: No items loaded.');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
currentTest = 'REAL: Bulk Update + Render';
|
|
308
|
+
const result = await measureReal(
|
|
309
|
+
`Bulk update ${items.length} items + render`,
|
|
310
|
+
() => {
|
|
311
|
+
items = items.map(item => ({
|
|
312
|
+
...item,
|
|
313
|
+
value: item.value * 1.1,
|
|
314
|
+
active: !item.active
|
|
315
|
+
}));
|
|
316
|
+
},
|
|
317
|
+
items.length
|
|
318
|
+
);
|
|
319
|
+
results = [...results, result];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Test R3: Filter change and wait for render
|
|
323
|
+
async function testRealFilterChange() {
|
|
324
|
+
if (items.length === 0) {
|
|
325
|
+
log('ERROR: No items loaded.');
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
currentTest = 'REAL: Filter Change + Render';
|
|
329
|
+
const result = await measureReal(
|
|
330
|
+
'Filter change + render',
|
|
331
|
+
() => {
|
|
332
|
+
filterValue = Math.random() * 1000;
|
|
333
|
+
// Force access to trigger derived
|
|
334
|
+
const _ = filteredItems.length;
|
|
335
|
+
},
|
|
336
|
+
items.length
|
|
337
|
+
);
|
|
338
|
+
results = [...results, result];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Test R4: Array push (1000 individual reactive updates) - THE SLOW ONE
|
|
342
|
+
async function testRealArrayPush() {
|
|
343
|
+
currentTest = 'REAL: Array Push (1000 individual updates)';
|
|
344
|
+
const pushCount = 1000;
|
|
345
|
+
|
|
346
|
+
const result = await measureReal(
|
|
347
|
+
`Push ${pushCount} items individually + render`,
|
|
348
|
+
() => {
|
|
349
|
+
for (let i = 0; i < pushCount; i++) {
|
|
350
|
+
items = [...items, {
|
|
351
|
+
id: items.length,
|
|
352
|
+
name: `New Item ${items.length}`,
|
|
353
|
+
value: Math.random() * 1000,
|
|
354
|
+
category: 'NEW',
|
|
355
|
+
active: true,
|
|
356
|
+
data: [1, 2, 3]
|
|
357
|
+
}];
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
pushCount
|
|
361
|
+
);
|
|
362
|
+
results = [...results, result];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Run all tests
|
|
366
|
+
async function runAllTests() {
|
|
367
|
+
isRunning = true;
|
|
368
|
+
results = [];
|
|
369
|
+
items = [];
|
|
370
|
+
|
|
371
|
+
log('=== STARTING FULL BENCHMARK SUITE ===');
|
|
372
|
+
log(`Data size: ${selectedSize} items`);
|
|
373
|
+
log(`Environment: ${import.meta.env.DEV ? 'DEVELOPMENT' : 'PRODUCTION'}`);
|
|
374
|
+
log(`User Agent: ${navigator.userAgent}`);
|
|
375
|
+
|
|
376
|
+
// Sync tests (JS only)
|
|
377
|
+
const syncTests = [
|
|
378
|
+
testDataGeneration,
|
|
379
|
+
testFilterOperation,
|
|
380
|
+
testSingleUpdate,
|
|
381
|
+
testBulkUpdate,
|
|
382
|
+
testGroupingDerived,
|
|
383
|
+
testMultipleDerivedAccess,
|
|
384
|
+
testArrayPush,
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
log('--- Running JS-only tests ---');
|
|
388
|
+
for (const test of syncTests) {
|
|
389
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
390
|
+
test();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Reset for real tests
|
|
394
|
+
items = [];
|
|
395
|
+
await waitForRender();
|
|
396
|
+
|
|
397
|
+
log('--- Running REAL tests (including render) ---');
|
|
398
|
+
|
|
399
|
+
// Async tests (including render)
|
|
400
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
401
|
+
await testRealDataGeneration();
|
|
402
|
+
|
|
403
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
404
|
+
await testRealBulkUpdate();
|
|
405
|
+
|
|
406
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
407
|
+
await testRealFilterChange();
|
|
408
|
+
|
|
409
|
+
log('=== BENCHMARK SUITE COMPLETED ===');
|
|
410
|
+
logSummary();
|
|
411
|
+
|
|
412
|
+
isRunning = false;
|
|
413
|
+
currentTest = '';
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Run only REAL tests (including the slow array push)
|
|
417
|
+
async function runRealTests() {
|
|
418
|
+
isRunning = true;
|
|
419
|
+
results = [];
|
|
420
|
+
items = [];
|
|
421
|
+
|
|
422
|
+
log('=== STARTING REAL-TIME BENCHMARK ===');
|
|
423
|
+
log(`Data size: ${selectedSize} items`);
|
|
424
|
+
log(`Environment: ${import.meta.env.DEV ? 'DEVELOPMENT' : 'PRODUCTION'}`);
|
|
425
|
+
|
|
426
|
+
await testRealDataGeneration();
|
|
427
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
428
|
+
|
|
429
|
+
await testRealBulkUpdate();
|
|
430
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
431
|
+
|
|
432
|
+
await testRealFilterChange();
|
|
433
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
434
|
+
|
|
435
|
+
// Reset and test array push
|
|
436
|
+
items = [];
|
|
437
|
+
await waitForRender();
|
|
438
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
439
|
+
|
|
440
|
+
await testRealArrayPush();
|
|
441
|
+
|
|
442
|
+
log('=== REAL-TIME BENCHMARK COMPLETED ===');
|
|
443
|
+
logSummary();
|
|
444
|
+
|
|
445
|
+
isRunning = false;
|
|
446
|
+
currentTest = '';
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function logSummary() {
|
|
450
|
+
console.log('%c\n=== BENCHMARK SUMMARY ===', 'color: #4CAF50; font-weight: bold; font-size: 14px;');
|
|
451
|
+
|
|
452
|
+
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
|
453
|
+
|
|
454
|
+
console.table(results.map(r => ({
|
|
455
|
+
Test: r.name,
|
|
456
|
+
Duration: formatDuration(r.duration),
|
|
457
|
+
Items: r.itemCount,
|
|
458
|
+
'Ops/sec': r.opsPerSecond > 1000
|
|
459
|
+
? `${(r.opsPerSecond / 1000).toFixed(1)}K`
|
|
460
|
+
: r.opsPerSecond.toFixed(1)
|
|
461
|
+
})));
|
|
462
|
+
|
|
463
|
+
console.log(`%cTotal time: ${formatDuration(totalDuration)}`, 'color: #FF9800; font-weight: bold;');
|
|
464
|
+
}
|
|
465
|
+
</script>
|
|
466
|
+
|
|
467
|
+
<div class="benchmark-container p-4">
|
|
468
|
+
<div class="card">
|
|
469
|
+
<div class="card-header bg-primary text-white">
|
|
470
|
+
<h5 class="mb-0">
|
|
471
|
+
<i class="bi bi-speedometer2 me-2"></i>
|
|
472
|
+
Svelte Reactivity Benchmark
|
|
473
|
+
</h5>
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
<div class="card-body">
|
|
477
|
+
<!-- Configuration -->
|
|
478
|
+
<div class="row mb-4">
|
|
479
|
+
<div class="col-md-6">
|
|
480
|
+
<label class="form-label fw-bold">Data Size</label>
|
|
481
|
+
<div class="btn-group w-100" role="group">
|
|
482
|
+
{#each DATA_SIZES as size}
|
|
483
|
+
<button
|
|
484
|
+
type="button"
|
|
485
|
+
class="btn {selectedSize === size ? 'btn-primary' : 'btn-outline-primary'}"
|
|
486
|
+
onclick={() => selectedSize = size}
|
|
487
|
+
disabled={isRunning}
|
|
488
|
+
>
|
|
489
|
+
{size.toLocaleString()}
|
|
490
|
+
</button>
|
|
491
|
+
{/each}
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
<div class="col-md-6">
|
|
495
|
+
<label class="form-label fw-bold">Environment</label>
|
|
496
|
+
<div class="alert alert-info py-2 mb-0">
|
|
497
|
+
<strong>{import.meta.env.DEV ? '🔧 DEVELOPMENT' : '🚀 PRODUCTION'}</strong>
|
|
498
|
+
<br>
|
|
499
|
+
<small class="text-muted">Items loaded: {items.length.toLocaleString()}</small>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
|
|
504
|
+
<!-- Test Buttons -->
|
|
505
|
+
<div class="row g-2 mb-4">
|
|
506
|
+
<div class="col-12">
|
|
507
|
+
<label class="form-label fw-bold">Individual Tests</label>
|
|
508
|
+
</div>
|
|
509
|
+
<div class="col-auto">
|
|
510
|
+
<button class="btn btn-outline-secondary" onclick={testDataGeneration} disabled={isRunning}>
|
|
511
|
+
1. Generate Data
|
|
512
|
+
</button>
|
|
513
|
+
</div>
|
|
514
|
+
<div class="col-auto">
|
|
515
|
+
<button class="btn btn-outline-secondary" onclick={testFilterOperation} disabled={isRunning || items.length === 0}>
|
|
516
|
+
2. Filter
|
|
517
|
+
</button>
|
|
518
|
+
</div>
|
|
519
|
+
<div class="col-auto">
|
|
520
|
+
<button class="btn btn-outline-secondary" onclick={testSingleUpdate} disabled={isRunning || items.length === 0}>
|
|
521
|
+
3. Single Update
|
|
522
|
+
</button>
|
|
523
|
+
</div>
|
|
524
|
+
<div class="col-auto">
|
|
525
|
+
<button class="btn btn-outline-secondary" onclick={testBulkUpdate} disabled={isRunning || items.length === 0}>
|
|
526
|
+
4. Bulk Update
|
|
527
|
+
</button>
|
|
528
|
+
</div>
|
|
529
|
+
<div class="col-auto">
|
|
530
|
+
<button class="btn btn-outline-secondary" onclick={testGroupingDerived} disabled={isRunning || items.length === 0}>
|
|
531
|
+
5. Grouping
|
|
532
|
+
</button>
|
|
533
|
+
</div>
|
|
534
|
+
<div class="col-auto">
|
|
535
|
+
<button class="btn btn-outline-secondary" onclick={testMultipleDerivedAccess} disabled={isRunning || items.length === 0}>
|
|
536
|
+
6. Multi-Derived
|
|
537
|
+
</button>
|
|
538
|
+
</div>
|
|
539
|
+
<div class="col-auto">
|
|
540
|
+
<button class="btn btn-outline-secondary" onclick={testArrayPush} disabled={isRunning}>
|
|
541
|
+
7. Array Push
|
|
542
|
+
</button>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
|
|
546
|
+
<!-- Run All -->
|
|
547
|
+
<div class="d-flex gap-2 mb-4">
|
|
548
|
+
<button
|
|
549
|
+
class="btn btn-success btn-lg flex-grow-1"
|
|
550
|
+
onclick={runAllTests}
|
|
551
|
+
disabled={isRunning}
|
|
552
|
+
>
|
|
553
|
+
{#if isRunning}
|
|
554
|
+
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
|
555
|
+
Running: {currentTest}...
|
|
556
|
+
{:else}
|
|
557
|
+
<i class="bi bi-play-fill me-2"></i>
|
|
558
|
+
Run All Tests
|
|
559
|
+
{/if}
|
|
560
|
+
</button>
|
|
561
|
+
<button
|
|
562
|
+
class="btn btn-warning btn-lg flex-grow-1"
|
|
563
|
+
onclick={runRealTests}
|
|
564
|
+
disabled={isRunning}
|
|
565
|
+
>
|
|
566
|
+
<i class="bi bi-stopwatch me-2"></i>
|
|
567
|
+
Run REAL Tests
|
|
568
|
+
</button>
|
|
569
|
+
<button class="btn btn-outline-danger" onclick={clearResults} disabled={isRunning}>
|
|
570
|
+
<i class="bi bi-trash"></i>
|
|
571
|
+
</button>
|
|
572
|
+
</div>
|
|
573
|
+
|
|
574
|
+
<!-- Results -->
|
|
575
|
+
{#if results.length > 0}
|
|
576
|
+
<div class="table-responsive">
|
|
577
|
+
<table class="table table-sm table-striped">
|
|
578
|
+
<thead class="table-dark">
|
|
579
|
+
<tr>
|
|
580
|
+
<th>#</th>
|
|
581
|
+
<th>Test</th>
|
|
582
|
+
<th class="text-end">Duration</th>
|
|
583
|
+
<th class="text-end">Items</th>
|
|
584
|
+
<th class="text-end">Ops/sec</th>
|
|
585
|
+
</tr>
|
|
586
|
+
</thead>
|
|
587
|
+
<tbody>
|
|
588
|
+
{#each results as result, i}
|
|
589
|
+
<tr class={result.duration > 1000 ? 'table-danger' : result.duration > 100 ? 'table-warning' : ''}>
|
|
590
|
+
<td>{i + 1}</td>
|
|
591
|
+
<td>{result.name}</td>
|
|
592
|
+
<td class="text-end font-monospace">
|
|
593
|
+
{formatDuration(result.duration)}
|
|
594
|
+
</td>
|
|
595
|
+
<td class="text-end">{result.itemCount.toLocaleString()}</td>
|
|
596
|
+
<td class="text-end font-monospace">
|
|
597
|
+
{result.opsPerSecond > 1000
|
|
598
|
+
? `${(result.opsPerSecond / 1000).toFixed(1)}K`
|
|
599
|
+
: result.opsPerSecond.toFixed(1)}
|
|
600
|
+
</td>
|
|
601
|
+
</tr>
|
|
602
|
+
{/each}
|
|
603
|
+
</tbody>
|
|
604
|
+
<tfoot class="table-light">
|
|
605
|
+
<tr class="fw-bold">
|
|
606
|
+
<td colspan="2">Total</td>
|
|
607
|
+
<td class="text-end font-monospace">
|
|
608
|
+
{formatDuration(results.reduce((sum, r) => sum + r.duration, 0))}
|
|
609
|
+
</td>
|
|
610
|
+
<td colspan="2"></td>
|
|
611
|
+
</tr>
|
|
612
|
+
</tfoot>
|
|
613
|
+
</table>
|
|
614
|
+
</div>
|
|
615
|
+
{:else}
|
|
616
|
+
<div class="text-center text-muted py-4">
|
|
617
|
+
<i class="bi bi-bar-chart" style="font-size: 3rem;"></i>
|
|
618
|
+
<p class="mt-2">Run tests to see results</p>
|
|
619
|
+
<p class="small">Check browser console (F12) for detailed logs</p>
|
|
620
|
+
</div>
|
|
621
|
+
{/if}
|
|
622
|
+
|
|
623
|
+
<!-- Current State Display -->
|
|
624
|
+
{#if items.length > 0}
|
|
625
|
+
<div class="mt-4 p-3 bg-light rounded">
|
|
626
|
+
<h6 class="mb-2">Current Derived State:</h6>
|
|
627
|
+
<div class="row text-center">
|
|
628
|
+
<div class="col">
|
|
629
|
+
<div class="fw-bold">{filteredItems.length}</div>
|
|
630
|
+
<small class="text-muted">Filtered</small>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="col">
|
|
633
|
+
<div class="fw-bold">{activeItems.length}</div>
|
|
634
|
+
<small class="text-muted">Active</small>
|
|
635
|
+
</div>
|
|
636
|
+
<div class="col">
|
|
637
|
+
<div class="fw-bold">{totalValue.toFixed(0)}</div>
|
|
638
|
+
<small class="text-muted">Total Value</small>
|
|
639
|
+
</div>
|
|
640
|
+
<div class="col">
|
|
641
|
+
<div class="fw-bold">{groupedByCategory().size}</div>
|
|
642
|
+
<small class="text-muted">Groups</small>
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
</div>
|
|
646
|
+
{/if}
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<div class="card-footer text-muted small">
|
|
650
|
+
<i class="bi bi-info-circle me-1"></i>
|
|
651
|
+
Open browser console (F12) for detailed benchmark logs.
|
|
652
|
+
Red rows = >1s, Yellow rows = >100ms
|
|
653
|
+
</div>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
|
|
657
|
+
<style>
|
|
658
|
+
.benchmark-container {
|
|
659
|
+
max-width: 900px;
|
|
660
|
+
margin: 0 auto;
|
|
661
|
+
}
|
|
662
|
+
</style>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Benchmark utilities for measuring Svelte reactivity performance
|
|
3
|
+
*/
|
|
4
|
+
export interface BenchmarkResult {
|
|
5
|
+
name: string;
|
|
6
|
+
duration: number;
|
|
7
|
+
itemCount: number;
|
|
8
|
+
opsPerSecond: number;
|
|
9
|
+
timestamp: Date;
|
|
10
|
+
}
|
|
11
|
+
export interface TestItem {
|
|
12
|
+
id: number;
|
|
13
|
+
name: string;
|
|
14
|
+
value: number;
|
|
15
|
+
category: string;
|
|
16
|
+
active: boolean;
|
|
17
|
+
data: number[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate test data array
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateTestData(count: number): TestItem[];
|
|
23
|
+
/**
|
|
24
|
+
* Measure execution time of a function
|
|
25
|
+
*/
|
|
26
|
+
export declare function measure<T>(fn: () => T): {
|
|
27
|
+
result: T;
|
|
28
|
+
duration: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Measure async execution time
|
|
32
|
+
*/
|
|
33
|
+
export declare function measureAsync<T>(fn: () => Promise<T>): Promise<{
|
|
34
|
+
result: T;
|
|
35
|
+
duration: number;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Format duration for display
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatDuration(ms: number): string;
|
|
41
|
+
/**
|
|
42
|
+
* Create a detailed log entry
|
|
43
|
+
*/
|
|
44
|
+
export declare function logBenchmark(result: BenchmarkResult): void;
|
|
45
|
+
/**
|
|
46
|
+
* Run a benchmark and return result
|
|
47
|
+
*/
|
|
48
|
+
export declare function runBenchmark(name: string, fn: () => void, itemCount: number): BenchmarkResult;
|