@blac/core 2.0.0-rn-12 → 2.0.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.
@@ -1,655 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { ProxyFactory } from '../ProxyFactory';
3
- import { Cubit } from '../../Cubit';
4
-
5
- // Mock consumer tracker
6
- const createMockTracker = () => ({
7
- trackAccess: vi.fn(),
8
- });
9
-
10
- // Test objects
11
- const simpleObject = {
12
- name: 'John',
13
- age: 30,
14
- active: true,
15
- };
16
-
17
- const nestedObject = {
18
- user: {
19
- name: 'John',
20
- profile: {
21
- email: 'john@example.com',
22
- settings: {
23
- theme: 'dark',
24
- notifications: true,
25
- },
26
- },
27
- },
28
- metadata: {
29
- created: new Date('2024-01-01'),
30
- tags: ['user', 'admin'],
31
- },
32
- };
33
-
34
- // Test Cubit with getters
35
- class TestCubit extends Cubit<{ count: number; multiplier: number }> {
36
- constructor() {
37
- super({ count: 0, multiplier: 2 });
38
- }
39
-
40
- get doubled() {
41
- return this.state.count * 2;
42
- }
43
-
44
- get quadrupled() {
45
- return this.doubled * 2;
46
- }
47
-
48
- increment = () => {
49
- this.patch({ count: this.state.count + 1 });
50
- };
51
- }
52
-
53
- describe('ProxyFactory', () => {
54
- let consumerRef: object;
55
- let tracker: ReturnType<typeof createMockTracker>;
56
-
57
- beforeEach(() => {
58
- consumerRef = { id: 'test-consumer' };
59
- tracker = createMockTracker();
60
- ProxyFactory.resetStats();
61
- });
62
-
63
- afterEach(() => {
64
- vi.clearAllMocks();
65
- });
66
-
67
- describe('State Proxy Creation', () => {
68
- it('should create state proxy and track property access', () => {
69
- const proxy = ProxyFactory.createStateProxy({
70
- target: simpleObject,
71
- consumerRef,
72
- consumerTracker: tracker,
73
- });
74
-
75
- // Access properties
76
- const name = proxy.name;
77
- const age = proxy.age;
78
-
79
- expect(name).toBe('John');
80
- expect(age).toBe(30);
81
-
82
- // Verify tracking
83
- expect(tracker.trackAccess).toHaveBeenCalledWith(
84
- consumerRef,
85
- 'state',
86
- 'name',
87
- 'John',
88
- );
89
- expect(tracker.trackAccess).toHaveBeenCalledWith(
90
- consumerRef,
91
- 'state',
92
- 'age',
93
- 30,
94
- );
95
- });
96
-
97
- it('should handle nested object proxying', () => {
98
- const proxy = ProxyFactory.createStateProxy({
99
- target: nestedObject,
100
- consumerRef,
101
- consumerTracker: tracker,
102
- });
103
-
104
- // Access nested properties
105
- const email = proxy.user.profile.email;
106
- const theme = proxy.user.profile.settings.theme;
107
-
108
- expect(email).toBe('john@example.com');
109
- expect(theme).toBe('dark');
110
-
111
- // Verify nested tracking
112
- expect(tracker.trackAccess).toHaveBeenCalledWith(
113
- consumerRef,
114
- 'state',
115
- 'user',
116
- undefined,
117
- );
118
- expect(tracker.trackAccess).toHaveBeenCalledWith(
119
- consumerRef,
120
- 'state',
121
- 'user.profile',
122
- undefined,
123
- );
124
- expect(tracker.trackAccess).toHaveBeenCalledWith(
125
- consumerRef,
126
- 'state',
127
- 'user.profile.email',
128
- 'john@example.com',
129
- );
130
- expect(tracker.trackAccess).toHaveBeenCalledWith(
131
- consumerRef,
132
- 'state',
133
- 'user.profile.settings',
134
- undefined,
135
- );
136
- expect(tracker.trackAccess).toHaveBeenCalledWith(
137
- consumerRef,
138
- 'state',
139
- 'user.profile.settings.theme',
140
- 'dark',
141
- );
142
- });
143
-
144
- it('should not proxy primitive values', () => {
145
- const proxy = ProxyFactory.createStateProxy({
146
- target: 'primitive' as any,
147
- consumerRef,
148
- consumerTracker: tracker,
149
- });
150
-
151
- expect(proxy).toBe('primitive');
152
- expect(tracker.trackAccess).not.toHaveBeenCalled();
153
- });
154
-
155
- it('should not proxy null or undefined', () => {
156
- const nullProxy = ProxyFactory.createStateProxy({
157
- target: null as any,
158
- consumerRef,
159
- consumerTracker: tracker,
160
- });
161
-
162
- const undefinedProxy = ProxyFactory.createStateProxy({
163
- target: undefined as any,
164
- consumerRef,
165
- consumerTracker: tracker,
166
- });
167
-
168
- expect(nullProxy).toBe(null);
169
- expect(undefinedProxy).toBe(undefined);
170
- });
171
-
172
- it('should handle arrays correctly', () => {
173
- const arrayObject = {
174
- items: [1, 2, 3],
175
- users: [{ name: 'John' }, { name: 'Jane' }],
176
- };
177
-
178
- const proxy = ProxyFactory.createStateProxy({
179
- target: arrayObject,
180
- consumerRef,
181
- consumerTracker: tracker,
182
- });
183
-
184
- // Access array properties
185
- const length = proxy.items.length;
186
- const firstItem = proxy.items[0];
187
- const userName = proxy.users[1].name;
188
-
189
- expect(length).toBe(3);
190
- expect(firstItem).toBe(1);
191
- expect(userName).toBe('Jane');
192
-
193
- // Array methods should work
194
- const mapped = proxy.items.map((x) => x * 2);
195
- expect(mapped).toEqual([2, 4, 6]);
196
- });
197
-
198
- it('should prevent state mutations', () => {
199
- const proxy = ProxyFactory.createStateProxy({
200
- target: simpleObject,
201
- consumerRef,
202
- consumerTracker: tracker,
203
- });
204
-
205
- // Try to mutate - proxy set trap returns false which throws in strict mode
206
- expect(() => {
207
- (proxy as any).name = 'Jane';
208
- }).toThrow(); // Throws in strict mode
209
-
210
- expect(proxy.name).toBe('John'); // Unchanged
211
- });
212
-
213
- it('should prevent property deletion', () => {
214
- const proxy = ProxyFactory.createStateProxy({
215
- target: { ...simpleObject },
216
- consumerRef,
217
- consumerTracker: tracker,
218
- });
219
-
220
- // Try to delete - proxy deleteProperty trap returns false which throws in strict mode
221
- expect(() => {
222
- delete (proxy as any).name;
223
- }).toThrow(); // Throws in strict mode
224
-
225
- expect(proxy.name).toBe('John'); // Still exists
226
- });
227
-
228
- it('should handle symbols correctly', () => {
229
- const sym = Symbol('test');
230
- const objWithSymbol = {
231
- [sym]: 'symbol value',
232
- regular: 'regular value',
233
- };
234
-
235
- const proxy = ProxyFactory.createStateProxy({
236
- target: objWithSymbol,
237
- consumerRef,
238
- consumerTracker: tracker,
239
- });
240
-
241
- // Symbol access should work without tracking
242
- expect(proxy[sym]).toBe('symbol value');
243
- expect(tracker.trackAccess).not.toHaveBeenCalledWith(
244
- consumerRef,
245
- 'state',
246
- expect.any(Symbol),
247
- expect.anything(),
248
- );
249
- });
250
- });
251
-
252
- describe('Class Proxy Creation', () => {
253
- it('should create class proxy and track getter access', () => {
254
- const cubit = new TestCubit();
255
- const proxy = ProxyFactory.createClassProxy({
256
- target: cubit,
257
- consumerRef,
258
- consumerTracker: tracker,
259
- });
260
-
261
- // Access getter
262
- const doubled = proxy.doubled;
263
-
264
- expect(doubled).toBe(0);
265
- expect(tracker.trackAccess).toHaveBeenCalledWith(
266
- consumerRef,
267
- 'class',
268
- 'doubled',
269
- 0,
270
- );
271
- });
272
-
273
- it('should not track method access', () => {
274
- const cubit = new TestCubit();
275
- const proxy = ProxyFactory.createClassProxy({
276
- target: cubit,
277
- consumerRef,
278
- consumerTracker: tracker,
279
- });
280
-
281
- // Access method
282
- const increment = proxy.increment;
283
-
284
- expect(typeof increment).toBe('function');
285
- expect(tracker.trackAccess).not.toHaveBeenCalledWith(
286
- consumerRef,
287
- 'class',
288
- 'increment',
289
- expect.anything(),
290
- );
291
-
292
- // Method should still work
293
- increment();
294
- expect(cubit.state.count).toBe(1);
295
- });
296
-
297
- it('should handle getter inheritance correctly', () => {
298
- class Parent {
299
- value = 10;
300
- get parentGetter() {
301
- return this.value * 2;
302
- }
303
- }
304
-
305
- class Child extends Parent {
306
- get childGetter() {
307
- return this.value * 3;
308
- }
309
- }
310
-
311
- const child = new Child();
312
- const proxy = ProxyFactory.createClassProxy({
313
- target: child,
314
- consumerRef,
315
- consumerTracker: tracker,
316
- });
317
-
318
- // Access both getters
319
- const parentValue = proxy.parentGetter;
320
- const childValue = proxy.childGetter;
321
-
322
- expect(parentValue).toBe(20);
323
- expect(childValue).toBe(30);
324
-
325
- // Both should be tracked
326
- expect(tracker.trackAccess).toHaveBeenCalledWith(
327
- consumerRef,
328
- 'class',
329
- 'parentGetter',
330
- 20,
331
- );
332
- expect(tracker.trackAccess).toHaveBeenCalledWith(
333
- consumerRef,
334
- 'class',
335
- 'childGetter',
336
- 30,
337
- );
338
- });
339
-
340
- it('should handle deep prototype chains safely', () => {
341
- // Create deep prototype chain
342
- const createDeepChain = (depth: number) => {
343
- let current: any = {
344
- get deepGetter() {
345
- return 'deep';
346
- },
347
- };
348
-
349
- for (let i = 0; i < depth; i++) {
350
- const parent = Object.create(current);
351
- current = parent;
352
- }
353
-
354
- return current;
355
- };
356
-
357
- const deepObject = createDeepChain(15); // Exceeds MAX_PROTOTYPE_DEPTH
358
- const proxy = ProxyFactory.createClassProxy({
359
- target: deepObject,
360
- consumerRef,
361
- consumerTracker: tracker,
362
- });
363
-
364
- // Should handle without infinite loop
365
- const value = proxy.deepGetter;
366
- expect(value).toBe('deep');
367
- });
368
-
369
- it('should handle circular prototype references', () => {
370
- // Can't actually create circular prototype chains - JS prevents this
371
- // Instead test deep prototype chains with WeakSet tracking
372
- const deepObj = { value: 1 };
373
- let current = deepObj;
374
-
375
- // Create a deep chain instead
376
- for (let i = 0; i < 15; i++) {
377
- const next = Object.create(current);
378
- next[`level${i}`] = i;
379
- current = next;
380
- }
381
-
382
- const proxy = ProxyFactory.createClassProxy({
383
- target: current,
384
- consumerRef,
385
- consumerTracker: tracker,
386
- });
387
-
388
- // Should handle deep chains
389
- expect(proxy.value).toBe(1);
390
- });
391
- });
392
-
393
- describe('Cache Management', () => {
394
- it('should cache proxies for same target and consumer', () => {
395
- const proxy1 = ProxyFactory.createStateProxy({
396
- target: simpleObject,
397
- consumerRef,
398
- consumerTracker: tracker,
399
- });
400
-
401
- const proxy2 = ProxyFactory.createStateProxy({
402
- target: simpleObject,
403
- consumerRef,
404
- consumerTracker: tracker,
405
- });
406
-
407
- expect(proxy1).toBe(proxy2); // Same reference
408
-
409
- // Stats should show cache hit
410
- const stats = ProxyFactory.getStats();
411
- expect(stats.cacheHits).toBeGreaterThan(0);
412
- });
413
-
414
- it('should create different proxies for different consumers', () => {
415
- const consumer1 = { id: 'consumer1' };
416
- const consumer2 = { id: 'consumer2' };
417
-
418
- const proxy1 = ProxyFactory.createStateProxy({
419
- target: simpleObject,
420
- consumerRef: consumer1,
421
- consumerTracker: tracker,
422
- });
423
-
424
- const proxy2 = ProxyFactory.createStateProxy({
425
- target: simpleObject,
426
- consumerRef: consumer2,
427
- consumerTracker: tracker,
428
- });
429
-
430
- expect(proxy1).not.toBe(proxy2); // Different references
431
- });
432
-
433
- it('should handle WeakMap cache correctly', () => {
434
- let target: any = { value: 'test' };
435
-
436
- const proxy = ProxyFactory.createStateProxy({
437
- target,
438
- consumerRef,
439
- consumerTracker: tracker,
440
- });
441
-
442
- expect(proxy.value).toBe('test');
443
-
444
- // Remove reference to target
445
- target = null;
446
-
447
- // Proxy should still work until garbage collected
448
- expect(proxy.value).toBe('test');
449
- });
450
- });
451
-
452
- describe('getProxyState and getProxyBlocInstance helpers', () => {
453
- it('should create state proxy through helper', () => {
454
- const state = { count: 5 };
455
- const proxy = ProxyFactory.getProxyState({
456
- state,
457
- consumerRef,
458
- consumerTracker: tracker,
459
- });
460
-
461
- const count = proxy.count;
462
- expect(count).toBe(5);
463
- expect(tracker.trackAccess).toHaveBeenCalledWith(
464
- consumerRef,
465
- 'state',
466
- 'count',
467
- 5,
468
- );
469
- });
470
-
471
- it('should create bloc instance proxy through helper', () => {
472
- const cubit = new TestCubit();
473
- const proxy = ProxyFactory.getProxyBlocInstance({
474
- blocInstance: cubit,
475
- consumerRef,
476
- consumerTracker: tracker,
477
- });
478
-
479
- const doubled = proxy.doubled;
480
- expect(doubled).toBe(0);
481
- expect(tracker.trackAccess).toHaveBeenCalledWith(
482
- consumerRef,
483
- 'class',
484
- 'doubled',
485
- 0,
486
- );
487
- });
488
- });
489
-
490
- describe('Statistics and Performance', () => {
491
- it('should track proxy creation statistics', () => {
492
- // Create various proxies
493
- ProxyFactory.createStateProxy({
494
- target: simpleObject,
495
- consumerRef,
496
- consumerTracker: tracker,
497
- });
498
-
499
- ProxyFactory.createClassProxy({
500
- target: new TestCubit(),
501
- consumerRef,
502
- consumerTracker: tracker,
503
- });
504
-
505
- ProxyFactory.createStateProxy({
506
- target: nestedObject,
507
- consumerRef,
508
- consumerTracker: tracker,
509
- });
510
-
511
- const stats = ProxyFactory.getStats();
512
- expect(stats.stateProxiesCreated).toBeGreaterThan(0);
513
- expect(stats.classProxiesCreated).toBeGreaterThan(0);
514
- expect(stats.totalProxiesCreated).toBe(
515
- stats.stateProxiesCreated + stats.classProxiesCreated,
516
- );
517
- expect(stats.propertyAccesses).toBe(0); // No properties accessed yet
518
- });
519
-
520
- it('should reset statistics', () => {
521
- // Create some proxies
522
- ProxyFactory.createStateProxy({
523
- target: simpleObject,
524
- consumerRef,
525
- consumerTracker: tracker,
526
- });
527
-
528
- let stats = ProxyFactory.getStats();
529
- expect(stats.totalProxiesCreated).toBeGreaterThan(0);
530
-
531
- // Reset
532
- ProxyFactory.resetStats();
533
-
534
- stats = ProxyFactory.getStats();
535
- expect(stats.totalProxiesCreated).toBe(0);
536
- expect(stats.stateProxiesCreated).toBe(0);
537
- expect(stats.classProxiesCreated).toBe(0);
538
- });
539
-
540
- it('should calculate cache efficiency', () => {
541
- const proxy1 = ProxyFactory.createStateProxy({
542
- target: simpleObject,
543
- consumerRef,
544
- consumerTracker: tracker,
545
- });
546
-
547
- // Cache hit
548
- const proxy2 = ProxyFactory.createStateProxy({
549
- target: simpleObject,
550
- consumerRef,
551
- consumerTracker: tracker,
552
- });
553
-
554
- const stats = ProxyFactory.getStats();
555
- expect(stats.cacheEfficiency).toMatch(/\d+\.\d+%/);
556
- });
557
- });
558
-
559
- describe('Edge Cases', () => {
560
- it('should handle objects with non-standard prototypes', () => {
561
- const objWithoutProto = Object.create(null);
562
- objWithoutProto.value = 'test';
563
-
564
- const proxy = ProxyFactory.createStateProxy({
565
- target: objWithoutProto,
566
- consumerRef,
567
- consumerTracker: tracker,
568
- });
569
-
570
- expect(proxy.value).toBe('test');
571
- });
572
-
573
- it('should handle Date objects', () => {
574
- const date = new Date('2024-01-01');
575
- const obj = { date };
576
-
577
- const proxy = ProxyFactory.createStateProxy({
578
- target: obj,
579
- consumerRef,
580
- consumerTracker: tracker,
581
- });
582
-
583
- // Date should not be proxied (not plain object)
584
- expect(proxy.date).toBe(date);
585
- expect(proxy.date instanceof Date).toBe(true);
586
- });
587
-
588
- it('should handle property descriptor edge cases', () => {
589
- const obj: { readOnly?: string } = {};
590
- Object.defineProperty(obj, 'readOnly', {
591
- value: 'fixed',
592
- writable: false,
593
- enumerable: true,
594
- });
595
-
596
- const proxy = ProxyFactory.createStateProxy({
597
- target: obj,
598
- consumerRef,
599
- consumerTracker: tracker,
600
- });
601
-
602
- expect(proxy.readOnly).toBe('fixed');
603
- expect(Object.getOwnPropertyDescriptor(proxy, 'readOnly')).toEqual({
604
- value: 'fixed',
605
- writable: false,
606
- enumerable: true,
607
- configurable: false,
608
- });
609
- });
610
-
611
- it('should handle missing tracker or consumerRef', () => {
612
- // Missing tracker
613
- const proxy1 = ProxyFactory.createStateProxy({
614
- target: simpleObject,
615
- consumerRef,
616
- consumerTracker: null as any,
617
- });
618
- expect(proxy1).toBe(simpleObject); // Returns original
619
-
620
- // Missing consumerRef
621
- const proxy2 = ProxyFactory.createStateProxy({
622
- target: simpleObject,
623
- consumerRef: null as any,
624
- consumerTracker: tracker,
625
- });
626
- expect(proxy2).toBe(simpleObject); // Returns original
627
- });
628
-
629
- it("should handle 'in' operator", () => {
630
- const proxy = ProxyFactory.createStateProxy({
631
- target: simpleObject,
632
- consumerRef,
633
- consumerTracker: tracker,
634
- });
635
-
636
- expect('name' in proxy).toBe(true);
637
- expect('nonexistent' in proxy).toBe(false);
638
- });
639
-
640
- it('should handle Object.keys and similar operations', () => {
641
- const proxy = ProxyFactory.createStateProxy({
642
- target: simpleObject,
643
- consumerRef,
644
- consumerTracker: tracker,
645
- });
646
-
647
- expect(Object.keys(proxy)).toEqual(['name', 'age', 'active']);
648
- expect(Object.getOwnPropertyNames(proxy)).toEqual([
649
- 'name',
650
- 'age',
651
- 'active',
652
- ]);
653
- });
654
- });
655
- });
@@ -1,3 +0,0 @@
1
- export * from './BlacAdapter';
2
- export * from './ConsumerTracker';
3
- export * from './ProxyFactory';
package/src/events.ts DELETED
@@ -1,3 +0,0 @@
1
- export class PropsUpdated<P = any> {
2
- constructor(public readonly props: P) {}
3
- }
package/src/index.ts DELETED
@@ -1,17 +0,0 @@
1
- export * from './Blac';
2
- export * from './BlacObserver';
3
- export * from './Bloc';
4
- export * from './BlocBase';
5
- export * from './Cubit';
6
- export * from './types';
7
- export * from './events';
8
-
9
- // Utilities
10
- export * from './utils/uuid';
11
- export * from './utils/shallowEqual';
12
-
13
- // Test utilities
14
- export * from './testing';
15
-
16
- // Adapter
17
- export * from './adapter';