@blac/core 2.0.5 → 2.0.7

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 (55) hide show
  1. package/README.md +98 -129
  2. package/dist/StateContainerRegistry-COxgpYAw.cjs +601 -0
  3. package/dist/StateContainerRegistry-COxgpYAw.cjs.map +1 -0
  4. package/dist/StateContainerRegistry-DS_wyKck.js +566 -0
  5. package/dist/StateContainerRegistry-DS_wyKck.js.map +1 -0
  6. package/dist/branded-DndTHjv2.cjs +18 -0
  7. package/dist/branded-DndTHjv2.cjs.map +1 -0
  8. package/dist/branded-mwGzzPF1.js +13 -0
  9. package/dist/branded-mwGzzPF1.js.map +1 -0
  10. package/dist/config-BD9MkJ0k.js +42 -0
  11. package/dist/config-BD9MkJ0k.js.map +1 -0
  12. package/dist/config-DTuCbHcJ.cjs +101 -0
  13. package/dist/config-DTuCbHcJ.cjs.map +1 -0
  14. package/dist/constants.d.cts +73 -0
  15. package/dist/constants.d.ts +0 -8
  16. package/dist/core/Cubit.d.ts +0 -17
  17. package/dist/core/StateContainer.d.ts +37 -88
  18. package/dist/core/StateContainerRegistry.d.ts +2 -3
  19. package/dist/core/symbols.d.ts +2 -0
  20. package/dist/debug.cjs +12 -0
  21. package/dist/debug.d.cts +35 -0
  22. package/dist/debug.js +3 -0
  23. package/dist/decorators/blac.d.ts +3 -10
  24. package/dist/index.cjs +175 -1613
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +2 -4
  27. package/dist/index.d.ts +2 -4
  28. package/dist/index.js +142 -1564
  29. package/dist/index.js.map +1 -1
  30. package/dist/plugin/BlacPlugin.d.ts +34 -3
  31. package/dist/plugins.cjs +4 -0
  32. package/dist/plugins.d.cts +25 -0
  33. package/dist/plugins.js +2 -0
  34. package/dist/registry/management.d.ts +1 -1
  35. package/dist/resolve-dependencies-8xtWXd53.js +648 -0
  36. package/dist/resolve-dependencies-8xtWXd53.js.map +1 -0
  37. package/dist/resolve-dependencies-DJ2UBTq1.cjs +773 -0
  38. package/dist/resolve-dependencies-DJ2UBTq1.cjs.map +1 -0
  39. package/dist/tracked-12W-U7tq.js +181 -0
  40. package/dist/tracked-12W-U7tq.js.map +1 -0
  41. package/dist/tracked-DLnq137F.cjs +216 -0
  42. package/dist/tracked-DLnq137F.cjs.map +1 -0
  43. package/dist/tracking.cjs +18 -0
  44. package/dist/tracking.d.cts +9 -0
  45. package/dist/tracking.js +2 -0
  46. package/dist/types/utilities.d.ts +0 -1
  47. package/dist/types.cjs +3 -0
  48. package/dist/types.d.cts +8 -0
  49. package/dist/types.js +2 -0
  50. package/dist/utils/idGenerator.d.ts +0 -16
  51. package/dist/utils/static-props.d.ts +0 -7
  52. package/dist/watch-entry.cjs +7 -0
  53. package/dist/watch-entry.d.cts +26 -0
  54. package/dist/watch-entry.js +2 -0
  55. package/package.json +58 -3
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { i as BLAC_DEFAULTS, n as globalRegistry, o as BLAC_STATIC_PROPS, r as PluginManager, t as getPluginManager } from "./StateContainerRegistry-DS_wyKck.js";
2
+ import { a as watch, i as instance, n as createTrackedContext, o as ensure, r as tracked, t as TrackedContext } from "./tracked-12W-U7tq.js";
3
+ import { a as clearAll, c as getAll, i as clear, l as getRefCount, n as getStats, o as register, r as setRegistry, s as forEach, t as getRegistry, u as hasInstance } from "./config-BD9MkJ0k.js";
4
+ import { t as instanceId } from "./branded-mwGzzPF1.js";
1
5
  //#region src/utils/idGenerator.ts
2
6
  /**
3
7
  * Generate simple ID with timestamp and random (no counter tracking)
@@ -17,608 +21,13 @@ function generateSimpleId(prefix, affix) {
17
21
  if (affix) return `${prefix}:${affix}`;
18
22
  return `${prefix}:${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
19
23
  }
20
- /**
21
- * Generate a unique isolated instance key
22
- * Uses base36 encoding for compact, URL-safe identifiers
23
- *
24
- * Format: "isolated-{9-char-random-string}"
25
- * Example: "isolated-k7x2m9p4q"
26
- *
27
- * @returns A unique isolated instance key
28
- */
29
- function generateIsolatedKey() {
30
- return `isolated-${Math.random().toString(36).slice(2, 11)}`;
31
- }
32
-
33
- //#endregion
34
- //#region src/constants.ts
35
- /**
36
- * Default configuration constants for BlaC
37
- *
38
- * Centralized location for all magic numbers and default values.
39
- */
40
- /**
41
- * Default configuration constants for BlaC
42
- */
43
- const BLAC_DEFAULTS = {
44
- DEFAULT_INSTANCE_KEY: "default",
45
- MAX_GETTER_DEPTH: 10,
46
- CLEANUP_INTERVAL_MS: 3e4,
47
- WEAKREF_CLEANUP_INTERVAL_MS: 1e4,
48
- MAX_SUBSCRIPTIONS: 1e3,
49
- MAX_SUBSCRIPTIONS_HIGH_PERF: 1e4,
50
- PIPELINE_TIMEOUT_MS: 5e3,
51
- CLEANUP_INTERVAL_HIGH_PERF_MS: 5e3,
52
- MAX_PIPELINE_STAGES: 30
53
- };
54
- /**
55
- * Static property names for StateContainer classes
56
- * Used for feature flags and configuration on bloc classes
57
- */
58
- const BLAC_STATIC_PROPS = {
59
- ISOLATED: "isolated",
60
- KEEP_ALIVE: "keepAlive",
61
- EXCLUDE_FROM_DEVTOOLS: "__excludeFromDevTools"
62
- };
63
- /**
64
- * Standard error message prefix
65
- */
66
- const BLAC_ERROR_PREFIX = "[BlaC]";
67
-
68
- //#endregion
69
- //#region src/plugin/PluginManager.ts
70
- /**
71
- * Manages plugin lifecycle for the BlaC state management system.
72
- * Plugins receive notifications about state container lifecycle events.
73
- *
74
- * @example
75
- * ```ts
76
- * const manager = createPluginManager(registry);
77
- * manager.install(myPlugin, { environment: 'development' });
78
- * ```
79
- */
80
- var PluginManager = class {
81
- /**
82
- * Create a new PluginManager
83
- * @param registry - The StateContainerRegistry to monitor for lifecycle events
84
- */
85
- constructor(registry) {
86
- this.plugins = /* @__PURE__ */ new Map();
87
- this.registry = registry;
88
- this.setupLifecycleHooks();
89
- }
90
- /**
91
- * Install a plugin with optional configuration
92
- * @param plugin - The plugin to install
93
- * @param config - Optional plugin configuration
94
- * @throws Error if plugin is already installed
95
- */
96
- install(plugin, config = {}) {
97
- const effectiveConfig = {
98
- enabled: true,
99
- environment: "all",
100
- ...config
101
- };
102
- if (!this.shouldEnablePlugin(effectiveConfig)) {
103
- console.log(`[BlaC] Plugin "${plugin.name}" skipped (environment mismatch)`);
104
- return;
105
- }
106
- if (this.plugins.has(plugin.name)) throw new Error(`Plugin "${plugin.name}" is already installed`);
107
- const context = this.createPluginContext();
108
- this.plugins.set(plugin.name, {
109
- plugin,
110
- config: effectiveConfig,
111
- context
112
- });
113
- if (plugin.onInstall) try {
114
- plugin.onInstall(context);
115
- } catch (error) {
116
- console.error(`[BlaC] Error installing plugin "${plugin.name}":`, error);
117
- this.plugins.delete(plugin.name);
118
- throw error;
119
- }
120
- console.log(`[BlaC] Plugin "${plugin.name}" v${plugin.version} installed`);
121
- }
122
- /**
123
- * Uninstall a plugin by name
124
- * @param pluginName - The name of the plugin to uninstall
125
- * @throws Error if plugin is not installed
126
- */
127
- uninstall(pluginName) {
128
- const installed = this.plugins.get(pluginName);
129
- if (!installed) throw new Error(`Plugin "${pluginName}" is not installed`);
130
- if (installed.plugin.onUninstall) try {
131
- installed.plugin.onUninstall();
132
- } catch (error) {
133
- console.error(`[BlaC] Error uninstalling plugin "${pluginName}":`, error);
134
- }
135
- this.plugins.delete(pluginName);
136
- console.log(`[BlaC] Plugin "${pluginName}" uninstalled`);
137
- }
138
- /**
139
- * Get an installed plugin by name
140
- * @param pluginName - The name of the plugin to retrieve
141
- * @returns The plugin instance or undefined if not found
142
- */
143
- getPlugin(pluginName) {
144
- return this.plugins.get(pluginName)?.plugin;
145
- }
146
- /**
147
- * Get all installed plugins
148
- * @returns Array of all installed plugins
149
- */
150
- getAllPlugins() {
151
- return Array.from(this.plugins.values()).map((p) => p.plugin);
152
- }
153
- /**
154
- * Check if a plugin is installed
155
- * @param pluginName - The name of the plugin to check
156
- * @returns true if the plugin is installed
157
- */
158
- hasPlugin(pluginName) {
159
- return this.plugins.has(pluginName);
160
- }
161
- /**
162
- * Uninstall all plugins
163
- */
164
- clear() {
165
- for (const name of this.plugins.keys()) this.uninstall(name);
166
- }
167
- /**
168
- * Setup lifecycle hooks to notify plugins
169
- */
170
- setupLifecycleHooks() {
171
- this.registry.on("created", (instance) => {
172
- this.notifyPlugins("onInstanceCreated", instance);
173
- });
174
- this.registry.on("stateChanged", (instance, previousState, currentState, callstack) => {
175
- this.notifyPlugins("onStateChanged", instance, previousState, currentState, callstack);
176
- });
177
- this.registry.on("disposed", (instance) => {
178
- this.notifyPlugins("onInstanceDisposed", instance);
179
- });
180
- }
181
- /**
182
- * Notify all plugins of a lifecycle event
183
- */
184
- notifyPlugins(hookName, ...args) {
185
- for (const { plugin, config, context } of this.plugins.values()) {
186
- if (!config.enabled) continue;
187
- const hook = plugin[hookName];
188
- if (typeof hook === "function") try {
189
- hook.apply(plugin, [...args, context]);
190
- } catch (error) {
191
- console.error(`[BlaC] Error in plugin "${plugin.name}" ${hookName}:`, error);
192
- }
193
- }
194
- }
195
- /**
196
- * Create plugin context with safe API access
197
- */
198
- createPluginContext() {
199
- return {
200
- getInstanceMetadata: (instance) => {
201
- return {
202
- id: instance.instanceId,
203
- className: instance.constructor.name,
204
- isDisposed: instance.isDisposed,
205
- name: instance.name,
206
- lastStateChangeTimestamp: instance.lastUpdateTimestamp,
207
- createdAt: instance.createdAt,
208
- state: instance.state,
209
- isIsolated: instance.instanceId.startsWith("isolated-")
210
- };
211
- },
212
- getState: (instance) => {
213
- return instance.state;
214
- },
215
- queryInstances: (typeClass) => {
216
- return this.registry.getAll(typeClass);
217
- },
218
- getAllTypes: () => {
219
- return this.registry.getTypes();
220
- },
221
- getStats: () => {
222
- return this.registry.getStats();
223
- }
224
- };
225
- }
226
- /**
227
- * Check if plugin should be enabled based on environment
228
- */
229
- shouldEnablePlugin(config) {
230
- if (!config.enabled) return false;
231
- if (config.environment === "all") return true;
232
- return this.getCurrentEnvironment() === config.environment;
233
- }
234
- /**
235
- * Get current environment
236
- */
237
- getCurrentEnvironment() {
238
- if (typeof process !== "undefined") {
239
- if (process.env.NODE_ENV === "test") return "test";
240
- if (process.env.NODE_ENV === "production") return "production";
241
- return "development";
242
- }
243
- return "development";
244
- }
245
- };
246
- /**
247
- * Create a plugin manager instance
248
- * @param registry - The StateContainerRegistry to monitor for lifecycle events
249
- * @returns A new PluginManager instance
250
- */
251
- function createPluginManager(registry) {
252
- return new PluginManager(registry);
253
- }
254
-
255
- //#endregion
256
- //#region src/utils/static-props.ts
257
- /**
258
- * Utility functions for accessing static properties on StateContainer classes
259
- */
260
- /**
261
- * Get a static property from a class constructor
262
- * Type-safe helper that avoids (Type as any) casts
263
- *
264
- * @param Type - The class constructor
265
- * @param propName - The property name to access
266
- * @param defaultValue - Optional default value if property is undefined
267
- * @returns The property value or default
268
- */
269
- function getStaticProp(Type, propName, defaultValue) {
270
- return Type[propName] ?? defaultValue;
271
- }
272
- /**
273
- * Check if a class is marked as isolated.
274
- * Isolated classes create separate instances per component.
275
- * @param Type - The class constructor to check
276
- * @returns true if the class has `static isolated = true`
277
- */
278
- function isIsolatedClass(Type) {
279
- return getStaticProp(Type, BLAC_STATIC_PROPS.ISOLATED) === true;
280
- }
281
- /**
282
- * Check if a class is marked as keepAlive.
283
- * KeepAlive classes are never auto-disposed when ref count reaches 0.
284
- * @param Type - The class constructor to check
285
- * @returns true if the class has `static keepAlive = true`
286
- */
287
- function isKeepAliveClass(Type) {
288
- return getStaticProp(Type, BLAC_STATIC_PROPS.KEEP_ALIVE) === true;
289
- }
290
-
291
24
  //#endregion
