@blac/core 2.0.0-rc.6 → 2.0.0-rc.8

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.
Files changed (60) hide show
  1. package/dist/adapter/framework-adapter.d.ts +114 -0
  2. package/dist/adapter/framework-adapter.d.ts.map +1 -0
  3. package/dist/adapter/index.d.ts +8 -0
  4. package/dist/adapter/index.d.ts.map +1 -0
  5. package/dist/constants.d.ts +82 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/core/Cubit.d.ts +82 -0
  8. package/dist/core/Cubit.d.ts.map +1 -0
  9. package/dist/core/StateContainer.d.ts +200 -0
  10. package/dist/core/StateContainer.d.ts.map +1 -0
  11. package/dist/core/StateContainerRegistry.d.ts +176 -0
  12. package/dist/core/StateContainerRegistry.d.ts.map +1 -0
  13. package/dist/core/Vertex.d.ts +113 -0
  14. package/dist/core/Vertex.d.ts.map +1 -0
  15. package/dist/index.cjs +1158 -326
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +20 -885
  18. package/dist/index.d.ts +19 -884
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +1122 -327
  21. package/dist/index.js.map +1 -1
  22. package/dist/logging/Logger.d.ts +57 -0
  23. package/dist/logging/Logger.d.ts.map +1 -0
  24. package/dist/plugin/BlacPlugin.d.ts +115 -0
  25. package/dist/plugin/BlacPlugin.d.ts.map +1 -0
  26. package/dist/plugin/PluginManager.d.ts +63 -0
  27. package/dist/plugin/PluginManager.d.ts.map +1 -0
  28. package/dist/proxy/ProxyTracker.d.ts +71 -0
  29. package/dist/proxy/ProxyTracker.d.ts.map +1 -0
  30. package/dist/registry/index.d.ts +20 -0
  31. package/dist/registry/index.d.ts.map +1 -0
  32. package/dist/test-utils/index.d.ts +43 -0
  33. package/dist/test-utils/index.d.ts.map +1 -0
  34. package/dist/tracking/dependency-tracker.d.ts +21 -0
  35. package/dist/tracking/dependency-tracker.d.ts.map +1 -0
  36. package/dist/tracking/getter-tracker.d.ts +31 -0
  37. package/dist/tracking/getter-tracker.d.ts.map +1 -0
  38. package/dist/tracking/index.d.ts +38 -0
  39. package/dist/tracking/index.d.ts.map +1 -0
  40. package/dist/tracking/path-utils.d.ts +39 -0
  41. package/dist/tracking/path-utils.d.ts.map +1 -0
  42. package/dist/tracking/proxy-tracker.d.ts +88 -0
  43. package/dist/tracking/proxy-tracker.d.ts.map +1 -0
  44. package/dist/types/branded.d.ts +9 -0
  45. package/dist/types/branded.d.ts.map +1 -0
  46. package/dist/types/events.d.ts +13 -0
  47. package/dist/types/events.d.ts.map +1 -0
  48. package/dist/types/utilities.d.ts +18 -0
  49. package/dist/types/utilities.d.ts.map +1 -0
  50. package/dist/utils/equality.d.ts +9 -0
  51. package/dist/utils/equality.d.ts.map +1 -0
  52. package/dist/utils/idGenerator.d.ts +76 -0
  53. package/dist/utils/idGenerator.d.ts.map +1 -0
  54. package/dist/utils/immutable.d.ts +16 -0
  55. package/dist/utils/immutable.d.ts.map +1 -0
  56. package/dist/utils/stack-trace.d.ts +19 -0
  57. package/dist/utils/stack-trace.d.ts.map +1 -0
  58. package/dist/utils/static-props.d.ts +29 -0
  59. package/dist/utils/static-props.d.ts.map +1 -0
  60. package/package.json +17 -4
package/dist/index.js CHANGED
@@ -15,15 +15,15 @@ const globalCounters = /* @__PURE__ */ new Map();
15
15
  * @example
16
16
  * ```ts
17
17
  * const generator = createIdGenerator('sub');
18
- * const id1 = generator.next(); // "sub_1698765432100_1_a3k9d7f2q"
19
- * const id2 = generator.next(); // "sub_1698765432101_2_b4n8e9g3r"
18
+ * const id1 = generator.next(); // "sub:1698765432100_1_a3k9d7f2q"
19
+ * const id2 = generator.next(); // "sub:1698765432101_2_b4n8e9g3r"
20
20
  * ```
21
21
  */
