@blac/core 2.0.0-rc-5 → 2.0.0-rc-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.
package/dist/index.es.js CHANGED
@@ -1,92 +1,96 @@
1
- var v = Object.defineProperty;
2
- var w = (l, e, t) => e in l ? v(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t;
3
- var n = (l, e, t) => w(l, typeof e != "symbol" ? e + "" : e, t);
4
- const d = class d {
1
+ var m = Object.defineProperty;
2
+ var y = (d, e, t) => e in d ? m(d, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[e] = t;
3
+ var i = (d, e, t) => y(d, typeof e != "symbol" ? e + "" : e, t);
4
+ const u = class u {
5
5
  getInstance() {
6
- return d._instance || (d._instance = new h({ __unsafe_ignore_singleton: !0 })), d._instance;
6
+ return u._instance || (u._instance = new l({ __unsafe_ignore_singleton: !0 })), u._instance;
7
7
  }
8
8
  setInstance(e) {
9
- d._instance = e;
9
+ u._instance = e;
10
10
  }
11
11
  resetInstance() {
12
- const e = d._instance;
13
- if (d._instance = new h({ __unsafe_ignore_singleton: !0 }), e)
12
+ const e = u._instance;
13
+ if (u._instance = new l({ __unsafe_ignore_singleton: !0 }), e)
14
14
  for (const t of e.keepAliveBlocs)
15
- d._instance.keepAliveBlocs.add(t), d._instance.uidRegistry.set(t.uid, t);
15
+ u._instance.keepAliveBlocs.add(t), u._instance.uidRegistry.set(t.uid, t);
16
16
  }
17
17
  };
18
- n(d, "_instance");
19
- let _ = d, u = new _();
20
- function b(l) {
21
- u = l;
18
+ i(u, "_instance");
19
+ let v = u, g = new v();
20
+ function M(d) {
21
+ g = d;
22
22
  }
23
- const r = class r {
23
+ const c = class c {
24
24
  /**
25
25
  * Creates a new Blac instance.
26
26
  * @param options - Configuration options including singleton control
27
27
  */
28
28
  constructor(e = {}) {
29
29
  /** Timestamp when the instance was created */
30
- n(this, "createdAt", Date.now());
30
+ i(this, "createdAt", Date.now());
31
31
  /** Map storing all registered bloc instances by their class name and ID */
32
- n(this, "blocInstanceMap", /* @__PURE__ */ new Map());
32
+ i(this, "blocInstanceMap", /* @__PURE__ */ new Map());
33
33
  /** Map storing isolated bloc instances grouped by their constructor */
34
- n(this, "isolatedBlocMap", /* @__PURE__ */ new Map());
34
+ i(this, "isolatedBlocMap", /* @__PURE__ */ new Map());
35
35
  /** Map for O(1) lookup of isolated blocs by UID */
36
- n(this, "isolatedBlocIndex", /* @__PURE__ */ new Map());
36
+ i(this, "isolatedBlocIndex", /* @__PURE__ */ new Map());
37
37
  /** Map tracking UIDs to prevent memory leaks */
38
- n(this, "uidRegistry", /* @__PURE__ */ new Map());
38
+ i(this, "uidRegistry", /* @__PURE__ */ new Map());
39
39
  /** Set of keep-alive blocs for controlled cleanup */
40
- n(this, "keepAliveBlocs", /* @__PURE__ */ new Set());
40
+ i(this, "keepAliveBlocs", /* @__PURE__ */ new Set());
41
41
  /** Flag to control whether changes should be posted to document */
42
- n(this, "postChangesToDocument", !1);
42
+ i(this, "postChangesToDocument", !1);
43
43
  /**
44
44
  * Logs messages to console when logging is enabled
45
45
  * @param args - Arguments to log
46
46
  */
47
- n(this, "log", (...e) => {
48
- r.enableLog && console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...e);
47
+ i(this, "log", (...e) => {
48
+ c.enableLog && console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...e);
49
49
  });
50
50
  /**
51
51
  * Logs a warning message
52
52
  * @param message - Warning message
53
53
  * @param args - Additional arguments
54
54
  */
55
- n(this, "warn", (e, ...t) => {
56
- r.enableLog && console.warn(`🚨 [Blac ${String(r.instance.createdAt)}]`, e, ...t);
55
+ i(this, "warn", (e, ...t) => {
56
+ c.enableLog && console.warn(`🚨 [Blac ${String(c.instance.createdAt)}]`, e, ...t);
57
57
  });
58
58
  /**
59
59
  * Logs an error message
60
60
  * @param message - Error message
61
61
  * @param args - Additional arguments
62
62
  */
63
- n(this, "error", (e, ...t) => {
64
- r.enableLog && console.error(`🚨 [Blac ${String(r.instance.createdAt)}]`, e, ...t);
63
+ i(this, "error", (e, ...t) => {
64
+ c.enableLog && console.error(`🚨 [Blac ${String(c.instance.createdAt)}]`, e, ...t);
65
65
  });
66
66
  /**
67
67
  * Resets the Blac instance to a new one, disposing non-keepAlive blocs
68
68
  * from the old instance.
69
69
  */
70
- n(this, "resetInstance", () => {
70
+ i(this, "resetInstance", () => {
71
71
  this.log("Reset Blac instance");
72
72
  const e = new Map(this.blocInstanceMap), t = new Map(this.isolatedBlocMap);
73
73
  e.forEach((s) => {
74
- s._keepAlive || s._dispose();
74
+ !s._keepAlive && s._disposalState === "active" && this.disposeBloc(s);
75
75
  }), t.forEach((s) => {
76
- s.forEach((i) => {
77
- i._keepAlive || i._dispose();
76
+ s.forEach((n) => {
77
+ !n._keepAlive && n._disposalState === "active" && this.disposeBloc(n);
78
78
  });
79
- }), this.blocInstanceMap.clear(), this.isolatedBlocMap.clear(), this.isolatedBlocIndex.clear(), u.resetInstance();
79
+ }), this.blocInstanceMap.clear(), this.isolatedBlocMap.clear(), this.isolatedBlocIndex.clear(), g.resetInstance();
80
80
  });
81
81
  /**
82
82
  * Disposes of a bloc instance by removing it from the appropriate registry
83
83
  * @param bloc - The bloc instance to dispose
84
84
  */
85
- n(this, "disposeBloc", (e) => {
85
+ i(this, "disposeBloc", (e) => {
86
+ if (e._disposalState !== "active") {
87
+ this.log(`[${e._name}:${String(e._id)}] disposeBloc called on already disposed bloc`);
88
+ return;
89
+ }
86
90
  const t = e.constructor, s = this.createBlocInstanceMapKey(e._name, e._id);
87
- this.log(`[${e._name}:${String(e._id)}] disposeBloc called. Isolated: ${String(t.isolated)}`), t.isolated ? (this.unregisterIsolatedBlocInstance(e), this.blocInstanceMap.delete(s)) : this.unregisterBlocInstance(e), this.log("dispatched bloc", e);
91
+ this.log(`[${e._name}:${String(e._id)}] disposeBloc called. Isolated: ${String(t.isolated)}`), e._dispose(), t.isolated ? (this.unregisterIsolatedBlocInstance(e), this.blocInstanceMap.delete(s)) : this.unregisterBlocInstance(e), this.log("dispatched bloc", e);
88
92
  });
89
- n(this, "activateBloc", (e) => {
93
+ i(this, "activateBloc", (e) => {
90
94
  const t = e.constructor;
91
95
  (e.isIsolated ? this.findIsolatedBlocInstance(t, e._id) : this.findRegisteredBlocInstance(t, e._id)) || (this.log(`[${e._name}:${String(e._id)}] activateBloc called. Isolated: ${String(e.isIsolated)}`), e.isIsolated ? this.registerIsolatedBlocInstance(e) : this.registerBlocInstance(e));
92
96
  });
@@ -101,27 +105,29 @@ const r = class r {
101
105
  * - instanceRef: Optional reference string for the instance
102
106
  * @returns The bloc instance
103
107
  */
104
- n(this, "getBloc", (e, t = {}) => {
105
- const { id: s } = t, i = e, a = s ?? e.name;
106
- if (i.isolated) {
107
- const c = this.findIsolatedBlocInstance(e, a);
108
- if (c)
109
- return this.log(`[${e.name}:${String(a)}] (getBloc) Found existing isolated instance.`, t), c;
108
+ i(this, "getBloc", (e, t = {}) => {
109
+ const { id: s } = t, n = e, o = s ?? e.name;
110
+ if (n.isolated) {
111
+ const r = this.findIsolatedBlocInstance(e, o);
112
+ if (r)
113
+ return this.log(`[${e.name}:${String(o)}] (getBloc) Found existing isolated instance.`, t), r;
110
114
  if (t.throwIfNotFound)
111
115
  throw new Error(`Isolated bloc ${e.name} not found`);
112
116
  } else {
113
- const c = this.findRegisteredBlocInstance(e, a);
114
- if (c)
115
- return this.log(`[${e.name}:${String(a)}] (getBloc) Found existing registered instance.`, t), c;
117
+ const r = this.findRegisteredBlocInstance(e, o);
118
+ if (r)
119
+ return this.log(`[${e.name}:${String(o)}] (getBloc) Found existing registered instance.`, t), r;
116
120
  if (t.throwIfNotFound)
117
121
  throw new Error(`Registered bloc ${e.name} not found`);
118
122
  }
119
- const o = this.createNewBlocInstance(
123
+ const a = this.createNewBlocInstance(
120
124
  e,
121
- a,
125
+ o,
122
126
  t
123
127
  );
124
- return this.log(`[${e.name}:${String(a)}] (getBloc) No existing instance found. Creating new one.`, t, o), o;
128
+ if (this.log(`[${e.name}:${String(o)}] (getBloc) No existing instance found. Creating new one.`, t, a), !a)
129
+ throw new Error(`[getBloc] Failed to create bloc instance for ${e.name}. This should never happen.`);
130
+ return a;
125
131
  });
126
132
  /**
127
133
  * Gets a bloc instance or throws an error if it doesn't exist
@@ -131,10 +137,10 @@ const r = class r {
131
137
  * - props: Properties to pass to the bloc constructor
132
138
  * - instanceRef: Optional reference string for the instance
133
139
  */
134
- n(this, "getBlocOrThrow", (e, t = {}) => {
135
- const s = e.isolated, i = t.id || e.name, a = s ? this.findIsolatedBlocInstance(e, i) : this.findRegisteredBlocInstance(e, i);
136
- if (a)
137
- return a;
140
+ i(this, "getBlocOrThrow", (e, t = {}) => {
141
+ const s = e.isolated, n = t.id || e.name, o = s ? this.findIsolatedBlocInstance(e, n) : this.findRegisteredBlocInstance(e, n);
142
+ if (o)
143
+ return o;
138
144
  throw new Error(`Bloc ${e.name} not found`);
139
145
  });
140
146
  /**
@@ -144,13 +150,13 @@ const r = class r {
144
150
  * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property)
145
151
  * @returns Array of matching bloc instances
146
152
  */
147
- n(this, "getAllBlocs", (e, t = {}) => {
153
+ i(this, "getAllBlocs", (e, t = {}) => {
148
154
  const s = [];
149
- if (this.blocInstanceMap.forEach((i) => {
150
- i.constructor === e && s.push(i);
155
+ if (this.blocInstanceMap.forEach((n) => {
156
+ n.constructor === e && s.push(n);
151
157
  }), t.searchIsolated !== !1) {
152
- const i = this.isolatedBlocMap.get(e);
153
- i && s.push(...i.map((a) => a));
158
+ const n = this.isolatedBlocMap.get(e);
159
+ n && s.push(...n.map((o) => o));
154
160
  }
155
161
  return s;
156
162
  });
@@ -158,7 +164,7 @@ const r = class r {
158
164
  * Disposes all keep-alive blocs of a specific type
159
165
  * @param blocClass - The bloc class to dispose
160
166
  */
161
- n(this, "disposeKeepAliveBlocs", (e) => {
167
+ i(this, "disposeKeepAliveBlocs", (e) => {
162
168
  const t = [];
163
169
  for (const s of this.keepAliveBlocs)
164
170
  (!e || s.constructor === e) && t.push(s);
@@ -168,19 +174,19 @@ const r = class r {
168
174
  * Disposes all blocs matching a pattern
169
175
  * @param predicate - Function to test each bloc for disposal
170
176
  */
171
- n(this, "disposeBlocs", (e) => {
177
+ i(this, "disposeBlocs", (e) => {
172
178
  const t = [];
173
179
  for (const s of this.blocInstanceMap.values())
174
180
  e(s) && t.push(s);
175
181
  for (const s of this.isolatedBlocMap.values())
176
- for (const i of s)
177
- e(i) && t.push(i);
182
+ for (const n of s)
183
+ e(n) && t.push(n);
178
184
  t.forEach((s) => s._dispose());
179
185
  });
180
186
  /**
181
187
  * Gets memory usage statistics for debugging
182
188
  */
183
- n(this, "getMemoryStats", () => ({
189
+ i(this, "getMemoryStats", () => ({
184
190
  totalBlocs: this.uidRegistry.size,
185
191
  registeredBlocs: this.blocInstanceMap.size,
186
192
  isolatedBlocs: Array.from(this.isolatedBlocMap.values()).reduce((e, t) => e + t.length, 0),
@@ -190,39 +196,42 @@ const r = class r {
190
196
  /**
191
197
  * Validates consumer references and cleans up orphaned consumers
192
198
  */
193
- n(this, "validateConsumers", () => {
199
+ i(this, "validateConsumers", () => {
194
200
  for (const e of this.uidRegistry.values())
195
- e._consumers.size === 0 && !e._keepAlive && setTimeout(() => {
196
- e._consumers.size === 0 && !e._keepAlive && e._dispose();
201
+ e._validateConsumers(), e._consumers.size === 0 && !e._keepAlive && e._disposalState === "active" && setTimeout(() => {
202
+ e._consumers.size === 0 && !e._keepAlive && e._disposalState === "active" && this.disposeBloc(e);
197
203
  }, 1e3);
198
204
  });
199
205
  const { __unsafe_ignore_singleton: t = !1 } = e;
200
206
  if (!t)
201
- return r.instance;
202
- u.setInstance(this);
207
+ return c.instance;
208
+ g.setInstance(this);
203
209
  }
204
210
  /** @deprecated Use getInstance() instead */
205
211
  static get instance() {
206
- return u.getInstance();
212
+ return g.getInstance();
207
213
  }
208
214
  static get getAllBlocs() {
209
- return r.instance.getAllBlocs;
215
+ return c.instance.getAllBlocs;
210
216
  }
211
217
  static get log() {
212
- return r.instance.log;
218
+ return c.instance.log;
213
219
  }
214
220
  /**
215
221
  * Gets the current Blac instance
216
222
  * @returns The Blac instance
217
223
  */
218
224
  static getInstance() {
219
- return u.getInstance();
225
+ return g.getInstance();
220
226
  }
221
227
  static get warn() {
222
- return r.instance.warn;
228
+ return c.instance.warn;
223
229
  }
224
230
  static get error() {
225
- return r.instance.error;
231
+ return c.instance.error;
232
+ }
233
+ static get disposeBloc() {
234
+ return c.instance.disposeBloc;
226
235
  }
227
236
  /**
228
237
  * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID
@@ -257,8 +266,8 @@ const r = class r {
257
266
  */
258
267
  findRegisteredBlocInstance(e, t) {
259
268
  if (e.isolated) return;
260
- const i = this.createBlocInstanceMapKey(e.name, t);
261
- return this.blocInstanceMap.get(i);
269
+ const n = this.createBlocInstanceMapKey(e.name, t);
270
+ return this.blocInstanceMap.get(n);
262
271
  }
263
272
  /**
264
273
  * Registers an isolated bloc instance in the isolated registry
@@ -274,19 +283,23 @@ const r = class r {
274
283
  */
275
284
  unregisterIsolatedBlocInstance(e) {
276
285
  const t = e.constructor, s = this.isolatedBlocMap.get(t);
286
+ let n = !1;
277
287
  if (s) {
278
- const i = s.findIndex((a) => a.uid === e.uid);
279
- i !== -1 && s.splice(i, 1), s.length === 0 && this.isolatedBlocMap.delete(t);
288
+ const a = s.findIndex((r) => r.uid === e.uid);
289
+ a !== -1 && (s.splice(a, 1), n = !0), s.length === 0 && this.isolatedBlocMap.delete(t);
280
290
  }
281
- this.isolatedBlocIndex.delete(e.uid), this.uidRegistry.delete(e.uid), this.keepAliveBlocs.delete(e);
291
+ const o = this.isolatedBlocIndex.delete(e.uid);
292
+ this.uidRegistry.delete(e.uid), this.keepAliveBlocs.delete(e), n !== o && this.warn(
293
+ `[Blac] Inconsistent state detected during isolated bloc cleanup for ${e._name}:${e.uid}. Map removal: ${n}, Index removal: ${o}`
294
+ );
282
295
  }
283
296
  /**
284
297
  * Finds an isolated bloc instance by its class and ID (O(n) lookup)
285
298
  */
286
299
  findIsolatedBlocInstance(e, t) {
287
300
  if (!e.isolated) return;
288
- const i = this.isolatedBlocMap.get(e);
289
- return i ? i.find((o) => o._id === t) : void 0;
301
+ const n = this.isolatedBlocMap.get(e);
302
+ return n ? n.find((a) => a._id === t) : void 0;
290
303
  }
291
304
  /**
292
305
  * Finds an isolated bloc instance by UID (O(1) lookup)
@@ -303,45 +316,45 @@ const r = class r {
303
316
  * @returns The newly created bloc instance
304
317
  */
305
318
  createNewBlocInstance(e, t, s = {}) {
306
- const { props: i, instanceRef: a } = s, o = new e(i);
307
- return o._instanceRef = a, o.props = i || null, o._updateId(t), o._setDisposalHandler((c) => this.disposeBloc(c)), o.isIsolated ? (this.registerIsolatedBlocInstance(o), o) : (this.registerBlocInstance(o), o);
319
+ const { props: n, instanceRef: o } = s, a = new e(n);
320
+ return a._instanceRef = o, a.props = n || null, a._updateId(t), a._setDisposalHandler((r) => this.disposeBloc(r)), a.isIsolated ? (this.registerIsolatedBlocInstance(a), a) : (this.registerBlocInstance(a), a);
308
321
  }
309
322
  static get activateBloc() {
310
- return r.instance.activateBloc;
323
+ return c.instance.activateBloc;
311
324
  }
312
325
  static get getBloc() {
313
- return r.instance.getBloc;
326
+ return c.instance.getBloc;
314
327
  }
315
328
  static get getBlocOrThrow() {
316
- return r.instance.getBlocOrThrow;
329
+ return c.instance.getBlocOrThrow;
317
330
  }
318
331
  static get disposeKeepAliveBlocs() {
319
- return r.instance.disposeKeepAliveBlocs;
332
+ return c.instance.disposeKeepAliveBlocs;
320
333
  }
321
334
  static get disposeBlocs() {
322
- return r.instance.disposeBlocs;
335
+ return c.instance.disposeBlocs;
323
336
  }
324
337
  static get getMemoryStats() {
325
- return r.instance.getMemoryStats;
338
+ return c.instance.getMemoryStats;
326
339
  }
327
340
  static get validateConsumers() {
328
- return r.instance.validateConsumers;
341
+ return c.instance.validateConsumers;
329
342
  }
330
343
  };
331
344
  /** Flag to enable/disable logging */
332
- n(r, "enableLog", !1), n(r, "resetInstance", () => {
333
- u.resetInstance();
345
+ i(c, "enableLog", !1), i(c, "resetInstance", () => {
346
+ g.resetInstance();
334
347
  });
335
- let h = r;
336
- class m {
348
+ let l = c;
349
+ class w {
337
350
  /**
338
351
  * Creates a new BlacObservable instance
339
352
  * @param bloc - The Bloc instance to observe
340
353
  */
341
354
  constructor(e) {
342
355
  /** The Bloc instance this observable is associated with */
343
- n(this, "bloc");
344
- n(this, "_observers", /* @__PURE__ */ new Set());
356
+ i(this, "bloc");
357
+ i(this, "_observers", /* @__PURE__ */ new Set());
345
358
  this.bloc = e;
346
359
  }
347
360
  /**
@@ -364,8 +377,8 @@ class m {
364
377
  * @returns A function that can be called to unsubscribe the observer
365
378
  */
366
379
  subscribe(e) {
367
- return h.log("BlacObservable.subscribe: Subscribing observer.", this.bloc, e), this._observers.add(e), e.lastState || (e.lastState = e.dependencyArray ? e.dependencyArray(this.bloc.state) : []), () => {
368
- h.log("BlacObservable.subscribe: Unsubscribing observer.", this.bloc, e), this.unsubscribe(e);
380
+ return l.log("BlacObservable.subscribe: Subscribing observer.", this.bloc, e), this._observers.add(e), () => {
381
+ l.log("BlacObservable.subscribe: Unsubscribing observer.", this.bloc, e), this.unsubscribe(e);
369
382
  };
370
383
  }
371
384
  /**
@@ -373,7 +386,7 @@ class m {
373
386
  * @param observer - The observer to unsubscribe
374
387
  */
375
388
  unsubscribe(e) {
376
- this._observers.delete(e), this.size === 0 && (h.log("BlacObservable.unsubscribe: No observers left. Disposing bloc.", this.bloc), this.bloc._dispose());
389
+ this._observers.delete(e), this.size === 0 && l.log("BlacObservable.unsubscribe: No observers left.", this.bloc);
377
390
  }
378
391
  /**
379
392
  * Notifies all observers of a state change
@@ -382,34 +395,48 @@ class m {
382
395
  * @param action - Optional action that triggered the state change
383
396
  */
384
397
  notify(e, t, s) {
385
- this._observers.forEach((i) => {
386
- let a = !1;
387
- if (i.dependencyArray) {
388
- const o = i.lastState || [], c = i.dependencyArray(e);
389
- for (let p = 0; p < c.length; p++) {
390
- const B = c[p], I = o[p] || [];
391
- for (let f = 0; f < B.length; f++)
392
- if (!Object.is(B[f], I[f])) {
393
- a = !0;
398
+ this._observers.forEach((n) => {
399
+ let o = !1;
400
+ if (n.dependencyArray) {
401
+ const a = n.lastState, r = n.dependencyArray(e);
402
+ if (!a)
403
+ o = !0;
404
+ else if (a.length !== r.length)
405
+ o = !0;
406
+ else
407
+ for (let h = 0; h < r.length; h++) {
408
+ const _ = r[h], p = a[h] || [];
409
+ if (_.length !== p.length) {
410
+ o = !0;
394
411
  break;
395
412
  }
396
- }
397
- i.lastState = c;
413
+ for (let f = 0; f < _.length; f++)
414
+ if (!Object.is(_[f], p[f])) {
415
+ o = !0;
416
+ break;
417
+ }
418
+ if (o) break;
419
+ }
420
+ n.lastState = r;
398
421
  } else
399
- a = !0;
400
- a && i.fn(e, t, s);
422
+ o = !0;
423
+ o && n.fn(e, t, s);
401
424
  });
402
425
  }
403
426
  /**
404
427
  * Clears the observer set
405
428
  */
406
429
  clear() {
407
- this._observers.forEach((e) => {
408
- this.unsubscribe(e);
409
- }), this._observers.clear();
430
+ this._observers.clear();
410
431
  }
411
432
  }
412
- class g {
433
+ function S() {
434
+ return typeof crypto < "u" && crypto.randomUUID ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(d) {
435
+ const e = Math.random() * 16 | 0;
436
+ return (d === "x" ? e : e & 3 | 8).toString(16);
437
+ });
438
+ }
439
+ class B {
413
440
  /**
414
441
  * Creates a new BlocBase instance with the given initial state.
415
442
  * Sets up the observer, registers with the Blac manager, and initializes addons.
@@ -417,57 +444,90 @@ class g {
417
444
  * @param initialState The initial state value for this Bloc
418
445
  */
419
446
  constructor(e) {
420
- n(this, "uid", crypto.randomUUID());
447
+ i(this, "uid", S());
421
448
  /**
422
449
  * Defines how dependencies are selected from the state for efficient updates.
423
450
  * When provided, observers will only be notified when selected dependencies change.
424
451
  */
425
- n(this, "defaultDependencySelector");
452
+ i(this, "defaultDependencySelector");
426
453
  /**
427
454
  * @internal
428
455
  * Indicates if this specific Bloc instance is isolated from others of the same type.
429
456
  */
430
- n(this, "_isolated", !1);
457
+ i(this, "_isolated", !1);
431
458
  /**
432
459
  * @internal
433
460
  * Observable responsible for managing state listeners and notifying consumers.
434
461
  */
435
- n(this, "_observer");
462
+ i(this, "_observer");
436
463
  /**
437
464
  * The unique identifier for this Bloc instance.
438
465
  * Defaults to the class name, but can be customized.
439
466
  */
440
- n(this, "_id");
467
+ i(this, "_id");
441
468
  /**
442
469
  * @internal
443
470
  * Reference string used internally for tracking and debugging.
444
471
  */
445
- n(this, "_instanceRef");
472
+ i(this, "_instanceRef");
446
473
  /**
447
474
  * @internal
448
475
  * Indicates if this specific Bloc instance should be kept alive when no consumers are present.
449
476
  */
450
- n(this, "_keepAlive", !1);
477
+ i(this, "_keepAlive", !1);
451
478
  /**
452
479
  * @readonly
453
480
  * Timestamp when this Bloc instance was created, useful for debugging and performance tracking.
454
481
  */
455
- n(this, "_createdAt", Date.now());
482
+ i(this, "_createdAt", Date.now());
483
+ /**
484
+ * @internal
485
+ * Disposal state to prevent race conditions
486
+ */
487
+ i(this, "_disposalState", "active");
456
488
  /**
457
489
  * @internal
458
490
  * The current state of the Bloc.
459
491
  */
460
- n(this, "_state");
492
+ i(this, "_state");
461
493
  /**
462
494
  * @internal
463
495
  * The previous state of the Bloc, maintained for comparison and history.
464
496
  */
465
- n(this, "_oldState");
497
+ i(this, "_oldState");
466
498
  /**
467
499
  * Props passed during Bloc instance creation.
468
500
  * Can be used to configure or parameterize the Bloc's behavior.
469
501
  */
470
- n(this, "props", null);
502
+ i(this, "props", null);
503
+ /**
504
+ * @internal
505
+ * Flag to prevent batching race conditions
506
+ */
507
+ i(this, "_batchingLock", !1);
508
+ /**
509
+ * @internal
510
+ * Pending batched updates
511
+ */
512
+ i(this, "_pendingUpdates", []);
513
+ /**
514
+ * @internal
515
+ * Map of consumer IDs to their WeakRef objects for proper cleanup
516
+ */
517
+ i(this, "_consumerRefs", /* @__PURE__ */ new Map());
518
+ /**
519
+ * @internal
520
+ * Validates that all consumer references are still alive
521
+ * Removes dead consumers automatically
522
+ */
523
+ i(this, "_validateConsumers", () => {
524
+ const e = [];
525
+ for (const [t, s] of this._consumerRefs)
526
+ s.deref() === void 0 && e.push(t);
527
+ for (const t of e)
528
+ this._consumers.delete(t), this._consumerRefs.delete(t);
529
+ this._consumers.size === 0 && !this._keepAlive && this._disposalState === "active" && this._scheduleDisposal();
530
+ });
471
531
  /**
472
532
  * @internal
473
533
  * Updates the Bloc instance's ID to a new value.
@@ -475,7 +535,7 @@ class g {
475
535
  *
476
536
  * @param id The new ID to assign to this Bloc instance
477
537
  */
478
- n(this, "_updateId", (e) => {
538
+ i(this, "_updateId", (e) => {
479
539
  const t = this._id;
480
540
  !e || e === t || (this._id = e);
481
541
  });
@@ -483,17 +543,12 @@ class g {
483
543
  * @internal
484
544
  * Optional function to be called when the Bloc is disposed.
485
545
  */
486
- n(this, "onDispose");
546
+ i(this, "onDispose");
487
547
  /**
488
548
  * @internal
489
549
  * Set of consumer IDs currently listening to this Bloc's state changes.
490
550
  */
491
- n(this, "_consumers", /* @__PURE__ */ new Set());
492
- /**
493
- * @internal
494
- * WeakSet to track consumer references for cleanup validation
495
- */
496
- n(this, "_consumerRefs", /* @__PURE__ */ new WeakSet());
551
+ i(this, "_consumers", /* @__PURE__ */ new Set());
497
552
  /**
498
553
  * @internal
499
554
  * Registers a new consumer to this Bloc instance.
@@ -502,8 +557,8 @@ class g {
502
557
  * @param consumerId The unique ID of the consumer being added
503
558
  * @param consumerRef Optional reference to the consumer object for cleanup validation
504
559
  */
505
- n(this, "_addConsumer", (e, t) => {
506
- this._consumers.has(e) || (this._consumers.add(e), t && this._consumerRefs.add(t));
560
+ i(this, "_addConsumer", (e, t) => {
561
+ this._disposalState === "active" && (this._consumers.has(e) || (this._consumers.add(e), t && this._consumerRefs.set(e, new WeakRef(t))));
507
562
  });
508
563
  /**
509
564
  * @internal
@@ -512,25 +567,20 @@ class g {
512
567
  *
513
568
  * @param consumerId The unique ID of the consumer being removed
514
569
  */
515
- n(this, "_removeConsumer", (e) => {
516
- this._consumers.has(e) && (this._consumers.delete(e), this._consumers.size === 0 && !this._keepAlive && this._scheduleDisposal());
570
+ i(this, "_removeConsumer", (e) => {
571
+ this._consumers.has(e) && (this._consumers.delete(e), this._consumerRefs.delete(e), this._consumers.size === 0 && !this._keepAlive && this._disposalState === "active" && this._scheduleDisposal());
517
572
  });
518
573
  /**
519
574
  * @internal
520
575
  * Handler function for disposal (can be set by Blac manager)
521
576
  */
522
- n(this, "_disposalHandler");
523
- n(this, "lastUpdate", Date.now());
577
+ i(this, "_disposalHandler");
578
+ i(this, "lastUpdate", Date.now());
524
579
  /**
525
580
  * @internal
526
581
  * Flag to indicate if batching is enabled for this bloc
527
582
  */
528
- n(this, "_batchingEnabled", !1);
529
- /**
530
- * @internal
531
- * Pending state updates when batching is enabled
532
- */
533
- n(this, "_pendingUpdates", []);
583
+ i(this, "_batchingEnabled", !1);
534
584
  /**
535
585
  * @internal
536
586
  * Updates the state and notifies all observers of the change.
@@ -539,17 +589,17 @@ class g {
539
589
  * @param oldState The previous state for comparison
540
590
  * @param action Optional metadata about what caused the state change
541
591
  */
542
- n(this, "_pushState", (e, t, s) => {
592
+ i(this, "_pushState", (e, t, s) => {
543
593
  if (e === void 0) {
544
594
  console.warn("BlocBase._pushState: newState is undefined", this);
545
595
  return;
546
596
  }
547
- if (s != null) {
548
- const i = typeof s;
549
- ["string", "object", "number"].includes(i) || console.warn("BlocBase._pushState: Invalid action type", this, s);
597
+ if (s !== void 0 && typeof s != "object" && typeof s != "function") {
598
+ console.warn("BlocBase._pushState: Invalid action type", this, s);
599
+ return;
550
600
  }
551
601
  if (this._batchingEnabled) {
552
- this._pendingUpdates.push({ newState: e, oldState: t, action: s });
602
+ this._pendingUpdates.push({ newState: e, oldState: t, action: s }), this._oldState = t, this._state = e;
553
603
  return;
554
604
  }
555
605
  this._oldState = t, this._state = e, this._observer.notify(e, t, s), this.lastUpdate = Date.now();
@@ -558,21 +608,22 @@ class g {
558
608
  * Enables batching for multiple state updates
559
609
  * @param batchFn Function to execute with batching enabled
560
610
  */
561
- n(this, "batch", (e) => {
562
- const t = this._batchingEnabled;
563
- this._batchingEnabled = !0;
611
+ i(this, "batch", (e) => {
612
+ if (this._batchingLock)
613
+ return e();
614
+ this._batchingLock = !0, this._batchingEnabled = !0, this._pendingUpdates = [];
564
615
  try {
565
- const s = e();
616
+ const t = e();
566
617
  if (this._pendingUpdates.length > 0) {
567
- const i = this._pendingUpdates[this._pendingUpdates.length - 1];
568
- this._oldState = this._pendingUpdates[0].oldState, this._state = i.newState, this._observer.notify(i.newState, this._oldState, i.action), this.lastUpdate = Date.now(), this._pendingUpdates = [];
618
+ const s = this._pendingUpdates[this._pendingUpdates.length - 1];
619
+ this._observer.notify(s.newState, s.oldState, s.action), this.lastUpdate = Date.now();
569
620
  }
570
- return s;
621
+ return t;
571
622
  } finally {
572
- this._batchingEnabled = t;
623
+ this._batchingEnabled = !1, this._batchingLock = !1, this._pendingUpdates = [];
573
624
  }
574
625
  });
575
- this._state = e, this._observer = new m(this), this._id = this.constructor.name;
626
+ this._state = e, this._observer = new w(this), this._id = this.constructor.name;
576
627
  const t = this.constructor;
577
628
  this._keepAlive = typeof t.keepAlive == "boolean" ? t.keepAlive : !1, this._isolated = typeof t.isolated == "boolean" ? t.isolated : !1;
578
629
  }
@@ -603,7 +654,7 @@ class g {
603
654
  */
604
655
  _dispose() {
605
656
  var e;
606
- this._consumers.clear(), this._observer.clear(), (e = this.onDispose) == null || e.call(this);
657
+ this._disposalState === "active" && (this._disposalState = "disposing", this._consumers.clear(), this._consumerRefs.clear(), this._observer.clear(), (e = this.onDispose) == null || e.call(this), this._disposalState = "disposed");
607
658
  }
608
659
  /**
609
660
  * @internal
@@ -617,9 +668,7 @@ class g {
617
668
  * Schedules disposal of this bloc instance if it has no consumers
618
669
  */
619
670
  _scheduleDisposal() {
620
- setTimeout(() => {
621
- this._consumers.size === 0 && !this._keepAlive && (this._disposalHandler ? this._disposalHandler(this) : this._dispose());
622
- }, 0);
671
+ this._disposalState === "active" && this._consumers.size === 0 && !this._keepAlive && (this._disposalHandler ? this._disposalHandler(this) : this._dispose());
623
672
  }
624
673
  }
625
674
  /**
@@ -627,49 +676,36 @@ class g {
627
676
  * Use this when state should not be shared between components.
628
677
  * @default false
629
678
  */
630
- n(g, "isolated", !1), /**
679
+ i(B, "isolated", !1), /**
631
680
  * When true, the Bloc instance persists even when there are no active consumers.
632
681
  * Useful for maintaining state between component unmount/remount cycles.
633
682
  * @default false
634
683
  */
635
- n(g, "keepAlive", !1);
636
- class A extends g {
684
+ i(B, "keepAlive", !1);
685
+ class b extends B {
637
686
  constructor() {
638
687
  super(...arguments);
639
688
  // Stores handlers: Map<EventConstructor (subtype of A), HandlerFunction>
640
689
  // The handler's event parameter will be correctly typed to the specific EventConstructor
641
690
  // by the 'on' method's signature.
642
- n(this, "eventHandlers", /* @__PURE__ */ new Map());
691
+ i(this, "eventHandlers", /* @__PURE__ */ new Map());
692
+ /**
693
+ * @internal
694
+ * Event queue to ensure sequential processing of async events
695
+ */
696
+ i(this, "_eventQueue", []);
697
+ /**
698
+ * @internal
699
+ * Flag indicating if an event is currently being processed
700
+ */
701
+ i(this, "_isProcessingEvent", !1);
643
702
  /**
644
703
  * Dispatches an action/event to the Bloc.
645
- * If a handler is registered for this specific event type (via 'on'), it will be invoked.
646
- * Asynchronous handlers are awaited.
704
+ * Events are queued and processed sequentially to prevent race conditions.
647
705
  * @param action The action/event instance to be processed.
648
706
  */
649
- n(this, "add", async (t) => {
650
- const s = t.constructor, i = this.eventHandlers.get(s);
651
- if (i) {
652
- const a = (o) => {
653
- const c = this.state;
654
- this._pushState(o, c, t);
655
- };
656
- try {
657
- await i(t, a);
658
- } catch (o) {
659
- h.error(
660
- `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${s.name}':`,
661
- o,
662
- "Action:",
663
- t
664
- );
665
- }
666
- } else {
667
- const a = t.constructor.name || "UnnamedConstructor";
668
- h.warn(
669
- `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${a}'. Action was:`,
670
- t
671
- );
672
- }
707
+ i(this, "add", async (t) => {
708
+ this._eventQueue.push(t), this._isProcessingEvent || await this._processEventQueue();
673
709
  });
674
710
  }
675
711
  /**
@@ -680,15 +716,73 @@ class A extends g {
680
716
  * The 'event' parameter in the handler will be typed to the specific eventConstructor.
681
717
  */
682
718
  on(t, s) {
683
- this.eventHandlers.has(t) && h.warn(
719
+ this.eventHandlers.has(t) && l.warn(
684
720
  `[Bloc ${this._name}:${String(this._id)}] Handler for event '${t.name}' already registered. It will be overwritten.`
685
721
  ), this.eventHandlers.set(
686
722
  t,
687
723
  s
688
724
  );
689
725
  }
726
+ /**
727
+ * @internal
728
+ * Processes events from the queue sequentially
729
+ */
730
+ async _processEventQueue() {
731
+ if (!this._isProcessingEvent) {
732
+ this._isProcessingEvent = !0;
733
+ try {
734
+ for (; this._eventQueue.length > 0; ) {
735
+ const t = this._eventQueue.shift();
736
+ await this._processEvent(t);
737
+ }
738
+ } finally {
739
+ this._isProcessingEvent = !1;
740
+ }
741
+ }
742
+ }
743
+ /**
744
+ * @internal
745
+ * Processes a single event
746
+ */
747
+ async _processEvent(t) {
748
+ const s = t.constructor, n = this.eventHandlers.get(s);
749
+ if (n) {
750
+ const o = (a) => {
751
+ const r = this.state;
752
+ this._pushState(a, r, t);
753
+ };
754
+ try {
755
+ await n(t, o);
756
+ } catch (a) {
757
+ const r = t.constructor.name || "UnnamedConstructor", h = {
758
+ blocName: this._name,
759
+ blocId: String(this._id),
760
+ eventType: r,
761
+ currentState: this.state,
762
+ action: t,
763
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
764
+ };
765
+ if (l.error(
766
+ `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${r}':`,
767
+ a,
768
+ "Context:",
769
+ h
770
+ ), a instanceof Error && a.name === "CriticalError")
771
+ throw a;
772
+ }
773
+ } else {
774
+ const o = t.constructor.name || "UnnamedConstructor";
775
+ l.warn(
776
+ `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${o}'.`,
777
+ "Registered handlers:",
778
+ Array.from(this.eventHandlers.keys()).map((a) => a.name),
779
+ "Action was:",
780
+ t
781
+ );
782
+ }
783
+ }
690
784
  }
691
- class M extends g {
785
+ class k extends B {
692
786
  /**
693
787
  * Updates the current state and notifies all observers of the change.
694
788
  * If the new state is identical to the current state (using Object.is),
@@ -711,7 +805,7 @@ class M extends g {
711
805
  */
712
806
  patch(e, t = !1) {
713
807
  if (typeof this.state != "object" || this.state === null) {
714
- h.warn(
808
+ l.warn(
715
809
  "Cubit.patch: was called on a cubit where the state is not an object. This is a no-op."
716
810
  );
717
811
  return;
@@ -720,10 +814,10 @@ class M extends g {
720
814
  if (t)
721
815
  s = !0;
722
816
  else
723
- for (const i in e)
724
- if (Object.prototype.hasOwnProperty.call(e, i)) {
725
- const o = this.state[i];
726
- if (!Object.is(e[i], o)) {
817
+ for (const n in e)
818
+ if (Object.prototype.hasOwnProperty.call(e, n)) {
819
+ const a = this.state[n];
820
+ if (!Object.is(e[n], a)) {
727
821
  s = !0;
728
822
  break;
729
823
  }
@@ -734,12 +828,160 @@ class M extends g {
734
828
  });
735
829
  }
736
830
  }
831
+ class x {
832
+ /**
833
+ * Sets up a clean test environment
834
+ */
835
+ static setUp() {
836
+ this.originalInstance = l.instance, l.resetInstance(), l.enableLog = !1;
837
+ }
838
+ /**
839
+ * Tears down the test environment and restores original state
840
+ */
841
+ static tearDown() {
842
+ l.resetInstance();
843
+ }
844
+ /**
845
+ * Creates a test bloc with automatic cleanup
846
+ */
847
+ static createBloc(e, ...t) {
848
+ const s = new e(...t);
849
+ return l.activateBloc(s), s;
850
+ }
851
+ /**
852
+ * Waits for a bloc to emit a specific state
853
+ */
854
+ static async waitForState(e, t, s = 5e3) {
855
+ return new Promise((n, o) => {
856
+ const a = setTimeout(() => {
857
+ o(new Error(`Timeout waiting for state matching predicate after ${s}ms`));
858
+ }, s), r = e._observer.subscribe({
859
+ id: `test-waiter-${crypto.randomUUID()}`,
860
+ fn: (h) => {
861
+ t(h) && (clearTimeout(a), r(), n(h));
862
+ }
863
+ });
864
+ t(e.state) && (clearTimeout(a), r(), n(e.state));
865
+ });
866
+ }
867
+ /**
868
+ * Expects a bloc to emit specific states in order
869
+ */
870
+ static async expectStates(e, t, s = 5e3) {
871
+ const n = [];
872
+ return new Promise((o, a) => {
873
+ const r = setTimeout(() => {
874
+ a(new Error(
875
+ `Timeout waiting for states. Expected: ${JSON.stringify(t)}, Received: ${JSON.stringify(n)}`
876
+ ));
877
+ }, s), h = e._observer.subscribe({
878
+ id: `test-expecter-${crypto.randomUUID()}`,
879
+ fn: (_) => {
880
+ if (n.push(_), n.length === t.length) {
881
+ clearTimeout(r), h();
882
+ for (let p = 0; p < t.length; p++) {
883
+ const f = t[p], I = n[p];
884
+ if (JSON.stringify(f) !== JSON.stringify(I)) {
885
+ a(new Error(
886
+ `State mismatch at index ${p}. Expected: ${JSON.stringify(f)}, Received: ${JSON.stringify(I)}`
887
+ ));
888
+ return;
889
+ }
890
+ }
891
+ o();
892
+ }
893
+ }
894
+ });
895
+ });
896
+ }
897
+ }
898
+ i(x, "originalInstance");
899
+ class A extends b {
900
+ constructor(t) {
901
+ super(t);
902
+ i(this, "mockHandlers", /* @__PURE__ */ new Map());
903
+ }
904
+ /**
905
+ * Mock an event handler for testing
906
+ */
907
+ mockEventHandler(t, s) {
908
+ this.on(t, s);
909
+ }
910
+ /**
911
+ * Get the number of registered handlers
912
+ */
913
+ getHandlerCount() {
914
+ return this.eventHandlers.size;
915
+ }
916
+ /**
917
+ * Check if a handler is registered for an event type
918
+ */
919
+ hasHandler(t) {
920
+ return this.eventHandlers.has(t);
921
+ }
922
+ }
923
+ class E extends k {
924
+ constructor(t) {
925
+ super(t);
926
+ i(this, "stateHistory", []);
927
+ this.stateHistory.push(t);
928
+ }
929
+ /**
930
+ * Override emit to track state history
931
+ */
932
+ emit(t) {
933
+ this.stateHistory.push(t), super.emit(t);
934
+ }
935
+ /**
936
+ * Get the history of all states
937
+ */
938
+ getStateHistory() {
939
+ return [...this.stateHistory];
940
+ }
941
+ /**
942
+ * Clear state history
943
+ */
944
+ clearStateHistory() {
945
+ this.stateHistory = [this.state];
946
+ }
947
+ }
948
+ class R {
949
+ constructor() {
950
+ i(this, "initialStats");
951
+ this.initialStats = l.getMemoryStats();
952
+ }
953
+ /**
954
+ * Check for memory leaks and return a report
955
+ */
956
+ checkForLeaks() {
957
+ const e = l.getMemoryStats(), t = e.registeredBlocs > this.initialStats.registeredBlocs || e.isolatedBlocs > this.initialStats.isolatedBlocs || e.keepAliveBlocs > this.initialStats.keepAliveBlocs, s = `
958
+ Memory Leak Detection Report:
959
+ - Initial registered blocs: ${this.initialStats.registeredBlocs}
960
+ - Current registered blocs: ${e.registeredBlocs}
961
+ - Initial isolated blocs: ${this.initialStats.isolatedBlocs}
962
+ - Current isolated blocs: ${e.isolatedBlocs}
963
+ - Initial keep-alive blocs: ${this.initialStats.keepAliveBlocs}
964
+ - Current keep-alive blocs: ${e.keepAliveBlocs}
965
+ - Potential leaks detected: ${t ? "YES" : "NO"}
966
+ `.trim();
967
+ return {
968
+ hasLeaks: t,
969
+ report: s,
970
+ stats: e
971
+ };
972
+ }
973
+ }
737
974
  export {
738
- h as Blac,
739
- m as BlacObservable,
740
- A as Bloc,
741
- g as BlocBase,
742
- M as Cubit,
743
- b as setBlacInstanceManager
975
+ l as Blac,
976
+ w as BlacObservable,
977
+ b as Bloc,
978
+ B as BlocBase,
979
+ x as BlocTest,
980
+ k as Cubit,
981
+ R as MemoryLeakDetector,
982
+ A as MockBloc,
983
+ E as MockCubit,
984
+ S as generateUUID,
985
+ M as setBlacInstanceManager
744
986
  };
745
987
  //# sourceMappingURL=index.es.js.map