292
- //#region src/core/StateContainerRegistry.ts
293
- /**
294
- * Central registry for managing StateContainer instances.
295
- * Handles instance lifecycle, ref counting, and lifecycle event emission.
296
- *
297
- * @example
298
- * ```ts
299
- * const registry = new StateContainerRegistry();
300
- * const instance = registry.acquire(MyBloc); // ownership, must release
301
- * const other = registry.ensure(OtherBloc); // no ownership, bloc-to-bloc
302
- * registry.on('stateChanged', (container, prev, next) => {
303
- * console.log('State changed:', prev, '->', next);
304
- * });
305
- * ```
306
- */
307
- var StateContainerRegistry = class {
308
- constructor() {
309
- this.instancesByConstructor = /* @__PURE__ */ new WeakMap();
310
- this.types = /* @__PURE__ */ new Set();
311
- this.typeConfigs = /* @__PURE__ */ new Map();
312
- this.listeners = /* @__PURE__ */ new Map();
313
- }
314
- /**
315
- * Register a type for lifecycle event tracking
316
- * @param constructor - The StateContainer class constructor
317
- */
318
- registerType(constructor) {
319
- this.types.add(constructor);
320
- }
321
- /**
322
- * Register a StateContainer class with configuration
323
- * @param constructor - The StateContainer class constructor
324
- * @param isolated - Whether instances should be isolated (component-scoped)
325
- * @throws Error if type is already registered
326
- */
327
- register(constructor, isolated = false) {
328
- const className = constructor.name;
329
- if (!isolated && isIsolatedClass(constructor)) isolated = true;
330
- if (this.typeConfigs.has(className)) throw new Error(`${BLAC_ERROR_PREFIX} Type "${className}" is already registered`);
331
- this.typeConfigs.set(className, { isolated });
332
- this.registerType(constructor);
333
- }
334
- ensureInstancesMap(Type) {
335
- let instances = this.instancesByConstructor.get(Type);
336
- if (!instances) {
337
- instances = /* @__PURE__ */ new Map();
338
- this.instancesByConstructor.set(Type, instances);
339
- }
340
- return instances;
341
- }
342
- /**
343
- * Get the instances Map for a specific class (public API for stats/debugging)
344
- */
345
- getInstancesMap(Type) {
346
- return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
347
- }
348
- /**
349
- * Acquire an instance with ref counting (ownership semantics).
350
- * Creates a new instance if one doesn't exist, or returns existing and increments ref count.
351
- * You must call `release()` when done to decrement the ref count.
352
- * @param Type - The StateContainer class constructor
353
- * @param instanceKey - Instance key (defaults to 'default')
354
- * @param options - Acquisition options
355
- * @param options.canCreate - Whether to create new instance if not found (default: true)
356
- * @param options.countRef - Whether to increment ref count (default: true)
357
- * @returns The state container instance
358
- */
359
- acquire(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, options = {}) {
360
- const { canCreate = true, countRef = true } = options;
361
- const registryConfig = this.typeConfigs.get(Type.name);
362
- const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
363
- const config = { instanceId: instanceKey };
364
- if (isolated && !canCreate) throw new Error(`${BLAC_ERROR_PREFIX} Cannot get isolated instance "${instanceKey}" of ${Type.name} when creation is disabled.`);
365
- if (isolated) {
366
- const instance = new Type();
367
- instance.initConfig(config);
368
- this.registerType(Type);
369
- return instance;
370
- }
371
- const instances = this.ensureInstancesMap(Type);
372
- let entry = instances.get(instanceKey);
373
- if (entry?.instance.isDisposed) {
374
- instances.delete(instanceKey);
375
- entry = void 0;
376
- }
377
- if (entry && countRef) entry.refCount++;
378
- if (entry) return entry.instance;
379
- if (!canCreate) throw new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found and creation is disabled.`);
380
- const instance = new Type();
381
- instance.initConfig(config);
382
- instances.set(instanceKey, {
383
- instance,
384
- refCount: 1
385
- });
386
- this.registerType(Type);
387
- return instance;
388
- }
389
- /**
390
- * Borrow an existing instance without incrementing ref count (borrowing semantics).
391
- * Tracks cross-bloc dependency for reactive updates.
392
- * @param Type - The StateContainer class constructor
393
- * @param instanceKey - Instance key (defaults to 'default')
394
- * @returns The state container instance
395
- * @throws Error if instance doesn't exist
396
- */
397
- borrow(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
398
- return this.acquire(Type, instanceKey, {
399
- canCreate: false,
400
- countRef: false
401
- });
402
- }
403
- /**
404
- * Safely borrow an existing instance (borrowing semantics with error handling).
405
- * Returns discriminated union for type-safe conditional access.
406
- * @param Type - The StateContainer class constructor
407
- * @param instanceKey - Instance key (defaults to 'default')
408
- * @returns Discriminated union with either the instance or an error
409
- */
410
- borrowSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
411
- try {
412
- return {
413
- error: null,
414
- instance: this.borrow(Type, instanceKey)
415
- };
416
- } catch (error) {
417
- return {
418
- error,
419
- instance: null
420
- };
421
- }
422
- }
423
- /**
424
- * Ensure an instance exists without taking ownership (for bloc-to-bloc communication).
425
- * Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
426
- * Tracks cross-bloc dependency for reactive updates.
427
- *
428
- * Use this in bloc-to-bloc communication when you need to ensure an instance exists
429
- * but don't want to claim ownership (no ref count increment).
430
- * @param Type - The StateContainer class constructor
431
- * @param instanceKey - Instance key (defaults to 'default')
432
- * @returns The state container instance
433
- */
434
- ensure(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
435
- return this.acquire(Type, instanceKey, {
436
- canCreate: true,
437
- countRef: false
438
- });
439
- }
440
- /**
441
- * Release a reference to an instance.
442
- * Decrements ref count and disposes when it reaches 0 (unless keepAlive).
443
- * @param Type - The StateContainer class constructor
444
- * @param instanceKey - Instance key (defaults to 'default')
445
- * @param forceDispose - Force immediate disposal regardless of ref count
446
- */
447
- release(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, forceDispose = false) {
448
- const instances = this.ensureInstancesMap(Type);
449
- const entry = instances.get(instanceKey);
450
- if (!entry) return;
451
- if (forceDispose) {
452
- if (!entry.instance.isDisposed) entry.instance.dispose();
453
- instances.delete(instanceKey);
454
- return;
455
- }
456
- entry.refCount--;
457
- const keepAlive = isKeepAliveClass(Type);
458
- if (entry.refCount <= 0 && !keepAlive) {
459
- if (!entry.instance.isDisposed) entry.instance.dispose();
460
- instances.delete(instanceKey);
461
- }
462
- }
463
- /**
464
- * Get all instances of a specific type.
465
- * @param Type - The StateContainer class constructor
466
- * @returns Array of all instances
467
- */
468
- getAll(Type) {
469
- const instances = this.ensureInstancesMap(Type);
470
- const result = [];
471
- for (const entry of instances.values()) result.push(entry.instance);
472
- return result;
473
- }
474
- /**
475
- * Safely iterate over all instances of a type.
476
- * Skips disposed instances and catches callback errors.
477
- * @param Type - The StateContainer class constructor
478
- * @param callback - Function to call for each instance
479
- */
480
- forEach(Type, callback) {
481
- const instances = this.ensureInstancesMap(Type);
482
- for (const entry of instances.values()) {
483
- const instance = entry.instance;
484
- if (!instance.isDisposed) try {
485
- callback(instance);
486
- } catch (error) {
487
- console.error(`${BLAC_ERROR_PREFIX} forEach callback error for ${Type.name}:`, error);
488
- }
489
- }
490
- }
491
- /**
492
- * Clear all instances of a specific type (disposes them).
493
- * @param Type - The StateContainer class constructor
494
- */
495
- clear(Type) {
496
- const instances = this.ensureInstancesMap(Type);
497
- for (const entry of instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
498
- instances.clear();
499
- }
500
- /**
501
- * Get reference count for an instance.
502
- * @param Type - The StateContainer class constructor
503
- * @param instanceKey - Instance key (defaults to 'default')
504
- * @returns Current ref count (0 if instance doesn't exist)
505
- */
506
- getRefCount(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
507
- return this.ensureInstancesMap(Type).get(instanceKey)?.refCount ?? 0;
508
- }
509
- /**
510
- * Check if an instance exists.
511
- * @param Type - The StateContainer class constructor
512
- * @param instanceKey - Instance key (defaults to 'default')
513
- * @returns true if instance exists
514
- */
515
- hasInstance(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
516
- return this.ensureInstancesMap(Type).has(instanceKey);
517
- }
518
- /**
519
- * Clear all instances from all types (for testing)
520
- *
521
- * Iterates all registered types and clears their instances.
522
- * Also clears type tracking to reset the registry state.
523
- */
524
- clearAll() {
525
- for (const Type of this.types) this.clear(Type);
526
- this.types.clear();
527
- this.typeConfigs.clear();
528
- }
529
- /**
530
- * Get registry statistics for debugging.
531
- * @returns Object with registeredTypes, totalInstances, and typeBreakdown
532
- */
533
- getStats() {
534
- const typeBreakdown = {};
535
- let totalInstances = 0;
536
- for (const Type of this.types) {
537
- const typeName = Type.name;
538
- const count = this.getInstancesMap(Type).size;
539
- typeBreakdown[typeName] = count;
540
- totalInstances += count;
541
- }
542
- return {
543
- registeredTypes: this.types.size,
544
- totalInstances,
545
- typeBreakdown
546
- };
547
- }
548
- /**
549
- * Get all registered types (for plugin system).
550
- * @returns Array of all registered StateContainer class constructors
551
- */
552
- getTypes() {
553
- return Array.from(this.types);
554
- }
555
- /**
556
- * Subscribe to lifecycle events
557
- * @param event - The lifecycle event to listen for
558
- * @param listener - The listener function to call when the event occurs
559
- * @returns Unsubscribe function
560
- */
561
- on(event, listener) {
562
- if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
563
- const instance = this.listeners.get(event);
564
- if (!instance) throw new Error(`${BLAC_ERROR_PREFIX} Failed to register listener for event '${event}'`);
565
- instance.add(listener);
566
- return () => {
567
- this.listeners.get(event)?.delete(listener);
568
- };
569
- }
570
- /**
571
- * Emit lifecycle event to all listeners
572
- * @internal - Called by StateContainer lifecycle methods
573
- */
574
- emit(event, ...args) {
575
- const listeners = this.listeners.get(event);
576
- if (!listeners || listeners.size === 0) return;
577
- for (const listener of listeners) try {
578
- listener(...args);
579
- } catch (error) {
580
- console.error(`${BLAC_ERROR_PREFIX} Listener error for '${event}':`, error);
581
- }
582
- }
583
- };
584
- /**
585
- * Global default registry instance
586
- */
587
- const globalRegistry = new StateContainerRegistry();
588
- /**
589
- * Global plugin manager (initialized lazily)
590
- */
591
- let _globalPluginManager = null;
592
- /**
593
- * Get the global plugin manager
594
- */
595
- function getPluginManager() {
596
- if (!_globalPluginManager) _globalPluginManager = createPluginManager(globalRegistry);
597
- return _globalPluginManager;
598
- }
599
-
25
+ //#region src/core/symbols.ts
26
+ const EMIT = Symbol("blac.emit");
27
+ const UPDATE = Symbol("blac.update");
600
28
  //#endregion
601
29
  //#region src/core/StateContainer.ts
602
30
  const EMPTY_DEPS = /* @__PURE__ */ new Map();
603
- /**
604
- * Abstract base class for all state containers in BlaC.
605
- * Provides lifecycle management, subscription handling, ref counting,
606
- * and integration with the global registry.
607
- *
608
- * @template S - State type managed by this container
609
- *
610
- * @example
611
- * ```ts
612
- * class CounterBloc extends StateContainer<{ count: number }> {
613
- * constructor() {
614
- * super({ count: 0 });
615
- * }
616
- * increment() {
617
- * this.emit({ count: this.state.count + 1 });
618
- * }
619
- * }
620
- * ```
621
- */
622
31
  var StateContainer = class StateContainer {
623
32
  static {
624
33
  this.__excludeFromDevTools = false;
@@ -635,21 +44,25 @@ var StateContainer = class StateContainer {
635
44
  return () => globalRegistry.ensure(Type, instanceKey);
636
45
  }
637
46
  constructor(initialState) {
638
- this.listeners = /* @__PURE__ */ new Set();
47
+ this._listeners = /* @__PURE__ */ new Set();
639
48
  this._disposed = false;
640
- this.config = {};
641
- this.systemEventHandlers = /* @__PURE__ */ new Map();
49
+ this._hydrationStatus = "idle";
50
+ this._changedWhileHydrating = false;
51
+ this._hydrationPromise = null;
52
+ this._hydrationPromiseSettled = false;
53
+ this._config = {};
54
+ this._systemEventHandlers = /* @__PURE__ */ new Map();
55
+ this._dependencies = null;
642
56
  this.name = this.constructor.name;
643
57
  this.debug = false;
644
58
  this.instanceId = generateSimpleId(this.constructor.name, "main");
645
59
  this.createdAt = Date.now();
646
- this._dependencies = null;
647
60
  this.lastUpdateTimestamp = Date.now();
648
61
  this.onSystemEvent = (event, handler) => {
649
- let handlers = this.systemEventHandlers.get(event);
62
+ let handlers = this._systemEventHandlers.get(event);
650
63
  if (!handlers) {
651
64
  handlers = /* @__PURE__ */ new Set();
652
- this.systemEventHandlers.set(event, handlers);
65
+ this._systemEventHandlers.set(event, handlers);
653
66
  }
654
67
  handlers.add(handler);
655
68
  return () => {
@@ -658,72 +71,89 @@ var StateContainer = class StateContainer {
658
71
  };
659
72
  this._state = initialState;
660
73
  }
661
- /**
662
- * Initialize configuration for this instance.
663
- * Called by the registry after construction.
664
- * @param config - Configuration options
665
- */
666
74
  initConfig(config) {
667
- this.config = { ...config };
668
- this.name = this.config.name || this.constructor.name;
669
- this.debug = this.config.debug ?? false;
670
- this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
75
+ this._config = { ...config };
76
+ this.name = this._config.name || this.constructor.name;
77
+ this.debug = this._config.debug ?? false;
78
+ this.instanceId = generateSimpleId(this.constructor.name, this._config.instanceId);
671
79
  globalRegistry.emit("created", this);
672
80
  }
673
- /** Current state value */
674
81
  get state() {
675
82
  return this._state;
676
83
  }
677
- /** Whether this instance has been disposed */
678
84
  get isDisposed() {
679
85
  return this._disposed;
680
86
  }
681
- /**
682
- * Subscribe to state changes
683
- * @param listener - Function called when state changes
684
- * @returns Unsubscribe function
685
- */
87
+ get hydrationStatus() {
88
+ return this._hydrationStatus;
89
+ }
90
+ get hydrationError() {
91
+ return this._hydrationError;
92
+ }
93
+ get isHydrated() {
94
+ return this._hydrationStatus === "hydrated";
95
+ }
96
+ get changedWhileHydrating() {
97
+ return this._changedWhileHydrating;
98
+ }
686
99
  subscribe(listener) {
687
100
  if (this._disposed) throw new Error(`Cannot subscribe to disposed container ${this.name}`);
688
- this.listeners.add(listener);
689
- return () => this.listeners.delete(listener);
101
+ this._listeners.add(listener);
102
+ return () => this._listeners.delete(listener);
690
103
  }
691
- /**
692
- * Dispose this instance and clean up resources.
693
- * Clears all listeners and emits the 'dispose' system event.
694
- */
695
104
  dispose() {
696
105
  if (this._disposed) return;
697
106
  if (this.debug) console.log(`[${this.name}] Disposing...`);
698
107
  this._disposed = true;
108
+ if (this._hydrationStatus === "hydrating") this.failHydration(/* @__PURE__ */ new Error(`Hydration cancelled because ${this.name} was disposed`));
699
109
  this.emitSystemEvent("dispose", void 0);
700
- this.listeners.clear();
701
- this.systemEventHandlers.clear();
110
+ this._listeners.clear();
111
+ this._systemEventHandlers.clear();
702
112
  globalRegistry.emit("disposed", this);
703
113
  if (this.debug) console.log(`[${this.name}] Disposed successfully`);
704
114
  }
705
- /**
706
- * Emit a new state value and notify all listeners.
707
- * @param newState - The new state value
708
- * @protected
709
- */
710
- emit(newState) {
711
- if (this._disposed) throw new Error(`Cannot emit state from disposed container ${this.name}`);
712
- if (this._state === newState) return;
713
- const previousState = this._state;
714
- this._state = newState;
715
- this.emitSystemEvent("stateChanged", {
716
- state: newState,
717
- previousState
718
- });
719
- for (const listener of this.listeners) try {
720
- listener(newState);
721
- } catch (error) {
722
- console.error(`[${this.name}] Error in listener:`, error);
115
+ [EMIT](newState) {
116
+ this.applyState(newState, "default");
117
+ }
118
+ [UPDATE](updater) {
119
+ if (this._disposed) throw new Error(`Cannot update state from disposed container ${this.name}`);
120
+ this[EMIT](updater(this._state));
121
+ }
122
+ beginHydration() {
123
+ if (this._disposed) throw new Error(`Cannot begin hydration for disposed container ${this.name}`);
124
+ this._changedWhileHydrating = false;
125
+ this._hydrationError = void 0;
126
+ this._hydrationPromise = null;
127
+ this._resolveHydrationPromise = void 0;
128
+ this._rejectHydrationPromise = void 0;
129
+ this._hydrationPromiseSettled = false;
130
+ this.ensureHydrationPromise();
131
+ this.setHydrationStatus("hydrating");
132
+ }
133
+ applyHydratedState(newState) {
134
+ if (this._disposed) return false;
135
+ if (this._hydrationStatus !== "hydrating" || this._changedWhileHydrating) return false;
136
+ this.applyState(newState, "hydration");
137
+ return true;
138
+ }
139
+ finishHydration() {
140
+ if (this._hydrationStatus !== "hydrating") {
141
+ if (this._hydrationStatus === "hydrated") return;
142
+ if (this._hydrationStatus === "error") this.ensureHydrationPromise();
723
143
  }
724
- const stackTrace = this.captureStackTrace();
725
- globalRegistry.emit("stateChanged", this, previousState, newState, stackTrace);
726
- this.lastUpdateTimestamp = Date.now();
144
+ this.setHydrationStatus("hydrated");
145
+ this.resolveHydration();
146
+ }
147
+ failHydration(error) {
148
+ const err = error instanceof Error ? error : /* @__PURE__ */ new Error(`Hydration failed: ${error}`);
149
+ this._hydrationError = err;
150
+ this.setHydrationStatus("error", err);
151
+ this.rejectHydration(err);
152
+ }
153
+ waitForHydration() {
154
+ if (this._hydrationStatus === "idle" || this._hydrationStatus === "hydrated") return Promise.resolve();
155
+ if (this._hydrationStatus === "error") return Promise.reject(this._hydrationError ?? /* @__PURE__ */ new Error(`Hydration failed for container ${this.name}`));
156
+ return this.ensureHydrationPromise();
727
157
  }
728
158
  captureStackTrace() {
729
159
  if (!StateContainer.enableStackTrace || typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
@@ -732,7 +162,7 @@ var StateContainer = class StateContainer {
732
162
  const formattedLines = [];
733
163
  for (const line of relevantLines) {
734
164
  if (!line.trim()) continue;
735
- if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("Cubit.patch")) continue;
165
+ if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("[blac.emit]") || line.includes("[blac.update]") || line.includes("Cubit.patch")) continue;
736
166
  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;
737
167
  const formatted = this.formatStackLine(line);
738
168
  if (formatted) formattedLines.push(formatted);
@@ -763,17 +193,65 @@ var StateContainer = class StateContainer {
763
193
  if (segments.length > 3) path = segments.slice(-3).join("/");
764
194
  return path;
765
195
  }
766
- /**
767
- * Update state using a transform function.
768
- * @param updater - Function that receives current state and returns new state
769
- * @protected
770
- */
771
- update(updater) {
772
- if (this._disposed) throw new Error(`Cannot update state from disposed container ${this.name}`);
773
- this.emit(updater(this._state));
196
+ applyState(newState, source) {
197
+ if (this._disposed) throw new Error(`Cannot emit state from disposed container ${this.name}`);
198
+ if (this._state === newState) return;
199
+ if (this._hydrationStatus === "hydrating" && source !== "hydration") this._changedWhileHydrating = true;
200
+ const previousState = this._state;
201
+ this._state = newState;
202
+ this.emitSystemEvent("stateChanged", {
203
+ state: newState,
204
+ previousState
205
+ });
206
+ const listeners = Array.from(this._listeners);
207
+ for (const listener of listeners) try {
208
+ listener(newState);
209
+ } catch (error) {
210
+ console.error(`[${this.name}] Error in listener:`, error);
211
+ }
212
+ const stackTrace = this.captureStackTrace();
213
+ globalRegistry.emit("stateChanged", this, previousState, newState, stackTrace);
214
+ this.lastUpdateTimestamp = Date.now();
215
+ }
216
+ setHydrationStatus(status, error) {
217
+ const previousStatus = this._hydrationStatus;
218
+ this._hydrationStatus = status;
219
+ this._hydrationError = error;
220
+ this.emitSystemEvent("hydrationChanged", {
221
+ status,
222
+ previousStatus,
223
+ error,
224
+ changedWhileHydrating: this._changedWhileHydrating
225
+ });
226
+ }
227
+ ensureHydrationPromise() {
228
+ if (!this._hydrationPromise || this._hydrationPromiseSettled) {
229
+ this._hydrationPromiseSettled = false;
230
+ this._hydrationPromise = new Promise((resolve, reject) => {
231
+ this._resolveHydrationPromise = () => {
232
+ if (this._hydrationPromiseSettled) return;
233
+ this._hydrationPromiseSettled = true;
234
+ resolve();
235
+ };
236
+ this._rejectHydrationPromise = (error) => {
237
+ if (this._hydrationPromiseSettled) return;
238
+ this._hydrationPromiseSettled = true;
239
+ reject(error);
240
+ };
241
+ });
242
+ this._hydrationPromise.catch(() => {});
243
+ }
244
+ return this._hydrationPromise;
245
+ }
246
+ resolveHydration() {
247
+ this._resolveHydrationPromise?.();
248
+ }
249
+ rejectHydration(error) {
250
+ this.ensureHydrationPromise();
251
+ this._rejectHydrationPromise?.(error);
774
252
  }
775
253
  emitSystemEvent(event, payload) {
776
- const handlers = this.systemEventHandlers.get(event);
254
+ const handlers = this._systemEventHandlers.get(event);
777
255
  if (!handlers) return;
778
256
  for (const handler of handlers) try {
779
257
  handler(payload);
@@ -782,15 +260,8 @@ var StateContainer = class StateContainer {
782
260
  }
783
261
  }
784
262
  };
785
-
786
263
  //#endregion
787
264
  //#region src/core/Cubit.ts
788
- /**
789
- * Simple state container with direct state emission.
790
- * Extends StateContainer with public methods for emitting and updating state.
791
- *
792
- * @template S - State type
793
- */
794
265
  var Cubit = class extends StateContainer {
795
266
  constructor(initialState) {
796
267
  super(initialState);
@@ -808,22 +279,13 @@ var Cubit = class extends StateContainer {
808
279
  }));
809
280
  });
810
281
  }
811
- /**
812
- * Replace state with a new value and notify all listeners
813
- * @param newState - The new state value
814
- */
815
282
  emit(newState) {
816
- super["emit"](newState);
283
+ this[EMIT](newState);
817
284
  }
818
- /**
819
- * Transform current state using an updater function and emit the new state
820
- * @param updater - Function that receives current state and returns new state
821
- */
822
285
  update(updater) {
823
- super["update"](updater);
286
+ this[UPDATE](updater);
824
287
  }
825
288
  };
826
-
827
289
  //#endregion
828
290
  //#region src/registry/acquire.ts
829
291
  function acquire(BlocClass, instanceKey) {
@@ -832,7 +294,6 @@ function acquire(BlocClass, instanceKey) {
832
294
  countRef: true
833
295
  });
834
296
  }
835
-
836
297
  //#endregion
837
298
  //#region src/registry/borrow.ts
838
299
  function borrow(BlocClass, instanceKey) {
@@ -841,60 +302,11 @@ function borrow(BlocClass, instanceKey) {
841
302
  function borrowSafe(BlocClass, instanceKey) {
842
303
  return globalRegistry.borrowSafe(BlocClass, instanceKey);
843
304
  }
844
-
845
- //#endregion
846
- //#region src/registry/ensure.ts
847
- function ensure(BlocClass, instanceKey) {
848
- return globalRegistry.ensure(BlocClass, instanceKey);
849
- }
850
-
851
305
  //#endregion
852
306
  //#region src/registry/release.ts
853
307
  function release(BlocClass, instanceKey, forceDispose = false) {
854
308
  globalRegistry.release(BlocClass, instanceKey, forceDispose);
855
309
  }
856
-
857
- //#endregion
858
- //#region src/registry/queries.ts
859
- function hasInstance(BlocClass, instanceKey) {
860
- return globalRegistry.hasInstance(BlocClass, instanceKey);
861
- }
862
- function getRefCount(BlocClass, instanceKey) {
863
- return globalRegistry.getRefCount(BlocClass, instanceKey);
864
- }
865
- function getAll(BlocClass) {
866
- return globalRegistry.getAll(BlocClass);
867
- }
868
- function forEach(BlocClass, callback) {
869
- globalRegistry.forEach(BlocClass, callback);
870
- }
871
-
872
- //#endregion
873
- //#region src/registry/management.ts
874
- function clear(BlocClass) {
875
- globalRegistry.clear(BlocClass);
876
- }
877
- function clearAll() {
878
- globalRegistry.clearAll();
879
- }
880
- function register(BlocClass, isolated = false) {
881
- globalRegistry.register(BlocClass, isolated);
882
- }
883
-
884
- //#endregion
885
- //#region src/registry/config.ts
886
- let _registry = globalRegistry;
887
- function getRegistry() {
888
- return _registry;
889
- }
890
- function setRegistry(registry) {
891
- _registry.clearAll();
892
- _registry = registry;
893
- }
894
- function getStats() {
895
- return _registry.getStats();
896
- }
897
-
898
310
  //#endregion
899
311
  //#region src/decorators/blac.ts
900
312
  /**
@@ -902,9 +314,6 @@ function getStats() {
902
314
  *
903
315
  * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)
904
316
  * ```ts
905
- * @blac({ isolated: true })
906
- * class FormBloc extends Cubit<FormState> {}
907
- *
908
317
  * @blac({ keepAlive: true })
909
318
  * class AuthBloc extends Cubit<AuthState> {}
910
319
  *
@@ -914,850 +323,19 @@ function getStats() {
914
323
  *
915
324
  * @example Function syntax (no decorator support needed)
916
325
  * ```ts
917
- * const FormBloc = blac({ isolated: true })(
918
- * class extends Cubit<FormState> {}
326
+ * const AuthBloc = blac({ keepAlive: true })(
327
+ * class extends Cubit<AuthState> {}
919
328
  * );
920
329
  * ```
921
330
  */
922
331
  function blac(options) {
923
332
  return function(target, _context) {
924
- if ("isolated" in options && options.isolated) target[BLAC_STATIC_PROPS.ISOLATED] = true;
925
333
  if ("keepAlive" in options && options.keepAlive) target[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;
926
334
  if ("excludeFromDevTools" in options && options.excludeFromDevTools) target[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;
927
335
  return target;
928
336
  };
929
337
  }
930
-
931
338
  //#endregion
932
- //#region src/tracking/path-utils.ts
933
- /**
934
- * Path utilities for dependency tracking
935
- *
936
- * Provides utilities for parsing property paths and extracting values
937
- * from nested objects using path strings.
938
- *
939
- * @internal
940
- */
941
- /**
942
- * Parse a property path string into an array of segments
943
- *
944
- * @internal
945
- *
946
- * Handles both dot notation (a.b.c) and bracket notation (a[0].b)
947
- *
948
- * @example
949
- * ```ts
950
- * parsePath('user.name') // ['user', 'name']
951
- * parsePath('items[0].name') // ['items', '0', 'name']
952
- * parsePath('data.users[2].address.city') // ['data', 'users', '2', 'address', 'city']
953
- * ```
954
- */
955
- function parsePath(path) {
956
- const segments = [];
957
- let current = "";
958
- let i = 0;
959
- while (i < path.length) {
960
- const char = path[i];
961
- if (char === ".") {
962
- if (current) segments.push(current);
963
- current = "";
964
- } else if (char === "[") {
965
- if (current) segments.push(current);
966
- current = "";
967
- i++;
968
- while (i < path.length && path[i] !== "]") current += path[i++];
969
- if (current) segments.push(current);
970
- current = "";
971
- } else current += char;
972
- i++;
973
- }
974
- if (current) segments.push(current);
975
- return segments;
976
- }
977
- /**
978
- * Get a value from an object using a path of segments
979
- *
980
- * @example
981
- * ```ts
982
- * const obj = { user: { name: 'Alice', age: 30 } }
983
- * getValueAtPath(obj, ['user', 'name']) // 'Alice'
984
- * getValueAtPath(obj, ['user', 'age']) // 30
985
- * getValueAtPath(obj, ['user', 'missing']) // undefined
986
- * ```
987
- *
988
- * @internal
989
- */
990
- function getValueAtPath(obj, segments) {
991
- if (obj == null) return void 0;
992
- let current = obj;
993
- for (let i = 0; i < segments.length; i++) {
994
- current = current[segments[i]];
995
- if (current == null) return void 0;
996
- }
997
- return current;
998
- }
999
- /**
1000
- * Shallow equality comparison for arrays
1001
- *
1002
- * Compares two arrays element-by-element using Object.is
1003
- *
1004
- * @example
1005
- * ```ts
1006
- * shallowEqual([1, 2, 3], [1, 2, 3]) // true
1007
- * shallowEqual([1, 2, 3], [1, 2, 4]) // false
1008
- * shallowEqual([1, 2], [1, 2, 3]) // false
1009
- * ```
1010
- *
1011
- * @internal
1012
- */
1013
- function shallowEqual(arr1, arr2) {
1014
- if (arr1.length !== arr2.length) return false;
1015
- for (let i = 0; i < arr1.length; i++) if (!Object.is(arr1[i], arr2[i])) return false;
1016
- return true;
1017
- }
339
+ export { Cubit, EMIT, PluginManager, StateContainer, TrackedContext, UPDATE, acquire, blac, borrow, borrowSafe, clear, clearAll, createTrackedContext, ensure, forEach, getAll, getPluginManager, getRefCount, getRegistry, getStats, globalRegistry, hasInstance, instance, instanceId, register, release, setRegistry, tracked, watch };
1018
340
 
1019
- //#endregion
1020
- //#region src/tracking/tracking-proxy.ts
1021
- /**
1022
- * Consolidated Tracking System
1023
- *
1024
- * This module provides all dependency and getter tracking functionality:
1025
- * - Proxy creation for automatic property access tracking
1026
- * - Dependency path tracking and change detection
1027
- * - Getter execution tracking for computed properties
1028
- * - Combined tracking proxy for watch/waitUntil use cases
1029
- */
1030
- /**
1031
- * Check if a value can be proxied
1032
- * Returns true for plain objects and arrays only.
1033
- * @internal
1034
- */
1035
- function isProxyable(value) {
1036
- if (typeof value !== "object" || value === null) return false;
1037
- const proto = Object.getPrototypeOf(value);
1038
- return proto === Object.prototype || proto === Array.prototype;
1039
- }
1040
- /**
1041
- * Create a new proxy tracker state
1042
- * @internal
1043
- */
1044
- function createProxyState() {
1045
- return {
1046
- trackedPaths: /* @__PURE__ */ new Set(),
1047
- isTracking: false,
1048
- proxyCache: /* @__PURE__ */ new WeakMap(),
1049
- boundFunctionsCache: null,
1050
- lastProxiedState: null,
1051
- lastProxy: null,
1052
- maxDepth: 10
1053
- };
1054
- }
1055
- /**
1056
- * Start tracking property accesses
1057
- * @internal
1058
- */
1059
- function startProxy(state) {
1060
- state.isTracking = true;
1061
- state.trackedPaths.clear();
1062
- }
1063
- /**
1064
- * Stop tracking and return the tracked paths
1065
- * @internal
1066
- */
1067
- function stopProxy(state) {
1068
- state.isTracking = false;
1069
- return new Set(state.trackedPaths);
1070
- }
1071
- /**
1072
- * Create a proxy for an array with property access tracking
1073
- * @internal
1074
- */
1075
- function createArrayProxy(state, target, path, depth = 0) {
1076
- const proxy = new Proxy(target, { get: (arr, prop) => {
1077
- if (typeof prop === "symbol") return Reflect.get(arr, prop);
1078
- const value = Reflect.get(arr, prop);
1079
- if (typeof value === "function") {
1080
- if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
1081
- const cached = state.boundFunctionsCache.get(value);
1082
- if (cached) return cached;
1083
- const bound = value.bind(arr);
1084
- state.boundFunctionsCache.set(value, bound);
1085
- return bound;
1086
- }
1087
- if (prop === "length") {
1088
- if (state.isTracking) {
1089
- const fullPath = path ? `${path}.length` : "length";
1090
- state.trackedPaths.add(fullPath);
1091
- }
1092
- return value;
1093
- }
1094
- let fullPath;
1095
- if (typeof prop === "string") {
1096
- const index = Number(prop);
1097
- if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
1098
- else fullPath = path ? `${path}.${prop}` : prop;
1099
- } else return value;
1100
- if (isProxyable(value)) return createInternal(state, value, fullPath, depth + 1);
1101
- if (state.isTracking) state.trackedPaths.add(fullPath);
1102
- return value;
1103
- } });
1104
- state.proxyCache.set(target, proxy);
1105
- return proxy;
1106
- }
1107
- /**
1108
- * Create a proxy for an object with property access tracking
1109
- * @internal
1110
- */
1111
- function createInternal(state, target, path = "", depth = 0) {
1112
- if (!state.isTracking || !isProxyable(target)) return target;
1113
- if (depth >= state.maxDepth) return target;
1114
- if (state.proxyCache.has(target)) return state.proxyCache.get(target);
1115
- if (Array.isArray(target)) return createArrayProxy(state, target, path, depth);
1116
- const proxy = new Proxy(target, {
1117
- get: (obj, prop) => {
1118
- if (typeof prop === "symbol") return Reflect.get(obj, prop);
1119
- const value = Reflect.get(obj, prop);
1120
- if (typeof value === "function") {
1121
- if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
1122
- const cached = state.boundFunctionsCache.get(value);
1123
- if (cached) return cached;
1124
- const bound = value.bind(obj);
1125
- state.boundFunctionsCache.set(value, bound);
1126
- return bound;
1127
- }
1128
- const fullPath = path ? `${path}.${String(prop)}` : String(prop);
1129
- if (typeof prop === "string" && state.isTracking) {
1130
- if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
1131
- }
1132
- if (isProxyable(value)) return createInternal(state, value, fullPath, depth + 1);
1133
- return value;
1134
- },
1135
- has: (obj, prop) => {
1136
- if (typeof prop === "string" && state.isTracking) {
1137
- const fullPath = path ? `${path}.${prop}` : prop;
1138
- state.trackedPaths.add(fullPath);
1139
- }
1140
- return Reflect.has(obj, prop);
1141
- },
1142
- ownKeys: (obj) => {
1143
- if (state.isTracking && path) state.trackedPaths.add(path);
1144
- return Reflect.ownKeys(obj);
1145
- }
1146
- });
1147
- state.proxyCache.set(target, proxy);
1148
- return proxy;
1149
- }
1150
- /**
1151
- * Create a proxy for a target with caching
1152
- * @internal
1153
- */
1154
- function createForTarget(state, target) {
1155
- if (state.lastProxiedState === target && state.lastProxy) return state.lastProxy;
1156
- state.proxyCache = /* @__PURE__ */ new WeakMap();
1157
- state.boundFunctionsCache = null;
1158
- const proxy = createInternal(state, target, "", 0);
1159
- state.lastProxiedState = target;
1160
- state.lastProxy = proxy;
1161
- return proxy;
1162
- }
1163
- function isChildPath(child, parent) {
1164
- if (child === parent) return false;
1165
- return child.startsWith(parent + ".") || child.startsWith(parent + "[");
1166
- }
1167
- function getArrayParentPath(path) {
1168
- if (path.endsWith(".length")) return path.slice(0, -7);
1169
- const arrayIndexMatch = path.match(/^(.+?)\[\d+\]/);
1170
- if (arrayIndexMatch) return arrayIndexMatch[1];
1171
- return null;
1172
- }
1173
- /**
1174
- * @internal
1175
- */
1176
- function optimizeTrackedPaths(paths) {
1177
- if (paths.size === 0) return /* @__PURE__ */ new Set();
1178
- if (paths.size === 1) return new Set(paths);
1179
- const sortedPaths = Array.from(paths).sort((a, b) => b.length - a.length);
1180
- const optimized = /* @__PURE__ */ new Set();
1181
- for (const path of sortedPaths) {
1182
- let hasMoreSpecificChild = false;
1183
- for (const optimizedPath of optimized) if (isChildPath(optimizedPath, path)) {
1184
- hasMoreSpecificChild = true;
1185
- break;
1186
- }
1187
- if (!hasMoreSpecificChild) optimized.add(path);
1188
- }
1189
- const arrayParents = /* @__PURE__ */ new Set();
1190
- for (const path of optimized) {
1191
- const arrayParent = getArrayParentPath(path);
1192
- if (arrayParent) arrayParents.add(arrayParent);
1193
- }
1194
- for (const arrayParent of arrayParents) optimized.add(arrayParent);
1195
- return optimized;
1196
- }
1197
- /**
1198
- * @internal
1199
- */
1200
- function createDependencyState() {
1201
- return {
1202
- proxyState: createProxyState(),
1203
- previousRenderPaths: /* @__PURE__ */ new Set(),
1204
- currentRenderPaths: /* @__PURE__ */ new Set(),
1205
- pathCache: /* @__PURE__ */ new Map(),
1206
- lastCheckedState: null,
1207
- lastCheckedValues: /* @__PURE__ */ new Map()
1208
- };
1209
- }
1210
- /**
1211
- * @internal
1212
- */
1213
- function startDependency(tracker) {
1214
- startProxy(tracker.proxyState);
1215
- }
1216
- /**
1217
- * @internal
1218
- */
1219
- function createDependencyProxy(tracker, state) {
1220
- return createForTarget(tracker.proxyState, state);
1221
- }
1222
- /**
1223
- * @internal
1224
- */
1225
- function capturePaths(tracker, state) {
1226
- tracker.previousRenderPaths = tracker.currentRenderPaths;
1227
- tracker.currentRenderPaths = optimizeTrackedPaths(stopProxy(tracker.proxyState));
1228
- if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
1229
- const trackedPathsUnion = new Set(tracker.previousRenderPaths);
1230
- for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
1231
- const canReuseCache = tracker.lastCheckedState === state;
1232
- for (const path of trackedPathsUnion) if (!tracker.pathCache.has(path)) {
1233
- const segments = parsePath(path);
1234
- const value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, segments);
1235
- tracker.pathCache.set(path, {
1236
- segments,
1237
- value
1238
- });
1239
- } else {
1240
- const info = tracker.pathCache.get(path);
1241
- info.value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, info.segments);
1242
- }
1243
- tracker.lastCheckedValues.clear();
1244
- }
1245
- /**
1246
- * @internal
1247
- */
1248
- function hasDependencyChanges(tracker, state) {
1249
- if (tracker.pathCache.size === 0) return true;
1250
- tracker.lastCheckedValues.clear();
1251
- for (const [path, info] of tracker.pathCache.entries()) {
1252
- const currentValue = getValueAtPath(state, info.segments);
1253
- tracker.lastCheckedValues.set(path, currentValue);
1254
- if (!Object.is(currentValue, info.value)) {
1255
- tracker.lastCheckedState = state;
1256
- return true;
1257
- }
1258
- }
1259
- tracker.lastCheckedState = state;
1260
- return false;
1261
- }
1262
- /**
1263
- * @internal
1264
- */
1265
- function hasTrackedData(tracker) {
1266
- return tracker.proxyState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
1267
- }
1268
- const descriptorCache = /* @__PURE__ */ new WeakMap();
1269
- const blocProxyCache = /* @__PURE__ */ new WeakMap();
1270
- const activeTrackerMap = /* @__PURE__ */ new WeakMap();
1271
- const MAX_GETTER_DEPTH = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
1272
- /**
1273
- * @internal
1274
- */
1275
- function getDescriptor(obj, prop) {
1276
- const constructor = obj.constructor;
1277
- let constructorCache = descriptorCache.get(constructor);
1278
- if (constructorCache?.has(prop)) return constructorCache.get(prop);
1279
- let current = obj;
1280
- let descriptor;
1281
- while (current && current !== Object.prototype) {
1282
- descriptor = Object.getOwnPropertyDescriptor(current, prop);
1283
- if (descriptor) break;
1284
- current = Object.getPrototypeOf(current);
1285
- }
1286
- if (!constructorCache) {
1287
- constructorCache = /* @__PURE__ */ new Map();
1288
- descriptorCache.set(constructor, constructorCache);
1289
- }
1290
- constructorCache.set(prop, descriptor);
1291
- return descriptor;
1292
- }
1293
- /**
1294
- * @internal
1295
- */
1296
- function isGetter(obj, prop) {
1297
- return getDescriptor(obj, prop)?.get !== void 0;
1298
- }
1299
- /**
1300
- * @internal
1301
- */
1302
- function createGetterState() {
1303
- return {
1304
- trackedValues: /* @__PURE__ */ new Map(),
1305
- currentlyAccessing: /* @__PURE__ */ new Set(),
1306
- trackedGetters: /* @__PURE__ */ new Set(),
1307
- isTracking: false,
1308
- renderCache: /* @__PURE__ */ new Map(),
1309
- cacheValid: false,
1310
- depth: 0,
1311
- visitedBlocs: /* @__PURE__ */ new Set()
1312
- };
1313
- }
1314
- /**
1315
- * @internal
1316
- */
1317
- function setActiveTracker(bloc, tracker) {
1318
- activeTrackerMap.set(bloc, tracker);
1319
- }
1320
- /**
1321
- * @internal
1322
- */
1323
- function clearActiveTracker(bloc) {
1324
- activeTrackerMap.delete(bloc);
1325
- }
1326
- /**
1327
- * @internal
1328
- */
1329
- function commitTrackedGetters(tracker) {
1330
- if (tracker.currentlyAccessing.size > 0) tracker.trackedGetters = new Set(tracker.currentlyAccessing);
1331
- tracker.currentlyAccessing.clear();
1332
- }
1333
- /**
1334
- * Execute a tracked getter with depth/circular dependency checks and context management.
1335
- * @internal
1336
- */
1337
- function executeTrackedGetter(target, prop, tracker) {
1338
- tracker.currentlyAccessing.add(prop);
1339
- if (tracker.cacheValid && tracker.renderCache.has(prop)) {
1340
- const cachedValue = tracker.renderCache.get(prop);
1341
- tracker.trackedValues.set(prop, cachedValue);
1342
- return cachedValue;
1343
- }
1344
- if (tracker.depth >= MAX_GETTER_DEPTH) {
1345
- console.warn(`${BLAC_ERROR_PREFIX} Maximum getter depth (${MAX_GETTER_DEPTH}) exceeded. Possible circular dependency in getter "${String(prop)}" on ${target.constructor.name}.`);
1346
- return;
1347
- }
1348
- if (tracker.visitedBlocs.has(target)) {
1349
- console.warn(`${BLAC_ERROR_PREFIX} Circular dependency detected: getter "${String(prop)}" on ${target.constructor.name}.`);
1350
- return;
1351
- }
1352
- const prevDepth = tracker.depth;
1353
- const prevVisited = new Set(tracker.visitedBlocs);
1354
- tracker.depth++;
1355
- tracker.visitedBlocs.add(target);
1356
- try {
1357
- const value = getDescriptor(target, prop).get.call(target);
1358
- tracker.trackedValues.set(prop, value);
1359
- return value;
1360
- } catch (error) {
1361
- tracker.currentlyAccessing.delete(prop);
1362
- throw error;
1363
- } finally {
1364
- tracker.depth = prevDepth;
1365
- tracker.visitedBlocs = prevVisited;
1366
- }
1367
- }
1368
- /**
1369
- * @internal
1370
- */
1371
- function createBlocProxy(bloc) {
1372
- const cached = blocProxyCache.get(bloc);
1373
- if (cached) return cached;
1374
- const proxy = new Proxy(bloc, { get(target, prop, receiver) {
1375
- const tracker = activeTrackerMap.get(target);
1376
- if (tracker?.isTracking && isGetter(target, prop)) return executeTrackedGetter(target, prop, tracker);
1377
- return Reflect.get(target, prop, receiver);
1378
- } });
1379
- blocProxyCache.set(bloc, proxy);
1380
- return proxy;
1381
- }
1382
- /**
1383
- * @internal
1384
- */
1385
- function hasGetterChanges(bloc, tracker) {
1386
- if (!tracker || tracker.trackedGetters.size === 0) return false;
1387
- tracker.renderCache.clear();
1388
- let hasAnyChange = false;
1389
- for (const prop of tracker.trackedGetters) try {
1390
- const descriptor = getDescriptor(bloc, prop);
1391
- if (!descriptor?.get) continue;
1392
- const newValue = descriptor.get.call(bloc);
1393
- const oldValue = tracker.trackedValues.get(prop);
1394
- tracker.renderCache.set(prop, newValue);
1395
- tracker.trackedValues.set(prop, newValue);
1396
- if (!Object.is(newValue, oldValue)) hasAnyChange = true;
1397
- } catch (error) {
1398
- console.warn(`${BLAC_ERROR_PREFIX} Getter "${String(prop)}" threw error during change detection. Stopping tracking for this getter.`, error);
1399
- tracker.trackedGetters.delete(prop);
1400
- tracker.trackedValues.delete(prop);
1401
- tracker.cacheValid = false;
1402
- return true;
1403
- }
1404
- tracker.cacheValid = true;
1405
- return hasAnyChange;
1406
- }
1407
- /**
1408
- * @internal
1409
- */
1410
- function invalidateRenderCache(tracker) {
1411
- tracker.cacheValid = false;
1412
- }
1413
- /**
1414
- * Create a new tracking proxy state.
1415
- */
1416
- function createState() {
1417
- return {
1418
- dependencyState: createDependencyState(),
1419
- getterState: createGetterState(),
1420
- dependencies: /* @__PURE__ */ new Set(),
1421
- isTracking: false
1422
- };
1423
- }
1424
- /**
1425
- * Start tracking on a tracking proxy.
1426
- */
1427
- function startTracking(tracker) {
1428
- tracker.isTracking = true;
1429
- tracker.dependencies.clear();
1430
- tracker.getterState.isTracking = true;
1431
- startDependency(tracker.dependencyState);
1432
- }
1433
- /**
1434
- * Stop tracking and collect all dependencies.
1435
- */
1436
- function stopTracking(tracker, bloc) {
1437
- tracker.isTracking = false;
1438
- tracker.getterState.isTracking = false;
1439
- capturePaths(tracker.dependencyState, bloc.state);
1440
- commitTrackedGetters(tracker.getterState);
1441
- return new Set(tracker.dependencies);
1442
- }
1443
- /**
1444
- * Check if tracked state or getters have changed.
1445
- */
1446
- function hasChanges(tracker, bloc) {
1447
- invalidateRenderCache(tracker.getterState);
1448
- const stateChanged = hasDependencyChanges(tracker.dependencyState, bloc.state);
1449
- const getterChanged = hasGetterChanges(bloc, tracker.getterState);
1450
- return stateChanged || getterChanged;
1451
- }
1452
- /**
1453
- * Create a tracking proxy for a bloc instance.
1454
- * Tracks both state property access and getter access.
1455
- */
1456
- function createTrackingProxy(bloc, tracker) {
1457
- tracker.dependencies.add(bloc);
1458
- const stateProxyCache = /* @__PURE__ */ new WeakMap();
1459
- return new Proxy(bloc, { get(target, prop, receiver) {
1460
- if (prop === "state") {
1461
- if (!tracker.isTracking) return target.state;
1462
- const rawState = target.state;
1463
- if (rawState === null || typeof rawState !== "object") return rawState;
1464
- if (stateProxyCache.has(rawState)) return stateProxyCache.get(rawState);
1465
- const stateProxy = createDependencyProxy(tracker.dependencyState, rawState);
1466
- stateProxyCache.set(rawState, stateProxy);
1467
- return stateProxy;
1468
- }
1469
- if (typeof prop === "symbol") return Reflect.get(target, prop, receiver);
1470
- const value = Reflect.get(target, prop, receiver);
1471
- if (typeof value === "function") return value.bind(target);
1472
- if (tracker.isTracking && isGetter(target, prop)) return executeTrackedGetter(target, prop, tracker.getterState);
1473
- return value;
1474
- } });
1475
- }
1476
-
1477
- //#endregion
1478
- //#region src/tracking/dependency-manager.ts
1479
- /**
1480
- * Manages subscriptions to state container dependencies.
1481
- * Provides efficient sync mechanism to add/remove subscriptions
1482
- * as dependencies change between callback invocations.
1483
- */
1484
- var DependencyManager = class {
1485
- constructor() {
1486
- this.subscriptions = /* @__PURE__ */ new Map();
1487
- this.currentDeps = /* @__PURE__ */ new Set();
1488
- }
1489
- /**
1490
- * Sync subscriptions with a new set of dependencies.
1491
- * Adds subscriptions for new deps, removes subscriptions for stale deps.
1492
- *
1493
- * @param newDeps - The new set of dependencies to subscribe to
1494
- * @param onChange - Callback to invoke when any dependency changes
1495
- * @param exclude - Optional instance to exclude from subscriptions (e.g., primary bloc)
1496
- * @returns true if the dependency set changed, false if unchanged
1497
- */
1498
- sync(newDeps, onChange, exclude) {
1499
- const filteredNewDeps = /* @__PURE__ */ new Set();
1500
- for (const dep of newDeps) if (dep !== exclude && !dep.isDisposed) filteredNewDeps.add(dep);
1501
- if (this.areSetsEqual(this.currentDeps, filteredNewDeps)) return false;
1502
- for (const dep of this.currentDeps) if (!filteredNewDeps.has(dep)) {
1503
- const unsub = this.subscriptions.get(dep);
1504
- if (unsub) {
1505
- unsub();
1506
- this.subscriptions.delete(dep);
1507
- }
1508
- }
1509
- for (const dep of filteredNewDeps) if (!this.currentDeps.has(dep) && !this.subscriptions.has(dep)) {
1510
- const unsub = dep.subscribe(onChange);
1511
- this.subscriptions.set(dep, unsub);
1512
- }
1513
- this.currentDeps = filteredNewDeps;
1514
- return true;
1515
- }
1516
- /**
1517
- * Add a single dependency subscription.
1518
- */
1519
- add(dep, onChange) {
1520
- if (this.subscriptions.has(dep) || dep.isDisposed) return;
1521
- const unsub = dep.subscribe(onChange);
1522
- this.subscriptions.set(dep, unsub);
1523
- this.currentDeps.add(dep);
1524
- }
1525
- /**
1526
- * Check if a dependency is currently subscribed.
1527
- */
1528
- has(dep) {
1529
- return this.currentDeps.has(dep);
1530
- }
1531
- /**
1532
- * Get the current set of dependencies.
1533
- */
1534
- getDependencies() {
1535
- return new Set(this.currentDeps);
1536
- }
1537
- /**
1538
- * Clean up all active subscriptions.
1539
- */
1540
- cleanup() {
1541
- for (const unsub of this.subscriptions.values()) unsub();
1542
- this.subscriptions.clear();
1543
- this.currentDeps.clear();
1544
- }
1545
- areSetsEqual(a, b) {
1546
- if (a.size !== b.size) return false;
1547
- for (const item of a) if (!b.has(item)) return false;
1548
- return true;
1549
- }
1550
- };
1551
-
1552
- //#endregion
1553
- //#region src/tracking/resolve-dependencies.ts
1554
- /**
1555
- * Resolve all transitive dependencies of a bloc via BFS over `dependencies` maps.
1556
- * Uses cycle detection to avoid infinite loops.
1557
- * @internal
1558
- */
1559
- function resolveDependencies(bloc) {
1560
- const result = /* @__PURE__ */ new Set();
1561
- const visited = /* @__PURE__ */ new Set();
1562
- const queue = [bloc];
1563
- while (queue.length > 0) {
1564
- const current = queue.shift();
1565
- for (const [Type, key] of current.dependencies) {
1566
- if (visited.has(Type)) continue;
1567
- visited.add(Type);
1568
- const dep = globalRegistry.ensure(Type, key);
1569
- result.add(dep);
1570
- if (dep.dependencies.size > 0) queue.push(dep);
1571
- }
1572
- }
1573
- result.delete(bloc);
1574
- return result;
1575
- }
1576
-
1577
- //#endregion
1578
- //#region src/watch/watch.ts
1579
- const STOP = Symbol("watch.STOP");
1580
- const BLOC_REF_MARKER = Symbol("BlocRef");
1581
- /**
1582
- * Create a reference to a specific bloc instance.
1583
- *
1584
- * @example
1585
- * ```ts
1586
- * watch(instance(UserBloc, 'user-123'), (userBloc) => {
1587
- * console.log(userBloc.state.name);
1588
- * });
1589
- * ```
1590
- */
1591
- function instance(BlocClass, instanceId) {
1592
- return {
1593
- [BLOC_REF_MARKER]: true,
1594
- blocClass: BlocClass,
1595
- instanceId
1596
- };
1597
- }
1598
- function isBlocRef(input) {
1599
- return typeof input === "object" && input !== null && BLOC_REF_MARKER in input;
1600
- }
1601
- function resolveBloc(input) {
1602
- if (isBlocRef(input)) return ensure(input.blocClass, input.instanceId);
1603
- return ensure(input, BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY);
1604
- }
1605
- function isArray(input) {
1606
- return Array.isArray(input);
1607
- }
1608
- function watchImpl(blocsOrBloc, callback) {
1609
- const isSingle = !isArray(blocsOrBloc);
1610
- const instances = (isSingle ? [blocsOrBloc] : blocsOrBloc).map(resolveBloc);
1611
- const tracker = createState();
1612
- const proxiedInstances = instances.map((inst) => createTrackingProxy(inst, tracker));
1613
- const externalDepsManager = new DependencyManager();
1614
- let disposed = false;
1615
- const primarySubscriptions = [];
1616
- const cleanup = () => {
1617
- if (disposed) return;
1618
- disposed = true;
1619
- primarySubscriptions.forEach((unsub) => unsub());
1620
- externalDepsManager.cleanup();
1621
- };
1622
- const runCallback = () => {
1623
- if (disposed) return;
1624
- startTracking(tracker);
1625
- let result;
1626
- try {
1627
- result = callback(isSingle ? proxiedInstances[0] : proxiedInstances);
1628
- } finally {
1629
- const externalDeps = /* @__PURE__ */ new Set();
1630
- for (const inst of instances) {
1631
- const deps = stopTracking(tracker, inst);
1632
- for (const dep of deps) externalDeps.add(dep);
1633
- for (const dep of resolveDependencies(inst)) externalDeps.add(dep);
1634
- }
1635
- for (const inst of instances) externalDeps.delete(inst);
1636
- externalDepsManager.sync(externalDeps, runCallback);
1637
- }
1638
- if (result === STOP) cleanup();
1639
- };
1640
- const onChange = () => {
1641
- if (disposed) return;
1642
- runCallback();
1643
- };
1644
- for (const inst of instances) primarySubscriptions.push(inst.subscribe(onChange));
1645
- runCallback();
1646
- return cleanup;
1647
- }
1648
- const watch = Object.assign(watchImpl, { STOP });
1649
-
1650
- //#endregion
1651
- //#region src/tracking/tracked.ts
1652
- /**
1653
- * Run a callback while tracking all bloc dependencies accessed.
1654
- * Returns both the result and the set of discovered dependencies.
1655
- *
1656
- * @example
1657
- * ```ts
1658
- * const { result, dependencies } = tracked(() => {
1659
- * const user = ensure(UserBloc);
1660
- * return user.fullName; // getter that may access other blocs
1661
- * });
1662
- * // dependencies contains UserBloc + any blocs accessed in fullName getter
1663
- * ```
1664
- */
1665
- function tracked(callback, options) {
1666
- const tracker = createState();
1667
- startTracking(tracker);
1668
- let result;
1669
- try {
1670
- result = callback();
1671
- } finally {
1672
- stopTracking(tracker, { state: null });
1673
- }
1674
- const dependencies = new Set(tracker.dependencies);
1675
- if (options?.exclude) dependencies.delete(options.exclude);
1676
- return {
1677
- result,
1678
- dependencies
1679
- };
1680
- }
1681
- /**
1682
- * Context for running tracked callbacks with bloc proxies.
1683
- * Provides methods to create proxies and check for changes.
1684
- */
1685
- var TrackedContext = class {
1686
- constructor() {
1687
- this.proxiedBlocs = /* @__PURE__ */ new WeakMap();
1688
- this.primaryBlocs = /* @__PURE__ */ new Set();
1689
- this.tracker = createState();
1690
- }
1691
- /**
1692
- * Get a tracking proxy for a bloc instance.
1693
- * The proxy will track state and getter accesses.
1694
- */
1695
- proxy(bloc) {
1696
- const cached = this.proxiedBlocs.get(bloc);
1697
- if (cached) return cached;
1698
- const proxied = createTrackingProxy(bloc, this.tracker);
1699
- this.proxiedBlocs.set(bloc, proxied);
1700
- this.primaryBlocs.add(bloc);
1701
- return proxied;
1702
- }
1703
- /**
1704
- * Start tracking for a new callback execution.
1705
- */
1706
- start() {
1707
- startTracking(this.tracker);
1708
- }
1709
- /**
1710
- * Stop tracking and get discovered dependencies.
1711
- * Excludes primary blocs (those explicitly proxied via proxy()).
1712
- */
1713
- stop() {
1714
- const allDeps = /* @__PURE__ */ new Set();
1715
- for (const bloc of this.primaryBlocs) {
1716
- const deps = stopTracking(this.tracker, bloc);
1717
- for (const dep of deps) if (!this.primaryBlocs.has(dep)) allDeps.add(dep);
1718
- }
1719
- return allDeps;
1720
- }
1721
- /**
1722
- * Check if any tracked state or getters have changed.
1723
- */
1724
- changed() {
1725
- for (const bloc of this.primaryBlocs) if (hasChanges(this.tracker, bloc)) return true;
1726
- return false;
1727
- }
1728
- /**
1729
- * Get all primary blocs (those explicitly proxied).
1730
- */
1731
- getPrimaryBlocs() {
1732
- return new Set(this.primaryBlocs);
1733
- }
1734
- /**
1735
- * Reset the context for reuse.
1736
- */
1737
- reset() {
1738
- this.tracker = createState();
1739
- this.proxiedBlocs = /* @__PURE__ */ new WeakMap();
1740
- this.primaryBlocs.clear();
1741
- }
1742
- };
1743
- /**
1744
- * Create a new tracked context for manual control over tracking.
1745
- */
1746
- function createTrackedContext() {
1747
- return new TrackedContext();
1748
- }
1749
-
1750
- //#endregion
1751
- //#region src/types/branded.ts
1752
- /**
1753
- * Create a branded InstanceId from a string
1754
- * @param id - The string ID to brand
1755
- * @returns Branded InstanceId
1756
- */
1757
- function instanceId(id) {
1758
- return id;
1759
- }
1760
-
1761
- //#endregion
1762
- export { Cubit, DependencyManager, PluginManager, StateContainer, TrackedContext, acquire, blac, borrow, borrowSafe, capturePaths, clear, clearActiveTracker, clearAll, commitTrackedGetters, createBlocProxy, createDependencyProxy, createDependencyState, createGetterState, createTrackedContext, ensure, forEach, generateIsolatedKey, getAll, getPluginManager, getRefCount, getRegistry, getStats, globalRegistry, hasDependencyChanges, hasGetterChanges, hasInstance, hasTrackedData, instance, instanceId, invalidateRenderCache, isIsolatedClass, register, release, resolveDependencies, setActiveTracker, setRegistry, shallowEqual, startDependency, tracked, watch };
1763
341
  //# sourceMappingURL=index.js.map