@zeix/cause-effect 0.16.1 → 0.17.1

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 (66) hide show
  1. package/.ai-context.md +85 -21
  2. package/.cursorrules +11 -5
  3. package/.github/copilot-instructions.md +64 -13
  4. package/CLAUDE.md +143 -163
  5. package/LICENSE +1 -1
  6. package/README.md +248 -333
  7. package/archive/benchmark.ts +688 -0
  8. package/archive/collection.ts +312 -0
  9. package/{src → archive}/computed.ts +21 -21
  10. package/archive/list.ts +551 -0
  11. package/archive/memo.ts +139 -0
  12. package/{src → archive}/state.ts +13 -11
  13. package/archive/store.ts +368 -0
  14. package/archive/task.ts +194 -0
  15. package/eslint.config.js +1 -0
  16. package/index.dev.js +938 -509
  17. package/index.js +1 -1
  18. package/index.ts +50 -23
  19. package/package.json +1 -1
  20. package/src/classes/collection.ts +282 -0
  21. package/src/classes/composite.ts +176 -0
  22. package/src/classes/computed.ts +333 -0
  23. package/src/classes/list.ts +305 -0
  24. package/src/classes/ref.ts +68 -0
  25. package/src/classes/state.ts +98 -0
  26. package/src/classes/store.ts +210 -0
  27. package/src/diff.ts +26 -53
  28. package/src/effect.ts +9 -9
  29. package/src/errors.ts +71 -25
  30. package/src/match.ts +5 -12
  31. package/src/resolve.ts +3 -2
  32. package/src/signal.ts +58 -41
  33. package/src/system.ts +79 -42
  34. package/src/util.ts +16 -34
  35. package/test/batch.test.ts +15 -17
  36. package/test/benchmark.test.ts +4 -4
  37. package/test/collection.test.ts +853 -0
  38. package/test/computed.test.ts +138 -130
  39. package/test/diff.test.ts +2 -2
  40. package/test/effect.test.ts +36 -35
  41. package/test/list.test.ts +754 -0
  42. package/test/match.test.ts +25 -25
  43. package/test/ref.test.ts +227 -0
  44. package/test/resolve.test.ts +17 -19
  45. package/test/signal.test.ts +70 -119
  46. package/test/state.test.ts +44 -44
  47. package/test/store.test.ts +253 -929
  48. package/types/index.d.ts +12 -9
  49. package/types/src/classes/collection.d.ts +46 -0
  50. package/types/src/classes/composite.d.ts +15 -0
  51. package/types/src/classes/computed.d.ts +97 -0
  52. package/types/src/classes/list.d.ts +41 -0
  53. package/types/src/classes/ref.d.ts +39 -0
  54. package/types/src/classes/state.d.ts +52 -0
  55. package/types/src/classes/store.d.ts +51 -0
  56. package/types/src/diff.d.ts +8 -12
  57. package/types/src/errors.d.ts +17 -11
  58. package/types/src/signal.d.ts +27 -14
  59. package/types/src/system.d.ts +41 -20
  60. package/types/src/util.d.ts +6 -4
  61. package/src/store.ts +0 -474
  62. package/types/src/collection.d.ts +0 -26
  63. package/types/src/computed.d.ts +0 -33
  64. package/types/src/scheduler.d.ts +0 -55
  65. package/types/src/state.d.ts +0 -24
  66. package/types/src/store.d.ts +0 -65
