@zeix/cause-effect 0.17.3 → 0.18.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.
- package/.ai-context.md +163 -232
- package/.cursorrules +41 -35
- package/.github/copilot-instructions.md +166 -116
- package/ARCHITECTURE.md +274 -0
- package/CLAUDE.md +199 -143
- package/COLLECTION_REFACTORING.md +161 -0
- package/GUIDE.md +298 -0
- package/README.md +232 -197
- package/REQUIREMENTS.md +100 -0
- package/bench/reactivity.bench.ts +577 -0
- package/index.dev.js +1325 -997
- package/index.js +1 -1
- package/index.ts +58 -74
- package/package.json +4 -1
- package/src/errors.ts +118 -74
- package/src/graph.ts +601 -0
- package/src/nodes/collection.ts +474 -0
- package/src/nodes/effect.ts +149 -0
- package/src/nodes/list.ts +588 -0
- package/src/nodes/memo.ts +120 -0
- package/src/nodes/sensor.ts +139 -0
- package/src/nodes/state.ts +135 -0
- package/src/nodes/store.ts +383 -0
- package/src/nodes/task.ts +146 -0
- package/src/signal.ts +112 -66
- package/src/util.ts +26 -57
- package/test/batch.test.ts +96 -62
- package/test/benchmark.test.ts +473 -487
- package/test/collection.test.ts +466 -706
- package/test/effect.test.ts +293 -696
- package/test/list.test.ts +335 -592
- package/test/memo.test.ts +380 -0
- package/test/regression.test.ts +156 -0
- package/test/scope.test.ts +191 -0
- package/test/sensor.test.ts +454 -0
- package/test/signal.test.ts +220 -213
- package/test/state.test.ts +217 -265
- package/test/store.test.ts +346 -446
- package/test/task.test.ts +395 -0
- package/test/untrack.test.ts +167 -0
- package/types/index.d.ts +13 -15
- package/types/src/errors.d.ts +73 -17
- package/types/src/graph.d.ts +208 -0
- package/types/src/nodes/collection.d.ts +64 -0
- package/types/src/nodes/effect.d.ts +48 -0
- package/types/src/nodes/list.d.ts +65 -0
- package/types/src/nodes/memo.d.ts +57 -0
- package/types/src/nodes/sensor.d.ts +75 -0
- package/types/src/nodes/state.d.ts +78 -0
- package/types/src/nodes/store.d.ts +51 -0
- package/types/src/nodes/task.d.ts +73 -0
- package/types/src/signal.d.ts +43 -29
- package/types/src/util.d.ts +9 -16
- package/archive/benchmark.ts +0 -683
- package/archive/collection.ts +0 -253
- package/archive/composite.ts +0 -85
- package/archive/computed.ts +0 -195
- package/archive/list.ts +0 -483
- package/archive/memo.ts +0 -139
- package/archive/state.ts +0 -90
- package/archive/store.ts +0 -298
- package/archive/task.ts +0 -189
- package/src/classes/collection.ts +0 -245
- package/src/classes/computed.ts +0 -349
- package/src/classes/list.ts +0 -343
- package/src/classes/ref.ts +0 -70
- package/src/classes/state.ts +0 -102
- package/src/classes/store.ts +0 -262
- package/src/diff.ts +0 -138
- package/src/effect.ts +0 -93
- package/src/match.ts +0 -45
- package/src/resolve.ts +0 -49
- package/src/system.ts +0 -257
- package/test/computed.test.ts +0 -1108
- package/test/diff.test.ts +0 -955
- package/test/match.test.ts +0 -388
- package/test/ref.test.ts +0 -353
- package/test/resolve.test.ts +0 -154
- package/types/src/classes/collection.d.ts +0 -45
- package/types/src/classes/computed.d.ts +0 -94
- package/types/src/classes/list.d.ts +0 -43
- package/types/src/classes/ref.d.ts +0 -35
- package/types/src/classes/state.d.ts +0 -49
- package/types/src/classes/store.d.ts +0 -52
- package/types/src/diff.d.ts +0 -28
- package/types/src/effect.d.ts +0 -15
- package/types/src/match.d.ts +0 -21
- package/types/src/resolve.d.ts +0 -29
- package/types/src/system.d.ts +0 -78
package/archive/benchmark.ts
DELETED
|
@@ -1,683 +0,0 @@
|
|
|
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
|
-
tempStores.push(createFactoryStore({ ...testData, id: i }))
|
|
163
|
-
return tempStores
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
// Analyze object structure
|
|
167
|
-
const sampleFactoryStore = createFactoryStore(testData)
|
|
168
|
-
const factoryAnalysis = analyzeObjectStructure(
|
|
169
|
-
sampleFactoryStore,
|
|
170
|
-
'Factory Store',
|
|
171
|
-
)
|
|
172
|
-
console.log(
|
|
173
|
-
`Factory Estimated Size: ${(factoryAnalysis.estimatedSize / 1024).toFixed(2)}KB per store`,
|
|
174
|
-
)
|
|
175
|
-
console.log(
|
|
176
|
-
`Factory Method Overhead: ${factoryAnalysis.ownMethods} own methods × ${ITERATIONS} stores = ${factoryAnalysis.ownMethods * ITERATIONS} method instances`,
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
// Test method call performance
|
|
180
|
-
measureTime('Factory Method Calls', () => {
|
|
181
|
-
for (let i = 0; i < METHOD_CALLS; i++) {
|
|
182
|
-
memoryStores.forEach(store => {
|
|
183
|
-
store.get()
|
|
184
|
-
const _name = store.user.name
|
|
185
|
-
const _emailNotification =
|
|
186
|
-
store.user.preferences.notifications.email
|
|
187
|
-
store.set({ ...testData, updated: true })
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// Test method identity (should be different instances)
|
|
193
|
-
const store1 = createFactoryStore(testData)
|
|
194
|
-
const store2 = createFactoryStore(testData)
|
|
195
|
-
console.log('Factory Methods Shared:', store1.get === store2.get) // Should be false
|
|
196
|
-
|
|
197
|
-
return memoryStores
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/* === Factory List Approach Benchmark === */
|
|
201
|
-
|
|
202
|
-
const benchmarkFactoryList = async () => {
|
|
203
|
-
console.log('\n=== Factory List Function Approach ===')
|
|
204
|
-
|
|
205
|
-
// biome-ignore lint/suspicious/noExplicitAny: test
|
|
206
|
-
let lists: any[] = []
|
|
207
|
-
|
|
208
|
-
// Test instantiation performance
|
|
209
|
-
measureTime('Factory List Instantiation', () => {
|
|
210
|
-
lists = []
|
|
211
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
212
|
-
lists.push(
|
|
213
|
-
createFactoryList([
|
|
214
|
-
...testListData.map(item => ({
|
|
215
|
-
...item,
|
|
216
|
-
id: item.id + i * 1000,
|
|
217
|
-
})),
|
|
218
|
-
]),
|
|
219
|
-
)
|
|
220
|
-
}
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
// Test memory usage
|
|
224
|
-
const memoryLists = await measureMemory('Factory List Memory Usage', () => {
|
|
225
|
-
const tempLists = []
|
|
226
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
227
|
-
tempLists.push(
|
|
228
|
-
createFactoryList([
|
|
229
|
-
...testListData.map(item => ({
|
|
230
|
-
...item,
|
|
231
|
-
id: item.id + i * 1000,
|
|
232
|
-
})),
|
|
233
|
-
]),
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
return tempLists
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
// Analyze object structure
|
|
240
|
-
const sampleFactoryList = createFactoryList(testListData)
|
|
241
|
-
const factoryAnalysis = analyzeObjectStructure(
|
|
242
|
-
sampleFactoryList,
|
|
243
|
-
'Factory List',
|
|
244
|
-
)
|
|
245
|
-
console.log(
|
|
246
|
-
`Factory List Estimated Size: ${(factoryAnalysis.estimatedSize / 1024).toFixed(2)}KB per list`,
|
|
247
|
-
)
|
|
248
|
-
console.log(
|
|
249
|
-
`Factory List Method Overhead: ${factoryAnalysis.ownMethods} own methods × ${ITERATIONS} lists = ${factoryAnalysis.ownMethods * ITERATIONS} method instances`,
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
// Test method call performance
|
|
253
|
-
measureTime('Factory List Method Calls', () => {
|
|
254
|
-
for (let i = 0; i < METHOD_CALLS; i++) {
|
|
255
|
-
memoryLists.forEach(list => {
|
|
256
|
-
list.get()
|
|
257
|
-
const _0 = list[0]
|
|
258
|
-
const _length = list.length
|
|
259
|
-
list.set([
|
|
260
|
-
...testListData,
|
|
261
|
-
{ id: 999, name: 'New', value: 999 },
|
|
262
|
-
])
|
|
263
|
-
list.sort()
|
|
264
|
-
})
|
|
265
|
-
}
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
// Test method identity (should be different instances)
|
|
269
|
-
const list1 = createFactoryList(testListData)
|
|
270
|
-
const list2 = createFactoryList(testListData)
|
|
271
|
-
console.log('Factory List Methods Shared:', list1.get === list2.get) // Should be false
|
|
272
|
-
|
|
273
|
-
return memoryLists
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/* === Direct Class List Approach Benchmark (No Proxy) === */
|
|
277
|
-
|
|
278
|
-
const benchmarkDirectClassList = async () => {
|
|
279
|
-
console.log('\n=== Direct Class-Based List Approach (No Proxy) ===')
|
|
280
|
-
|
|
281
|
-
// biome-ignore lint/suspicious/noExplicitAny: test
|
|
282
|
-
let lists: any[] = []
|
|
283
|
-
|
|
284
|
-
// Test instantiation performance
|
|
285
|
-
measureTime('Direct Class List Instantiation', () => {
|
|
286
|
-
lists = []
|
|
287
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
288
|
-
lists.push(
|
|
289
|
-
new List([
|
|
290
|
-
...testListData.map(item => ({
|
|
291
|
-
...item,
|
|
292
|
-
id: item.id + i * 1000,
|
|
293
|
-
})),
|
|
294
|
-
]),
|
|
295
|
-
)
|
|
296
|
-
}
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
// Test memory usage
|
|
300
|
-
const memoryLists = await measureMemory(
|
|
301
|
-
'Direct Class List Memory Usage',
|
|
302
|
-
() => {
|
|
303
|
-
const tempLists = []
|
|
304
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
305
|
-
tempLists.push(
|
|
306
|
-
new List([
|
|
307
|
-
...testListData.map(item => ({
|
|
308
|
-
...item,
|
|
309
|
-
id: item.id + i * 1000,
|
|
310
|
-
})),
|
|
311
|
-
]),
|
|
312
|
-
)
|
|
313
|
-
}
|
|
314
|
-
return tempLists
|
|
315
|
-
},
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
// Analyze object structure
|
|
319
|
-
const sampleDirectClassList = new List(testListData)
|
|
320
|
-
const classAnalysis = analyzeObjectStructure(
|
|
321
|
-
sampleDirectClassList,
|
|
322
|
-
'Direct Class List',
|
|
323
|
-
)
|
|
324
|
-
console.log(
|
|
325
|
-
`Direct Class List Estimated Size: ${(classAnalysis.estimatedSize / 1024).toFixed(2)}KB per list`,
|
|
326
|
-
)
|
|
327
|
-
console.log(
|
|
328
|
-
`Direct Class List Method Overhead: ${classAnalysis.prototypeMethods} shared methods × ${ITERATIONS} lists = ${classAnalysis.prototypeMethods} method instances (shared)`,
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
// Test method call performance
|
|
332
|
-
measureTime('Direct Class List Method Calls', () => {
|
|
333
|
-
for (let i = 0; i < METHOD_CALLS; i++) {
|
|
334
|
-
memoryLists.forEach(list => {
|
|
335
|
-
list.get()
|
|
336
|
-
const _0 = list.at(0)
|
|
337
|
-
const _length = list.length
|
|
338
|
-
list.set([
|
|
339
|
-
...testListData,
|
|
340
|
-
{ id: 999, name: 'New', value: 999 },
|
|
341
|
-
])
|
|
342
|
-
list.sort()
|
|
343
|
-
})
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
// Test method identity (should be same instances - shared prototype)
|
|
348
|
-
const list1 = new List(testListData)
|
|
349
|
-
const list2 = new List(testListData)
|
|
350
|
-
console.log('Direct Class List Methods Shared:', list1.get === list2.get) // Should be true
|
|
351
|
-
|
|
352
|
-
return memoryLists
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/* === Class Approach Benchmark === */
|
|
356
|
-
|
|
357
|
-
const benchmarkClass = async () => {
|
|
358
|
-
console.log('\n=== Class-Based Approach ===')
|
|
359
|
-
|
|
360
|
-
// biome-ignore lint/suspicious/noExplicitAny: test
|
|
361
|
-
let stores: any[] = []
|
|
362
|
-
|
|
363
|
-
// Test instantiation performance
|
|
364
|
-
measureTime('Class Instantiation', () => {
|
|
365
|
-
stores = []
|
|
366
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
367
|
-
stores.push(createClassStore({ ...testData, id: i }))
|
|
368
|
-
}
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
// Test memory usage
|
|
372
|
-
const memoryStores = await measureMemory('Class Memory Usage', () => {
|
|
373
|
-
const tempStores = []
|
|
374
|
-
for (let i = 0; i < ITERATIONS; i++)
|
|
375
|
-
tempStores.push(createClassStore({ ...testData, id: i }))
|
|
376
|
-
return tempStores
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
// Analyze object structure
|
|
380
|
-
const sampleClassStore = createClassStore(testData)
|
|
381
|
-
const classAnalysis = analyzeObjectStructure(
|
|
382
|
-
sampleClassStore,
|
|
383
|
-
'Class Store',
|
|
384
|
-
)
|
|
385
|
-
console.log(
|
|
386
|
-
`Class Estimated Size: ${(classAnalysis.estimatedSize / 1024).toFixed(2)}KB per store`,
|
|
387
|
-
)
|
|
388
|
-
console.log(
|
|
389
|
-
`Class Method Overhead: ${classAnalysis.prototypeMethods} shared methods × ${ITERATIONS} stores = ${classAnalysis.prototypeMethods} method instances (shared)`,
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
// Test method call performance
|
|
393
|
-
measureTime('Class Method Calls', () => {
|
|
394
|
-
for (let i = 0; i < METHOD_CALLS; i++) {
|
|
395
|
-
memoryStores.forEach(store => {
|
|
396
|
-
store.get()
|
|
397
|
-
const _name = store.user.name
|
|
398
|
-
const _emailNotification =
|
|
399
|
-
store.user.preferences.notifications.email
|
|
400
|
-
store.set({ ...testData, updated: true })
|
|
401
|
-
})
|
|
402
|
-
}
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
// Test method identity (should be same instances - shared prototype)
|
|
406
|
-
const store1 = createClassStore(testData)
|
|
407
|
-
const store2 = createClassStore(testData)
|
|
408
|
-
console.log('Class Methods Shared:', store1.get === store2.get) // Should be true
|
|
409
|
-
|
|
410
|
-
return memoryStores
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/* === Direct Class Approach Benchmark (No Proxy) === */
|
|
414
|
-
|
|
415
|
-
const benchmarkDirectClass = async () => {
|
|
416
|
-
console.log('\n=== Direct Class-Based Approach (No Proxy) ===')
|
|
417
|
-
|
|
418
|
-
// biome-ignore lint/suspicious/noExplicitAny: test
|
|
419
|
-
let stores: any[] = []
|
|
420
|
-
|
|
421
|
-
// Test instantiation performance
|
|
422
|
-
measureTime('Direct Class Instantiation', () => {
|
|
423
|
-
stores = []
|
|
424
|
-
for (let i = 0; i < ITERATIONS; i++) {
|
|
425
|
-
stores.push(new BaseStore({ ...testData, id: i }))
|
|
426
|
-
}
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
// Test memory usage
|
|
430
|
-
const memoryStores = await measureMemory(
|
|
431
|
-
'Direct Class Memory Usage',
|
|
432
|
-
() => {
|
|
433
|
-
const tempStores = []
|
|
434
|
-
for (let i = 0; i < ITERATIONS; i++)
|
|
435
|
-
tempStores.push(new BaseStore({ ...testData, id: i }))
|
|
436
|
-
return tempStores
|
|
437
|
-
},
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
// Analyze object structure
|
|
441
|
-
const sampleDirectClassStore = new BaseStore(testData)
|
|
442
|
-
const classAnalysis = analyzeObjectStructure(
|
|
443
|
-
sampleDirectClassStore,
|
|
444
|
-
'Direct Class Store',
|
|
445
|
-
)
|
|
446
|
-
console.log(
|
|
447
|
-
`Direct Class Estimated Size: ${(classAnalysis.estimatedSize / 1024).toFixed(2)}KB per store`,
|
|
448
|
-
)
|
|
449
|
-
console.log(
|
|
450
|
-
`Direct Class Method Overhead: ${classAnalysis.prototypeMethods} shared methods × ${ITERATIONS} stores = ${classAnalysis.prototypeMethods} method instances (shared)`,
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
// Test method call performance
|
|
454
|
-
measureTime('Direct Class Method Calls', () => {
|
|
455
|
-
for (let i = 0; i < METHOD_CALLS; i++) {
|
|
456
|
-
memoryStores.forEach(store => {
|
|
457
|
-
store.get()
|
|
458
|
-
const _name = store.byKey('user').byKey('name')
|
|
459
|
-
const _emailNotification = store
|
|
460
|
-
.byKey('user')
|
|
461
|
-
.byKey('preferences')
|
|
462
|
-
.byKey('notifications')
|
|
463
|
-
.byKey('email')
|
|
464
|
-
store.set({ ...testData, updated: true })
|
|
465
|
-
})
|
|
466
|
-
}
|
|
467
|
-
})
|
|
468
|
-
|
|
469
|
-
// Test method identity (should be same instances - shared prototype)
|
|
470
|
-
const store1 = new BaseStore(testData)
|
|
471
|
-
const store2 = new BaseStore(testData)
|
|
472
|
-
console.log('Direct Class Methods Shared:', store1.get === store2.get) // Should be true
|
|
473
|
-
|
|
474
|
-
return memoryStores
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/* === List Functionality Test === */
|
|
478
|
-
|
|
479
|
-
const testListFunctionality = () => {
|
|
480
|
-
console.log('\n=== List Functionality Comparison ===')
|
|
481
|
-
|
|
482
|
-
console.log('\n--- Factory List ---')
|
|
483
|
-
const factoryList = createFactoryList([
|
|
484
|
-
{ id: 1, name: 'A' },
|
|
485
|
-
{ id: 2, name: 'B' },
|
|
486
|
-
])
|
|
487
|
-
console.log('Initial:', factoryList.get())
|
|
488
|
-
console.log('Length:', factoryList.length)
|
|
489
|
-
factoryList.add({ id: 3, name: 'C' })
|
|
490
|
-
console.log('After add:', factoryList.get())
|
|
491
|
-
console.log('Length:', factoryList.length)
|
|
492
|
-
factoryList.splice(1, 1, { id: 4, name: 'D' })
|
|
493
|
-
console.log('After splice:', factoryList.get())
|
|
494
|
-
|
|
495
|
-
console.log('\n--- Class List ---')
|
|
496
|
-
const classList = new List([
|
|
497
|
-
{ id: 1, name: 'A' },
|
|
498
|
-
{ id: 2, name: 'B' },
|
|
499
|
-
])
|
|
500
|
-
console.log('Initial:', classList.get())
|
|
501
|
-
console.log('Length:', classList.length)
|
|
502
|
-
classList.add({ id: 3, name: 'C' })
|
|
503
|
-
console.log('After add:', classList.get())
|
|
504
|
-
console.log('Length:', classList.length)
|
|
505
|
-
classList.splice(1, 1, { id: 4, name: 'D' })
|
|
506
|
-
console.log('After splice:', classList.get())
|
|
507
|
-
|
|
508
|
-
// Test that both approaches produce equivalent results (after same operations)
|
|
509
|
-
const factoryList2 = createFactoryList([{ id: 1, name: 'Test' }])
|
|
510
|
-
const classList2 = new List([{ id: 1, name: 'Test' }])
|
|
511
|
-
factoryList2.add({ id: 2, name: 'Test2' })
|
|
512
|
-
classList2.add({ id: 2, name: 'Test2' })
|
|
513
|
-
|
|
514
|
-
const factoryResult = JSON.stringify(factoryList2.get())
|
|
515
|
-
const classResult = JSON.stringify(classList2.get())
|
|
516
|
-
console.log(
|
|
517
|
-
'\nList Functionally Equivalent:',
|
|
518
|
-
factoryResult === classResult,
|
|
519
|
-
)
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/* === Store Functionality Test === */
|
|
523
|
-
|
|
524
|
-
const testStoreFunctionality = () => {
|
|
525
|
-
console.log('\n=== Functionality Comparison ===')
|
|
526
|
-
|
|
527
|
-
console.log('\n--- Factory Store ---')
|
|
528
|
-
const factoryStore = createFactoryStore<{
|
|
529
|
-
a: number
|
|
530
|
-
b: number
|
|
531
|
-
c?: number
|
|
532
|
-
}>({ a: 1, b: 2 })
|
|
533
|
-
console.log('Initial:', factoryStore.get())
|
|
534
|
-
factoryStore.set({ a: 10, b: 20, c: 30 })
|
|
535
|
-
console.log('After set:', factoryStore.get())
|
|
536
|
-
console.log(
|
|
537
|
-
'Keys:',
|
|
538
|
-
Array.from(factoryStore).map(([key]) => key),
|
|
539
|
-
)
|
|
540
|
-
|
|
541
|
-
console.log('\n--- Class Store ---')
|
|
542
|
-
const classStore = createClassStore<{
|
|
543
|
-
a: number
|
|
544
|
-
b: number
|
|
545
|
-
c?: number
|
|
546
|
-
}>({ a: 1, b: 2 })
|
|
547
|
-
console.log('Initial:', classStore.get())
|
|
548
|
-
classStore.set({ a: 10, b: 20, c: 30 })
|
|
549
|
-
console.log('After set:', classStore.get())
|
|
550
|
-
console.log(
|
|
551
|
-
'Keys:',
|
|
552
|
-
Array.from(classStore).map(([key]) => key),
|
|
553
|
-
)
|
|
554
|
-
|
|
555
|
-
// Test that both approaches produce equivalent results
|
|
556
|
-
const factoryResult = JSON.stringify(factoryStore.get())
|
|
557
|
-
const classResult = JSON.stringify(classStore.get())
|
|
558
|
-
console.log(
|
|
559
|
-
'\nStore Functionally Equivalent:',
|
|
560
|
-
factoryResult === classResult,
|
|
561
|
-
)
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/* === Comparative Analysis === */
|
|
565
|
-
|
|
566
|
-
const runComparison = async () => {
|
|
567
|
-
console.log(`\n${'='.repeat(60)}`)
|
|
568
|
-
console.log('STORE & LIST IMPLEMENTATION BENCHMARK')
|
|
569
|
-
console.log(`Iterations: ${ITERATIONS} | Method Calls: ${METHOD_CALLS}`)
|
|
570
|
-
console.log(`${'='.repeat(60)}`)
|
|
571
|
-
|
|
572
|
-
// Test functionality first
|
|
573
|
-
testStoreFunctionality()
|
|
574
|
-
testListFunctionality()
|
|
575
|
-
|
|
576
|
-
// Run Store benchmarks
|
|
577
|
-
console.log(`\n${'='.repeat(40)}`)
|
|
578
|
-
console.log('STORE BENCHMARKS')
|
|
579
|
-
console.log(`${'='.repeat(40)}`)
|
|
580
|
-
const factoryStores = await benchmarkFactory()
|
|
581
|
-
const classStores = await benchmarkClass()
|
|
582
|
-
const directClassStores = await benchmarkDirectClass()
|
|
583
|
-
|
|
584
|
-
// Run List benchmarks
|
|
585
|
-
console.log(`\n${'='.repeat(40)}`)
|
|
586
|
-
console.log('LIST BENCHMARKS')
|
|
587
|
-
console.log(`${'='.repeat(40)}`)
|
|
588
|
-
const factoryLists = await benchmarkFactoryList()
|
|
589
|
-
const directClassLists = await benchmarkDirectClassList()
|
|
590
|
-
|
|
591
|
-
// Detailed memory analysis for both Store and List
|
|
592
|
-
const sampleFactory = createFactoryStore(testData)
|
|
593
|
-
const sampleClass = createClassStore(testData)
|
|
594
|
-
const sampleFactoryList = createFactoryList(testListData)
|
|
595
|
-
const sampleClassList = new List(testListData)
|
|
596
|
-
|
|
597
|
-
const factoryStoreAnalysis = analyzeObjectStructure(
|
|
598
|
-
sampleFactory,
|
|
599
|
-
'Final Factory Store Analysis',
|
|
600
|
-
)
|
|
601
|
-
const classStoreAnalysis = analyzeObjectStructure(
|
|
602
|
-
sampleClass,
|
|
603
|
-
'Final Class Store Analysis',
|
|
604
|
-
)
|
|
605
|
-
const factoryListAnalysis = analyzeObjectStructure(
|
|
606
|
-
sampleFactoryList,
|
|
607
|
-
'Final Factory List Analysis',
|
|
608
|
-
)
|
|
609
|
-
const classListAnalysis = analyzeObjectStructure(
|
|
610
|
-
sampleClassList,
|
|
611
|
-
'Final Class List Analysis',
|
|
612
|
-
)
|
|
613
|
-
|
|
614
|
-
console.log('\n=== Memory Analysis Summary ===')
|
|
615
|
-
|
|
616
|
-
// Store Memory Analysis
|
|
617
|
-
console.log('\n--- Store Memory Analysis ---')
|
|
618
|
-
console.log(
|
|
619
|
-
`Store Factory Method Duplication: ${factoryStoreAnalysis.ownMethods * ITERATIONS} function instances`,
|
|
620
|
-
)
|
|
621
|
-
console.log(
|
|
622
|
-
`Store Class Method Sharing: ${classStoreAnalysis.prototypeMethods} shared function instances`,
|
|
623
|
-
)
|
|
624
|
-
|
|
625
|
-
const storeMethodMemorySaving =
|
|
626
|
-
((factoryStoreAnalysis.ownMethods * ITERATIONS -
|
|
627
|
-
classStoreAnalysis.prototypeMethods) *
|
|
628
|
-
200) /
|
|
629
|
-
1024 /
|
|
630
|
-
1024
|
|
631
|
-
console.log(
|
|
632
|
-
`Store Estimated Method Memory Savings: ${storeMethodMemorySaving.toFixed(2)}MB`,
|
|
633
|
-
)
|
|
634
|
-
|
|
635
|
-
// List Memory Analysis
|
|
636
|
-
console.log('\n--- List Memory Analysis ---')
|
|
637
|
-
console.log(
|
|
638
|
-
`List Factory Method Duplication: ${factoryListAnalysis.ownMethods * ITERATIONS} function instances`,
|
|
639
|
-
)
|
|
640
|
-
console.log(
|
|
641
|
-
`List Class Method Sharing: ${classListAnalysis.prototypeMethods} shared function instances`,
|
|
642
|
-
)
|
|
643
|
-
|
|
644
|
-
const listMethodMemorySaving =
|
|
645
|
-
((factoryListAnalysis.ownMethods * ITERATIONS -
|
|
646
|
-
classListAnalysis.prototypeMethods) *
|
|
647
|
-
200) /
|
|
648
|
-
1024 /
|
|
649
|
-
1024
|
|
650
|
-
console.log(
|
|
651
|
-
`List Estimated Method Memory Savings: ${listMethodMemorySaving.toFixed(2)}MB`,
|
|
652
|
-
)
|
|
653
|
-
|
|
654
|
-
const totalMethodMemorySaving =
|
|
655
|
-
storeMethodMemorySaving + listMethodMemorySaving
|
|
656
|
-
console.log(
|
|
657
|
-
`Total Estimated Method Memory Savings: ${totalMethodMemorySaving.toFixed(2)}MB`,
|
|
658
|
-
)
|
|
659
|
-
|
|
660
|
-
console.log('\n=== Performance Comparison Summary ===')
|
|
661
|
-
console.log('Direct Class (No Proxy) vs Proxy Class vs Factory approaches:')
|
|
662
|
-
console.log('- Direct Class should have fastest method calls')
|
|
663
|
-
console.log('- Proxy Class has convenience but method call overhead')
|
|
664
|
-
console.log('- Factory has per-instance method overhead')
|
|
665
|
-
|
|
666
|
-
// Keep references to prevent GC during measurement
|
|
667
|
-
return {
|
|
668
|
-
factoryStores,
|
|
669
|
-
classStores,
|
|
670
|
-
directClassStores,
|
|
671
|
-
factoryLists,
|
|
672
|
-
directClassLists,
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/* === Export === */
|
|
677
|
-
|
|
678
|
-
export { runComparison }
|
|
679
|
-
|
|
680
|
-
// Auto-run if this file is executed directly
|
|
681
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
682
|
-
runComparison().catch(console.error)
|
|
683
|
-
}
|