@blac/core 2.0.0-rc-16 → 2.0.0-rc.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/dist/.tsbuildinfo +1 -0
- package/dist/Blac.d.ts +246 -0
- package/dist/Blac.d.ts.map +1 -0
- package/dist/Blac.js +625 -0
- package/dist/Blac.js.map +1 -0
- package/dist/Bloc.d.ts +41 -0
- package/dist/Bloc.d.ts.map +1 -0
- package/dist/Bloc.js +140 -0
- package/dist/Bloc.js.map +1 -0
- package/dist/BlocBase.d.ts +110 -0
- package/dist/BlocBase.d.ts.map +1 -0
- package/dist/BlocBase.js +287 -0
- package/dist/BlocBase.js.map +1 -0
- package/dist/Cubit.d.ts +25 -0
- package/dist/Cubit.d.ts.map +1 -0
- package/dist/Cubit.js +64 -0
- package/dist/Cubit.js.map +1 -0
- package/dist/adapter/BlacAdapter.d.ts +71 -0
- package/dist/adapter/BlacAdapter.d.ts.map +1 -0
- package/dist/adapter/BlacAdapter.js +373 -0
- package/dist/adapter/BlacAdapter.js.map +1 -0
- package/dist/adapter/ProxyFactory.d.ts +50 -0
- package/dist/adapter/ProxyFactory.d.ts.map +1 -0
- package/dist/adapter/ProxyFactory.js +150 -0
- package/dist/adapter/ProxyFactory.js.map +1 -0
- package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +19 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/errors/BlacError.d.ts +38 -0
- package/dist/errors/BlacError.d.ts.map +1 -0
- package/dist/errors/BlacError.js +55 -0
- package/dist/errors/BlacError.js.map +1 -0
- package/dist/errors/ErrorManager.d.ts +39 -0
- package/dist/errors/ErrorManager.d.ts.map +1 -0
- package/dist/errors/ErrorManager.js +91 -0
- package/dist/errors/ErrorManager.js.map +1 -0
- package/dist/errors/handleError.d.ts +24 -0
- package/dist/errors/handleError.d.ts.map +1 -0
- package/dist/errors/handleError.js +62 -0
- package/dist/errors/handleError.js.map +1 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/{src/events.ts → dist/events.d.ts} +4 -2
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/index.cjs +38 -0
- package/{src/index.ts → dist/index.d.ts} +1 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
- package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
- package/dist/lifecycle/BlocLifecycle.js +123 -0
- package/dist/lifecycle/BlocLifecycle.js.map +1 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +18 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
- package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/BlocPluginRegistry.js +201 -0
- package/dist/plugins/BlocPluginRegistry.js.map +1 -0
- package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
- package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/SystemPluginRegistry.js +184 -0
- package/dist/plugins/SystemPluginRegistry.js.map +1 -0
- package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +26 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +103 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +3 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/subscription/SubscriptionManager.d.ts +67 -0
- package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
- package/dist/subscription/SubscriptionManager.js +317 -0
- package/dist/subscription/SubscriptionManager.js.map +1 -0
- package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
- package/dist/subscription/index.d.ts.map +1 -0
- package/dist/subscription/index.js +19 -0
- package/dist/subscription/index.js.map +1 -0
- package/dist/subscription/types.d.ts +65 -0
- package/dist/subscription/types.d.ts.map +1 -0
- package/dist/subscription/types.js +3 -0
- package/dist/subscription/types.js.map +1 -0
- package/dist/testing.d.ts +85 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +186 -0
- package/dist/testing.js.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +11 -28
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/BatchingManager.d.ts +28 -0
- package/dist/utils/BatchingManager.d.ts.map +1 -0
- package/dist/utils/BatchingManager.js +48 -0
- package/dist/utils/BatchingManager.js.map +1 -0
- package/dist/utils/RerenderLogger.d.ts +60 -0
- package/dist/utils/RerenderLogger.d.ts.map +1 -0
- package/dist/utils/RerenderLogger.js +205 -0
- package/dist/utils/RerenderLogger.js.map +1 -0
- package/dist/utils/generateInstanceId.d.ts +11 -0
- package/dist/utils/generateInstanceId.d.ts.map +1 -0
- package/dist/utils/generateInstanceId.js +50 -0
- package/dist/utils/generateInstanceId.js.map +1 -0
- package/dist/utils/shallowEqual.d.ts +9 -0
- package/dist/utils/shallowEqual.d.ts.map +1 -0
- package/dist/utils/shallowEqual.js +35 -0
- package/dist/utils/shallowEqual.js.map +1 -0
- package/dist/utils/uuid.d.ts +10 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +24 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +17 -6
- package/src/Blac.ts +0 -802
- package/src/Bloc.ts +0 -180
- package/src/BlocBase.ts +0 -398
- package/src/Cubit.ts +0 -68
- package/src/__tests__/Blac.config.test.ts +0 -79
- package/src/__tests__/Bloc.event.test.ts +0 -394
- package/src/__tests__/BlocBase.subscription.test.ts +0 -349
- package/src/__tests__/Cubit.test.ts +0 -462
- package/src/__tests__/memory-leaks.test.ts +0 -383
- package/src/__tests__/plugins.test.ts +0 -464
- package/src/adapter/BlacAdapter.ts +0 -467
- package/src/adapter/ProxyFactory.ts +0 -249
- package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
- package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
- package/src/errors/BlacError.ts +0 -69
- package/src/errors/ErrorManager.ts +0 -101
- package/src/errors/handleError.ts +0 -114
- package/src/errors/index.ts +0 -13
- package/src/lifecycle/BlocLifecycle.ts +0 -156
- package/src/lifecycle/index.ts +0 -1
- package/src/plugins/BlocPluginRegistry.ts +0 -240
- package/src/plugins/SystemPluginRegistry.ts +0 -242
- package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
- package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
- package/src/plugins/types.ts +0 -129
- package/src/subscription/README.md +0 -132
- package/src/subscription/SubscriptionManager.ts +0 -395
- package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
- package/src/subscription/types.ts +0 -90
- package/src/testing.ts +0 -246
- package/src/utils/BatchingManager.ts +0 -59
- package/src/utils/RerenderLogger.ts +0 -265
- package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
- package/src/utils/__tests__/shallowEqual.test.ts +0 -171
- package/src/utils/__tests__/uuid.test.ts +0 -144
- package/src/utils/generateInstanceId.ts +0 -54
- package/src/utils/shallowEqual.ts +0 -40
- package/src/utils/uuid.ts +0 -22
|
@@ -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: number) => 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
|
-
});
|
package/src/errors/BlacError.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export enum ErrorCategory {
|
|
2
|
-
LIFECYCLE = 'LIFECYCLE',
|
|
3
|
-
STATE = 'STATE',
|
|
4
|
-
PLUGIN = 'PLUGIN',
|
|
5
|
-
VALIDATION = 'VALIDATION',
|
|
6
|
-
DISPOSAL = 'DISPOSAL',
|
|
7
|
-
CONSUMER = 'CONSUMER',
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export enum ErrorSeverity {
|
|
11
|
-
FATAL = 'FATAL', // System cannot continue
|
|
12
|
-
ERROR = 'ERROR', // Operation failed but system continues
|
|
13
|
-
WARNING = 'WARNING', // Potential issue but operation succeeded
|
|
14
|
-
INFO = 'INFO', // Informational, not an error
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type BlacErrorContext = {
|
|
18
|
-
blocName?: string;
|
|
19
|
-
blocId?: string;
|
|
20
|
-
consumerId?: string;
|
|
21
|
-
pluginName?: string;
|
|
22
|
-
operation?: string;
|
|
23
|
-
metadata?: Record<string, unknown>;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export class BlacError extends Error {
|
|
27
|
-
constructor(
|
|
28
|
-
message: string,
|
|
29
|
-
public readonly category: ErrorCategory,
|
|
30
|
-
public readonly severity: ErrorSeverity,
|
|
31
|
-
public readonly context?: BlacErrorContext,
|
|
32
|
-
public readonly cause?: unknown,
|
|
33
|
-
) {
|
|
34
|
-
super(message);
|
|
35
|
-
this.name = 'BlacError';
|
|
36
|
-
|
|
37
|
-
// Ensure proper prototype chain
|
|
38
|
-
Object.setPrototypeOf(this, BlacError.prototype);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
toString(): string {
|
|
42
|
-
const contextStr = this.context
|
|
43
|
-
? ` [${Object.entries(this.context)
|
|
44
|
-
.filter(([_, v]) => v !== undefined)
|
|
45
|
-
.map(([k, v]) => `${k}: ${v}`)
|
|
46
|
-
.join(', ')}]`
|
|
47
|
-
: '';
|
|
48
|
-
|
|
49
|
-
return `${this.name} [${this.severity}/${this.category}]: ${this.message}${contextStr}`;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export type ErrorHandler = (error: BlacError) => void;
|
|
54
|
-
|
|
55
|
-
export type ErrorHandlingStrategy = {
|
|
56
|
-
shouldPropagate: (error: BlacError) => boolean;
|
|
57
|
-
shouldLog: (error: BlacError) => boolean;
|
|
58
|
-
handle: (error: BlacError) => void;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const DefaultErrorStrategy: ErrorHandlingStrategy = {
|
|
62
|
-
shouldPropagate: (error) => error.severity === ErrorSeverity.FATAL,
|
|
63
|
-
shouldLog: (error) => error.severity !== ErrorSeverity.INFO,
|
|
64
|
-
handle: (error) => {
|
|
65
|
-
if (DefaultErrorStrategy.shouldLog(error)) {
|
|
66
|
-
console.error(error.toString(), error.cause);
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
};
|