@blac/core 2.0.0-rc-2 → 2.0.0-rc-4

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,44 +1,67 @@
1
- var B = Object.defineProperty;
2
- var m = (c, t, e) => t in c ? B(c, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[t] = e;
3
- var n = (c, t, e) => m(c, typeof t != "symbol" ? t + "" : t, e);
4
- var _ = /* @__PURE__ */ ((c) => (c.BLOC_DISPOSED = "BLOC_DISPOSED", c.BLOC_CREATED = "BLOC_CREATED", c.LISTENER_REMOVED = "LISTENER_REMOVED", c.LISTENER_ADDED = "LISTENER_ADDED", c.STATE_CHANGED = "STATE_CHANGED", c.BLOC_CONSUMER_REMOVED = "BLOC_CONSUMER_REMOVED", c.BLOC_CONSUMER_ADDED = "BLOC_CONSUMER_ADDED", c))(_ || {});
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 {
5
+ getInstance() {
6
+ return d._instance || (d._instance = new h({ __unsafe_ignore_singleton: !0 })), d._instance;
7
+ }
8
+ setInstance(e) {
9
+ d._instance = e;
10
+ }
11
+ resetInstance() {
12
+ const e = d._instance;
13
+ d._instance = new h({ __unsafe_ignore_singleton: !0 });
14
+ for (const t of e.keepAliveBlocs)
15
+ d._instance.keepAliveBlocs.add(t), d._instance.uidRegistry.set(t.uid, t);
16
+ }
17
+ };
18
+ n(d, "_instance");
19
+ let _ = d, u = new _();
20
+ function b(l) {
21
+ u = l;
22
+ }
5
23
  const r = class r {
6
24
  /**
7
25
  * Creates a new Blac instance.
8
26
  * @param options - Configuration options including singleton control
9
27
  */
10
- constructor(t = {}) {
28
+ constructor(e = {}) {
11
29
  /** Timestamp when the instance was created */
12
30
  n(this, "createdAt", Date.now());
13
31
  /** Map storing all registered bloc instances by their class name and ID */
14
32
  n(this, "blocInstanceMap", /* @__PURE__ */ new Map());
15
33
  /** Map storing isolated bloc instances grouped by their constructor */
16
34
  n(this, "isolatedBlocMap", /* @__PURE__ */ new Map());
17
- n(this, "pluginList", []);
35
+ /** Map for O(1) lookup of isolated blocs by UID */
36
+ n(this, "isolatedBlocIndex", /* @__PURE__ */ new Map());
37
+ /** Map tracking UIDs to prevent memory leaks */
38
+ n(this, "uidRegistry", /* @__PURE__ */ new Map());
39
+ /** Set of keep-alive blocs for controlled cleanup */
40
+ n(this, "keepAliveBlocs", /* @__PURE__ */ new Set());
18
41
  /** Flag to control whether changes should be posted to document */
19
42
  n(this, "postChangesToDocument", !1);
20
43
  /**
21
44
  * Logs messages to console when logging is enabled
22
45
  * @param args - Arguments to log
23
46
  */
24
- n(this, "log", (...t) => {
25
- r.enableLog && console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...t);
47
+ n(this, "log", (...e) => {
48
+ r.enableLog && console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...e);
26
49
  });
27
50
  /**
28
51
  * Logs a warning message
29
52
  * @param message - Warning message
30
53
  * @param args - Additional arguments
31
54
  */
32
- n(this, "warn", (t, ...e) => {
33
- r.enableLog && console.warn(`🚨 [Blac ${String(r.instance.createdAt)}]`, t, ...e);
55
+ n(this, "warn", (e, ...t) => {
56
+ r.enableLog && console.warn(`🚨 [Blac ${String(r.instance.createdAt)}]`, e, ...t);
34
57
  });
35
58
  /**
36
59
  * Logs an error message
37
60
  * @param message - Error message
38
61
  * @param args - Additional arguments
39
62
  */
40
- n(this, "error", (t, ...e) => {
41
- r.enableLog && console.error(`🚨 [Blac ${String(r.instance.createdAt)}]`, t, ...e);
63
+ n(this, "error", (e, ...t) => {
64
+ r.enableLog && console.error(`🚨 [Blac ${String(r.instance.createdAt)}]`, e, ...t);
42
65
  });
43
66
  /**
44
67
  * Resets the Blac instance to a new one, disposing non-keepAlive blocs
@@ -46,66 +69,26 @@ const r = class r {
46
69
  */
47
70
  n(this, "resetInstance", () => {
48
71
  this.log("Reset Blac instance");
49
- const t = new Map(this.blocInstanceMap), e = new Map(this.isolatedBlocMap);
50
- t.forEach((s) => {
51
- s._dispose();
52
- }), e.forEach((s) => {
72
+ const e = new Map(this.blocInstanceMap), t = new Map(this.isolatedBlocMap);
73
+ e.forEach((s) => {
74
+ s._keepAlive || s._dispose();
75
+ }), t.forEach((s) => {
53
76
  s.forEach((i) => {
54
- i._dispose();
77
+ i._keepAlive || i._dispose();
55
78
  });
56
- }), this.blocInstanceMap.clear(), this.isolatedBlocMap.clear(), r.instance = new r({
57
- __unsafe_ignore_singleton: !0
58
- });
59
- });
60
- /**
61
- * Adds a plugin to the Blac instance
62
- * @param plugin - The plugin to add
63
- */
64
- n(this, "addPlugin", (t) => {
65
- this.pluginList.findIndex((s) => s.name === t.name) === -1 && (this.log("Add plugin", t.name), this.pluginList.push(t));
66
- });
67
- /**
68
- * Dispatches a lifecycle event to all registered plugins
69
- * @param event - The lifecycle event to dispatch
70
- * @param bloc - The bloc instance involved in the event
71
- * @param params - Additional parameters for the event
72
- */
73
- n(this, "dispatchEventToPlugins", (t, e, s) => {
74
- this.pluginList.forEach((i) => {
75
- i.onEvent(t, e, s);
76
- });
77
- });
78
- /**
79
- * Dispatches a lifecycle event and handles related cleanup actions.
80
- * This method is responsible for:
81
- * - Logging the event
82
- * - Handling bloc disposal when needed
83
- * - Managing bloc consumer cleanup
84
- * - Forwarding the event to plugins
85
- *
86
- * @param event - The lifecycle event to dispatch
87
- * @param bloc - The bloc instance involved in the event
88
- * @param params - Additional parameters for the event
89
- */
90
- n(this, "dispatchEvent", (t, e, s) => {
91
- switch (this.log(t, e, s), t) {
92
- case "BLOC_DISPOSED":
93
- this.disposeBloc(e);
94
- break;
95
- case "BLOC_CONSUMER_REMOVED":
96
- case "LISTENER_REMOVED":
97
- this.log(`[${e._name}:${String(e._id)}] Listener/Consumer removed. Listeners: ${String(e._observer.size)}, Consumers: ${String(e._consumers.size)}, KeepAlive: ${String(e._keepAlive)}`), e._observer.size === 0 && e._consumers.size === 0 && !e._keepAlive && (this.log(`[${e._name}:${String(e._id)}] No listeners or consumers left and not keepAlive. Disposing.`), e._dispose());
98
- break;
99
- }
100
- this.dispatchEventToPlugins(t, e, s);
79
+ }), this.blocInstanceMap.clear(), this.isolatedBlocMap.clear(), this.isolatedBlocIndex.clear(), u.resetInstance();
101
80
  });
102
81
  /**
103
82
  * Disposes of a bloc instance by removing it from the appropriate registry
104
83
  * @param bloc - The bloc instance to dispose
105
84
  */
106
- n(this, "disposeBloc", (t) => {
107
- const e = t.constructor, s = this.createBlocInstanceMapKey(t._name, t._id);
108
- this.log(`[${t._name}:${String(t._id)}] disposeBloc called. Isolated: ${String(e.isolated)}`), e.isolated ? (this.unregisterIsolatedBlocInstance(t), this.blocInstanceMap.delete(s)) : this.unregisterBlocInstance(t), this.dispatchEventToPlugins("BLOC_DISPOSED", t);
85
+ n(this, "disposeBloc", (e) => {
86
+ 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);
88
+ });
89
+ n(this, "activateBloc", (e) => {
90
+ const t = e.constructor;
91
+ (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));
109
92
  });
110
93
  /**
111
94
  * Gets or creates a bloc instance based on the provided class and options.
@@ -118,23 +101,27 @@ const r = class r {
118
101
  * - instanceRef: Optional reference string for the instance
119
102
  * @returns The bloc instance
120
103
  */
121
- n(this, "getBloc", (t, e = {}) => {
122
- const { id: s } = e, i = t, a = s ?? t.name;
123
- if (this.log(`[${t.name}:${String(a)}] getBloc called. Options:`, e), i.isolated) {
124
- const o = this.findIsolatedBlocInstance(t, a);
125
- if (o)
126
- return this.log(`[${t.name}:${String(a)}] Found existing isolated instance.`), o;
127
- }
128
- if (!i.isolated) {
129
- const o = this.findRegisteredBlocInstance(t, a);
130
- if (o)
131
- return this.log(`[${t.name}:${String(a)}] Found existing registered instance.`), o;
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;
110
+ if (t.throwIfNotFound)
111
+ throw new Error(`Isolated bloc ${e.name} not found`);
112
+ } 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;
116
+ if (t.throwIfNotFound)
117
+ throw new Error(`Registered bloc ${e.name} not found`);
132
118
  }
133
- return this.log(`[${t.name}:${String(a)}] No existing instance found. Creating new one.`), this.createNewBlocInstance(
134
- t,
119
+ const o = this.createNewBlocInstance(
120
+ e,
135
121
  a,
136
- e
122
+ t
137
123
  );
124
+ return this.log(`[${e.name}:${String(a)}] (getBloc) No existing instance found. Creating new one.`, t, o), o;
138
125
  });
139
126
  /**
140
127
  * Gets a bloc instance or throws an error if it doesn't exist
@@ -144,11 +131,11 @@ const r = class r {
144
131
  * - props: Properties to pass to the bloc constructor
145
132
  * - instanceRef: Optional reference string for the instance
146
133
  */
147
- n(this, "getBlocOrThrow", (t, e = {}) => {
148
- const s = t.isolated, i = e.id || t.name, a = s ? this.findIsolatedBlocInstance(t, i) : this.findRegisteredBlocInstance(t, i);
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);
149
136
  if (a)
150
137
  return a;
151
- throw new Error(`Bloc ${t.name} not found`);
138
+ throw new Error(`Bloc ${e.name} not found`);
152
139
  });
153
140
  /**
154
141
  * Gets all instances of a specific bloc class
@@ -157,27 +144,85 @@ const r = class r {
157
144
  * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property)
158
145
  * @returns Array of matching bloc instances
159
146
  */
160
- n(this, "getAllBlocs", (t, e = {}) => {
147
+ n(this, "getAllBlocs", (e, t = {}) => {
161
148
  const s = [];
162
149
  if (this.blocInstanceMap.forEach((i) => {
163
- i.constructor === t && s.push(i);
164
- }), e.searchIsolated !== !1) {
165
- const i = this.isolatedBlocMap.get(t);
150
+ i.constructor === e && s.push(i);
151
+ }), t.searchIsolated !== !1) {
152
+ const i = this.isolatedBlocMap.get(e);
166
153
  i && s.push(...i.map((a) => a));
167
154
  }
168
155
  return s;
169
156
  });
170
- const { __unsafe_ignore_singleton: e = !1 } = t;
171
- if (!e)
157
+ /**
158
+ * Disposes all keep-alive blocs of a specific type
159
+ * @param blocClass - The bloc class to dispose
160
+ */
161
+ n(this, "disposeKeepAliveBlocs", (e) => {
162
+ const t = [];
163
+ for (const s of this.keepAliveBlocs)
164
+ (!e || s.constructor === e) && t.push(s);
165
+ t.forEach((s) => s._dispose());
166
+ });
167
+ /**
168
+ * Disposes all blocs matching a pattern
169
+ * @param predicate - Function to test each bloc for disposal
170
+ */
171
+ n(this, "disposeBlocs", (e) => {
172
+ const t = [];
173
+ for (const s of this.blocInstanceMap.values())
174
+ e(s) && t.push(s);
175
+ for (const s of this.isolatedBlocMap.values())
176
+ for (const i of s)
177
+ e(i) && t.push(i);
178
+ t.forEach((s) => s._dispose());
179
+ });
180
+ /**
181
+ * Gets memory usage statistics for debugging
182
+ */
183
+ n(this, "getMemoryStats", () => ({
184
+ totalBlocs: this.uidRegistry.size,
185
+ registeredBlocs: this.blocInstanceMap.size,
186
+ isolatedBlocs: Array.from(this.isolatedBlocMap.values()).reduce((e, t) => e + t.length, 0),
187
+ keepAliveBlocs: this.keepAliveBlocs.size,
188
+ isolatedBlocTypes: this.isolatedBlocMap.size
189
+ }));
190
+ /**
191
+ * Validates consumer references and cleans up orphaned consumers
192
+ */
193
+ n(this, "validateConsumers", () => {
194
+ for (const e of this.uidRegistry.values())
195
+ e._consumers.size === 0 && !e._keepAlive && setTimeout(() => {
196
+ e._consumers.size === 0 && !e._keepAlive && e._dispose();
197
+ }, 1e3);
198
+ });
199
+ const { __unsafe_ignore_singleton: t = !1 } = e;
200
+ if (!t)
172
201
  return r.instance;
173
- r.instance = this;
202
+ u.setInstance(this);
203
+ }
204
+ /** @deprecated Use getInstance() instead */
205
+ static get instance() {
206
+ return u.getInstance();
207
+ }
208
+ static get getAllBlocs() {
209
+ return r.instance.getAllBlocs;
210
+ }
211
+ static get log() {
212
+ return r.instance.log;
174
213
  }
175
214
  /**
176
- * Gets the singleton instance of Blac
215
+ * Gets the current Blac instance
177
216
  * @returns The Blac instance
178
217
  */
179
218
  static getInstance() {
180
- return r.instance;
219
+ return u.getInstance();
220
+ }
221
+ static get warn() {
222
+ return r.instance.warn;
223
+ }
224
+ static get error() {
225
+ return r.instance.error;
181
226
  }
182
227
  /**
183
228
  * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID
@@ -185,24 +230,24 @@ const r = class r {
185
230
  * @param id - The instance ID
186
231
  * @returns A unique key string in the format "className:id"
187
232
  */
188
- createBlocInstanceMapKey(t, e) {
189
- return `${t}:${String(e)}`;
233
+ createBlocInstanceMapKey(e, t) {
234
+ return `${e}:${String(t)}`;
190
235
  }
191
236
  /**
192
237
  * Unregister a bloc instance from the main registry
193
238
  * @param bloc - The bloc instance to unregister
194
239
  */
195
- unregisterBlocInstance(t) {
196
- const e = this.createBlocInstanceMapKey(t._name, t._id);
197
- this.blocInstanceMap.delete(e);
240
+ unregisterBlocInstance(e) {
241
+ const t = this.createBlocInstanceMapKey(e._name, e._id);
242
+ this.blocInstanceMap.delete(t), this.uidRegistry.delete(e.uid), this.keepAliveBlocs.delete(e);
198
243
  }
199
244
  /**
200
245
  * Registers a bloc instance in the main registry
201
246
  * @param bloc - The bloc instance to register
202
247
  */
203
- registerBlocInstance(t) {
204
- const e = this.createBlocInstanceMapKey(t._name, t._id);
205
- this.blocInstanceMap.set(e, t);
248
+ registerBlocInstance(e) {
249
+ const t = this.createBlocInstanceMapKey(e._name, e._id);
250
+ this.blocInstanceMap.set(t, e), this.uidRegistry.set(e.uid, e), e._keepAlive && this.keepAliveBlocs.add(e);
206
251
  }
207
252
  /**
208
253
  * Finds a registered bloc instance by its class and ID
@@ -210,39 +255,44 @@ const r = class r {
210
255
  * @param id - The instance ID
211
256
  * @returns The found bloc instance or undefined if not found
212
257
  */
213
- findRegisteredBlocInstance(t, e) {
214
- if (t.isolated) return;
215
- const i = this.createBlocInstanceMapKey(t.name, e), a = this.blocInstanceMap.get(i);
216
- return a && this.log(`[${t.name}:${String(e)}] Found registered instance. Returning.`), a;
258
+ findRegisteredBlocInstance(e, t) {
259
+ if (e.isolated) return;
260
+ const i = this.createBlocInstanceMapKey(e.name, t);
261
+ return this.blocInstanceMap.get(i);
217
262
  }
218
263
  /**
219
264
  * Registers an isolated bloc instance in the isolated registry
220
265
  * @param bloc - The isolated bloc instance to register
221
266
  */
222
- registerIsolatedBlocInstance(t) {
223
- const e = t.constructor, s = this.isolatedBlocMap.get(e);
224
- s ? s.push(t) : this.isolatedBlocMap.set(e, [t]);
267
+ registerIsolatedBlocInstance(e) {
268
+ const t = e.constructor, s = this.isolatedBlocMap.get(t);
269
+ s ? s.push(e) : this.isolatedBlocMap.set(t, [e]), this.isolatedBlocIndex.set(e.uid, e), this.uidRegistry.set(e.uid, e), e._keepAlive && this.keepAliveBlocs.add(e);
225
270
  }
226
271
  /**
227
272
  * Unregister an isolated bloc instance from the isolated registry
228
273
  * @param bloc - The isolated bloc instance to unregister
229
274
  */
230
- unregisterIsolatedBlocInstance(t) {
231
- const e = t.constructor, s = this.isolatedBlocMap.get(e);
275
+ unregisterIsolatedBlocInstance(e) {
276
+ const t = e.constructor, s = this.isolatedBlocMap.get(t);
232
277
  if (s) {
233
- const i = s.findIndex((a) => a._id === t._id);
234
- i !== -1 && s.splice(i, 1), s.length === 0 && this.isolatedBlocMap.delete(e);
278
+ const i = s.findIndex((a) => a.uid === e.uid);
279
+ i !== -1 && s.splice(i, 1), s.length === 0 && this.isolatedBlocMap.delete(t);
235
280
  }
281
+ this.isolatedBlocIndex.delete(e.uid), this.uidRegistry.delete(e.uid), this.keepAliveBlocs.delete(e);
236
282
  }
237
283
  /**
238
- * Finds an isolated bloc instance by its class and ID
284
+ * Finds an isolated bloc instance by its class and ID (O(n) lookup)
239
285
  */
240
- findIsolatedBlocInstance(t, e) {
241
- if (!t.isolated) return;
242
- const i = this.isolatedBlocMap.get(t);
243
- if (!i) return;
244
- const a = i.find((o) => o._id === e);
245
- return a && this.log(`[${t.name}:${String(e)}] Found isolated instance. Returning.`), a;
286
+ findIsolatedBlocInstance(e, t) {
287
+ if (!e.isolated) return;
288
+ const i = this.isolatedBlocMap.get(e);
289
+ return i ? i.find((o) => o._id === t) : void 0;
290
+ }
291
+ /**
292
+ * Finds an isolated bloc instance by UID (O(1) lookup)
293
+ */
294
+ findIsolatedBlocInstanceByUid(e) {
295
+ return this.isolatedBlocIndex.get(e);
246
296
  }
247
297
  /**
248
298
  * Creates a new bloc instance and registers it in the appropriate registry
@@ -252,25 +302,47 @@ const r = class r {
252
302
  * @param instanceRef - Optional reference string for the instance
253
303
  * @returns The newly created bloc instance
254
304
  */
255
- createNewBlocInstance(t, e, s = {}) {
256
- const { props: i, instanceRef: a } = s, o = new t(i);
257
- return o._instanceRef = a, o.props = i || null, o._updateId(e), o.isIsolated ? (this.registerIsolatedBlocInstance(o), o) : (this.registerBlocInstance(o), o);
305
+ 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);
308
+ }
309
+ static get activateBloc() {
310
+ return r.instance.activateBloc;
311
+ }
312
+ static get getBloc() {
313
+ return r.instance.getBloc;
314
+ }
315
+ static get getBlocOrThrow() {
316
+ return r.instance.getBlocOrThrow;
317
+ }
318
+ static get disposeKeepAliveBlocs() {
319
+ return r.instance.disposeKeepAliveBlocs;
320
+ }
321
+ static get disposeBlocs() {
322
+ return r.instance.disposeBlocs;
323
+ }
324
+ static get getMemoryStats() {
325
+ return r.instance.getMemoryStats;
326
+ }
327
+ static get validateConsumers() {
328
+ return r.instance.validateConsumers;
258
329
  }
259
330
  };
260
- /** The singleton instance of Blac */
261
- n(r, "instance", new r()), n(r, "getAllBlocs", r.instance.getAllBlocs), n(r, "addPlugin", r.instance.addPlugin), /** Flag to enable/disable logging */
262
- n(r, "enableLog", !1), n(r, "log", r.instance.log), n(r, "warn", r.instance.warn), n(r, "error", r.instance.error), n(r, "resetInstance", r.instance.resetInstance), n(r, "getBloc", r.instance.getBloc), n(r, "getBlocOrThrow", r.instance.getBlocOrThrow);
331
+ /** Flag to enable/disable logging */
332
+ n(r, "enableLog", !1), n(r, "resetInstance", () => {
333
+ u.resetInstance();
334
+ });
263
335
  let h = r;
264
- class D {
336
+ class m {
265
337
  /**
266
338
  * Creates a new BlacObservable instance
267
339
  * @param bloc - The Bloc instance to observe
268
340
  */
269
- constructor(t) {
341
+ constructor(e) {
270
342
  /** The Bloc instance this observable is associated with */
271
343
  n(this, "bloc");
272
344
  n(this, "_observers", /* @__PURE__ */ new Set());
273
- this.bloc = t;
345
+ this.bloc = e;
274
346
  }
275
347
  /**
276
348
  * Gets the number of active observers
@@ -291,18 +363,17 @@ class D {
291
363
  * @param observer - The observer to subscribe
292
364
  * @returns A function that can be called to unsubscribe the observer
293
365
  */
294
- subscribe(t) {
295
- return this._observers.add(t), h.instance.dispatchEvent(_.LISTENER_ADDED, this.bloc, { listenerId: t.id }), t.lastState || (t.lastState = t.dependencyArray ? t.dependencyArray(this.bloc.state) : []), () => {
296
- this.unsubscribe(t);
366
+ 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);
297
369
  };
298
370
  }
299
371
  /**
300
372
  * Unsubscribes an observer from state changes
301
373
  * @param observer - The observer to unsubscribe
302
374
  */
303
- unsubscribe(t) {
304
- var e;
305
- (e = t.dispose) == null || e.call(t), this._observers.delete(t), h.instance.dispatchEvent(_.LISTENER_REMOVED, this.bloc, { listenerId: t.id });
375
+ unsubscribe(e) {
376
+ this._observers.delete(e), this.size === 0 && (h.log("BlacObservable.unsubscribe: No observers left. Disposing bloc.", this.bloc), this.bloc._dispose());
306
377
  }
307
378
  /**
308
379
  * Notifies all observers of a state change
@@ -310,52 +381,48 @@ class D {
310
381
  * @param oldState - The previous state value
311
382
  * @param action - Optional action that triggered the state change
312
383
  */
313
- notify(t, e, s) {
384
+ notify(e, t, s) {
314
385
  this._observers.forEach((i) => {
315
386
  let a = !1;
316
387
  if (i.dependencyArray) {
317
- const o = i.lastState || [], g = i.dependencyArray(t);
318
- for (let p = 0; p < g.length; p++) {
319
- const S = g[p], l = o[p] || [];
320
- for (let d = 0; d < S.length; d++)
321
- if (!Object.is(S[d], l[d])) {
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])) {
322
393
  a = !0;
323
394
  break;
324
395
  }
325
396
  }
326
- i.lastState = g;
397
+ i.lastState = c;
327
398
  } else
328
399
  a = !0;
329
- a && i.fn(t, e, s);
400
+ a && i.fn(e, t, s);
330
401
  });
331
402
  }
332
403
  /**
333
- * Disposes of all observers and clears the observer set
404
+ * Clears the observer set
334
405
  */
335
- dispose() {
336
- this._observers.forEach((t) => {
337
- this.unsubscribe(t);
406
+ clear() {
407
+ this._observers.forEach((e) => {
408
+ this.unsubscribe(e);
338
409
  }), this._observers.clear();
339
410
  }
340
411
  }
341
- class E {
412
+ class g {
342
413
  /**
343
414
  * Creates a new BlocBase instance with the given initial state.
344
415
  * Sets up the observer, registers with the Blac manager, and initializes addons.
345
416
  *
346
417
  * @param initialState The initial state value for this Bloc
347
418
  */
348
- constructor(t) {
419
+ constructor(e) {
420
+ n(this, "uid", crypto.randomUUID());
349
421
  /**
350
422
  * Defines how dependencies are selected from the state for efficient updates.
351
423
  * When provided, observers will only be notified when selected dependencies change.
352
424
  */
353
425
  n(this, "defaultDependencySelector");
354
- /**
355
- * @internal
356
- * Optional array of addons to extend the functionality of this Bloc.
357
- */
358
- n(this, "_addons");
359
426
  /**
360
427
  * @internal
361
428
  * Indicates if this specific Bloc instance is isolated from others of the same type.
@@ -366,11 +433,6 @@ class E {
366
433
  * Observable responsible for managing state listeners and notifying consumers.
367
434
  */
368
435
  n(this, "_observer");
369
- /**
370
- * @internal
371
- * Reference to the global Blac manager instance.
372
- */
373
- n(this, "_blac", h.getInstance());
374
436
  /**
375
437
  * The unique identifier for this Bloc instance.
376
438
  * Defaults to the class name, but can be customized.
@@ -413,9 +475,9 @@ class E {
413
475
  *
414
476
  * @param id The new ID to assign to this Bloc instance
415
477
  */
416
- n(this, "_updateId", (t) => {
417
- const e = this._id;
418
- !t || t === e || (this._id = t);
478
+ n(this, "_updateId", (e) => {
479
+ const t = this._id;
480
+ !e || e === t || (this._id = e);
419
481
  });
420
482
  /**
421
483
  * @internal
@@ -427,15 +489,21 @@ class E {
427
489
  * Set of consumer IDs currently listening to this Bloc's state changes.
428
490
  */
429
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());
430
497
  /**
431
498
  * @internal
432
499
  * Registers a new consumer to this Bloc instance.
433
500
  * Notifies the Blac manager that a consumer has been added.
434
501
  *
435
502
  * @param consumerId The unique ID of the consumer being added
503
+ * @param consumerRef Optional reference to the consumer object for cleanup validation
436
504
  */
437
- n(this, "_addConsumer", (t) => {
438
- this._consumers.add(t), this._blac.dispatchEvent(_.BLOC_CONSUMER_ADDED, this, { consumerId: t });
505
+ n(this, "_addConsumer", (e, t) => {
506
+ this._consumers.has(e) || (this._consumers.add(e), t && this._consumerRefs.add(t));
439
507
  });
440
508
  /**
441
509
  * @internal
@@ -444,22 +512,25 @@ class E {
444
512
  *
445
513
  * @param consumerId The unique ID of the consumer being removed
446
514
  */
447
- n(this, "_removeConsumer", (t) => {
448
- this._blac.log(`[${this._name}:${String(this._id ?? "default_id")}] Removing consumer: ${t}`), this._consumers.delete(t), this._blac.dispatchEvent(_.BLOC_CONSUMER_REMOVED, this, { consumerId: t });
515
+ n(this, "_removeConsumer", (e) => {
516
+ this._consumers.has(e) && (this._consumers.delete(e), this._consumers.size === 0 && !this._keepAlive && this._scheduleDisposal());
449
517
  });
450
518
  /**
451
519
  * @internal
452
- * Initializes all registered addons for this Bloc instance.
453
- * Calls the onInit lifecycle method on each addon if defined.
454
- */
455
- n(this, "_connectAddons", () => {
456
- const { _addons: t } = this;
457
- t && t.forEach((e) => {
458
- var s;
459
- (s = e.onInit) == null || s.call(e, this);
460
- });
461
- });
520
+ * Handler function for disposal (can be set by Blac manager)
521
+ */
522
+ n(this, "_disposalHandler");
462
523
  n(this, "lastUpdate", Date.now());
524
+ /**
525
+ * @internal
526
+ * Flag to indicate if batching is enabled for this bloc
527
+ */
528
+ n(this, "_batchingEnabled", !1);
529
+ /**
530
+ * @internal
531
+ * Pending state updates when batching is enabled
532
+ */
533
+ n(this, "_pendingUpdates", []);
463
534
  /**
464
535
  * @internal
465
536
  * Updates the state and notifies all observers of the change.
@@ -468,12 +539,42 @@ class E {
468
539
  * @param oldState The previous state for comparison
469
540
  * @param action Optional metadata about what caused the state change
470
541
  */
471
- n(this, "_pushState", (t, e, s) => {
472
- this._state = t, this._observer.notify(t, e, s), this.lastUpdate = Date.now();
542
+ n(this, "_pushState", (e, t, s) => {
543
+ if (e === void 0) {
544
+ console.warn("BlocBase._pushState: newState is undefined", this);
545
+ return;
546
+ }
547
+ if (s != null) {
548
+ const i = typeof s;
549
+ ["string", "object", "number"].includes(i) || console.warn("BlocBase._pushState: Invalid action type", this, s);
550
+ }
551
+ if (this._batchingEnabled) {
552
+ this._pendingUpdates.push({ newState: e, oldState: t, action: s });
553
+ return;
554
+ }
555
+ this._oldState = t, this._state = e, this._observer.notify(e, t, s), this.lastUpdate = Date.now();
556
+ });
557
+ /**
558
+ * Enables batching for multiple state updates
559
+ * @param batchFn Function to execute with batching enabled
560
+ */
561
+ n(this, "batch", (e) => {
562
+ const t = this._batchingEnabled;
563
+ this._batchingEnabled = !0;
564
+ try {
565
+ const s = e();
566
+ 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 = [];
569
+ }
570
+ return s;
571
+ } finally {
572
+ this._batchingEnabled = t;
573
+ }
473
574
  });
474
- this._state = t, this._observer = new D(this), this._blac.dispatchEvent(_.BLOC_CREATED, this), this._id = this.constructor.name;
475
- const e = this.constructor;
476
- this._keepAlive = e.keepAlive, this._isolated = e.isolated, this._addons = e.addons, this._connectAddons();
575
+ this._state = e, this._observer = new m(this), this._id = this.constructor.name;
576
+ const t = this.constructor;
577
+ this._keepAlive = typeof t.keepAlive == "boolean" ? t.keepAlive : !1, this._isolated = typeof t.isolated == "boolean" ? t.isolated : !1;
477
578
  }
478
579
  get isIsolated() {
479
580
  return this._isolated;
@@ -501,8 +602,24 @@ class E {
501
602
  * Notifies the Blac manager and clears all observers.
502
603
  */
503
604
  _dispose() {
504
- var t;
505
- this._observer.dispose(), this._blac.dispatchEvent(_.BLOC_DISPOSED, this), (t = this.onDispose) == null || t.call(this);
605
+ var e;
606
+ this._consumers.clear(), this._observer.clear(), (e = this.onDispose) == null || e.call(this);
607
+ }
608
+ /**
609
+ * @internal
610
+ * Sets the disposal handler for this bloc
611
+ */
612
+ _setDisposalHandler(e) {
613
+ this._disposalHandler = e;
614
+ }
615
+ /**
616
+ * @internal
617
+ * Schedules disposal of this bloc instance if it has no consumers
618
+ */
619
+ _scheduleDisposal() {
620
+ setTimeout(() => {
621
+ this._consumers.size === 0 && !this._keepAlive && (this._disposalHandler ? this._disposalHandler(this) : this._dispose());
622
+ }, 0);
506
623
  }
507
624
  }
508
625
  /**
@@ -510,59 +627,13 @@ class E {
510
627
  * Use this when state should not be shared between components.
511
628
  * @default false
512
629
  */
513
- n(E, "isolated", !1), /**
630
+ n(g, "isolated", !1), /**
514
631
  * When true, the Bloc instance persists even when there are no active consumers.
515
632
  * Useful for maintaining state between component unmount/remount cycles.
516
633
  * @default false
517
634
  */
518
- n(E, "keepAlive", !1);
519
- class M extends E {
520
- /**
521
- * Updates the current state and notifies all observers of the change.
522
- * If the new state is identical to the current state (using Object.is),
523
- * no update will occur.
524
- * @param state - The new state to set
525
- */
526
- emit(t) {
527
- if (Object.is(t, this.state))
528
- return;
529
- const e = this.state, s = t;
530
- this._pushState(s, e);
531
- }
532
- /**
533
- * Partially updates the current state by merging it with the provided state patch.
534
- * This method is only applicable when the state is an object type.
535
- *
536
- * @param statePatch - A partial state object containing only the properties to update
537
- * @param ignoreChangeCheck - If true, skips checking if the state has actually changed
538
- * @throws {TypeError} If the state is not an object type
539
- */
540
- patch(t, e = !1) {
541
- if (typeof this.state != "object" || this.state === null) {
542
- h.warn(
543
- "Cubit.patch: was called on a cubit where the state is not an object. This is a no-op."
544
- );
545
- return;
546
- }
547
- let s = !1;
548
- if (e)
549
- s = !0;
550
- else
551
- for (const i in t)
552
- if (Object.prototype.hasOwnProperty.call(t, i)) {
553
- const o = this.state[i];
554
- if (!Object.is(t[i], o)) {
555
- s = !0;
556
- break;
557
- }
558
- }
559
- s && this.emit({
560
- ...this.state,
561
- ...t
562
- });
563
- }
564
- }
565
- class w extends E {
635
+ n(g, "keepAlive", !1);
636
+ class A extends g {
566
637
  constructor() {
567
638
  super(...arguments);
568
639
  // Stores handlers: Map<EventConstructor (subtype of A), HandlerFunction>
@@ -575,28 +646,28 @@ class w extends E {
575
646
  * Asynchronous handlers are awaited.
576
647
  * @param action The action/event instance to be processed.
577
648
  */
578
- n(this, "add", async (e) => {
579
- const s = e.constructor, i = this.eventHandlers.get(s);
649
+ n(this, "add", async (t) => {
650
+ const s = t.constructor, i = this.eventHandlers.get(s);
580
651
  if (i) {
581
652
  const a = (o) => {
582
- const g = this.state;
583
- this._pushState(o, g, e);
653
+ const c = this.state;
654
+ this._pushState(o, c, t);
584
655
  };
585
656
  try {
586
- await i(e, a);
657
+ await i(t, a);
587
658
  } catch (o) {
588
659
  h.error(
589
660
  `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${s.name}':`,
590
661
  o,
591
662
  "Action:",
592
- e
663
+ t
593
664
  );
594
665
  }
595
666
  } else {
596
- const a = e.constructor.name || "UnnamedConstructor";
667
+ const a = t.constructor.name || "UnnamedConstructor";
597
668
  h.warn(
598
669
  `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${a}'. Action was:`,
599
- e
670
+ t
600
671
  );
601
672
  }
602
673
  });
@@ -608,63 +679,67 @@ class w extends E {
608
679
  * @param handler A function that processes the event and can emit new states.
609
680
  * The 'event' parameter in the handler will be typed to the specific eventConstructor.
610
681
  */
611
- on(e, s) {
612
- this.eventHandlers.has(e) && h.warn(
613
- `[Bloc ${this._name}:${String(this._id)}] Handler for event '${e.name}' already registered. It will be overwritten.`
682
+ on(t, s) {
683
+ this.eventHandlers.has(t) && h.warn(
684
+ `[Bloc ${this._name}:${String(this._id)}] Handler for event '${t.name}' already registered. It will be overwritten.`
614
685
  ), this.eventHandlers.set(
615
- e,
686
+ t,
616
687
  s
617
688
  );
618
689
  }
619
690
  }
620
- function I(c) {
621
- switch (c) {
622
- case "localStorage":
623
- return localStorage;
624
- case "sessionStorage":
625
- return sessionStorage;
626
- default:
627
- return localStorage;
691
+ class M extends g {
692
+ /**
693
+ * Updates the current state and notifies all observers of the change.
694
+ * If the new state is identical to the current state (using Object.is),
695
+ * no update will occur.
696
+ * @param state - The new state to set
697
+ */
698
+ emit(e) {
699
+ if (Object.is(e, this.state))
700
+ return;
701
+ const t = this.state, s = e;
702
+ this._pushState(s, t);
628
703
  }
629
- }
630
- function $(c = {}) {
631
- const {
632
- keyPrefix: t = "blac",
633
- keyName: e,
634
- defaultValue: s,
635
- storageType: i = "localStorage"
636
- } = c, a = (l) => `${t}:${String(l)}`, o = (l) => {
637
- var d;
638
- try {
639
- const u = I(i).getItem(a(l));
640
- if (typeof u != "string")
641
- return s;
642
- const f = JSON.parse(u);
643
- return typeof f.v < "u" ? f.v : s;
644
- } catch (u) {
645
- return (d = c.onError) == null || d.call(c, u), s;
646
- }
647
- }, g = (l) => {
648
- const d = e ?? l._id, u = o(d);
649
- l._pushState(u, null);
650
- };
651
- let p = "";
652
- return {
653
- name: "Persist",
654
- onInit: g,
655
- onEmit: ({ newState: l, cubit: d }) => {
656
- const u = e ?? d._id, f = JSON.stringify({ v: l });
657
- f !== p && (I(i).setItem(a(u), f), p = f);
704
+ /**
705
+ * Partially updates the current state by merging it with the provided state patch.
706
+ * This method is only applicable when the state is an object type.
707
+ *
708
+ * @param statePatch - A partial state object containing only the properties to update
709
+ * @param ignoreChangeCheck - If true, skips checking if the state has actually changed
710
+ * @throws {TypeError} If the state is not an object type
711
+ */
712
+ patch(e, t = !1) {
713
+ if (typeof this.state != "object" || this.state === null) {
714
+ h.warn(
715
+ "Cubit.patch: was called on a cubit where the state is not an object. This is a no-op."
716
+ );
717
+ return;
658
718
  }
659
- };
719
+ let s = !1;
720
+ if (t)
721
+ s = !0;
722
+ 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)) {
727
+ s = !0;
728
+ break;
729
+ }
730
+ }
731
+ s && this.emit({
732
+ ...this.state,
733
+ ...e
734
+ });
735
+ }
660
736
  }
661
737
  export {
662
738
  h as Blac,
663
- _ as BlacLifecycleEvent,
664
- D as BlacObservable,
665
- w as Bloc,
666
- E as BlocBase,
739
+ m as BlacObservable,
740
+ A as Bloc,
741
+ g as BlocBase,
667
742
  M as Cubit,
668
- $ as Persist
743
+ b as setBlacInstanceManager
669
744
  };
670
745
  //# sourceMappingURL=index.es.js.map