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