@blac/core 2.0.0-rc.1 → 2.0.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/index.cjs +2100 -38
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +1298 -0
  4. package/dist/index.d.ts +1298 -17
  5. package/dist/index.js +2028 -37
  6. package/dist/index.js.map +1 -1
  7. package/package.json +35 -13
  8. package/dist/.tsbuildinfo +0 -1
  9. package/dist/Blac.d.ts +0 -246
  10. package/dist/Blac.d.ts.map +0 -1
  11. package/dist/Blac.js +0 -624
  12. package/dist/Blac.js.map +0 -1
  13. package/dist/Bloc.d.ts +0 -41
  14. package/dist/Bloc.d.ts.map +0 -1
  15. package/dist/Bloc.js +0 -140
  16. package/dist/Bloc.js.map +0 -1
  17. package/dist/BlocBase.d.ts +0 -110
  18. package/dist/BlocBase.d.ts.map +0 -1
  19. package/dist/BlocBase.js +0 -287
  20. package/dist/BlocBase.js.map +0 -1
  21. package/dist/Cubit.d.ts +0 -25
  22. package/dist/Cubit.d.ts.map +0 -1
  23. package/dist/Cubit.js +0 -64
  24. package/dist/Cubit.js.map +0 -1
  25. package/dist/adapter/BlacAdapter.d.ts +0 -67
  26. package/dist/adapter/BlacAdapter.d.ts.map +0 -1
  27. package/dist/adapter/BlacAdapter.js +0 -263
  28. package/dist/adapter/BlacAdapter.js.map +0 -1
  29. package/dist/adapter/ProxyFactory.d.ts +0 -50
  30. package/dist/adapter/ProxyFactory.d.ts.map +0 -1
  31. package/dist/adapter/ProxyFactory.js +0 -150
  32. package/dist/adapter/ProxyFactory.js.map +0 -1
  33. package/dist/adapter/index.d.ts +0 -3
  34. package/dist/adapter/index.d.ts.map +0 -1
  35. package/dist/adapter/index.js +0 -19
  36. package/dist/adapter/index.js.map +0 -1
  37. package/dist/errors/BlacError.d.ts +0 -38
  38. package/dist/errors/BlacError.d.ts.map +0 -1
  39. package/dist/errors/BlacError.js +0 -55
  40. package/dist/errors/BlacError.js.map +0 -1
  41. package/dist/errors/ErrorManager.d.ts +0 -39
  42. package/dist/errors/ErrorManager.d.ts.map +0 -1
  43. package/dist/errors/ErrorManager.js +0 -91
  44. package/dist/errors/ErrorManager.js.map +0 -1
  45. package/dist/errors/handleError.d.ts +0 -24
  46. package/dist/errors/handleError.d.ts.map +0 -1
  47. package/dist/errors/handleError.js +0 -62
  48. package/dist/errors/handleError.js.map +0 -1
  49. package/dist/errors/index.d.ts +0 -5
  50. package/dist/errors/index.d.ts.map +0 -1
  51. package/dist/errors/index.js +0 -13
  52. package/dist/errors/index.js.map +0 -1
  53. package/dist/events.d.ts +0 -11
  54. package/dist/events.d.ts.map +0 -1
  55. package/dist/events.js +0 -17
  56. package/dist/events.js.map +0 -1
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/lifecycle/BlocLifecycle.d.ts +0 -51
  59. package/dist/lifecycle/BlocLifecycle.d.ts.map +0 -1
  60. package/dist/lifecycle/BlocLifecycle.js +0 -123
  61. package/dist/lifecycle/BlocLifecycle.js.map +0 -1
  62. package/dist/lifecycle/index.d.ts +0 -2
  63. package/dist/lifecycle/index.d.ts.map +0 -1
  64. package/dist/lifecycle/index.js +0 -18
  65. package/dist/lifecycle/index.js.map +0 -1
  66. package/dist/plugins/BlocPluginRegistry.d.ts +0 -58
  67. package/dist/plugins/BlocPluginRegistry.d.ts.map +0 -1
  68. package/dist/plugins/BlocPluginRegistry.js +0 -201
  69. package/dist/plugins/BlocPluginRegistry.js.map +0 -1
  70. package/dist/plugins/SystemPluginRegistry.d.ts +0 -79
  71. package/dist/plugins/SystemPluginRegistry.d.ts.map +0 -1
  72. package/dist/plugins/SystemPluginRegistry.js +0 -184
  73. package/dist/plugins/SystemPluginRegistry.js.map +0 -1
  74. package/dist/plugins/index.d.ts +0 -4
  75. package/dist/plugins/index.d.ts.map +0 -1
  76. package/dist/plugins/index.js +0 -26
  77. package/dist/plugins/index.js.map +0 -1
  78. package/dist/plugins/types.d.ts +0 -103
  79. package/dist/plugins/types.d.ts.map +0 -1
  80. package/dist/plugins/types.js +0 -3
  81. package/dist/plugins/types.js.map +0 -1
  82. package/dist/subscription/SubscriptionManager.d.ts +0 -67
  83. package/dist/subscription/SubscriptionManager.d.ts.map +0 -1
  84. package/dist/subscription/SubscriptionManager.js +0 -317
  85. package/dist/subscription/SubscriptionManager.js.map +0 -1
  86. package/dist/subscription/index.d.ts +0 -3
  87. package/dist/subscription/index.d.ts.map +0 -1
  88. package/dist/subscription/index.js +0 -19
  89. package/dist/subscription/index.js.map +0 -1
  90. package/dist/subscription/types.d.ts +0 -65
  91. package/dist/subscription/types.d.ts.map +0 -1
  92. package/dist/subscription/types.js +0 -3
  93. package/dist/subscription/types.js.map +0 -1
  94. package/dist/testing.d.ts +0 -33
  95. package/dist/testing.d.ts.map +0 -1
  96. package/dist/testing.js +0 -68
  97. package/dist/testing.js.map +0 -1
  98. package/dist/types.d.ts +0 -57
  99. package/dist/types.d.ts.map +0 -1
  100. package/dist/types.js +0 -3
  101. package/dist/types.js.map +0 -1
  102. package/dist/utils/BatchingManager.d.ts +0 -28
  103. package/dist/utils/BatchingManager.d.ts.map +0 -1
  104. package/dist/utils/BatchingManager.js +0 -48
  105. package/dist/utils/BatchingManager.js.map +0 -1
  106. package/dist/utils/RerenderLogger.d.ts +0 -60
  107. package/dist/utils/RerenderLogger.d.ts.map +0 -1
  108. package/dist/utils/RerenderLogger.js +0 -205
  109. package/dist/utils/RerenderLogger.js.map +0 -1
  110. package/dist/utils/generateInstanceId.d.ts +0 -11
  111. package/dist/utils/generateInstanceId.d.ts.map +0 -1
  112. package/dist/utils/generateInstanceId.js +0 -50
  113. package/dist/utils/generateInstanceId.js.map +0 -1
  114. package/dist/utils/shallowEqual.d.ts +0 -9
  115. package/dist/utils/shallowEqual.d.ts.map +0 -1
  116. package/dist/utils/shallowEqual.js +0 -35
  117. package/dist/utils/shallowEqual.js.map +0 -1
  118. package/dist/utils/uuid.d.ts +0 -10
  119. package/dist/utils/uuid.d.ts.map +0 -1
  120. package/dist/utils/uuid.js +0 -24
  121. package/dist/utils/uuid.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,38 +1,2100 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./Blac"), exports);