@@ -0,0 +1,688 @@
1
+ import { List } from '../src/classes/list'
2
+ import {
3
+ BaseStore,
4
+ createStore as createClassStore,
5
+ } from '../src/classes/store'
6
+ import { createList as createFactoryList } from './list'
7
+ import { createStore as createFactoryStore } from './store'
8
+
9
+ /* === Benchmark Configuration === */
10
+
11
+ const ITERATIONS = 1000
12
+ const METHOD_CALLS = 100
13
+
14
+ /* === Test Data === */
15
+
16
+ const testData = {
17
+ user: {
18
+ id: 42,
19
+ name: 'Alice Johnson',
20
+ email: 'alice@example.com',
21
+ preferences: {
22
+ theme: 'dark' as const,
23
+ language: 'en',
24
+ notifications: {
25
+ email: true,
26
+ push: false,
27
+ desktop: true,
28
+ },
29
+ },
30
+ },
31
+ app: {
32
+ version: '2.1.0',
33
+ config: {
34
+ api: {
35
+ baseUrl: 'https://api.example.com',
36
+ timeout: 5000,
37
+ },
38
+ },
39
+ },
40
+ }
41
+
42
+ const testListData = [
43
+ { id: 1, name: 'Item 1', value: 10 },
44
+ { id: 2, name: 'Item 2', value: 20 },
45
+ { id: 3, name: 'Item 3', value: 30 },
46
+ { id: 4, name: 'Item 4', value: 40 },
47
+ { id: 5, name: 'Item 5', value: 50 },
48
+ ]
49
+
50
+ /* === Benchmarking Utilities === */
51
+
52
+ const measureTime = (label: string, fn: () => void): number => {
53
+ const start = performance.now()
54
+ fn()
55
+ const end = performance.now()
56
+ const duration = end - start
57
+ console.log(`${label}: ${duration.toFixed(2)}ms`)
58
+ return duration
59
+ }
60
+
61
+ // biome-ignore lint/suspicious/noExplicitAny: test
62
+ const analyzeObjectStructure = (obj: any, label: string) => {
63
+ const ownProps = Object.getOwnPropertyNames(obj)
64
+ const ownMethods = ownProps.filter(prop => {
65
+ const descriptor = Object.getOwnPropertyDescriptor(obj, prop)
66
+ return descriptor && typeof descriptor.value === 'function'
67
+ })
68
+ const ownData = ownProps.filter(prop => {
69
+ const descriptor = Object.getOwnPropertyDescriptor(obj, prop)
70
+ return (
71
+ descriptor &&
72
+ (typeof descriptor.value !== 'function' ||
73
+ descriptor.get ||
74
+ descriptor.set)
75
+ )
76
+ })
77
+
78
+ const prototype = Object.getPrototypeOf(obj)
79
+ const prototypeProps = Object.getOwnPropertyNames(prototype)
80
+ const prototypeMethods = prototypeProps.filter(prop => {
81
+ if (prop === 'constructor') return false
82
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, prop)
83
+ return descriptor && typeof descriptor.value === 'function'
84
+ })
85
+
86
+ console.log(`\n${label} Structure Analysis:`)
87
+ console.log(
88
+ ` Own Properties: ${ownProps.length} (${ownData.length} data, ${ownMethods.length} methods)`,
89
+ )
90
+ console.log(` Prototype Methods: ${prototypeMethods.length}`)
91
+ console.log(` Own Methods: [${ownMethods.join(', ')}]`)
92
+ console.log(
93
+ ` Prototype Methods: [${prototypeMethods.slice(0, 5).join(', ')}${prototypeMethods.length > 5 ? '...' : ''}]`,
94
+ )
95
+
96
+ // Estimate more realistic memory usage
97
+ let estimatedSize = 0
98
+ estimatedSize += ownData.length * 32 // Property slots
99
+ estimatedSize += ownMethods.length * 200 // Function objects (factories only)
100
+ estimatedSize += prototypeMethods.length * 8 // Method references (shared)
101
+ estimatedSize += 64 // Base object overhead
102
+
103
+ return {
104
+ ownMethods: ownMethods.length,
105
+ prototypeMethods: prototypeMethods.length,
106
+ ownData: ownData.length,
107
+ estimatedSize,
108
+ }
109
+ }
110
+
111
+ const measureMemory = async (
112
+ label: string,
113
+ // biome-ignore lint/suspicious/noExplicitAny: test
114
+ fn: () => any[],
115
+ // biome-ignore lint/suspicious/noExplicitAny: test
116
+ ): Promise<any[]> => {
117
+ // Force garbage collection multiple times to ensure clean baseline
118
+ if ('gc' in globalThis && typeof globalThis.gc === 'function') {
119
+ for (let i = 0; i < 3; i++) {
120
+ globalThis.gc()
121
+ await new Promise(resolve => setTimeout(resolve, 10))
122
+ }
123
+ }
124
+
125
+ const memBefore = process.memoryUsage().heapUsed
126
+ const result = fn()
127
+
128
+ // Force another GC cycle and wait
129
+ if ('gc' in globalThis && typeof globalThis.gc === 'function') {
130
+ await new Promise(resolve => setTimeout(resolve, 50))
131
+ globalThis.gc()
132
+ await new Promise(resolve => setTimeout(resolve, 10))
133
+ }
134
+
135
+ const memAfter = process.memoryUsage().heapUsed
136
+ const memDiff = memAfter - memBefore
137
+
138
+ console.log(`${label} Memory: ${(memDiff / 1024 / 1024).toFixed(2)}MB`)
139
+ return result
140
+ }
141
+
142
+ /* === Factory Approach Benchmark === */
143
+
144
+ const benchmarkFactory = async () => {
145
+ console.log('\n=== Factory Function Approach ===')
146
+
147
+ // biome-ignore lint/suspicious/noExplicitAny: test
148
+ let stores: any[] = []
149
+
150
+ // Test instantiation performance
151
+ measureTime('Factory Instantiation', () => {
152
+ stores = []
153
+ for (let i = 0; i < ITERATIONS; i++) {
154
+ stores.push(createFactoryStore({ ...testData, id: i }))
155
+ }
156
+ })
157
+
158
+ // Test memory usage
159
+ const memoryStores = await measureMemory('Factory Memory Usage', () => {
160
+ const tempStores = []
161
+ for (let i = 0; i < ITERATIONS; i++)
162
+ // @ts-expect-error ignore
163
+ tempStores.push(createFactoryStore({ ...testData, id: i }))
164
+ return tempStores
165
+ })
166
+
167
+ // Analyze object structure
168
+ const sampleFactoryStore = createFactoryStore(testData)
169
+ const factoryAnalysis = analyzeObjectStructure(
170
+ sampleFactoryStore,
171
+ 'Factory Store',
172
+ )
173
+ console.log(
174
+ `Factory Estimated Size: ${(factoryAnalysis.estimatedSize / 1024).toFixed(2)}KB per store`,
175
+ )
176
+ console.log(
177
+ `Factory Method Overhead: ${factoryAnalysis.ownMethods} own methods × ${ITERATIONS} stores = ${factoryAnalysis.ownMethods * ITERATIONS} method instances`,
178
+ )
179
+
180
+ // Test method call performance
181
+ measureTime('Factory Method Calls', () => {
182
+ for (let i = 0; i < METHOD_CALLS; i++) {
183
+ memoryStores.forEach(store => {
184
+ store.get()
185
+ const _name = store.user.name
186
+ const _emailNotification =
187
+ store.user.preferences.notifications.email
188
+ store.set({ ...testData, updated: true })
189
+ })
190
+ }
191
+ })
192
+
193
+ // Test method identity (should be different instances)
194
+ const store1 = createFactoryStore(testData)
195
+ const store2 = createFactoryStore(testData)
196
+ console.log('Factory Methods Shared:', store1.get === store2.get) // Should be false
197
+
198
+ return memoryStores
199
+ }
200
+
201
+ /* === Factory List Approach Benchmark === */
202
+
203
+ const benchmarkFactoryList = async () => {
204
+ console.log('\n=== Factory List Function Approach ===')
205
+
206
+ // biome-ignore lint/suspicious/noExplicitAny: test
207
+ let lists: any[] = []
208
+
209
+ // Test instantiation performance
210
+ measureTime('Factory List Instantiation', () => {
211
+ lists = []
212
+ for (let i = 0; i < ITERATIONS; i++) {
213
+ lists.push(
214
+ createFactoryList([
215
+ ...testListData.map(item => ({
216
+ ...item,
217
+ id: item.id + i * 1000,
218
+ })),
219
+ ]),
220
+ )
221
+ }
222
+ })
223
+
224
+ // Test memory usage
225
+ const memoryLists = await measureMemory('Factory List Memory Usage', () => {
226
+ const tempLists = []
227
+ for (let i = 0; i < ITERATIONS; i++) {
228
+ tempLists.push(
229
+ // @ts-expect-error ignore
230
+ createFactoryList([
231
+ ...testListData.map(item => ({
232
+ ...item,
233
+ id: item.id + i * 1000,
234
+ })),
235
+ ]),
236
+ )
237
+ }
238
+ return tempLists
239
+ })
240
+
241
+ // Analyze object structure
242
+ const sampleFactoryList = createFactoryList(testListData)
243
+ const factoryAnalysis = analyzeObjectStructure(
244
+ sampleFactoryList,
245
+ 'Factory List',
246
+ )
247
+ console.log(
248
+ `Factory List Estimated Size: ${(factoryAnalysis.estimatedSize / 1024).toFixed(2)}KB per list`,
249
+ )
250
+ console.log(
251
+ `Factory List Method Overhead: ${factoryAnalysis.ownMethods} own methods × ${ITERATIONS} lists = ${factoryAnalysis.ownMethods * ITERATIONS} method instances`,
252
+ )
253
+
254
+ // Test method call performance
255
+ measureTime('Factory List Method Calls', () => {
256
+ for (let i = 0; i < METHOD_CALLS; i++) {
257
+ memoryLists.forEach(list => {
258
+ list.get()
259
+ const _0 = list[0]
260
+ const _length = list.length
261
+ list.set([
262
+ ...testListData,
263
+ { id: 999, name: 'New', value: 999 },
264
+ ])
265
+ list.sort()
266
+ })
267
+ }
268
+ })
269
+
270
+ // Test method identity (should be different instances)
271
+ const list1 = createFactoryList(testListData)
272
+ const list2 = createFactoryList(testListData)
273
+ console.log('Factory List Methods Shared:', list1.get === list2.get) // Should be false
274
+
275
+ return memoryLists
276
+ }
277
+
278
+ /* === Direct Class List Approach Benchmark (No Proxy) === */
279
+
280
+ const benchmarkDirectClassList = async () => {
281
+ console.log('\n=== Direct Class-Based List Approach (No Proxy) ===')
282
+
283
+ // biome-ignore lint/suspicious/noExplicitAny: test
284
+ let lists: any[] = []
285
+
286
+ // Test instantiation performance
287
+ measureTime('Direct Class List Instantiation', () => {
288
+ lists = []
289
+ for (let i = 0; i < ITERATIONS; i++) {
290
+ lists.push(
291
+ new List([
292
+ ...testListData.map(item => ({
293
+ ...item,
294
+ id: item.id + i * 1000,
295
+ })),
296
+ ]),
297
+ )
298
+ }
299
+ })
300
+
301
+ // Test memory usage
302
+ const memoryLists = await measureMemory(
303
+ 'Direct Class List Memory Usage',
304
+ () => {
305
+ const tempLists = []
306
+ for (let i = 0; i < ITERATIONS; i++) {
307
+ tempLists.push(
308
+ // @ts-expect-error ignore
309
+ new List([
310
+ ...testListData.map(item => ({
311
+ ...item,
312
+ id: item.id + i * 1000,
313
+ })),
314
+ ]),
315
+ )
316
+ }
317
+ return tempLists
318
+ },
319
+ )
320
+
321
+ // Analyze object structure
322
+ const sampleDirectClassList = new List(testListData)
323
+ const classAnalysis = analyzeObjectStructure(
324
+ sampleDirectClassList,
325
+ 'Direct Class List',
326
+ )
327
+ console.log(
328
+ `Direct Class List Estimated Size: ${(classAnalysis.estimatedSize / 1024).toFixed(2)}KB per list`,
329
+ )
330
+ console.log(
331
+ `Direct Class List Method Overhead: ${classAnalysis.prototypeMethods} shared methods × ${ITERATIONS} lists = ${classAnalysis.prototypeMethods} method instances (shared)`,
332
+ )
333
+
334
+ // Test method call performance
335
+ measureTime('Direct Class List Method Calls', () => {
336
+ for (let i = 0; i < METHOD_CALLS; i++) {
337
+ memoryLists.forEach(list => {
338
+ list.get()
339
+ const _0 = list.at(0)
340
+ const _length = list.length
341
+ list.set([
342
+ ...testListData,
343
+ { id: 999, name: 'New', value: 999 },
344
+ ])
345
+ list.sort()
346
+ })
347
+ }
348
+ })
349
+
350
+ // Test method identity (should be same instances - shared prototype)
351
+ const list1 = new List(testListData)
352
+ const list2 = new List(testListData)
353
+ console.log('Direct Class List Methods Shared:', list1.get === list2.get) // Should be true
354
+
355
+ return memoryLists
356
+ }
357
+
358
+ /* === Class Approach Benchmark === */
359
+
360
+ const benchmarkClass = async () => {
361
+ console.log('\n=== Class-Based Approach ===')
362
+
363
+ // biome-ignore lint/suspicious/noExplicitAny: test
364
+ let stores: any[] = []
365
+
366
+ // Test instantiation performance
367
+ measureTime('Class Instantiation', () => {
368
+ stores = []
369
+ for (let i = 0; i < ITERATIONS; i++) {
370
+ stores.push(createClassStore({ ...testData, id: i }))
371
+ }
372
+ })
373
+
374
+ // Test memory usage
375
+ const memoryStores = await measureMemory('Class Memory Usage', () => {
376
+ const tempStores = []
377
+ for (let i = 0; i < ITERATIONS; i++)
378
+ // @ts-expect-error ignore
379
+ tempStores.push(createClassStore({ ...testData, id: i }))
380
+ return tempStores
381
+ })
382
+
383
+ // Analyze object structure
384
+ const sampleClassStore = createClassStore(testData)
385
+ const classAnalysis = analyzeObjectStructure(
386
+ sampleClassStore,
387
+ 'Class Store',
388
+ )
389
+ console.log(
390
+ `Class Estimated Size: ${(classAnalysis.estimatedSize / 1024).toFixed(2)}KB per store`,
391
+ )
392
+ console.log(
393
+ `Class Method Overhead: ${classAnalysis.prototypeMethods} shared methods × ${ITERATIONS} stores = ${classAnalysis.prototypeMethods} method instances (shared)`,
394
+ )
395
+
396
+ // Test method call performance
397
+ measureTime('Class Method Calls', () => {
398
+ for (let i = 0; i < METHOD_CALLS; i++) {
399
+ memoryStores.forEach(store => {
400
+ store.get()
401
+ const _name = store.user.name
402
+ const _emailNotification =
403
+ store.user.preferences.notifications.email
404
+ store.set({ ...testData, updated: true })
405
+ })
406
+ }
407
+ })
408
+
409
+ // Test method identity (should be same instances - shared prototype)
410
+ const store1 = createClassStore(testData)
411
+ const store2 = createClassStore(testData)
412
+ console.log('Class Methods Shared:', store1.get === store2.get) // Should be true
413
+
414
+ return memoryStores
415
+ }
416
+
417
+ /* === Direct Class Approach Benchmark (No Proxy) === */
418
+
419
+ const benchmarkDirectClass = async () => {
420
+ console.log('\n=== Direct Class-Based Approach (No Proxy) ===')
421
+
422
+ // biome-ignore lint/suspicious/noExplicitAny: test
423
+ let stores: any[] = []
424
+
425
+ // Test instantiation performance
426
+ measureTime('Direct Class Instantiation', () => {
427
+ stores = []
428
+ for (let i = 0; i < ITERATIONS; i++) {
429
+ stores.push(new BaseStore({ ...testData, id: i }))
430
+ }
431
+ })
432
+
433
+ // Test memory usage
434
+ const memoryStores = await measureMemory(
435
+ 'Direct Class Memory Usage',
436
+ () => {
437
+ const tempStores = []
438
+ for (let i = 0; i < ITERATIONS; i++)
439
+ // @ts-expect-error ignore
440
+ tempStores.push(new BaseStore({ ...testData, id: i }))
441
+ return tempStores
442
+ },
443
+ )
444
+
445
+ // Analyze object structure
446
+ const sampleDirectClassStore = new BaseStore(testData)
447
+ const classAnalysis = analyzeObjectStructure(
448
+ sampleDirectClassStore,
449
+ 'Direct Class Store',
450
+ )
451
+ console.log(
452
+ `Direct Class Estimated Size: ${(classAnalysis.estimatedSize / 1024).toFixed(2)}KB per store`,
453
+ )
454
+ console.log(
455
+ `Direct Class Method Overhead: ${classAnalysis.prototypeMethods} shared methods × ${ITERATIONS} stores = ${classAnalysis.prototypeMethods} method instances (shared)`,
456
+ )
457
+
458
+ // Test method call performance
459
+ measureTime('Direct Class Method Calls', () => {
460
+ for (let i = 0; i < METHOD_CALLS; i++) {
461
+ memoryStores.forEach(store => {
462
+ store.get()
463
+ const _name = store.byKey('user').byKey('name')
464
+ const _emailNotification = store
465
+ .byKey('user')
466
+ .byKey('preferences')
467
+ .byKey('notifications')
468
+ .byKey('email')
469
+ store.set({ ...testData, updated: true })
470
+ })
471
+ }
472
+ })
473
+
474
+ // Test method identity (should be same instances - shared prototype)
475
+ const store1 = new BaseStore(testData)
476
+ const store2 = new BaseStore(testData)
477
+ console.log('Direct Class Methods Shared:', store1.get === store2.get) // Should be true
478
+
479
+ return memoryStores
480
+ }
481
+
482
+ /* === List Functionality Test === */
483
+
484
+ const testListFunctionality = () => {
485
+ console.log('\n=== List Functionality Comparison ===')
486
+
487
+ console.log('\n--- Factory List ---')
488
+ const factoryList = createFactoryList([
489
+ { id: 1, name: 'A' },
490
+ { id: 2, name: 'B' },
491
+ ])
492
+ console.log('Initial:', factoryList.get())
493
+ console.log('Length:', factoryList.length)
494
+ factoryList.add({ id: 3, name: 'C' })
495
+ console.log('After add:', factoryList.get())
496
+ console.log('Length:', factoryList.length)
497
+ factoryList.splice(1, 1, { id: 4, name: 'D' })
498
+ console.log('After splice:', factoryList.get())
499
+
500
+ console.log('\n--- Class List ---')
501
+ const classList = new List([
502
+ { id: 1, name: 'A' },
503
+ { id: 2, name: 'B' },
504
+ ])
505
+ console.log('Initial:', classList.get())
506
+ console.log('Length:', classList.length)
507
+ classList.add({ id: 3, name: 'C' })
508
+ console.log('After add:', classList.get())
509
+ console.log('Length:', classList.length)
510
+ classList.splice(1, 1, { id: 4, name: 'D' })
511
+ console.log('After splice:', classList.get())
512
+
513
+ // Test that both approaches produce equivalent results (after same operations)
514
+ const factoryList2 = createFactoryList([{ id: 1, name: 'Test' }])
515
+ const classList2 = new List([{ id: 1, name: 'Test' }])
516
+ factoryList2.add({ id: 2, name: 'Test2' })
517
+ classList2.add({ id: 2, name: 'Test2' })
518
+
519
+ const factoryResult = JSON.stringify(factoryList2.get())
520
+ const classResult = JSON.stringify(classList2.get())
521
+ console.log(
522
+ '\nList Functionally Equivalent:',
523
+ factoryResult === classResult,
524
+ )
525
+ }
526
+
527
+ /* === Store Functionality Test === */
528
+
529
+ const testStoreFunctionality = () => {
530
+ console.log('\n=== Functionality Comparison ===')
531
+
532
+ console.log('\n--- Factory Store ---')
533
+ const factoryStore = createFactoryStore<{
534
+ a: number
535
+ b: number
536
+ c?: number
537
+ }>({ a: 1, b: 2 })
538
+ console.log('Initial:', factoryStore.get())
539
+ factoryStore.set({ a: 10, b: 20, c: 30 })
540
+ console.log('After set:', factoryStore.get())
541
+ console.log(
542
+ 'Keys:',
543
+ Array.from(factoryStore).map(([key]) => key),
544
+ )
545
+
546
+ console.log('\n--- Class Store ---')
547
+ const classStore = createClassStore<{
548
+ a: number
549
+ b: number
550
+ c?: number
551
+ }>({ a: 1, b: 2 })
552
+ console.log('Initial:', classStore.get())
553
+ classStore.set({ a: 10, b: 20, c: 30 })
554
+ console.log('After set:', classStore.get())
555
+ console.log(
556
+ 'Keys:',
557
+ Array.from(classStore).map(([key]) => key),
558
+ )
559
+
560
+ // Test that both approaches produce equivalent results
561
+ const factoryResult = JSON.stringify(factoryStore.get())
562
+ const classResult = JSON.stringify(classStore.get())
563
+ console.log(
564
+ '\nStore Functionally Equivalent:',
565
+ factoryResult === classResult,
566
+ )
567
+ }
568
+
569
+ /* === Comparative Analysis === */
570
+
571
+ const runComparison = async () => {
572
+ console.log(`\n${'='.repeat(60)}`)
573
+ console.log('STORE & LIST IMPLEMENTATION BENCHMARK')
574
+ console.log(`Iterations: ${ITERATIONS} | Method Calls: ${METHOD_CALLS}`)
575
+ console.log(`${'='.repeat(60)}`)
576
+
577
+ // Test functionality first
578
+ testStoreFunctionality()
579
+ testListFunctionality()
580
+
581
+ // Run Store benchmarks
582
+ console.log(`\n${'='.repeat(40)}`)
583
+ console.log('STORE BENCHMARKS')
584
+ console.log(`${'='.repeat(40)}`)
585
+ const factoryStores = await benchmarkFactory()
586
+ const classStores = await benchmarkClass()
587
+ const directClassStores = await benchmarkDirectClass()
588
+
589
+ // Run List benchmarks
590
+ console.log(`\n${'='.repeat(40)}`)
591
+ console.log('LIST BENCHMARKS')
592
+ console.log(`${'='.repeat(40)}`)
593
+ const factoryLists = await benchmarkFactoryList()
594
+ const directClassLists = await benchmarkDirectClassList()
595
+
596
+ // Detailed memory analysis for both Store and List
597
+ const sampleFactory = createFactoryStore(testData)
598
+ const sampleClass = createClassStore(testData)
599
+ const sampleFactoryList = createFactoryList(testListData)
600
+ const sampleClassList = new List(testListData)
601
+
602
+ const factoryStoreAnalysis = analyzeObjectStructure(
603
+ sampleFactory,
604
+ 'Final Factory Store Analysis',
605
+ )
606
+ const classStoreAnalysis = analyzeObjectStructure(
607
+ sampleClass,
608
+ 'Final Class Store Analysis',
609
+ )
610
+ const factoryListAnalysis = analyzeObjectStructure(
611
+ sampleFactoryList,
612
+ 'Final Factory List Analysis',
613
+ )
614
+ const classListAnalysis = analyzeObjectStructure(
615
+ sampleClassList,
616
+ 'Final Class List Analysis',
617
+ )
618
+
619
+ console.log('\n=== Memory Analysis Summary ===')
620
+
621
+ // Store Memory Analysis
622
+ console.log('\n--- Store Memory Analysis ---')
623
+ console.log(
624
+ `Store Factory Method Duplication: ${factoryStoreAnalysis.ownMethods * ITERATIONS} function instances`,
625
+ )
626
+ console.log(
627
+ `Store Class Method Sharing: ${classStoreAnalysis.prototypeMethods} shared function instances`,
628
+ )
629
+
630
+ const storeMethodMemorySaving =
631
+ ((factoryStoreAnalysis.ownMethods * ITERATIONS -
632
+ classStoreAnalysis.prototypeMethods) *
633
+ 200) /
634
+ 1024 /
635
+ 1024
636
+ console.log(
637
+ `Store Estimated Method Memory Savings: ${storeMethodMemorySaving.toFixed(2)}MB`,
638
+ )
639
+
640
+ // List Memory Analysis
641
+ console.log('\n--- List Memory Analysis ---')
642
+ console.log(
643
+ `List Factory Method Duplication: ${factoryListAnalysis.ownMethods * ITERATIONS} function instances`,
644
+ )
645
+ console.log(
646
+ `List Class Method Sharing: ${classListAnalysis.prototypeMethods} shared function instances`,
647
+ )
648
+
649
+ const listMethodMemorySaving =
650
+ ((factoryListAnalysis.ownMethods * ITERATIONS -
651
+ classListAnalysis.prototypeMethods) *
652
+ 200) /
653
+ 1024 /
654
+ 1024
655
+ console.log(
656
+ `List Estimated Method Memory Savings: ${listMethodMemorySaving.toFixed(2)}MB`,
657
+ )
658
+
659
+ const totalMethodMemorySaving =
660
+ storeMethodMemorySaving + listMethodMemorySaving
661
+ console.log(
662
+ `Total Estimated Method Memory Savings: ${totalMethodMemorySaving.toFixed(2)}MB`,
663
+ )
664
+
665
+ console.log('\n=== Performance Comparison Summary ===')
666
+ console.log('Direct Class (No Proxy) vs Proxy Class vs Factory approaches:')
667
+ console.log('- Direct Class should have fastest method calls')
668
+ console.log('- Proxy Class has convenience but method call overhead')
669
+ console.log('- Factory has per-instance method overhead')
670
+
671
+ // Keep references to prevent GC during measurement
672
+ return {
673
+ factoryStores,
674
+ classStores,
675
+ directClassStores,
676
+ factoryLists,
677
+ directClassLists,
678
+ }
679
+ }
680
+
681
+ /* === Export === */
682
+
683
+ export { runComparison }
684
+
685
+ // Auto-run if this file is executed directly
686
+ if (import.meta.url === `file://${process.argv[1]}`) {
687
+ runComparison().catch(console.error)
688
+ }