@blac/core 2.0.0-rc-8 → 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/src/testing.ts CHANGED
@@ -9,7 +9,7 @@ import { BlocEventConstraint } from './types';
9
9
  */
10
10
  export class BlocTest {
11
11
  private static originalInstance: Blac;
12
-
12
+
13
13
  /**
14
14
  * Sets up a clean test environment
15
15
  */
@@ -18,7 +18,7 @@ export class BlocTest {
18
18
  Blac.resetInstance();
19
19
  Blac.enableLog = false; // Disable logging in tests by default
20
20
  }
21
-
21
+
22
22
  /**
23
23
  * Tears down the test environment and restores original state
24
24
  */
@@ -27,7 +27,7 @@ export class BlocTest {
27
27
  // Note: Cannot restore original instance due to singleton pattern
28
28
  // Tests should use setUp/tearDown properly to manage state
29
29
  }
30
-
30
+
31
31
  /**
32
32
  * Creates a test bloc with automatic cleanup
33
33
  */
@@ -39,31 +39,35 @@ export class BlocTest {
39
39
  Blac.activateBloc(bloc);
40
40
  return bloc;
41
41
  }
42
-
42
+
43
43
  /**
44
44
  * Waits for a bloc to emit a specific state
45
45
  */
46
46
  static async waitForState<T extends BlocBase<S>, S>(
47
47
  bloc: T,
48
48
  predicate: (state: S) => boolean,
49
- timeout = 5000
49
+ timeout = 5000,
50
50
  ): Promise<S> {
51
51
  return new Promise<S>((resolve, reject) => {
52
52
  const timeoutId = setTimeout(() => {
53
- reject(new Error(`Timeout waiting for state matching predicate after ${timeout}ms`));
53
+ reject(
54
+ new Error(
55
+ `Timeout waiting for state matching predicate after ${timeout}ms`,
56
+ ),
57
+ );
54
58
  }, timeout);
55
-
59
+
56
60
  const unsubscribe = bloc._observer.subscribe({
57
- id: `test-waiter-${crypto.randomUUID()}`,
61
+ id: `test-waiter-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
58
62
  fn: (newState: S) => {
59
63
  if (predicate(newState)) {
60
64
  clearTimeout(timeoutId);
61
65
  unsubscribe();
62
66
  resolve(newState);
63
67
  }
64
- }
68
+ },
65
69
  });
66
-
70
+
67
71
  // Check current state immediately
68
72
  if (predicate(bloc.state)) {
69
73
  clearTimeout(timeoutId);
@@ -72,53 +76,57 @@ export class BlocTest {
72
76
  }
73
77
  });
74
78
  }
75
-
79
+
76
80
  /**
77
81
  * Expects a bloc to emit specific states in order
78
82
  */
79
83
  static async expectStates<T extends BlocBase<S>, S>(
80
84
  bloc: T,
81
85
  expectedStates: S[],
82
- timeout = 5000
86
+ timeout = 5000,
83
87
  ): Promise<void> {
84
88
  const receivedStates: S[] = [];
85
-
89
+
86
90
  return new Promise<void>((resolve, reject) => {
87
91
  const timeoutId = setTimeout(() => {
88
- reject(new Error(
89
- `Timeout waiting for states. Expected: ${JSON.stringify(expectedStates)}, ` +
90
- `Received: ${JSON.stringify(receivedStates)}`
91
- ));
92
+ reject(
93
+ new Error(
94
+ `Timeout waiting for states. Expected: ${JSON.stringify(expectedStates)}, ` +
95
+ `Received: ${JSON.stringify(receivedStates)}`,
96
+ ),
97
+ );
92
98
  }, timeout);
93
-
99
+
94
100
  const unsubscribe = bloc._observer.subscribe({
95
101
  id: `test-expecter-${crypto.randomUUID()}`,
96
102
  fn: (newState: S) => {
97
103
  receivedStates.push(newState);
98
-
104
+
99
105
  // Check if we have all expected states
100
106
  if (receivedStates.length === expectedStates.length) {
101
107
  clearTimeout(timeoutId);
102
108
  unsubscribe();
103
-
109
+
104
110
  // Verify all states match using deep equality
105
111
  for (let i = 0; i < expectedStates.length; i++) {
106
112
  const expected = expectedStates[i];
107
113
  const received = receivedStates[i];
108
-
114
+
109
115
  // Use JSON comparison for deep equality
110
116
  if (JSON.stringify(expected) !== JSON.stringify(received)) {
111
- reject(new Error(
112
- `State mismatch at index ${i}. Expected: ${JSON.stringify(expected)}, ` +
113
- `Received: ${JSON.stringify(received)}`
114
- ));
117
+ reject(
118
+ new Error(
119
+ `State mismatch at index ${i}. Expected: ${JSON.stringify(expected)}, ` +
120
+ `Received: ${JSON.stringify(received)}`,
121
+ ),
122
+ );
115
123
  return;
116
124
  }
117
125
  }
118
-
126
+
119
127
  resolve();
120
128
  }
121
- }
129
+ },
122
130
  });
123
131
  });
124
132
  }
@@ -127,31 +135,37 @@ export class BlocTest {
127
135
  /**
128
136
  * Mock Bloc for testing
129
137
  */
130
- export class MockBloc<S, A extends BlocEventConstraint = BlocEventConstraint> extends Bloc<S, A> {
131
- private mockHandlers = new Map<string, (event: A, emit: (newState: S) => void) => void | Promise<void>>();
132
-
138
+ export class MockBloc<
139
+ S,
140
+ A extends BlocEventConstraint = BlocEventConstraint,
141
+ > extends Bloc<S, A> {
142
+ private mockHandlers = new Map<
143
+ string,
144
+ (event: A, emit: (newState: S) => void) => void | Promise<void>
145
+ >();
146
+
133
147
  constructor(initialState: S) {
134
148
  super(initialState);
135
149
  }
136
-
150
+
137
151
  /**
138
152
  * Mock an event handler for testing
139
153
  */
140
154
  mockEventHandler<E extends A>(
141
155
  eventConstructor: new (...args: any[]) => E,
142
- handler: (event: E, emit: (newState: S) => void) => void | Promise<void>
156
+ handler: (event: E, emit: (newState: S) => void) => void | Promise<void>,
143
157
  ): void {
144
158
  // Use the on method to register the mock handler
145
159
  this.on(eventConstructor, handler);
146
160
  }
147
-
161
+
148
162
  /**
149
163
  * Get the number of registered handlers
150
164
  */
151
165
  getHandlerCount(): number {
152
166
  return this.eventHandlers.size;
153
167
  }
154
-
168
+
155
169
  /**
156
170
  * Check if a handler is registered for an event type
157
171
  */
@@ -165,12 +179,12 @@ export class MockBloc<S, A extends BlocEventConstraint = BlocEventConstraint> ex
165
179
  */
166
180
  export class MockCubit<S> extends Cubit<S> {
167
181
  private stateHistory: S[] = [];
168
-
182
+
169
183
  constructor(initialState: S) {
170
184
  super(initialState);
171
185
  this.stateHistory.push(initialState);
172
186
  }
173
-
187
+
174
188
  /**
175
189
  * Override emit to track state history
176
190
  */
@@ -178,14 +192,14 @@ export class MockCubit<S> extends Cubit<S> {
178
192
  this.stateHistory.push(newState);
179
193
  super.emit(newState);
180
194
  }
181
-
195
+
182
196
  /**
183
197
  * Get the history of all states
184
198
  */
185
199
  getStateHistory(): S[] {
186
200
  return [...this.stateHistory];
187
201
  }
188
-
202
+
189
203
  /**
190
204
  * Clear state history
191
205
  */
@@ -199,11 +213,11 @@ export class MockCubit<S> extends Cubit<S> {
199
213
  */
200
214
  export class MemoryLeakDetector {
201
215
  private initialStats: ReturnType<typeof Blac.getMemoryStats>;
202
-
216
+
203
217
  constructor() {
204
218
  this.initialStats = Blac.getMemoryStats();
205
219
  }
206
-
220
+
207
221
  /**
208
222
  * Check for memory leaks and return a report
209
223
  */
@@ -213,12 +227,11 @@ export class MemoryLeakDetector {
213
227
  stats: ReturnType<typeof Blac.getMemoryStats>;
214
228
  } {
215
229
  const currentStats = Blac.getMemoryStats();
216
- const hasLeaks = (
230
+ const hasLeaks =
217
231
  currentStats.registeredBlocs > this.initialStats.registeredBlocs ||
218
232
  currentStats.isolatedBlocs > this.initialStats.isolatedBlocs ||
219
- currentStats.keepAliveBlocs > this.initialStats.keepAliveBlocs
220
- );
221
-
233
+ currentStats.keepAliveBlocs > this.initialStats.keepAliveBlocs;
234
+
222
235
  const report = `
223
236
  Memory Leak Detection Report:
224
237
  - Initial registered blocs: ${this.initialStats.registeredBlocs}
@@ -229,11 +242,11 @@ Memory Leak Detection Report:
229
242
  - Current keep-alive blocs: ${currentStats.keepAliveBlocs}
230
243
  - Potential leaks detected: ${hasLeaks ? 'YES' : 'NO'}
231
244
  `.trim();
232
-
245
+
233
246
  return {
234
247
  hasLeaks,
235
248
  report,
236
- stats: currentStats
249
+ stats: currentStats,
237
250
  };
238
251
  }
239
- }
252
+ }
package/src/types.ts CHANGED
@@ -20,7 +20,10 @@ export type BlocBaseAbstract =
20
20
  * Represents a constructor type for a Bloc that can take any parameters
21
21
  * @template B - The type of the Bloc instance
22
22
  */
23
- export type BlocConstructor<B> = new (...args: any) => B;
23
+ export type BlocConstructor<B> = (new (...args: any) => B) & {
24
+ isolated?: boolean;
25
+ keepAlive?: boolean;
26
+ };
24
27
 
25
28
  /**
26
29
  * Extracts the state type from a BlocBase instance
@@ -53,25 +56,21 @@ export type InferPropsFromGeneric<T> =
53
56
  export type BlocConstructorParameters<B extends BlocBase<any>> =
54
57
  BlocConstructor<B> extends new (...args: infer P) => B ? P : never;
55
58
 
56
- /**
57
- * Base interface for all Bloc events
58
- * Events must be objects with a constructor to enable proper type matching
59
- */
60
- export interface BlocEvent {
61
- readonly type?: string;
62
- readonly timestamp?: number;
63
- }
64
-
65
59
  /**
66
60
  * Enhanced constraint for Bloc events - must be objects with proper constructor
67
61
  */
68
- export type BlocEventConstraint = BlocEvent & object;
62
+ export type BlocEventConstraint = object;
69
63
 
70
64
  /**
71
65
  * Error boundary interface for Bloc error handling
72
66
  */
73
67
  export interface BlocErrorBoundary<S, A extends BlocEventConstraint> {
74
- onError: (error: Error, event: A, currentState: S, bloc: { name: string; id: string }) => void | Promise<void>;
68
+ onError: (
69
+ error: Error,
70
+ event: A,
71
+ currentState: S,
72
+ bloc: { name: string; id: string },
73
+ ) => void | Promise<void>;
75
74
  shouldRethrow?: (error: Error, event: A) => boolean;
76
75
  }
77
76
 
@@ -88,5 +87,5 @@ export interface BlocErrorBoundary<S, A extends BlocEventConstraint> {
88
87
  export type BlocHookDependencyArrayFn<S, I = any> = (
89
88
  currentState: S,
90
89
  previousState: S | undefined,
91
- instance: I
90
+ instance: I,
92
91
  ) => unknown[];
@@ -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
+ }
package/src/utils/uuid.ts CHANGED
@@ -14,9 +14,9 @@ export function generateUUID(): string {
14
14
  }
15
15
 
16
16
  // Fallback implementation for React Native/Hermes and older environments
17
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
18
- const r = Math.random() * 16 | 0;
19
- const v = c === 'x' ? r : (r & 0x3 | 0x8);
17
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
18
+ const r = (Math.random() * 16) | 0;
19
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
20
20
  return v.toString(16);
21
21
  });
22
- }
22
+ }
package/src/BlacEvent.ts DELETED
@@ -1,5 +0,0 @@
1
- export default class BlacEvent<T = any> extends CustomEvent<T> {
2
- constructor(type: string, eventInitDict?: CustomEventInit<T> | undefined) {
3
- super(type, eventInitDict);
4
- }
5
- }