18
- __exportStar(require("./Bloc"), exports);
19
- __exportStar(require("./BlocBase"), exports);
20
- __exportStar(require("./Cubit"), exports);
21
- __exportStar(require("./types"), exports);
22
- __exportStar(require("./events"), exports);
23
- __exportStar(require("./subscription"), exports);
24
- __exportStar(require("./lifecycle"), exports);
25
- // Utilities
26
- __exportStar(require("./utils/uuid"), exports);
27
- __exportStar(require("./utils/shallowEqual"), exports);
28
- __exportStar(require("./utils/generateInstanceId"), exports);
29
- __exportStar(require("./utils/RerenderLogger"), exports);
30
- // Test utilities
31
- __exportStar(require("./testing"), exports);
32
- // Adapter
33
- __exportStar(require("./adapter"), exports);
34
- // Plugins
35
- __exportStar(require("./plugins"), exports);
36
- // Error handling
37
- __exportStar(require("./errors"), exports);
38
- //# sourceMappingURL=index.js.map
1
+
2
+ //#region src/utils/idGenerator.ts
3
+ /**
4
+ * Centralized ID Generation
5
+ *
6
+ * Provides consistent, collision-resistant ID generation for all BlaC subsystems.
7
+ * Uses timestamp + counter + random suffix for uniqueness.
8
+ */
9
+ const globalCounters = /* @__PURE__ */ new Map();
10
+ /**
11
+ * Creates an ID generator with isolated counter state
12
+ *
13
+ * @param prefix - Prefix for generated IDs
14
+ * @returns Object with next(), nextSimple(), and reset() methods
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const generator = createIdGenerator('sub');
19
+ * const id1 = generator.next(); // "sub:1698765432100_1_a3k9d7f2q"
20
+ * const id2 = generator.next(); // "sub:1698765432101_2_b4n8e9g3r"
21
+ * ```
22
+ */
23
+ function createIdGenerator(prefix) {
24
+ let counter = 0;
25
+ return {
26
+ next: () => `${prefix}:${Date.now()}_${++counter}_${Math.random().toString(36).substring(2, 11)}`,
27
+ nextSimple: () => `${prefix}:${++counter}`,
28
+ reset: () => {
29
+ counter = 0;
30
+ }
31
+ };
32
+ }
33
+ /**
34
+ * Generate ID with timestamp, counter, and random suffix (tree-shakeable)
35
+ *
36
+ * Format: `${prefix}:${timestamp}_${counter}_${random}`
37
+ *
38
+ * @param prefix - Prefix for the ID (e.g., 'sub', 'consumer', 'stage')
39
+ * @returns Branded ID string
40
+ *
41
+ * @example
42
+ * const id = generateId('sub');
43
+ * // Returns: "sub:1698765432100_1_a3k9d7f2q"
44
+ */
45
+ function generateId(prefix) {
46
+ const counter = (globalCounters.get(prefix) ?? 0) + 1;
47
+ globalCounters.set(prefix, counter);
48
+ return `${prefix}:${Date.now()}_${counter}_${Math.random().toString(36).substring(2, 11)}`;
49
+ }
50
+ /**
51
+ * Generate simple ID with timestamp and random (no counter tracking)
52
+ *
53
+ * Format: `${prefix}:${timestamp}_${random}`
54
+ *
55
+ * @param prefix - Prefix for the ID
56
+ * @returns Branded ID string
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const id = generateSimpleId('CounterBloc');
61
+ * // Returns: "CounterBloc:1698765432100_a3k9d7f2q"
62
+ * ```
63
+ */
64
+ function generateSimpleId(prefix, affix) {
65
+ if (affix) return `${prefix}:${affix}`;
66
+ return `${prefix}:${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
67
+ }
68
+ /**
69
+ * Reset all counters for testing purposes
70
+ * @internal
71
+ */
72
+ function __resetIdCounters() {
73
+ globalCounters.clear();
74
+ }
75
+ /**
76
+ * Generate a unique isolated instance key
77
+ * Uses base36 encoding for compact, URL-safe identifiers
78
+ *
79
+ * Format: "isolated-{9-char-random-string}"
80
+ * Example: "isolated-k7x2m9p4q"
81
+ *
82
+ * @returns A unique isolated instance key
83
+ */
84
+ function generateIsolatedKey() {
85
+ return `isolated-${Math.random().toString(36).slice(2, 11)}`;
86
+ }
87
+ /**
88
+ * Check if a key is an isolated instance key
89
+ * @param key - The instance key to check
90
+ * @returns true if the key is an isolated instance key
91
+ */
92
+ function isIsolatedKey(key) {
93
+ return key.startsWith("isolated-");
94
+ }
95
+
96
+ //#endregion
97
+ //#region src/plugin/PluginManager.ts
98
+ /**
99
+ * Manages plugin lifecycle for the BlaC state management system.
100
+ * Plugins receive notifications about state container lifecycle events.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const manager = createPluginManager(registry);
105
+ * manager.install(myPlugin, { environment: 'development' });
106
+ * ```
107
+ */
108
+ var PluginManager = class {
109
+ /**
110
+ * Create a new PluginManager
111
+ * @param registry - The StateContainerRegistry to monitor for lifecycle events
112
+ */
113
+ constructor(registry) {
114
+ this.plugins = /* @__PURE__ */ new Map();
115
+ this.registry = registry;
116
+ this.setupLifecycleHooks();
117
+ }
118
+ /**
119
+ * Install a plugin with optional configuration
120
+ * @param plugin - The plugin to install
121
+ * @param config - Optional plugin configuration
122
+ * @throws Error if plugin is already installed
123
+ */
124
+ install(plugin, config = {}) {
125
+ const effectiveConfig = {
126
+ enabled: true,
127
+ environment: "all",
128
+ ...config
129
+ };
130
+ if (!this.shouldEnablePlugin(effectiveConfig)) {
131
+ console.log(`[BlaC] Plugin "${plugin.name}" skipped (environment mismatch)`);
132
+ return;
133
+ }
134
+ if (this.plugins.has(plugin.name)) throw new Error(`Plugin "${plugin.name}" is already installed`);
135
+ const context = this.createPluginContext();
136
+ this.plugins.set(plugin.name, {
137
+ plugin,
138
+ config: effectiveConfig,
139
+ context
140
+ });
141
+ if (plugin.onInstall) try {
142
+ plugin.onInstall(context);
143
+ } catch (error$1) {
144
+ console.error(`[BlaC] Error installing plugin "${plugin.name}":`, error$1);
145
+ this.plugins.delete(plugin.name);
146
+ throw error$1;
147
+ }
148
+ console.log(`[BlaC] Plugin "${plugin.name}" v${plugin.version} installed`);
149
+ }
150
+ /**
151
+ * Uninstall a plugin by name
152
+ * @param pluginName - The name of the plugin to uninstall
153
+ * @throws Error if plugin is not installed
154
+ */
155
+ uninstall(pluginName) {
156
+ const installed = this.plugins.get(pluginName);
157
+ if (!installed) throw new Error(`Plugin "${pluginName}" is not installed`);
158
+ if (installed.plugin.onUninstall) try {
159
+ installed.plugin.onUninstall();
160
+ } catch (error$1) {
161
+ console.error(`[BlaC] Error uninstalling plugin "${pluginName}":`, error$1);
162
+ }
163
+ this.plugins.delete(pluginName);
164
+ console.log(`[BlaC] Plugin "${pluginName}" uninstalled`);
165
+ }
166
+ /**
167
+ * Get an installed plugin by name
168
+ * @param pluginName - The name of the plugin to retrieve
169
+ * @returns The plugin instance or undefined if not found
170
+ */
171
+ getPlugin(pluginName) {
172
+ return this.plugins.get(pluginName)?.plugin;
173
+ }
174
+ /**
175
+ * Get all installed plugins
176
+ * @returns Array of all installed plugins
177
+ */
178
+ getAllPlugins() {
179
+ return Array.from(this.plugins.values()).map((p) => p.plugin);
180
+ }
181
+ /**
182
+ * Check if a plugin is installed
183
+ * @param pluginName - The name of the plugin to check
184
+ * @returns true if the plugin is installed
185
+ */
186
+ hasPlugin(pluginName) {
187
+ return this.plugins.has(pluginName);
188
+ }
189
+ /**
190
+ * Uninstall all plugins
191
+ */
192
+ clear() {
193
+ for (const name of this.plugins.keys()) this.uninstall(name);
194
+ }
195
+ /**
196
+ * Setup lifecycle hooks to notify plugins
197
+ */
198
+ setupLifecycleHooks() {
199
+ this.registry.on("created", (instance) => {
200
+ this.notifyPlugins("onInstanceCreated", instance);
201
+ });
202
+ this.registry.on("stateChanged", (instance, previousState, currentState, callstack) => {
203
+ this.notifyPlugins("onStateChanged", instance, previousState, currentState, callstack);
204
+ });
205
+ this.registry.on("eventAdded", (vertex, event) => {
206
+ this.notifyPlugins("onEventAdded", vertex, event);
207
+ });
208
+ this.registry.on("disposed", (instance) => {
209
+ this.notifyPlugins("onInstanceDisposed", instance);
210
+ });
211
+ }
212
+ /**
213
+ * Notify all plugins of a lifecycle event
214
+ */
215
+ notifyPlugins(hookName, ...args) {
216
+ for (const { plugin, config, context } of this.plugins.values()) {
217
+ if (!config.enabled) continue;
218
+ const hook = plugin[hookName];
219
+ if (typeof hook === "function") try {
220
+ hook.apply(plugin, [...args, context]);
221
+ } catch (error$1) {
222
+ console.error(`[BlaC] Error in plugin "${plugin.name}" ${hookName}:`, error$1);
223
+ }
224
+ }
225
+ }
226
+ /**
227
+ * Create plugin context with safe API access
228
+ */
229
+ createPluginContext() {
230
+ return {
231
+ getInstanceMetadata: (instance) => {
232
+ return {
233
+ id: instance.instanceId,
234
+ className: instance.constructor.name,
235
+ isDisposed: instance.isDisposed,
236
+ name: instance.name,
237
+ lastStateChangeTimestamp: instance.lastUpdateTimestamp,
238
+ createdAt: instance.createdAt,
239
+ state: instance.state,
240
+ isIsolated: instance.instanceId.startsWith("isolated-")
241
+ };
242
+ },
243
+ getState: (instance) => {
244
+ return instance.state;
245
+ },
246
+ queryInstances: (typeClass) => {
247
+ return typeClass.getAll();
248
+ },
249
+ getAllTypes: () => {
250
+ return this.registry.getTypes();
251
+ },
252
+ getStats: () => {
253
+ return this.registry.getStats();
254
+ }
255
+ };
256
+ }
257
+ /**
258
+ * Check if plugin should be enabled based on environment
259
+ */
260
+ shouldEnablePlugin(config) {
261
+ if (!config.enabled) return false;
262
+ if (config.environment === "all") return true;
263
+ return this.getCurrentEnvironment() === config.environment;
264
+ }
265
+ /**
266
+ * Get current environment
267
+ */
268
+ getCurrentEnvironment() {
269
+ if (typeof process !== "undefined") {
270
+ if (process.env.NODE_ENV === "test") return "test";
271
+ if (process.env.NODE_ENV === "production") return "production";
272
+ return "development";
273
+ }
274
+ return "development";
275
+ }
276
+ };
277
+ /**
278
+ * Create a plugin manager instance
279
+ */
280
+ function createPluginManager(registry) {
281
+ return new PluginManager(registry);
282
+ }
283
+
284
+ //#endregion
285
+ //#region src/constants.ts
286
+ /**
287
+ * Default configuration constants for BlaC
288
+ *
289
+ * Centralized location for all magic numbers and default values.
290
+ */
291
+ /**
292
+ * Default configuration constants for BlaC
293
+ */
294
+ const BLAC_DEFAULTS = {
295
+ DEFAULT_INSTANCE_KEY: "default",
296
+ MAX_GETTER_DEPTH: 10,
297
+ CLEANUP_INTERVAL_MS: 3e4,
298
+ WEAKREF_CLEANUP_INTERVAL_MS: 1e4,
299
+ MAX_SUBSCRIPTIONS: 1e3,
300
+ MAX_SUBSCRIPTIONS_HIGH_PERF: 1e4,
301
+ PIPELINE_TIMEOUT_MS: 5e3,
302
+ CLEANUP_INTERVAL_HIGH_PERF_MS: 5e3,
303
+ MAX_PIPELINE_STAGES: 30
304
+ };
305
+ /**
306
+ * Static property names for StateContainer classes
307
+ * Used for feature flags and configuration on bloc classes
308
+ */
309
+ const BLAC_STATIC_PROPS = {
310
+ ISOLATED: "isolated",
311
+ KEEP_ALIVE: "keepAlive",
312
+ EXCLUDE_FROM_DEVTOOLS: "__excludeFromDevTools"
313
+ };
314
+ /**
315
+ * ID generation patterns and constants
316
+ */
317
+ const BLAC_ID_PATTERNS = {
318
+ ISOLATED_PREFIX: "isolated-",
319
+ ID_LENGTH: 9
320
+ };
321
+ /**
322
+ * Standard error message prefix
323
+ */
324
+ const BLAC_ERROR_PREFIX = "[BlaC]";
325
+
326
+ //#endregion
327
+ //#region src/tracking/getter-tracker.ts
328
+ const descriptorCache = /* @__PURE__ */ new WeakMap();
329
+ const blocProxyCache = /* @__PURE__ */ new WeakMap();
330
+ const activeTrackerMap = /* @__PURE__ */ new WeakMap();
331
+ const getterExecutionContext = {
332
+ tracker: null,
333
+ currentBloc: null,
334
+ depth: 0,
335
+ visitedBlocs: /* @__PURE__ */ new Set()
336
+ };
337
+ const MAX_GETTER_DEPTH = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
338
+ /**
339
+ * @internal
340
+ */
341
+ function getGetterExecutionContext() {
342
+ return getterExecutionContext;
343
+ }
344
+ /**
345
+ * @internal
346
+ */
347
+ function getDescriptor(obj, prop) {
348
+ const constructor = obj.constructor;
349
+ let constructorCache = descriptorCache.get(constructor);
350
+ if (constructorCache?.has(prop)) return constructorCache.get(prop);
351
+ let current = obj;
352
+ let descriptor;
353
+ while (current && current !== Object.prototype) {
354
+ descriptor = Object.getOwnPropertyDescriptor(current, prop);
355
+ if (descriptor) break;
356
+ current = Object.getPrototypeOf(current);
357
+ }
358
+ if (!constructorCache) {
359
+ constructorCache = /* @__PURE__ */ new Map();
360
+ descriptorCache.set(constructor, constructorCache);
361
+ }
362
+ constructorCache.set(prop, descriptor);
363
+ return descriptor;
364
+ }
365
+ /**
366
+ * @internal
367
+ */
368
+ function isGetter(obj, prop) {
369
+ return getDescriptor(obj, prop)?.get !== void 0;
370
+ }
371
+ /**
372
+ * @internal
373
+ */
374
+ function createGetterTracker() {
375
+ return {
376
+ trackedValues: /* @__PURE__ */ new Map(),
377
+ currentlyAccessing: /* @__PURE__ */ new Set(),
378
+ trackedGetters: /* @__PURE__ */ new Set(),
379
+ isTracking: false,
380
+ renderCache: /* @__PURE__ */ new Map(),
381
+ cacheValid: false,
382
+ externalDependencies: /* @__PURE__ */ new Set()
383
+ };
384
+ }
385
+ /**
386
+ * @internal
387
+ */
388
+ function setActiveTracker(bloc, tracker) {
389
+ activeTrackerMap.set(bloc, tracker);
390
+ }
391
+ /**
392
+ * @internal
393
+ */
394
+ function clearActiveTracker(bloc) {
395
+ activeTrackerMap.delete(bloc);
396
+ }
397
+ /**
398
+ * @internal
399
+ */
400
+ function getActiveTracker(bloc) {
401
+ return activeTrackerMap.get(bloc);
402
+ }
403
+ /**
404
+ * @internal
405
+ */
406
+ function commitTrackedGetters(tracker) {
407
+ if (tracker.currentlyAccessing.size > 0) tracker.trackedGetters = new Set(tracker.currentlyAccessing);
408
+ tracker.currentlyAccessing.clear();
409
+ }
410
+ /**
411
+ * @internal
412
+ */
413
+ function createBlocProxy(bloc) {
414
+ const cached = blocProxyCache.get(bloc);
415
+ if (cached) return cached;
416
+ const proxy = new Proxy(bloc, { get(target, prop, receiver) {
417
+ const tracker = activeTrackerMap.get(target);
418
+ if (tracker?.isTracking && isGetter(target, prop)) {
419
+ tracker.currentlyAccessing.add(prop);
420
+ if (tracker.cacheValid && tracker.renderCache.has(prop)) {
421
+ const cachedValue = tracker.renderCache.get(prop);
422
+ tracker.trackedValues.set(prop, cachedValue);
423
+ return cachedValue;
424
+ }
425
+ if (getterExecutionContext.depth >= MAX_GETTER_DEPTH) {
426
+ 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.`);
427
+ return;
428
+ }
429
+ if (getterExecutionContext.visitedBlocs.has(target)) {
430
+ 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.`);
431
+ return;
432
+ }
433
+ const prevContext = {
434
+ tracker: getterExecutionContext.tracker,
435
+ currentBloc: getterExecutionContext.currentBloc,
436
+ depth: getterExecutionContext.depth,
437
+ visitedBlocs: new Set(getterExecutionContext.visitedBlocs)
438
+ };
439
+ getterExecutionContext.tracker = tracker;
440
+ getterExecutionContext.currentBloc = target;
441
+ getterExecutionContext.depth++;
442
+ getterExecutionContext.visitedBlocs.add(target);
443
+ try {
444
+ const value = getDescriptor(target, prop).get.call(target);
445
+ tracker.trackedValues.set(prop, value);
446
+ return value;
447
+ } catch (error$1) {
448
+ tracker.currentlyAccessing.delete(prop);
449
+ throw error$1;
450
+ } finally {
451
+ getterExecutionContext.tracker = prevContext.tracker;
452
+ getterExecutionContext.currentBloc = prevContext.currentBloc;
453
+ getterExecutionContext.depth = prevContext.depth;
454
+ getterExecutionContext.visitedBlocs = prevContext.visitedBlocs;
455
+ }
456
+ }
457
+ return Reflect.get(target, prop, receiver);
458
+ } });
459
+ blocProxyCache.set(bloc, proxy);
460
+ return proxy;
461
+ }
462
+ /**
463
+ * @internal
464
+ */
465
+ function hasGetterChanges(bloc, tracker) {
466
+ if (!tracker || tracker.trackedGetters.size === 0) return false;
467
+ tracker.renderCache.clear();
468
+ let hasAnyChange = false;
469
+ for (const prop of tracker.trackedGetters) try {
470
+ const descriptor = getDescriptor(bloc, prop);
471
+ if (!descriptor?.get) continue;
472
+ const newValue = descriptor.get.call(bloc);
473
+ const oldValue = tracker.trackedValues.get(prop);
474
+ tracker.renderCache.set(prop, newValue);
475
+ tracker.trackedValues.set(prop, newValue);
476
+ if (!Object.is(newValue, oldValue)) hasAnyChange = true;
477
+ } catch (error$1) {
478
+ console.warn(`${BLAC_ERROR_PREFIX} Getter "${String(prop)}" threw error during change detection. Stopping tracking for this getter.`, error$1);
479
+ tracker.trackedGetters.delete(prop);
480
+ tracker.trackedValues.delete(prop);
481
+ tracker.cacheValid = false;
482
+ return true;
483
+ }
484
+ tracker.cacheValid = true;
485
+ return hasAnyChange;
486
+ }
487
+ /**
488
+ * @internal
489
+ */
490
+ function invalidateRenderCache(tracker) {
491
+ tracker.cacheValid = false;
492
+ }
493
+ /**
494
+ * @internal
495
+ */
496
+ function clearExternalDependencies(tracker) {
497
+ tracker.externalDependencies.clear();
498
+ }
499
+ /**
500
+ * @internal
501
+ */
502
+ function resetGetterTracker(tracker) {
503
+ tracker.trackedValues.clear();
504
+ tracker.currentlyAccessing.clear();
505
+ tracker.trackedGetters.clear();
506
+ tracker.renderCache.clear();
507
+ tracker.cacheValid = false;
508
+ tracker.isTracking = false;
509
+ tracker.externalDependencies.clear();
510
+ }
511
+
512
+ //#endregion
513
+ //#region src/utils/static-props.ts
514
+ /**
515
+ * Utility functions for accessing static properties on StateContainer classes
516
+ */
517
+ /**
518
+ * Get a static property from a class constructor
519
+ * Type-safe helper that avoids (Type as any) casts
520
+ *
521
+ * @param Type - The class constructor
522
+ * @param propName - The property name to access
523
+ * @param defaultValue - Optional default value if property is undefined
524
+ * @returns The property value or default
525
+ */
526
+ function getStaticProp(Type, propName, defaultValue) {
527
+ return Type[propName] ?? defaultValue;
528
+ }
529
+ /**
530
+ * Check if a class is marked as isolated
531
+ * Isolated classes create separate instances per component
532
+ */
533
+ function isIsolatedClass(Type) {
534
+ return getStaticProp(Type, BLAC_STATIC_PROPS.ISOLATED) === true;
535
+ }
536
+ /**
537
+ * Check if a class is marked as keepAlive
538
+ * KeepAlive classes are never auto-disposed when ref count reaches 0
539
+ */
540
+ function isKeepAliveClass(Type) {
541
+ return getStaticProp(Type, BLAC_STATIC_PROPS.KEEP_ALIVE) === true;
542
+ }
543
+ /**
544
+ * Check if a class should be excluded from DevTools
545
+ * Used to prevent infinite loops when DevTools tracks itself
546
+ */
547
+ function isExcludedFromDevTools(Type) {
548
+ return getStaticProp(Type, BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS) === true;
549
+ }
550
+
551
+ //#endregion
552
+ //#region src/core/StateContainerRegistry.ts
553
+ /**
554
+ * Central registry for managing StateContainer instances.
555
+ * Handles instance lifecycle, ref counting, and lifecycle event emission.
556
+ *
557
+ * @example
558
+ * ```ts
559
+ * const registry = new StateContainerRegistry();
560
+ * const instance = registry.resolve(MyBloc);
561
+ * registry.on('stateChanged', (container, prev, next) => {
562
+ * console.log('State changed:', prev, '->', next);
563
+ * });
564
+ * ```
565
+ */
566
+ var StateContainerRegistry = class {
567
+ constructor() {
568
+ this.instancesByConstructor = /* @__PURE__ */ new WeakMap();
569
+ this.types = /* @__PURE__ */ new Set();
570
+ this.typeConfigs = /* @__PURE__ */ new Map();
571
+ this.listeners = /* @__PURE__ */ new Map();
572
+ }
573
+ /**
574
+ * Register a type for lifecycle event tracking
575
+ * @param constructor - The StateContainer class constructor
576
+ */
577
+ registerType(constructor) {
578
+ this.types.add(constructor);
579
+ }
580
+ /**
581
+ * Register a StateContainer class with configuration
582
+ * @param constructor - The StateContainer class constructor
583
+ * @param isolated - Whether instances should be isolated (component-scoped)
584
+ * @throws Error if type is already registered
585
+ */
586
+ register(constructor, isolated = false) {
587
+ const className = constructor.name;
588
+ if (!isolated && isIsolatedClass(constructor)) isolated = true;
589
+ if (this.typeConfigs.has(className)) throw new Error(`${BLAC_ERROR_PREFIX} Type "${className}" is already registered`);
590
+ this.typeConfigs.set(className, { isolated });
591
+ this.registerType(constructor);
592
+ }
593
+ ensureInstancesMap(Type) {
594
+ let instances = this.instancesByConstructor.get(Type);
595
+ if (!instances) {
596
+ instances = /* @__PURE__ */ new Map();
597
+ this.instancesByConstructor.set(Type, instances);
598
+ }
599
+ return instances;
600
+ }
601
+ /**
602
+ * Get the instances Map for a specific class (public API for stats/debugging)
603
+ */
604
+ getInstancesMap(Type) {
605
+ return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
606
+ }
607
+ /**
608
+ * Resolve an instance with ref counting (ownership semantics)
609
+ */
610
+ resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
611
+ const registryConfig = this.typeConfigs.get(Type.name);
612
+ const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
613
+ const config = { instanceId: instanceKey };
614
+ if (isolated) {
615
+ const instance$1 = new Type(constructorArgs);
616
+ instance$1.initConfig(config);
617
+ this.registerType(Type);
618
+ return instance$1;
619
+ }
620
+ const instances = this.ensureInstancesMap(Type);
621
+ const entry = instances.get(instanceKey);
622
+ if (entry) {
623
+ entry.refCount++;
624
+ return entry.instance;
625
+ }
626
+ const instance = new Type(constructorArgs);
627
+ instance.initConfig(config);
628
+ instances.set(instanceKey, {
629
+ instance,
630
+ refCount: 1
631
+ });
632
+ this.registerType(Type);
633
+ return instance;
634
+ }
635
+ /**
636
+ * Get an existing instance without ref counting (borrowing semantics)
637
+ * @throws Error if instance doesn't exist
638
+ */
639
+ get(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
640
+ const entry = this.ensureInstancesMap(Type).get(instanceKey);
641
+ if (!entry) throw new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found.\nUse .resolve() to create and claim ownership, or .getSafe() for conditional access.`);
642
+ const context = getGetterExecutionContext();
643
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
644
+ return entry.instance;
645
+ }
646
+ /**
647
+ * Safely get an existing instance (borrowing semantics with error handling)
648
+ * Returns discriminated union for type-safe conditional access
649
+ */
650
+ getSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
651
+ const entry = this.ensureInstancesMap(Type).get(instanceKey);
652
+ if (!entry) return {
653
+ error: /* @__PURE__ */ new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found`),
654
+ instance: null
655
+ };
656
+ const context = getGetterExecutionContext();
657
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
658
+ return {
659
+ error: null,
660
+ instance: entry.instance
661
+ };
662
+ }
663
+ /**
664
+ * Connect to an instance with borrowing semantics (for B2B communication)
665
+ * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
666
+ * Tracks cross-bloc dependency for reactive updates.
667
+ *
668
+ * Use this in bloc-to-bloc communication when you need to ensure an instance exists
669
+ * but don't want to claim ownership (no ref count increment).
670
+ *
671
+ * @param Type - The bloc class constructor
672
+ * @param instanceKey - Optional instance key (defaults to 'default')
673
+ * @param constructorArgs - Constructor arguments (only used if creating new instance)
674
+ * @returns The bloc instance
675
+ */
676
+ connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
677
+ const registryConfig = this.typeConfigs.get(Type.name);
678
+ if (isIsolatedClass(Type) || registryConfig?.isolated === true) throw new Error(`${BLAC_ERROR_PREFIX} Cannot use .connect() with isolated bloc "${Type.name}".\nIsolated blocs are component-scoped. Use .resolve() in components instead.`);
679
+ const instances = this.ensureInstancesMap(Type);
680
+ let entry = instances.get(instanceKey);
681
+ if (!entry) {
682
+ const instance = new Type(constructorArgs);
683
+ const config = { instanceId: instanceKey };
684
+ instance.initConfig(config);
685
+ entry = {
686
+ instance,
687
+ refCount: 1
688
+ };
689
+ instances.set(instanceKey, entry);
690
+ this.registerType(Type);
691
+ }
692
+ const context = getGetterExecutionContext();
693
+ if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
694
+ return entry.instance;
695
+ }
696
+ /**
697
+ * Release a reference to an instance
698
+ */
699
+ release(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, forceDispose = false) {
700
+ const instances = this.ensureInstancesMap(Type);
701
+ const entry = instances.get(instanceKey);
702
+ if (!entry) return;
703
+ if (forceDispose) {
704
+ if (!entry.instance.isDisposed) entry.instance.dispose();
705
+ instances.delete(instanceKey);
706
+ return;
707
+ }
708
+ entry.refCount--;
709
+ const keepAlive = isKeepAliveClass(Type);
710
+ if (entry.refCount <= 0 && !keepAlive) {
711
+ if (!entry.instance.isDisposed) entry.instance.dispose();
712
+ instances.delete(instanceKey);
713
+ }
714
+ }
715
+ /**
716
+ * Get all instances of a specific type
717
+ */
718
+ getAll(Type) {
719
+ const instances = this.ensureInstancesMap(Type);
720
+ const result = [];
721
+ for (const entry of instances.values()) result.push(entry.instance);
722
+ return result;
723
+ }
724
+ /**
725
+ * Safely iterate over all instances of a type
726
+ */
727
+ forEach(Type, callback) {
728
+ const instances = this.ensureInstancesMap(Type);
729
+ for (const entry of instances.values()) {
730
+ const instance = entry.instance;
731
+ if (!instance.isDisposed) try {
732
+ callback(instance);
733
+ } catch (error$1) {
734
+ console.error(`${BLAC_ERROR_PREFIX} forEach callback error for ${Type.name}:`, error$1);
735
+ }
736
+ }
737
+ }
738
+ /**
739
+ * Clear all instances of a specific type
740
+ */
741
+ clear(Type) {
742
+ const instances = this.ensureInstancesMap(Type);
743
+ for (const entry of instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
744
+ instances.clear();
745
+ }
746
+ /**
747
+ * Get reference count for an instance
748
+ */
749
+ getRefCount(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
750
+ return this.ensureInstancesMap(Type).get(instanceKey)?.refCount ?? 0;
751
+ }
752
+ /**
753
+ * Check if an instance exists
754
+ */
755
+ hasInstance(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
756
+ return this.ensureInstancesMap(Type).has(instanceKey);
757
+ }
758
+ /**
759
+ * Clear all instances from all types (for testing)
760
+ *
761
+ * Iterates all registered types and clears their instances.
762
+ * Also clears type tracking to reset the registry state.
763
+ */
764
+ clearAll() {
765
+ for (const Type of this.types) this.clear(Type);
766
+ this.types.clear();
767
+ this.typeConfigs.clear();
768
+ }
769
+ /**
770
+ * Get registry statistics (for debugging)
771
+ */
772
+ getStats() {
773
+ const typeBreakdown = {};
774
+ let totalInstances = 0;
775
+ for (const Type of this.types) {
776
+ const typeName = Type.name;
777
+ const count = this.getInstancesMap(Type).size;
778
+ typeBreakdown[typeName] = count;
779
+ totalInstances += count;
780
+ }
781
+ return {
782
+ registeredTypes: this.types.size,
783
+ totalInstances,
784
+ typeBreakdown
785
+ };
786
+ }
787
+ /**
788
+ * Get all registered types (for plugin system)
789
+ */
790
+ getTypes() {
791
+ return Array.from(this.types);
792
+ }
793
+ /**
794
+ * Subscribe to lifecycle events
795
+ * @param event - The lifecycle event to listen for
796
+ * @param listener - The listener function to call when the event occurs
797
+ * @returns Unsubscribe function
798
+ */
799
+ on(event, listener) {
800
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
801
+ this.listeners.get(event).add(listener);
802
+ return () => {
803
+ this.listeners.get(event)?.delete(listener);
804
+ };
805
+ }
806
+ /**
807
+ * Emit lifecycle event to all listeners
808
+ * @internal - Called by StateContainer/Vertex lifecycle methods
809
+ */
810
+ emit(event, ...args) {
811
+ const listeners = this.listeners.get(event);
812
+ if (!listeners || listeners.size === 0) return;
813
+ for (const listener of listeners) try {
814
+ listener(...args);
815
+ } catch (error$1) {
816
+ console.error(`${BLAC_ERROR_PREFIX} Listener error for '${event}':`, error$1);
817
+ }
818
+ }
819
+ };
820
+ /**
821
+ * Global default registry instance
822
+ */
823
+ const globalRegistry = new StateContainerRegistry();
824
+ /**
825
+ * Global plugin manager (initialized lazily)
826
+ */
827
+ let _globalPluginManager = null;
828
+ /**
829
+ * Get the global plugin manager
830
+ */
831
+ function getPluginManager() {
832
+ if (!_globalPluginManager) _globalPluginManager = createPluginManager(globalRegistry);
833
+ return _globalPluginManager;
834
+ }
835
+
836
+ //#endregion
837
+ //#region src/core/StateContainer.ts
838
+ /**
839
+ * Abstract base class for all state containers in BlaC.
840
+ * Provides lifecycle management, subscription handling, ref counting,
841
+ * and integration with the global registry.
842
+ *
843
+ * @template S - State type managed by this container
844
+ * @template P - Props type passed to the container (optional)
845
+ *
846
+ * @example
847
+ * ```ts
848
+ * class CounterBloc extends StateContainer<number> {
849
+ * constructor() {
850
+ * super(0);
851
+ * }
852
+ * increment() {
853
+ * this.emit(this.state + 1);
854
+ * }
855
+ * }
856
+ * ```
857
+ */
858
+ var StateContainer = class StateContainer {
859
+ static {
860
+ this.__excludeFromDevTools = false;
861
+ }
862
+ static {
863
+ this._registry = globalRegistry;
864
+ }
865
+ /**
866
+ * Get the global StateContainerRegistry
867
+ * @returns The registry managing all state container instances
868
+ */
869
+ static getRegistry() {
870
+ return StateContainer._registry;
871
+ }
872
+ /**
873
+ * Replace the global registry (clears existing instances)
874
+ * @param registry - The new registry to use
875
+ */
876
+ static setRegistry(registry) {
877
+ StateContainer._registry.clearAll();
878
+ StateContainer._registry = registry;
879
+ }
880
+ /**
881
+ * Register this class with the global registry
882
+ * @param isolated - Whether instances should be isolated (component-scoped)
883
+ */
884
+ static register(isolated = false) {
885
+ StateContainer._registry.register(this, isolated);
886
+ }
887
+ /**
888
+ * Resolve an instance with ref counting (ownership semantics).
889
+ * Creates a new instance if one doesn't exist, or returns existing and increments ref count.
890
+ * @param instanceKey - Optional instance key (defaults to 'default')
891
+ * @param constructorArgs - Arguments to pass to constructor if creating new instance
892
+ * @returns The state container instance
893
+ */
894
+ static resolve(instanceKey, constructorArgs) {
895
+ return StateContainer._registry.resolve(this, instanceKey, constructorArgs);
896
+ }
897
+ /**
898
+ * Get an existing instance without incrementing ref count (borrowing semantics).
899
+ * @param instanceKey - Optional instance key (defaults to 'default')
900
+ * @returns The state container instance
901
+ * @throws Error if instance doesn't exist
902
+ */
903
+ static get(instanceKey) {
904
+ return StateContainer._registry.get(this, instanceKey);
905
+ }
906
+ /**
907
+ * Safely get an existing instance with error handling.
908
+ * @param instanceKey - Optional instance key (defaults to 'default')
909
+ * @returns Discriminated union with either the instance or an error
910
+ */
911
+ static getSafe(instanceKey) {
912
+ return StateContainer._registry.getSafe(this, instanceKey);
913
+ }
914
+ /**
915
+ * Connect to an instance for bloc-to-bloc communication (borrowing semantics).
916
+ * Gets or creates instance without incrementing ref count.
917
+ * @param instanceKey - Optional instance key (defaults to 'default')
918
+ * @param constructorArgs - Arguments to pass to constructor if creating new instance
919
+ * @returns The state container instance
920
+ */
921
+ static connect(instanceKey, constructorArgs) {
922
+ return StateContainer._registry.connect(this, instanceKey, constructorArgs);
923
+ }
924
+ /**
925
+ * Release a reference to an instance.
926
+ * Decrements ref count and disposes when it reaches 0 (unless keepAlive).
927
+ * @param instanceKey - Optional instance key (defaults to 'default')
928
+ * @param forceDispose - Force immediate disposal regardless of ref count
929
+ */
930
+ static release(instanceKey, forceDispose = false) {
931
+ StateContainer._registry.release(this, instanceKey, forceDispose);
932
+ }
933
+ /**
934
+ * Get all instances of this class
935
+ * @returns Array of all instances
936
+ */
937
+ static getAll() {
938
+ return StateContainer._registry.getAll(this);
939
+ }
940
+ /**
941
+ * Iterate over all instances of this class
942
+ * @param callback - Function to call for each instance
943
+ */
944
+ static forEach(callback) {
945
+ StateContainer._registry.forEach(this, callback);
946
+ }
947
+ /**
948
+ * Clear all instances of this class (disposes them)
949
+ */
950
+ static clear() {
951
+ StateContainer._registry.clear(this);
952
+ }
953
+ /**
954
+ * Clear all instances from all registered types
955
+ */
956
+ static clearAllInstances() {
957
+ StateContainer._registry.clearAll();
958
+ }
959
+ /**
960
+ * Get registry statistics for debugging
961
+ * @returns Object with registeredTypes, totalInstances, and typeBreakdown
962
+ */
963
+ static getStats() {
964
+ return StateContainer._registry.getStats();
965
+ }
966
+ /**
967
+ * Get reference count for an instance
968
+ * @param instanceKey - Optional instance key (defaults to 'default')
969
+ * @returns Current ref count (0 if instance doesn't exist)
970
+ */
971
+ static getRefCount(instanceKey) {
972
+ return StateContainer._registry.getRefCount(this, instanceKey);
973
+ }
974
+ /**
975
+ * Check if an instance exists
976
+ * @param instanceKey - Optional instance key (defaults to 'default')
977
+ * @returns true if instance exists
978
+ */
979
+ static hasInstance(instanceKey) {
980
+ return StateContainer._registry.hasInstance(this, instanceKey);
981
+ }
982
+ /** Current props value passed to this container */
983
+ get props() {
984
+ return this._props;
985
+ }
986
+ constructor(initialState) {
987
+ this.listeners = /* @__PURE__ */ new Set();
988
+ this._disposed = false;
989
+ this.config = {};
990
+ this._props = void 0;
991
+ this.systemEventHandlers = /* @__PURE__ */ new Map();
992
+ this.name = this.constructor.name;
993
+ this.debug = false;
994
+ this.instanceId = generateSimpleId(this.constructor.name, "main");
995
+ this.createdAt = Date.now();
996
+ this.lastUpdateTimestamp = Date.now();
997
+ this.onSystemEvent = (event, handler) => {
998
+ let handlers = this.systemEventHandlers.get(event);
999
+ if (!handlers) {
1000
+ handlers = /* @__PURE__ */ new Set();
1001
+ this.systemEventHandlers.set(event, handlers);
1002
+ }
1003
+ handlers.add(handler);
1004
+ return () => {
1005
+ handlers?.delete(handler);
1006
+ };
1007
+ };
1008
+ this._state = initialState;
1009
+ }
1010
+ /**
1011
+ * Initialize configuration for this instance.
1012
+ * Called by the registry after construction.
1013
+ * @param config - Configuration options
1014
+ */
1015
+ initConfig(config) {
1016
+ this.config = { ...config };
1017
+ this.name = this.config.name || this.constructor.name;
1018
+ this.debug = this.config.debug ?? false;
1019
+ this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
1020
+ StateContainer._registry.emit("created", this);
1021
+ }
1022
+ /** Current state value */
1023
+ get state() {
1024
+ return this._state;
1025
+ }
1026
+ /** Whether this instance has been disposed */
1027
+ get isDisposed() {
1028
+ return this._disposed;
1029
+ }
1030
+ /**
1031
+ * Subscribe to state changes
1032
+ * @param listener - Function called when state changes
1033
+ * @returns Unsubscribe function
1034
+ */
1035
+ subscribe(listener) {
1036
+ if (this._disposed) throw new Error(`Cannot subscribe to disposed container ${this.name}`);
1037
+ this.listeners.add(listener);
1038
+ return () => this.listeners.delete(listener);
1039
+ }
1040
+ /**
1041
+ * Dispose this instance and clean up resources.
1042
+ * Clears all listeners and emits the 'dispose' system event.
1043
+ */
1044
+ dispose() {
1045
+ if (this._disposed) return;
1046
+ if (this.debug) console.log(`[${this.name}] Disposing...`);
1047
+ this._disposed = true;
1048
+ this.emitSystemEvent("dispose", void 0);
1049
+ this.listeners.clear();
1050
+ this.systemEventHandlers.clear();
1051
+ StateContainer._registry.emit("disposed", this);
1052
+ if (this.debug) console.log(`[${this.name}] Disposed successfully`);
1053
+ }
1054
+ /**
1055
+ * Emit a new state value and notify all listeners.
1056
+ * @param newState - The new state value
1057
+ * @protected
1058
+ */
1059
+ emit(newState) {
1060
+ if (this._disposed) throw new Error(`Cannot emit state from disposed container ${this.name}`);
1061
+ if (this._state === newState) return;
1062
+ const previousState = this._state;
1063
+ this._state = newState;
1064
+ this.emitSystemEvent("stateChanged", {
1065
+ state: newState,
1066
+ previousState
1067
+ });
1068
+ for (const listener of this.listeners) try {
1069
+ listener(newState);
1070
+ } catch (error$1) {
1071
+ console.error(`[${this.name}] Error in listener:`, error$1);
1072
+ }
1073
+ const stackTrace = this.captureStackTrace();
1074
+ StateContainer._registry.emit("stateChanged", this, previousState, newState, stackTrace);
1075
+ this.lastUpdateTimestamp = Date.now();
1076
+ }
1077
+ static {
1078
+ this.enableStackTrace = true;
1079
+ }
1080
+ captureStackTrace() {
1081
+ if (!StateContainer.enableStackTrace || typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
1082
+ try {
1083
+ const relevantLines = ((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(1);
1084
+ const formattedLines = [];
1085
+ for (const line of relevantLines) {
1086
+ if (!line.trim()) continue;
1087
+ if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("Cubit.patch") || line.includes("Vertex.")) continue;
1088
+ 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;
1089
+ const formatted = this.formatStackLine(line);
1090
+ if (formatted) formattedLines.push(formatted);
1091
+ }
1092
+ return formattedLines.join("\n");
1093
+ } catch {
1094
+ return "";
1095
+ }
1096
+ }
1097
+ formatStackLine(line) {
1098
+ const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
1099
+ if (!match) {
1100
+ const simpleMatch = line.match(/at\s+(.+?):(\d+):(\d+)/);
1101
+ if (simpleMatch) {
1102
+ const [, url$1, lineNum$1, col$1] = simpleMatch;
1103
+ return ` at ${this.cleanFilePath(url$1)}:${lineNum$1}:${col$1}`;
1104
+ }
1105
+ return null;
1106
+ }
1107
+ const [, functionName, url, lineNum, col] = match;
1108
+ return ` at ${functionName} (${this.cleanFilePath(url)}:${lineNum}:${col})`;
1109
+ }
1110
+ cleanFilePath(url) {
1111
+ let path = url.replace(/http:\/\/localhost:\d+\/@fs/, "").replace(/http:\/\/localhost:\d+\//, "").replace(/\?t=\d+/, "").replace(/\?v=[a-f0-9]+/, "");
1112
+ const projectMatch = path.match(/\/Projects\/blac\/(.+)/);
1113
+ if (projectMatch) path = projectMatch[1];
1114
+ const segments = path.split("/");
1115
+ if (segments.length > 3) path = segments.slice(-3).join("/");
1116
+ return path;
1117
+ }
1118
+ /**
1119
+ * Update state using a transform function.
1120
+ * @param updater - Function that receives current state and returns new state
1121
+ * @protected
1122
+ */
1123
+ update(updater) {
1124
+ if (this._disposed) throw new Error(`Cannot update state from disposed container ${this.name}`);
1125
+ this.emit(updater(this._state));
1126
+ }
1127
+ emitSystemEvent(event, payload) {
1128
+ const handlers = this.systemEventHandlers.get(event);
1129
+ if (!handlers) return;
1130
+ for (const handler of handlers) try {
1131
+ handler(payload);
1132
+ } catch (error$1) {
1133
+ console.error(`[${this.name}] Error in system event handler:`, error$1);
1134
+ }
1135
+ }
1136
+ /**
1137
+ * Update the props for this container.
1138
+ * Emits the 'propsUpdated' system event.
1139
+ * @param newProps - The new props value
1140
+ */
1141
+ updateProps(newProps) {
1142
+ if (this._disposed) throw new Error(`Cannot update props on disposed container ${this.name}`);
1143
+ const previousProps = this._props;
1144
+ this._props = newProps;
1145
+ this.emitSystemEvent("propsUpdated", {
1146
+ props: newProps,
1147
+ previousProps
1148
+ });
1149
+ if (this.debug) console.log(`[${this.name}] Props updated:`, {
1150
+ newProps,
1151
+ previousProps
1152
+ });
1153
+ }
1154
+ };
1155
+
1156
+ //#endregion
1157
+ //#region src/core/Cubit.ts
1158
+ /**
1159
+ * Simple state container with direct state emission.
1160
+ * Extends StateContainer with public methods for emitting and updating state.
1161
+ *
1162
+ * @template S - State type
1163
+ * @template P - Props type (optional)
1164
+ */
1165
+ var Cubit = class extends StateContainer {
1166
+ constructor(initialState) {
1167
+ super(initialState);
1168
+ this.patch = ((partial) => {
1169
+ if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
1170
+ this.update((current) => ({
1171
+ ...current,
1172
+ ...partial
1173
+ }));
1174
+ });
1175
+ }
1176
+ /**
1177
+ * Replace state with a new value and notify all listeners
1178
+ * @param newState - The new state value
1179
+ */
1180
+ emit(newState) {
1181
+ super["emit"](newState);
1182
+ }
1183
+ /**
1184
+ * Transform current state using an updater function and emit the new state
1185
+ * @param updater - Function that receives current state and returns new state
1186
+ */
1187
+ update(updater) {
1188
+ super["update"](updater);
1189
+ }
1190
+ };
1191
+
1192
+ //#endregion
1193
+ //#region src/core/Vertex.ts
1194
+ /**
1195
+ * Event-driven state container that processes events to update state.
1196
+ * Use with event handlers registered via the on() method.
1197
+ *
1198
+ * @template S - State type
1199
+ * @template E - Event type (extends BaseEvent)
1200
+ * @template P - Props type (optional)
1201
+ */
1202
+ var Vertex = class extends StateContainer {
1203
+ constructor(initialState) {
1204
+ super(initialState);
1205
+ this.eventHandlers = /* @__PURE__ */ new Map();
1206
+ this.isProcessing = false;
1207
+ this.eventQueue = [];
1208
+ this.on = (EventClass, handler) => {
1209
+ const className = EventClass.name;
1210
+ const registrations = this.eventHandlers.get(className) || [];
1211
+ registrations.push({
1212
+ eventClass: EventClass,
1213
+ handler
1214
+ });
1215
+ this.eventHandlers.set(className, registrations);
1216
+ };
1217
+ this.add = (event) => {
1218
+ StateContainer._registry.emit("eventAdded", this, event);
1219
+ this.processEvent(event);
1220
+ };
1221
+ }
1222
+ processEvent(event) {
1223
+ if (this.isProcessing) {
1224
+ this.eventQueue.push(event);
1225
+ return;
1226
+ }
1227
+ this.isProcessing = true;
1228
+ try {
1229
+ this.handleEvent(event);
1230
+ while (this.eventQueue.length > 0) {
1231
+ const nextEvent = this.eventQueue.shift();
1232
+ this.handleEvent(nextEvent);
1233
+ }
1234
+ } finally {
1235
+ this.isProcessing = false;
1236
+ }
1237
+ }
1238
+ handleEvent(event) {
1239
+ const className = event.constructor.name;
1240
+ const registrations = this.eventHandlers.get(className);
1241
+ if (!registrations || registrations.length === 0) {
1242
+ if (this.debug) console.warn(`No handler registered for event: ${className}`);
1243
+ return;
1244
+ }
1245
+ const emitFn = (state) => {
1246
+ this.emit(state);
1247
+ };
1248
+ for (const registration of registrations) try {
1249
+ registration.handler(event, emitFn);
1250
+ } catch (error$1) {
1251
+ if (this.debug) console.error(`Error handling event ${className}:`, error$1);
1252
+ this.onEventError(event, error$1);
1253
+ }
1254
+ }
1255
+ /**
1256
+ * Handle errors that occur during event processing.
1257
+ * Override this method to implement custom error handling.
1258
+ * @param _event - The event that caused the error
1259
+ * @param _error - The error that occurred
1260
+ */
1261
+ onEventError(_event, _error) {}
1262
+ };
1263
+
1264
+ //#endregion
1265
+ //#region src/plugin/BlacPlugin.ts
1266
+ /**
1267
+ * Type guard to check if a plugin has a required onInstall hook
1268
+ * @param plugin - The plugin to check
1269
+ * @returns true if the plugin implements BlacPluginWithInit
1270
+ */
1271
+ function hasInitHook(plugin) {
1272
+ return typeof plugin.onInstall === "function";
1273
+ }
1274
+
1275
+ //#endregion
1276
+ //#region src/tracking/proxy-tracker.ts
1277
+ /**
1278
+ * Functional proxy tracker for automatic dependency tracking
1279
+ *
1280
+ * This module provides a functional approach to proxy-based dependency tracking.
1281
+ * Unlike the class-based ProxyTracker, this uses a state object pattern that's
1282
+ * easier to integrate with functional programming patterns and framework hooks.
1283
+ *
1284
+ * @internal
1285
+ */
1286
+ /**
1287
+ * Check if a value can be proxied
1288
+ *
1289
+ * Returns true for plain objects and arrays only.
1290
+ * Excludes built-in objects like Date, Map, Set, etc.
1291
+ *
1292
+ * @internal
1293
+ */
1294
+ function isProxyable(value) {
1295
+ if (typeof value !== "object" || value === null) return false;
1296
+ const proto = Object.getPrototypeOf(value);
1297
+ return proto === Object.prototype || proto === Array.prototype;
1298
+ }
1299
+ /**
1300
+ * Create a new proxy tracker state
1301
+ *
1302
+ * @example
1303
+ * const state = createProxyTrackerState<MyState>();
1304
+ * startProxyTracking(state);
1305
+ * const proxy = createProxyForTarget(state, myObject);
1306
+ * // ... use proxy ...
1307
+ * const paths = stopProxyTracking(state);
1308
+ *
1309
+ * @internal
1310
+ */
1311
+ function createProxyTrackerState() {
1312
+ return {
1313
+ trackedPaths: /* @__PURE__ */ new Set(),
1314
+ isTracking: false,
1315
+ proxyCache: /* @__PURE__ */ new WeakMap(),
1316
+ boundFunctionsCache: null,
1317
+ lastProxiedState: null,
1318
+ lastProxy: null,
1319
+ maxDepth: 10
1320
+ };
1321
+ }
1322
+ /**
1323
+ * Start tracking property accesses
1324
+ *
1325
+ * Clears previous tracked paths and enables tracking mode.
1326
+ *
1327
+ * @internal
1328
+ */
1329
+ function startProxyTracking(state) {
1330
+ state.isTracking = true;
1331
+ state.trackedPaths.clear();
1332
+ }
1333
+ /**
1334
+ * Stop tracking and return the tracked paths
1335
+ *
1336
+ * Returns a new Set containing all tracked paths.
1337
+ *
1338
+ * @internal
1339
+ */
1340
+ function stopProxyTracking(state) {
1341
+ state.isTracking = false;
1342
+ return new Set(state.trackedPaths);
1343
+ }
1344
+ /**
1345
+ * Create a proxy for an array with property access tracking
1346
+ *
1347
+ * Tracks:
1348
+ * - Array element access (arr[0])
1349
+ * - Length access (arr.length)
1350
+ * - Array method calls (arr.map, arr.filter, etc.)
1351
+ *
1352
+ * @internal
1353
+ */
1354
+ function createArrayProxy(state, target, path, depth = 0) {
1355
+ const proxy = new Proxy(target, { get: (arr, prop) => {
1356
+ if (typeof prop === "symbol") return Reflect.get(arr, prop);
1357
+ const value = Reflect.get(arr, prop);
1358
+ if (typeof value === "function") {
1359
+ if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
1360
+ const cached = state.boundFunctionsCache.get(value);
1361
+ if (cached) return cached;
1362
+ const bound = value.bind(arr);
1363
+ state.boundFunctionsCache.set(value, bound);
1364
+ return bound;
1365
+ }
1366
+ if (prop === "length") {
1367
+ if (state.isTracking) {
1368
+ const fullPath$1 = path ? `${path}.length` : "length";
1369
+ state.trackedPaths.add(fullPath$1);
1370
+ }
1371
+ return value;
1372
+ }
1373
+ let fullPath;
1374
+ if (typeof prop === "string") {
1375
+ const index = Number(prop);
1376
+ if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
1377
+ else fullPath = path ? `${path}.${prop}` : prop;
1378
+ } else return value;
1379
+ if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
1380
+ if (state.isTracking) state.trackedPaths.add(fullPath);
1381
+ return value;
1382
+ } });
1383
+ state.proxyCache.set(target, proxy);
1384
+ return proxy;
1385
+ }
1386
+ /**
1387
+ * Create a proxy for an object with property access tracking
1388
+ *
1389
+ * This is the core proxy creation function that recursively creates proxies
1390
+ * for nested objects and arrays.
1391
+ *
1392
+ * Tracks:
1393
+ * - Property access (obj.prop)
1394
+ * - Nested property access (obj.nested.prop)
1395
+ * - 'in' operator usage ('prop' in obj)
1396
+ * - Object.keys, Object.entries, etc.
1397
+ *
1398
+ * @internal
1399
+ */
1400
+ function createProxyInternal(state, target, path = "", depth = 0) {
1401
+ if (!state.isTracking || !isProxyable(target)) return target;
1402
+ if (depth >= state.maxDepth) return target;
1403
+ if (state.proxyCache.has(target)) return state.proxyCache.get(target);
1404
+ if (Array.isArray(target)) return createArrayProxy(state, target, path, depth);
1405
+ const proxy = new Proxy(target, {
1406
+ get: (obj, prop) => {
1407
+ if (typeof prop === "symbol") return Reflect.get(obj, prop);
1408
+ const value = Reflect.get(obj, prop);
1409
+ if (typeof value === "function") {
1410
+ if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
1411
+ const cached = state.boundFunctionsCache.get(value);
1412
+ if (cached) return cached;
1413
+ const bound = value.bind(obj);
1414
+ state.boundFunctionsCache.set(value, bound);
1415
+ return bound;
1416
+ }
1417
+ const fullPath = path ? `${path}.${String(prop)}` : String(prop);
1418
+ if (typeof prop === "string" && state.isTracking) {
1419
+ if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
1420
+ }
1421
+ if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
1422
+ return value;
1423
+ },
1424
+ has: (obj, prop) => {
1425
+ if (typeof prop === "string" && state.isTracking) {
1426
+ const fullPath = path ? `${path}.${prop}` : prop;
1427
+ state.trackedPaths.add(fullPath);
1428
+ }
1429
+ return Reflect.has(obj, prop);
1430
+ },
1431
+ ownKeys: (obj) => {
1432
+ if (state.isTracking && path) state.trackedPaths.add(path);
1433
+ return Reflect.ownKeys(obj);
1434
+ }
1435
+ });
1436
+ state.proxyCache.set(target, proxy);
1437
+ return proxy;
1438
+ }
1439
+ /**
1440
+ * Create a proxy for a target with caching
1441
+ *
1442
+ * If the target hasn't changed since the last call, returns the cached proxy.
1443
+ * Otherwise, creates a new proxy and caches it.
1444
+ *
1445
+ * This is the main entry point for creating tracked proxies.
1446
+ *
1447
+ * @internal
1448
+ */
1449
+ function createProxyForTarget(state, target) {
1450
+ if (state.lastProxiedState === target && state.lastProxy) return state.lastProxy;
1451
+ state.proxyCache = /* @__PURE__ */ new WeakMap();
1452
+ state.boundFunctionsCache = null;
1453
+ const proxy = createProxyInternal(state, target, "", 0);
1454
+ state.lastProxiedState = target;
1455
+ state.lastProxy = proxy;
1456
+ return proxy;
1457
+ }
1458
+
1459
+ //#endregion
1460
+ //#region src/tracking/path-utils.ts
1461
+ /**
1462
+ * Path utilities for dependency tracking
1463
+ *
1464
+ * Provides utilities for parsing property paths and extracting values
1465
+ * from nested objects using path strings.
1466
+ *
1467
+ * @internal
1468
+ */
1469
+ /**
1470
+ * Parse a property path string into an array of segments
1471
+ *
1472
+ * @internal
1473
+ *
1474
+ * Handles both dot notation (a.b.c) and bracket notation (a[0].b)
1475
+ *
1476
+ * @example
1477
+ * ```ts
1478
+ * parsePath('user.name') // ['user', 'name']
1479
+ * parsePath('items[0].name') // ['items', '0', 'name']
1480
+ * parsePath('data.users[2].address.city') // ['data', 'users', '2', 'address', 'city']
1481
+ * ```
1482
+ */
1483
+ function parsePath(path) {
1484
+ const segments = [];
1485
+ let current = "";
1486
+ let i = 0;
1487
+ while (i < path.length) {
1488
+ const char = path[i];
1489
+ if (char === ".") {
1490
+ if (current) segments.push(current);
1491
+ current = "";
1492
+ } else if (char === "[") {
1493
+ if (current) segments.push(current);
1494
+ current = "";
1495
+ i++;
1496
+ while (i < path.length && path[i] !== "]") current += path[i++];
1497
+ if (current) segments.push(current);
1498
+ current = "";
1499
+ } else current += char;
1500
+ i++;
1501
+ }
1502
+ if (current) segments.push(current);
1503
+ return segments;
1504
+ }
1505
+ /**
1506
+ * Get a value from an object using a path of segments
1507
+ *
1508
+ * @example
1509
+ * ```ts
1510
+ * const obj = { user: { name: 'Alice', age: 30 } }
1511
+ * getValueAtPath(obj, ['user', 'name']) // 'Alice'
1512
+ * getValueAtPath(obj, ['user', 'age']) // 30
1513
+ * getValueAtPath(obj, ['user', 'missing']) // undefined
1514
+ * ```
1515
+ *
1516
+ * @internal
1517
+ */
1518
+ function getValueAtPath(obj, segments) {
1519
+ if (obj == null) return void 0;
1520
+ let current = obj;
1521
+ for (let i = 0; i < segments.length; i++) {
1522
+ current = current[segments[i]];
1523
+ if (current == null) return void 0;
1524
+ }
1525
+ return current;
1526
+ }
1527
+ /**
1528
+ * Shallow equality comparison for arrays
1529
+ *
1530
+ * Compares two arrays element-by-element using Object.is
1531
+ *
1532
+ * @example
1533
+ * ```ts
1534
+ * shallowEqual([1, 2, 3], [1, 2, 3]) // true
1535
+ * shallowEqual([1, 2, 3], [1, 2, 4]) // false
1536
+ * shallowEqual([1, 2], [1, 2, 3]) // false
1537
+ * ```
1538
+ *
1539
+ * @internal
1540
+ */
1541
+ function shallowEqual(arr1, arr2) {
1542
+ if (arr1.length !== arr2.length) return false;
1543
+ for (let i = 0; i < arr1.length; i++) if (!Object.is(arr1[i], arr2[i])) return false;
1544
+ return true;
1545
+ }
1546
+
1547
+ //#endregion
1548
+ //#region src/tracking/dependency-tracker.ts
1549
+ function isChildPath(child, parent) {
1550
+ if (child === parent) return false;
1551
+ return child.startsWith(parent + ".") || child.startsWith(parent + "[");
1552
+ }
1553
+ /**
1554
+ * Extract the parent array path from an array child path
1555
+ * Examples:
1556
+ * 'messages.length' -> 'messages'
1557
+ * 'messages[0]' -> 'messages'
1558
+ * 'messages[0].text' -> 'messages'
1559
+ * 'data.items[0]' -> 'data.items'
1560
+ */
1561
+ function getArrayParentPath(path) {
1562
+ if (path.endsWith(".length")) return path.slice(0, -7);
1563
+ const arrayIndexMatch = path.match(/^(.+?)\[\d+\]/);
1564
+ if (arrayIndexMatch) return arrayIndexMatch[1];
1565
+ return null;
1566
+ }
1567
+ /**
1568
+ * @internal
1569
+ */
1570
+ function optimizeTrackedPaths(paths) {
1571
+ if (paths.size === 0) return /* @__PURE__ */ new Set();
1572
+ if (paths.size === 1) return new Set(paths);
1573
+ const sortedPaths = Array.from(paths).sort((a, b) => b.length - a.length);
1574
+ const optimized = /* @__PURE__ */ new Set();
1575
+ for (const path of sortedPaths) {
1576
+ let hasMoreSpecificChild = false;
1577
+ for (const optimizedPath of optimized) if (isChildPath(optimizedPath, path)) {
1578
+ hasMoreSpecificChild = true;
1579
+ break;
1580
+ }
1581
+ if (!hasMoreSpecificChild) optimized.add(path);
1582
+ }
1583
+ const arrayParents = /* @__PURE__ */ new Set();
1584
+ for (const path of optimized) {
1585
+ const arrayParent = getArrayParentPath(path);
1586
+ if (arrayParent) arrayParents.add(arrayParent);
1587
+ }
1588
+ for (const arrayParent of arrayParents) optimized.add(arrayParent);
1589
+ return optimized;
1590
+ }
1591
+ /**
1592
+ * @internal
1593
+ */
1594
+ function createTrackerState() {
1595
+ return {
1596
+ proxyTrackerState: createProxyTrackerState(),
1597
+ previousRenderPaths: /* @__PURE__ */ new Set(),
1598
+ currentRenderPaths: /* @__PURE__ */ new Set(),
1599
+ pathCache: /* @__PURE__ */ new Map(),
1600
+ lastCheckedState: null,
1601
+ lastCheckedValues: /* @__PURE__ */ new Map()
1602
+ };
1603
+ }
1604
+ /**
1605
+ * @internal
1606
+ */
1607
+ function startTracking(tracker) {
1608
+ startProxyTracking(tracker.proxyTrackerState);
1609
+ }
1610
+ /**
1611
+ * @internal
1612
+ */
1613
+ function createProxy(tracker, state) {
1614
+ return createProxyForTarget(tracker.proxyTrackerState, state);
1615
+ }
1616
+ /**
1617
+ * @internal
1618
+ */
1619
+ function captureTrackedPaths(tracker, state) {
1620
+ tracker.previousRenderPaths = tracker.currentRenderPaths;
1621
+ tracker.currentRenderPaths = optimizeTrackedPaths(stopProxyTracking(tracker.proxyTrackerState));
1622
+ if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
1623
+ const trackedPathsUnion = new Set(tracker.previousRenderPaths);
1624
+ for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
1625
+ const canReuseCache = tracker.lastCheckedState === state;
1626
+ for (const path of trackedPathsUnion) if (!tracker.pathCache.has(path)) {
1627
+ const segments = parsePath(path);
1628
+ const value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, segments);
1629
+ tracker.pathCache.set(path, {
1630
+ segments,
1631
+ value
1632
+ });
1633
+ } else {
1634
+ const info$1 = tracker.pathCache.get(path);
1635
+ info$1.value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, info$1.segments);
1636
+ }
1637
+ tracker.lastCheckedValues.clear();
1638
+ }
1639
+ /**
1640
+ * @internal
1641
+ */
1642
+ function hasChanges(tracker, state) {
1643
+ if (tracker.pathCache.size === 0) return true;
1644
+ tracker.lastCheckedValues.clear();
1645
+ for (const [path, info$1] of tracker.pathCache.entries()) {
1646
+ const currentValue = getValueAtPath(state, info$1.segments);
1647
+ tracker.lastCheckedValues.set(path, currentValue);
1648
+ if (!Object.is(currentValue, info$1.value)) {
1649
+ tracker.lastCheckedState = state;
1650
+ return true;
1651
+ }
1652
+ }
1653
+ tracker.lastCheckedState = state;
1654
+ return false;
1655
+ }
1656
+ /**
1657
+ * @internal
1658
+ */
1659
+ function hasTrackedData(tracker) {
1660
+ return tracker.proxyTrackerState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
1661
+ }
1662
+
1663
+ //#endregion
1664
+ //#region src/adapter/framework-adapter.ts
1665
+ /**
1666
+ * Manages subscriptions to external bloc dependencies for getter tracking.
1667
+ * When a getter accesses another bloc's state, this manager ensures
1668
+ * re-renders occur when those external dependencies change.
1669
+ */
1670
+ var ExternalDependencyManager = class {
1671
+ constructor() {
1672
+ this.subscriptions = [];
1673
+ this.previousDeps = /* @__PURE__ */ new Set();
1674
+ }
1675
+ areDependenciesEqual(oldDeps, newDeps) {
1676
+ if (oldDeps.size !== newDeps.size) return false;
1677
+ for (const dep of newDeps) if (!oldDeps.has(dep)) return false;
1678
+ return true;
1679
+ }
1680
+ /**
1681
+ * Update subscriptions to external bloc dependencies.
1682
+ * Creates subscriptions to blocs accessed via getters.
1683
+ * @param getterTracker - The getter tracker state with external dependencies
1684
+ * @param rawInstance - The primary bloc instance (excluded from subscriptions)
1685
+ * @param onGetterChange - Callback to invoke when external dependency changes
1686
+ * @returns true if subscriptions were updated, false if unchanged
1687
+ */
1688
+ updateSubscriptions(getterTracker, rawInstance, onGetterChange) {
1689
+ if (!getterTracker?.externalDependencies) return false;
1690
+ const currentDeps = getterTracker.externalDependencies;
1691
+ if (this.areDependenciesEqual(this.previousDeps, currentDeps)) {
1692
+ clearExternalDependencies(getterTracker);
1693
+ return false;
1694
+ }
1695
+ this.cleanup();
1696
+ for (const externalBloc of currentDeps) {
1697
+ if (externalBloc === rawInstance) continue;
1698
+ const unsub = externalBloc.subscribe(() => {
1699
+ invalidateRenderCache(getterTracker);
1700
+ if (hasGetterChanges(rawInstance, getterTracker)) onGetterChange();
1701
+ });
1702
+ this.subscriptions.push(unsub);
1703
+ }
1704
+ this.previousDeps = new Set(currentDeps);
1705
+ clearExternalDependencies(getterTracker);
1706
+ return true;
1707
+ }
1708
+ /**
1709
+ * Clean up all active subscriptions
1710
+ */
1711
+ cleanup() {
1712
+ this.subscriptions.forEach((unsub) => unsub());
1713
+ this.subscriptions = [];
1714
+ }
1715
+ };
1716
+ /**
1717
+ * Create a subscribe function for auto-tracking mode.
1718
+ * Only triggers callback when tracked properties change.
1719
+ * @param instance - The state container instance
1720
+ * @param adapterState - The adapter state for tracking
1721
+ * @returns Subscribe function for use with useSyncExternalStore
1722
+ */
1723
+ function createAutoTrackSubscribe(instance, adapterState) {
1724
+ return (callback) => {
1725
+ return instance.subscribe(() => {
1726
+ const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
1727
+ const hasStateDeps = tracker.pathCache && tracker.pathCache.size > 0;
1728
+ const hasGetterDeps = adapterState.getterTracker && adapterState.getterTracker.trackedGetters.size > 0;
1729
+ const isPrimitiveState = instance.state !== null && typeof instance.state !== "object" && typeof instance.state !== "function";
1730
+ if (!hasStateDeps && !hasGetterDeps && !isPrimitiveState) return;
1731
+ let stateChanged = hasChanges(tracker, instance.state);
1732
+ if (!hasStateDeps && hasGetterDeps) stateChanged = false;
1733
+ if (stateChanged) {
1734
+ callback();
1735
+ return;
1736
+ }
1737
+ if (hasGetterChanges(instance, adapterState.getterTracker)) callback();
1738
+ });
1739
+ };
1740
+ }
1741
+ /**
1742
+ * Create a subscribe function for manual dependency tracking mode.
1743
+ * Only triggers callback when dependencies array changes.
1744
+ * @param instance - The state container instance
1745
+ * @param adapterState - The adapter state for caching
1746
+ * @param config - Configuration with dependencies function
1747
+ * @returns Subscribe function for use with useSyncExternalStore
1748
+ */
1749
+ function createManualDepsSubscribe(instance, adapterState, config) {
1750
+ return (callback) => {
1751
+ return instance.subscribe(() => {
1752
+ const newDeps = config.dependencies(instance.state, instance);
1753
+ if (!adapterState.manualDepsCache || !shallowEqual(adapterState.manualDepsCache, newDeps)) {
1754
+ adapterState.manualDepsCache = newDeps;
1755
+ callback();
1756
+ }
1757
+ });
1758
+ };
1759
+ }
1760
+ /**
1761
+ * Create a subscribe function for no-tracking mode.
1762
+ * Triggers callback on every state change.
1763
+ * @param instance - The state container instance
1764
+ * @returns Subscribe function for use with useSyncExternalStore
1765
+ */
1766
+ function createNoTrackSubscribe(instance) {
1767
+ return (callback) => instance.subscribe(callback);
1768
+ }
1769
+ /**
1770
+ * Create a snapshot function for auto-tracking mode.
1771
+ * Returns a proxied state that tracks property access.
1772
+ * @param instance - The state container instance
1773
+ * @param adapterState - The adapter state for tracking
1774
+ * @returns Snapshot function for use with useSyncExternalStore
1775
+ */
1776
+ function createAutoTrackSnapshot(instance, adapterState) {
1777
+ return () => {
1778
+ const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
1779
+ if (hasTrackedData(tracker)) captureTrackedPaths(tracker, instance.state);
1780
+ if (adapterState.getterTracker) {
1781
+ invalidateRenderCache(adapterState.getterTracker);
1782
+ commitTrackedGetters(adapterState.getterTracker);
1783
+ adapterState.getterTracker.isTracking = true;
1784
+ setActiveTracker(instance, adapterState.getterTracker);
1785
+ }
1786
+ startTracking(tracker);
1787
+ return createProxy(tracker, instance.state);
1788
+ };
1789
+ }
1790
+ /**
1791
+ * Create a snapshot function for manual dependency tracking mode.
1792
+ * Caches dependencies for comparison on next render.
1793
+ * @param instance - The state container instance
1794
+ * @param adapterState - The adapter state for caching
1795
+ * @param config - Configuration with dependencies function
1796
+ * @returns Snapshot function for use with useSyncExternalStore
1797
+ */
1798
+ function createManualDepsSnapshot(instance, adapterState, config) {
1799
+ return () => {
1800
+ adapterState.manualDepsCache = config.dependencies(instance.state, instance);
1801
+ return instance.state;
1802
+ };
1803
+ }
1804
+ /**
1805
+ * Create a snapshot function for no-tracking mode.
1806
+ * Returns the raw state directly.
1807
+ * @param instance - The state container instance
1808
+ * @returns Snapshot function for use with useSyncExternalStore
1809
+ */
1810
+ function createNoTrackSnapshot(instance) {
1811
+ return () => instance.state;
1812
+ }
1813
+ /**
1814
+ * Initialize adapter state for auto-tracking mode.
1815
+ * Creates getter tracker and proxied bloc instance.
1816
+ * @param instance - The state container instance
1817
+ * @returns Initialized adapter state
1818
+ */
1819
+ function initAutoTrackState(instance) {
1820
+ return {
1821
+ tracker: null,
1822
+ manualDepsCache: null,
1823
+ getterTracker: createGetterTracker(),
1824
+ proxiedBloc: createBlocProxy(instance)
1825
+ };
1826
+ }
1827
+ /**
1828
+ * Initialize adapter state for manual dependency tracking mode.
1829
+ * No proxy is created; bloc is used directly.
1830
+ * @param instance - The state container instance
1831
+ * @returns Initialized adapter state
1832
+ */
1833
+ function initManualDepsState(instance) {
1834
+ return {
1835
+ tracker: null,
1836
+ manualDepsCache: null,
1837
+ getterTracker: null,
1838
+ proxiedBloc: instance
1839
+ };
1840
+ }
1841
+ /**
1842
+ * Initialize adapter state for no-tracking mode.
1843
+ * No tracking or proxy is created.
1844
+ * @param instance - The state container instance
1845
+ * @returns Initialized adapter state
1846
+ */
1847
+ function initNoTrackState(instance) {
1848
+ return {
1849
+ tracker: null,
1850
+ manualDepsCache: null,
1851
+ getterTracker: null,
1852
+ proxiedBloc: instance
1853
+ };
1854
+ }
1855
+ /**
1856
+ * Disable getter tracking after render phase completes.
1857
+ * Clears the active tracker to prevent tracking outside of render.
1858
+ * @param adapterState - The adapter state
1859
+ * @param rawInstance - The raw bloc instance
1860
+ */
1861
+ function disableGetterTracking(adapterState, rawInstance) {
1862
+ if (adapterState.getterTracker) {
1863
+ adapterState.getterTracker.isTracking = false;
1864
+ clearActiveTracker(rawInstance);
1865
+ }
1866
+ }
1867
+
1868
+ //#endregion
1869
+ //#region src/logging/Logger.ts
1870
+ /**
1871
+ * Log severity levels (lower number = higher severity)
1872
+ */
1873
+ let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
1874
+ /** Critical errors that may cause application failure */
1875
+ LogLevel$1[LogLevel$1["ERROR"] = 0] = "ERROR";
1876
+ /** Warning conditions that should be addressed */
1877
+ LogLevel$1[LogLevel$1["WARN"] = 1] = "WARN";
1878
+ /** Informational messages about application state */
1879
+ LogLevel$1[LogLevel$1["INFO"] = 2] = "INFO";
1880
+ /** Detailed debugging information */
1881
+ LogLevel$1[LogLevel$1["DEBUG"] = 3] = "DEBUG";
1882
+ return LogLevel$1;
1883
+ }({});
1884
+ /**
1885
+ * Creates a logger instance with given configuration
1886
+ *
1887
+ * @param config - Logger configuration
1888
+ * @returns Logger instance with debug, info, warn, error, and configure methods
1889
+ *
1890
+ * @example
1891
+ * ```ts
1892
+ * const logger = createLogger({
1893
+ * enabled: true,
1894
+ * level: LogLevel.DEBUG,
1895
+ * output: (entry) => console.log(JSON.stringify(entry))
1896
+ * });
1897
+ * logger.debug('MyComponent', 'Rendering', { props: { foo: 'bar' } });
1898
+ * ```
1899
+ */
1900
+ function createLogger(config) {
1901
+ const shouldLog = (level) => {
1902
+ return config.enabled && config.level >= level;
1903
+ };
1904
+ const log = (level, context, message, data) => {
1905
+ try {
1906
+ const entry = {
1907
+ level,
1908
+ context,
1909
+ message,
1910
+ timestamp: Date.now(),
1911
+ ...data !== void 0 && { data: serialize(data) }
1912
+ };
1913
+ config.output(entry);
1914
+ } catch (e) {
1915
+ console.error("[BlacLogger] Error logging:", e);
1916
+ }
1917
+ };
1918
+ const serialize = (data) => {
1919
+ try {
1920
+ return JSON.parse(JSON.stringify(data));
1921
+ } catch {
1922
+ return String(data);
1923
+ }
1924
+ };
1925
+ return {
1926
+ debug: (context, message, data) => {
1927
+ if (shouldLog(LogLevel.DEBUG)) log("DEBUG", context, message, data);
1928
+ },
1929
+ info: (context, message, data) => {
1930
+ if (shouldLog(LogLevel.INFO)) log("INFO", context, message, data);
1931
+ },
1932
+ warn: (context, message, data) => {
1933
+ if (shouldLog(LogLevel.WARN)) log("WARN", context, message, data);
1934
+ },
1935
+ error: (context, message, data) => {
1936
+ if (shouldLog(LogLevel.ERROR)) log("ERROR", context, message, data);
1937
+ },
1938
+ configure: (opts) => {
1939
+ Object.assign(config, opts);
1940
+ }
1941
+ };
1942
+ }
1943
+ let currentConfig = {
1944
+ enabled: false,
1945
+ level: LogLevel.INFO,
1946
+ output: (entry) => console.log(JSON.stringify(entry))
1947
+ };
1948
+ let defaultLogger = createLogger(currentConfig);
1949
+ /**
1950
+ * Log a debug message (tree-shakeable export)
1951
+ * @param context - Context identifier (module or component name)
1952
+ * @param message - Log message
1953
+ * @param data - Optional structured data
1954
+ */
1955
+ const debug = (context, message, data) => defaultLogger.debug(context, message, data);
1956
+ /**
1957
+ * Log an info message (tree-shakeable export)
1958
+ * @param context - Context identifier (module or component name)
1959
+ * @param message - Log message
1960
+ * @param data - Optional structured data
1961
+ */
1962
+ const info = (context, message, data) => defaultLogger.info(context, message, data);
1963
+ /**
1964
+ * Log a warning message (tree-shakeable export)
1965
+ * @param context - Context identifier (module or component name)
1966
+ * @param message - Log message
1967
+ * @param data - Optional structured data
1968
+ */
1969
+ const warn = (context, message, data) => defaultLogger.warn(context, message, data);
1970
+ /**
1971
+ * Log an error message (tree-shakeable export)
1972
+ * @param context - Context identifier (module or component name)
1973
+ * @param message - Log message
1974
+ * @param data - Optional structured data
1975
+ */
1976
+ const error = (context, message, data) => defaultLogger.error(context, message, data);
1977
+ /**
1978
+ * Configuration function that recreates the default logger
1979
+ *
1980
+ * @param opts - Partial logger configuration
1981
+ *
1982
+ * @example
1983
+ * ```ts
1984
+ * configureLogger({ enabled: true, level: LogLevel.DEBUG });
1985
+ * ```
1986
+ */
1987
+ function configureLogger(opts) {
1988
+ currentConfig = {
1989
+ ...currentConfig,
1990
+ ...opts
1991
+ };
1992
+ defaultLogger = createLogger(currentConfig);
1993
+ }
1994
+
1995
+ //#endregion
1996
+ //#region src/decorators/blac.ts
1997
+ /**
1998
+ * Decorator to configure StateContainer classes.
1999
+ *
2000
+ * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)
2001
+ * ```ts
2002
+ * @blac({ isolated: true })
2003
+ * class FormBloc extends Cubit<FormState> {}
2004
+ *
2005
+ * @blac({ keepAlive: true })
2006
+ * class AuthBloc extends Cubit<AuthState> {}
2007
+ *
2008
+ * @blac({ excludeFromDevTools: true })
2009
+ * class InternalBloc extends Cubit<InternalState> {}
2010
+ * ```
2011
+ *
2012
+ * @example Function syntax (no decorator support needed)
2013
+ * ```ts
2014
+ * const FormBloc = blac({ isolated: true })(
2015
+ * class extends Cubit<FormState> {}
2016
+ * );
2017
+ * ```
2018
+ */
2019
+ function blac(options) {
2020
+ return function(target, _context) {
2021
+ if ("isolated" in options && options.isolated) target[BLAC_STATIC_PROPS.ISOLATED] = true;
2022
+ if ("keepAlive" in options && options.keepAlive) target[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;
2023
+ if ("excludeFromDevTools" in options && options.excludeFromDevTools) target[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;
2024
+ return target;
2025
+ };
2026
+ }
2027
+
2028
+ //#endregion
2029
+ exports.BLAC_DEFAULTS = BLAC_DEFAULTS;
2030
+ exports.BLAC_ERROR_PREFIX = BLAC_ERROR_PREFIX;
2031
+ exports.BLAC_ID_PATTERNS = BLAC_ID_PATTERNS;
2032
+ exports.BLAC_STATIC_PROPS = BLAC_STATIC_PROPS;
2033
+ exports.Cubit = Cubit;
2034
+ exports.ExternalDependencyManager = ExternalDependencyManager;
2035
+ exports.LogLevel = LogLevel;
2036
+ exports.PluginManager = PluginManager;
2037
+ exports.StateContainer = StateContainer;
2038
+ exports.StateContainerRegistry = StateContainerRegistry;
2039
+ exports.Vertex = Vertex;
2040
+ exports.__resetIdCounters = __resetIdCounters;
2041
+ exports.blac = blac;
2042
+ exports.captureTrackedPaths = captureTrackedPaths;
2043
+ exports.clearActiveTracker = clearActiveTracker;
2044
+ exports.clearExternalDependencies = clearExternalDependencies;
2045
+ exports.commitTrackedGetters = commitTrackedGetters;
2046
+ exports.configureLogger = configureLogger;
2047
+ exports.createArrayProxy = createArrayProxy;
2048
+ exports.createAutoTrackSnapshot = createAutoTrackSnapshot;
2049
+ exports.createAutoTrackSubscribe = createAutoTrackSubscribe;
2050
+ exports.createBlocProxy = createBlocProxy;
2051
+ exports.createGetterTracker = createGetterTracker;
2052
+ exports.createIdGenerator = createIdGenerator;
2053
+ exports.createLogger = createLogger;
2054
+ exports.createManualDepsSnapshot = createManualDepsSnapshot;
2055
+ exports.createManualDepsSubscribe = createManualDepsSubscribe;
2056
+ exports.createNoTrackSnapshot = createNoTrackSnapshot;
2057
+ exports.createNoTrackSubscribe = createNoTrackSubscribe;
2058
+ exports.createPluginManager = createPluginManager;
2059
+ exports.createProxy = createProxy;
2060
+ exports.createProxyForTarget = createProxyForTarget;
2061
+ exports.createProxyInternal = createProxyInternal;
2062
+ exports.createProxyTrackerState = createProxyTrackerState;
2063
+ exports.createTrackerState = createTrackerState;
2064
+ exports.debug = debug;
2065
+ exports.disableGetterTracking = disableGetterTracking;
2066
+ exports.error = error;
2067
+ exports.generateId = generateId;
2068
+ exports.generateIsolatedKey = generateIsolatedKey;
2069
+ exports.generateSimpleId = generateSimpleId;
2070
+ exports.getActiveTracker = getActiveTracker;
2071
+ exports.getDescriptor = getDescriptor;
2072
+ exports.getGetterExecutionContext = getGetterExecutionContext;
2073
+ exports.getPluginManager = getPluginManager;
2074
+ exports.getStaticProp = getStaticProp;
2075
+ exports.getValueAtPath = getValueAtPath;
2076
+ exports.globalRegistry = globalRegistry;
2077
+ exports.hasChanges = hasChanges;
2078
+ exports.hasGetterChanges = hasGetterChanges;
2079
+ exports.hasInitHook = hasInitHook;
2080
+ exports.hasTrackedData = hasTrackedData;
2081
+ exports.info = info;
2082
+ exports.initAutoTrackState = initAutoTrackState;
2083
+ exports.initManualDepsState = initManualDepsState;
2084
+ exports.initNoTrackState = initNoTrackState;
2085
+ exports.invalidateRenderCache = invalidateRenderCache;
2086
+ exports.isExcludedFromDevTools = isExcludedFromDevTools;
2087
+ exports.isGetter = isGetter;
2088
+ exports.isIsolatedClass = isIsolatedClass;
2089
+ exports.isIsolatedKey = isIsolatedKey;
2090
+ exports.isKeepAliveClass = isKeepAliveClass;
2091
+ exports.isProxyable = isProxyable;
2092
+ exports.parsePath = parsePath;
2093
+ exports.resetGetterTracker = resetGetterTracker;
2094
+ exports.setActiveTracker = setActiveTracker;
2095
+ exports.shallowEqual = shallowEqual;
2096
+ exports.startProxyTracking = startProxyTracking;
2097
+ exports.startTracking = startTracking;
2098
+ exports.stopProxyTracking = stopProxyTracking;
2099
+ exports.warn = warn;
2100
+ //# sourceMappingURL=index.cjs.map