@blac/core 2.0.0-rc-10 → 2.0.0-rc-11
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/README.md +27 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +40 -11
- package/dist/index.es.js +432 -364
- package/dist/index.es.js.map +1 -1
- package/package.json +1 -1
- package/src/Blac.ts +46 -7
- package/src/Bloc.ts +0 -49
- package/src/Cubit.ts +14 -0
- package/src/__tests__/Blac.config.test.ts +86 -0
- package/src/__tests__/props.test.ts +256 -0
- package/src/adapter/BlacAdapter.ts +73 -8
- package/src/events.ts +3 -0
- package/src/index.ts +2 -0
- package/src/types.ts +1 -10
- package/src/utils/shallowEqual.ts +40 -0
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Blac } from '../Blac';
|
|
2
|
-
import { BlocBase } from '../BlocBase';
|
|
2
|
+
import { BlocBase, BlocLifecycleState } from '../BlocBase';
|
|
3
3
|
import { BlocConstructor, BlocState, InferPropsFromGeneric } from '../types';
|
|
4
|
+
import { PropsUpdated } from '../events';
|
|
4
5
|
import { generateUUID } from '../utils/uuid';
|
|
6
|
+
import { shallowEqual } from '../utils/shallowEqual';
|
|
5
7
|
import { ConsumerTracker, DependencyArray } from './ConsumerTracker';
|
|
6
8
|
import { ProxyFactory } from './ProxyFactory';
|
|
7
9
|
|
|
8
10
|
export interface AdapterOptions<B extends BlocBase<any>> {
|
|
9
11
|
id?: string;
|
|
10
12
|
dependencies?: (bloc: B) => unknown[];
|
|
11
|
-
props?:
|
|
13
|
+
props?: any;
|
|
12
14
|
onMount?: (bloc: B) => void;
|
|
13
15
|
onUnmount?: (bloc: B) => void;
|
|
14
16
|
}
|
|
@@ -26,6 +28,10 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
26
28
|
// Core components
|
|
27
29
|
private consumerTracker: ConsumerTracker;
|
|
28
30
|
|
|
31
|
+
// Props ownership tracking
|
|
32
|
+
private static propsOwners = new WeakMap<BlocBase<any>, string>();
|
|
33
|
+
private lastProps?: any;
|
|
34
|
+
|
|
29
35
|
unmountTime: number = 0;
|
|
30
36
|
mountTime: number = 0;
|
|
31
37
|
|
|
@@ -59,6 +65,11 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
59
65
|
if (this.isUsingDependencies && options?.dependencies) {
|
|
60
66
|
this.dependencyValues = options.dependencies(this.blocInstance);
|
|
61
67
|
}
|
|
68
|
+
|
|
69
|
+
// Handle initial props if provided
|
|
70
|
+
if (options?.props !== undefined) {
|
|
71
|
+
this.updateProps(options.props);
|
|
72
|
+
}
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
trackAccess(
|
|
@@ -133,7 +144,7 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
133
144
|
newState: BlocState<InstanceType<B>>,
|
|
134
145
|
oldState: BlocState<InstanceType<B>>,
|
|
135
146
|
) => {
|
|
136
|
-
//
|
|
147
|
+
// Case 1: Manual dependencies provided
|
|
137
148
|
if (this.isUsingDependencies && this.options?.dependencies) {
|
|
138
149
|
const newValues = this.options.dependencies(this.blocInstance);
|
|
139
150
|
const hasChanged = this.hasDependencyValuesChanged(
|
|
@@ -146,7 +157,15 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
146
157
|
}
|
|
147
158
|
|
|
148
159
|
this.dependencyValues = newValues;
|
|
149
|
-
}
|
|
160
|
+
}
|
|
161
|
+
// Case 2: Proxy tracking disabled globally (and no manual dependencies)
|
|
162
|
+
else if (!Blac.config.proxyDependencyTracking) {
|
|
163
|
+
// Always trigger re-render when proxy tracking is disabled
|
|
164
|
+
options.onChange();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// Case 3: Proxy tracking enabled (default behavior)
|
|
168
|
+
else {
|
|
150
169
|
// Check if any tracked values have changed (proxy-based tracking)
|
|
151
170
|
const consumerInfo = this.consumerTracker.getConsumerInfo(
|
|
152
171
|
this.componentRef.current,
|
|
@@ -202,6 +221,11 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
202
221
|
this.consumerTracker.unregister(this.componentRef.current);
|
|
203
222
|
this.blocInstance._removeConsumer(this.id);
|
|
204
223
|
|
|
224
|
+
// Clear ownership if we own this bloc
|
|
225
|
+
if (BlacAdapter.propsOwners.get(this.blocInstance) === this.id) {
|
|
226
|
+
BlacAdapter.propsOwners.delete(this.blocInstance);
|
|
227
|
+
}
|
|
228
|
+
|
|
205
229
|
// Call onUnmount callback
|
|
206
230
|
if (this.options?.onUnmount) {
|
|
207
231
|
try {
|
|
@@ -215,8 +239,9 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
215
239
|
getProxyState = (
|
|
216
240
|
state: BlocState<InstanceType<B>>,
|
|
217
241
|
): BlocState<InstanceType<B>> => {
|
|
218
|
-
if
|
|
219
|
-
|
|
242
|
+
// Return raw state if proxy tracking is disabled globally or using manual dependencies
|
|
243
|
+
if (this.isUsingDependencies || !Blac.config.proxyDependencyTracking) {
|
|
244
|
+
return state;
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
return ProxyFactory.getProxyState({
|
|
@@ -227,8 +252,9 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
227
252
|
};
|
|
228
253
|
|
|
229
254
|
getProxyBlocInstance = (): InstanceType<B> => {
|
|
230
|
-
if
|
|
231
|
-
|
|
255
|
+
// Return raw instance if proxy tracking is disabled globally or using manual dependencies
|
|
256
|
+
if (this.isUsingDependencies || !Blac.config.proxyDependencyTracking) {
|
|
257
|
+
return this.blocInstance;
|
|
232
258
|
}
|
|
233
259
|
|
|
234
260
|
return ProxyFactory.getProxyBlocInstance({
|
|
@@ -243,6 +269,45 @@ export class BlacAdapter<B extends BlocConstructor<BlocBase<any>>> {
|
|
|
243
269
|
return this.hasMounted;
|
|
244
270
|
}
|
|
245
271
|
|
|
272
|
+
updateProps(props: any): void {
|
|
273
|
+
const bloc = this.blocInstance;
|
|
274
|
+
|
|
275
|
+
// Check ownership
|
|
276
|
+
if (!BlacAdapter.propsOwners.has(bloc)) {
|
|
277
|
+
// First adapter with props becomes the owner
|
|
278
|
+
BlacAdapter.propsOwners.set(bloc, this.id);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (BlacAdapter.propsOwners.get(bloc) !== this.id) {
|
|
282
|
+
Blac.warn(
|
|
283
|
+
`[BlacAdapter] Attempted to set props on ${bloc.constructor.name} from non-owner adapter`
|
|
284
|
+
);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Disposal safety
|
|
289
|
+
if ((bloc as any)._disposalState === BlocLifecycleState.DISPOSED ||
|
|
290
|
+
(bloc as any)._disposalState === BlocLifecycleState.DISPOSING) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Check if props have changed
|
|
295
|
+
if (shallowEqual(this.lastProps, props)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Update props based on bloc type
|
|
300
|
+
if ('add' in bloc && typeof bloc.add === 'function') {
|
|
301
|
+
// Bloc: dispatch PropsUpdated event
|
|
302
|
+
(bloc as any).add(new PropsUpdated(props));
|
|
303
|
+
} else if ('_updateProps' in bloc && typeof (bloc as any)._updateProps === 'function') {
|
|
304
|
+
// Cubit: direct props update
|
|
305
|
+
(bloc as any)._updateProps(props);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this.lastProps = props;
|
|
309
|
+
}
|
|
310
|
+
|
|
246
311
|
private hasDependencyValuesChanged(
|
|
247
312
|
prev: unknown[] | undefined,
|
|
248
313
|
next: unknown[],
|
package/src/events.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -4,9 +4,11 @@ export * from './Bloc';
|
|
|
4
4
|
export * from './BlocBase';
|
|
5
5
|
export * from './Cubit';
|
|
6
6
|
export * from './types';
|
|
7
|
+
export * from './events';
|
|
7
8
|
|
|
8
9
|
// Utilities
|
|
9
10
|
export * from './utils/uuid';
|
|
11
|
+
export * from './utils/shallowEqual';
|
|
10
12
|
|
|
11
13
|
// Test utilities
|
|
12
14
|
export * from './testing';
|
package/src/types.ts
CHANGED
|
@@ -56,19 +56,10 @@ export type InferPropsFromGeneric<T> =
|
|
|
56
56
|
export type BlocConstructorParameters<B extends BlocBase<any>> =
|
|
57
57
|
BlocConstructor<B> extends new (...args: infer P) => B ? P : never;
|
|
58
58
|
|
|
59
|
-
/**
|
|
60
|
-
* Base interface for all Bloc events
|
|
61
|
-
* Events must be objects with a constructor to enable proper type matching
|
|
62
|
-
*/
|
|
63
|
-
export interface BlocEvent {
|
|
64
|
-
readonly type?: string;
|
|
65
|
-
readonly timestamp?: number;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
59
|
/**
|
|
69
60
|
* Enhanced constraint for Bloc events - must be objects with proper constructor
|
|
70
61
|
*/
|
|
71
|
-
export type BlocEventConstraint =
|
|
62
|
+
export type BlocEventConstraint = object;
|
|
72
63
|
|
|
73
64
|
/**
|
|
74
65
|
* Error boundary interface for Bloc error handling
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performs a shallow equality comparison between two values.
|
|
3
|
+
* For objects, it checks if all top-level properties are equal using Object.is.
|
|
4
|
+
* @param a First value to compare
|
|
5
|
+
* @param b Second value to compare
|
|
6
|
+
* @returns true if values are shallowly equal, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
export function shallowEqual(a: any, b: any): boolean {
|
|
9
|
+
if (Object.is(a, b)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (
|
|
14
|
+
typeof a !== 'object' ||
|
|
15
|
+
a === null ||
|
|
16
|
+
typeof b !== 'object' ||
|
|
17
|
+
b === null
|
|
18
|
+
) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const keysA = Object.keys(a);
|
|
23
|
+
const keysB = Object.keys(b);
|
|
24
|
+
|
|
25
|
+
if (keysA.length !== keysB.length) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < keysA.length; i++) {
|
|
30
|
+
const key = keysA[i];
|
|
31
|
+
if (
|
|
32
|
+
!Object.prototype.hasOwnProperty.call(b, key) ||
|
|
33
|
+
!Object.is(a[key], b[key])
|
|
34
|
+
) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true;
|
|
40
|
+
}
|