@blac/core 2.0.0-rc.13 → 2.0.0-rc.15

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 CHANGED
@@ -276,6 +276,8 @@ var PluginManager = class {
276
276
  };
277
277
  /**
278
278
  * Create a plugin manager instance
279
+ * @param registry - The StateContainerRegistry to monitor for lifecycle events
280
+ * @returns A new PluginManager instance
279
281
  */
280
282
  function createPluginManager(registry) {
281
283
  return new PluginManager(registry);
@@ -527,22 +529,28 @@ function getStaticProp(Type, propName, defaultValue) {
527
529
  return Type[propName] ?? defaultValue;
528
530
  }
529
531
  /**
530
- * Check if a class is marked as isolated
531
- * Isolated classes create separate instances per component
532
+ * Check if a class is marked as isolated.
533
+ * Isolated classes create separate instances per component.
534
+ * @param Type - The class constructor to check
535
+ * @returns true if the class has `static isolated = true`
532
536
  */
533
537
  function isIsolatedClass(Type) {
534
538
  return getStaticProp(Type, BLAC_STATIC_PROPS.ISOLATED) === true;
535
539
  }
536
540
  /**
537
- * Check if a class is marked as keepAlive
538
- * KeepAlive classes are never auto-disposed when ref count reaches 0
541
+ * Check if a class is marked as keepAlive.
542
+ * KeepAlive classes are never auto-disposed when ref count reaches 0.
543
+ * @param Type - The class constructor to check
544
+ * @returns true if the class has `static keepAlive = true`
539
545
  */
540
546
  function isKeepAliveClass(Type) {
541
547
  return getStaticProp(Type, BLAC_STATIC_PROPS.KEEP_ALIVE) === true;
542
548
  }
543
549
  /**
544
- * Check if a class should be excluded from DevTools
545
- * Used to prevent infinite loops when DevTools tracks itself
550
+ * Check if a class should be excluded from DevTools.
551
+ * Used to prevent infinite loops when DevTools tracks itself.
552
+ * @param Type - The class constructor to check
553
+ * @returns true if the class has `static __excludeFromDevTools = true`
546
554
  */
547
555
  function isExcludedFromDevTools(Type) {
548
556
  return getStaticProp(Type, BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS) === true;
@@ -604,97 +612,111 @@ var StateContainerRegistry = class {
604
612
  getInstancesMap(Type) {
605
613
  return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
606
614
  }
615
+ trackExecutionContext(instance) {
616
+ const context = getGetterExecutionContext();
617
+ if (context.tracker && context.currentBloc && context.currentBloc !== instance) context.tracker.externalDependencies.add(instance);
618
+ }
607
619
  /**
608
- * Resolve an instance with ref counting (ownership semantics)
620
+ * Resolve an instance with ref counting (ownership semantics).
621
+ * Creates a new instance if one doesn't exist, or returns existing and increments ref count.
622
+ * @param Type - The StateContainer class constructor
623
+ * @param instanceKey - Instance key (defaults to 'default')
624
+ * @param options - Resolution options
625
+ * @param options.canCreate - Whether to create new instance if not found (default: true)
626
+ * @param options.countRef - Whether to increment ref count (default: true)
627
+ * @param options.props - Props to pass to constructor if creating new instance
628
+ * @param options.trackExecutionContext - Whether to track cross-bloc dependency (default: false)
629
+ * @returns The state container instance
609
630
  */
610
- resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
631
+ resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, options = {}) {
632
+ const { canCreate = true, countRef = true, props, trackExecutionContext = false } = options;
611
633
  const registryConfig = this.typeConfigs.get(Type.name);
612
634
  const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
613
635
  const config = { instanceId: instanceKey };
636
+ if (isolated && !canCreate) throw new Error(`${BLAC_ERROR_PREFIX} Cannot get isolated instance "${instanceKey}" of ${Type.name} when creation is disabled.`);
614
637
  if (isolated) {
615
- const instance$1 = new Type(constructorArgs);
638
+ const instance$1 = new Type(props);
616
639
  instance$1.initConfig(config);
617
640
  this.registerType(Type);
641
+ if (trackExecutionContext) this.trackExecutionContext(instance$1);
618
642
  return instance$1;
619
643
  }
620
644
  const instances = this.ensureInstancesMap(Type);
621
645
  const entry = instances.get(instanceKey);
646
+ if (entry && (countRef || entry.refCount === 0)) entry.refCount++;
622
647
  if (entry) {
623
- entry.refCount++;
648
+ if (trackExecutionContext) this.trackExecutionContext(entry.instance);
624
649
  return entry.instance;
625
650
  }
626
- const instance = new Type(constructorArgs);
651
+ if (!canCreate) throw new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found and creation is disabled.`);
652
+ const instance = new Type(props);
627
653
  instance.initConfig(config);
628
654
  instances.set(instanceKey, {
629
655
  instance,
630
656
  refCount: 1
631
657
  });
632
658
  this.registerType(Type);
659
+ if (trackExecutionContext) this.trackExecutionContext(instance);
633
660
  return instance;
634
661
  }
635
662
  /**
636
- * Get an existing instance without ref counting (borrowing semantics)
663
+ * Get an existing instance without incrementing ref count (borrowing semantics).
664
+ * Tracks cross-bloc dependency for reactive updates.
665
+ * @param Type - The StateContainer class constructor
666
+ * @param instanceKey - Instance key (defaults to 'default')
667
+ * @returns The state container instance
637
668
  * @throws Error if instance doesn't exist
638
669
  */
639
670
  get(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
640
- const entry = this.ensureInstancesMap(Type).get(instanceKey);
641
- if (!entry) throw new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found.\nUse .resolve() to create and claim ownership, or .getSafe() for conditional access.`);
642
- const context = getGetterExecutionContext();
643
- if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
644
- return entry.instance;
671
+ return this.resolve(Type, instanceKey, {
672
+ canCreate: false,
673
+ trackExecutionContext: true
674
+ });
645
675
  }
646
676
  /**
647
- * Safely get an existing instance (borrowing semantics with error handling)
648
- * Returns discriminated union for type-safe conditional access
677
+ * Safely get an existing instance (borrowing semantics with error handling).
678
+ * Returns discriminated union for type-safe conditional access.
679
+ * @param Type - The StateContainer class constructor
680
+ * @param instanceKey - Instance key (defaults to 'default')
681
+ * @returns Discriminated union with either the instance or an error
649
682
  */
650
683
  getSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
651
- const entry = this.ensureInstancesMap(Type).get(instanceKey);
652
- if (!entry) return {
653
- error: /* @__PURE__ */ new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found`),
654
- instance: null
655
- };
656
- const context = getGetterExecutionContext();
657
- if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
658
- return {
659
- error: null,
660
- instance: entry.instance
661
- };
684
+ try {
685
+ return {
686
+ error: null,
687
+ instance: this.get(Type, instanceKey)
688
+ };
689
+ } catch (error$1) {
690
+ return {
691
+ error: error$1,
692
+ instance: null
693
+ };
694
+ }
662
695
  }
663
696
  /**
664
- * Connect to an instance with borrowing semantics (for B2B communication)
697
+ * Connect to an instance with borrowing semantics (for B2B communication).
665
698
  * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
666
699
  * Tracks cross-bloc dependency for reactive updates.
667
700
  *
668
701
  * Use this in bloc-to-bloc communication when you need to ensure an instance exists
669
702
  * but don't want to claim ownership (no ref count increment).
670
- *
671
- * @param Type - The bloc class constructor
672
- * @param instanceKey - Optional instance key (defaults to 'default')
673
- * @param constructorArgs - Constructor arguments (only used if creating new instance)
674
- * @returns The bloc instance
703
+ * @param Type - The StateContainer class constructor
704
+ * @param instanceKey - Instance key (defaults to 'default')
705
+ * @returns The state container instance
675
706
  */
676
- connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
677
- const registryConfig = this.typeConfigs.get(Type.name);
678
- if (isIsolatedClass(Type) || registryConfig?.isolated === true) throw new Error(`${BLAC_ERROR_PREFIX} Cannot use .connect() with isolated bloc "${Type.name}".\nIsolated blocs are component-scoped. Use .resolve() in components instead.`);
679
- const instances = this.ensureInstancesMap(Type);
680
- let entry = instances.get(instanceKey);
681
- if (!entry) {
682
- const instance = new Type(constructorArgs);
683
- const config = { instanceId: instanceKey };
684
- instance.initConfig(config);
685
- entry = {
686
- instance,
687
- refCount: 1
688
- };
689
- instances.set(instanceKey, entry);
690
- this.registerType(Type);
691
- }
692
- const context = getGetterExecutionContext();
693
- if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
694
- return entry.instance;
707
+ connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
708
+ return this.resolve(Type, instanceKey, {
709
+ canCreate: true,
710
+ countRef: false,
711
+ trackExecutionContext: true
712
+ });
695
713
  }
696
714
  /**
697
- * Release a reference to an instance
715
+ * Release a reference to an instance.
716
+ * Decrements ref count and disposes when it reaches 0 (unless keepAlive).
717
+ * @param Type - The StateContainer class constructor
718
+ * @param instanceKey - Instance key (defaults to 'default')
719
+ * @param forceDispose - Force immediate disposal regardless of ref count
698
720
  */
699
721
  release(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, forceDispose = false) {
700
722
  const instances = this.ensureInstancesMap(Type);
@@ -713,7 +735,9 @@ var StateContainerRegistry = class {
713
735
  }
714
736
  }
715
737
  /**
716
- * Get all instances of a specific type
738
+ * Get all instances of a specific type.
739
+ * @param Type - The StateContainer class constructor
740
+ * @returns Array of all instances
717
741
  */
718
742
  getAll(Type) {
719
743
  const instances = this.ensureInstancesMap(Type);
@@ -722,7 +746,10 @@ var StateContainerRegistry = class {
722
746
  return result;
723
747
  }
724
748
  /**
725
- * Safely iterate over all instances of a type
749
+ * Safely iterate over all instances of a type.
750
+ * Skips disposed instances and catches callback errors.
751
+ * @param Type - The StateContainer class constructor
752
+ * @param callback - Function to call for each instance
726
753
  */
727
754
  forEach(Type, callback) {
728
755
  const instances = this.ensureInstancesMap(Type);
@@ -736,7 +763,8 @@ var StateContainerRegistry = class {
736
763
  }
737
764
  }
738
765
  /**
739
- * Clear all instances of a specific type
766
+ * Clear all instances of a specific type (disposes them).
767
+ * @param Type - The StateContainer class constructor
740
768
  */
741
769
  clear(Type) {
742
770
  const instances = this.ensureInstancesMap(Type);
@@ -744,13 +772,19 @@ var StateContainerRegistry = class {
744
772
  instances.clear();
745
773
  }
746
774
  /**
747
- * Get reference count for an instance
775
+ * Get reference count for an instance.
776
+ * @param Type - The StateContainer class constructor
777
+ * @param instanceKey - Instance key (defaults to 'default')
778
+ * @returns Current ref count (0 if instance doesn't exist)
748
779
  */
749
780
  getRefCount(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
750
781
  return this.ensureInstancesMap(Type).get(instanceKey)?.refCount ?? 0;
751
782
  }
752
783
  /**
753
- * Check if an instance exists
784
+ * Check if an instance exists.
785
+ * @param Type - The StateContainer class constructor
786
+ * @param instanceKey - Instance key (defaults to 'default')
787
+ * @returns true if instance exists
754
788
  */
755
789
  hasInstance(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
756
790
  return this.ensureInstancesMap(Type).has(instanceKey);
@@ -767,7 +801,8 @@ var StateContainerRegistry = class {
767
801
  this.typeConfigs.clear();
768
802
  }
769
803
  /**
770
- * Get registry statistics (for debugging)
804
+ * Get registry statistics for debugging.
805
+ * @returns Object with registeredTypes, totalInstances, and typeBreakdown
771
806
  */
772
807
  getStats() {
773
808
  const typeBreakdown = {};
@@ -785,7 +820,8 @@ var StateContainerRegistry = class {
785
820
  };
786
821
  }
787
822
  /**
788
- * Get all registered types (for plugin system)
823
+ * Get all registered types (for plugin system).
824
+ * @returns Array of all registered StateContainer class constructors
789
825
  */
790
826
  getTypes() {
791
827
  return Array.from(this.types);
@@ -798,7 +834,9 @@ var StateContainerRegistry = class {
798
834
  */
799
835
  on(event, listener) {
800
836
  if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
801
- this.listeners.get(event).add(listener);
837
+ const instance = this.listeners.get(event);
838
+ if (!instance) throw new Error(`${BLAC_ERROR_PREFIX} Failed to register listener for event '${event}'`);
839
+ instance.add(listener);
802
840
  return () => {
803
841
  this.listeners.get(event)?.delete(listener);
804
842
  };
@@ -845,12 +883,12 @@ function getPluginManager() {
845
883
  *
846
884
  * @example
847
885
  * ```ts
848
- * class CounterBloc extends StateContainer<number> {
886
+ * class CounterBloc extends StateContainer<{ count: number }> {
849
887
  * constructor() {
850
- * super(0);
888
+ * super({ count: 0 });
851
889
  * }
852
890
  * increment() {
853
- * this.emit(this.state + 1);
891
+ * this.emit({ count: this.state.count + 1 });
854
892
  * }
855
893
  * }
856
894
  * ```
@@ -890,11 +928,11 @@ var StateContainer = class StateContainer {
890
928
  * @template S - Optional state type override for generic StateContainers
891
929
  * @template T - The StateContainer type (inferred from class)
892
930
  * @param instanceKey - Optional instance key (defaults to 'default')
893
- * @param constructorArgs - Arguments to pass to constructor if creating new instance
931
+ * @param options - Resolution options (canCreate, countRef, props, trackExecutionContext)
894
932
  * @returns The state container instance
895
933
  */
896
- static resolve(instanceKey, constructorArgs) {
897
- return StateContainer._registry.resolve(this, instanceKey, constructorArgs);
934
+ static resolve(instanceKey, options) {
935
+ return StateContainer._registry.resolve(this, instanceKey, options);
898
936
  }
899
937
  /**
900
938
  * Get an existing instance without incrementing ref count (borrowing semantics).
@@ -920,14 +958,14 @@ var StateContainer = class StateContainer {
920
958
  /**
921
959
  * Connect to an instance for bloc-to-bloc communication (borrowing semantics).
922
960
  * Gets or creates instance without incrementing ref count.
961
+ * Tracks cross-bloc dependency for reactive updates.
923
962
  * @template S - Optional state type override for generic StateContainers
924
963
  * @template T - The StateContainer type (inferred from class)
925
964
  * @param instanceKey - Optional instance key (defaults to 'default')
926
- * @param constructorArgs - Arguments to pass to constructor if creating new instance
927
965
  * @returns The state container instance
928
966
  */
929
- static connect(instanceKey, constructorArgs) {
930
- return StateContainer._registry.connect(this, instanceKey, constructorArgs);
967
+ static connect(instanceKey) {
968
+ return StateContainer._registry.connect(this, instanceKey);
931
969
  }
932
970
  /**
933
971
  * Release a reference to an instance.