@blac/core 2.0.0-rc-14 → 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.
Files changed (162) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/Blac.d.ts +246 -0
  3. package/dist/Blac.d.ts.map +1 -0
  4. package/dist/Blac.js +625 -0
  5. package/dist/Blac.js.map +1 -0
  6. package/dist/Bloc.d.ts +41 -0
  7. package/dist/Bloc.d.ts.map +1 -0
  8. package/dist/Bloc.js +140 -0
  9. package/dist/Bloc.js.map +1 -0
  10. package/dist/BlocBase.d.ts +110 -0
  11. package/dist/BlocBase.d.ts.map +1 -0
  12. package/dist/BlocBase.js +287 -0
  13. package/dist/BlocBase.js.map +1 -0
  14. package/dist/Cubit.d.ts +25 -0
  15. package/dist/Cubit.d.ts.map +1 -0
  16. package/dist/Cubit.js +64 -0
  17. package/dist/Cubit.js.map +1 -0
  18. package/dist/adapter/BlacAdapter.d.ts +71 -0
  19. package/dist/adapter/BlacAdapter.d.ts.map +1 -0
  20. package/dist/adapter/BlacAdapter.js +373 -0
  21. package/dist/adapter/BlacAdapter.js.map +1 -0
  22. package/dist/adapter/ProxyFactory.d.ts +50 -0
  23. package/dist/adapter/ProxyFactory.d.ts.map +1 -0
  24. package/dist/adapter/ProxyFactory.js +150 -0
  25. package/dist/adapter/ProxyFactory.js.map +1 -0
  26. package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
  27. package/dist/adapter/index.d.ts.map +1 -0
  28. package/dist/adapter/index.js +19 -0
  29. package/dist/adapter/index.js.map +1 -0
  30. package/dist/errors/BlacError.d.ts +38 -0
  31. package/dist/errors/BlacError.d.ts.map +1 -0
  32. package/dist/errors/BlacError.js +55 -0
  33. package/dist/errors/BlacError.js.map +1 -0
  34. package/dist/errors/ErrorManager.d.ts +39 -0
  35. package/dist/errors/ErrorManager.d.ts.map +1 -0
  36. package/dist/errors/ErrorManager.js +91 -0
  37. package/dist/errors/ErrorManager.js.map +1 -0
  38. package/dist/errors/handleError.d.ts +24 -0
  39. package/dist/errors/handleError.d.ts.map +1 -0
  40. package/dist/errors/handleError.js +62 -0
  41. package/dist/errors/handleError.js.map +1 -0
  42. package/dist/errors/index.d.ts +5 -0
  43. package/dist/errors/index.d.ts.map +1 -0
  44. package/dist/errors/index.js +13 -0
  45. package/dist/errors/index.js.map +1 -0
  46. package/{src/events.ts → dist/events.d.ts} +4 -2
  47. package/dist/events.d.ts.map +1 -0
  48. package/dist/events.js +17 -0
  49. package/dist/events.js.map +1 -0
  50. package/dist/index.cjs +38 -0
  51. package/dist/index.d.ts +17 -1303
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +38 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
  56. package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
  57. package/dist/lifecycle/BlocLifecycle.js +123 -0
  58. package/dist/lifecycle/BlocLifecycle.js.map +1 -0
  59. package/dist/lifecycle/index.d.ts +2 -0
  60. package/dist/lifecycle/index.d.ts.map +1 -0
  61. package/dist/lifecycle/index.js +18 -0
  62. package/dist/lifecycle/index.js.map +1 -0
  63. package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
  64. package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
  65. package/dist/plugins/BlocPluginRegistry.js +201 -0
  66. package/dist/plugins/BlocPluginRegistry.js.map +1 -0
  67. package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
  68. package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
  69. package/dist/plugins/SystemPluginRegistry.js +184 -0
  70. package/dist/plugins/SystemPluginRegistry.js.map +1 -0
  71. package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
  72. package/dist/plugins/index.d.ts.map +1 -0
  73. package/dist/plugins/index.js +26 -0
  74. package/dist/plugins/index.js.map +1 -0
  75. package/dist/plugins/types.d.ts +103 -0
  76. package/dist/plugins/types.d.ts.map +1 -0
  77. package/dist/plugins/types.js +3 -0
  78. package/dist/plugins/types.js.map +1 -0
  79. package/dist/subscription/SubscriptionManager.d.ts +67 -0
  80. package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
  81. package/dist/subscription/SubscriptionManager.js +317 -0
  82. package/dist/subscription/SubscriptionManager.js.map +1 -0
  83. package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
  84. package/dist/subscription/index.d.ts.map +1 -0
  85. package/dist/subscription/index.js +19 -0
  86. package/dist/subscription/index.js.map +1 -0
  87. package/dist/subscription/types.d.ts +65 -0
  88. package/dist/subscription/types.d.ts.map +1 -0
  89. package/dist/subscription/types.js +3 -0
  90. package/dist/subscription/types.js.map +1 -0
  91. package/dist/testing.d.ts +85 -0
  92. package/dist/testing.d.ts.map +1 -0
  93. package/dist/testing.js +186 -0
  94. package/dist/testing.js.map +1 -0
  95. package/{src/types.ts → dist/types.d.ts} +11 -28
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +3 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/utils/BatchingManager.d.ts +28 -0
  100. package/dist/utils/BatchingManager.d.ts.map +1 -0
  101. package/dist/utils/BatchingManager.js +48 -0
  102. package/dist/utils/BatchingManager.js.map +1 -0
  103. package/dist/utils/RerenderLogger.d.ts +60 -0
  104. package/dist/utils/RerenderLogger.d.ts.map +1 -0
  105. package/dist/utils/RerenderLogger.js +205 -0
  106. package/dist/utils/RerenderLogger.js.map +1 -0
  107. package/dist/utils/generateInstanceId.d.ts +11 -0
  108. package/dist/utils/generateInstanceId.d.ts.map +1 -0
  109. package/dist/utils/generateInstanceId.js +50 -0
  110. package/dist/utils/generateInstanceId.js.map +1 -0
  111. package/dist/utils/shallowEqual.d.ts +9 -0
  112. package/dist/utils/shallowEqual.d.ts.map +1 -0
  113. package/dist/utils/shallowEqual.js +35 -0
  114. package/dist/utils/shallowEqual.js.map +1 -0
  115. package/dist/utils/uuid.d.ts +10 -0
  116. package/dist/utils/uuid.d.ts.map +1 -0
  117. package/dist/utils/uuid.js +24 -0
  118. package/dist/utils/uuid.js.map +1 -0
  119. package/package.json +17 -10
  120. package/dist/index.cjs.js +0 -11
  121. package/dist/index.cjs.js.map +0 -1
  122. package/dist/index.es.js +0 -2275
  123. package/dist/index.es.js.map +0 -1
  124. package/src/Blac.ts +0 -802
  125. package/src/Bloc.ts +0 -180
  126. package/src/BlocBase.ts +0 -398
  127. package/src/Cubit.ts +0 -68
  128. package/src/__tests__/Blac.config.test.ts +0 -79
  129. package/src/__tests__/Bloc.event.test.ts +0 -394
  130. package/src/__tests__/BlocBase.subscription.test.ts +0 -349
  131. package/src/__tests__/Cubit.test.ts +0 -462
  132. package/src/__tests__/memory-leaks.test.ts +0 -383
  133. package/src/__tests__/plugins.test.ts +0 -464
  134. package/src/adapter/BlacAdapter.ts +0 -467
  135. package/src/adapter/ProxyFactory.ts +0 -249
  136. package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
  137. package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
  138. package/src/errors/BlacError.ts +0 -69
  139. package/src/errors/ErrorManager.ts +0 -101
  140. package/src/errors/handleError.ts +0 -114
  141. package/src/errors/index.ts +0 -13
  142. package/src/index.ts +0 -26
  143. package/src/lifecycle/BlocLifecycle.ts +0 -156
  144. package/src/lifecycle/index.ts +0 -1
  145. package/src/plugins/BlocPluginRegistry.ts +0 -240
  146. package/src/plugins/SystemPluginRegistry.ts +0 -242
  147. package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
  148. package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
  149. package/src/plugins/types.ts +0 -129
  150. package/src/subscription/README.md +0 -132
  151. package/src/subscription/SubscriptionManager.ts +0 -395
  152. package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
  153. package/src/subscription/types.ts +0 -90
  154. package/src/testing.ts +0 -246
  155. package/src/utils/BatchingManager.ts +0 -59
  156. package/src/utils/RerenderLogger.ts +0 -265
  157. package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
  158. package/src/utils/__tests__/shallowEqual.test.ts +0 -171
  159. package/src/utils/__tests__/uuid.test.ts +0 -144
  160. package/src/utils/generateInstanceId.ts +0 -54
  161. package/src/utils/shallowEqual.ts +0 -40
  162. package/src/utils/uuid.ts +0 -22
