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

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
@@ -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,656 @@ 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
+ } catch (error$1) {
401
+ tracker.currentlyAccessing.delete(prop);
402
+ throw error$1;
403
+ } finally {
404
+ getterExecutionContext.tracker = prevContext.tracker;
405
+ getterExecutionContext.currentBloc = prevContext.currentBloc;
406
+ getterExecutionContext.depth = prevContext.depth;
407
+ getterExecutionContext.visitedBlocs = prevContext.visitedBlocs;
408
+ }
409
+ }
410
+ return Reflect.get(target, prop, receiver);
411
+ } });
412
+ blocProxyCache.set(bloc, proxy);
413
+ return proxy;
414
+ }
415
+ function hasGetterChanges(bloc, tracker) {
416
+ if (!tracker || tracker.trackedGetters.size === 0) return false;
417
+ tracker.renderCache.clear();
418
+ let hasAnyChange = false;
419
+ for (const prop of tracker.trackedGetters) try {
420
+ const descriptor = getDescriptor(bloc, prop);
421
+ if (!descriptor?.get) continue;
422
+ const newValue = descriptor.get.call(bloc);
423
+ const oldValue = tracker.trackedValues.get(prop);
424
+ tracker.renderCache.set(prop, newValue);
425
+ tracker.trackedValues.set(prop, newValue);
426
+ if (!Object.is(newValue, oldValue)) hasAnyChange = true;
427
+ } catch (error$1) {
428
+ console.warn(`${BLAC_ERROR_PREFIX} Getter "${String(prop)}" threw error during change detection. Stopping tracking for this getter.`, error$1);
429
+ tracker.trackedGetters.delete(prop);
430
+ tracker.trackedValues.delete(prop);
431
+ tracker.cacheValid = false;
432
+ return true;
433
+ }
434
+ tracker.cacheValid = true;
435
+ return hasAnyChange;
436
+ }
437
+ function invalidateRenderCache(tracker) {
438
+ tracker.cacheValid = false;
439
+ }
440
+ function clearExternalDependencies(tracker) {
441
+ tracker.externalDependencies.clear();
442
+ }
443
+ function resetGetterTracker(tracker) {
444
+ tracker.trackedValues.clear();
445
+ tracker.currentlyAccessing.clear();
446
+ tracker.trackedGetters.clear();
447
+ tracker.renderCache.clear();
448
+ tracker.cacheValid = false;
449
+ tracker.isTracking = false;
450
+ tracker.externalDependencies.clear();
451
+ }
452
+
453
+ //#endregion
454
+ //#region src/utils/static-props.ts
455
+ /**
456
+ * Utility functions for accessing static properties on StateContainer classes
457
+ */
458
+ /**
459
+ * Get a static property from a class constructor
460
+ * Type-safe helper that avoids (Type as any) casts
461
+ *
462
+ * @param Type - The class constructor
463
+ * @param propName - The property name to access
464
+ * @param defaultValue - Optional default value if property is undefined
465
+ * @returns The property value or default
466
+ */
467
+ function getStaticProp(Type, propName, defaultValue) {
468
+ return Type[propName] ?? defaultValue;
469
+ }
470
+ /**
471
+ * Check if a class is marked as isolated
472
+ * Isolated classes create separate instances per component
473
+ */
474
+ function isIsolatedClass(Type) {
475
+ return getStaticProp(Type, BLAC_STATIC_PROPS.ISOLATED) === true;
476
+ }
477
+ /**
478
+ * Check if a class is marked as keepAlive
479
+ * KeepAlive classes are never auto-disposed when ref count reaches 0
480
+ */
481
+ function isKeepAliveClass(Type) {
482
+ return getStaticProp(Type, BLAC_STATIC_PROPS.KEEP_ALIVE) === true;
483
+ }
484
+ /**
485
+ * Check if a class should be excluded from DevTools
486
+ * Used to prevent infinite loops when DevTools tracks itself
487
+ */
488
+ function isExcludedFromDevTools(Type) {
489
+ return getStaticProp(Type, BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS) === true;
490
+ }
75
491
 
76
492
  //#endregion
77
493
  //#region src/core/StateContainerRegistry.ts
78
494
  /**
79
- * Registry for managing StateContainer instances
495
+ * Registry for coordinating StateContainer lifecycle and managing instances
496
+ *
497
+ * Centralizes all instance management using WeakMap to ensure proper isolation.
80
498
  */
81
499
  var StateContainerRegistry = class {
82
- instances = /* @__PURE__ */ new Map();
83
- types = /* @__PURE__ */ new Map();
84
- listeners = /* @__PURE__ */ new Map();
500
+ constructor() {
501
+ this.instancesByConstructor = /* @__PURE__ */ new WeakMap();
502
+ this.types = /* @__PURE__ */ new Set();
503
+ this.typeConfigs = /* @__PURE__ */ new Map();
504
+ this.listeners = /* @__PURE__ */ new Map();
505
+ }
85
506
  /**
86
- * Register a type with isolation mode
507
+ * Register a type for tracking
508
+ * Called automatically on first instance creation
509
+ */
510
+ registerType(constructor) {
511
+ this.types.add(constructor);
512
+ }
513
+ /**
514
+ * Register a type with isolation mode (explicit registration)
87
515
  */
88
516
  register(constructor, isolated = false) {
89
517
  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 });
518
+ if (!isolated && isIsolatedClass(constructor)) isolated = true;
519
+ if (this.typeConfigs.has(className)) throw new Error(`${BLAC_ERROR_PREFIX} Type "${className}" is already registered`);
520
+ this.typeConfigs.set(className, { isolated });
521
+ this.registerType(constructor);
93
522
  }
94
523
  /**
95
- * Get or create a StateContainer instance
96
- * For isolated: always creates new
97
- * For shared: singleton per key
524
+ * Get the instances Map for a specific class
525
+ * Creates a new Map on first access.
98
526
  */
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);
527
+ ensureInstancesMap(Type) {
528
+ let instances = this.instancesByConstructor.get(Type);
529
+ if (!instances) {
530
+ instances = /* @__PURE__ */ new Map();
531
+ this.instancesByConstructor.set(Type, instances);
106
532
  }
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;
533
+ return instances;
534
+ }
535
+ /**
536
+ * Get the instances Map for a specific class (public API for stats/debugging)
537
+ */
538
+ getInstancesMap(Type) {
539
+ return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
540
+ }
541
+ /**
542
+ * Resolve an instance with ref counting (ownership semantics)
543
+ */
544
+ resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
545
+ const registryConfig = this.typeConfigs.get(Type.name);
546
+ const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
547
+ const config = { instanceId: instanceKey };
548
+ if (isolated) {
549
+ const instance$1 = new Type(constructorArgs);
550
+ instance$1.initConfig(config);
551
+ this.registerType(Type);
552
+ return instance$1;
113
553
  }