22
22
  function createIdGenerator(prefix) {
23
23
  let counter = 0;
24
24
  return {
25
- next: () => `${prefix}_${Date.now()}_${++counter}_${Math.random().toString(36).substring(2, 11)}`,
26
- nextSimple: () => `${prefix}_${++counter}`,
25
+ next: () => `${prefix}:${Date.now()}_${++counter}_${Math.random().toString(36).substring(2, 11)}`,
26
+ nextSimple: () => `${prefix}:${++counter}`,
27
27
  reset: () => {
28
28
  counter = 0;
29
29
  }
@@ -32,7 +32,7 @@ function createIdGenerator(prefix) {
32
32
  /**
33
33
  * Generate ID with timestamp, counter, and random suffix (tree-shakeable)
34
34
  *
35
- * Format: `${prefix}_${timestamp}_${counter}_${random}`
35
+ * Format: `${prefix}:${timestamp}_${counter}_${random}`
36
36
  *
37
37
  * @param prefix - Prefix for the ID (e.g., 'sub', 'consumer', 'stage')
38
38
  * @returns Branded ID string
@@ -40,18 +40,18 @@ function createIdGenerator(prefix) {
40
40
  * @example
41
41
  * ```ts
42
42
  * const id = generateId('sub');
43
- * // Returns: "sub_1698765432100_1_a3k9d7f2q"
43
+ * // Returns: "sub:1698765432100_1_a3k9d7f2q"
44
44
  * ```
45
45
  */
46
46
  function generateId(prefix) {
47
47
  const counter = (globalCounters.get(prefix) ?? 0) + 1;
48
48
  globalCounters.set(prefix, counter);
49
- return `${prefix}_${Date.now()}_${counter}_${Math.random().toString(36).substring(2, 11)}`;
49
+ return `${prefix}:${Date.now()}_${counter}_${Math.random().toString(36).substring(2, 11)}`;
50
50
  }
51
51
  /**
52
52
  * Generate simple ID with timestamp and random (no counter tracking)
53
53
  *
54
- * Format: `${prefix}_${timestamp}_${random}`
54
+ * Format: `${prefix}:${timestamp}_${random}`
55
55
  *
56
56
  * @param prefix - Prefix for the ID
57
57
  * @returns Branded ID string
@@ -59,11 +59,12 @@ function generateId(prefix) {
59
59
  * @example
60
60
  * ```ts
61
61
  * const id = generateSimpleId('CounterBloc');
62
- * // Returns: "CounterBloc_1698765432100_a3k9d7f2q"
62
+ * // Returns: "CounterBloc:1698765432100_a3k9d7f2q"
63
63
  * ```
64
64
  */
65
- function generateSimpleId(prefix) {
66
- return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
65
+ function generateSimpleId(prefix, affix) {
66
+ if (affix) return `${prefix}:${affix}`;
67
+ return `${prefix}:${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
67
68
  }
68
69
  /**
69
70
  * Reset all counters for testing purposes
@@ -72,129 +73,653 @@ function generateSimpleId(prefix) {
72
73
  function __resetIdCounters() {
73
74
  globalCounters.clear();
74
75
  }
76
+ /**
77
+ * Generate a unique isolated instance key
78
+ * Uses base36 encoding for compact, URL-safe identifiers
79
+ *
80
+ * Format: "isolated-{9-char-random-string}"
81
+ * Example: "isolated-k7x2m9p4q"
82
+ *
83
+ * @returns A unique isolated instance key
84
+ */
85
+ function generateIsolatedKey() {
86
+ return `isolated-${Math.random().toString(36).slice(2, 11)}`;
87
+ }
88
+ /**
89
+ * Check if a key is an isolated instance key
90
+ * @param key - The instance key to check
91
+ * @returns true if the key is an isolated instance key
92
+ */
93
+ function isIsolatedKey(key) {
94
+ return key.startsWith("isolated-");
95
+ }
96
+
97
+ //#endregion
98
+ //#region src/plugin/PluginManager.ts
99
+ /**
100
+ * Plugin manager for registering and coordinating plugins
101
+ */
102
+ var PluginManager = class {
103
+ constructor(registry) {
104
+ this.plugins = /* @__PURE__ */ new Map();
105
+ this.registry = registry;
106
+ this.setupLifecycleHooks();
107
+ }
108
+ /**
109
+ * Install a plugin
110
+ */
111
+ install(plugin, config = {}) {
112
+ const effectiveConfig = {
113
+ enabled: true,
114
+ environment: "all",
115
+ ...config
116
+ };
117
+ if (!this.shouldEnablePlugin(effectiveConfig)) {
118
+ console.log(`[BlaC] Plugin "${plugin.name}" skipped (environment mismatch)`);
119
+ return;
120
+ }
121
+ if (this.plugins.has(plugin.name)) throw new Error(`Plugin "${plugin.name}" is already installed`);
122
+ const context = this.createPluginContext();
123
+ this.plugins.set(plugin.name, {
124
+ plugin,
125
+ config: effectiveConfig,
126
+ context
127
+ });
128
+ if (plugin.onInstall) try {
129
+ plugin.onInstall(context);
130
+ } catch (error$1) {
131
+ console.error(`[BlaC] Error installing plugin "${plugin.name}":`, error$1);
132
+ this.plugins.delete(plugin.name);
133
+ throw error$1;
134
+ }
135
+ console.log(`[BlaC] Plugin "${plugin.name}" v${plugin.version} installed`);
136
+ }
137
+ /**
138
+ * Uninstall a plugin
139
+ */
140
+ uninstall(pluginName) {
141
+ const installed = this.plugins.get(pluginName);
142
+ if (!installed) throw new Error(`Plugin "${pluginName}" is not installed`);
143
+ if (installed.plugin.onUninstall) try {
144
+ installed.plugin.onUninstall();
145
+ } catch (error$1) {
146
+ console.error(`[BlaC] Error uninstalling plugin "${pluginName}":`, error$1);
147
+ }
148
+ this.plugins.delete(pluginName);
149
+ console.log(`[BlaC] Plugin "${pluginName}" uninstalled`);
150
+ }
151
+ /**
152
+ * Get installed plugin
153
+ */
154
+ getPlugin(pluginName) {
155
+ return this.plugins.get(pluginName)?.plugin;
156
+ }
157
+ /**
158
+ * Get all installed plugins
159
+ */
160
+ getAllPlugins() {
161
+ return Array.from(this.plugins.values()).map((p) => p.plugin);
162
+ }
163
+ /**
164
+ * Check if plugin is installed
165
+ */
166
+ hasPlugin(pluginName) {
167
+ return this.plugins.has(pluginName);
168
+ }
169
+ /**
170
+ * Clear all plugins (for testing)
171
+ */
172
+ clear() {
173
+ for (const name of this.plugins.keys()) this.uninstall(name);
174
+ }
175
+ /**
176
+ * Setup lifecycle hooks to notify plugins
177
+ */
178
+ setupLifecycleHooks() {
179
+ this.registry.on("created", (instance) => {
180
+ this.notifyPlugins("onInstanceCreated", instance);
181
+ });
182
+ this.registry.on("stateChanged", (instance, previousState, currentState, callstack) => {
183
+ this.notifyPlugins("onStateChanged", instance, previousState, currentState, callstack);
184
+ });
185
+ this.registry.on("eventAdded", (vertex, event) => {
186
+ this.notifyPlugins("onEventAdded", vertex, event);
187
+ });
188
+ this.registry.on("disposed", (instance) => {
189
+ this.notifyPlugins("onInstanceDisposed", instance);
190
+ });
191
+ }
192
+ /**
193
+ * Notify all plugins of a lifecycle event
194
+ */
195
+ notifyPlugins(hookName, ...args) {
196
+ for (const { plugin, config, context } of this.plugins.values()) {
197
+ if (!config.enabled) continue;
198
+ const hook = plugin[hookName];
199
+ if (typeof hook === "function") try {
200
+ hook.apply(plugin, [...args, context]);
201
+ } catch (error$1) {
202
+ console.error(`[BlaC] Error in plugin "${plugin.name}" ${hookName}:`, error$1);
203
+ }
204
+ }
205
+ }
206
+ /**
207
+ * Create plugin context with safe API access
208
+ */
209
+ createPluginContext() {
210
+ return {
211
+ getInstanceMetadata: (instance) => {
212
+ return {
213
+ id: instance.instanceId,
214
+ className: instance.constructor.name,
215
+ isDisposed: instance.isDisposed,
216
+ name: instance.name,
217
+ lastStateChangeTimestamp: instance.lastUpdateTimestamp,
218
+ createdAt: instance.createdAt,
219
+ state: instance.state,
220
+ isIsolated: instance.instanceId.startsWith("isolated-")
221
+ };
222
+ },
223
+ getState: (instance) => {
224
+ return instance.state;
225
+ },
226
+ queryInstances: (typeClass) => {
227
+ return typeClass.getAll();
228
+ },
229
+ getAllTypes: () => {
230
+ return this.registry.getTypes();
231
+ },
232
+ getStats: () => {
233
+ return this.registry.getStats();
234
+ }
235
+ };
236
+ }
237
+ /**
238
+ * Check if plugin should be enabled based on environment
239
+ */
240
+ shouldEnablePlugin(config) {
241
+ if (!config.enabled) return false;
242
+ if (config.environment === "all") return true;
243
+ return this.getCurrentEnvironment() === config.environment;
244
+ }
245
+ /**
246
+ * Get current environment
247
+ */
248
+ getCurrentEnvironment() {
249
+ if (typeof process !== "undefined") {
250
+ if (process.env.NODE_ENV === "test") return "test";
251
+ if (process.env.NODE_ENV === "production") return "production";
252
+ return "development";
253
+ }
254
+ return "development";
255
+ }
256
+ };
257
+ /**
258
+ * Create a plugin manager instance
259
+ */
260
+ function createPluginManager(registry) {
261
+ return new PluginManager(registry);
262
+ }
263
+
264
+ //#endregion
265
+ //#region src/constants.ts
266
+ /**
267
+ * Default configuration constants for BlaC
268
+ *
269
+ * Centralized location for all magic numbers and default values.
270
+ */
271
+ /**
272
+ * Default configuration constants for BlaC
273
+ */
274
+ const BLAC_DEFAULTS = {
275
+ DEFAULT_INSTANCE_KEY: "default",
276
+ MAX_GETTER_DEPTH: 10,
277
+ CLEANUP_INTERVAL_MS: 3e4,
278
+ WEAKREF_CLEANUP_INTERVAL_MS: 1e4,
279
+ MAX_SUBSCRIPTIONS: 1e3,
280
+ MAX_SUBSCRIPTIONS_HIGH_PERF: 1e4,
281
+ PIPELINE_TIMEOUT_MS: 5e3,
282
+ CLEANUP_INTERVAL_HIGH_PERF_MS: 5e3,
283
+ MAX_PIPELINE_STAGES: 30
284
+ };
285
+ /**
286
+ * Static property names for StateContainer classes
287
+ * Used for feature flags and configuration on bloc classes
288
+ */
289
+ const BLAC_STATIC_PROPS = {
290
+ ISOLATED: "isolated",
291
+ KEEP_ALIVE: "keepAlive",
292
+ EXCLUDE_FROM_DEVTOOLS: "__excludeFromDevTools"
293
+ };
294
+ /**
295
+ * ID generation patterns and constants
296
+ */
297
+ const BLAC_ID_PATTERNS = {
298
+ ISOLATED_PREFIX: "isolated-",
299
+ ID_LENGTH: 9
300
+ };
301
+ /**
302
+ * Standard error message prefix
303
+ */
304
+ const BLAC_ERROR_PREFIX = "[BlaC]";
305
+
306
+ //#endregion
307
+ //#region src/tracking/getter-tracker.ts
308
+ const descriptorCache = /* @__PURE__ */ new WeakMap();
309
+ const blocProxyCache = /* @__PURE__ */ new WeakMap();
310
+ const activeTrackerMap = /* @__PURE__ */ new WeakMap();
311
+ const getterExecutionContext = {
312
+ tracker: null,
313
+ currentBloc: null,
314
+ depth: 0,
315
+ visitedBlocs: /* @__PURE__ */ new Set()
316
+ };
317
+ const MAX_GETTER_DEPTH = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
318
+ function getGetterExecutionContext() {
319
+ return getterExecutionContext;
320
+ }
321
+ function getDescriptor(obj, prop) {
322
+ const constructor = obj.constructor;
323
+ let constructorCache = descriptorCache.get(constructor);
324
+ if (constructorCache?.has(prop)) return constructorCache.get(prop);
325
+ let current = obj;
326
+ let descriptor;
327
+ while (current && current !== Object.prototype) {
328
+ descriptor = Object.getOwnPropertyDescriptor(current, prop);
329
+ if (descriptor) break;
330
+ current = Object.getPrototypeOf(current);
331
+ }
332
+ if (!constructorCache) {
333
+ constructorCache = /* @__PURE__ */ new Map();
334
+ descriptorCache.set(constructor, constructorCache);
335
+ }
336
+ constructorCache.set(prop, descriptor);
337
+ return descriptor;
338
+ }
339
+ function isGetter(obj, prop) {
340
+ return getDescriptor(obj, prop)?.get !== void 0;
341
+ }
342
+ function createGetterTracker() {
343
+ return {
344
+ trackedValues: /* @__PURE__ */ new Map(),
345
+ currentlyAccessing: /* @__PURE__ */ new Set(),
346
+ trackedGetters: /* @__PURE__ */ new Set(),
347
+ isTracking: false,
348
+ renderCache: /* @__PURE__ */ new Map(),
349
+ cacheValid: false,
350
+ externalDependencies: /* @__PURE__ */ new Set()
351
+ };
352
+ }
353
+ function setActiveTracker(bloc, tracker) {
354
+ activeTrackerMap.set(bloc, tracker);
355
+ }
356
+ function clearActiveTracker(bloc) {
357
+ activeTrackerMap.delete(bloc);
358
+ }
359
+ function getActiveTracker(bloc) {
360
+ return activeTrackerMap.get(bloc);
361
+ }
362
+ function commitTrackedGetters(tracker) {
363
+ if (tracker.currentlyAccessing.size > 0) tracker.trackedGetters = new Set(tracker.currentlyAccessing);
364
+ tracker.currentlyAccessing.clear();
365
+ }
366
+ function createBlocProxy(bloc) {
367
+ const cached = blocProxyCache.get(bloc);
368
+ if (cached) return cached;
369
+ const proxy = new Proxy(bloc, { get(target, prop, receiver) {
370
+ const tracker = activeTrackerMap.get(target);
371
+ if (tracker?.isTracking && isGetter(target, prop)) {
372
+ tracker.currentlyAccessing.add(prop);
373
+ if (tracker.cacheValid && tracker.renderCache.has(prop)) {
374
+ const cachedValue = tracker.renderCache.get(prop);
375
+ tracker.trackedValues.set(prop, cachedValue);
376
+ return cachedValue;
377
+ }
378
+ if (getterExecutionContext.depth >= MAX_GETTER_DEPTH) {
379
+ 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.`);
380
+ return;
381
+ }
382
+ if (getterExecutionContext.visitedBlocs.has(target)) {
383
+ console.warn(`${BLAC_ERROR_PREFIX} Circular dependency detected: getter "${String(prop)}" on ${target.constructor.name} is calling back into itself. Returning undefined to prevent infinite recursion.`);
384
+ return;
385
+ }
386
+ const prevContext = {
387
+ tracker: getterExecutionContext.tracker,
388
+ currentBloc: getterExecutionContext.currentBloc,
389
+ depth: getterExecutionContext.depth,
390
+ visitedBlocs: new Set(getterExecutionContext.visitedBlocs)
391
+ };
392
+ getterExecutionContext.tracker = tracker;
393
+ getterExecutionContext.currentBloc = target;
394
+ getterExecutionContext.depth++;
395
+ getterExecutionContext.visitedBlocs.add(target);
396
+ try {
397
+ const value = getDescriptor(target, prop).get.call(target);
398
+ tracker.trackedValues.set(prop, value);
399
+ return value;
400
+ } finally {
401
+ getterExecutionContext.tracker = prevContext.tracker;
402
+ getterExecutionContext.currentBloc = prevContext.currentBloc;
403
+ getterExecutionContext.depth = prevContext.depth;
404
+ getterExecutionContext.visitedBlocs = prevContext.visitedBlocs;
405
+ }
406
+ }
407
+ return Reflect.get(target, prop, receiver);
408
+ } });
409
+ blocProxyCache.set(bloc, proxy);
410
+ return proxy;
411
+ }
412
+ function hasGetterChanges(bloc, tracker) {
413
+ if (!tracker || tracker.trackedGetters.size === 0) return false;
414
+ tracker.renderCache.clear();
415
+ let hasAnyChange = false;
416
+ for (const prop of tracker.trackedGetters) try {
417
+ const descriptor = getDescriptor(bloc, prop);
418
+ if (!descriptor?.get) continue;
419
+ const newValue = descriptor.get.call(bloc);
420
+ const oldValue = tracker.trackedValues.get(prop);
421
+ tracker.renderCache.set(prop, newValue);
422
+ tracker.trackedValues.set(prop, newValue);
423
+ if (!Object.is(newValue, oldValue)) hasAnyChange = true;
424
+ } catch (error$1) {
425
+ console.warn(`${BLAC_ERROR_PREFIX} Getter "${String(prop)}" threw error during change detection. Stopping tracking for this getter.`, error$1);
426
+ tracker.trackedGetters.delete(prop);
427
+ tracker.trackedValues.delete(prop);
428
+ tracker.cacheValid = false;
429
+ return true;
430
+ }
431
+ tracker.cacheValid = true;
432
+ return hasAnyChange;
433
+ }
434
+ function invalidateRenderCache(tracker) {
435
+ tracker.cacheValid = false;
436
+ }
437
+ function clearExternalDependencies(tracker) {
438
+ tracker.externalDependencies.clear();
439
+ }
440
+ function resetGetterTracker(tracker) {
441
+ tracker.trackedValues.clear();
442
+ tracker.currentlyAccessing.clear();
443
+ tracker.trackedGetters.clear();
444
+ tracker.renderCache.clear();
445
+ tracker.cacheValid = false;
446
+ tracker.isTracking = false;
447
+ tracker.externalDependencies.clear();
448
+ }
449
+
450
+ //#endregion
451
+ //#region src/utils/static-props.ts
452
+ /**
453
+ * Utility functions for accessing static properties on StateContainer classes
454
+ */
455
+ /**
456
+ * Get a static property from a class constructor
457
+ * Type-safe helper that avoids (Type as any) casts
458
+ *
459
+ * @param Type - The class constructor
460
+ * @param propName - The property name to access
461
+ * @param defaultValue - Optional default value if property is undefined
462
+ * @returns The property value or default
463
+ */
464
+ function getStaticProp(Type, propName, defaultValue) {
465
+ return Type[propName] ?? defaultValue;
466
+ }
467
+ /**
468
+ * Check if a class is marked as isolated
469
+ * Isolated classes create separate instances per component
470
+ */
471
+ function isIsolatedClass(Type) {
472
+ return getStaticProp(Type, BLAC_STATIC_PROPS.ISOLATED) === true;
473
+ }
474
+ /**
475
+ * Check if a class is marked as keepAlive
476
+ * KeepAlive classes are never auto-disposed when ref count reaches 0
477
+ */
478
+ function isKeepAliveClass(Type) {
479
+ return getStaticProp(Type, BLAC_STATIC_PROPS.KEEP_ALIVE) === true;
480
+ }
481
+ /**
482
+ * Check if a class should be excluded from DevTools
483
+ * Used to prevent infinite loops when DevTools tracks itself
484
+ */
485
+ function isExcludedFromDevTools(Type) {
486
+ return getStaticProp(Type, BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS) === true;
487
+ }
75
488
 