@@ -1,467 +0,0 @@
1
- import { Blac } from '../Blac';
2
- import { BlocBase } from '../BlocBase';
3
- import { BlocConstructor, BlocState } from '../types';
4
- import { generateUUID } from '../utils/uuid';
5
- import { generateInstanceIdFromProps } from '../utils/generateInstanceId';
6
- import { ProxyFactory } from './ProxyFactory';
7
-
8
- export interface AdapterOptions<B extends BlocBase<any>> {
9
- instanceId?: string;
10
- dependencies?: (bloc: B) => unknown[];
11
- staticProps?: any;
12
- onMount?: (bloc: B) => void;
13
- onUnmount?: (bloc: B) => void;
14
- }
15
-
16
- /**
17
- * BlacAdapter orchestrates the connection between Bloc instances and React components.
18
- * It manages dependency tracking, lifecycle hooks, and proxy creation.
19
- */
20
- export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
21
- public readonly id = `adapter-${generateUUID()}`;
22
- public readonly blocConstructor: B;
23
- public readonly componentRef: { current: object } = { current: {} };
24
- public blocInstance!: InstanceType<B>;
25
-
26
- unmountTime: number = 0;
27
- mountTime: number = 0;
28
-
29
- // Subscription management
30
- private subscriptionId?: string;
31
- private unsubscribe?: () => void;
32
-
33
- // Dependency tracking
34
- private dependencyValues?: unknown[];
35
- private isUsingDependencies: boolean = false;
36
- private trackedPaths = new Set<string>();
37
- private pendingTrackedPaths = new Set<string>(); // Paths tracked before subscription exists
38
-
39
- // Proxy caching
40
- private cachedStateProxy?: BlocState<InstanceType<B>>;
41
- private cachedBlocProxy?: InstanceType<B>;
42
- private lastProxiedState?: BlocState<InstanceType<B>>;
43
-
44
- // Lifecycle state
45
- private hasMounted = false;
46
- private mountCount = 0;
47
-
48
- // Rerender tracking
49
- private lastState?: BlocState<InstanceType<B>>;
50
- private lastDependencyValues?: unknown[];
51
- private componentName?: string;
52
- private renderCount = 0;
53
- private lastDependenciesFn?: (bloc: InstanceType<B>) => unknown[];
54
-
55
- options?: AdapterOptions<InstanceType<B>>;
56
-
57
- constructor(
58
- instanceProps: { componentRef: { current: object }; blocConstructor: B },
59
- options?: typeof this.options,
60
- ) {
61
- this.options = options;
62
- this.blocConstructor = instanceProps.blocConstructor;
63
- this.componentRef = instanceProps.componentRef;
64
- this.isUsingDependencies = !!options?.dependencies;
65
-
66
- // Initialize bloc instance
67
- this.updateBlocInstance();
68
-
69
- // Initialize dependency values if using dependencies
70
- if (this.isUsingDependencies && options?.dependencies) {
71
- this.dependencyValues = options.dependencies(this.blocInstance);
72
- }
73
-
74
- // Notify plugins
75
- const metadata = this.getAdapterMetadata();
76
- Blac.getInstance().plugins.notifyAdapterCreated(this, metadata);
77
- }
78
-
79
- trackAccess(
80
- consumerRef: object,
81
- type: 'state' | 'class',
82
- path: string,
83
- value?: any,
84
- ): void {
85
- const fullPath = type === 'class' ? `_class.${path}` : path;
86
- this.trackedPaths.add(fullPath);
87
-
88
- if (this.subscriptionId) {
89
- this.blocInstance.trackAccess(this.subscriptionId, fullPath, value);
90
- } else {
91
- // No subscription ID yet - store for later
92
- this.pendingTrackedPaths.add(fullPath);
93
- if ((Blac.config as any).logLevel === 'debug') {
94
- Blac.log(`[BlacAdapter] trackAccess storing pending path: ${path}`);
95
- }
96
- }
97
- }
98
-
99
- updateBlocInstance(): InstanceType<B> {
100
- // Determine the instance ID
101
- let instanceId = this.options?.instanceId;
102
-
103
- // If no explicit instanceId provided but staticProps exist, generate from them
104
- if (!instanceId && this.options?.staticProps) {
105
- const generatedId = generateInstanceIdFromProps(this.options.staticProps);
106
- if (generatedId) {
107
- instanceId = generatedId;
108
- }
109
- }
110
-
111
- this.blocInstance = Blac.instance.getBloc<B>(this.blocConstructor, {
112
- constructorParams: this.options?.staticProps,
113
- id: instanceId,
114
- instanceRef: this.id,
115
- });
116
- return this.blocInstance;
117
- }
118
-
119
- createSubscription = (options: { onChange: () => void }) => {
120
- // If using manual dependencies, create a selector-based subscription
121
- if (this.isUsingDependencies && this.options?.dependencies) {
122
- this.unsubscribe = this.blocInstance.subscribeWithSelector(
123
- (_state) => this.options!.dependencies!(this.blocInstance),
124
- (newValues) => {
125
- const hasChanged = this.hasDependencyValuesChanged(
126
- this.dependencyValues,
127
- newValues as unknown[],
128
- );
129
-
130
- if (hasChanged) {
131
- this.dependencyValues = newValues as unknown[];
132
- options.onChange();
133
- }
134
- },
135
- );
136
- } else {
137
- // Create a component subscription with weak reference
138
- const weakRef = new WeakRef(this.componentRef.current);
139
- this.unsubscribe = this.blocInstance.subscribeComponent(
140
- weakRef,
141
- options.onChange,
142
- );
143
-
144
- // Get the subscription ID for tracking
145
- const subscriptions = (this.blocInstance._subscriptionManager as any)
146
- .subscriptions as Map<string, any>;
147
- this.subscriptionId = Array.from(subscriptions.keys()).pop();
148
-
149
- // Apply any pending tracked paths
150
- if (this.subscriptionId && this.pendingTrackedPaths.size > 0) {
151
- for (const path of this.pendingTrackedPaths) {
152
- this.blocInstance.trackAccess(this.subscriptionId, path);
153
- }
154
- this.pendingTrackedPaths.clear();
155
- }
156
- }
157
-
158
- // Don't call onChange initially - let React handle the initial render
159
-
160
- return () => {
161
- if (this.unsubscribe) {
162
- this.unsubscribe();
163
- this.unsubscribe = undefined;
164
- this.subscriptionId = undefined;
165
- }
166
- };
167
- };
168
-
169
- hasDependencyValuesChanged = (
170
- oldValues: unknown[] | undefined,
171
- newValues: unknown[],
172
- ): boolean => {
173
- if (!oldValues) return true;
174
- if (oldValues.length !== newValues.length) return true;
175
-
176
- for (let i = 0; i < oldValues.length; i++) {
177
- if (!Object.is(oldValues[i], newValues[i])) {
178
- return true;
179
- }
180
- }
181
-
182
- return false;
183
- };
184
-
185
- getStateProxy = (): BlocState<InstanceType<B>> => {
186
- // If using manual dependencies or proxy tracking is disabled, return raw state
187
- if (this.isUsingDependencies || !Blac.config.proxyDependencyTracking) {
188
- return this.blocInstance.state;
189
- }
190
-
191
- // Return cached proxy if state hasn't changed
192
- const currentState = this.blocInstance.state;
193
- if (this.cachedStateProxy && this.lastProxiedState === currentState) {
194
- return this.cachedStateProxy;
195
- }
196
-
197
- // Create new proxy for new state
198
- this.lastProxiedState = currentState;
199
- this.cachedStateProxy = this.createStateProxy({ target: currentState });
200
- return this.cachedStateProxy!;
201
- };
202
-
203
- getBlocProxy = (): InstanceType<B> => {
204
- // If using manual dependencies or proxy tracking is disabled, return raw bloc
205
- if (this.isUsingDependencies || !Blac.config.proxyDependencyTracking) {
206
- return this.blocInstance;
207
- }
208
-
209
- // Return cached proxy if bloc instance hasn't changed
210
- if (this.cachedBlocProxy) {
211
- return this.cachedBlocProxy;
212
- }
213
-
214
- // Create and cache proxy
215
- this.cachedBlocProxy = this.createClassProxy({ target: this.blocInstance });
216
- return this.cachedBlocProxy;
217
- };
218
-
219
- createStateProxy = <T extends object>(props: { target: T }): T => {
220
- return ProxyFactory.createStateProxy({
221
- target: props.target,
222
- consumerRef: this.componentRef.current,
223
- consumerTracker: this as any,
224
- });
225
- };
226
-
227
- createClassProxy = <T extends object>(props: { target: T }): T => {
228
- return ProxyFactory.createClassProxy({
229
- target: props.target,
230
- consumerRef: this.componentRef.current,
231
- consumerTracker: this as any,
232
- });
233
- };
234
-
235
- // Lifecycle methods
236
- mount = () => {
237
- this.hasMounted = true;
238
- this.mountCount++;
239
- this.mountTime = Date.now();
240
-
241
- // Call onMount hook
242
- if (this.options?.onMount) {
243
- this.options.onMount(this.blocInstance);
244
- }
245
-
246
- // Refresh dependencies if using manual tracking
247
- if (this.isUsingDependencies && this.options?.dependencies) {
248
- this.dependencyValues = this.options.dependencies(this.blocInstance);
249
- }
250
-
251
- // Notify plugins
252
- const metadata = this.getAdapterMetadata();
253
- Blac.getInstance().plugins.notifyAdapterMount(this, metadata);
254
- };
255
-
256
- unmount = () => {
257
- this.unmountTime = Date.now();
258
-
259
- // Cancel subscription
260
- if (this.unsubscribe) {
261
- this.unsubscribe();
262
- this.unsubscribe = undefined;
263
- this.subscriptionId = undefined;
264
- }
265
-
266
- // Call onUnmount hook
267
- if (this.options?.onUnmount) {
268
- try {
269
- this.options.onUnmount(this.blocInstance);
270
- } catch (error) {
271
- console.error('Error in onUnmount hook:', error);
272
- }
273
- }
274
-
275
- // Notify plugins
276
- const metadata = this.getAdapterMetadata();
277
- Blac.getInstance().plugins.notifyAdapterUnmount(this, metadata);
278
- };
279
-
280
- // Reset tracking for next render
281
- resetTracking(): void {
282
- // Clear tracked paths from previous render
283
- this.trackedPaths.clear();
284
- this.pendingTrackedPaths.clear();
285
-
286
- // Clear subscription dependencies to track only current render
287
- if (this.subscriptionId) {
288
- const subscription = (
289
- this.blocInstance._subscriptionManager as any
290
- ).subscriptions.get(this.subscriptionId);
291
- if (subscription && subscription.dependencies) {
292
- // Clear old dependencies - we'll track new ones in this render
293
- subscription.dependencies.clear();
294
- }
295
- }
296
- }
297
-
298
- // Set component name for rerender logging
299
- setComponentName(name: string): void {
300
- this.componentName = name;
301
- }
302
-
303
- // Notify plugins about render
304
- notifyRender(): void {
305
- this.renderCount++;
306
-
307
- // Update dependency values if using manual tracking
308
- if (this.isUsingDependencies && this.options?.dependencies) {
309
- this.lastDependencyValues = this.dependencyValues;
310
- this.dependencyValues = this.options.dependencies(this.blocInstance);
311
- }
312
-
313
- const metadata = this.getAdapterMetadata();
314
- Blac.getInstance().plugins.notifyAdapterRender(this, metadata);
315
- }
316
-
317
- // Get adapter metadata for plugins
318
- private getAdapterMetadata(): any {
319
- return {
320
- componentName: this.componentName,
321
- blocInstance: this.blocInstance,
322
- renderCount: this.renderCount,
323
- trackedPaths: Array.from(this.trackedPaths),
324
- isUsingDependencies: this.isUsingDependencies,
325
- lastState: this.lastState,
326
- lastDependencyValues: this.lastDependencyValues,
327
- currentDependencyValues: this.dependencyValues,
328
- };
329
- }
330
-
331
- // Get value at a dot-notated path
332
- private getValueAtPath(obj: any, path: string): any {
333
- const parts = path.split('.');
334
- let current = obj;
335
-
336
- for (const part of parts) {
337
- if (current == null) return undefined;
338
- current = current[part];
339
- }
340
-
341
- return current;
342
- }
343
-
344
- // Get paths that changed between states
345
- private getStateChangedPaths(oldState: any, newState: any): string[] {
346
- const changedPaths: string[] = [];
347
-
348
- // Only check tracked paths if we have any
349
- if (this.trackedPaths.size > 0) {
350
- for (const path of this.trackedPaths) {
351
- // Skip class property paths
352
- if (path.startsWith('_class.')) continue;
353
-
354
- const oldValue = this.getValueAtPath(oldState, path);
355
- const newValue = this.getValueAtPath(newState, path);
356
-
357
- if (!Object.is(oldValue, newValue)) {
358
- changedPaths.push(path);
359
- }
360
- }
361
- } else {
362
- // No tracked paths - do deep comparison to find what changed
363
- if (oldState !== newState) {
364
- const deepChanges = this.getDeepChangedPaths(oldState, newState);
365
- if (deepChanges.length > 0) {
366
- return deepChanges;
367
- }
368
- changedPaths.push('(entire state)');
369
- }
370
- }
371
-
372
- return changedPaths;
373
- }
374
-
375
- // Deep comparison to find all changed paths
376
- private getDeepChangedPaths(
377
- oldObj: any,
378
- newObj: any,
379
- basePath: string = '',
380
- ): string[] {
381
- const changes: string[] = [];
382
-
383
- // Handle null/undefined
384
- if (oldObj === newObj) return changes;
385
- if (oldObj == null || newObj == null) {
386
- changes.push(basePath || '(root)');
387
- return changes;
388
- }
389
-
390
- // Handle primitives
391
- if (typeof oldObj !== 'object' || typeof newObj !== 'object') {
392
- if (oldObj !== newObj) {
393
- changes.push(basePath || '(root)');
394
- }
395
- return changes;
396
- }
397
-
398
- // Handle arrays
399
- if (Array.isArray(oldObj) || Array.isArray(newObj)) {
400
- if (
401
- !Array.isArray(oldObj) ||
402
- !Array.isArray(newObj) ||
403
- oldObj.length !== newObj.length
404
- ) {
405
- changes.push(basePath || '(root)');
406
- return changes;
407
- }
408
- // Compare array elements
409
- for (let i = 0; i < oldObj.length; i++) {
410
- const elementPath = basePath ? `${basePath}[${i}]` : `[${i}]`;
411
- changes.push(
412
- ...this.getDeepChangedPaths(oldObj[i], newObj[i], elementPath),
413
- );
414
- }
415
- return changes;
416
- }
417
-
418
- // Handle objects
419
- const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
420
- for (const key of allKeys) {
421
- const keyPath = basePath ? `${basePath}.${key}` : key;
422
-
423
- if (!(key in oldObj)) {
424
- changes.push(`${keyPath} (added)`);
425
- } else if (!(key in newObj)) {
426
- changes.push(`${keyPath} (removed)`);
427
- } else {
428
- const oldVal = oldObj[key];
429
- const newVal = newObj[key];
430
-
431
- // Use Object.is for comparison
432
- if (!Object.is(oldVal, newVal)) {
433
- // For primitives and different object references, record the change
434
- if (
435
- typeof oldVal !== 'object' ||
436
- typeof newVal !== 'object' ||
437
- oldVal === null ||
438
- newVal === null
439
- ) {
440
- changes.push(keyPath);
441
- } else {
442
- // For objects/arrays, recurse deeper
443
- const deeperChanges = this.getDeepChangedPaths(
444
- oldVal,
445
- newVal,
446
- keyPath,
447
- );
448
- if (deeperChanges.length > 0) {
449
- changes.push(...deeperChanges);
450
- } else {
451
- // Objects are different references but have same content
452
- changes.push(keyPath);
453
- }
454
- }
455
- }
456
- }
457
- }
458
-
459
- return changes;
460
- }
461
-
462
- // Get last rerender reason
463
- getLastRerenderReason(): any {
464
- // This method is deprecated - render reason is now handled by the RenderLoggingPlugin
465
- return undefined;
466
- }
467
- }
@@ -1,249 +0,0 @@
1
- import { BlocBase } from '../BlocBase';
2
-
3
- interface ConsumerTracker {
4
- trackAccess: (
5
- consumerRef: object,
6
- type: 'state' | 'class',
7
- path: string,
8
- value?: any,
9
- ) => void;
10
- }
11
-
12
- // Minimal cache for backward compatibility
13
- const proxyCache = new WeakMap<object, WeakMap<object, any>>();
14
-
15
- // Minimal stats for backward compatibility
16
- const stats = {
17
- stateProxiesCreated: 0,
18
- classProxiesCreated: 0,
19
- cacheHits: 0,
20
- cacheMisses: 0,
21
- totalProxiesCreated: 0,
22
- };
23
-
24
- /**
25
- * Creates a proxy for state objects that tracks property access
26
- */
27
- export const createStateProxy = <T extends object>(
28
- target: T,
29
- consumerRef: object,
30
- consumerTracker: ConsumerTracker,
31
- path = '',
32
- ): T => {
33
- if (
34
- !consumerRef ||
35
- !consumerTracker ||
36
- typeof target !== 'object' ||
37
- target === null
38
- ) {
39
- return target;
40
- }
41
-
42
- // Check cache for backward compatibility with tests
43
- if (!path) {
44
- // Only cache root objects
45
- let refCache = proxyCache.get(target);
46
- if (!refCache) {
47
- refCache = new WeakMap();
48
- proxyCache.set(target, refCache);
49
- }
50
-
51
- const cached = refCache.get(consumerRef);
52
- if (cached) {
53
- stats.cacheHits++;
54
- return cached;
55
- }
56
- stats.cacheMisses++;
57
- }
58
-
59
- const proxy = new Proxy(target, {
60
- get(obj: T, prop: string | symbol): any {
61
- // Handle symbols and special properties
62
- if (typeof prop === 'symbol' || prop === 'constructor') {
63
- return Reflect.get(obj, prop);
64
- }
65
-
66
- const fullPath = path ? `${path}.${prop}` : prop;
67
- const value = Reflect.get(obj, prop);
68
- const isObject = value && typeof value === 'object';
69
-
70
- // Track access with value for primitives
71
- consumerTracker.trackAccess(
72
- consumerRef,
73
- 'state',
74
- fullPath,
75
- isObject ? undefined : value,
76
- );
77
-
78
- // Recursively proxy nested objects and arrays
79
- if (
80
- isObject &&
81
- (Array.isArray(value) ||
82
- Object.getPrototypeOf(value) === Object.prototype)
83
- ) {
84
- return createStateProxy(value, consumerRef, consumerTracker, fullPath);
85
- }
86
-
87
- return value;
88
- },
89
-
90
- set: () => false, // State should be immutable
91
- deleteProperty: () => false, // State properties should not be deleted
92
- });
93
-
94
- // Cache root proxies
95
- if (!path) {
96
- const refCache = proxyCache.get(target)!;
97
- refCache.set(consumerRef, proxy);
98
- stats.stateProxiesCreated++;
99
- stats.totalProxiesCreated++;
100
- }
101
-
102
- return proxy;
103
- };
104
-
105
- /**
106
- * Creates a proxy for bloc instances that tracks getter access
107
- */
108
- export const createBlocProxy = <T extends object>(
109
- target: T,
110
- consumerRef: object,
111
- consumerTracker: ConsumerTracker,
112
- ): T => {
113
- if (!consumerRef || !consumerTracker) {
114
- return target;
115
- }
116
-
117
- // Check cache
118
- let refCache = proxyCache.get(target);
119
- if (!refCache) {
120
- refCache = new WeakMap();
121
- proxyCache.set(target, refCache);
122
- }
123
-
124
- const cached = refCache.get(consumerRef);
125
- if (cached) {
126
- stats.cacheHits++;
127
- return cached;
128
- }
129
- stats.cacheMisses++;
130
-
131
- const proxy = new Proxy(target, {
132
- get(obj: T, prop: string | symbol): any {
133
- const value = Reflect.get(obj, prop);
134
-
135
- // Only track getters, not methods or regular properties
136
- const descriptor = findPropertyDescriptor(obj, prop);
137
- if (descriptor?.get) {
138
- // Track getter access with value for primitives
139
- const isPrimitive = value !== null && typeof value !== 'object';
140
- consumerTracker.trackAccess(
141
- consumerRef,
142
- 'class',
143
- String(prop),
144
- isPrimitive ? value : undefined,
145
- );
146
- }
147
-
148
- return value;
149
- },
150
- });
151
-
152
- // Cache proxy
153
- refCache.set(consumerRef, proxy);
154
- stats.classProxiesCreated++;
155
- stats.totalProxiesCreated++;
156
-
157
- return proxy;
158
- };
159
-
160
- /**
161
- * Helper to find property descriptor in prototype chain
162
- */
163
- const findPropertyDescriptor = (
164
- obj: any,
165
- prop: string | symbol,
166
- maxDepth = 10,
167
- ): PropertyDescriptor | undefined => {
168
- let current = obj;
169
- let depth = 0;
170
-
171
- while (current && depth < maxDepth) {
172
- const descriptor = Object.getOwnPropertyDescriptor(current, prop);
173
- if (descriptor) return descriptor;
174
-
175
- current = Object.getPrototypeOf(current);
176
- depth++;
177
- }
178
-
179
- return undefined;
180
- };
181
-
182
- // Export compatibility functions for easier migration
183
- export const ProxyFactory = {
184
- createStateProxy: (options: {
185
- target: any;
186
- consumerRef: object;
187
- consumerTracker: ConsumerTracker;
188
- path?: string;
189
- }) =>
190
- createStateProxy(
191
- options.target,
192
- options.consumerRef,
193
- options.consumerTracker,
194
- options.path,
195
- ),
196
-
197
- createClassProxy: (options: {
198
- target: any;
199
- consumerRef: object;
200
- consumerTracker: ConsumerTracker;
201
- }) =>
202
- createBlocProxy(
203
- options.target,
204
- options.consumerRef,
205
- options.consumerTracker,
206
- ),
207
-
208
- getProxyState: <B extends BlocBase<any>>(options: {
209
- state: any;
210
- consumerRef: object;
211
- consumerTracker: ConsumerTracker;
212
- }) =>
213
- createStateProxy(
214
- options.state,
215
- options.consumerRef,
216
- options.consumerTracker,
217
- ),
218
-
219
- getProxyBlocInstance: <B extends BlocBase<any>>(options: {
220
- blocInstance: B;
221
- consumerRef: object;
222
- consumerTracker: ConsumerTracker;
223
- }) =>
224
- createBlocProxy(
225
- options.blocInstance,
226
- options.consumerRef,
227
- options.consumerTracker,
228
- ),
229
-
230
- // Compatibility functions that return expected structure
231
- getStats: () => ({
232
- ...stats,
233
- propertyAccesses: 0,
234
- nestedProxiesCreated: 0,
235
- cacheEfficiency:
236
- stats.cacheHits + stats.cacheMisses > 0
237
- ? `${((stats.cacheHits / (stats.cacheHits + stats.cacheMisses)) * 100).toFixed(1)}%`
238
- : 'N/A',
239
- lifetime: '0ms',
240
- proxiesPerSecond: 'N/A',
241
- }),
242
- resetStats: () => {
243
- stats.stateProxiesCreated = 0;
244
- stats.classProxiesCreated = 0;
245
- stats.cacheHits = 0;
246
- stats.cacheMisses = 0;
247
- stats.totalProxiesCreated = 0;
248
- },
249
- };