@blac/core 2.0.0-rc.16 → 2.0.0-rc.17
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 +1041 -297
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +619 -224
- package/dist/index.d.ts +619 -224
- package/dist/index.js +986 -276
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -195,17 +195,17 @@ var PluginManager = class {
|
|
|
195
195
|
* Setup lifecycle hooks to notify plugins
|
|
196
196
|
*/
|
|
197
197
|
setupLifecycleHooks() {
|
|
198
|
-
this.registry.on("created", (instance) => {
|
|
199
|
-
this.notifyPlugins("onInstanceCreated", instance);
|
|
198
|
+
this.registry.on("created", (instance$1) => {
|
|
199
|
+
this.notifyPlugins("onInstanceCreated", instance$1);
|
|
200
200
|
});
|
|
201
|
-
this.registry.on("stateChanged", (instance, previousState, currentState, callstack) => {
|
|
202
|
-
this.notifyPlugins("onStateChanged", instance, previousState, currentState, callstack);
|
|
201
|
+
this.registry.on("stateChanged", (instance$1, previousState, currentState, callstack) => {
|
|
202
|
+
this.notifyPlugins("onStateChanged", instance$1, previousState, currentState, callstack);
|
|
203
203
|
});
|
|
204
204
|
this.registry.on("eventAdded", (vertex, event) => {
|
|
205
205
|
this.notifyPlugins("onEventAdded", vertex, event);
|
|
206
206
|
});
|
|
207
|
-
this.registry.on("disposed", (instance) => {
|
|
208
|
-
this.notifyPlugins("onInstanceDisposed", instance);
|
|
207
|
+
this.registry.on("disposed", (instance$1) => {
|
|
208
|
+
this.notifyPlugins("onInstanceDisposed", instance$1);
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
211
|
/**
|
|
@@ -227,23 +227,23 @@ var PluginManager = class {
|
|
|
227
227
|
*/
|
|
228
228
|
createPluginContext() {
|
|
229
229
|
return {
|
|
230
|
-
getInstanceMetadata: (instance) => {
|
|
230
|
+
getInstanceMetadata: (instance$1) => {
|
|
231
231
|
return {
|
|
232
|
-
id: instance.instanceId,
|
|
233
|
-
className: instance.constructor.name,
|
|
234
|
-
isDisposed: instance.isDisposed,
|
|
235
|
-
name: instance.name,
|
|
236
|
-
lastStateChangeTimestamp: instance.lastUpdateTimestamp,
|
|
237
|
-
createdAt: instance.createdAt,
|
|
238
|
-
state: instance.state,
|
|
239
|
-
isIsolated: instance.instanceId.startsWith("isolated-")
|
|
232
|
+
id: instance$1.instanceId,
|
|
233
|
+
className: instance$1.constructor.name,
|
|
234
|
+
isDisposed: instance$1.isDisposed,
|
|
235
|
+
name: instance$1.name,
|
|
236
|
+
lastStateChangeTimestamp: instance$1.lastUpdateTimestamp,
|
|
237
|
+
createdAt: instance$1.createdAt,
|
|
238
|
+
state: instance$1.state,
|
|
239
|
+
isIsolated: instance$1.instanceId.startsWith("isolated-")
|
|
240
240
|
};
|
|
241
241
|
},
|
|
242
|
-
getState: (instance) => {
|
|
243
|
-
return instance.state;
|
|
242
|
+
getState: (instance$1) => {
|
|
243
|
+
return instance$1.state;
|
|
244
244
|
},
|
|
245
245
|
queryInstances: (typeClass) => {
|
|
246
|
-
return
|
|
246
|
+
return this.registry.getAll(typeClass);
|
|
247
247
|
},
|
|
248
248
|
getAllTypes: () => {
|
|
249
249
|
return this.registry.getTypes();
|
|
@@ -335,7 +335,7 @@ const getterExecutionContext = {
|
|
|
335
335
|
depth: 0,
|
|
336
336
|
visitedBlocs: /* @__PURE__ */ new Set()
|
|
337
337
|
};
|
|
338
|
-
const MAX_GETTER_DEPTH = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
|
|
338
|
+
const MAX_GETTER_DEPTH$1 = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
|
|
339
339
|
/**
|
|
340
340
|
* @internal
|
|
341
341
|
*/
|
|
@@ -372,7 +372,7 @@ function isGetter(obj, prop) {
|
|
|
372
372
|
/**
|
|
373
373
|
* @internal
|
|
374
374
|
*/
|
|
375
|
-
function
|
|
375
|
+
function createGetterState() {
|
|
376
376
|
return {
|
|
377
377
|
trackedValues: /* @__PURE__ */ new Map(),
|
|
378
378
|
currentlyAccessing: /* @__PURE__ */ new Set(),
|
|
@@ -423,8 +423,8 @@ function createBlocProxy(bloc) {
|
|
|
423
423
|
tracker.trackedValues.set(prop, cachedValue);
|
|
424
424
|
return cachedValue;
|
|
425
425
|
}
|
|
426
|
-
if (getterExecutionContext.depth >= MAX_GETTER_DEPTH) {
|
|
427
|
-
console.warn(`${BLAC_ERROR_PREFIX} Maximum getter depth (${MAX_GETTER_DEPTH}) exceeded. Possible circular dependency in getter "${String(prop)}" on ${target.constructor.name}. Returning undefined to prevent stack overflow.`);
|
|
426
|
+
if (getterExecutionContext.depth >= MAX_GETTER_DEPTH$1) {
|
|
427
|
+
console.warn(`${BLAC_ERROR_PREFIX} Maximum getter depth (${MAX_GETTER_DEPTH$1}) exceeded. Possible circular dependency in getter "${String(prop)}" on ${target.constructor.name}. Returning undefined to prevent stack overflow.`);
|
|
428
428
|
return;
|
|
429
429
|
}
|
|
430
430
|
if (getterExecutionContext.visitedBlocs.has(target)) {
|
|
@@ -500,7 +500,7 @@ function clearExternalDependencies(tracker) {
|
|
|
500
500
|
/**
|
|
501
501
|
* @internal
|
|
502
502
|
*/
|
|
503
|
-
function
|
|
503
|
+
function resetGetterState(tracker) {
|
|
504
504
|
tracker.trackedValues.clear();
|
|
505
505
|
tracker.currentlyAccessing.clear();
|
|
506
506
|
tracker.trackedGetters.clear();
|
|
@@ -510,6 +510,19 @@ function resetGetterTracker(tracker) {
|
|
|
510
510
|
tracker.externalDependencies.clear();
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
+
//#endregion
|
|
514
|
+
//#region src/core/markers.ts
|
|
515
|
+
/**
|
|
516
|
+
* Marker symbols for identifying container types.
|
|
517
|
+
* These are in a separate file to avoid circular dependencies.
|
|
518
|
+
* @internal
|
|
519
|
+
*/
|
|
520
|
+
/**
|
|
521
|
+
* Marker symbol to identify stateless containers at the type level.
|
|
522
|
+
* Used by React hooks to prevent `useBloc` from accepting stateless containers.
|
|
523
|
+
*/
|
|
524
|
+
const STATELESS_MARKER = Symbol("STATELESS_CONTAINER");
|
|
525
|
+
|
|
513
526
|
//#endregion
|
|
514
527
|
//#region src/utils/static-props.ts
|
|
515
528
|
/**
|
|
@@ -554,6 +567,15 @@ function isKeepAliveClass(Type) {
|
|
|
554
567
|
function isExcludedFromDevTools(Type) {
|
|
555
568
|
return getStaticProp(Type, BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS) === true;
|
|
556
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* Check if a class is a stateless container (StatelessCubit or StatelessVertex).
|
|
572
|
+
* Stateless containers don't have state, emit, update, or subscribe methods.
|
|
573
|
+
* @param Type - The class constructor to check
|
|
574
|
+
* @returns true if the class is a stateless container
|
|
575
|
+
*/
|
|
576
|
+
function isStatelessClass(Type) {
|
|
577
|
+
return Type[STATELESS_MARKER] === true;
|
|
578
|
+
}
|
|
557
579
|
|
|
558
580
|
//#endregion
|
|
559
581
|
//#region src/core/StateContainerRegistry.ts
|
|
@@ -564,7 +586,8 @@ function isExcludedFromDevTools(Type) {
|
|
|
564
586
|
* @example
|
|
565
587
|
* ```ts
|
|
566
588
|
* const registry = new StateContainerRegistry();
|
|
567
|
-
* const instance = registry.
|
|
589
|
+
* const instance = registry.acquire(MyBloc); // ownership, must release
|
|
590
|
+
* const other = registry.ensure(OtherBloc); // no ownership, bloc-to-bloc
|
|
568
591
|
* registry.on('stateChanged', (container, prev, next) => {
|
|
569
592
|
* console.log('State changed:', prev, '->', next);
|
|
570
593
|
* });
|
|
@@ -611,34 +634,35 @@ var StateContainerRegistry = class {
|
|
|
611
634
|
getInstancesMap(Type) {
|
|
612
635
|
return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
|
|
613
636
|
}
|
|
614
|
-
trackExecutionContext(instance) {
|
|
637
|
+
trackExecutionContext(instance$1) {
|
|
615
638
|
const context = getGetterExecutionContext();
|
|
616
|
-
if (context.tracker && context.currentBloc && context.currentBloc !== instance) context.tracker.externalDependencies.add(instance);
|
|
639
|
+
if (context.tracker && context.currentBloc && context.currentBloc !== instance$1) context.tracker.externalDependencies.add(instance$1);
|
|
617
640
|
}
|
|
618
641
|
/**
|
|
619
|
-
*
|
|
642
|
+
* Acquire an instance with ref counting (ownership semantics).
|
|
620
643
|
* Creates a new instance if one doesn't exist, or returns existing and increments ref count.
|
|
644
|
+
* You must call `release()` when done to decrement the ref count.
|
|
621
645
|
* @param Type - The StateContainer class constructor
|
|
622
646
|
* @param instanceKey - Instance key (defaults to 'default')
|
|
623
|
-
* @param options -
|
|
647
|
+
* @param options - Acquisition options
|
|
624
648
|
* @param options.canCreate - Whether to create new instance if not found (default: true)
|
|
625
649
|
* @param options.countRef - Whether to increment ref count (default: true)
|
|
626
650
|
* @param options.props - Props to pass to constructor if creating new instance
|
|
627
651
|
* @param options.trackExecutionContext - Whether to track cross-bloc dependency (default: false)
|
|
628
652
|
* @returns The state container instance
|
|
629
653
|
*/
|
|
630
|
-
|
|
654
|
+
acquire(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, options = {}) {
|
|
631
655
|
const { canCreate = true, countRef = true, props, trackExecutionContext = false } = options;
|
|
632
656
|
const registryConfig = this.typeConfigs.get(Type.name);
|
|
633
657
|
const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
|
|
634
658
|
const config = { instanceId: instanceKey };
|
|
635
659
|
if (isolated && !canCreate) throw new Error(`${BLAC_ERROR_PREFIX} Cannot get isolated instance "${instanceKey}" of ${Type.name} when creation is disabled.`);
|
|
636
660
|
if (isolated) {
|
|
637
|
-
const instance$
|
|
638
|
-
instance$
|
|
661
|
+
const instance$2 = new Type(props);
|
|
662
|
+
instance$2.initConfig(config);
|
|
639
663
|
this.registerType(Type);
|
|
640
|
-
if (trackExecutionContext) this.trackExecutionContext(instance$
|
|
641
|
-
return instance$
|
|
664
|
+
if (trackExecutionContext) this.trackExecutionContext(instance$2);
|
|
665
|
+
return instance$2;
|
|
642
666
|
}
|
|
643
667
|
const instances = this.ensureInstancesMap(Type);
|
|
644
668
|
const entry = instances.get(instanceKey);
|
|
@@ -648,42 +672,42 @@ var StateContainerRegistry = class {
|
|
|
648
672
|
return entry.instance;
|
|
649
673
|
}
|
|
650
674
|
if (!canCreate) throw new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found and creation is disabled.`);
|
|
651
|
-
const instance = new Type(props);
|
|
652
|
-
instance.initConfig(config);
|
|
675
|
+
const instance$1 = new Type(props);
|
|
676
|
+
instance$1.initConfig(config);
|
|
653
677
|
instances.set(instanceKey, {
|
|
654
|
-
instance,
|
|
678
|
+
instance: instance$1,
|
|
655
679
|
refCount: 1
|
|
656
680
|
});
|
|
657
681
|
this.registerType(Type);
|
|
658
|
-
if (trackExecutionContext) this.trackExecutionContext(instance);
|
|
659
|
-
return instance;
|
|
682
|
+
if (trackExecutionContext) this.trackExecutionContext(instance$1);
|
|
683
|
+
return instance$1;
|
|
660
684
|
}
|
|
661
685
|
/**
|
|
662
|
-
*
|
|
686
|
+
* Borrow an existing instance without incrementing ref count (borrowing semantics).
|
|
663
687
|
* Tracks cross-bloc dependency for reactive updates.
|
|
664
688
|
* @param Type - The StateContainer class constructor
|
|
665
689
|
* @param instanceKey - Instance key (defaults to 'default')
|
|
666
690
|
* @returns The state container instance
|
|
667
691
|
* @throws Error if instance doesn't exist
|
|
668
692
|
*/
|
|
669
|
-
|
|
670
|
-
return this.
|
|
693
|
+
borrow(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
694
|
+
return this.acquire(Type, instanceKey, {
|
|
671
695
|
canCreate: false,
|
|
672
696
|
trackExecutionContext: true
|
|
673
697
|
});
|
|
674
698
|
}
|
|
675
699
|
/**
|
|
676
|
-
* Safely
|
|
700
|
+
* Safely borrow an existing instance (borrowing semantics with error handling).
|
|
677
701
|
* Returns discriminated union for type-safe conditional access.
|
|
678
702
|
* @param Type - The StateContainer class constructor
|
|
679
703
|
* @param instanceKey - Instance key (defaults to 'default')
|
|
680
704
|
* @returns Discriminated union with either the instance or an error
|
|
681
705
|
*/
|
|
682
|
-
|
|
706
|
+
borrowSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
683
707
|
try {
|
|
684
708
|
return {
|
|
685
709
|
error: null,
|
|
686
|
-
instance: this.
|
|
710
|
+
instance: this.borrow(Type, instanceKey)
|
|
687
711
|
};
|
|
688
712
|
} catch (error$1) {
|
|
689
713
|
return {
|
|
@@ -693,7 +717,7 @@ var StateContainerRegistry = class {
|
|
|
693
717
|
}
|
|
694
718
|
}
|
|
695
719
|
/**
|
|
696
|
-
*
|
|
720
|
+
* Ensure an instance exists without taking ownership (for bloc-to-bloc communication).
|
|
697
721
|
* Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
|
|
698
722
|
* Tracks cross-bloc dependency for reactive updates.
|
|
699
723
|
*
|
|
@@ -703,8 +727,8 @@ var StateContainerRegistry = class {
|
|
|
703
727
|
* @param instanceKey - Instance key (defaults to 'default')
|
|
704
728
|
* @returns The state container instance
|
|
705
729
|
*/
|
|
706
|
-
|
|
707
|
-
return this.
|
|
730
|
+
ensure(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
731
|
+
return this.acquire(Type, instanceKey, {
|
|
708
732
|
canCreate: true,
|
|
709
733
|
countRef: false,
|
|
710
734
|
trackExecutionContext: true
|
|
@@ -753,9 +777,9 @@ var StateContainerRegistry = class {
|
|
|
753
777
|
forEach(Type, callback) {
|
|
754
778
|
const instances = this.ensureInstancesMap(Type);
|
|
755
779
|
for (const entry of instances.values()) {
|
|
756
|
-
const instance = entry.instance;
|
|
757
|
-
if (!instance.isDisposed) try {
|
|
758
|
-
callback(instance);
|
|
780
|
+
const instance$1 = entry.instance;
|
|
781
|
+
if (!instance$1.isDisposed) try {
|
|
782
|
+
callback(instance$1);
|
|
759
783
|
} catch (error$1) {
|
|
760
784
|
console.error(`${BLAC_ERROR_PREFIX} forEach callback error for ${Type.name}:`, error$1);
|
|
761
785
|
}
|
|
@@ -833,9 +857,9 @@ var StateContainerRegistry = class {
|
|
|
833
857
|
*/
|
|
834
858
|
on(event, listener) {
|
|
835
859
|
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
836
|
-
const instance = this.listeners.get(event);
|
|
837
|
-
if (!instance) throw new Error(`${BLAC_ERROR_PREFIX} Failed to register listener for event '${event}'`);
|
|
838
|
-
instance.add(listener);
|
|
860
|
+
const instance$1 = this.listeners.get(event);
|
|
861
|
+
if (!instance$1) throw new Error(`${BLAC_ERROR_PREFIX} Failed to register listener for event '${event}'`);
|
|
862
|
+
instance$1.add(listener);
|
|
839
863
|
return () => {
|
|
840
864
|
this.listeners.get(event)?.delete(listener);
|
|
841
865
|
};
|
|
@@ -897,132 +921,7 @@ var StateContainer = class StateContainer {
|
|
|
897
921
|
this.__excludeFromDevTools = false;
|
|
898
922
|
}
|
|
899
923
|
static {
|
|
900
|
-
this.
|
|
901
|
-
}
|
|
902
|
-
/**
|
|
903
|
-
* Get the global StateContainerRegistry
|
|
904
|
-
* @returns The registry managing all state container instances
|
|
905
|
-
*/
|
|
906
|
-
static getRegistry() {
|
|
907
|
-
return StateContainer._registry;
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Replace the global registry (clears existing instances)
|
|
911
|
-
* @param registry - The new registry to use
|
|
912
|
-
*/
|
|
913
|
-
static setRegistry(registry) {
|
|
914
|
-
StateContainer._registry.clearAll();
|
|
915
|
-
StateContainer._registry = registry;
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Register this class with the global registry
|
|
919
|
-
* @param isolated - Whether instances should be isolated (component-scoped)
|
|
920
|
-
*/
|
|
921
|
-
static register(isolated = false) {
|
|
922
|
-
StateContainer._registry.register(this, isolated);
|
|
923
|
-
}
|
|
924
|
-
/**
|
|
925
|
-
* Resolve an instance with ref counting (ownership semantics).
|
|
926
|
-
* Creates a new instance if one doesn't exist, or returns existing and increments ref count.
|
|
927
|
-
* @template S - Optional state type override for generic StateContainers
|
|
928
|
-
* @template T - The StateContainer type (inferred from class)
|
|
929
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
930
|
-
* @param options - Resolution options (canCreate, countRef, props, trackExecutionContext)
|
|
931
|
-
* @returns The state container instance
|
|
932
|
-
*/
|
|
933
|
-
static resolve(instanceKey, options) {
|
|
934
|
-
return StateContainer._registry.resolve(this, instanceKey, options);
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
|
-
* Get an existing instance without incrementing ref count (borrowing semantics).
|
|
938
|
-
* @template S - Optional state type override for generic StateContainers
|
|
939
|
-
* @template T - The StateContainer type (inferred from class)
|
|
940
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
941
|
-
* @returns The state container instance
|
|
942
|
-
* @throws Error if instance doesn't exist
|
|
943
|
-
*/
|
|
944
|
-
static get(instanceKey) {
|
|
945
|
-
return StateContainer._registry.get(this, instanceKey);
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Safely get an existing instance with error handling.
|
|
949
|
-
* @template S - Optional state type override for generic StateContainers
|
|
950
|
-
* @template T - The StateContainer type (inferred from class)
|
|
951
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
952
|
-
* @returns Discriminated union with either the instance or an error
|
|
953
|
-
*/
|
|
954
|
-
static getSafe(instanceKey) {
|
|
955
|
-
return StateContainer._registry.getSafe(this, instanceKey);
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Connect to an instance for bloc-to-bloc communication (borrowing semantics).
|
|
959
|
-
* Gets or creates instance without incrementing ref count.
|
|
960
|
-
* Tracks cross-bloc dependency for reactive updates.
|
|
961
|
-
* @template S - Optional state type override for generic StateContainers
|
|
962
|
-
* @template T - The StateContainer type (inferred from class)
|
|
963
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
964
|
-
* @returns The state container instance
|
|
965
|
-
*/
|
|
966
|
-
static connect(instanceKey) {
|
|
967
|
-
return StateContainer._registry.connect(this, instanceKey);
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Release a reference to an instance.
|
|
971
|
-
* Decrements ref count and disposes when it reaches 0 (unless keepAlive).
|
|
972
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
973
|
-
* @param forceDispose - Force immediate disposal regardless of ref count
|
|
974
|
-
*/
|
|
975
|
-
static release(instanceKey, forceDispose = false) {
|
|
976
|
-
StateContainer._registry.release(this, instanceKey, forceDispose);
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Get all instances of this class
|
|
980
|
-
* @returns Array of all instances
|
|
981
|
-
*/
|
|
982
|
-
static getAll() {
|
|
983
|
-
return StateContainer._registry.getAll(this);
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Iterate over all instances of this class
|
|
987
|
-
* @param callback - Function to call for each instance
|
|
988
|
-
*/
|
|
989
|
-
static forEach(callback) {
|
|
990
|
-
StateContainer._registry.forEach(this, callback);
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Clear all instances of this class (disposes them)
|
|
994
|
-
*/
|
|
995
|
-
static clear() {
|
|
996
|
-
StateContainer._registry.clear(this);
|
|
997
|
-
}
|
|
998
|
-
/**
|
|
999
|
-
* Clear all instances from all registered types
|
|
1000
|
-
*/
|
|
1001
|
-
static clearAllInstances() {
|
|
1002
|
-
StateContainer._registry.clearAll();
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Get registry statistics for debugging
|
|
1006
|
-
* @returns Object with registeredTypes, totalInstances, and typeBreakdown
|
|
1007
|
-
*/
|
|
1008
|
-
static getStats() {
|
|
1009
|
-
return StateContainer._registry.getStats();
|
|
1010
|
-
}
|
|
1011
|
-
/**
|
|
1012
|
-
* Get reference count for an instance
|
|
1013
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
1014
|
-
* @returns Current ref count (0 if instance doesn't exist)
|
|
1015
|
-
*/
|
|
1016
|
-
static getRefCount(instanceKey) {
|
|
1017
|
-
return StateContainer._registry.getRefCount(this, instanceKey);
|
|
1018
|
-
}
|
|
1019
|
-
/**
|
|
1020
|
-
* Check if an instance exists
|
|
1021
|
-
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
1022
|
-
* @returns true if instance exists
|
|
1023
|
-
*/
|
|
1024
|
-
static hasInstance(instanceKey) {
|
|
1025
|
-
return StateContainer._registry.hasInstance(this, instanceKey);
|
|
924
|
+
this.enableStackTrace = true;
|
|
1026
925
|
}
|
|
1027
926
|
/** Current props value passed to this container */
|
|
1028
927
|
get props() {
|
|
@@ -1062,7 +961,7 @@ var StateContainer = class StateContainer {
|
|
|
1062
961
|
this.name = this.config.name || this.constructor.name;
|
|
1063
962
|
this.debug = this.config.debug ?? false;
|
|
1064
963
|
this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
|
|
1065
|
-
|
|
964
|
+
globalRegistry.emit("created", this);
|
|
1066
965
|
}
|
|
1067
966
|
/** Current state value */
|
|
1068
967
|
get state() {
|
|
@@ -1093,7 +992,7 @@ var StateContainer = class StateContainer {
|
|
|
1093
992
|
this.emitSystemEvent("dispose", void 0);
|
|
1094
993
|
this.listeners.clear();
|
|
1095
994
|
this.systemEventHandlers.clear();
|
|
1096
|
-
|
|
995
|
+
globalRegistry.emit("disposed", this);
|
|
1097
996
|
if (this.debug) console.log(`[${this.name}] Disposed successfully`);
|
|
1098
997
|
}
|
|
1099
998
|
/**
|
|
@@ -1116,12 +1015,9 @@ var StateContainer = class StateContainer {
|
|
|
1116
1015
|
console.error(`[${this.name}] Error in listener:`, error$1);
|
|
1117
1016
|
}
|
|
1118
1017
|
const stackTrace = this.captureStackTrace();
|
|
1119
|
-
|
|
1018
|
+
globalRegistry.emit("stateChanged", this, previousState, newState, stackTrace);
|
|
1120
1019
|
this.lastUpdateTimestamp = Date.now();
|
|
1121
1020
|
}
|
|
1122
|
-
static {
|
|
1123
|
-
this.enableStackTrace = true;
|
|
1124
|
-
}
|
|
1125
1021
|
captureStackTrace() {
|
|
1126
1022
|
if (!StateContainer.enableStackTrace || typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
|
|
1127
1023
|
try {
|
|
@@ -1280,7 +1176,7 @@ var Vertex = class extends StateContainer {
|
|
|
1280
1176
|
this.eventQueue = [];
|
|
1281
1177
|
this.add = (event) => {
|
|
1282
1178
|
const enrichedEvent = this.enrichEvent(event);
|
|
1283
|
-
|
|
1179
|
+
globalRegistry.emit("eventAdded", this, enrichedEvent);
|
|
1284
1180
|
this.processEvent(enrichedEvent);
|
|
1285
1181
|
};
|
|
1286
1182
|
}
|
|
@@ -1344,6 +1240,223 @@ var Vertex = class extends StateContainer {
|
|
|
1344
1240
|
onEventError(_event, _error) {}
|
|
1345
1241
|
};
|
|
1346
1242
|
|
|
1243
|
+
//#endregion
|
|
1244
|
+
//#region src/core/StatelessCubit.ts
|
|
1245
|
+
/**
|
|
1246
|
+
* A Cubit without state management capabilities.
|
|
1247
|
+
* Use this for action-only containers that don't need to manage or emit state.
|
|
1248
|
+
*
|
|
1249
|
+
* StatelessCubit:
|
|
1250
|
+
* - Has no `emit()`, `update()`, or `patch()` methods
|
|
1251
|
+
* - Has no accessible `state` property
|
|
1252
|
+
* - Cannot be subscribed to (throws error)
|
|
1253
|
+
* - Is rejected by `useBloc` hook (use `useBlocActions` instead)
|
|
1254
|
+
*
|
|
1255
|
+
* @template P - Props type (optional)
|
|
1256
|
+
*
|
|
1257
|
+
* @example
|
|
1258
|
+
* ```typescript
|
|
1259
|
+
* class AnalyticsService extends StatelessCubit {
|
|
1260
|
+
* trackPageView(page: string) {
|
|
1261
|
+
* analytics.track('page_view', { page });
|
|
1262
|
+
* }
|
|
1263
|
+
*
|
|
1264
|
+
* trackClick(element: string) {
|
|
1265
|
+
* analytics.track('click', { element });
|
|
1266
|
+
* }
|
|
1267
|
+
* }
|
|
1268
|
+
*
|
|
1269
|
+
* // In React component:
|
|
1270
|
+
* const analytics = useBlocActions(AnalyticsService);
|
|
1271
|
+
* analytics.trackPageView('/home');
|
|
1272
|
+
* ```
|
|
1273
|
+
*/
|
|
1274
|
+
var StatelessCubit = class extends Cubit {
|
|
1275
|
+
static {
|
|
1276
|
+
this[STATELESS_MARKER] = true;
|
|
1277
|
+
}
|
|
1278
|
+
constructor() {
|
|
1279
|
+
super({});
|
|
1280
|
+
this.patch = (_partial) => {
|
|
1281
|
+
throw new Error(`${this.name} is a StatelessCubit and does not support patch(). Use a regular Cubit if you need state management.`);
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* State is not available on StatelessCubit.
|
|
1286
|
+
* @throws Always throws an error when accessed.
|
|
1287
|
+
*/
|
|
1288
|
+
get state() {
|
|
1289
|
+
throw new Error(`${this.name} is a StatelessCubit and does not have state. Use a regular Cubit if you need state management.`);
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Subscriptions are not available on StatelessCubit.
|
|
1293
|
+
* @throws Always throws an error when called.
|
|
1294
|
+
*/
|
|
1295
|
+
subscribe(_listener) {
|
|
1296
|
+
throw new Error(`${this.name} is a StatelessCubit and does not support subscriptions. Use useBlocActions() instead of useBloc() in React.`);
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* emit() is not available on StatelessCubit.
|
|
1300
|
+
* TypeScript will show an error if you try to call this method.
|
|
1301
|
+
*/
|
|
1302
|
+
emit(_newState) {
|
|
1303
|
+
throw new Error(`${this.name} is a StatelessCubit and does not support emit(). Use a regular Cubit if you need state management.`);
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* update() is not available on StatelessCubit.
|
|
1307
|
+
* TypeScript will show an error if you try to call this method.
|
|
1308
|
+
*/
|
|
1309
|
+
update(_updater) {
|
|
1310
|
+
throw new Error(`${this.name} is a StatelessCubit and does not support update(). Use a regular Cubit if you need state management.`);
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
//#endregion
|
|
1315
|
+
//#region src/core/StatelessVertex.ts
|
|
1316
|
+
/**
|
|
1317
|
+
* An event-driven container without state management capabilities.
|
|
1318
|
+
* Use this for event processing that only produces side effects (logging, analytics, etc).
|
|
1319
|
+
*
|
|
1320
|
+
* StatelessVertex:
|
|
1321
|
+
* - Processes events via `add()` method
|
|
1322
|
+
* - Handlers receive only the event (no `emit` function)
|
|
1323
|
+
* - Has no accessible `state` property
|
|
1324
|
+
* - Cannot be subscribed to (throws error)
|
|
1325
|
+
* - Is rejected by `useBloc` hook (use `useBlocActions` instead)
|
|
1326
|
+
*
|
|
1327
|
+
* @template E - Event union type (discriminated by 'type' property)
|
|
1328
|
+
* @template P - Props type (optional)
|
|
1329
|
+
*
|
|
1330
|
+
* @example
|
|
1331
|
+
* ```typescript
|
|
1332
|
+
* type LogEvent =
|
|
1333
|
+
* | { type: 'info'; message: string }
|
|
1334
|
+
* | { type: 'error'; message: string; error: Error }
|
|
1335
|
+
* | { type: 'warn'; message: string };
|
|
1336
|
+
*
|
|
1337
|
+
* class LoggingVertex extends StatelessVertex<LogEvent> {
|
|
1338
|
+
* constructor() {
|
|
1339
|
+
* super();
|
|
1340
|
+
* this.createHandlers({
|
|
1341
|
+
* info: (event) => console.log('[INFO]', event.message),
|
|
1342
|
+
* error: (event) => console.error('[ERROR]', event.message, event.error),
|
|
1343
|
+
* warn: (event) => console.warn('[WARN]', event.message),
|
|
1344
|
+
* });
|
|
1345
|
+
* }
|
|
1346
|
+
*
|
|
1347
|
+
* info = (message: string) => this.add({ type: 'info', message });
|
|
1348
|
+
* error = (message: string, error: Error) => this.add({ type: 'error', message, error });
|
|
1349
|
+
* warn = (message: string) => this.add({ type: 'warn', message });
|
|
1350
|
+
* }
|
|
1351
|
+
*
|
|
1352
|
+
* // In React component:
|
|
1353
|
+
* const logger = useBlocActions(LoggingVertex);
|
|
1354
|
+
* logger.info('Page loaded');
|
|
1355
|
+
* ```
|
|
1356
|
+
*/
|
|
1357
|
+
var StatelessVertex = class extends StateContainer {
|
|
1358
|
+
static {
|
|
1359
|
+
this[STATELESS_MARKER] = true;
|
|
1360
|
+
}
|
|
1361
|
+
constructor() {
|
|
1362
|
+
super({});
|
|
1363
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
1364
|
+
this.isProcessing = false;
|
|
1365
|
+
this.eventQueue = [];
|
|
1366
|
+
this.add = (event) => {
|
|
1367
|
+
const enrichedEvent = this.enrichEvent(event);
|
|
1368
|
+
globalRegistry.emit("eventAdded", this, enrichedEvent);
|
|
1369
|
+
this.processEvent(enrichedEvent);
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* State is not available on StatelessVertex.
|
|
1374
|
+
* @throws Always throws an error when accessed.
|
|
1375
|
+
*/
|
|
1376
|
+
get state() {
|
|
1377
|
+
throw new Error(`${this.name} is a StatelessVertex and does not have state. Use a regular Vertex if you need state management.`);
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Subscriptions are not available on StatelessVertex.
|
|
1381
|
+
* @throws Always throws an error when called.
|
|
1382
|
+
*/
|
|
1383
|
+
subscribe(_listener) {
|
|
1384
|
+
throw new Error(`${this.name} is a StatelessVertex and does not support subscriptions. Use useBlocActions() instead of useBloc() in React.`);
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Register all event handlers with exhaustive type checking.
|
|
1388
|
+
* TypeScript will error if any event type from the union is missing.
|
|
1389
|
+
*
|
|
1390
|
+
* Unlike regular Vertex, handlers do NOT receive an emit function
|
|
1391
|
+
* since StatelessVertex cannot emit state.
|
|
1392
|
+
*
|
|
1393
|
+
* @param handlers - Map of event type to handler function
|
|
1394
|
+
*/
|
|
1395
|
+
createHandlers(handlers) {
|
|
1396
|
+
for (const [eventType, handler] of Object.entries(handlers)) this.handlers.set(eventType, handler);
|
|
1397
|
+
}
|
|
1398
|
+
/** @internal */
|
|
1399
|
+
enrichEvent(event) {
|
|
1400
|
+
return {
|
|
1401
|
+
...event,
|
|
1402
|
+
timestamp: Date.now()
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
/** @internal */
|
|
1406
|
+
processEvent(event) {
|
|
1407
|
+
if (this.isProcessing) {
|
|
1408
|
+
this.eventQueue.push(event);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
this.isProcessing = true;
|
|
1412
|
+
try {
|
|
1413
|
+
this.handleEvent(event);
|
|
1414
|
+
while (this.eventQueue.length > 0) {
|
|
1415
|
+
const nextEvent = this.eventQueue.shift();
|
|
1416
|
+
this.handleEvent(nextEvent);
|
|
1417
|
+
}
|
|
1418
|
+
} finally {
|
|
1419
|
+
this.isProcessing = false;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
/** @internal */
|
|
1423
|
+
handleEvent(event) {
|
|
1424
|
+
const handler = this.handlers.get(event.type);
|
|
1425
|
+
if (!handler) {
|
|
1426
|
+
if (this.debug) console.warn(`No handler registered for event type: ${event.type}`);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
try {
|
|
1430
|
+
handler(event);
|
|
1431
|
+
} catch (error$1) {
|
|
1432
|
+
if (this.debug) console.error(`Error handling event ${event.type}:`, error$1);
|
|
1433
|
+
this.onEventError(event, error$1);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Called when an error occurs during event processing.
|
|
1438
|
+
* Override to implement custom error handling.
|
|
1439
|
+
*
|
|
1440
|
+
* @param _event - The event that caused the error
|
|
1441
|
+
* @param _error - The error that occurred
|
|
1442
|
+
*/
|
|
1443
|
+
onEventError(_event, _error) {}
|
|
1444
|
+
/**
|
|
1445
|
+
* emit() is not available on StatelessVertex.
|
|
1446
|
+
* @internal
|
|
1447
|
+
*/
|
|
1448
|
+
emit(_newState) {
|
|
1449
|
+
throw new Error(`${this.name} is a StatelessVertex and does not support emit(). Use a regular Vertex if you need state management.`);
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* update() is not available on StatelessVertex.
|
|
1453
|
+
* @internal
|
|
1454
|
+
*/
|
|
1455
|
+
update(_updater) {
|
|
1456
|
+
throw new Error(`${this.name} is a StatelessVertex and does not support update(). Use a regular Vertex if you need state management.`);
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1347
1460
|
//#endregion
|
|
1348
1461
|
//#region src/plugin/BlacPlugin.ts
|
|
1349
1462
|
/**
|
|
@@ -1391,7 +1504,7 @@ function isProxyable(value) {
|
|
|
1391
1504
|
*
|
|
1392
1505
|
* @internal
|
|
1393
1506
|
*/
|
|
1394
|
-
function
|
|
1507
|
+
function createProxyState() {
|
|
1395
1508
|
return {
|
|
1396
1509
|
trackedPaths: /* @__PURE__ */ new Set(),
|
|
1397
1510
|
isTracking: false,
|
|
@@ -1409,7 +1522,7 @@ function createProxyTrackerState() {
|
|
|
1409
1522
|
*
|
|
1410
1523
|
* @internal
|
|
1411
1524
|
*/
|
|
1412
|
-
function
|
|
1525
|
+
function startProxy(state) {
|
|
1413
1526
|
state.isTracking = true;
|
|
1414
1527
|
state.trackedPaths.clear();
|
|
1415
1528
|
}
|
|
@@ -1420,7 +1533,7 @@ function startProxyTracking(state) {
|
|
|
1420
1533
|
*
|
|
1421
1534
|
* @internal
|
|
1422
1535
|
*/
|
|
1423
|
-
function
|
|
1536
|
+
function stopProxy(state) {
|
|
1424
1537
|
state.isTracking = false;
|
|
1425
1538
|
return new Set(state.trackedPaths);
|
|
1426
1539
|
}
|
|
@@ -1459,7 +1572,7 @@ function createArrayProxy(state, target, path, depth = 0) {
|
|
|
1459
1572
|
if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
1460
1573
|
else fullPath = path ? `${path}.${prop}` : prop;
|
|
1461
1574
|
} else return value;
|
|
1462
|
-
if (isProxyable(value)) return
|
|
1575
|
+
if (isProxyable(value)) return createInternal(state, value, fullPath, depth + 1);
|
|
1463
1576
|
if (state.isTracking) state.trackedPaths.add(fullPath);
|
|
1464
1577
|
return value;
|
|
1465
1578
|
} });
|
|
@@ -1480,7 +1593,7 @@ function createArrayProxy(state, target, path, depth = 0) {
|
|
|
1480
1593
|
*
|
|
1481
1594
|
* @internal
|
|
1482
1595
|
*/
|
|
1483
|
-
function
|
|
1596
|
+
function createInternal(state, target, path = "", depth = 0) {
|
|
1484
1597
|
if (!state.isTracking || !isProxyable(target)) return target;
|
|
1485
1598
|
if (depth >= state.maxDepth) return target;
|
|
1486
1599
|
if (state.proxyCache.has(target)) return state.proxyCache.get(target);
|
|
@@ -1501,7 +1614,7 @@ function createProxyInternal(state, target, path = "", depth = 0) {
|
|
|
1501
1614
|
if (typeof prop === "string" && state.isTracking) {
|
|
1502
1615
|
if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
|
|
1503
1616
|
}
|
|
1504
|
-
if (isProxyable(value)) return
|
|
1617
|
+
if (isProxyable(value)) return createInternal(state, value, fullPath, depth + 1);
|
|
1505
1618
|
return value;
|
|
1506
1619
|
},
|
|
1507
1620
|
has: (obj, prop) => {
|
|
@@ -1529,11 +1642,11 @@ function createProxyInternal(state, target, path = "", depth = 0) {
|
|
|
1529
1642
|
*
|
|
1530
1643
|
* @internal
|
|
1531
1644
|
*/
|
|
1532
|
-
function
|
|
1645
|
+
function createForTarget(state, target) {
|
|
1533
1646
|
if (state.lastProxiedState === target && state.lastProxy) return state.lastProxy;
|
|
1534
1647
|
state.proxyCache = /* @__PURE__ */ new WeakMap();
|
|
1535
1648
|
state.boundFunctionsCache = null;
|
|
1536
|
-
const proxy =
|
|
1649
|
+
const proxy = createInternal(state, target, "", 0);
|
|
1537
1650
|
state.lastProxiedState = target;
|
|
1538
1651
|
state.lastProxy = proxy;
|
|
1539
1652
|
return proxy;
|
|
@@ -1674,9 +1787,9 @@ function optimizeTrackedPaths(paths) {
|
|
|
1674
1787
|
/**
|
|
1675
1788
|
* @internal
|
|
1676
1789
|
*/
|
|
1677
|
-
function
|
|
1790
|
+
function createDependencyState() {
|
|
1678
1791
|
return {
|
|
1679
|
-
|
|
1792
|
+
proxyState: createProxyState(),
|
|
1680
1793
|
previousRenderPaths: /* @__PURE__ */ new Set(),
|
|
1681
1794
|
currentRenderPaths: /* @__PURE__ */ new Set(),
|
|
1682
1795
|
pathCache: /* @__PURE__ */ new Map(),
|
|
@@ -1687,21 +1800,21 @@ function createTrackerState() {
|
|
|
1687
1800
|
/**
|
|
1688
1801
|
* @internal
|
|
1689
1802
|
*/
|
|
1690
|
-
function
|
|
1691
|
-
|
|
1803
|
+
function startDependency(tracker) {
|
|
1804
|
+
startProxy(tracker.proxyState);
|
|
1692
1805
|
}
|
|
1693
1806
|
/**
|
|
1694
1807
|
* @internal
|
|
1695
1808
|
*/
|
|
1696
|
-
function
|
|
1697
|
-
return
|
|
1809
|
+
function createDependencyProxy(tracker, state) {
|
|
1810
|
+
return createForTarget(tracker.proxyState, state);
|
|
1698
1811
|
}
|
|
1699
1812
|
/**
|
|
1700
1813
|
* @internal
|
|
1701
1814
|
*/
|
|
1702
|
-
function
|
|
1815
|
+
function capturePaths(tracker, state) {
|
|
1703
1816
|
tracker.previousRenderPaths = tracker.currentRenderPaths;
|
|
1704
|
-
tracker.currentRenderPaths = optimizeTrackedPaths(
|
|
1817
|
+
tracker.currentRenderPaths = optimizeTrackedPaths(stopProxy(tracker.proxyState));
|
|
1705
1818
|
if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
|
|
1706
1819
|
const trackedPathsUnion = new Set(tracker.previousRenderPaths);
|
|
1707
1820
|
for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
|
|
@@ -1722,7 +1835,7 @@ function captureTrackedPaths(tracker, state) {
|
|
|
1722
1835
|
/**
|
|
1723
1836
|
* @internal
|
|
1724
1837
|
*/
|
|
1725
|
-
function
|
|
1838
|
+
function hasDependencyChanges(tracker, state) {
|
|
1726
1839
|
if (tracker.pathCache.size === 0) return true;
|
|
1727
1840
|
tracker.lastCheckedValues.clear();
|
|
1728
1841
|
for (const [path, info$1] of tracker.pathCache.entries()) {
|
|
@@ -1740,7 +1853,294 @@ function hasChanges(tracker, state) {
|
|
|
1740
1853
|
* @internal
|
|
1741
1854
|
*/
|
|
1742
1855
|
function hasTrackedData(tracker) {
|
|
1743
|
-
return tracker.
|
|
1856
|
+
return tracker.proxyState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
//#endregion
|
|
1860
|
+
//#region src/tracking/tracking-proxy.ts
|
|
1861
|
+
/**
|
|
1862
|
+
* Create a new tracking proxy state.
|
|
1863
|
+
*/
|
|
1864
|
+
function createState() {
|
|
1865
|
+
return {
|
|
1866
|
+
dependencyState: createDependencyState(),
|
|
1867
|
+
getterState: createGetterState(),
|
|
1868
|
+
dependencies: /* @__PURE__ */ new Set(),
|
|
1869
|
+
isTracking: false
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Start tracking on a tracking proxy.
|
|
1874
|
+
*/
|
|
1875
|
+
function startTracking(tracker) {
|
|
1876
|
+
tracker.isTracking = true;
|
|
1877
|
+
tracker.dependencies.clear();
|
|
1878
|
+
tracker.getterState.isTracking = true;
|
|
1879
|
+
tracker.getterState.externalDependencies.clear();
|
|
1880
|
+
startDependency(tracker.dependencyState);
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Stop tracking and collect all dependencies.
|
|
1884
|
+
*/
|
|
1885
|
+
function stopTracking(tracker, bloc) {
|
|
1886
|
+
tracker.isTracking = false;
|
|
1887
|
+
tracker.getterState.isTracking = false;
|
|
1888
|
+
capturePaths(tracker.dependencyState, bloc.state);
|
|
1889
|
+
commitTrackedGetters(tracker.getterState);
|
|
1890
|
+
const allDeps = new Set(tracker.dependencies);
|
|
1891
|
+
for (const dep of tracker.getterState.externalDependencies) allDeps.add(dep);
|
|
1892
|
+
tracker.getterState.externalDependencies.clear();
|
|
1893
|
+
return allDeps;
|
|
1894
|
+
}
|
|
1895
|
+
/**
|
|
1896
|
+
* Check if tracked state or getters have changed.
|
|
1897
|
+
*/
|
|
1898
|
+
function hasChanges(tracker, bloc) {
|
|
1899
|
+
invalidateRenderCache(tracker.getterState);
|
|
1900
|
+
const stateChanged = hasDependencyChanges(tracker.dependencyState, bloc.state);
|
|
1901
|
+
const getterChanged = hasGetterChanges(bloc, tracker.getterState);
|
|
1902
|
+
return stateChanged || getterChanged;
|
|
1903
|
+
}
|
|
1904
|
+
const MAX_GETTER_DEPTH = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
|
|
1905
|
+
/**
|
|
1906
|
+
* Create a tracking proxy for a bloc instance.
|
|
1907
|
+
* Tracks both state property access and getter access.
|
|
1908
|
+
*/
|
|
1909
|
+
function createTrackingProxy(bloc, tracker) {
|
|
1910
|
+
tracker.dependencies.add(bloc);
|
|
1911
|
+
const stateProxyCache = /* @__PURE__ */ new WeakMap();
|
|
1912
|
+
return new Proxy(bloc, { get(target, prop, receiver) {
|
|
1913
|
+
if (prop === "state") {
|
|
1914
|
+
if (!tracker.isTracking) return target.state;
|
|
1915
|
+
const rawState = target.state;
|
|
1916
|
+
if (rawState === null || typeof rawState !== "object") return rawState;
|
|
1917
|
+
if (stateProxyCache.has(rawState)) return stateProxyCache.get(rawState);
|
|
1918
|
+
const stateProxy = createDependencyProxy(tracker.dependencyState, rawState);
|
|
1919
|
+
stateProxyCache.set(rawState, stateProxy);
|
|
1920
|
+
return stateProxy;
|
|
1921
|
+
}
|
|
1922
|
+
if (typeof prop === "symbol") return Reflect.get(target, prop, receiver);
|
|
1923
|
+
const value = Reflect.get(target, prop, receiver);
|
|
1924
|
+
if (typeof value === "function") return value.bind(target);
|
|
1925
|
+
if (tracker.isTracking && isGetter(target, prop)) {
|
|
1926
|
+
tracker.getterState.currentlyAccessing.add(prop);
|
|
1927
|
+
if (tracker.getterState.cacheValid && tracker.getterState.renderCache.has(prop)) {
|
|
1928
|
+
const cachedValue = tracker.getterState.renderCache.get(prop);
|
|
1929
|
+
tracker.getterState.trackedValues.set(prop, cachedValue);
|
|
1930
|
+
return cachedValue;
|
|
1931
|
+
}
|
|
1932
|
+
const context = getGetterExecutionContext();
|
|
1933
|
+
if (context.depth >= MAX_GETTER_DEPTH) {
|
|
1934
|
+
console.warn(`${BLAC_ERROR_PREFIX} Maximum getter depth (${MAX_GETTER_DEPTH}) exceeded. Possible circular dependency in getter "${String(prop)}" on ${target.constructor.name}.`);
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
if (context.visitedBlocs.has(target)) {
|
|
1938
|
+
console.warn(`${BLAC_ERROR_PREFIX} Circular dependency detected: getter "${String(prop)}" on ${target.constructor.name}.`);
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
const prevContext = {
|
|
1942
|
+
tracker: context.tracker,
|
|
1943
|
+
currentBloc: context.currentBloc,
|
|
1944
|
+
depth: context.depth,
|
|
1945
|
+
visitedBlocs: new Set(context.visitedBlocs)
|
|
1946
|
+
};
|
|
1947
|
+
context.tracker = tracker.getterState;
|
|
1948
|
+
context.currentBloc = target;
|
|
1949
|
+
context.depth++;
|
|
1950
|
+
context.visitedBlocs.add(target);
|
|
1951
|
+
try {
|
|
1952
|
+
const getterValue = getDescriptor(target, prop).get.call(target);
|
|
1953
|
+
tracker.getterState.trackedValues.set(prop, getterValue);
|
|
1954
|
+
return getterValue;
|
|
1955
|
+
} catch (error$1) {
|
|
1956
|
+
tracker.getterState.currentlyAccessing.delete(prop);
|
|
1957
|
+
throw error$1;
|
|
1958
|
+
} finally {
|
|
1959
|
+
context.tracker = prevContext.tracker;
|
|
1960
|
+
context.currentBloc = prevContext.currentBloc;
|
|
1961
|
+
context.depth = prevContext.depth;
|
|
1962
|
+
context.visitedBlocs = prevContext.visitedBlocs;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
return value;
|
|
1966
|
+
} });
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
//#endregion
|
|
1970
|
+
//#region src/tracking/dependency-manager.ts
|
|
1971
|
+
/**
|
|
1972
|
+
* Manages subscriptions to state container dependencies.
|
|
1973
|
+
* Provides efficient sync mechanism to add/remove subscriptions
|
|
1974
|
+
* as dependencies change between callback invocations.
|
|
1975
|
+
*/
|
|
1976
|
+
var DependencyManager = class {
|
|
1977
|
+
constructor() {
|
|
1978
|
+
this.subscriptions = /* @__PURE__ */ new Map();
|
|
1979
|
+
this.currentDeps = /* @__PURE__ */ new Set();
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Sync subscriptions with a new set of dependencies.
|
|
1983
|
+
* Adds subscriptions for new deps, removes subscriptions for stale deps.
|
|
1984
|
+
*
|
|
1985
|
+
* @param newDeps - The new set of dependencies to subscribe to
|
|
1986
|
+
* @param onChange - Callback to invoke when any dependency changes
|
|
1987
|
+
* @param exclude - Optional instance to exclude from subscriptions (e.g., primary bloc)
|
|
1988
|
+
* @returns true if the dependency set changed, false if unchanged
|
|
1989
|
+
*/
|
|
1990
|
+
sync(newDeps, onChange, exclude) {
|
|
1991
|
+
const filteredNewDeps = /* @__PURE__ */ new Set();
|
|
1992
|
+
for (const dep of newDeps) if (dep !== exclude && !dep.isDisposed) filteredNewDeps.add(dep);
|
|
1993
|
+
if (this.areSetsEqual(this.currentDeps, filteredNewDeps)) return false;
|
|
1994
|
+
for (const dep of this.currentDeps) if (!filteredNewDeps.has(dep)) {
|
|
1995
|
+
const unsub = this.subscriptions.get(dep);
|
|
1996
|
+
if (unsub) {
|
|
1997
|
+
unsub();
|
|
1998
|
+
this.subscriptions.delete(dep);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
for (const dep of filteredNewDeps) if (!this.currentDeps.has(dep) && !this.subscriptions.has(dep)) {
|
|
2002
|
+
const unsub = dep.subscribe(onChange);
|
|
2003
|
+
this.subscriptions.set(dep, unsub);
|
|
2004
|
+
}
|
|
2005
|
+
this.currentDeps = filteredNewDeps;
|
|
2006
|
+
return true;
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Add a single dependency subscription.
|
|
2010
|
+
*/
|
|
2011
|
+
add(dep, onChange) {
|
|
2012
|
+
if (this.subscriptions.has(dep) || dep.isDisposed) return;
|
|
2013
|
+
const unsub = dep.subscribe(onChange);
|
|
2014
|
+
this.subscriptions.set(dep, unsub);
|
|
2015
|
+
this.currentDeps.add(dep);
|
|
2016
|
+
}
|
|
2017
|
+
/**
|
|
2018
|
+
* Check if a dependency is currently subscribed.
|
|
2019
|
+
*/
|
|
2020
|
+
has(dep) {
|
|
2021
|
+
return this.currentDeps.has(dep);
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Get the current set of dependencies.
|
|
2025
|
+
*/
|
|
2026
|
+
getDependencies() {
|
|
2027
|
+
return new Set(this.currentDeps);
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* Clean up all active subscriptions.
|
|
2031
|
+
*/
|
|
2032
|
+
cleanup() {
|
|
2033
|
+
for (const unsub of this.subscriptions.values()) unsub();
|
|
2034
|
+
this.subscriptions.clear();
|
|
2035
|
+
this.currentDeps.clear();
|
|
2036
|
+
}
|
|
2037
|
+
areSetsEqual(a, b) {
|
|
2038
|
+
if (a.size !== b.size) return false;
|
|
2039
|
+
for (const item of a) if (!b.has(item)) return false;
|
|
2040
|
+
return true;
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
|
|
2044
|
+
//#endregion
|
|
2045
|
+
//#region src/tracking/tracked.ts
|
|
2046
|
+
/**
|
|
2047
|
+
* Run a callback while tracking all bloc dependencies accessed.
|
|
2048
|
+
* Returns both the result and the set of discovered dependencies.
|
|
2049
|
+
*
|
|
2050
|
+
* @example
|
|
2051
|
+
* ```ts
|
|
2052
|
+
* const { result, dependencies } = tracked(() => {
|
|
2053
|
+
* const user = ensure(UserBloc);
|
|
2054
|
+
* return user.fullName; // getter that may access other blocs
|
|
2055
|
+
* });
|
|
2056
|
+
* // dependencies contains UserBloc + any blocs accessed in fullName getter
|
|
2057
|
+
* ```
|
|
2058
|
+
*/
|
|
2059
|
+
function tracked(callback, options) {
|
|
2060
|
+
const tracker = createState();
|
|
2061
|
+
startTracking(tracker);
|
|
2062
|
+
let result;
|
|
2063
|
+
try {
|
|
2064
|
+
result = callback();
|
|
2065
|
+
} finally {
|
|
2066
|
+
stopTracking(tracker, { state: null });
|
|
2067
|
+
}
|
|
2068
|
+
const dependencies = new Set(tracker.dependencies);
|
|
2069
|
+
for (const dep of tracker.getterState.externalDependencies) dependencies.add(dep);
|
|
2070
|
+
if (options?.exclude) dependencies.delete(options.exclude);
|
|
2071
|
+
return {
|
|
2072
|
+
result,
|
|
2073
|
+
dependencies
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Context for running tracked callbacks with bloc proxies.
|
|
2078
|
+
* Provides methods to create proxies and check for changes.
|
|
2079
|
+
*/
|
|
2080
|
+
var TrackedContext = class {
|
|
2081
|
+
constructor() {
|
|
2082
|
+
this.proxiedBlocs = /* @__PURE__ */ new WeakMap();
|
|
2083
|
+
this.primaryBlocs = /* @__PURE__ */ new Set();
|
|
2084
|
+
this.tracker = createState();
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Get a tracking proxy for a bloc instance.
|
|
2088
|
+
* The proxy will track state and getter accesses.
|
|
2089
|
+
*/
|
|
2090
|
+
proxy(bloc) {
|
|
2091
|
+
const cached = this.proxiedBlocs.get(bloc);
|
|
2092
|
+
if (cached) return cached;
|
|
2093
|
+
const proxied = createTrackingProxy(bloc, this.tracker);
|
|
2094
|
+
this.proxiedBlocs.set(bloc, proxied);
|
|
2095
|
+
this.primaryBlocs.add(bloc);
|
|
2096
|
+
return proxied;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Start tracking for a new callback execution.
|
|
2100
|
+
*/
|
|
2101
|
+
start() {
|
|
2102
|
+
startTracking(this.tracker);
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Stop tracking and get discovered dependencies.
|
|
2106
|
+
* Excludes primary blocs (those explicitly proxied via proxy()).
|
|
2107
|
+
*/
|
|
2108
|
+
stop() {
|
|
2109
|
+
const allDeps = /* @__PURE__ */ new Set();
|
|
2110
|
+
for (const bloc of this.primaryBlocs) {
|
|
2111
|
+
const deps = stopTracking(this.tracker, bloc);
|
|
2112
|
+
for (const dep of deps) if (!this.primaryBlocs.has(dep)) allDeps.add(dep);
|
|
2113
|
+
}
|
|
2114
|
+
for (const dep of this.tracker.getterState.externalDependencies) if (!this.primaryBlocs.has(dep)) allDeps.add(dep);
|
|
2115
|
+
return allDeps;
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* Check if any tracked state or getters have changed.
|
|
2119
|
+
*/
|
|
2120
|
+
changed() {
|
|
2121
|
+
for (const bloc of this.primaryBlocs) if (hasChanges(this.tracker, bloc)) return true;
|
|
2122
|
+
return false;
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* Get all primary blocs (those explicitly proxied).
|
|
2126
|
+
*/
|
|
2127
|
+
getPrimaryBlocs() {
|
|
2128
|
+
return new Set(this.primaryBlocs);
|
|
2129
|
+
}
|
|
2130
|
+
/**
|
|
2131
|
+
* Reset the context for reuse.
|
|
2132
|
+
*/
|
|
2133
|
+
reset() {
|
|
2134
|
+
this.tracker = createState();
|
|
2135
|
+
this.proxiedBlocs = /* @__PURE__ */ new WeakMap();
|
|
2136
|
+
this.primaryBlocs.clear();
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
/**
|
|
2140
|
+
* Create a new tracked context for manual control over tracking.
|
|
2141
|
+
*/
|
|
2142
|
+
function createTrackedContext() {
|
|
2143
|
+
return new TrackedContext();
|
|
1744
2144
|
}
|
|
1745
2145
|
|
|
1746
2146
|
//#endregion
|
|
@@ -1749,16 +2149,13 @@ function hasTrackedData(tracker) {
|
|
|
1749
2149
|
* Manages subscriptions to external bloc dependencies for getter tracking.
|
|
1750
2150
|
* When a getter accesses another bloc's state, this manager ensures
|
|
1751
2151
|
* re-renders occur when those external dependencies change.
|
|
2152
|
+
*
|
|
2153
|
+
* @deprecated Use DependencyManager from tracking module instead.
|
|
2154
|
+
* This class is kept for backward compatibility.
|
|
1752
2155
|
*/
|
|
1753
|
-
var
|
|
2156
|
+
var ExternalDepsManager = class {
|
|
1754
2157
|
constructor() {
|
|
1755
|
-
this.
|
|
1756
|
-
this.previousDeps = /* @__PURE__ */ new Set();
|
|
1757
|
-
}
|
|
1758
|
-
areDependenciesEqual(oldDeps, newDeps) {
|
|
1759
|
-
if (oldDeps.size !== newDeps.size) return false;
|
|
1760
|
-
for (const dep of newDeps) if (!oldDeps.has(dep)) return false;
|
|
1761
|
-
return true;
|
|
2158
|
+
this.manager = new DependencyManager();
|
|
1762
2159
|
}
|
|
1763
2160
|
/**
|
|
1764
2161
|
* Update subscriptions to external bloc dependencies.
|
|
@@ -1768,32 +2165,22 @@ var ExternalDependencyManager = class {
|
|
|
1768
2165
|
* @param onGetterChange - Callback to invoke when external dependency changes
|
|
1769
2166
|
* @returns true if subscriptions were updated, false if unchanged
|
|
1770
2167
|
*/
|
|
1771
|
-
updateSubscriptions(
|
|
1772
|
-
if (!
|
|
1773
|
-
const currentDeps =
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1778
|
-
this.
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
const unsub = externalBloc.subscribe(() => {
|
|
1782
|
-
invalidateRenderCache(getterTracker);
|
|
1783
|
-
if (hasGetterChanges(rawInstance, getterTracker)) onGetterChange();
|
|
1784
|
-
});
|
|
1785
|
-
this.subscriptions.push(unsub);
|
|
1786
|
-
}
|
|
1787
|
-
this.previousDeps = new Set(currentDeps);
|
|
1788
|
-
clearExternalDependencies(getterTracker);
|
|
1789
|
-
return true;
|
|
2168
|
+
updateSubscriptions(getterState, rawInstance, onGetterChange) {
|
|
2169
|
+
if (!getterState?.externalDependencies) return false;
|
|
2170
|
+
const currentDeps = getterState.externalDependencies;
|
|
2171
|
+
const onExternalChange = () => {
|
|
2172
|
+
invalidateRenderCache(getterState);
|
|
2173
|
+
if (hasGetterChanges(rawInstance, getterState)) onGetterChange();
|
|
2174
|
+
};
|
|
2175
|
+
const changed = this.manager.sync(currentDeps, onExternalChange, rawInstance);
|
|
2176
|
+
clearExternalDependencies(getterState);
|
|
2177
|
+
return changed;
|
|
1790
2178
|
}
|
|
1791
2179
|
/**
|
|
1792
2180
|
* Clean up all active subscriptions
|
|
1793
2181
|
*/
|
|
1794
2182
|
cleanup() {
|
|
1795
|
-
this.
|
|
1796
|
-
this.subscriptions = [];
|
|
2183
|
+
this.manager.cleanup();
|
|
1797
2184
|
}
|
|
1798
2185
|
};
|
|
1799
2186
|
/**
|
|
@@ -1803,21 +2190,21 @@ var ExternalDependencyManager = class {
|
|
|
1803
2190
|
* @param adapterState - The adapter state for tracking
|
|
1804
2191
|
* @returns Subscribe function for use with useSyncExternalStore
|
|
1805
2192
|
*/
|
|
1806
|
-
function
|
|
2193
|
+
function autoTrackSubscribe(instance$1, adapterState) {
|
|
1807
2194
|
return (callback) => {
|
|
1808
|
-
return instance.subscribe(() => {
|
|
1809
|
-
const
|
|
1810
|
-
const hasStateDeps =
|
|
1811
|
-
const hasGetterDeps = adapterState.
|
|
1812
|
-
const isPrimitiveState = instance.state !== null && typeof instance.state !== "object" && typeof instance.state !== "function";
|
|
2195
|
+
return instance$1.subscribe(() => {
|
|
2196
|
+
const depState = adapterState.dependencyState || (adapterState.dependencyState = createDependencyState());
|
|
2197
|
+
const hasStateDeps = depState.pathCache && depState.pathCache.size > 0;
|
|
2198
|
+
const hasGetterDeps = adapterState.getterState && adapterState.getterState.trackedGetters.size > 0;
|
|
2199
|
+
const isPrimitiveState = instance$1.state !== null && typeof instance$1.state !== "object" && typeof instance$1.state !== "function";
|
|
1813
2200
|
if (!hasStateDeps && !hasGetterDeps && !isPrimitiveState) return;
|
|
1814
|
-
let stateChanged =
|
|
2201
|
+
let stateChanged = hasDependencyChanges(depState, instance$1.state);
|
|
1815
2202
|
if (!hasStateDeps && hasGetterDeps) stateChanged = false;
|
|
1816
2203
|
if (stateChanged) {
|
|
1817
2204
|
callback();
|
|
1818
2205
|
return;
|
|
1819
2206
|
}
|
|
1820
|
-
if (hasGetterChanges(instance, adapterState.
|
|
2207
|
+
if (hasGetterChanges(instance$1, adapterState.getterState)) callback();
|
|
1821
2208
|
});
|
|
1822
2209
|
};
|
|
1823
2210
|
}
|
|
@@ -1829,10 +2216,10 @@ function createAutoTrackSubscribe(instance, adapterState) {
|
|
|
1829
2216
|
* @param config - Configuration with dependencies function
|
|
1830
2217
|
* @returns Subscribe function for use with useSyncExternalStore
|
|
1831
2218
|
*/
|
|
1832
|
-
function
|
|
2219
|
+
function manualDepsSubscribe(instance$1, adapterState, config) {
|
|
1833
2220
|
return (callback) => {
|
|
1834
|
-
return instance.subscribe(() => {
|
|
1835
|
-
const newDeps = config.dependencies(instance.state, instance);
|
|
2221
|
+
return instance$1.subscribe(() => {
|
|
2222
|
+
const newDeps = config.dependencies(instance$1.state, instance$1);
|
|
1836
2223
|
if (!adapterState.manualDepsCache || !shallowEqual(adapterState.manualDepsCache, newDeps)) {
|
|
1837
2224
|
adapterState.manualDepsCache = newDeps;
|
|
1838
2225
|
callback();
|
|
@@ -1846,8 +2233,8 @@ function createManualDepsSubscribe(instance, adapterState, config) {
|
|
|
1846
2233
|
* @param instance - The state container instance
|
|
1847
2234
|
* @returns Subscribe function for use with useSyncExternalStore
|
|
1848
2235
|
*/
|
|
1849
|
-
function
|
|
1850
|
-
return (callback) => instance.subscribe(callback);
|
|
2236
|
+
function noTrackSubscribe(instance$1) {
|
|
2237
|
+
return (callback) => instance$1.subscribe(callback);
|
|
1851
2238
|
}
|
|
1852
2239
|
/**
|
|
1853
2240
|
* Create a snapshot function for auto-tracking mode.
|
|
@@ -1856,18 +2243,18 @@ function createNoTrackSubscribe(instance) {
|
|
|
1856
2243
|
* @param adapterState - The adapter state for tracking
|
|
1857
2244
|
* @returns Snapshot function for use with useSyncExternalStore
|
|
1858
2245
|
*/
|
|
1859
|
-
function
|
|
2246
|
+
function autoTrackSnapshot(instance$1, adapterState) {
|
|
1860
2247
|
return () => {
|
|
1861
|
-
const
|
|
1862
|
-
if (hasTrackedData(
|
|
1863
|
-
if (adapterState.
|
|
1864
|
-
invalidateRenderCache(adapterState.
|
|
1865
|
-
commitTrackedGetters(adapterState.
|
|
1866
|
-
adapterState.
|
|
1867
|
-
setActiveTracker(instance, adapterState.
|
|
2248
|
+
const depState = adapterState.dependencyState || (adapterState.dependencyState = createDependencyState());
|
|
2249
|
+
if (hasTrackedData(depState)) capturePaths(depState, instance$1.state);
|
|
2250
|
+
if (adapterState.getterState) {
|
|
2251
|
+
invalidateRenderCache(adapterState.getterState);
|
|
2252
|
+
commitTrackedGetters(adapterState.getterState);
|
|
2253
|
+
adapterState.getterState.isTracking = true;
|
|
2254
|
+
setActiveTracker(instance$1, adapterState.getterState);
|
|
1868
2255
|
}
|
|
1869
|
-
|
|
1870
|
-
return
|
|
2256
|
+
startDependency(depState);
|
|
2257
|
+
return createDependencyProxy(depState, instance$1.state);
|
|
1871
2258
|
};
|
|
1872
2259
|
}
|
|
1873
2260
|
/**
|
|
@@ -1878,10 +2265,10 @@ function createAutoTrackSnapshot(instance, adapterState) {
|
|
|
1878
2265
|
* @param config - Configuration with dependencies function
|
|
1879
2266
|
* @returns Snapshot function for use with useSyncExternalStore
|
|
1880
2267
|
*/
|
|
1881
|
-
function
|
|
2268
|
+
function manualDepsSnapshot(instance$1, adapterState, config) {
|
|
1882
2269
|
return () => {
|
|
1883
|
-
adapterState.manualDepsCache = config.dependencies(instance.state, instance);
|
|
1884
|
-
return instance.state;
|
|
2270
|
+
adapterState.manualDepsCache = config.dependencies(instance$1.state, instance$1);
|
|
2271
|
+
return instance$1.state;
|
|
1885
2272
|
};
|
|
1886
2273
|
}
|
|
1887
2274
|
/**
|
|
@@ -1890,8 +2277,8 @@ function createManualDepsSnapshot(instance, adapterState, config) {
|
|
|
1890
2277
|
* @param instance - The state container instance
|
|
1891
2278
|
* @returns Snapshot function for use with useSyncExternalStore
|
|
1892
2279
|
*/
|
|
1893
|
-
function
|
|
1894
|
-
return () => instance.state;
|
|
2280
|
+
function noTrackSnapshot(instance$1) {
|
|
2281
|
+
return () => instance$1.state;
|
|
1895
2282
|
}
|
|
1896
2283
|
/**
|
|
1897
2284
|
* Initialize adapter state for auto-tracking mode.
|
|
@@ -1899,12 +2286,12 @@ function createNoTrackSnapshot(instance) {
|
|
|
1899
2286
|
* @param instance - The state container instance
|
|
1900
2287
|
* @returns Initialized adapter state
|
|
1901
2288
|
*/
|
|
1902
|
-
function
|
|
2289
|
+
function autoTrackInit(instance$1) {
|
|
1903
2290
|
return {
|
|
1904
|
-
|
|
2291
|
+
dependencyState: null,
|
|
1905
2292
|
manualDepsCache: null,
|
|
1906
|
-
|
|
1907
|
-
proxiedBloc: createBlocProxy(instance)
|
|
2293
|
+
getterState: createGetterState(),
|
|
2294
|
+
proxiedBloc: createBlocProxy(instance$1)
|
|
1908
2295
|
};
|
|
1909
2296
|
}
|
|
1910
2297
|
/**
|
|
@@ -1913,12 +2300,12 @@ function initAutoTrackState(instance) {
|
|
|
1913
2300
|
* @param instance - The state container instance
|
|
1914
2301
|
* @returns Initialized adapter state
|
|
1915
2302
|
*/
|
|
1916
|
-
function
|
|
2303
|
+
function manualDepsInit(instance$1) {
|
|
1917
2304
|
return {
|
|
1918
|
-
|
|
2305
|
+
dependencyState: null,
|
|
1919
2306
|
manualDepsCache: null,
|
|
1920
|
-
|
|
1921
|
-
proxiedBloc: instance
|
|
2307
|
+
getterState: null,
|
|
2308
|
+
proxiedBloc: instance$1
|
|
1922
2309
|
};
|
|
1923
2310
|
}
|
|
1924
2311
|
/**
|
|
@@ -1927,12 +2314,12 @@ function initManualDepsState(instance) {
|
|
|
1927
2314
|
* @param instance - The state container instance
|
|
1928
2315
|
* @returns Initialized adapter state
|
|
1929
2316
|
*/
|
|
1930
|
-
function
|
|
2317
|
+
function noTrackInit(instance$1) {
|
|
1931
2318
|
return {
|
|
1932
|
-
|
|
2319
|
+
dependencyState: null,
|
|
1933
2320
|
manualDepsCache: null,
|
|
1934
|
-
|
|
1935
|
-
proxiedBloc: instance
|
|
2321
|
+
getterState: null,
|
|
2322
|
+
proxiedBloc: instance$1
|
|
1936
2323
|
};
|
|
1937
2324
|
}
|
|
1938
2325
|
/**
|
|
@@ -1942,8 +2329,8 @@ function initNoTrackState(instance) {
|
|
|
1942
2329
|
* @param rawInstance - The raw bloc instance
|
|
1943
2330
|
*/
|
|
1944
2331
|
function disableGetterTracking(adapterState, rawInstance) {
|
|
1945
|
-
if (adapterState.
|
|
1946
|
-
adapterState.
|
|
2332
|
+
if (adapterState.getterState) {
|
|
2333
|
+
adapterState.getterState.isTracking = false;
|
|
1947
2334
|
clearActiveTracker(rawInstance);
|
|
1948
2335
|
}
|
|
1949
2336
|
}
|
|
@@ -2170,5 +2557,328 @@ function blac(options) {
|
|
|
2170
2557
|
}
|
|
2171
2558
|
|
|
2172
2559
|
//#endregion
|
|
2173
|
-
|
|
2560
|
+
//#region src/registry/acquire.ts
|
|
2561
|
+
function acquire(BlocClass, instanceKey, options) {
|
|
2562
|
+
return globalRegistry.acquire(BlocClass, instanceKey, {
|
|
2563
|
+
canCreate: true,
|
|
2564
|
+
countRef: true,
|
|
2565
|
+
props: options?.props
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
//#endregion
|
|
2570
|
+
//#region src/registry/borrow.ts
|
|
2571
|
+
function borrow(BlocClass, instanceKey) {
|
|
2572
|
+
return globalRegistry.borrow(BlocClass, instanceKey);
|
|
2573
|
+
}
|
|
2574
|
+
function borrowSafe(BlocClass, instanceKey) {
|
|
2575
|
+
return globalRegistry.borrowSafe(BlocClass, instanceKey);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
//#endregion
|
|
2579
|
+
//#region src/registry/ensure.ts
|
|
2580
|
+
function ensure(BlocClass, instanceKey) {
|
|
2581
|
+
return globalRegistry.ensure(BlocClass, instanceKey);
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
//#endregion
|
|
2585
|
+
//#region src/registry/release.ts
|
|
2586
|
+
function release(BlocClass, instanceKey, forceDispose = false) {
|
|
2587
|
+
globalRegistry.release(BlocClass, instanceKey, forceDispose);
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
//#endregion
|
|
2591
|
+
//#region src/registry/queries.ts
|
|
2592
|
+
function hasInstance(BlocClass, instanceKey) {
|
|
2593
|
+
return globalRegistry.hasInstance(BlocClass, instanceKey);
|
|
2594
|
+
}
|
|
2595
|
+
function getRefCount(BlocClass, instanceKey) {
|
|
2596
|
+
return globalRegistry.getRefCount(BlocClass, instanceKey);
|
|
2597
|
+
}
|
|
2598
|
+
function getAll(BlocClass) {
|
|
2599
|
+
return globalRegistry.getAll(BlocClass);
|
|
2600
|
+
}
|
|
2601
|
+
function forEach(BlocClass, callback) {
|
|
2602
|
+
globalRegistry.forEach(BlocClass, callback);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
//#endregion
|
|
2606
|
+
//#region src/registry/management.ts
|
|
2607
|
+
function clear(BlocClass) {
|
|
2608
|
+
globalRegistry.clear(BlocClass);
|
|
2609
|
+
}
|
|
2610
|
+
function clearAll() {
|
|
2611
|
+
globalRegistry.clearAll();
|
|
2612
|
+
}
|
|
2613
|
+
function register(BlocClass, isolated = false) {
|
|
2614
|
+
globalRegistry.register(BlocClass, isolated);
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
//#endregion
|
|
2618
|
+
//#region src/registry/config.ts
|
|
2619
|
+
let _registry = globalRegistry;
|
|
2620
|
+
function getRegistry() {
|
|
2621
|
+
return _registry;
|
|
2622
|
+
}
|
|
2623
|
+
function setRegistry(registry) {
|
|
2624
|
+
_registry.clearAll();
|
|
2625
|
+
_registry = registry;
|
|
2626
|
+
}
|
|
2627
|
+
function getStats() {
|
|
2628
|
+
return _registry.getStats();
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
//#endregion
|
|
2632
|
+
//#region src/waitUntil/errors.ts
|
|
2633
|
+
var WaitUntilTimeoutError = class extends Error {
|
|
2634
|
+
constructor(timeout) {
|
|
2635
|
+
super(`waitUntil timed out after ${timeout}ms`);
|
|
2636
|
+
this.name = "WaitUntilTimeoutError";
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
var WaitUntilAbortedError = class extends Error {
|
|
2640
|
+
constructor() {
|
|
2641
|
+
super("waitUntil was aborted");
|
|
2642
|
+
this.name = "WaitUntilAbortedError";
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
var WaitUntilDisposedError = class extends Error {
|
|
2646
|
+
constructor(blocName) {
|
|
2647
|
+
super(`waitUntil failed: ${blocName} was disposed while waiting`);
|
|
2648
|
+
this.name = "WaitUntilDisposedError";
|
|
2649
|
+
}
|
|
2650
|
+
};
|
|
2651
|
+
|
|
2652
|
+
//#endregion
|
|
2653
|
+
//#region src/waitUntil/waitUntil.ts
|
|
2654
|
+
function waitUntil(BlocClass, selectorOrPredicate, predicateOrOptions, maybeOptions) {
|
|
2655
|
+
if (typeof predicateOrOptions !== "function") return waitUntilSimple(BlocClass, selectorOrPredicate, predicateOrOptions);
|
|
2656
|
+
else return waitUntilSelector(BlocClass, selectorOrPredicate, predicateOrOptions, maybeOptions);
|
|
2657
|
+
}
|
|
2658
|
+
function waitUntilSimple(BlocClass, predicate, options) {
|
|
2659
|
+
const { instanceId = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, timeout, signal } = options ?? {};
|
|
2660
|
+
return new Promise((resolve, reject) => {
|
|
2661
|
+
if (signal?.aborted) {
|
|
2662
|
+
reject(new WaitUntilAbortedError());
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
const bloc = ensure(BlocClass, instanceId);
|
|
2666
|
+
if (bloc.isDisposed) {
|
|
2667
|
+
reject(new WaitUntilDisposedError(BlocClass.name));
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
const tracker = createState();
|
|
2671
|
+
const proxiedBloc = createTrackingProxy(bloc, tracker);
|
|
2672
|
+
const externalDepsManager = new DependencyManager();
|
|
2673
|
+
const runPredicate = () => {
|
|
2674
|
+
startTracking(tracker);
|
|
2675
|
+
try {
|
|
2676
|
+
return predicate(proxiedBloc);
|
|
2677
|
+
} finally {
|
|
2678
|
+
const deps = stopTracking(tracker, bloc);
|
|
2679
|
+
for (const dep of tracker.getterState.externalDependencies) deps.add(dep);
|
|
2680
|
+
deps.delete(bloc);
|
|
2681
|
+
externalDepsManager.sync(deps, checkCondition, bloc);
|
|
2682
|
+
}
|
|
2683
|
+
};
|
|
2684
|
+
if (runPredicate()) {
|
|
2685
|
+
resolve(bloc);
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
let unsubscribe = null;
|
|
2689
|
+
let unsubscribeDispose = null;
|
|
2690
|
+
let timeoutId = null;
|
|
2691
|
+
let abortHandler = null;
|
|
2692
|
+
const cleanup = () => {
|
|
2693
|
+
unsubscribe?.();
|
|
2694
|
+
unsubscribeDispose?.();
|
|
2695
|
+
externalDepsManager.cleanup();
|
|
2696
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2697
|
+
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
2698
|
+
};
|
|
2699
|
+
function checkCondition() {
|
|
2700
|
+
try {
|
|
2701
|
+
if (runPredicate()) {
|
|
2702
|
+
cleanup();
|
|
2703
|
+
resolve(bloc);
|
|
2704
|
+
}
|
|
2705
|
+
} catch (error$1) {
|
|
2706
|
+
cleanup();
|
|
2707
|
+
reject(error$1);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
unsubscribe = bloc.subscribe(checkCondition);
|
|
2711
|
+
unsubscribeDispose = getRegistry().on("disposed", (instance$1) => {
|
|
2712
|
+
if (instance$1 === bloc) {
|
|
2713
|
+
cleanup();
|
|
2714
|
+
reject(new WaitUntilDisposedError(BlocClass.name));
|
|
2715
|
+
}
|
|
2716
|
+
});
|
|
2717
|
+
if (timeout !== void 0) timeoutId = setTimeout(() => {
|
|
2718
|
+
cleanup();
|
|
2719
|
+
reject(new WaitUntilTimeoutError(timeout));
|
|
2720
|
+
}, timeout);
|
|
2721
|
+
if (signal) {
|
|
2722
|
+
abortHandler = () => {
|
|
2723
|
+
cleanup();
|
|
2724
|
+
reject(new WaitUntilAbortedError());
|
|
2725
|
+
};
|
|
2726
|
+
signal.addEventListener("abort", abortHandler);
|
|
2727
|
+
}
|
|
2728
|
+
});
|
|
2729
|
+
}
|
|
2730
|
+
function waitUntilSelector(BlocClass, selector, predicate, options) {
|
|
2731
|
+
const { instanceId = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, timeout, signal } = options ?? {};
|
|
2732
|
+
return new Promise((resolve, reject) => {
|
|
2733
|
+
if (signal?.aborted) {
|
|
2734
|
+
reject(new WaitUntilAbortedError());
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
const bloc = ensure(BlocClass, instanceId);
|
|
2738
|
+
if (bloc.isDisposed) {
|
|
2739
|
+
reject(new WaitUntilDisposedError(BlocClass.name));
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
const tracker = createState();
|
|
2743
|
+
const proxiedBloc = createTrackingProxy(bloc, tracker);
|
|
2744
|
+
const externalDepsManager = new DependencyManager();
|
|
2745
|
+
const runSelector = () => {
|
|
2746
|
+
startTracking(tracker);
|
|
2747
|
+
try {
|
|
2748
|
+
const value = selector(proxiedBloc);
|
|
2749
|
+
return {
|
|
2750
|
+
value,
|
|
2751
|
+
matches: predicate(value)
|
|
2752
|
+
};
|
|
2753
|
+
} finally {
|
|
2754
|
+
const deps = stopTracking(tracker, bloc);
|
|
2755
|
+
for (const dep of tracker.getterState.externalDependencies) deps.add(dep);
|
|
2756
|
+
deps.delete(bloc);
|
|
2757
|
+
externalDepsManager.sync(deps, checkCondition, bloc);
|
|
2758
|
+
}
|
|
2759
|
+
};
|
|
2760
|
+
const initial = runSelector();
|
|
2761
|
+
if (initial.matches) {
|
|
2762
|
+
resolve(initial.value);
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
let unsubscribe = null;
|
|
2766
|
+
let unsubscribeDispose = null;
|
|
2767
|
+
let timeoutId = null;
|
|
2768
|
+
let abortHandler = null;
|
|
2769
|
+
const cleanup = () => {
|
|
2770
|
+
unsubscribe?.();
|
|
2771
|
+
unsubscribeDispose?.();
|
|
2772
|
+
externalDepsManager.cleanup();
|
|
2773
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2774
|
+
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
2775
|
+
};
|
|
2776
|
+
function checkCondition() {
|
|
2777
|
+
try {
|
|
2778
|
+
const result = runSelector();
|
|
2779
|
+
if (result.matches) {
|
|
2780
|
+
cleanup();
|
|
2781
|
+
resolve(result.value);
|
|
2782
|
+
}
|
|
2783
|
+
} catch (error$1) {
|
|
2784
|
+
cleanup();
|
|
2785
|
+
reject(error$1);
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
unsubscribe = bloc.subscribe(checkCondition);
|
|
2789
|
+
unsubscribeDispose = getRegistry().on("disposed", (instance$1) => {
|
|
2790
|
+
if (instance$1 === bloc) {
|
|
2791
|
+
cleanup();
|
|
2792
|
+
reject(new WaitUntilDisposedError(BlocClass.name));
|
|
2793
|
+
}
|
|
2794
|
+
});
|
|
2795
|
+
if (timeout !== void 0) timeoutId = setTimeout(() => {
|
|
2796
|
+
cleanup();
|
|
2797
|
+
reject(new WaitUntilTimeoutError(timeout));
|
|
2798
|
+
}, timeout);
|
|
2799
|
+
if (signal) {
|
|
2800
|
+
abortHandler = () => {
|
|
2801
|
+
cleanup();
|
|
2802
|
+
reject(new WaitUntilAbortedError());
|
|
2803
|
+
};
|
|
2804
|
+
signal.addEventListener("abort", abortHandler);
|
|
2805
|
+
}
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
//#endregion
|
|
2810
|
+
//#region src/watch/watch.ts
|
|
2811
|
+
const STOP = Symbol("watch.STOP");
|
|
2812
|
+
const BLOC_REF_MARKER = Symbol("BlocRef");
|
|
2813
|
+
/**
|
|
2814
|
+
* Create a reference to a specific bloc instance.
|
|
2815
|
+
*
|
|
2816
|
+
* @example
|
|
2817
|
+
* ```ts
|
|
2818
|
+
* watch(instance(UserBloc, 'user-123'), (userBloc) => {
|
|
2819
|
+
* console.log(userBloc.state.name);
|
|
2820
|
+
* });
|
|
2821
|
+
* ```
|
|
2822
|
+
*/
|
|
2823
|
+
function instance(BlocClass, instanceId) {
|
|
2824
|
+
return {
|
|
2825
|
+
[BLOC_REF_MARKER]: true,
|
|
2826
|
+
blocClass: BlocClass,
|
|
2827
|
+
instanceId
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
function isBlocRef(input) {
|
|
2831
|
+
return typeof input === "object" && input !== null && BLOC_REF_MARKER in input;
|
|
2832
|
+
}
|
|
2833
|
+
function resolveBloc(input) {
|
|
2834
|
+
if (isBlocRef(input)) return ensure(input.blocClass, input.instanceId);
|
|
2835
|
+
return ensure(input, BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY);
|
|
2836
|
+
}
|
|
2837
|
+
function isArray(input) {
|
|
2838
|
+
return Array.isArray(input);
|
|
2839
|
+
}
|
|
2840
|
+
function watchImpl(blocsOrBloc, callback) {
|
|
2841
|
+
const isSingle = !isArray(blocsOrBloc);
|
|
2842
|
+
const instances = (isSingle ? [blocsOrBloc] : blocsOrBloc).map(resolveBloc);
|
|
2843
|
+
const tracker = createState();
|
|
2844
|
+
const proxiedInstances = instances.map((inst) => createTrackingProxy(inst, tracker));
|
|
2845
|
+
const externalDepsManager = new DependencyManager();
|
|
2846
|
+
let disposed = false;
|
|
2847
|
+
const primarySubscriptions = [];
|
|
2848
|
+
const cleanup = () => {
|
|
2849
|
+
if (disposed) return;
|
|
2850
|
+
disposed = true;
|
|
2851
|
+
primarySubscriptions.forEach((unsub) => unsub());
|
|
2852
|
+
externalDepsManager.cleanup();
|
|
2853
|
+
};
|
|
2854
|
+
const runCallback = () => {
|
|
2855
|
+
if (disposed) return;
|
|
2856
|
+
startTracking(tracker);
|
|
2857
|
+
let result;
|
|
2858
|
+
try {
|
|
2859
|
+
result = callback(isSingle ? proxiedInstances[0] : proxiedInstances);
|
|
2860
|
+
} finally {
|
|
2861
|
+
const externalDeps = /* @__PURE__ */ new Set();
|
|
2862
|
+
for (const inst of instances) {
|
|
2863
|
+
const deps = stopTracking(tracker, inst);
|
|
2864
|
+
for (const dep of deps) externalDeps.add(dep);
|
|
2865
|
+
}
|
|
2866
|
+
for (const dep of tracker.getterState.externalDependencies) externalDeps.add(dep);
|
|
2867
|
+
for (const inst of instances) externalDeps.delete(inst);
|
|
2868
|
+
externalDepsManager.sync(externalDeps, runCallback);
|
|
2869
|
+
}
|
|
2870
|
+
if (result === STOP) cleanup();
|
|
2871
|
+
};
|
|
2872
|
+
const onChange = () => {
|
|
2873
|
+
if (disposed) return;
|
|
2874
|
+
runCallback();
|
|
2875
|
+
};
|
|
2876
|
+
for (const inst of instances) primarySubscriptions.push(inst.subscribe(onChange));
|
|
2877
|
+
runCallback();
|
|
2878
|
+
return cleanup;
|
|
2879
|
+
}
|
|
2880
|
+
const watch = Object.assign(watchImpl, { STOP });
|
|
2881
|
+
|
|
2882
|
+
//#endregion
|
|
2883
|
+
export { BLAC_DEFAULTS, BLAC_ERROR_PREFIX, BLAC_ID_PATTERNS, BLAC_STATIC_PROPS, Cubit, DependencyManager, ExternalDepsManager, LogLevel, PluginManager, STATELESS_MARKER, StateContainer, StateContainerRegistry, StatelessCubit, StatelessVertex, TrackedContext, Vertex, WaitUntilAbortedError, WaitUntilDisposedError, WaitUntilTimeoutError, __resetIdCounters, acquire, autoTrackInit, autoTrackSnapshot, autoTrackSubscribe, blac, borrow, borrowSafe, capturePaths, clear, clearActiveTracker, clearAll, clearExternalDependencies, commitTrackedGetters, configureBlac, configureLogger, createArrayProxy, createBlocProxy, createDependencyProxy, createDependencyState, createForTarget, createGetterState, createIdGenerator, createInternal, createLogger, createPluginManager, createProxyState, createTrackedContext, createTrackingProxy, createState as createTrackingProxyState, debug, disableGetterTracking, ensure, error, forEach, generateId, generateIsolatedKey, generateSimpleId, getActiveTracker, getAll, getBlacConfig, getDescriptor, getGetterExecutionContext, getPluginManager, getRefCount, getRegistry, getStaticProp, getStats, getValueAtPath, globalRegistry, hasDependencyChanges, hasGetterChanges, hasInitHook, hasInstance, hasTrackedData, hasChanges as hasTrackingProxyChanges, info, instance, invalidateRenderCache, isDevMode, isExcludedFromDevTools, isGetter, isIsolatedClass, isIsolatedKey, isKeepAliveClass, isProxyable, isStatelessClass, manualDepsInit, manualDepsSnapshot, manualDepsSubscribe, noTrackInit, noTrackSnapshot, noTrackSubscribe, parsePath, register, release, resetBlacConfig, resetGetterState, setActiveTracker, setRegistry, shallowEqual, startDependency, startProxy, startTracking as startTrackingProxy, stopProxy, stopTracking as stopTrackingProxy, tracked, waitUntil, warn, watch };
|
|
2174
2884
|
//# sourceMappingURL=index.js.map
|