@blac/core 2.0.0-rc-16 → 2.0.0-rc.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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/Blac.d.ts +246 -0
- package/dist/Blac.d.ts.map +1 -0
- package/dist/Blac.js +624 -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 +67 -0
- package/dist/adapter/BlacAdapter.d.ts.map +1 -0
- package/dist/adapter/BlacAdapter.js +263 -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 +33 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +68 -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 +19 -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,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
|
-
};
|