@blac/core 2.0.0-rc-7 → 2.0.0-rc-10
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/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +194 -7
- package/dist/index.es.js +1096 -563
- package/dist/index.es.js.map +1 -1
- package/package.json +5 -4
- package/src/Blac.ts +204 -99
- package/src/BlacObserver.ts +62 -25
- package/src/Bloc.ts +166 -149
- package/src/BlocBase.ts +269 -85
- package/src/Cubit.ts +1 -1
- package/src/__tests__/Bloc.event.test.ts +419 -0
- package/src/__tests__/BlocBase.lifecycle.test.ts +441 -0
- package/src/__tests__/Cubit.test.ts +473 -0
- package/src/adapter/BlacAdapter.ts +262 -0
- package/src/adapter/ConsumerTracker.ts +241 -0
- package/src/adapter/ProxyFactory.ts +308 -0
- package/src/adapter/__tests__/BlacAdapter.test.ts +561 -0
- package/src/adapter/__tests__/ConsumerTracker.test.ts +562 -0
- package/src/adapter/__tests__/ProxyFactory.test.ts +655 -0
- package/src/adapter/index.ts +3 -0
- package/src/index.ts +2 -1
- package/src/testing.ts +60 -47
- package/src/types.ts +21 -6
- package/src/utils/uuid.ts +4 -4
- package/src/BlacEvent.ts +0 -5
package/src/Blac.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
// TODO:
|
|
3
|
-
|
|
2
|
+
// TODO: The 'any' types in this file are necessary for proper type inference in complex generic scenarios.
|
|
3
|
+
// Specifically:
|
|
4
|
+
// 1. BlocConstructor<any> in Maps - allows any bloc type to be stored while maintaining type safety in usage
|
|
5
|
+
// 2. Type assertions for _disposalState - private property access across inheritance hierarchy
|
|
6
|
+
// 3. Constructor argument types - enables flexible bloc instantiation patterns
|
|
7
|
+
// These 'any' usages are carefully controlled and don't compromise runtime type safety.
|
|
8
|
+
import { BlocBase, BlocInstanceId, BlocLifecycleState } from './BlocBase';
|
|
4
9
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from
|
|
10
|
+
BlocBaseAbstract,
|
|
11
|
+
BlocConstructor,
|
|
12
|
+
BlocHookDependencyArrayFn,
|
|
13
|
+
BlocState,
|
|
14
|
+
InferPropsFromGeneric,
|
|
15
|
+
} from './types';
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* Configuration options for the Blac instance
|
|
@@ -24,6 +29,8 @@ export interface GetBlocOptions<B extends BlocBase<unknown>> {
|
|
|
24
29
|
onMount?: (bloc: B) => void;
|
|
25
30
|
instanceRef?: string;
|
|
26
31
|
throwIfNotFound?: boolean;
|
|
32
|
+
/** Force creation of a new instance for isolated blocs, bypassing instance lookup */
|
|
33
|
+
forceNewInstance?: boolean;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
/**
|
|
@@ -43,7 +50,9 @@ class SingletonBlacManager implements BlacInstanceManager {
|
|
|
43
50
|
|
|
44
51
|
getInstance(): Blac {
|
|
45
52
|
if (!SingletonBlacManager._instance) {
|
|
46
|
-
SingletonBlacManager._instance = new Blac({
|
|
53
|
+
SingletonBlacManager._instance = new Blac({
|
|
54
|
+
__unsafe_ignore_singleton: true,
|
|
55
|
+
});
|
|
47
56
|
}
|
|
48
57
|
return SingletonBlacManager._instance;
|
|
49
58
|
}
|
|
@@ -54,8 +63,10 @@ class SingletonBlacManager implements BlacInstanceManager {
|
|
|
54
63
|
|
|
55
64
|
resetInstance(): void {
|
|
56
65
|
const oldInstance = SingletonBlacManager._instance;
|
|
57
|
-
SingletonBlacManager._instance = new Blac({
|
|
58
|
-
|
|
66
|
+
SingletonBlacManager._instance = new Blac({
|
|
67
|
+
__unsafe_ignore_singleton: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
59
70
|
// Transfer any keep-alive blocs to the new instance
|
|
60
71
|
if (oldInstance) {
|
|
61
72
|
for (const bloc of oldInstance.keepAliveBlocs) {
|
|
@@ -82,14 +93,13 @@ export function setBlacInstanceManager(manager: BlacInstanceManager): void {
|
|
|
82
93
|
* Main Blac class that manages the state management system.
|
|
83
94
|
* Can work with singleton pattern or dependency injection.
|
|
84
95
|
* Handles bloc lifecycle, and instance tracking.
|
|
85
|
-
*
|
|
96
|
+
*
|
|
86
97
|
* Key responsibilities:
|
|
87
98
|
* - Managing bloc instances (creation, disposal, lookup)
|
|
88
99
|
* - Handling isolated and non-isolated blocs
|
|
89
100
|
* - Providing logging and debugging capabilities
|
|
90
101
|
*/
|
|
91
102
|
export class Blac {
|
|
92
|
-
/** @deprecated Use getInstance() instead */
|
|
93
103
|
static get instance(): Blac {
|
|
94
104
|
return instanceManager.getInstance();
|
|
95
105
|
}
|
|
@@ -101,9 +111,12 @@ export class Blac {
|
|
|
101
111
|
/** Map storing all registered bloc instances by their class name and ID */
|
|
102
112
|
blocInstanceMap: Map<string, BlocBase<unknown>> = new Map();
|
|
103
113
|
/** Map storing isolated bloc instances grouped by their constructor */
|
|
114
|
+
// Using BlocConstructor<BlocBase<unknown>> would break type inference when storing
|
|
115
|
+
// different bloc types in the same map. The 'any' allows proper polymorphic storage
|
|
116
|
+
// while maintaining type safety at usage sites through the BlocConstructor constraint.
|
|
104
117
|
isolatedBlocMap: Map<BlocConstructor<any>, BlocBase<unknown>[]> = new Map();
|
|
105
118
|
/** Map for O(1) lookup of isolated blocs by UID */
|
|
106
|
-
|
|
119
|
+
isolatedBlocIndex: Map<string, BlocBase<unknown>> = new Map();
|
|
107
120
|
/** Map tracking UIDs to prevent memory leaks */
|
|
108
121
|
uidRegistry: Map<string, BlocBase<unknown>> = new Map();
|
|
109
122
|
/** Set of keep-alive blocs for controlled cleanup */
|
|
@@ -125,15 +138,27 @@ export class Blac {
|
|
|
125
138
|
|
|
126
139
|
/** Flag to enable/disable logging */
|
|
127
140
|
static enableLog = false;
|
|
141
|
+
static logLevel: 'warn' | 'log' = 'warn';
|
|
142
|
+
static logSpy: ((...args: unknown[]) => void) | null = null;
|
|
128
143
|
|
|
129
144
|
/**
|
|
130
145
|
* Logs messages to console when logging is enabled
|
|
131
146
|
* @param args - Arguments to log
|
|
132
147
|
*/
|
|
133
148
|
log = (...args: unknown[]) => {
|
|
134
|
-
if (Blac.
|
|
149
|
+
if (Blac.logSpy) {
|
|
150
|
+
Blac.logSpy(args);
|
|
151
|
+
}
|
|
152
|
+
if (Blac.enableLog && Blac.logLevel === 'warn') {
|
|
153
|
+
// Logging disabled - console.warn removed
|
|
154
|
+
}
|
|
155
|
+
if (Blac.enableLog && Blac.logLevel === 'log') {
|
|
156
|
+
// Logging disabled - console.log removed
|
|
157
|
+
}
|
|
135
158
|
};
|
|
136
|
-
static get log() {
|
|
159
|
+
static get log() {
|
|
160
|
+
return Blac.instance.log;
|
|
161
|
+
}
|
|
137
162
|
|
|
138
163
|
/**
|
|
139
164
|
* Gets the current Blac instance
|
|
@@ -143,7 +168,6 @@ export class Blac {
|
|
|
143
168
|
return instanceManager.getInstance();
|
|
144
169
|
}
|
|
145
170
|
|
|
146
|
-
|
|
147
171
|
/**
|
|
148
172
|
* Logs a warning message
|
|
149
173
|
* @param message - Warning message
|
|
@@ -151,10 +175,12 @@ export class Blac {
|
|
|
151
175
|
*/
|
|
152
176
|
warn = (message: string, ...args: unknown[]) => {
|
|
153
177
|
if (Blac.enableLog) {
|
|
154
|
-
console.warn
|
|
178
|
+
// Logging disabled - console.warn removed
|
|
155
179
|
}
|
|
156
180
|
};
|
|
157
|
-
static get warn() {
|
|
181
|
+
static get warn() {
|
|
182
|
+
return Blac.instance.warn;
|
|
183
|
+
}
|
|
158
184
|
/**
|
|
159
185
|
* Logs an error message
|
|
160
186
|
* @param message - Error message
|
|
@@ -162,17 +188,19 @@ export class Blac {
|
|
|
162
188
|
*/
|
|
163
189
|
error = (message: string, ...args: unknown[]) => {
|
|
164
190
|
if (Blac.enableLog) {
|
|
165
|
-
console.error
|
|
191
|
+
// Logging disabled - console.error removed
|
|
166
192
|
}
|
|
167
193
|
};
|
|
168
|
-
static get error() {
|
|
194
|
+
static get error() {
|
|
195
|
+
return Blac.instance.error;
|
|
196
|
+
}
|
|
169
197
|
|
|
170
198
|
/**
|
|
171
199
|
* Resets the Blac instance to a new one, disposing non-keepAlive blocs
|
|
172
200
|
* from the old instance.
|
|
173
201
|
*/
|
|
174
202
|
resetInstance = (): void => {
|
|
175
|
-
this.log(
|
|
203
|
+
this.log('Reset Blac instance');
|
|
176
204
|
|
|
177
205
|
// Create snapshots to avoid concurrent modification issues
|
|
178
206
|
const oldBlocInstanceMap = new Map(this.blocInstanceMap);
|
|
@@ -181,14 +209,22 @@ export class Blac {
|
|
|
181
209
|
// Dispose non-keepAlive blocs from the current instance
|
|
182
210
|
// Use disposeBloc method to ensure proper cleanup
|
|
183
211
|
oldBlocInstanceMap.forEach((bloc) => {
|
|
184
|
-
|
|
212
|
+
// TODO: Type assertion for private property access (see explanation above)
|
|
213
|
+
if (
|
|
214
|
+
!bloc._keepAlive &&
|
|
215
|
+
(bloc as any)._disposalState === BlocLifecycleState.ACTIVE
|
|
216
|
+
) {
|
|
185
217
|
this.disposeBloc(bloc);
|
|
186
218
|
}
|
|
187
219
|
});
|
|
188
220
|
|
|
189
221
|
oldIsolatedBlocMap.forEach((blocArray) => {
|
|
190
222
|
blocArray.forEach((bloc) => {
|
|
191
|
-
|
|
223
|
+
// TODO: Type assertion for private property access (see explanation above)
|
|
224
|
+
if (
|
|
225
|
+
!bloc._keepAlive &&
|
|
226
|
+
(bloc as any)._disposalState === BlocLifecycleState.ACTIVE
|
|
227
|
+
) {
|
|
192
228
|
this.disposeBloc(bloc);
|
|
193
229
|
}
|
|
194
230
|
});
|
|
@@ -201,7 +237,7 @@ export class Blac {
|
|
|
201
237
|
|
|
202
238
|
// Use instance manager to reset
|
|
203
239
|
instanceManager.resetInstance();
|
|
204
|
-
}
|
|
240
|
+
};
|
|
205
241
|
static resetInstance = (): void => {
|
|
206
242
|
instanceManager.resetInstance();
|
|
207
243
|
};
|
|
@@ -212,18 +248,32 @@ export class Blac {
|
|
|
212
248
|
*/
|
|
213
249
|
disposeBloc = (bloc: BlocBase<unknown>): void => {
|
|
214
250
|
// Check if bloc is already disposed to prevent double disposal
|
|
215
|
-
|
|
216
|
-
|
|
251
|
+
// TODO: Type assertion needed to access private _disposalState property from external class.
|
|
252
|
+
// This is safe because we know BlocBase has this property, but TypeScript can't verify
|
|
253
|
+
// private property access across class boundaries. Alternative would be to make
|
|
254
|
+
// _disposalState protected, but that would expose internal implementation details.
|
|
255
|
+
const currentState = (bloc as any)._disposalState;
|
|
256
|
+
const validStatesForDisposal = [
|
|
257
|
+
BlocLifecycleState.ACTIVE,
|
|
258
|
+
BlocLifecycleState.DISPOSAL_REQUESTED,
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
if (!validStatesForDisposal.includes(currentState)) {
|
|
262
|
+
this.log(
|
|
263
|
+
`[${bloc._name}:${String(bloc._id)}] disposeBloc called on bloc in invalid state: ${currentState}`,
|
|
264
|
+
);
|
|
217
265
|
return;
|
|
218
266
|
}
|
|
219
267
|
|
|
220
268
|
const base = bloc.constructor as unknown as BlocBaseAbstract;
|
|
221
269
|
const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);
|
|
222
|
-
this.log(
|
|
270
|
+
this.log(
|
|
271
|
+
`[${bloc._name}:${String(bloc._id)}] disposeBloc called. Isolated: ${String(base.isolated)}`,
|
|
272
|
+
);
|
|
223
273
|
|
|
224
274
|
// First dispose the bloc to prevent further operations
|
|
225
275
|
bloc._dispose();
|
|
226
|
-
|
|
276
|
+
|
|
227
277
|
// Then clean up from registries
|
|
228
278
|
if (base.isolated) {
|
|
229
279
|
this.unregisterIsolatedBlocInstance(bloc);
|
|
@@ -231,10 +281,12 @@ export class Blac {
|
|
|
231
281
|
} else {
|
|
232
282
|
this.unregisterBlocInstance(bloc);
|
|
233
283
|
}
|
|
234
|
-
|
|
235
|
-
this.log('dispatched bloc', bloc)
|
|
284
|
+
|
|
285
|
+
this.log('dispatched bloc', bloc);
|
|
236
286
|
};
|
|
237
|
-
static get disposeBloc() {
|
|
287
|
+
static get disposeBloc() {
|
|
288
|
+
return Blac.instance.disposeBloc;
|
|
289
|
+
}
|
|
238
290
|
|
|
239
291
|
/**
|
|
240
292
|
* Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID
|
|
@@ -253,10 +305,10 @@ export class Blac {
|
|
|
253
305
|
unregisterBlocInstance(bloc: BlocBase<unknown>): void {
|
|
254
306
|
const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);
|
|
255
307
|
this.blocInstanceMap.delete(key);
|
|
256
|
-
|
|
308
|
+
|
|
257
309
|
// Clean up UID tracking
|
|
258
310
|
this.uidRegistry.delete(bloc.uid);
|
|
259
|
-
|
|
311
|
+
|
|
260
312
|
// Remove from keep-alive set
|
|
261
313
|
this.keepAliveBlocs.delete(bloc);
|
|
262
314
|
}
|
|
@@ -268,10 +320,10 @@ export class Blac {
|
|
|
268
320
|
registerBlocInstance(bloc: BlocBase<unknown>): void {
|
|
269
321
|
const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);
|
|
270
322
|
this.blocInstanceMap.set(key, bloc);
|
|
271
|
-
|
|
323
|
+
|
|
272
324
|
// Track UID for cleanup
|
|
273
325
|
this.uidRegistry.set(bloc.uid, bloc);
|
|
274
|
-
|
|
326
|
+
|
|
275
327
|
// Track keep-alive blocs
|
|
276
328
|
if (bloc._keepAlive) {
|
|
277
329
|
this.keepAliveBlocs.add(bloc);
|
|
@@ -293,7 +345,7 @@ export class Blac {
|
|
|
293
345
|
|
|
294
346
|
const key = this.createBlocInstanceMapKey(blocClass.name, id);
|
|
295
347
|
const found = this.blocInstanceMap.get(key) as InstanceType<B> | undefined;
|
|
296
|
-
return found
|
|
348
|
+
return found;
|
|
297
349
|
}
|
|
298
350
|
|
|
299
351
|
/**
|
|
@@ -308,13 +360,13 @@ export class Blac {
|
|
|
308
360
|
} else {
|
|
309
361
|
this.isolatedBlocMap.set(blocClass, [bloc]);
|
|
310
362
|
}
|
|
311
|
-
|
|
363
|
+
|
|
312
364
|
// Add to isolated index for O(1) lookups
|
|
313
365
|
this.isolatedBlocIndex.set(bloc.uid, bloc);
|
|
314
|
-
|
|
366
|
+
|
|
315
367
|
// Track UID for cleanup
|
|
316
368
|
this.uidRegistry.set(bloc.uid, bloc);
|
|
317
|
-
|
|
369
|
+
|
|
318
370
|
// Track keep-alive blocs
|
|
319
371
|
if (bloc._keepAlive) {
|
|
320
372
|
this.keepAliveBlocs.add(bloc);
|
|
@@ -328,10 +380,10 @@ export class Blac {
|
|
|
328
380
|
unregisterIsolatedBlocInstance(bloc: BlocBase<unknown>): void {
|
|
329
381
|
const blocClass = bloc.constructor;
|
|
330
382
|
const blocs = this.isolatedBlocMap.get(blocClass as BlocConstructor<any>);
|
|
331
|
-
|
|
383
|
+
|
|
332
384
|
// Ensure both data structures are synchronized
|
|
333
385
|
let wasRemoved = false;
|
|
334
|
-
|
|
386
|
+
|
|
335
387
|
if (blocs) {
|
|
336
388
|
const index = blocs.findIndex((b) => b.uid === bloc.uid);
|
|
337
389
|
if (index !== -1) {
|
|
@@ -343,21 +395,21 @@ export class Blac {
|
|
|
343
395
|
this.isolatedBlocMap.delete(blocClass as BlocConstructor<any>);
|
|
344
396
|
}
|
|
345
397
|
}
|
|
346
|
-
|
|
398
|
+
|
|
347
399
|
// Always try to remove from isolated index, even if not found in map
|
|
348
400
|
const wasInIndex = this.isolatedBlocIndex.delete(bloc.uid);
|
|
349
|
-
|
|
401
|
+
|
|
350
402
|
// Clean up UID tracking
|
|
351
403
|
this.uidRegistry.delete(bloc.uid);
|
|
352
|
-
|
|
404
|
+
|
|
353
405
|
// Remove from keep-alive set
|
|
354
406
|
this.keepAliveBlocs.delete(bloc);
|
|
355
|
-
|
|
407
|
+
|
|
356
408
|
// Log inconsistency for debugging
|
|
357
409
|
if (wasRemoved !== wasInIndex) {
|
|
358
410
|
this.warn(
|
|
359
411
|
`[Blac] Inconsistent state detected during isolated bloc cleanup for ${bloc._name}:${bloc.uid}. ` +
|
|
360
|
-
|
|
412
|
+
`Map removal: ${wasRemoved}, Index removal: ${wasInIndex}`,
|
|
361
413
|
);
|
|
362
414
|
}
|
|
363
415
|
}
|
|
@@ -377,7 +429,9 @@ export class Blac {
|
|
|
377
429
|
return undefined;
|
|
378
430
|
}
|
|
379
431
|
// Find the specific bloc by ID within the isolated array
|
|
380
|
-
const found = blocs.find((b) => b._id === id) as
|
|
432
|
+
const found = blocs.find((b) => b._id === id) as
|
|
433
|
+
| InstanceType<B>
|
|
434
|
+
| undefined;
|
|
381
435
|
return found;
|
|
382
436
|
}
|
|
383
437
|
|
|
@@ -385,7 +439,7 @@ export class Blac {
|
|
|
385
439
|
* Finds an isolated bloc instance by UID (O(1) lookup)
|
|
386
440
|
*/
|
|
387
441
|
findIsolatedBlocInstanceByUid<B extends BlocBase<unknown>>(
|
|
388
|
-
uid: string
|
|
442
|
+
uid: string,
|
|
389
443
|
): B | undefined {
|
|
390
444
|
return this.isolatedBlocIndex.get(uid) as B | undefined;
|
|
391
445
|
}
|
|
@@ -421,29 +475,42 @@ export class Blac {
|
|
|
421
475
|
return newBloc;
|
|
422
476
|
}
|
|
423
477
|
|
|
424
|
-
|
|
425
478
|
activateBloc = (bloc: BlocBase<unknown>): void => {
|
|
479
|
+
// Don't activate disposed blocs
|
|
480
|
+
if ((bloc as any)._disposalState !== BlocLifecycleState.ACTIVE) {
|
|
481
|
+
this.log(
|
|
482
|
+
`[${bloc._name}:${String(bloc._id)}] activateBloc called on disposed bloc. Ignoring.`,
|
|
483
|
+
);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
426
487
|
const base = bloc.constructor as unknown as BlocConstructor<BlocBase<any>>;
|
|
427
488
|
const isIsolated = bloc.isIsolated;
|
|
428
489
|
|
|
429
|
-
let found = isIsolated
|
|
490
|
+
let found = isIsolated
|
|
491
|
+
? this.findIsolatedBlocInstance(base, bloc._id)
|
|
492
|
+
: this.findRegisteredBlocInstance(base, bloc._id);
|
|
430
493
|
if (found) {
|
|
431
494
|
return;
|
|
432
495
|
}
|
|
433
496
|
|
|
434
|
-
this.log(
|
|
497
|
+
this.log(
|
|
498
|
+
`[${bloc._name}:${String(bloc._id)}] activateBloc called. Isolated: ${String(bloc.isIsolated)}`,
|
|
499
|
+
);
|
|
435
500
|
if (bloc.isIsolated) {
|
|
436
501
|
this.registerIsolatedBlocInstance(bloc);
|
|
437
502
|
} else {
|
|
438
503
|
this.registerBlocInstance(bloc);
|
|
439
504
|
}
|
|
440
505
|
};
|
|
441
|
-
static get activateBloc() {
|
|
506
|
+
static get activateBloc() {
|
|
507
|
+
return Blac.instance.activateBloc;
|
|
508
|
+
}
|
|
442
509
|
|
|
443
510
|
/**
|
|
444
511
|
* Gets or creates a bloc instance based on the provided class and options.
|
|
445
512
|
* If a bloc with the given ID exists, it will be returned. Otherwise, a new instance will be created.
|
|
446
|
-
*
|
|
513
|
+
*
|
|
447
514
|
* @param blocClass - The bloc class to get or create
|
|
448
515
|
* @param options - Options including:
|
|
449
516
|
* - id: The instance ID (defaults to class name if not provided)
|
|
@@ -455,47 +522,61 @@ export class Blac {
|
|
|
455
522
|
blocClass: B,
|
|
456
523
|
options: GetBlocOptions<InstanceType<B>> = {},
|
|
457
524
|
): InstanceType<B> => {
|
|
458
|
-
const { id } = options;
|
|
525
|
+
const { id, forceNewInstance } = options;
|
|
459
526
|
const base = blocClass as unknown as BlocBaseAbstract;
|
|
460
527
|
const blocId = id ?? blocClass.name;
|
|
528
|
+
this.log(`[${blocClass.name}:${String(blocId)}] (getBloc) Called:`, {
|
|
529
|
+
options,
|
|
530
|
+
base,
|
|
531
|
+
});
|
|
461
532
|
|
|
462
|
-
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
if (
|
|
470
|
-
|
|
533
|
+
// Skip instance lookup if forceNewInstance is true for isolated blocs
|
|
534
|
+
if (!forceNewInstance) {
|
|
535
|
+
if (base.isolated) {
|
|
536
|
+
const isolatedBloc = this.findIsolatedBlocInstance<B>(
|
|
537
|
+
blocClass,
|
|
538
|
+
options.instanceRef ?? blocId,
|
|
539
|
+
);
|
|
540
|
+
if (isolatedBloc) {
|
|
541
|
+
return isolatedBloc;
|
|
542
|
+
} else {
|
|
543
|
+
if (options.throwIfNotFound) {
|
|
544
|
+
throw new Error(`Isolated bloc ${blocClass.name} not found`);
|
|
545
|
+
}
|
|
471
546
|
}
|
|
472
|
-
}
|
|
473
|
-
} else {
|
|
474
|
-
const registeredBloc = this.findRegisteredBlocInstance(blocClass, blocId)
|
|
475
|
-
if (registeredBloc) {
|
|
476
|
-
this.log(`[${blocClass.name}:${String(blocId)}] (getBloc) Found existing registered instance.`, options);
|
|
477
|
-
return registeredBloc
|
|
478
547
|
} else {
|
|
479
|
-
|
|
480
|
-
|
|
548
|
+
const registeredBloc = this.findRegisteredBlocInstance(
|
|
549
|
+
blocClass,
|
|
550
|
+
blocId,
|
|
551
|
+
);
|
|
552
|
+
if (registeredBloc) {
|
|
553
|
+
return registeredBloc;
|
|
554
|
+
} else {
|
|
555
|
+
if (options.throwIfNotFound) {
|
|
556
|
+
throw new Error(`Registered bloc ${blocClass.name} not found`);
|
|
557
|
+
}
|
|
481
558
|
}
|
|
482
559
|
}
|
|
483
560
|
}
|
|
484
561
|
|
|
485
|
-
const bloc = this.createNewBlocInstance(
|
|
486
|
-
|
|
487
|
-
blocId
|
|
562
|
+
const bloc = this.createNewBlocInstance(blocClass, blocId, options);
|
|
563
|
+
this.log(
|
|
564
|
+
`[${blocClass.name}:${String(blocId)}] (getBloc) No existing instance found. Creating new one.`,
|
|
488
565
|
options,
|
|
566
|
+
bloc,
|
|
489
567
|
);
|
|
490
|
-
|
|
491
|
-
|
|
568
|
+
|
|
492
569
|
if (!bloc) {
|
|
493
|
-
throw new Error(
|
|
570
|
+
throw new Error(
|
|
571
|
+
`[getBloc] Failed to create bloc instance for ${blocClass.name}. This should never happen.`,
|
|
572
|
+
);
|
|
494
573
|
}
|
|
495
|
-
|
|
574
|
+
|
|
496
575
|
return bloc;
|
|
497
576
|
};
|
|
498
|
-
static get getBloc() {
|
|
577
|
+
static get getBloc() {
|
|
578
|
+
return Blac.instance.getBloc;
|
|
579
|
+
}
|
|
499
580
|
|
|
500
581
|
/**
|
|
501
582
|
* Gets a bloc instance or throws an error if it doesn't exist
|
|
@@ -525,7 +606,9 @@ export class Blac {
|
|
|
525
606
|
}
|
|
526
607
|
throw new Error(`Bloc ${blocClass.name} not found`);
|
|
527
608
|
};
|
|
528
|
-
static get getBlocOrThrow() {
|
|
609
|
+
static get getBlocOrThrow() {
|
|
610
|
+
return Blac.instance.getBlocOrThrow;
|
|
611
|
+
}
|
|
529
612
|
|
|
530
613
|
/**
|
|
531
614
|
* Gets all instances of a specific bloc class
|
|
@@ -545,7 +628,8 @@ export class Blac {
|
|
|
545
628
|
|
|
546
629
|
// Search non-isolated blocs
|
|
547
630
|
this.blocInstanceMap.forEach((blocInstance) => {
|
|
548
|
-
if (blocInstance.constructor === blocClass) {
|
|
631
|
+
if (blocInstance.constructor === blocClass) {
|
|
632
|
+
// Strict constructor check
|
|
549
633
|
results.push(blocInstance as InstanceType<B>);
|
|
550
634
|
}
|
|
551
635
|
});
|
|
@@ -554,7 +638,7 @@ export class Blac {
|
|
|
554
638
|
if (options.searchIsolated !== false) {
|
|
555
639
|
const isolatedBlocs = this.isolatedBlocMap.get(blocClass);
|
|
556
640
|
if (isolatedBlocs) {
|
|
557
|
-
results.push(...isolatedBlocs.map(bloc => bloc as InstanceType<B>));
|
|
641
|
+
results.push(...isolatedBlocs.map((bloc) => bloc as InstanceType<B>));
|
|
558
642
|
}
|
|
559
643
|
}
|
|
560
644
|
|
|
@@ -566,19 +650,21 @@ export class Blac {
|
|
|
566
650
|
* @param blocClass - The bloc class to dispose
|
|
567
651
|
*/
|
|
568
652
|
disposeKeepAliveBlocs = <B extends BlocConstructor<any>>(
|
|
569
|
-
blocClass?: B
|
|
653
|
+
blocClass?: B,
|
|
570
654
|
): void => {
|
|
571
655
|
const toDispose: BlocBase<unknown>[] = [];
|
|
572
|
-
|
|
656
|
+
|
|
573
657
|
for (const bloc of this.keepAliveBlocs) {
|
|
574
658
|
if (!blocClass || bloc.constructor === blocClass) {
|
|
575
659
|
toDispose.push(bloc);
|
|
576
660
|
}
|
|
577
661
|
}
|
|
578
|
-
|
|
579
|
-
toDispose.forEach(bloc => bloc._dispose());
|
|
662
|
+
|
|
663
|
+
toDispose.forEach((bloc) => bloc._dispose());
|
|
580
664
|
};
|
|
581
|
-
static get disposeKeepAliveBlocs() {
|
|
665
|
+
static get disposeKeepAliveBlocs() {
|
|
666
|
+
return Blac.instance.disposeKeepAliveBlocs;
|
|
667
|
+
}
|
|
582
668
|
|
|
583
669
|
/**
|
|
584
670
|
* Disposes all blocs matching a pattern
|
|
@@ -586,14 +672,14 @@ export class Blac {
|
|
|
586
672
|
*/
|
|
587
673
|
disposeBlocs = (predicate: (bloc: BlocBase<unknown>) => boolean): void => {
|
|
588
674
|
const toDispose: BlocBase<unknown>[] = [];
|
|
589
|
-
|
|
675
|
+
|
|
590
676
|
// Check registered blocs
|
|
591
677
|
for (const bloc of this.blocInstanceMap.values()) {
|
|
592
678
|
if (predicate(bloc)) {
|
|
593
679
|
toDispose.push(bloc);
|
|
594
680
|
}
|
|
595
681
|
}
|
|
596
|
-
|
|
682
|
+
|
|
597
683
|
// Check isolated blocs
|
|
598
684
|
for (const blocs of this.isolatedBlocMap.values()) {
|
|
599
685
|
for (const bloc of blocs) {
|
|
@@ -602,10 +688,12 @@ export class Blac {
|
|
|
602
688
|
}
|
|
603
689
|
}
|
|
604
690
|
}
|
|
605
|
-
|
|
606
|
-
toDispose.forEach(bloc => bloc._dispose());
|
|
691
|
+
|
|
692
|
+
toDispose.forEach((bloc) => bloc._dispose());
|
|
607
693
|
};
|
|
608
|
-
static get disposeBlocs() {
|
|
694
|
+
static get disposeBlocs() {
|
|
695
|
+
return Blac.instance.disposeBlocs;
|
|
696
|
+
}
|
|
609
697
|
|
|
610
698
|
/**
|
|
611
699
|
* Gets memory usage statistics for debugging
|
|
@@ -614,12 +702,17 @@ export class Blac {
|
|
|
614
702
|
return {
|
|
615
703
|
totalBlocs: this.uidRegistry.size,
|
|
616
704
|
registeredBlocs: this.blocInstanceMap.size,
|
|
617
|
-
isolatedBlocs: Array.from(this.isolatedBlocMap.values()).reduce(
|
|
705
|
+
isolatedBlocs: Array.from(this.isolatedBlocMap.values()).reduce(
|
|
706
|
+
(sum, arr) => sum + arr.length,
|
|
707
|
+
0,
|
|
708
|
+
),
|
|
618
709
|
keepAliveBlocs: this.keepAliveBlocs.size,
|
|
619
710
|
isolatedBlocTypes: this.isolatedBlocMap.size,
|
|
620
711
|
};
|
|
621
712
|
};
|
|
622
|
-
static get getMemoryStats() {
|
|
713
|
+
static get getMemoryStats() {
|
|
714
|
+
return Blac.instance.getMemoryStats;
|
|
715
|
+
}
|
|
623
716
|
|
|
624
717
|
/**
|
|
625
718
|
* Validates consumer references and cleans up orphaned consumers
|
|
@@ -628,18 +721,30 @@ export class Blac {
|
|
|
628
721
|
for (const bloc of this.uidRegistry.values()) {
|
|
629
722
|
// Validate consumers using the bloc's own validation method
|
|
630
723
|
bloc._validateConsumers();
|
|
631
|
-
|
|
724
|
+
|
|
632
725
|
// Check if bloc should be disposed after validation
|
|
633
|
-
|
|
726
|
+
// TODO: Type assertion for private property access (see explanation above)
|
|
727
|
+
if (
|
|
728
|
+
bloc._consumers.size === 0 &&
|
|
729
|
+
!bloc._keepAlive &&
|
|
730
|
+
(bloc as any)._disposalState === BlocLifecycleState.ACTIVE
|
|
731
|
+
) {
|
|
634
732
|
// Schedule disposal for blocs with no consumers
|
|
635
733
|
setTimeout(() => {
|
|
636
734
|
// Double-check conditions before disposal
|
|
637
|
-
|
|
735
|
+
// TODO: Type assertion for private property access (see explanation above)
|
|
736
|
+
if (
|
|
737
|
+
bloc._consumers.size === 0 &&
|
|
738
|
+
!bloc._keepAlive &&
|
|
739
|
+
(bloc as any)._disposalState === BlocLifecycleState.ACTIVE
|
|
740
|
+
) {
|
|
638
741
|
this.disposeBloc(bloc);
|
|
639
742
|
}
|
|
640
743
|
}, 1000); // Give a grace period
|
|
641
744
|
}
|
|
642
745
|
}
|
|
643
746
|
};
|
|
644
|
-
static get validateConsumers() {
|
|
747
|
+
static get validateConsumers() {
|
|
748
|
+
return Blac.instance.validateConsumers;
|
|
749
|
+
}
|
|
645
750
|
}
|