114
- const instance = new constructor(...args);
115
- this.instances.set(instanceKey, {
554
+ const instances = this.ensureInstancesMap(Type);
555
+ const entry = instances.get(instanceKey);
556
+ if (entry) {
557
+ entry.refCount++;
558
+ return entry.instance;
559
+ }
560
+ const instance = new Type(constructorArgs);
561
+ instance.initConfig(config);
562
+ instances.set(instanceKey, {
116
563
  instance,
117
564
  refCount: 1
118
565
  });
566
+ this.registerType(Type);
119
567
  return instance;
120
568
  }
121
569
  /**
570
+ * Get an existing instance without ref counting (borrowing semantics)
571
+ * @throws Error if instance doesn't exist
572
+ */
573
+ get(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
574
+ const entry = this.ensureInstancesMap(Type).get(instanceKey);
575
+ 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.`);
576
+ const context = getGetterExecutionContext();
577
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
578
+ return entry.instance;
579
+ }
580
+ /**
581
+ * Safely get an existing instance (borrowing semantics with error handling)
582
+ * Returns discriminated union for type-safe conditional access
583
+ */
584
+ getSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
585
+ const entry = this.ensureInstancesMap(Type).get(instanceKey);
586
+ if (!entry) return {
587
+ error: /* @__PURE__ */ new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found`),
588
+ instance: null
589
+ };
590
+ const context = getGetterExecutionContext();
591
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
592
+ return {
593
+ error: null,
594
+ instance: entry.instance
595
+ };
596
+ }
597
+ /**
598
+ * Connect to an instance with borrowing semantics (for B2B communication)
599
+ * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
600
+ * Tracks cross-bloc dependency for reactive updates.
601
+ *
602
+ * Use this in bloc-to-bloc communication when you need to ensure an instance exists
603
+ * but don't want to claim ownership (no ref count increment).
604
+ *
605
+ * @param Type - The bloc class constructor
606
+ * @param instanceKey - Optional instance key (defaults to 'default')
607
+ * @param constructorArgs - Constructor arguments (only used if creating new instance)
608
+ * @returns The bloc instance
609
+ */
610
+ connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
611
+ const registryConfig = this.typeConfigs.get(Type.name);
612
+ 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.`);
613
+ const instances = this.ensureInstancesMap(Type);
614
+ let entry = instances.get(instanceKey);
615
+ if (!entry) {
616
+ const instance = new Type(constructorArgs);
617
+ const config = { instanceId: instanceKey };
618
+ instance.initConfig(config);
619
+ entry = {
620
+ instance,
621
+ refCount: 1
622
+ };
623
+ instances.set(instanceKey, entry);
624
+ this.registerType(Type);
625
+ }
626
+ const context = getGetterExecutionContext();
627
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
628
+ return entry.instance;
629
+ }
630
+ /**
122
631
  * Release a reference to an instance
123
- * Disposes when ref count reaches zero (unless static keepAlive is true)
124
632
  */
125
- release(constructor, key, forceDispose = false) {
126
- const instanceKey = `${constructor.name}:${key || "default"}`;
127
- const entry = this.instances.get(instanceKey);
633
+ release(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, forceDispose = false) {
634
+ const instances = this.ensureInstancesMap(Type);
635
+ const entry = instances.get(instanceKey);
128
636
  if (!entry) return;
129
637
  if (forceDispose) {
130
- entry.instance.dispose();
131
- this.instances.delete(instanceKey);
638
+ if (!entry.instance.isDisposed) entry.instance.dispose();
639
+ instances.delete(instanceKey);
132
640
  return;
133
641
  }
134
642
  entry.refCount--;
135
- const keepAlive = constructor.keepAlive === true;
643
+ const keepAlive = isKeepAliveClass(Type);
136
644
  if (entry.refCount <= 0 && !keepAlive) {
137
- entry.instance.dispose();
138
- this.instances.delete(instanceKey);
645
+ if (!entry.instance.isDisposed) entry.instance.dispose();
646
+ instances.delete(instanceKey);
139
647
  }
140
648
  }
141
649
  /**
142
- * Get all instances of a type (shared only, isolated aren't tracked)
650
+ * Get all instances of a specific type
143
651
  */
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;
652
+ getAll(Type) {
653
+ const instances = this.ensureInstancesMap(Type);
654
+ const result = [];
655
+ for (const entry of instances.values()) result.push(entry.instance);
656
+ return result;
149
657
  }
150
658
  /**
151
- * Clear all instances of a type
659
+ * Safely iterate over all instances of a type
152
660
  */
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);
661
+ forEach(Type, callback) {
662
+ const instances = this.ensureInstancesMap(Type);
663
+ for (const entry of instances.values()) {
664
+ const instance = entry.instance;
665
+ if (!instance.isDisposed) try {
666
+ callback(instance);
667
+ } catch (error$1) {
668
+ console.error(`${BLAC_ERROR_PREFIX} forEach callback error for ${Type.name}:`, error$1);
669
+ }
159
670
  }
160
- for (const key of toDelete) this.instances.delete(key);
671
+ }
672
+ /**
673
+ * Clear all instances of a specific type
674
+ */
675
+ clear(Type) {
676
+ const instances = this.ensureInstancesMap(Type);
677
+ for (const entry of instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
678
+ instances.clear();
679
+ }
680
+ /**
681
+ * Get reference count for an instance
682
+ */
683
+ getRefCount(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
684
+ return this.ensureInstancesMap(Type).get(instanceKey)?.refCount ?? 0;
685
+ }
686
+ /**
687
+ * Check if an instance exists
688
+ */
689
+ hasInstance(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
690
+ return this.ensureInstancesMap(Type).has(instanceKey);
161
691
  }
162
692
  /**
163
693
  * Clear all instances from all types (for testing)
694
+ *
695
+ * Iterates all registered types and clears their instances.
696
+ * Also clears type tracking to reset the registry state.
164
697
  */
165
698
  clearAll() {
166
- for (const entry of this.instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
167
- this.instances.clear();
699
+ for (const Type of this.types) this.clear(Type);
168
700
  this.types.clear();
701
+ this.typeConfigs.clear();
169
702
  }
170
703
  /**
171
704
  * Get registry statistics (for debugging)
172
705
  */
173
706
  getStats() {
174
707
  const typeBreakdown = {};
175
- for (const key of this.instances.keys()) {
176
- const typeName = key.split(":")[0];
177
- typeBreakdown[typeName] = (typeBreakdown[typeName] || 0) + 1;
708
+ let totalInstances = 0;
709
+ for (const Type of this.types) {
710
+ const typeName = Type.name;
711
+ const count = this.getInstancesMap(Type).size;
712
+ typeBreakdown[typeName] = count;
713
+ totalInstances += count;
178
714
  }
179
715
  return {
180
716
  registeredTypes: this.types.size,
181
- totalInstances: this.instances.size,
717
+ totalInstances,
182
718
  typeBreakdown
183
719
  };
184
720
  }
185
721
  /**
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
722
+ * Get all registered types (for plugin system)
194
723
  */
195
- hasInstance(constructor, key) {
196
- const instanceKey = `${constructor.name}:${key || "default"}`;
197
- return this.instances.has(instanceKey);
724
+ getTypes() {
725
+ return Array.from(this.types);
198
726
  }
199
727
  /**
200
728
  * Subscribe to lifecycle events
@@ -219,7 +747,7 @@ var StateContainerRegistry = class {
219
747
  for (const listener of listeners) try {
220
748
  listener(...args);
221
749
  } catch (error$1) {
222
- console.error(`[BlaC] Listener error for '${event}':`, error$1);
750
+ console.error(`${BLAC_ERROR_PREFIX} Listener error for '${event}':`, error$1);
223
751
  }
224
752
  }
225
753
  };
@@ -227,18 +755,51 @@ var StateContainerRegistry = class {
227
755
  * Global default registry instance
228
756
  */
229
757
  const globalRegistry = new StateContainerRegistry();
758
+ /**
759
+ * Global plugin manager (initialized lazily)
760
+ */
761
+ let _globalPluginManager = null;
762
+ /**
763
+ * Get the global plugin manager
764
+ */
765
+ function getPluginManager() {
766
+ if (!_globalPluginManager) _globalPluginManager = createPluginManager(globalRegistry);
767
+ return _globalPluginManager;
768
+ }
230
769
 
231
770
  //#endregion
232
771
  //#region src/core/StateContainer.ts
233
772
  /**
773
+ * StateContainer - Clean, minimal state management container
774
+ *
775
+ * Responsibilities:
776
+ * - State storage and updates
777
+ * - Change notifications to subscribers
778
+ * - Lifecycle management
779
+ */
780
+ /**
234
781
  * Base abstract class for all state containers
235
782
  */
236
783
  var StateContainer = class StateContainer {
237
- static _registry = globalRegistry;
784
+ static {
785
+ this.__excludeFromDevTools = false;
786
+ }
787
+ static {
788
+ this._registry = globalRegistry;
789
+ }
790
+ /**
791
+ * Get the global registry (mainly for testing)
792
+ */
793
+ static getRegistry() {
794
+ return StateContainer._registry;
795
+ }
238
796
  /**
239
797
  * Set a custom registry (mainly for testing)
798
+ *
799
+ * Clears all instances before switching to ensure clean test isolation.
240
800
  */
241
801
  static setRegistry(registry) {
802
+ StateContainer._registry.clearAll();
242
803
  StateContainer._registry = registry;
243
804
  }
244
805
  /**
@@ -248,75 +809,123 @@ var StateContainer = class StateContainer {
248
809
  StateContainer._registry.register(this, isolated);
249
810
  }
250
811
  /**
251
- * Get or create an instance
252
- * @param key - Optional instance key for shared instances
253
- * @param args - Constructor arguments
812
+ * Resolve an instance with ref counting (ownership semantics)
813
+ *
814
+ * Delegates to the global registry for instance management.
815
+ *
816
+ * @param instanceKey - Optional instance key for shared instances
817
+ * @param constructorArgs - Constructor arguments
818
+ * @returns Instance with incremented ref count
819
+ */
820
+ static resolve(instanceKey, constructorArgs) {
821
+ return StateContainer._registry.resolve(this, instanceKey, constructorArgs);
822
+ }
823
+ /**
824
+ * Get an existing instance without ref counting (borrowing semantics)
825
+ * Delegates to the global registry.
826
+ */
827
+ static get(instanceKey) {
828
+ return StateContainer._registry.get(this, instanceKey);
829
+ }
830
+ /**
831
+ * Safely get an existing instance (borrowing semantics with error handling)
832
+ * Delegates to the global registry.
833
+ */
834
+ static getSafe(instanceKey) {
835
+ return StateContainer._registry.getSafe(this, instanceKey);
836
+ }
837
+ /**
838
+ * Connect to an instance with borrowing semantics (for B2B communication)
839
+ * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
840
+ * Tracks cross-bloc dependency for reactive updates.
841
+ *
842
+ * Use this in bloc-to-bloc communication when you need to ensure an instance exists
843
+ * but don't want to claim ownership (no ref count increment).
844
+ *
845
+ * Delegates to the global registry.
846
+ *
847
+ * @param instanceKey - Optional instance key (defaults to 'default')
848
+ * @param constructorArgs - Constructor arguments (only used if creating new instance)
849
+ * @returns The bloc instance
254
850
  */
255
- static getOrCreate(key, ...args) {
256
- return StateContainer._registry.getOrCreate(this, key, ...args);
851
+ static connect(instanceKey, constructorArgs) {
852
+ return StateContainer._registry.connect(this, instanceKey, constructorArgs);
257
853
  }
258
854
  /**
259
855
  * Release a reference to an instance
260
- * @param key - Optional instance key
261
- * @param forceDispose - Force dispose regardless of ref count or keepAlive
856
+ * Delegates to the global registry.
262
857
  */
263
- static release(key, forceDispose = false) {
264
- StateContainer._registry.release(this, key, forceDispose);
858
+ static release(instanceKey, forceDispose = false) {
859
+ StateContainer._registry.release(this, instanceKey, forceDispose);
265
860
  }
266
861
  /**
267
862
  * Get all instances of this type
863
+ * Delegates to the global registry.
268
864
  */
269
865
  static getAll() {
270
866
  return StateContainer._registry.getAll(this);
271
867
  }
272
868
  /**
869
+ * Safely iterate over all instances of this type
870
+ * Delegates to the global registry.
871
+ */
872
+ static forEach(callback) {
873
+ StateContainer._registry.forEach(this, callback);
874
+ }
875
+ /**
273
876
  * Clear all instances of this type
877
+ * Delegates to the global registry.
274
878
  */
275
879
  static clear() {
276
880
  StateContainer._registry.clear(this);
277
881
  }
278
882
  /**
279
883
  * Clear all instances from all types (for testing)
884
+ * Delegates to the global registry.
280
885
  */
281
886
  static clearAllInstances() {
282
887
  StateContainer._registry.clearAll();
283
888
  }
284
889
  /**
285
890
  * Get registry statistics (for debugging)
891
+ * Delegates to the global registry.
286
892
  */
287
893
  static getStats() {
288
894
  return StateContainer._registry.getStats();
289
895
  }
290
896
  /**
291
897
  * Get reference count for an instance
292
- * @param key - Optional instance key
898
+ * Delegates to the global registry.
293
899
  */
294
- static getRefCount(key) {
295
- return StateContainer._registry.getRefCount(this, key);
900
+ static getRefCount(instanceKey) {
901
+ return StateContainer._registry.getRefCount(this, instanceKey);
296
902
  }
297
903
  /**
298
904
  * Check if an instance exists
299
- * @param key - Optional instance key
905
+ * Delegates to the global registry.
300
906
  */
301
- static hasInstance(key) {
302
- return StateContainer._registry.hasInstance(this, key);
907
+ static hasInstance(instanceKey) {
908
+ return StateContainer._registry.hasInstance(this, instanceKey);
303
909
  }
304
- _state;
305
- listeners = /* @__PURE__ */ new Set();
306
- _disposed = false;
307
- config;
308
- name;
309
- debug;
310
- instanceId;
311
910
  /**
312
911
  * Create a new StateContainer
313
912
  */
314
- constructor(initialState, config = {}) {
913
+ constructor(initialState) {
914
+ this.listeners = /* @__PURE__ */ new Set();
915
+ this._disposed = false;
916
+ this.config = {};
917
+ this.name = this.constructor.name;
918
+ this.debug = false;
919
+ this.instanceId = generateSimpleId(this.constructor.name, "main");
920
+ this.createdAt = Date.now();
921
+ this.lastUpdateTimestamp = Date.now();
315
922
  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);
923
+ }
924
+ initConfig(config) {
925
+ this.config = { ...config };
926
+ this.name = this.config.name || this.constructor.name;
927
+ this.debug = this.config.debug ?? false;
928
+ this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
320
929
  StateContainer._registry.emit("created", this);
321
930
  }
322
931
  /**
@@ -366,7 +975,61 @@ var StateContainer = class StateContainer {
366
975
  } catch (error$1) {
367
976
  console.error(`[${this.name}] Error in listener:`, error$1);
368
977
  }
369
- StateContainer._registry.emit("stateChanged", this, previousState, newState);
978
+ const stackTrace = this.captureStackTrace();
979
+ StateContainer._registry.emit("stateChanged", this, previousState, newState, stackTrace);
980
+ this.lastUpdateTimestamp = Date.now();
981
+ }
982
+ static {
983
+ this.enableStackTrace = true;
984
+ }
985
+ /**
986
+ * Capture stack trace for debugging
987
+ * Can be overridden to disable in production
988
+ */
989
+ captureStackTrace() {
990
+ if (!StateContainer.enableStackTrace || typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
991
+ try {
992
+ const relevantLines = ((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(1);
993
+ const formattedLines = [];
994
+ for (const line of relevantLines) {
995
+ if (!line.trim()) continue;
996
+ if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("Cubit.patch") || line.includes("Vertex.")) continue;
997
+ 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;
998
+ const formatted = this.formatStackLine(line);
999
+ if (formatted) formattedLines.push(formatted);
1000
+ }
1001
+ return formattedLines.join("\n");
1002
+ } catch {
1003
+ return "";
1004
+ }
1005
+ }
1006
+ /**
1007
+ * Format a single stack trace line for better readability
1008
+ * Removes URL prefixes, query params, and cleans up the output
1009
+ */
1010
+ formatStackLine(line) {
1011
+ const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
1012
+ if (!match) {
1013
+ const simpleMatch = line.match(/at\s+(.+?):(\d+):(\d+)/);
1014
+ if (simpleMatch) {
1015
+ const [, url$1, lineNum$1, col$1] = simpleMatch;
1016
+ return ` at ${this.cleanFilePath(url$1)}:${lineNum$1}:${col$1}`;
1017
+ }
1018
+ return null;
1019
+ }
1020
+ const [, functionName, url, lineNum, col] = match;
1021
+ return ` at ${functionName} (${this.cleanFilePath(url)}:${lineNum}:${col})`;
1022
+ }
1023
+ /**
1024
+ * Clean up file path by removing URL prefixes and query params
1025
+ */
1026
+ cleanFilePath(url) {
1027
+ let path = url.replace(/http:\/\/localhost:\d+\/@fs/, "").replace(/http:\/\/localhost:\d+\//, "").replace(/\?t=\d+/, "").replace(/\?v=[a-f0-9]+/, "");
1028
+ const projectMatch = path.match(/\/Projects\/blac\/(.+)/);
1029
+ if (projectMatch) path = projectMatch[1];
1030
+ const segments = path.split("/");
1031
+ if (segments.length > 3) path = segments.slice(-3).join("/");
1032
+ return path;
370
1033
  }
371
1034
  /**
372
1035
  * Update state using a function
@@ -380,6 +1043,12 @@ var StateContainer = class StateContainer {
380
1043
  //#endregion
381
1044
  //#region src/core/Cubit.ts
382
1045
  /**
1046
+ * Cubit - Simple state container with direct state emission
1047
+ *
1048
+ * This validates that our StateContainer design can support
1049
+ * the simple Cubit pattern from the original BlaC.
1050
+ */
1051
+ /**
383
1052
  * Cubit is a simple state container that allows direct state emission
384
1053
  *
385
1054
  * Unlike StateContainer (where emit is protected), Cubit exposes emit and update
@@ -389,10 +1058,16 @@ var Cubit = class extends StateContainer {
389
1058
  /**
390
1059
  * Create a new Cubit
391
1060
  * @param initialState Initial state value
392
- * @param config Configuration options
393
1061
  */
394
- constructor(initialState, config) {
395
- super(initialState, config);
1062
+ constructor(initialState) {
1063
+ super(initialState);
1064
+ this.patch = ((partial) => {
1065
+ if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
1066
+ this.update((current) => ({
1067
+ ...current,
1068
+ ...partial
1069
+ }));
1070
+ });
396
1071
  }
397
1072
  /**
398
1073
  * Emit a new state (public override of protected parent method)
@@ -406,89 +1081,70 @@ var Cubit = class extends StateContainer {
406
1081
  update(updater) {
407
1082
  super["update"](updater);
408
1083
  }
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
1084
  };
431
1085
  /**
432
1086
  * Example: Counter Cubit
433
1087
  */
434
1088
  var CounterCubit = class extends Cubit {
435
1089
  constructor() {
436
- super(0, { name: "CounterCubit" });
1090
+ super(0);
1091
+ this.increment = () => {
1092
+ this.emit(this.state + 1);
1093
+ };
1094
+ this.decrement = () => {
1095
+ this.emit(this.state - 1);
1096
+ };
1097
+ this.reset = () => {
1098
+ this.emit(0);
1099
+ };
1100
+ this.addAmount = (amount) => {
1101
+ this.emit(this.state + amount);
1102
+ };
437
1103
  }
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
1104
  };
451
1105
  var TodoCubit = class extends Cubit {
452
- static keepAlive = true;
453
- nextId = 0;
1106
+ static {
1107
+ this.keepAlive = true;
1108
+ }
454
1109
  constructor() {
455
1110
  super({
456
1111
  todos: [],
457
1112
  filter: "all",
458
1113
  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
- };
1114
+ });
1115
+ this.nextId = 0;
1116
+ this.addTodo = (text) => {
1117
+ this.update((state) => ({
1118
+ ...state,
1119
+ todos: [...state.todos, {
1120
+ id: `todo-${Date.now()}-${this.nextId++}`,
1121
+ text,
1122
+ done: false
1123
+ }]
1124
+ }));
1125
+ };
1126
+ this.toggleTodo = (id) => {
1127
+ this.update((state) => ({
1128
+ ...state,
1129
+ todos: state.todos.map((todo) => todo.id === id ? {
1130
+ ...todo,
1131
+ done: !todo.done
1132
+ } : todo)
1133
+ }));
1134
+ };
1135
+ this.removeTodo = (id) => {
1136
+ this.update((state) => ({
1137
+ ...state,
1138
+ todos: state.todos.filter((todo) => todo.id !== id)
1139
+ }));
1140
+ };
1141
+ this.setFilter = (filter) => {
1142
+ this.patch({ filter });
1143
+ };
1144
+ this.setLoading = (isLoading) => {
1145
+ this.patch({ isLoading });
1146
+ };
1147
+ }
492
1148
  get visibleTodos() {
493
1149
  const todos = this.state.todos;
494
1150
  switch (this.state.filter) {
@@ -506,45 +1162,42 @@ var TodoCubit = class extends Cubit {
506
1162
  //#endregion
507
1163
  //#region src/core/Vertex.ts
508
1164
  /**
1165
+ * Vertex - Event-driven state container (Bloc pattern)
1166
+ *
1167
+ * Simplified version that includes its own event handling,
1168
+ * since events are only needed for the Vertex pattern.
1169
+ */
1170
+ /**
509
1171
  * Vertex is an event-driven state container (Bloc pattern)
510
1172
  *
511
1173
  * Includes its own simple event system since events are only needed
512
1174
  * for the Vertex pattern, not for the base StateContainer.
513
1175
  */
514
1176
  var Vertex = class extends StateContainer {
515
- eventHandlers = /* @__PURE__ */ new Map();
516
- isProcessing = false;
517
- eventQueue = [];
518
1177
  /**
519
1178
  * Create a new Vertex
520
1179
  * @param initialState Initial state value
521
- * @param config Configuration options
522
1180
  */
523
- constructor(initialState, config) {
524
- super(initialState, config);
1181
+ constructor(initialState) {
1182
+ super(initialState);
1183
+ this.eventHandlers = /* @__PURE__ */ new Map();
1184
+ this.isProcessing = false;
1185
+ this.eventQueue = [];
1186
+ this.on = (EventClass, handler) => {
1187
+ const className = EventClass.name;
1188
+ const registrations = this.eventHandlers.get(className) || [];
1189
+ registrations.push({
1190
+ eventClass: EventClass,
1191
+ handler
1192
+ });
1193
+ this.eventHandlers.set(className, registrations);
1194
+ };
1195
+ this.add = (event) => {
1196
+ StateContainer._registry.emit("eventAdded", this, event);
1197
+ this.processEvent(event);
1198
+ };
525
1199
  }
526
1200
  /**
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
1201
  * Process an event (synchronously)
549
1202
  */
550
1203
  processEvent(event) {
@@ -586,28 +1239,30 @@ var Vertex = class extends StateContainer {
586
1239
  /**
587
1240
  * Hook for handling event processing errors
588
1241
  */
589
- onEventError(event, error$1) {}
1242
+ onEventError(_event, _error) {}
590
1243
  };
591
1244
  /**
592
1245
  * Example: Counter Events
593
1246
  */
594
1247
  var IncrementEvent = class {
595
- type = "increment";
596
- timestamp = Date.now();
597
1248
  constructor(amount = 1) {
598
1249
  this.amount = amount;
1250
+ this.type = "increment";
1251
+ this.timestamp = Date.now();
599
1252
  }
600
1253
  };
601
1254
  var DecrementEvent = class {
602
- type = "decrement";
603
- timestamp = Date.now();
604
1255
  constructor(amount = 1) {
605
1256
  this.amount = amount;
1257
+ this.type = "decrement";
1258
+ this.timestamp = Date.now();
606
1259
  }
607
1260
  };
608
1261
  var ResetEvent = class {
609
- type = "reset";
610
- timestamp = Date.now();
1262
+ constructor() {
1263
+ this.type = "reset";
1264
+ this.timestamp = Date.now();
1265
+ }
611
1266
  };
612
1267
  /**
613
1268
  * Example: Counter Vertex
@@ -616,6 +1271,15 @@ var ResetEvent = class {
616
1271
  var CounterVertex = class extends Vertex {
617
1272
  constructor() {
618
1273
  super(0);
1274
+ this.increment = (amount = 1) => {
1275
+ this.add(new IncrementEvent(amount));
1276
+ };
1277
+ this.decrement = (amount = 1) => {
1278
+ this.add(new DecrementEvent(amount));
1279
+ };
1280
+ this.reset = () => {
1281
+ this.add(new ResetEvent());
1282
+ };
619
1283
  this.on(IncrementEvent, (event, emit) => {
620
1284
  emit(this.state + event.amount);
621
1285
  });
@@ -626,27 +1290,20 @@ var CounterVertex = class extends Vertex {
626
1290
  emit(0);
627
1291
  });
628
1292
  }
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
1293
  };
639
1294
  var LoginEvent = class {
640
- type = "login";
641
- timestamp = Date.now();
642
1295
  constructor(email, password) {
643
1296
  this.email = email;
644
1297
  this.password = password;
1298
+ this.type = "login";
1299
+ this.timestamp = Date.now();
645
1300
  }
646
1301
  };
647
1302
  var LogoutEvent = class {
648
- type = "logout";
649
- timestamp = Date.now();
1303
+ constructor() {
1304
+ this.type = "logout";
1305
+ this.timestamp = Date.now();
1306
+ }
650
1307
  };
651
1308
  var AuthVertex = class extends Vertex {
652
1309
  constructor() {
@@ -656,6 +1313,12 @@ var AuthVertex = class extends Vertex {
656
1313
  user: null,
657
1314
  error: null
658
1315
  });
1316
+ this.login = (email, password) => {
1317
+ this.add(new LoginEvent(email, password));
1318
+ };
1319
+ this.logout = () => {
1320
+ this.add(new LogoutEvent());
1321
+ };
659
1322
  this.on(LoginEvent, (event, emit) => {
660
1323
  emit({
661
1324
  ...this.state,
@@ -688,14 +1351,17 @@ var AuthVertex = class extends Vertex {
688
1351
  });
689
1352
  });
690
1353
  }
691
- login = (email, password) => {
692
- this.add(new LoginEvent(email, password));
693
- };
694
- logout = () => {
695
- this.add(new LogoutEvent());
696
- };
697
1354
  };
698
1355
 
1356
+ //#endregion
1357
+ //#region src/plugin/BlacPlugin.ts
1358
+ /**
1359
+ * Type guard for plugins with init
1360
+ */
1361
+ function hasInitHook(plugin) {
1362
+ return typeof plugin.onInstall === "function";
1363
+ }
1364
+
699
1365
  //#endregion
700
1366
  //#region src/tracking/proxy-tracker.ts
701
1367
  /**
@@ -706,18 +1372,6 @@ var AuthVertex = class extends Vertex {
706
1372
  * easier to integrate with functional programming patterns and framework hooks.
707
1373
  */
708
1374
  /**
709
- * Array methods that should trigger tracking of the entire array
710
- */
711
- const ARRAY_METHODS = new Set([
712
- "map",
713
- "filter",
714
- "forEach",
715
- "find",
716
- "some",
717
- "every",
718
- "reduce"
719
- ]);
720
- /**
721
1375
  * Check if a value can be proxied
722
1376
  *
723
1377
  * Returns true for plain objects and arrays only.
@@ -780,9 +1434,6 @@ function createArrayProxy(state, target, path, depth = 0) {
780
1434
  if (typeof prop === "symbol") return Reflect.get(arr, prop);
781
1435
  const value = Reflect.get(arr, prop);
782
1436
  if (typeof value === "function") {
783
- if (ARRAY_METHODS.has(prop)) {
784
- if (state.isTracking && path) state.trackedPaths.add(path);
785
- }
786
1437
  if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
787
1438
  const cached = state.boundFunctionsCache.get(value);
788
1439
  if (cached) return cached;
@@ -790,9 +1441,15 @@ function createArrayProxy(state, target, path, depth = 0) {
790
1441
  state.boundFunctionsCache.set(value, bound);
791
1442
  return bound;
792
1443
  }
1444
+ if (prop === "length") {
1445
+ if (state.isTracking) {
1446
+ const fullPath$1 = path ? `${path}.length` : "length";
1447
+ state.trackedPaths.add(fullPath$1);
1448
+ }
1449
+ return value;
1450
+ }
793
1451
  let fullPath;
794
- if (prop === "length") fullPath = path ? `${path}.length` : "length";
795
- else if (typeof prop === "string") {
1452
+ if (typeof prop === "string") {
796
1453
  const index = Number(prop);
797
1454
  if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
798
1455
  else fullPath = path ? `${path}.${prop}` : prop;
@@ -834,10 +1491,10 @@ function createProxyInternal(state, target, path = "", depth = 0) {
834
1491
  return bound;
835
1492
  }
836
1493
  const fullPath = path ? `${path}.${String(prop)}` : String(prop);
837
- if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
838
1494
  if (typeof prop === "string" && state.isTracking) {
839
1495
  if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
840
1496
  }
1497
+ if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
841
1498
  return value;
842
1499
  },
843
1500
  has: (obj, prop) => {
@@ -949,13 +1606,45 @@ function shallowEqual(arr1, arr2) {
949
1606
 
950
1607
  //#endregion
951
1608
  //#region src/tracking/dependency-tracker.ts
1609
+ function isChildPath(child, parent) {
1610
+ if (child === parent) return false;
1611
+ return child.startsWith(parent + ".") || child.startsWith(parent + "[");
1612
+ }
952
1613
  /**
953
- * How often to clean up unused paths from the cache
954
- */
955
- const CLEANUP_INTERVAL = 10;
956
- /**
957
- * Create a new tracker state
1614
+ * Extract the parent array path from an array child path
1615
+ * Examples:
1616
+ * 'messages.length' -> 'messages'
1617
+ * 'messages[0]' -> 'messages'
1618
+ * 'messages[0].text' -> 'messages'
1619
+ * 'data.items[0]' -> 'data.items'
958
1620
  */
1621
+ function getArrayParentPath(path) {
1622
+ if (path.endsWith(".length")) return path.slice(0, -7);
1623
+ const arrayIndexMatch = path.match(/^(.+?)\[\d+\]/);
1624
+ if (arrayIndexMatch) return arrayIndexMatch[1];
1625
+ return null;
1626
+ }
1627
+ function optimizeTrackedPaths(paths) {
1628
+ if (paths.size === 0) return /* @__PURE__ */ new Set();
1629
+ if (paths.size === 1) return new Set(paths);
1630
+ const sortedPaths = Array.from(paths).sort((a, b) => b.length - a.length);
1631
+ const optimized = /* @__PURE__ */ new Set();
1632
+ for (const path of sortedPaths) {
1633
+ let hasMoreSpecificChild = false;
1634
+ for (const optimizedPath of optimized) if (isChildPath(optimizedPath, path)) {
1635
+ hasMoreSpecificChild = true;
1636
+ break;
1637
+ }
1638
+ if (!hasMoreSpecificChild) optimized.add(path);
1639
+ }
1640
+ const arrayParents = /* @__PURE__ */ new Set();
1641
+ for (const path of optimized) {
1642
+ const arrayParent = getArrayParentPath(path);
1643
+ if (arrayParent) arrayParents.add(arrayParent);
1644
+ }
1645
+ for (const arrayParent of arrayParents) optimized.add(arrayParent);
1646
+ return optimized;
1647
+ }
959
1648
  function createTrackerState() {
960
1649
  return {
961
1650
  proxyTrackerState: createProxyTrackerState(),
@@ -963,31 +1652,18 @@ function createTrackerState() {
963
1652
  currentRenderPaths: /* @__PURE__ */ new Set(),
964
1653
  pathCache: /* @__PURE__ */ new Map(),
965
1654
  lastCheckedState: null,
966
- lastCheckedValues: /* @__PURE__ */ new Map(),
967
- cleanupCounter: 0
1655
+ lastCheckedValues: /* @__PURE__ */ new Map()
968
1656
  };
969
1657
  }
970
- /**
971
- * Start tracking property accesses
972
- */
973
1658
  function startTracking(tracker) {
974
1659
  startProxyTracking(tracker.proxyTrackerState);
975
1660
  }
976
- /**
977
- * Create a proxy for the given state
978
- */
979
1661
  function createProxy(tracker, state) {
980
1662
  return createProxyForTarget(tracker.proxyTrackerState, state);
981
1663
  }
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
1664
  function captureTrackedPaths(tracker, state) {
989
1665
  tracker.previousRenderPaths = tracker.currentRenderPaths;
990
- tracker.currentRenderPaths = stopProxyTracking(tracker.proxyTrackerState);
1666
+ tracker.currentRenderPaths = optimizeTrackedPaths(stopProxyTracking(tracker.proxyTrackerState));
991
1667
  if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
992
1668
  const trackedPathsUnion = new Set(tracker.previousRenderPaths);
993
1669
  for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
@@ -1003,19 +1679,8 @@ function captureTrackedPaths(tracker, state) {
1003
1679
  const info$1 = tracker.pathCache.get(path);
1004
1680
  info$1.value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, info$1.segments);
1005
1681
  }
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
1682
  tracker.lastCheckedValues.clear();
1012
1683
  }
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
1684
  function hasChanges(tracker, state) {
1020
1685
  if (tracker.pathCache.size === 0) return true;
1021
1686
  tracker.lastCheckedValues.clear();
@@ -1030,15 +1695,180 @@ function hasChanges(tracker, state) {
1030
1695
  tracker.lastCheckedState = state;
1031
1696
  return false;
1032
1697
  }
1033
- /**
1034
- * Check if tracker has any tracked data
1035
- *
1036
- * Returns true if there are any tracked paths or cached paths.
1037
- */
1038
1698
  function hasTrackedData(tracker) {
1039
1699
  return tracker.proxyTrackerState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
1040
1700
  }
1041
1701
 
1702
+ //#endregion
1703
+ //#region src/adapter/framework-adapter.ts
1704
+ /**
1705
+ * External dependency manager
1706
+ * Handles subscriptions to external blocs accessed in getters
1707
+ */
1708
+ var ExternalDependencyManager = class {
1709
+ constructor() {
1710
+ this.subscriptions = [];
1711
+ this.previousDeps = /* @__PURE__ */ new Set();
1712
+ }
1713
+ /**
1714
+ * Check if dependencies have changed
1715
+ */
1716
+ areDependenciesEqual(oldDeps, newDeps) {
1717
+ if (oldDeps.size !== newDeps.size) return false;
1718
+ for (const dep of newDeps) if (!oldDeps.has(dep)) return false;
1719
+ return true;
1720
+ }
1721
+ /**
1722
+ * Update external subscriptions
1723
+ * @param getterTracker - The getter tracker state
1724
+ * @param rawInstance - The raw bloc instance (exclude from subscriptions)
1725
+ * @param onGetterChange - Callback when external getter changes
1726
+ * @returns Whether subscriptions were updated
1727
+ */
1728
+ updateSubscriptions(getterTracker, rawInstance, onGetterChange) {
1729
+ if (!getterTracker?.externalDependencies) return false;
1730
+ const currentDeps = getterTracker.externalDependencies;
1731
+ if (this.areDependenciesEqual(this.previousDeps, currentDeps)) {
1732
+ clearExternalDependencies(getterTracker);
1733
+ return false;
1734
+ }
1735
+ this.cleanup();
1736
+ for (const externalBloc of currentDeps) {
1737
+ if (externalBloc === rawInstance) continue;
1738
+ const unsub = externalBloc.subscribe(() => {
1739
+ invalidateRenderCache(getterTracker);
1740
+ if (hasGetterChanges(rawInstance, getterTracker)) onGetterChange();
1741
+ });
1742
+ this.subscriptions.push(unsub);
1743
+ }
1744
+ this.previousDeps = new Set(currentDeps);
1745
+ clearExternalDependencies(getterTracker);
1746
+ return true;
1747
+ }
1748
+ /**
1749
+ * Cleanup all subscriptions
1750
+ */
1751
+ cleanup() {
1752
+ this.subscriptions.forEach((unsub) => unsub());
1753
+ this.subscriptions = [];
1754
+ }
1755
+ };
1756
+ /**
1757
+ * Create a subscription function for auto-tracking mode
1758
+ */
1759
+ function createAutoTrackSubscribe(instance, adapterState) {
1760
+ return (callback) => {
1761
+ return instance.subscribe(() => {
1762
+ const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
1763
+ const hasStateDeps = tracker.pathCache && tracker.pathCache.size > 0;
1764
+ const hasGetterDeps = adapterState.getterTracker && adapterState.getterTracker.trackedGetters.size > 0;
1765
+ const isPrimitiveState = instance.state !== null && typeof instance.state !== "object" && typeof instance.state !== "function";
1766
+ if (!hasStateDeps && !hasGetterDeps && !isPrimitiveState) return;
1767
+ let stateChanged = hasChanges(tracker, instance.state);
1768
+ if (!hasStateDeps && hasGetterDeps) stateChanged = false;
1769
+ if (stateChanged) {
1770
+ callback();
1771
+ return;
1772
+ }
1773
+ if (hasGetterChanges(instance, adapterState.getterTracker)) callback();
1774
+ });
1775
+ };
1776
+ }
1777
+ /**
1778
+ * Create a subscription function for manual dependencies mode
1779
+ */
1780
+ function createManualDepsSubscribe(instance, adapterState, config) {
1781
+ return (callback) => {
1782
+ return instance.subscribe(() => {
1783
+ const newDeps = config.dependencies(instance.state, instance);
1784
+ if (!adapterState.manualDepsCache || !shallowEqual(adapterState.manualDepsCache, newDeps)) {
1785
+ adapterState.manualDepsCache = newDeps;
1786
+ callback();
1787
+ }
1788
+ });
1789
+ };
1790
+ }
1791
+ /**
1792
+ * Create a subscription function for no-tracking mode
1793
+ */
1794
+ function createNoTrackSubscribe(instance) {
1795
+ return (callback) => instance.subscribe(callback);
1796
+ }
1797
+ /**
1798
+ * Create a snapshot function for auto-tracking mode
1799
+ */
1800
+ function createAutoTrackSnapshot(instance, adapterState) {
1801
+ return () => {
1802
+ const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
1803
+ if (hasTrackedData(tracker)) captureTrackedPaths(tracker, instance.state);
1804
+ if (adapterState.getterTracker) {
1805
+ invalidateRenderCache(adapterState.getterTracker);
1806
+ commitTrackedGetters(adapterState.getterTracker);
1807
+ adapterState.getterTracker.isTracking = true;
1808
+ setActiveTracker(instance, adapterState.getterTracker);
1809
+ }
1810
+ startTracking(tracker);
1811
+ return createProxy(tracker, instance.state);
1812
+ };
1813
+ }
1814
+ /**
1815
+ * Create a snapshot function for manual dependencies mode
1816
+ */
1817
+ function createManualDepsSnapshot(instance, adapterState, config) {
1818
+ return () => {
1819
+ adapterState.manualDepsCache = config.dependencies(instance.state, instance);
1820
+ return instance.state;
1821
+ };
1822
+ }
1823
+ /**
1824
+ * Create a snapshot function for no-tracking mode
1825
+ */
1826
+ function createNoTrackSnapshot(instance) {
1827
+ return () => instance.state;
1828
+ }
1829
+ /**
1830
+ * Initialize adapter state for auto-tracking mode
1831
+ */
1832
+ function initAutoTrackState(instance) {
1833
+ return {
1834
+ tracker: null,
1835
+ manualDepsCache: null,
1836
+ getterTracker: createGetterTracker(),
1837
+ proxiedBloc: createBlocProxy(instance)
1838
+ };
1839
+ }
1840
+ /**
1841
+ * Initialize adapter state for manual dependencies mode
1842
+ */
1843
+ function initManualDepsState(instance) {
1844
+ return {
1845
+ tracker: null,
1846
+ manualDepsCache: null,
1847
+ getterTracker: null,
1848
+ proxiedBloc: instance
1849
+ };
1850
+ }
1851
+ /**
1852
+ * Initialize adapter state for no-tracking mode
1853
+ */
1854
+ function initNoTrackState(instance) {
1855
+ return {
1856
+ tracker: null,
1857
+ manualDepsCache: null,
1858
+ getterTracker: null,
1859
+ proxiedBloc: instance
1860
+ };
1861
+ }
1862
+ /**
1863
+ * Disable getter tracking (call after render completes)
1864
+ */
1865
+ function disableGetterTracking(adapterState, rawInstance) {
1866
+ if (adapterState.getterTracker) {
1867
+ adapterState.getterTracker.isTracking = false;
1868
+ clearActiveTracker(rawInstance);
1869
+ }
1870
+ }
1871
+
1042
1872
  //#endregion
1043
1873
  //#region src/logging/Logger.ts
1044
1874
  let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
@@ -1136,209 +1966,5 @@ function configureLogger(opts) {
1136
1966
  }
1137
1967
 
1138
1968
  //#endregion
1139
- //#region src/proxy/ProxyTracker.ts
1140
- /**
1141
- * Check if a value is an object that can be proxied
1142
- */
1143
- function isProxyable$1(value) {
1144
- return value !== null && typeof value === "object" && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Map) && !(value instanceof Set) && !(value instanceof WeakMap) && !(value instanceof WeakSet) && !(value instanceof Promise);
1145
- }
1146
- /**
1147
- * Tracks property access on state objects
1148
- */
1149
- var ProxyTracker = class {
1150
- trackedPaths = /* @__PURE__ */ new Set();
1151
- isTracking = false;
1152
- proxyCache = /* @__PURE__ */ new WeakMap();
1153
- maxDepth = 10;
1154
- /**
1155
- * Create a proxy that tracks property access
1156
- */
1157
- createProxy(target, path = "", depth = 0) {
1158
- if (!this.isTracking || !isProxyable$1(target)) return target;
1159
- if (depth >= this.maxDepth) return target;
1160
- if (this.proxyCache.has(target)) return this.proxyCache.get(target);
1161
- if (Array.isArray(target)) return this.createArrayProxy(target, path, depth);
1162
- const proxy = new Proxy(target, {
1163
- get: (obj, prop) => {
1164
- const value = Reflect.get(obj, prop);
1165
- if (typeof value === "function") return value.bind(obj);
1166
- const fullPath = path ? `${path}.${String(prop)}` : String(prop);
1167
- if (isProxyable$1(value)) return this.createProxy(value, fullPath, depth + 1);
1168
- if (typeof prop === "string" && this.isTracking) {
1169
- if (!prop.startsWith("_") && !prop.startsWith("$$")) {
1170
- this.trackedPaths.add(fullPath);
1171
- debug("ProxyTracker", "Tracked property access", {
1172
- path: fullPath,
1173
- valueType: typeof value
1174
- });
1175
- }
1176
- }
1177
- return value;
1178
- },
1179
- has: (obj, prop) => {
1180
- if (typeof prop === "string" && this.isTracking) {
1181
- const fullPath = path ? `${path}.${prop}` : prop;
1182
- this.trackedPaths.add(fullPath);
1183
- debug("ProxyTracker", "Tracked \"in\" operator access", { path: fullPath });
1184
- }
1185
- return Reflect.has(obj, prop);
1186
- },
1187
- ownKeys: (obj) => {
1188
- if (this.isTracking && path) {
1189
- this.trackedPaths.add(path);
1190
- debug("ProxyTracker", "Tracked object keys iteration", { path });
1191
- }
1192
- return Reflect.ownKeys(obj);
1193
- }
1194
- });
1195
- this.proxyCache.set(target, proxy);
1196
- return proxy;
1197
- }
1198
- /**
1199
- * Create a proxy for arrays with special handling
1200
- */
1201
- createArrayProxy(target, path, depth = 0) {
1202
- const proxy = new Proxy(target, { get: (arr, prop) => {
1203
- const value = Reflect.get(arr, prop);
1204
- 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
- }
1221
- }
1222
- return value.bind(arr);
1223
- }
1224
- let fullPath;
1225
- if (prop === "length") fullPath = path ? `${path}.length` : "length";
1226
- else if (typeof prop === "string") {
1227
- const index = Number(prop);
1228
- if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
1229
- else fullPath = path ? `${path}.${prop}` : prop;
1230
- } else return value;
1231
- if (isProxyable$1(value)) return this.createProxy(value, fullPath, depth + 1);
1232
- if (this.isTracking) {
1233
- this.trackedPaths.add(fullPath);
1234
- debug("ProxyTracker", "Tracked array property access", {
1235
- path: fullPath,
1236
- valueType: typeof value
1237
- });
1238
- }
1239
- return value;
1240
- } });
1241
- this.proxyCache.set(target, proxy);
1242
- return proxy;
1243
- }
1244
- /**
1245
- * Start tracking property access
1246
- */
1247
- startTracking() {
1248
- this.isTracking = true;
1249
- this.trackedPaths.clear();
1250
- debug("ProxyTracker", "Started tracking");
1251
- }
1252
- /**
1253
- * Stop tracking and return tracked paths
1254
- */
1255
- stopTracking() {
1256
- this.isTracking = false;
1257
- debug("ProxyTracker", "Stopped tracking", {
1258
- pathCount: this.trackedPaths.size,
1259
- paths: Array.from(this.trackedPaths)
1260
- });
1261
- return new Set(this.trackedPaths);
1262
- }
1263
- /**
1264
- * Get currently tracked paths
1265
- */
1266
- getTrackedPaths() {
1267
- return new Set(this.trackedPaths);
1268
- }
1269
- /**
1270
- * Check if currently tracking
1271
- */
1272
- isCurrentlyTracking() {
1273
- return this.isTracking;
1274
- }
1275
- /**
1276
- * Clear proxy cache (useful for memory management)
1277
- */
1278
- clearCache() {
1279
- this.proxyCache = /* @__PURE__ */ new WeakMap();
1280
- }
1281
- /**
1282
- * Set maximum tracking depth
1283
- */
1284
- setMaxDepth(depth) {
1285
- this.maxDepth = Math.max(1, depth);
1286
- }
1287
- /**
1288
- * Add a path manually (useful for forcing dependencies)
1289
- */
1290
- addPath(path) {
1291
- this.trackedPaths.add(path);
1292
- }
1293
- /**
1294
- * Remove a path manually
1295
- */
1296
- removePath(path) {
1297
- this.trackedPaths.delete(path);
1298
- }
1299
- /**
1300
- * Clear all tracked paths
1301
- */
1302
- clearPaths() {
1303
- this.trackedPaths.clear();
1304
- }
1305
- };
1306
- /**
1307
- * Global proxy tracker instance for convenience
1308
- */
1309
- const globalProxyTracker = new ProxyTracker();
1310
- /**
1311
- * Utility function to track property access in a callback
1312
- */
1313
- function trackAccess(state, callback) {
1314
- const tracker = new ProxyTracker();
1315
- tracker.startTracking();
1316
- return {
1317
- result: callback(tracker.createProxy(state)),
1318
- trackedPaths: tracker.stopTracking()
1319
- };
1320
- }
1321
-
1322
- //#endregion
1323
- //#region src/constants.ts
1324
- /**
1325
- * Default configuration constants for BlaC
1326
- *
1327
- * Centralized location for all magic numbers and default values.
1328
- */
1329
- /**
1330
- * Default configuration constants for BlaC
1331
- */
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
- };
1341
-
1342
- //#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 };
1969
+ export { AuthVertex, BLAC_DEFAULTS, BLAC_ERROR_PREFIX, BLAC_ID_PATTERNS, BLAC_STATIC_PROPS, CounterCubit, CounterVertex, Cubit, DecrementEvent, ExternalDependencyManager, IncrementEvent, LogLevel, LoginEvent, LogoutEvent, PluginManager, 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, globalRegistry, hasChanges, hasGetterChanges, hasInitHook, hasTrackedData, info, initAutoTrackState, initManualDepsState, initNoTrackState, invalidateRenderCache, isExcludedFromDevTools, isGetter, isIsolatedClass, isIsolatedKey, isKeepAliveClass, isProxyable, parsePath, resetGetterTracker, setActiveTracker, shallowEqual, startProxyTracking, startTracking, stopProxyTracking, warn };
1344
1970
  //# sourceMappingURL=index.js.map