@blac/core 2.0.0-rc-5 → 2.0.0-rc-6

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