@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.
@@ -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?: InferPropsFromGeneric<B>;
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
- // Handle dependency-based change detection
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
- } else {
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 (this.isUsingDependencies) {
219
- return state; // Return raw state when using dependencies
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 (this.isUsingDependencies) {
231
- return this.blocInstance; // Return raw instance when using dependencies
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
@@ -0,0 +1,3 @@
1
+ export class PropsUpdated<P = any> {
2
+ constructor(public readonly props: P) {}
3
+ }
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 = BlocEvent & object;
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
+ }