@blac/core 2.0.0-rc.15 → 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.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 typeClass.getAll();
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 createGetterTracker() {
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 resetGetterTracker(tracker) {
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.resolve(MyBloc);
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
- * Resolve an instance with ref counting (ownership semantics).
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 - Resolution 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
- resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, options = {}) {
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$1 = new Type(props);
638
- instance$1.initConfig(config);
661
+ const instance$2 = new Type(props);
662
+ instance$2.initConfig(config);
639
663
  this.registerType(Type);
640
- if (trackExecutionContext) this.trackExecutionContext(instance$1);
641
- return instance$1;
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
- * Get an existing instance without incrementing ref count (borrowing semantics).
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
- get(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
670
- return this.resolve(Type, instanceKey, {
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 get an existing instance (borrowing semantics with error handling).
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
- getSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
706
+ borrowSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
683
707
  try {
684
708
  return {
685
709
  error: null,
686
- instance: this.get(Type, instanceKey)
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
- * Connect to an instance with borrowing semantics (for B2B communication).
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
- connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
707
- return this.resolve(Type, instanceKey, {
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._registry = globalRegistry;
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
- StateContainer._registry.emit("created", this);
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
- StateContainer._registry.emit("disposed", this);
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
- StateContainer._registry.emit("stateChanged", this, previousState, newState, stackTrace);
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 {
@@ -1237,33 +1133,70 @@ var Cubit = class extends StateContainer {
1237
1133
  //#endregion
1238
1134
  //#region src/core/Vertex.ts
1239
1135
  /**
1240
- * Event-driven state container that processes events to update state.
1241
- * Use with event handlers registered via the on() method.
1136
+ * Event-driven state container using discriminated union events.
1137
+ * Extends StateContainer with event-based state transitions and
1138
+ * compile-time exhaustive checking for event handlers.
1242
1139
  *
1243
- * @template S - State type
1244
- * @template E - Event type (extends BaseEvent)
1140
+ * @template S - State type (must be an object)
1141
+ * @template E - Event union type (discriminated by 'type' property)
1245
1142
  * @template P - Props type (optional)
1143
+ *
1144
+ * @example
1145
+ * ```typescript
1146
+ * type CounterEvent =
1147
+ * | { type: 'increment'; amount: number }
1148
+ * | { type: 'decrement' }
1149
+ * | { type: 'reset' };
1150
+ *
1151
+ * class CounterVertex extends Vertex<{ count: number }, CounterEvent> {
1152
+ * constructor() {
1153
+ * super({ count: 0 });
1154
+ * this.createHandlers({
1155
+ * increment: (event, emit) => {
1156
+ * emit({ count: this.state.count + event.amount });
1157
+ * },
1158
+ * decrement: (_, emit) => {
1159
+ * emit({ count: this.state.count - 1 });
1160
+ * },
1161
+ * reset: (_, emit) => {
1162
+ * emit({ count: 0 });
1163
+ * },
1164
+ * });
1165
+ * }
1166
+ *
1167
+ * increment = (amount = 1) => this.add({ type: 'increment', amount });
1168
+ * }
1169
+ * ```
1246
1170
  */
1247
1171
  var Vertex = class extends StateContainer {
1248
1172
  constructor(initialState) {
1249
1173
  super(initialState);
1250
- this.eventHandlers = /* @__PURE__ */ new Map();
1174
+ this.handlers = /* @__PURE__ */ new Map();
1251
1175
  this.isProcessing = false;
1252
1176
  this.eventQueue = [];
1253
- this.on = (EventClass, handler) => {
1254
- const className = EventClass.name;
1255
- const registrations = this.eventHandlers.get(className) || [];
1256
- registrations.push({
1257
- eventClass: EventClass,
1258
- handler
1259
- });
1260
- this.eventHandlers.set(className, registrations);
1261
- };
1262
1177
  this.add = (event) => {
1263
- StateContainer._registry.emit("eventAdded", this, event);
1264
- this.processEvent(event);
1178
+ const enrichedEvent = this.enrichEvent(event);
1179
+ globalRegistry.emit("eventAdded", this, enrichedEvent);
1180
+ this.processEvent(enrichedEvent);
1181
+ };
1182
+ }
1183
+ /**
1184
+ * Register all event handlers with exhaustive type checking.
1185
+ * TypeScript will error if any event type from the union is missing.
1186
+ *
1187
+ * @param handlers - Map of event type to handler function
1188
+ */
1189
+ createHandlers(handlers) {
1190
+ for (const [eventType, handler] of Object.entries(handlers)) this.handlers.set(eventType, handler);
1191
+ }
1192
+ /** @internal */
1193
+ enrichEvent(event) {
1194
+ return {
1195
+ ...event,
1196
+ timestamp: Date.now()
1265
1197
  };
1266
1198
  }
1199
+ /** @internal */
1267
1200
  processEvent(event) {
1268
1201
  if (this.isProcessing) {
1269
1202
  this.eventQueue.push(event);
@@ -1280,30 +1213,248 @@ var Vertex = class extends StateContainer {
1280
1213
  this.isProcessing = false;
1281
1214
  }
1282
1215
  }
1216
+ /** @internal */
1283
1217
  handleEvent(event) {
1284
- const className = event.constructor.name;
1285
- const registrations = this.eventHandlers.get(className);
1286
- if (!registrations || registrations.length === 0) {
1287
- if (this.debug) console.warn(`No handler registered for event: ${className}`);
1218
+ const handler = this.handlers.get(event.type);
1219
+ if (!handler) {
1220
+ if (this.debug) console.warn(`No handler registered for event type: ${event.type}`);
1288
1221
  return;
1289
1222
  }
1290
1223
  const emitFn = (state) => {
1291
1224
  this.emit(state);
1292
1225
  };
1293
- for (const registration of registrations) try {
1294
- registration.handler(event, emitFn);
1226
+ try {
1227
+ handler(event, emitFn);
1228
+ } catch (error$1) {
1229
+ if (this.debug) console.error(`Error handling event ${event.type}:`, error$1);
1230
+ this.onEventError(event, error$1);
1231
+ }
1232
+ }
1233
+ /**
1234
+ * Called when an error occurs during event processing.
1235
+ * Override to implement custom error handling.
1236
+ *
1237
+ * @param _event - The event that caused the error
1238
+ * @param _error - The error that occurred
1239
+ */
1240
+ onEventError(_event, _error) {}
1241
+ };
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);
1295
1431
  } catch (error$1) {
1296
- if (this.debug) console.error(`Error handling event ${className}:`, error$1);
1432
+ if (this.debug) console.error(`Error handling event ${event.type}:`, error$1);
1297
1433
  this.onEventError(event, error$1);
1298
1434
  }
1299
1435
  }
1300
1436
  /**
1301
- * Handle errors that occur during event processing.
1302
- * Override this method to implement custom error handling.
1437
+ * Called when an error occurs during event processing.
1438
+ * Override to implement custom error handling.
1439
+ *
1303
1440
  * @param _event - The event that caused the error
1304
1441
  * @param _error - The error that occurred
1305
1442
  */
1306
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
+ }
1307
1458
  };
1308
1459
 
1309
1460
  //#endregion
@@ -1353,7 +1504,7 @@ function isProxyable(value) {
1353
1504
  *
1354
1505
  * @internal
1355
1506
  */
1356
- function createProxyTrackerState() {
1507
+ function createProxyState() {
1357
1508
  return {
1358
1509
  trackedPaths: /* @__PURE__ */ new Set(),
1359
1510
  isTracking: false,
@@ -1371,7 +1522,7 @@ function createProxyTrackerState() {
1371
1522
  *
1372
1523
  * @internal
1373
1524
  */
1374
- function startProxyTracking(state) {
1525
+ function startProxy(state) {
1375
1526
  state.isTracking = true;
1376
1527
  state.trackedPaths.clear();
1377
1528
  }
@@ -1382,7 +1533,7 @@ function startProxyTracking(state) {
1382
1533
  *
1383
1534
  * @internal
1384
1535
  */
1385
- function stopProxyTracking(state) {
1536
+ function stopProxy(state) {
1386
1537
  state.isTracking = false;
1387
1538
  return new Set(state.trackedPaths);
1388
1539
  }
@@ -1421,7 +1572,7 @@ function createArrayProxy(state, target, path, depth = 0) {
1421
1572
  if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
1422
1573
  else fullPath = path ? `${path}.${prop}` : prop;
1423
1574
  } else return value;
1424
- if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
1575
+ if (isProxyable(value)) return createInternal(state, value, fullPath, depth + 1);
1425
1576
  if (state.isTracking) state.trackedPaths.add(fullPath);
1426
1577
  return value;
1427
1578
  } });
@@ -1442,7 +1593,7 @@ function createArrayProxy(state, target, path, depth = 0) {
1442
1593
  *
1443
1594
  * @internal
1444
1595
  */
1445
- function createProxyInternal(state, target, path = "", depth = 0) {
1596
+ function createInternal(state, target, path = "", depth = 0) {
1446
1597
  if (!state.isTracking || !isProxyable(target)) return target;
1447
1598
  if (depth >= state.maxDepth) return target;
1448
1599
  if (state.proxyCache.has(target)) return state.proxyCache.get(target);
@@ -1463,7 +1614,7 @@ function createProxyInternal(state, target, path = "", depth = 0) {
1463
1614
  if (typeof prop === "string" && state.isTracking) {
1464
1615
  if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
1465
1616
  }
1466
- if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
1617
+ if (isProxyable(value)) return createInternal(state, value, fullPath, depth + 1);
1467
1618
  return value;
1468
1619
  },
1469
1620
  has: (obj, prop) => {
@@ -1491,11 +1642,11 @@ function createProxyInternal(state, target, path = "", depth = 0) {
1491
1642
  *
1492
1643
  * @internal
1493
1644
  */
1494
- function createProxyForTarget(state, target) {
1645
+ function createForTarget(state, target) {
1495
1646
  if (state.lastProxiedState === target && state.lastProxy) return state.lastProxy;
1496
1647
  state.proxyCache = /* @__PURE__ */ new WeakMap();
1497
1648
  state.boundFunctionsCache = null;
1498
- const proxy = createProxyInternal(state, target, "", 0);
1649
+ const proxy = createInternal(state, target, "", 0);
1499
1650
  state.lastProxiedState = target;
1500
1651
  state.lastProxy = proxy;
1501
1652
  return proxy;
@@ -1636,9 +1787,9 @@ function optimizeTrackedPaths(paths) {
1636
1787
  /**
1637
1788
  * @internal
1638
1789
  */
1639
- function createTrackerState() {
1790
+ function createDependencyState() {
1640
1791
  return {
1641
- proxyTrackerState: createProxyTrackerState(),
1792
+ proxyState: createProxyState(),
1642
1793
  previousRenderPaths: /* @__PURE__ */ new Set(),
1643
1794
  currentRenderPaths: /* @__PURE__ */ new Set(),
1644
1795
  pathCache: /* @__PURE__ */ new Map(),
@@ -1649,21 +1800,21 @@ function createTrackerState() {
1649
1800
  /**
1650
1801
  * @internal
1651
1802
  */
1652
- function startTracking(tracker) {
1653
- startProxyTracking(tracker.proxyTrackerState);
1803
+ function startDependency(tracker) {
1804
+ startProxy(tracker.proxyState);
1654
1805
  }
1655
1806
  /**
1656
1807
  * @internal
1657
1808
  */
1658
- function createProxy(tracker, state) {
1659
- return createProxyForTarget(tracker.proxyTrackerState, state);
1809
+ function createDependencyProxy(tracker, state) {
1810
+ return createForTarget(tracker.proxyState, state);
1660
1811
  }
1661
1812
  /**
1662
1813
  * @internal
1663
1814
  */
1664
- function captureTrackedPaths(tracker, state) {
1815
+ function capturePaths(tracker, state) {
1665
1816
  tracker.previousRenderPaths = tracker.currentRenderPaths;
1666
- tracker.currentRenderPaths = optimizeTrackedPaths(stopProxyTracking(tracker.proxyTrackerState));
1817
+ tracker.currentRenderPaths = optimizeTrackedPaths(stopProxy(tracker.proxyState));
1667
1818
  if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
1668
1819
  const trackedPathsUnion = new Set(tracker.previousRenderPaths);
1669
1820
  for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
@@ -1684,7 +1835,7 @@ function captureTrackedPaths(tracker, state) {
1684
1835
  /**
1685
1836
  * @internal
1686
1837
  */
1687
- function hasChanges(tracker, state) {
1838
+ function hasDependencyChanges(tracker, state) {
1688
1839
  if (tracker.pathCache.size === 0) return true;
1689
1840
  tracker.lastCheckedValues.clear();
1690
1841
  for (const [path, info$1] of tracker.pathCache.entries()) {
@@ -1702,7 +1853,294 @@ function hasChanges(tracker, state) {
1702
1853
  * @internal
1703
1854
  */
1704
1855
  function hasTrackedData(tracker) {
1705
- return tracker.proxyTrackerState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
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();
1706
2144
  }
1707
2145
 
1708
2146
  //#endregion
@@ -1711,16 +2149,13 @@ function hasTrackedData(tracker) {
1711
2149
  * Manages subscriptions to external bloc dependencies for getter tracking.
1712
2150
  * When a getter accesses another bloc's state, this manager ensures
1713
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.
1714
2155
  */
1715
- var ExternalDependencyManager = class {
2156
+ var ExternalDepsManager = class {
1716
2157
  constructor() {
1717
- this.subscriptions = [];
1718
- this.previousDeps = /* @__PURE__ */ new Set();
1719
- }
1720
- areDependenciesEqual(oldDeps, newDeps) {
1721
- if (oldDeps.size !== newDeps.size) return false;
1722
- for (const dep of newDeps) if (!oldDeps.has(dep)) return false;
1723
- return true;
2158
+ this.manager = new DependencyManager();
1724
2159
  }
1725
2160
  /**
1726
2161
  * Update subscriptions to external bloc dependencies.
@@ -1730,32 +2165,22 @@ var ExternalDependencyManager = class {
1730
2165
  * @param onGetterChange - Callback to invoke when external dependency changes
1731
2166
  * @returns true if subscriptions were updated, false if unchanged
1732
2167
  */
1733
- updateSubscriptions(getterTracker, rawInstance, onGetterChange) {
1734
- if (!getterTracker?.externalDependencies) return false;
1735
- const currentDeps = getterTracker.externalDependencies;
1736
- if (this.areDependenciesEqual(this.previousDeps, currentDeps)) {
1737
- clearExternalDependencies(getterTracker);
1738
- return false;
1739
- }
1740
- this.cleanup();
1741
- for (const externalBloc of currentDeps) {
1742
- if (externalBloc === rawInstance) continue;
1743
- const unsub = externalBloc.subscribe(() => {
1744
- invalidateRenderCache(getterTracker);
1745
- if (hasGetterChanges(rawInstance, getterTracker)) onGetterChange();
1746
- });
1747
- this.subscriptions.push(unsub);
1748
- }
1749
- this.previousDeps = new Set(currentDeps);
1750
- clearExternalDependencies(getterTracker);
1751
- 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;
1752
2178
  }
1753
2179
  /**
1754
2180
  * Clean up all active subscriptions
1755
2181
  */
1756
2182
  cleanup() {
1757
- this.subscriptions.forEach((unsub) => unsub());
1758
- this.subscriptions = [];
2183
+ this.manager.cleanup();
1759
2184
  }
1760
2185
  };
1761
2186
  /**
@@ -1765,21 +2190,21 @@ var ExternalDependencyManager = class {
1765
2190
  * @param adapterState - The adapter state for tracking
1766
2191
  * @returns Subscribe function for use with useSyncExternalStore
1767
2192
  */
1768
- function createAutoTrackSubscribe(instance, adapterState) {
2193
+ function autoTrackSubscribe(instance$1, adapterState) {
1769
2194
  return (callback) => {
1770
- return instance.subscribe(() => {
1771
- const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
1772
- const hasStateDeps = tracker.pathCache && tracker.pathCache.size > 0;
1773
- const hasGetterDeps = adapterState.getterTracker && adapterState.getterTracker.trackedGetters.size > 0;
1774
- 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";
1775
2200
  if (!hasStateDeps && !hasGetterDeps && !isPrimitiveState) return;
1776
- let stateChanged = hasChanges(tracker, instance.state);
2201
+ let stateChanged = hasDependencyChanges(depState, instance$1.state);
1777
2202
  if (!hasStateDeps && hasGetterDeps) stateChanged = false;
1778
2203
  if (stateChanged) {
1779
2204
  callback();
1780
2205
  return;
1781
2206
  }
1782
- if (hasGetterChanges(instance, adapterState.getterTracker)) callback();
2207
+ if (hasGetterChanges(instance$1, adapterState.getterState)) callback();
1783
2208
  });
1784
2209
  };
1785
2210
  }
@@ -1791,10 +2216,10 @@ function createAutoTrackSubscribe(instance, adapterState) {
1791
2216
  * @param config - Configuration with dependencies function
1792
2217
  * @returns Subscribe function for use with useSyncExternalStore
1793
2218
  */
1794
- function createManualDepsSubscribe(instance, adapterState, config) {
2219
+ function manualDepsSubscribe(instance$1, adapterState, config) {
1795
2220
  return (callback) => {
1796
- return instance.subscribe(() => {
1797
- const newDeps = config.dependencies(instance.state, instance);
2221
+ return instance$1.subscribe(() => {
2222
+ const newDeps = config.dependencies(instance$1.state, instance$1);
1798
2223
  if (!adapterState.manualDepsCache || !shallowEqual(adapterState.manualDepsCache, newDeps)) {
1799
2224
  adapterState.manualDepsCache = newDeps;
1800
2225
  callback();
@@ -1808,8 +2233,8 @@ function createManualDepsSubscribe(instance, adapterState, config) {
1808
2233
  * @param instance - The state container instance
1809
2234
  * @returns Subscribe function for use with useSyncExternalStore
1810
2235
  */
1811
- function createNoTrackSubscribe(instance) {
1812
- return (callback) => instance.subscribe(callback);
2236
+ function noTrackSubscribe(instance$1) {
2237
+ return (callback) => instance$1.subscribe(callback);
1813
2238
  }
1814
2239
  /**
1815
2240
  * Create a snapshot function for auto-tracking mode.
@@ -1818,18 +2243,18 @@ function createNoTrackSubscribe(instance) {
1818
2243
  * @param adapterState - The adapter state for tracking
1819
2244
  * @returns Snapshot function for use with useSyncExternalStore
1820
2245
  */
1821
- function createAutoTrackSnapshot(instance, adapterState) {
2246
+ function autoTrackSnapshot(instance$1, adapterState) {
1822
2247
  return () => {
1823
- const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
1824
- if (hasTrackedData(tracker)) captureTrackedPaths(tracker, instance.state);
1825
- if (adapterState.getterTracker) {
1826
- invalidateRenderCache(adapterState.getterTracker);
1827
- commitTrackedGetters(adapterState.getterTracker);
1828
- adapterState.getterTracker.isTracking = true;
1829
- setActiveTracker(instance, adapterState.getterTracker);
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);
1830
2255
  }
1831
- startTracking(tracker);
1832
- return createProxy(tracker, instance.state);
2256
+ startDependency(depState);
2257
+ return createDependencyProxy(depState, instance$1.state);
1833
2258
  };
1834
2259
  }
1835
2260
  /**
@@ -1840,10 +2265,10 @@ function createAutoTrackSnapshot(instance, adapterState) {
1840
2265
  * @param config - Configuration with dependencies function
1841
2266
  * @returns Snapshot function for use with useSyncExternalStore
1842
2267
  */
1843
- function createManualDepsSnapshot(instance, adapterState, config) {
2268
+ function manualDepsSnapshot(instance$1, adapterState, config) {
1844
2269
  return () => {
1845
- adapterState.manualDepsCache = config.dependencies(instance.state, instance);
1846
- return instance.state;
2270
+ adapterState.manualDepsCache = config.dependencies(instance$1.state, instance$1);
2271
+ return instance$1.state;
1847
2272
  };
1848
2273
  }
1849
2274
  /**
@@ -1852,8 +2277,8 @@ function createManualDepsSnapshot(instance, adapterState, config) {
1852
2277
  * @param instance - The state container instance
1853
2278
  * @returns Snapshot function for use with useSyncExternalStore
1854
2279
  */
1855
- function createNoTrackSnapshot(instance) {
1856
- return () => instance.state;
2280
+ function noTrackSnapshot(instance$1) {
2281
+ return () => instance$1.state;
1857
2282
  }
1858
2283
  /**
1859
2284
  * Initialize adapter state for auto-tracking mode.
@@ -1861,12 +2286,12 @@ function createNoTrackSnapshot(instance) {
1861
2286
  * @param instance - The state container instance
1862
2287
  * @returns Initialized adapter state
1863
2288
  */
1864
- function initAutoTrackState(instance) {
2289
+ function autoTrackInit(instance$1) {
1865
2290
  return {
1866
- tracker: null,
2291
+ dependencyState: null,
1867
2292
  manualDepsCache: null,
1868
- getterTracker: createGetterTracker(),
1869
- proxiedBloc: createBlocProxy(instance)
2293
+ getterState: createGetterState(),
2294
+ proxiedBloc: createBlocProxy(instance$1)
1870
2295
  };
1871
2296
  }
1872
2297
  /**
@@ -1875,12 +2300,12 @@ function initAutoTrackState(instance) {
1875
2300
  * @param instance - The state container instance
1876
2301
  * @returns Initialized adapter state
1877
2302
  */
1878
- function initManualDepsState(instance) {
2303
+ function manualDepsInit(instance$1) {
1879
2304
  return {
1880
- tracker: null,
2305
+ dependencyState: null,
1881
2306
  manualDepsCache: null,
1882
- getterTracker: null,
1883
- proxiedBloc: instance
2307
+ getterState: null,
2308
+ proxiedBloc: instance$1
1884
2309
  };
1885
2310
  }
1886
2311
  /**
@@ -1889,12 +2314,12 @@ function initManualDepsState(instance) {
1889
2314
  * @param instance - The state container instance
1890
2315
  * @returns Initialized adapter state
1891
2316
  */
1892
- function initNoTrackState(instance) {
2317
+ function noTrackInit(instance$1) {
1893
2318
  return {
1894
- tracker: null,
2319
+ dependencyState: null,
1895
2320
  manualDepsCache: null,
1896
- getterTracker: null,
1897
- proxiedBloc: instance
2321
+ getterState: null,
2322
+ proxiedBloc: instance$1
1898
2323
  };
1899
2324
  }
1900
2325
  /**
@@ -1904,8 +2329,8 @@ function initNoTrackState(instance) {
1904
2329
  * @param rawInstance - The raw bloc instance
1905
2330
  */
1906
2331
  function disableGetterTracking(adapterState, rawInstance) {
1907
- if (adapterState.getterTracker) {
1908
- adapterState.getterTracker.isTracking = false;
2332
+ if (adapterState.getterState) {
2333
+ adapterState.getterState.isTracking = false;
1909
2334
  clearActiveTracker(rawInstance);
1910
2335
  }
1911
2336
  }
@@ -2037,6 +2462,67 @@ function configureLogger(opts) {
2037
2462
  defaultLogger = createLogger(currentConfig);
2038
2463
  }
2039
2464
 
2465
+ //#endregion
2466
+ //#region src/config.ts
2467
+ const defaultConfig = {
2468
+ devMode: process.env.NODE_ENV !== "production",
2469
+ logger: {
2470
+ enabled: false,
2471
+ level: LogLevel.INFO
2472
+ }
2473
+ };
2474
+ let globalConfig = { ...defaultConfig };
2475
+ /**
2476
+ * Configure global defaults for @blac/core.
2477
+ *
2478
+ * @example
2479
+ * ```ts
2480
+ * import { configureBlac, LogLevel } from '@blac/core';
2481
+ *
2482
+ * configureBlac({
2483
+ * devMode: true,
2484
+ * logger: {
2485
+ * enabled: true,
2486
+ * level: LogLevel.DEBUG
2487
+ * }
2488
+ * });
2489
+ * ```
2490
+ *
2491
+ * @param config - Partial configuration to merge with defaults
2492
+ */
2493
+ function configureBlac(config) {
2494
+ globalConfig = {
2495
+ ...globalConfig,
2496
+ ...config,
2497
+ logger: {
2498
+ ...globalConfig.logger,
2499
+ ...config.logger
2500
+ }
2501
+ };
2502
+ if (config.logger) configureLogger(config.logger);
2503
+ }
2504
+ /**
2505
+ * Get the current global configuration.
2506
+ * @internal
2507
+ */
2508
+ function getBlacConfig() {
2509
+ return globalConfig;
2510
+ }
2511
+ /**
2512
+ * Check if development mode is enabled.
2513
+ */
2514
+ function isDevMode() {
2515
+ return globalConfig.devMode;
2516
+ }
2517
+ /**
2518
+ * Reset configuration to defaults (useful for testing).
2519
+ * @internal
2520
+ */
2521
+ function resetBlacConfig() {
2522
+ globalConfig = { ...defaultConfig };
2523
+ configureLogger(defaultConfig.logger);
2524
+ }
2525
+
2040
2526
  //#endregion
2041
2527
  //#region src/decorators/blac.ts
2042
2528
  /**
@@ -2071,5 +2557,328 @@ function blac(options) {
2071
2557
  }
2072
2558
 
2073
2559
  //#endregion
2074
- export { BLAC_DEFAULTS, BLAC_ERROR_PREFIX, BLAC_ID_PATTERNS, BLAC_STATIC_PROPS, Cubit, ExternalDependencyManager, LogLevel, PluginManager, StateContainer, StateContainerRegistry, Vertex, __resetIdCounters, blac, captureTrackedPaths, clearActiveTracker, clearExternalDependencies, commitTrackedGetters, configureLogger, createArrayProxy, createAutoTrackSnapshot, createAutoTrackSubscribe, createBlocProxy, createGetterTracker, createIdGenerator, createLogger, createManualDepsSnapshot, createManualDepsSubscribe, createNoTrackSnapshot, createNoTrackSubscribe, createPluginManager, createProxy, createProxyForTarget, createProxyInternal, createProxyTrackerState, createTrackerState, debug, disableGetterTracking, error, generateId, generateIsolatedKey, generateSimpleId, getActiveTracker, getDescriptor, getGetterExecutionContext, getPluginManager, getStaticProp, getValueAtPath, globalRegistry, hasChanges, hasGetterChanges, hasInitHook, hasTrackedData, info, initAutoTrackState, initManualDepsState, initNoTrackState, invalidateRenderCache, isExcludedFromDevTools, isGetter, isIsolatedClass, isIsolatedKey, isKeepAliveClass, isProxyable, parsePath, resetGetterTracker, setActiveTracker, shallowEqual, startProxyTracking, startTracking, stopProxyTracking, warn };
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 };
2075
2884
  //# sourceMappingURL=index.js.map