76
489
  //#endregion
77
490
  //#region src/core/StateContainerRegistry.ts
78
491
  /**
79
- * Registry for managing StateContainer instances
492
+ * Registry for coordinating StateContainer lifecycle and managing instances
493
+ *
494
+ * Centralizes all instance management using WeakMap to ensure proper isolation.
80
495
  */
81
496
  var StateContainerRegistry = class {
82
- instances = /* @__PURE__ */ new Map();
83
- types = /* @__PURE__ */ new Map();
84
- listeners = /* @__PURE__ */ new Map();
497
+ constructor() {
498
+ this.instancesByConstructor = /* @__PURE__ */ new WeakMap();
499
+ this.types = /* @__PURE__ */ new Set();
500
+ this.typeConfigs = /* @__PURE__ */ new Map();
501
+ this.listeners = /* @__PURE__ */ new Map();
502
+ }
85
503
  /**
86
- * Register a type with isolation mode
504
+ * Register a type for tracking
505
+ * Called automatically on first instance creation
506
+ */
507
+ registerType(constructor) {
508
+ this.types.add(constructor);
509
+ }
510
+ /**
511
+ * Register a type with isolation mode (explicit registration)
87
512
  */
88
513
  register(constructor, isolated = false) {
89
514
  const className = constructor.name;
90
- if (!isolated && constructor.isolated === true) isolated = true;
91
- if (this.types.has(className)) throw new Error(`Type "${className}" is already registered`);
92
- this.types.set(className, { isolated });
515
+ if (!isolated && isIsolatedClass(constructor)) isolated = true;
516
+ if (this.typeConfigs.has(className)) throw new Error(`${BLAC_ERROR_PREFIX} Type "${className}" is already registered`);
517
+ this.typeConfigs.set(className, { isolated });
518
+ this.registerType(constructor);
93
519
  }
94
520
  /**
95
- * Get or create a StateContainer instance
96
- * For isolated: always creates new
97
- * For shared: singleton per key
521
+ * Get the instances Map for a specific class
522
+ * Creates a new Map on first access.
98
523
  */
99
- getOrCreate(constructor, key, ...args) {
100
- const className = constructor.name;
101
- let config = this.types.get(className);
102
- if (!config) {
103
- const isolated = constructor.isolated === true;
104
- this.types.set(className, { isolated });
105
- config = this.types.get(className);
524
+ ensureInstancesMap(Type) {
525
+ let instances = this.instancesByConstructor.get(Type);
526
+ if (!instances) {
527
+ instances = /* @__PURE__ */ new Map();
528
+ this.instancesByConstructor.set(Type, instances);
529
+ }
530
+ return instances;
531
+ }
532
+ /**
533
+ * Get the instances Map for a specific class (public API for stats/debugging)
534
+ */
535
+ getInstancesMap(Type) {
536
+ return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
537
+ }
538
+ /**
539
+ * Resolve an instance with ref counting (ownership semantics)
540
+ */
541
+ resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
542
+ const registryConfig = this.typeConfigs.get(Type.name);
543
+ const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
544
+ const config = { instanceId: instanceKey };
545
+ if (isolated) {
546
+ const instance$1 = new Type(constructorArgs);
547
+ instance$1.initiConfig(config);
548
+ this.registerType(Type);
549
+ return instance$1;
106
550
  }
107
- if (config.isolated) return new constructor(...args);
108
- const instanceKey = `${className}:${key || "default"}`;
109
- const existing = this.instances.get(instanceKey);
110
- if (existing) {
111
- existing.refCount++;
112
- return existing.instance;
551
+ const instances = this.ensureInstancesMap(Type);
552
+ const entry = instances.get(instanceKey);
553
+ if (entry) {
554
+ entry.refCount++;
555
+ return entry.instance;
113
556
  }
114
- const instance = new constructor(...args);
115
- this.instances.set(instanceKey, {
557
+ const instance = new Type(constructorArgs);
558
+ instance.initiConfig(config);
559
+ instances.set(instanceKey, {
116
560
  instance,
117
561
  refCount: 1
118
562
  });
563
+ this.registerType(Type);
119
564
  return instance;
120
565
  }
121
566
  /**
567
+ * Get an existing instance without ref counting (borrowing semantics)
568
+ * @throws Error if instance doesn't exist
569
+ */
570
+ get(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
571
+ const entry = this.ensureInstancesMap(Type).get(instanceKey);
572
+ 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.`);
573
+ const context = getGetterExecutionContext();
574
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
575
+ return entry.instance;
576
+ }
577
+ /**
578
+ * Safely get an existing instance (borrowing semantics with error handling)
579
+ * Returns discriminated union for type-safe conditional access
580
+ */
581
+ getSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
582
+ const entry = this.ensureInstancesMap(Type).get(instanceKey);
583
+ if (!entry) return {
584
+ error: /* @__PURE__ */ new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found`),
585
+ instance: null
586
+ };
587
+ const context = getGetterExecutionContext();
588
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
589
+ return {
590
+ error: null,
591
+ instance: entry.instance
592
+ };
593
+ }
594
+ /**
595
+ * Connect to an instance with borrowing semantics (for B2B communication)
596
+ * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
597
+ * Tracks cross-bloc dependency for reactive updates.
598
+ *
599
+ * Use this in bloc-to-bloc communication when you need to ensure an instance exists
600
+ * but don't want to claim ownership (no ref count increment).
601
+ *
602
+ * @param Type - The bloc class constructor
603
+ * @param instanceKey - Optional instance key (defaults to 'default')
604
+ * @param constructorArgs - Constructor arguments (only used if creating new instance)
605
+ * @returns The bloc instance
606
+ */
607
+ connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
608
+ const registryConfig = this.typeConfigs.get(Type.name);
609
+ 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.`);
610
+ const instances = this.ensureInstancesMap(Type);
611
+ let entry = instances.get(instanceKey);
612
+ if (!entry) {
613
+ const instance = new Type(constructorArgs);
614
+ const config = { instanceId: instanceKey };
615
+ instance.initiConfig(config);
616
+ entry = {
617
+ instance,
618
+ refCount: 1
619
+ };
620
+ instances.set(instanceKey, entry);
621
+ this.registerType(Type);
622
+ }
623
+ const context = getGetterExecutionContext();
624
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
625
+ return entry.instance;
626
+ }
627
+ /**
122
628
  * Release a reference to an instance
123
- * Disposes when ref count reaches zero (unless static keepAlive is true)
124
629
  */
125
- release(constructor, key, forceDispose = false) {
126
- const instanceKey = `${constructor.name}:${key || "default"}`;
127
- const entry = this.instances.get(instanceKey);
630
+ release(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, forceDispose = false) {
631
+ const instances = this.ensureInstancesMap(Type);
632
+ const entry = instances.get(instanceKey);
128
633
  if (!entry) return;
129
634
  if (forceDispose) {
130
- entry.instance.dispose();
131
- this.instances.delete(instanceKey);
635
+ if (!entry.instance.isDisposed) entry.instance.dispose();
636
+ instances.delete(instanceKey);
132
637
  return;
133
638
  }
134
639
  entry.refCount--;
135
- const keepAlive = constructor.keepAlive === true;
640
+ const keepAlive = isKeepAliveClass(Type);
136
641
  if (entry.refCount <= 0 && !keepAlive) {
137
- entry.instance.dispose();
138
- this.instances.delete(instanceKey);
642
+ if (!entry.instance.isDisposed) entry.instance.dispose();
643
+ instances.delete(instanceKey);
139
644
  }
140
645
  }
141
646
  /**
142
- * Get all instances of a type (shared only, isolated aren't tracked)
647
+ * Get all instances of a specific type
143
648
  */
144
- getAll(constructor) {
145
- const prefix = `${constructor.name}:`;
146
- const results = [];
147
- for (const [key, entry] of this.instances) if (key.startsWith(prefix)) results.push(entry.instance);
148
- return results;
649
+ getAll(Type) {
650
+ const instances = this.ensureInstancesMap(Type);
651
+ const result = [];
652
+ for (const entry of instances.values()) result.push(entry.instance);
653
+ return result;
149
654
  }
150
655
  /**
151
- * Clear all instances of a type
656
+ * Safely iterate over all instances of a type
152
657
  */
153
- clear(constructor) {
154
- const prefix = `${constructor.name}:`;
155
- const toDelete = [];
156
- for (const [key, entry] of this.instances) if (key.startsWith(prefix)) {
157
- if (!entry.instance.isDisposed) entry.instance.dispose();
158
- toDelete.push(key);
658
+ forEach(Type, callback) {
659
+ const instances = this.ensureInstancesMap(Type);
660
+ for (const entry of instances.values()) {
661
+ const instance = entry.instance;
662
+ if (!instance.isDisposed) try {
663
+ callback(instance);
664
+ } catch (error$1) {
665
+ console.error(`${BLAC_ERROR_PREFIX} forEach callback error for ${Type.name}:`, error$1);
666
+ }
159
667
  }
160
- for (const key of toDelete) this.instances.delete(key);
668
+ }
669
+ /**
670
+ * Clear all instances of a specific type
671
+ */
672
+ clear(Type) {
673
+ const instances = this.ensureInstancesMap(Type);
674
+ for (const entry of instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
675
+ instances.clear();
676
+ }
677
+ /**
678
+ * Get reference count for an instance
679
+ */
680
+ getRefCount(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
681
+ return this.ensureInstancesMap(Type).get(instanceKey)?.refCount ?? 0;
682
+ }
683
+ /**
684
+ * Check if an instance exists
685
+ */
686
+ hasInstance(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
687
+ return this.ensureInstancesMap(Type).has(instanceKey);
161
688
  }
162
689
  /**
163
690
  * Clear all instances from all types (for testing)
691
+ *
692
+ * Iterates all registered types and clears their instances.
693
+ * Also clears type tracking to reset the registry state.
164
694
  */
165
695
  clearAll() {
166
- for (const entry of this.instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
167
- this.instances.clear();
696
+ for (const Type of this.types) this.clear(Type);
168
697
  this.types.clear();
698
+ this.typeConfigs.clear();
169
699
  }
170
700
  /**
171
701
  * Get registry statistics (for debugging)
172
702
  */
173
703
  getStats() {
174
704
  const typeBreakdown = {};
175
- for (const key of this.instances.keys()) {
176
- const typeName = key.split(":")[0];
177
- typeBreakdown[typeName] = (typeBreakdown[typeName] || 0) + 1;
705
+ let totalInstances = 0;
706
+ for (const Type of this.types) {
707
+ const typeName = Type.name;
708
+ const count = this.getInstancesMap(Type).size;
709
+ typeBreakdown[typeName] = count;
710
+ totalInstances += count;
178
711
  }
179
712
  return {
180
713
  registeredTypes: this.types.size,
181
- totalInstances: this.instances.size,
714
+ totalInstances,
182
715
  typeBreakdown
183
716
  };
184
717
  }
185
718
  /**
186
- * Get reference count for an instance
187
- */
188
- getRefCount(constructor, key) {
189
- const instanceKey = `${constructor.name}:${key || "default"}`;
190
- return this.instances.get(instanceKey)?.refCount ?? 0;
191
- }
192
- /**
193
- * Check if an instance exists
719
+ * Get all registered types (for plugin system)
194
720
  */
195
- hasInstance(constructor, key) {
196
- const instanceKey = `${constructor.name}:${key || "default"}`;
197
- return this.instances.has(instanceKey);
721
+ getTypes() {
722
+ return Array.from(this.types);
198
723
  }
199
724
  /**
200
725
  * Subscribe to lifecycle events
@@ -219,7 +744,7 @@ var StateContainerRegistry = class {
219
744
  for (const listener of listeners) try {
220
745
  listener(...args);
221
746
  } catch (error$1) {
222
- console.error(`[BlaC] Listener error for '${event}':`, error$1);
747
+ console.error(`${BLAC_ERROR_PREFIX} Listener error for '${event}':`, error$1);
223
748
  }
224
749
  }
225
750
  };
@@ -227,18 +752,51 @@ var StateContainerRegistry = class {
227
752
  * Global default registry instance
228
753
  */
229
754
  const globalRegistry = new StateContainerRegistry();
755
+ /**
756
+ * Global plugin manager (initialized lazily)
757
+ */
758
+ let _globalPluginManager = null;
759
+ /**
760
+ * Get the global plugin manager
761
+ */
762
+ function getPluginManager() {
763
+ if (!_globalPluginManager) _globalPluginManager = createPluginManager(globalRegistry);
764
+ return _globalPluginManager;
765
+ }
230
766
 
231
767
  //#endregion
232
768
  //#region src/core/StateContainer.ts
233
769
  /**
770
+ * StateContainer - Clean, minimal state management container
771
+ *
772
+ * Responsibilities:
773
+ * - State storage and updates
774
+ * - Change notifications to subscribers
775
+ * - Lifecycle management
776
+ */
777
+ /**
234
778
  * Base abstract class for all state containers
235
779
  */
236
780
  var StateContainer = class StateContainer {
237
- static _registry = globalRegistry;
781
+ static {
782
+ this.__excludeFromDevTools = false;
783
+ }
784
+ static {
785
+ this._registry = globalRegistry;
786
+ }
787
+ /**
788
+ * Get the global registry (mainly for testing)
789
+ */
790
+ static getRegistry() {
791
+ return StateContainer._registry;
792
+ }
238
793
  /**
239
794
  * Set a custom registry (mainly for testing)
795
+ *
796
+ * Clears all instances before switching to ensure clean test isolation.
240
797
  */
241
798
  static setRegistry(registry) {
799
+ StateContainer._registry.clearAll();
242
800
  StateContainer._registry = registry;
243
801
  }
244
802
  /**
@@ -248,75 +806,123 @@ var StateContainer = class StateContainer {
248
806
  StateContainer._registry.register(this, isolated);
249
807
  }
250
808
  /**
251
- * Get or create an instance
252
- * @param key - Optional instance key for shared instances
253
- * @param args - Constructor arguments
809
+ * Resolve an instance with ref counting (ownership semantics)
810
+ *
811
+ * Delegates to the global registry for instance management.
812
+ *
813
+ * @param instanceKey - Optional instance key for shared instances
814
+ * @param constructorArgs - Constructor arguments
815
+ * @returns Instance with incremented ref count
816
+ */
817
+ static resolve(instanceKey, constructorArgs) {
818
+ return StateContainer._registry.resolve(this, instanceKey, constructorArgs);
819
+ }
820
+ /**
821
+ * Get an existing instance without ref counting (borrowing semantics)
822
+ * Delegates to the global registry.
254
823
  */
255
- static getOrCreate(key, ...args) {
256
- return StateContainer._registry.getOrCreate(this, key, ...args);
824
+ static get(instanceKey) {
825
+ return StateContainer._registry.get(this, instanceKey);
826
+ }
827
+ /**
828
+ * Safely get an existing instance (borrowing semantics with error handling)
829
+ * Delegates to the global registry.
830
+ */
831
+ static getSafe(instanceKey) {
832
+ return StateContainer._registry.getSafe(this, instanceKey);
833
+ }
834
+ /**
835
+ * Connect to an instance with borrowing semantics (for B2B communication)
836
+ * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
837
+ * Tracks cross-bloc dependency for reactive updates.
838
+ *
839
+ * Use this in bloc-to-bloc communication when you need to ensure an instance exists
840
+ * but don't want to claim ownership (no ref count increment).
841
+ *
842
+ * Delegates to the global registry.
843
+ *
844
+ * @param instanceKey - Optional instance key (defaults to 'default')
845
+ * @param constructorArgs - Constructor arguments (only used if creating new instance)
846
+ * @returns The bloc instance
847
+ */
848
+ static connect(instanceKey, constructorArgs) {
849
+ return StateContainer._registry.connect(this, instanceKey, constructorArgs);
257
850
  }
258
851
  /**
259
852
  * Release a reference to an instance
260
- * @param key - Optional instance key
261
- * @param forceDispose - Force dispose regardless of ref count or keepAlive
853
+ * Delegates to the global registry.
262
854
  */
263
- static release(key, forceDispose = false) {
264
- StateContainer._registry.release(this, key, forceDispose);
855
+ static release(instanceKey, forceDispose = false) {
856
+ StateContainer._registry.release(this, instanceKey, forceDispose);
265
857
  }
266
858
  /**
267
859
  * Get all instances of this type
860
+ * Delegates to the global registry.
268
861
  */
269
862
  static getAll() {
270
863
  return StateContainer._registry.getAll(this);
271
864
  }
272
865
  /**
866
+ * Safely iterate over all instances of this type
867
+ * Delegates to the global registry.
868
+ */
869
+ static forEach(callback) {
870
+ StateContainer._registry.forEach(this, callback);
871
+ }
872
+ /**
273
873
  * Clear all instances of this type
874
+ * Delegates to the global registry.
274
875
  */
275
876
  static clear() {
276
877
  StateContainer._registry.clear(this);
277
878
  }
278
879
  /**
279
880
  * Clear all instances from all types (for testing)
881
+ * Delegates to the global registry.
280
882
  */
281
883
  static clearAllInstances() {
282
884
  StateContainer._registry.clearAll();
283
885
  }
284
886
  /**
285
887
  * Get registry statistics (for debugging)
888
+ * Delegates to the global registry.
286
889
  */
287
890
  static getStats() {
288
891
  return StateContainer._registry.getStats();
289
892
  }
290
893
  /**
291
894
  * Get reference count for an instance
292
- * @param key - Optional instance key
895
+ * Delegates to the global registry.
293
896
  */
294
- static getRefCount(key) {
295
- return StateContainer._registry.getRefCount(this, key);
897
+ static getRefCount(instanceKey) {
898
+ return StateContainer._registry.getRefCount(this, instanceKey);
296
899
  }
297
900
  /**
298
901
  * Check if an instance exists
299
- * @param key - Optional instance key
902
+ * Delegates to the global registry.
300
903
  */
301
- static hasInstance(key) {
302
- return StateContainer._registry.hasInstance(this, key);
904
+ static hasInstance(instanceKey) {
905
+ return StateContainer._registry.hasInstance(this, instanceKey);
303
906
  }
304
- _state;
305
- listeners = /* @__PURE__ */ new Set();
306
- _disposed = false;
307
- config;
308
- name;
309
- debug;
310
- instanceId;
311
907
  /**
312
908
  * Create a new StateContainer
313
909
  */
314
- constructor(initialState, config = {}) {
910
+ constructor(initialState) {
911
+ this.listeners = /* @__PURE__ */ new Set();
912
+ this._disposed = false;
913
+ this.config = {};
914
+ this.name = this.constructor.name;
915
+ this.debug = false;
916
+ this.instanceId = generateSimpleId(this.constructor.name, "main");
917
+ this.createdAt = Date.now();
918
+ this.lastUpdateTimestamp = Date.now();
315
919
  this._state = initialState;
316
- this.config = config;
317
- this.name = config.name || this.constructor.name;
318
- this.debug = config.debug ?? false;
319
- this.instanceId = config.instanceId || generateSimpleId(this.constructor.name);
920
+ }
921
+ initiConfig(config) {
922
+ this.config = { ...config };
923
+ this.name = this.config.name || this.constructor.name;
924
+ this.debug = this.config.debug ?? false;
925
+ this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
320
926
  StateContainer._registry.emit("created", this);
321
927
  }
322
928
  /**
@@ -366,7 +972,58 @@ var StateContainer = class StateContainer {
366
972
  } catch (error$1) {
367
973
  console.error(`[${this.name}] Error in listener:`, error$1);
368
974
  }
369
- StateContainer._registry.emit("stateChanged", this, previousState, newState);
975
+ const stackTrace = this.captureStackTrace();
976
+ StateContainer._registry.emit("stateChanged", this, previousState, newState, stackTrace);
977
+ this.lastUpdateTimestamp = Date.now();
978
+ }
979
+ /**
980
+ * Capture stack trace for debugging
981
+ * Can be overridden to disable in production
982
+ */
983
+ captureStackTrace() {
984
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
985
+ try {
986
+ const relevantLines = ((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(1);
987
+ const formattedLines = [];
988
+ for (const line of relevantLines) {
989
+ if (!line.trim()) continue;
990
+ if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("Cubit.patch") || line.includes("Vertex.")) continue;
991
+ if (line.includes("node_modules") || line.includes("react-dom") || line.includes("react_jsx") || line.includes(".vite/deps") || line.includes("executeDispatch") || line.includes("runWithFiber") || line.includes("invokeGuarded") || line.includes("callCallback") || line.includes("processDispatchQueue") || line.includes("dispatchEvent") || line.includes("batchedUpdates")) continue;
992
+ const formatted = this.formatStackLine(line);
993
+ if (formatted) formattedLines.push(formatted);
994
+ }
995
+ return formattedLines.join("\n");
996
+ } catch {
997
+ return "";
998
+ }
999
+ }
1000
+ /**
1001
+ * Format a single stack trace line for better readability
1002
+ * Removes URL prefixes, query params, and cleans up the output
1003
+ */
1004
+ formatStackLine(line) {
1005
+ const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
1006
+ if (!match) {
1007
+ const simpleMatch = line.match(/at\s+(.+?):(\d+):(\d+)/);
1008
+ if (simpleMatch) {
1009
+ const [, url$1, lineNum$1, col$1] = simpleMatch;
1010
+ return ` at ${this.cleanFilePath(url$1)}:${lineNum$1}:${col$1}`;
1011
+ }
1012
+ return null;
1013
+ }
1014
+ const [, functionName, url, lineNum, col] = match;
1015
+ return ` at ${functionName} (${this.cleanFilePath(url)}:${lineNum}:${col})`;
1016
+ }
1017
+ /**
1018
+ * Clean up file path by removing URL prefixes and query params
1019
+ */
1020
+ cleanFilePath(url) {
1021
+ let path = url.replace(/http:\/\/localhost:\d+\/@fs/, "").replace(/http:\/\/localhost:\d+\//, "").replace(/\?t=\d+/, "").replace(/\?v=[a-f0-9]+/, "");
1022
+ const projectMatch = path.match(/\/Projects\/blac\/(.+)/);
1023
+ if (projectMatch) path = projectMatch[1];
1024
+ const segments = path.split("/");
1025
+ if (segments.length > 3) path = segments.slice(-3).join("/");
1026
+ return path;
370
1027
  }
371
1028
  /**
372
1029
  * Update state using a function
@@ -380,6 +1037,12 @@ var StateContainer = class StateContainer {
380
1037
  //#endregion
381
1038
  //#region src/core/Cubit.ts
382
1039
  /**
1040
+ * Cubit - Simple state container with direct state emission
1041
+ *
1042
+ * This validates that our StateContainer design can support
1043
+ * the simple Cubit pattern from the original BlaC.
1044
+ */
1045
+ /**
383
1046
  * Cubit is a simple state container that allows direct state emission
384
1047
  *
385
1048
  * Unlike StateContainer (where emit is protected), Cubit exposes emit and update
@@ -389,10 +1052,16 @@ var Cubit = class extends StateContainer {
389
1052
  /**
390
1053
  * Create a new Cubit
391
1054
  * @param initialState Initial state value
392
- * @param config Configuration options
393
1055
  */
394
- constructor(initialState, config) {
395
- super(initialState, config);
1056
+ constructor(initialState) {
1057
+ super(initialState);
1058
+ this.patch = ((partial) => {
1059
+ if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
1060
+ this.update((current) => ({
1061
+ ...current,
1062
+ ...partial
1063
+ }));
1064
+ });
396
1065
  }
397
1066
  /**
398
1067
  * Emit a new state (public override of protected parent method)
@@ -406,89 +1075,70 @@ var Cubit = class extends StateContainer {
406
1075
  update(updater) {
407
1076
  super["update"](updater);
408
1077
  }
409
- /**
410
- * Patch state with partial updates (shallow merge)
411
- * Only available when state is an object type
412
- * Arrow function to maintain correct 'this' binding in React
413
- *
414
- * @example
415
- * ```typescript
416
- * // Update single field
417
- * this.patch({ count: 5 });
418
- *
419
- * // Update multiple fields
420
- * this.patch({ count: 5, name: 'Updated' });
421
- * ```
422
- */
423
- patch = ((partial) => {
424
- if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
425
- this.update((current) => ({
426
- ...current,
427
- ...partial
428
- }));
429
- });
430
1078
  };
431
1079
  /**
432
1080
  * Example: Counter Cubit
433
1081
  */
434
1082
  var CounterCubit = class extends Cubit {
435
1083
  constructor() {
436
- super(0, { name: "CounterCubit" });
1084
+ super(0);
1085
+ this.increment = () => {
1086
+ this.emit(this.state + 1);
1087
+ };
1088
+ this.decrement = () => {
1089
+ this.emit(this.state - 1);
1090
+ };
1091
+ this.reset = () => {
1092
+ this.emit(0);
1093
+ };
1094
+ this.addAmount = (amount) => {
1095
+ this.emit(this.state + amount);
1096
+ };
437
1097
  }
438
- increment = () => {
439
- this.emit(this.state + 1);
440
- };
441
- decrement = () => {
442
- this.emit(this.state - 1);
443
- };
444
- reset = () => {
445
- this.emit(0);
446
- };
447
- addAmount = (amount) => {
448
- this.emit(this.state + amount);
449
- };
450
1098
  };
451
1099
  var TodoCubit = class extends Cubit {
452
- static keepAlive = true;
453
- nextId = 0;
1100
+ static {
1101
+ this.keepAlive = true;
1102
+ }
454
1103
  constructor() {
455
1104
  super({
456
1105
  todos: [],
457
1106
  filter: "all",
458
1107
  isLoading: false
459
- }, { name: "TodoCubit" });
460
- }
461
- addTodo = (text) => {
462
- this.update((state) => ({
463
- ...state,
464
- todos: [...state.todos, {
465
- id: `todo-${Date.now()}-${this.nextId++}`,
466
- text,
467
- done: false
468
- }]
469
- }));
470
- };
471
- toggleTodo = (id) => {
472
- this.update((state) => ({
473
- ...state,
474
- todos: state.todos.map((todo) => todo.id === id ? {
475
- ...todo,
476
- done: !todo.done
477
- } : todo)
478
- }));
479
- };
480
- removeTodo = (id) => {
481
- this.update((state) => ({
482
- ...state,
483
- todos: state.todos.filter((todo) => todo.id !== id)
484
- }));
485
- };
486
- setFilter = (filter) => {
487
- this.patch({ filter });
488
- };
489
- setLoading = (isLoading) => {
490
- this.patch({ isLoading });
491
- };
1108
+ });
1109
+ this.nextId = 0;
1110
+ this.addTodo = (text) => {
1111
+ this.update((state) => ({
1112
+ ...state,
1113
+ todos: [...state.todos, {
1114
+ id: `todo-${Date.now()}-${this.nextId++}`,
1115
+ text,
1116
+ done: false
1117
+ }]
1118
+ }));
1119
+ };
1120
+ this.toggleTodo = (id) => {
1121
+ this.update((state) => ({
1122
+ ...state,
1123
+ todos: state.todos.map((todo) => todo.id === id ? {
1124
+ ...todo,
1125
+ done: !todo.done
1126
+ } : todo)
1127
+ }));
1128
+ };
1129
+ this.removeTodo = (id) => {
1130
+ this.update((state) => ({
1131
+ ...state,
1132
+ todos: state.todos.filter((todo) => todo.id !== id)
1133
+ }));
1134
+ };
1135
+ this.setFilter = (filter) => {
1136
+ this.patch({ filter });
1137
+ };
1138
+ this.setLoading = (isLoading) => {
1139
+ this.patch({ isLoading });
1140
+ };
1141
+ }
492
1142
  get visibleTodos() {
493
1143
  const todos = this.state.todos;
494
1144
  switch (this.state.filter) {
@@ -506,45 +1156,42 @@ var TodoCubit = class extends Cubit {
506
1156
  //#endregion
507
1157
  //#region src/core/Vertex.ts
508
1158
  /**
1159
+ * Vertex - Event-driven state container (Bloc pattern)
1160
+ *
1161
+ * Simplified version that includes its own event handling,
1162
+ * since events are only needed for the Vertex pattern.
1163
+ */
1164
+ /**
509
1165
  * Vertex is an event-driven state container (Bloc pattern)
510
1166
  *
511
1167
  * Includes its own simple event system since events are only needed
512
1168
  * for the Vertex pattern, not for the base StateContainer.
513
1169
  */
514
1170
  var Vertex = class extends StateContainer {
515
- eventHandlers = /* @__PURE__ */ new Map();
516
- isProcessing = false;
517
- eventQueue = [];
518
1171
  /**
519
1172
  * Create a new Vertex
520
1173
  * @param initialState Initial state value
521
- * @param config Configuration options
522
1174
  */
523
- constructor(initialState, config) {
524
- super(initialState, config);
1175
+ constructor(initialState) {
1176
+ super(initialState);
1177
+ this.eventHandlers = /* @__PURE__ */ new Map();
1178
+ this.isProcessing = false;
1179
+ this.eventQueue = [];
1180
+ this.on = (EventClass, handler) => {
1181
+ const className = EventClass.name;
1182
+ const registrations = this.eventHandlers.get(className) || [];
1183
+ registrations.push({
1184
+ eventClass: EventClass,
1185
+ handler
1186
+ });
1187
+ this.eventHandlers.set(className, registrations);
1188
+ };
1189
+ this.add = (event) => {
1190
+ StateContainer._registry.emit("eventAdded", this, event);
1191
+ this.processEvent(event);
1192
+ };
525
1193
  }
526
1194
  /**
527
- * Register an event handler
528
- * Arrow function to maintain correct 'this' binding
529
- */
530
- on = (EventClass, handler) => {
531
- const className = EventClass.name;
532
- const registrations = this.eventHandlers.get(className) || [];
533
- registrations.push({
534
- eventClass: EventClass,
535
- handler
536
- });
537
- this.eventHandlers.set(className, registrations);
538
- };
539
- /**
540
- * Add an event to be processed
541
- * Arrow function to maintain correct 'this' binding in React
542
- */
543
- add = (event) => {
544
- StateContainer._registry.emit("eventAdded", this, event);
545
- this.processEvent(event);
546
- };
547
- /**
548
1195
  * Process an event (synchronously)
549
1196
  */
550
1197
  processEvent(event) {
@@ -586,28 +1233,30 @@ var Vertex = class extends StateContainer {
586
1233
  /**
587
1234
  * Hook for handling event processing errors
588
1235
  */
589
- onEventError(event, error$1) {}
1236
+ onEventError(_event, _error) {}
590
1237
  };
591
1238
  /**
592
1239
  * Example: Counter Events
593
1240
  */
594
1241
  var IncrementEvent = class {
595
- type = "increment";
596
- timestamp = Date.now();
597
1242
  constructor(amount = 1) {
598
1243
  this.amount = amount;
1244
+ this.type = "increment";
1245
+ this.timestamp = Date.now();
599
1246
  }
600
1247
  };
601
1248
  var DecrementEvent = class {
602
- type = "decrement";
603
- timestamp = Date.now();
604
1249
  constructor(amount = 1) {
605
1250
  this.amount = amount;
1251
+ this.type = "decrement";
1252
+ this.timestamp = Date.now();
606
1253
  }
607
1254
  };
608
1255
  var ResetEvent = class {
609
- type = "reset";
610
- timestamp = Date.now();
1256
+ constructor() {
1257
+ this.type = "reset";
1258
+ this.timestamp = Date.now();
1259
+ }
611
1260
  };
612
1261
  /**
613
1262
  * Example: Counter Vertex
@@ -616,6 +1265,15 @@ var ResetEvent = class {
616
1265
  var CounterVertex = class extends Vertex {
617
1266
  constructor() {
618
1267
  super(0);
1268
+ this.increment = (amount = 1) => {
1269
+ this.add(new IncrementEvent(amount));
1270
+ };
1271
+ this.decrement = (amount = 1) => {
1272
+ this.add(new DecrementEvent(amount));
1273
+ };
1274
+ this.reset = () => {
1275
+ this.add(new ResetEvent());
1276
+ };
619
1277
  this.on(IncrementEvent, (event, emit) => {
620
1278
  emit(this.state + event.amount);
621
1279
  });
@@ -626,27 +1284,20 @@ var CounterVertex = class extends Vertex {
626
1284
  emit(0);
627
1285
  });
628
1286
  }
629
- increment = (amount = 1) => {
630
- this.add(new IncrementEvent(amount));
631
- };
632
- decrement = (amount = 1) => {
633
- this.add(new DecrementEvent(amount));
634
- };
635
- reset = () => {
636
- this.add(new ResetEvent());
637
- };
638
1287
  };
639
1288
  var LoginEvent = class {
640
- type = "login";
641
- timestamp = Date.now();
642
1289
  constructor(email, password) {
643
1290
  this.email = email;
644
1291
  this.password = password;
1292
+ this.type = "login";
1293
+ this.timestamp = Date.now();
645
1294
  }
646
1295
  };
647
1296
  var LogoutEvent = class {
648
- type = "logout";
649
- timestamp = Date.now();
1297
+ constructor() {
1298
+ this.type = "logout";
1299
+ this.timestamp = Date.now();
1300
+ }
650
1301
  };
651
1302
  var AuthVertex = class extends Vertex {
652
1303
  constructor() {
@@ -656,6 +1307,12 @@ var AuthVertex = class extends Vertex {
656
1307
  user: null,
657
1308
  error: null
658
1309
  });
1310
+ this.login = (email, password) => {
1311
+ this.add(new LoginEvent(email, password));
1312
+ };
1313
+ this.logout = () => {
1314
+ this.add(new LogoutEvent());
1315
+ };
659
1316
  this.on(LoginEvent, (event, emit) => {
660
1317
  emit({
661
1318
  ...this.state,
@@ -688,35 +1345,19 @@ var AuthVertex = class extends Vertex {
688
1345
  });
689
1346
  });
690
1347
  }
691
- login = (email, password) => {
692
- this.add(new LoginEvent(email, password));
693
- };
694
- logout = () => {
695
- this.add(new LogoutEvent());
696
- };
697
1348
  };
698
1349
 
699
1350
  //#endregion
700
- //#region src/tracking/proxy-tracker.ts
1351
+ //#region src/plugin/BlacPlugin.ts
701
1352
  /**
702
- * Functional proxy tracker for automatic dependency tracking
703
- *
704
- * This module provides a functional approach to proxy-based dependency tracking.
705
- * Unlike the class-based ProxyTracker, this uses a state object pattern that's
706
- * easier to integrate with functional programming patterns and framework hooks.
707
- */
708
- /**
709
- * Array methods that should trigger tracking of the entire array
1353
+ * Type guard for plugins with init
710
1354
  */
711
- const ARRAY_METHODS = new Set([
712
- "map",
713
- "filter",
714
- "forEach",
715
- "find",
716
- "some",
717
- "every",
718
- "reduce"
719
- ]);
1355
+ function hasInitHook(plugin) {
1356
+ return typeof plugin.onInstall === "function";
1357
+ }
1358
+
1359
+ //#endregion
1360
+ //#region src/tracking/proxy-tracker.ts
720
1361
  /**
721
1362
  * Check if a value can be proxied
722
1363
  *
@@ -780,9 +1421,6 @@ function createArrayProxy(state, target, path, depth = 0) {
780
1421
  if (typeof prop === "symbol") return Reflect.get(arr, prop);
781
1422
  const value = Reflect.get(arr, prop);
782
1423
  if (typeof value === "function") {
783
- if (ARRAY_METHODS.has(prop)) {
784
- if (state.isTracking && path) state.trackedPaths.add(path);
785
- }
786
1424
  if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
787
1425
  const cached = state.boundFunctionsCache.get(value);
788
1426
  if (cached) return cached;
@@ -790,9 +1428,15 @@ function createArrayProxy(state, target, path, depth = 0) {
790
1428
  state.boundFunctionsCache.set(value, bound);
791
1429
  return bound;
792
1430
  }
1431
+ if (prop === "length") {
1432
+ if (state.isTracking) {
1433
+ const fullPath$1 = path ? `${path}.length` : "length";
1434
+ state.trackedPaths.add(fullPath$1);
1435
+ }
1436
+ return value;
1437
+ }
793
1438
  let fullPath;
794
- if (prop === "length") fullPath = path ? `${path}.length` : "length";
795
- else if (typeof prop === "string") {
1439
+ if (typeof prop === "string") {
796
1440
  const index = Number(prop);
797
1441
  if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
798
1442
  else fullPath = path ? `${path}.${prop}` : prop;
@@ -834,10 +1478,10 @@ function createProxyInternal(state, target, path = "", depth = 0) {
834
1478
  return bound;
835
1479
  }
836
1480
  const fullPath = path ? `${path}.${String(prop)}` : String(prop);
837
- if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
838
1481
  if (typeof prop === "string" && state.isTracking) {
839
1482
  if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
840
1483
  }
1484
+ if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
841
1485
  return value;
842
1486
  },
843
1487
  has: (obj, prop) => {
@@ -949,13 +1593,45 @@ function shallowEqual(arr1, arr2) {
949
1593
 
950
1594
  //#endregion
951
1595
  //#region src/tracking/dependency-tracker.ts
1596
+ function isChildPath(child, parent) {
1597
+ if (child === parent) return false;
1598
+ return child.startsWith(parent + ".") || child.startsWith(parent + "[");
1599
+ }
952
1600
  /**
953
- * How often to clean up unused paths from the cache
954
- */
955
- const CLEANUP_INTERVAL = 10;
956
- /**
957
- * Create a new tracker state
1601
+ * Extract the parent array path from an array child path
1602
+ * Examples:
1603
+ * 'messages.length' -> 'messages'
1604
+ * 'messages[0]' -> 'messages'
1605
+ * 'messages[0].text' -> 'messages'
1606
+ * 'data.items[0]' -> 'data.items'
958
1607
  */
1608
+ function getArrayParentPath(path) {
1609
+ if (path.endsWith(".length")) return path.slice(0, -7);
1610
+ const arrayIndexMatch = path.match(/^(.+?)\[\d+\]/);
1611
+ if (arrayIndexMatch) return arrayIndexMatch[1];
1612
+ return null;
1613
+ }
1614
+ function optimizeTrackedPaths(paths) {
1615
+ if (paths.size === 0) return /* @__PURE__ */ new Set();
1616
+ if (paths.size === 1) return new Set(paths);
1617
+ const sortedPaths = Array.from(paths).sort((a, b) => b.length - a.length);
1618
+ const optimized = /* @__PURE__ */ new Set();
1619
+ for (const path of sortedPaths) {
1620
+ let hasMoreSpecificChild = false;
1621
+ for (const optimizedPath of optimized) if (isChildPath(optimizedPath, path)) {
1622
+ hasMoreSpecificChild = true;
1623
+ break;
1624
+ }
1625
+ if (!hasMoreSpecificChild) optimized.add(path);
1626
+ }
1627
+ const arrayParents = /* @__PURE__ */ new Set();
1628
+ for (const path of optimized) {
1629
+ const arrayParent = getArrayParentPath(path);
1630
+ if (arrayParent) arrayParents.add(arrayParent);
1631
+ }
1632
+ for (const arrayParent of arrayParents) optimized.add(arrayParent);
1633
+ return optimized;
1634
+ }
959
1635
  function createTrackerState() {
960
1636
  return {
961
1637
  proxyTrackerState: createProxyTrackerState(),
@@ -963,31 +1639,18 @@ function createTrackerState() {
963
1639
  currentRenderPaths: /* @__PURE__ */ new Set(),
964
1640
  pathCache: /* @__PURE__ */ new Map(),
965
1641
  lastCheckedState: null,
966
- lastCheckedValues: /* @__PURE__ */ new Map(),
967
- cleanupCounter: 0
1642
+ lastCheckedValues: /* @__PURE__ */ new Map()
968
1643
  };
969
1644
  }
970
- /**
971
- * Start tracking property accesses
972
- */
973
1645
  function startTracking(tracker) {
974
1646
  startProxyTracking(tracker.proxyTrackerState);
975
1647
  }
976
- /**
977
- * Create a proxy for the given state
978
- */
979
1648
  function createProxy(tracker, state) {
980
1649
  return createProxyForTarget(tracker.proxyTrackerState, state);
981
1650
  }
982
- /**
983
- * Capture the paths that were tracked during a render
984
- *
985
- * This should be called after each render to record which paths were accessed.
986
- * It also performs periodic cleanup of unused paths to prevent memory leaks.
987
- */
988
1651
  function captureTrackedPaths(tracker, state) {
989
1652
  tracker.previousRenderPaths = tracker.currentRenderPaths;
990
- tracker.currentRenderPaths = stopProxyTracking(tracker.proxyTrackerState);
1653
+ tracker.currentRenderPaths = optimizeTrackedPaths(stopProxyTracking(tracker.proxyTrackerState));
991
1654
  if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
992
1655
  const trackedPathsUnion = new Set(tracker.previousRenderPaths);
993
1656
  for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
@@ -1003,19 +1666,8 @@ function captureTrackedPaths(tracker, state) {
1003
1666
  const info$1 = tracker.pathCache.get(path);
1004
1667
  info$1.value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, info$1.segments);
1005
1668
  }
1006
- tracker.cleanupCounter++;
1007
- if (tracker.cleanupCounter >= CLEANUP_INTERVAL) {
1008
- tracker.cleanupCounter = 0;
1009
- for (const path of tracker.pathCache.keys()) if (!trackedPathsUnion.has(path)) tracker.pathCache.delete(path);
1010
- }
1011
1669
  tracker.lastCheckedValues.clear();
1012
1670
  }
1013
- /**
1014
- * Check if any tracked paths have changed
1015
- *
1016
- * This is called when state changes to determine if a re-render is needed.
1017
- * Returns true if any tracked path has a different value than before.
1018
- */
1019
1671
  function hasChanges(tracker, state) {
1020
1672
  if (tracker.pathCache.size === 0) return true;
1021
1673
  tracker.lastCheckedValues.clear();
@@ -1030,11 +1682,6 @@ function hasChanges(tracker, state) {
1030
1682
  tracker.lastCheckedState = state;
1031
1683
  return false;
1032
1684
  }
1033
- /**
1034
- * Check if tracker has any tracked data
1035
- *
1036
- * Returns true if there are any tracked paths or cached paths.
1037
- */
1038
1685
  function hasTrackedData(tracker) {
1039
1686
  return tracker.proxyTrackerState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
1040
1687
  }
@@ -1138,6 +1785,12 @@ function configureLogger(opts) {
1138
1785
  //#endregion
1139
1786
  //#region src/proxy/ProxyTracker.ts
1140
1787
  /**
1788
+ * ProxyTracker - Automatic dependency tracking through Proxies
1789
+ *
1790
+ * Tracks property access on state objects to automatically determine
1791
+ * which parts of the state a component depends on.
1792
+ */
1793
+ /**
1141
1794
  * Check if a value is an object that can be proxied
1142
1795
  */
1143
1796
  function isProxyable$1(value) {
@@ -1147,10 +1800,12 @@ function isProxyable$1(value) {
1147
1800
  * Tracks property access on state objects
1148
1801
  */
1149
1802
  var ProxyTracker = class {
1150
- trackedPaths = /* @__PURE__ */ new Set();
1151
- isTracking = false;
1152
- proxyCache = /* @__PURE__ */ new WeakMap();
1153
- maxDepth = 10;
1803
+ constructor() {
1804
+ this.trackedPaths = /* @__PURE__ */ new Set();
1805
+ this.isTracking = false;
1806
+ this.proxyCache = /* @__PURE__ */ new WeakMap();
1807
+ this.maxDepth = 10;
1808
+ }
1154
1809
  /**
1155
1810
  * Create a proxy that tracks property access
1156
1811
  */
@@ -1202,22 +1857,12 @@ var ProxyTracker = class {
1202
1857
  const proxy = new Proxy(target, { get: (arr, prop) => {
1203
1858
  const value = Reflect.get(arr, prop);
1204
1859
  if (typeof value === "function") {
1205
- if ([
1206
- "map",
1207
- "filter",
1208
- "forEach",
1209
- "find",
1210
- "some",
1211
- "every",
1212
- "reduce"
1213
- ].includes(String(prop))) {
1214
- if (this.isTracking && path) {
1215
- this.trackedPaths.add(path);
1216
- debug("ProxyTracker", "Tracked array method access", {
1217
- path,
1218
- method: String(prop)
1219
- });
1220
- }
1860
+ if (this.isTracking && path && typeof prop === "string") {
1861
+ this.trackedPaths.add(path);
1862
+ debug("ProxyTracker", "Tracked array method access", {
1863
+ path,
1864
+ method: String(prop)
1865
+ });
1221
1866
  }
1222
1867
  return value.bind(arr);
1223
1868
  }
@@ -1320,25 +1965,175 @@ function trackAccess(state, callback) {
1320
1965
  }
1321
1966
 
1322
1967
  //#endregion
1323
- //#region src/constants.ts
1968
+ //#region src/adapter/framework-adapter.ts
1324
1969
  /**
1325
- * Default configuration constants for BlaC
1326
- *
1327
- * Centralized location for all magic numbers and default values.
1970
+ * External dependency manager
1971
+ * Handles subscriptions to external blocs accessed in getters
1328
1972
  */
1973
+ var ExternalDependencyManager = class {
1974
+ constructor() {
1975
+ this.subscriptions = [];
1976
+ this.previousDeps = /* @__PURE__ */ new Set();
1977
+ }
1978
+ /**
1979
+ * Check if dependencies have changed
1980
+ */
1981
+ areDependenciesEqual(oldDeps, newDeps) {
1982
+ if (oldDeps.size !== newDeps.size) return false;
1983
+ for (const dep of newDeps) if (!oldDeps.has(dep)) return false;
1984
+ return true;
1985
+ }
1986
+ /**
1987
+ * Update external subscriptions
1988
+ * @param getterTracker - The getter tracker state
1989
+ * @param rawInstance - The raw bloc instance (exclude from subscriptions)
1990
+ * @param onGetterChange - Callback when external getter changes
1991
+ * @returns Whether subscriptions were updated
1992
+ */
1993
+ updateSubscriptions(getterTracker, rawInstance, onGetterChange) {
1994
+ if (!getterTracker?.externalDependencies) return false;
1995
+ const currentDeps = getterTracker.externalDependencies;
1996
+ if (this.areDependenciesEqual(this.previousDeps, currentDeps)) {
1997
+ clearExternalDependencies(getterTracker);
1998
+ return false;
1999
+ }
2000
+ this.cleanup();
2001
+ for (const externalBloc of currentDeps) {
2002
+ if (externalBloc === rawInstance) continue;
2003
+ const unsub = externalBloc.subscribe(() => {
2004
+ invalidateRenderCache(getterTracker);
2005
+ if (hasGetterChanges(rawInstance, getterTracker)) onGetterChange();
2006
+ });
2007
+ this.subscriptions.push(unsub);
2008
+ }
2009
+ this.previousDeps = new Set(currentDeps);
2010
+ clearExternalDependencies(getterTracker);
2011
+ return true;
2012
+ }
2013
+ /**
2014
+ * Cleanup all subscriptions
2015
+ */
2016
+ cleanup() {
2017
+ this.subscriptions.forEach((unsub) => unsub());
2018
+ this.subscriptions = [];
2019
+ }
2020
+ };
1329
2021
  /**
1330
- * Default configuration constants for BlaC
2022
+ * Create a subscription function for auto-tracking mode
1331
2023
  */
1332
- const BLAC_DEFAULTS = {
1333
- CLEANUP_INTERVAL_MS: 3e4,
1334
- WEAKREF_CLEANUP_INTERVAL_MS: 1e4,
1335
- MAX_SUBSCRIPTIONS: 1e3,
1336
- MAX_SUBSCRIPTIONS_HIGH_PERF: 1e4,
1337
- PIPELINE_TIMEOUT_MS: 5e3,
1338
- CLEANUP_INTERVAL_HIGH_PERF_MS: 5e3,
1339
- MAX_PIPELINE_STAGES: 30
1340
- };
2024
+ function createAutoTrackSubscribe(instance, adapterState) {
2025
+ return (callback) => {
2026
+ return instance.subscribe(() => {
2027
+ const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
2028
+ const hasStateDeps = tracker.pathCache && tracker.pathCache.size > 0;
2029
+ const hasGetterDeps = adapterState.getterTracker && adapterState.getterTracker.trackedGetters.size > 0;
2030
+ const isPrimitiveState = instance.state !== null && typeof instance.state !== "object" && typeof instance.state !== "function";
2031
+ if (!hasStateDeps && !hasGetterDeps && !isPrimitiveState) return;
2032
+ let stateChanged = hasChanges(tracker, instance.state);
2033
+ if (!hasStateDeps && hasGetterDeps) stateChanged = false;
2034
+ if (stateChanged) {
2035
+ callback();
2036
+ return;
2037
+ }
2038
+ if (hasGetterChanges(instance, adapterState.getterTracker)) callback();
2039
+ });
2040
+ };
2041
+ }
2042
+ /**
2043
+ * Create a subscription function for manual dependencies mode
2044
+ */
2045
+ function createManualDepsSubscribe(instance, adapterState, config) {
2046
+ return (callback) => {
2047
+ return instance.subscribe(() => {
2048
+ const newDeps = config.dependencies(instance.state, instance);
2049
+ if (!adapterState.manualDepsCache || !shallowEqual(adapterState.manualDepsCache, newDeps)) {
2050
+ adapterState.manualDepsCache = newDeps;
2051
+ callback();
2052
+ }
2053
+ });
2054
+ };
2055
+ }
2056
+ /**
2057
+ * Create a subscription function for no-tracking mode
2058
+ */
2059
+ function createNoTrackSubscribe(instance) {
2060
+ return (callback) => instance.subscribe(callback);
2061
+ }
2062
+ /**
2063
+ * Create a snapshot function for auto-tracking mode
2064
+ */
2065
+ function createAutoTrackSnapshot(instance, adapterState) {
2066
+ return () => {
2067
+ const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
2068
+ if (hasTrackedData(tracker)) captureTrackedPaths(tracker, instance.state);
2069
+ if (adapterState.getterTracker) {
2070
+ invalidateRenderCache(adapterState.getterTracker);
2071
+ commitTrackedGetters(adapterState.getterTracker);
2072
+ adapterState.getterTracker.isTracking = true;
2073
+ setActiveTracker(instance, adapterState.getterTracker);
2074
+ }
2075
+ startTracking(tracker);
2076
+ return createProxy(tracker, instance.state);
2077
+ };
2078
+ }
2079
+ /**
2080
+ * Create a snapshot function for manual dependencies mode
2081
+ */
2082
+ function createManualDepsSnapshot(instance, adapterState, config) {
2083
+ return () => {
2084
+ adapterState.manualDepsCache = config.dependencies(instance.state, instance);
2085
+ return instance.state;
2086
+ };
2087
+ }
2088
+ /**
2089
+ * Create a snapshot function for no-tracking mode
2090
+ */
2091
+ function createNoTrackSnapshot(instance) {
2092
+ return () => instance.state;
2093
+ }
2094
+ /**
2095
+ * Initialize adapter state for auto-tracking mode
2096
+ */
2097
+ function initAutoTrackState(instance) {
2098
+ return {
2099
+ tracker: null,
2100
+ manualDepsCache: null,
2101
+ getterTracker: createGetterTracker(),
2102
+ proxiedBloc: createBlocProxy(instance)
2103
+ };
2104
+ }
2105
+ /**
2106
+ * Initialize adapter state for manual dependencies mode
2107
+ */
2108
+ function initManualDepsState(instance) {
2109
+ return {
2110
+ tracker: null,
2111
+ manualDepsCache: null,
2112
+ getterTracker: null,
2113
+ proxiedBloc: instance
2114
+ };
2115
+ }
2116
+ /**
2117
+ * Initialize adapter state for no-tracking mode
2118
+ */
2119
+ function initNoTrackState(instance) {
2120
+ return {
2121
+ tracker: null,
2122
+ manualDepsCache: null,
2123
+ getterTracker: null,
2124
+ proxiedBloc: instance
2125
+ };
2126
+ }
2127
+ /**
2128
+ * Disable getter tracking (call after render completes)
2129
+ */
2130
+ function disableGetterTracking(adapterState, rawInstance) {
2131
+ if (adapterState.getterTracker) {
2132
+ adapterState.getterTracker.isTracking = false;
2133
+ clearActiveTracker(rawInstance);
2134
+ }
2135
+ }
1341
2136
 
1342
2137
  //#endregion
1343
- export { AuthVertex, BLAC_DEFAULTS, CounterCubit, CounterVertex, Cubit, DecrementEvent, IncrementEvent, LogLevel, LoginEvent, LogoutEvent, ProxyTracker, ResetEvent, StateContainer, StateContainerRegistry, TodoCubit, Vertex, __resetIdCounters, captureTrackedPaths, configureLogger, createArrayProxy, createIdGenerator, createLogger, createProxy, createProxyForTarget, createProxyInternal, createProxyTrackerState, createTrackerState, debug, error, generateId, generateSimpleId, getValueAtPath, globalProxyTracker, globalRegistry, hasChanges, hasTrackedData, info, isProxyable, parsePath, shallowEqual, startProxyTracking, startTracking, stopProxyTracking, trackAccess, warn };
2138
+ export { AuthVertex, BLAC_DEFAULTS, BLAC_ERROR_PREFIX, BLAC_ID_PATTERNS, BLAC_STATIC_PROPS, CounterCubit, CounterVertex, Cubit, DecrementEvent, ExternalDependencyManager, IncrementEvent, LogLevel, LoginEvent, LogoutEvent, PluginManager, ProxyTracker, ResetEvent, StateContainer, StateContainerRegistry, TodoCubit, Vertex, __resetIdCounters, 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, globalProxyTracker, globalRegistry, hasChanges, hasGetterChanges, hasInitHook, hasTrackedData, info, initAutoTrackState, initManualDepsState, initNoTrackState, invalidateRenderCache, isExcludedFromDevTools, isGetter, isIsolatedClass, isIsolatedKey, isKeepAliveClass, isProxyable, parsePath, resetGetterTracker, setActiveTracker, shallowEqual, startProxyTracking, startTracking, stopProxyTracking, trackAccess, warn };
1344
2139
  //# sourceMappingURL=index.js.map