@blac/core 2.0.4 → 2.0.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/README.md +98 -126
- package/dist/StateContainerRegistry-Brmferpf.js +588 -0
- package/dist/StateContainerRegistry-Brmferpf.js.map +1 -0
- package/dist/StateContainerRegistry-CVPTMaHn.cjs +629 -0
- package/dist/StateContainerRegistry-CVPTMaHn.cjs.map +1 -0
- package/dist/branded-DndTHjv2.cjs +18 -0
- package/dist/branded-DndTHjv2.cjs.map +1 -0
- package/dist/branded-mwGzzPF1.js +13 -0
- package/dist/branded-mwGzzPF1.js.map +1 -0
- package/dist/config-BInS_9WT.cjs +101 -0
- package/dist/config-BInS_9WT.cjs.map +1 -0
- package/dist/config-BmFnPVM0.js +42 -0
- package/dist/config-BmFnPVM0.js.map +1 -0
- package/dist/constants.d.cts +81 -0
- package/dist/constants.d.ts +81 -0
- package/dist/core/Cubit.d.ts +24 -0
- package/dist/core/StateContainer.d.ts +177 -0
- package/dist/core/StateContainerRegistry.d.ts +189 -0
- package/dist/debug.cjs +11 -26
- package/dist/debug.d.cts +35 -0
- package/dist/debug.d.ts +35 -0
- package/dist/debug.js +2 -18
- package/dist/decorators/blac.d.ts +40 -0
- package/dist/decorators/index.d.ts +1 -0
- package/dist/index.cjs +162 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -794
- package/dist/index.d.ts +17 -794
- package/dist/index.js +145 -31
- package/dist/index.js.map +1 -1
- package/dist/plugin/BlacPlugin.d.ts +143 -0
- package/dist/plugin/PluginManager.d.ts +81 -0
- package/dist/plugins.cjs +3 -3
- package/dist/plugins.d.cts +25 -0
- package/dist/plugins.d.ts +25 -0
- package/dist/plugins.js +2 -3
- package/dist/registry/acquire.d.ts +2 -0
- package/dist/registry/borrow.d.ts +9 -0
- package/dist/registry/config.d.ts +8 -0
- package/dist/registry/ensure.d.ts +2 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/management.d.ts +4 -0
- package/dist/registry/queries.d.ts +5 -0
- package/dist/registry/release.d.ts +2 -0
- package/dist/resolve-dependencies-B22YjFGk.cjs +773 -0
- package/dist/resolve-dependencies-B22YjFGk.cjs.map +1 -0
- package/dist/resolve-dependencies-Cvq0glJF.js +648 -0
- package/dist/resolve-dependencies-Cvq0glJF.js.map +1 -0
- package/dist/tracked-B_MUXIpe.cjs +216 -0
- package/dist/tracked-B_MUXIpe.cjs.map +1 -0
- package/dist/tracked-I_q9buYg.js +181 -0
- package/dist/tracked-I_q9buYg.js.map +1 -0
- package/dist/tracking.cjs +3 -3
- package/dist/tracking.js +2 -3
- package/dist/types/branded.d.ts +26 -0
- package/dist/types/utilities.d.ts +51 -0
- package/dist/types.cjs +3 -14
- package/dist/types.js +1 -12
- package/dist/utils/idGenerator.d.ts +73 -0
- package/dist/utils/static-props.d.ts +35 -0
- package/dist/watch/index.d.ts +2 -0
- package/dist/watch/watch.d.ts +50 -0
- package/dist/watch-entry.cjs +7 -109
- package/dist/watch-entry.d.cts +26 -0
- package/dist/watch-entry.d.ts +26 -0
- package/dist/watch-entry.js +1 -104
- package/package.json +29 -51
- package/dist/StateContainerRegistry.cjs.map +0 -1
- package/dist/StateContainerRegistry.js.map +0 -1
- package/dist/debug.cjs.map +0 -1
- package/dist/debug.js.map +0 -1
- package/dist/management.cjs.map +0 -1
- package/dist/management.js.map +0 -1
- package/dist/resolve-dependencies.cjs.map +0 -1
- package/dist/resolve-dependencies.js.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/watch-entry.cjs.map +0 -1
- package/dist/watch-entry.js.map +0 -1
- package/dist/watch.cjs.map +0 -1
- package/dist/watch.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import "./tracking.js";
|
|
6
|
-
|
|
1
|
+
import { a as BLAC_DEFAULTS, i as PluginManager, n as globalRegistry, r as isIsolatedClass, s as BLAC_STATIC_PROPS, t as getPluginManager } from "./StateContainerRegistry-Brmferpf.js";
|
|
2
|
+
import { a as watch, i as instance, n as createTrackedContext, o as ensure, r as tracked, t as TrackedContext } from "./tracked-I_q9buYg.js";
|
|
3
|
+
import { a as clearAll, c as getAll, i as clear, l as getRefCount, n as getStats, o as register, r as setRegistry, s as forEach, t as getRegistry, u as hasInstance } from "./config-BmFnPVM0.js";
|
|
4
|
+
import { t as instanceId } from "./branded-mwGzzPF1.js";
|
|
7
5
|
//#region src/utils/idGenerator.ts
|
|
8
6
|
/**
|
|
9
7
|
* Generate simple ID with timestamp and random (no counter tracking)
|
|
@@ -35,7 +33,6 @@ function generateSimpleId(prefix, affix) {
|
|
|
35
33
|
function generateIsolatedKey() {
|
|
36
34
|
return `isolated-${Math.random().toString(36).slice(2, 11)}`;
|
|
37
35
|
}
|
|
38
|
-
|
|
39
36
|
//#endregion
|
|
40
37
|
//#region src/core/StateContainer.ts
|
|
41
38
|
const EMPTY_DEPS = /* @__PURE__ */ new Map();
|
|
@@ -76,6 +73,10 @@ var StateContainer = class StateContainer {
|
|
|
76
73
|
constructor(initialState) {
|
|
77
74
|
this.listeners = /* @__PURE__ */ new Set();
|
|
78
75
|
this._disposed = false;
|
|
76
|
+
this._hydrationStatus = "idle";
|
|
77
|
+
this._changedWhileHydrating = false;
|
|
78
|
+
this.hydrationPromise = null;
|
|
79
|
+
this.hydrationPromiseSettled = false;
|
|
79
80
|
this.config = {};
|
|
80
81
|
this.systemEventHandlers = /* @__PURE__ */ new Map();
|
|
81
82
|
this.name = this.constructor.name;
|
|
@@ -117,6 +118,22 @@ var StateContainer = class StateContainer {
|
|
|
117
118
|
get isDisposed() {
|
|
118
119
|
return this._disposed;
|
|
119
120
|
}
|
|
121
|
+
/** Current hydration status for this instance */
|
|
122
|
+
get hydrationStatus() {
|
|
123
|
+
return this._hydrationStatus;
|
|
124
|
+
}
|
|
125
|
+
/** Error from the latest failed hydration, if any */
|
|
126
|
+
get hydrationError() {
|
|
127
|
+
return this._hydrationError;
|
|
128
|
+
}
|
|
129
|
+
/** Whether this instance has completed hydration successfully */
|
|
130
|
+
get isHydrated() {
|
|
131
|
+
return this._hydrationStatus === "hydrated";
|
|
132
|
+
}
|
|
133
|
+
/** Whether user state changes happened while hydration was pending */
|
|
134
|
+
get changedWhileHydrating() {
|
|
135
|
+
return this._changedWhileHydrating;
|
|
136
|
+
}
|
|
120
137
|
/**
|
|
121
138
|
* Subscribe to state changes
|
|
122
139
|
* @param listener - Function called when state changes
|
|
@@ -135,6 +152,7 @@ var StateContainer = class StateContainer {
|
|
|
135
152
|
if (this._disposed) return;
|
|
136
153
|
if (this.debug) console.log(`[${this.name}] Disposing...`);
|
|
137
154
|
this._disposed = true;
|
|
155
|
+
if (this._hydrationStatus === "hydrating") this.failHydration(/* @__PURE__ */ new Error(`Hydration cancelled because ${this.name} was disposed`));
|
|
138
156
|
this.emitSystemEvent("dispose", void 0);
|
|
139
157
|
this.listeners.clear();
|
|
140
158
|
this.systemEventHandlers.clear();
|
|
@@ -147,22 +165,60 @@ var StateContainer = class StateContainer {
|
|
|
147
165
|
* @protected
|
|
148
166
|
*/
|
|
149
167
|
emit(newState) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
168
|
+
this.applyState(newState, "default");
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Mark this instance as hydrating. Plugins should call this before starting
|
|
172
|
+
* asynchronous rehydration work.
|
|
173
|
+
*/
|
|
174
|
+
beginHydration() {
|
|
175
|
+
if (this._disposed) throw new Error(`Cannot begin hydration for disposed container ${this.name}`);
|
|
176
|
+
this._changedWhileHydrating = false;
|
|
177
|
+
this._hydrationError = void 0;
|
|
178
|
+
this.hydrationPromise = null;
|
|
179
|
+
this.resolveHydrationPromise = void 0;
|
|
180
|
+
this.rejectHydrationPromise = void 0;
|
|
181
|
+
this.hydrationPromiseSettled = false;
|
|
182
|
+
this.ensureHydrationPromise();
|
|
183
|
+
this.setHydrationStatus("hydrating");
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Apply hydrated state if no user writes happened since hydration started.
|
|
187
|
+
* Returns false when hydration should be skipped.
|
|
188
|
+
*/
|
|
189
|
+
applyHydratedState(newState) {
|
|
190
|
+
if (this._disposed) return false;
|
|
191
|
+
if (this._hydrationStatus !== "hydrating" || this._changedWhileHydrating) return false;
|
|
192
|
+
this.applyState(newState, "hydration");
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Mark hydration as completed.
|
|
197
|
+
*/
|
|
198
|
+
finishHydration() {
|
|
199
|
+
if (this._hydrationStatus !== "hydrating") {
|
|
200
|
+
if (this._hydrationStatus === "hydrated") return;
|
|
201
|
+
if (this._hydrationStatus === "error") this.ensureHydrationPromise();
|
|
162
202
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
203
|
+
this.setHydrationStatus("hydrated");
|
|
204
|
+
this.resolveHydration();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Mark hydration as failed.
|
|
208
|
+
*/
|
|
209
|
+
failHydration(error) {
|
|
210
|
+
const err = error instanceof Error ? error : /* @__PURE__ */ new Error(`Hydration failed: ${error}`);
|
|
211
|
+
this._hydrationError = err;
|
|
212
|
+
this.setHydrationStatus("error", err);
|
|
213
|
+
this.rejectHydration(err);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Wait until the current hydration cycle finishes.
|
|
217
|
+
*/
|
|
218
|
+
waitForHydration() {
|
|
219
|
+
if (this._hydrationStatus === "idle" || this._hydrationStatus === "hydrated") return Promise.resolve();
|
|
220
|
+
if (this._hydrationStatus === "error") return Promise.reject(this._hydrationError ?? /* @__PURE__ */ new Error(`Hydration failed for container ${this.name}`));
|
|
221
|
+
return this.ensureHydrationPromise();
|
|
166
222
|
}
|
|
167
223
|
captureStackTrace() {
|
|
168
224
|
if (!StateContainer.enableStackTrace || typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
|
|
@@ -211,6 +267,63 @@ var StateContainer = class StateContainer {
|
|
|
211
267
|
if (this._disposed) throw new Error(`Cannot update state from disposed container ${this.name}`);
|
|
212
268
|
this.emit(updater(this._state));
|
|
213
269
|
}
|
|
270
|
+
applyState(newState, source) {
|
|
271
|
+
if (this._disposed) throw new Error(`Cannot emit state from disposed container ${this.name}`);
|
|
272
|
+
if (this._state === newState) return;
|
|
273
|
+
if (this._hydrationStatus === "hydrating" && source !== "hydration") this._changedWhileHydrating = true;
|
|
274
|
+
const previousState = this._state;
|
|
275
|
+
this._state = newState;
|
|
276
|
+
this.emitSystemEvent("stateChanged", {
|
|
277
|
+
state: newState,
|
|
278
|
+
previousState
|
|
279
|
+
});
|
|
280
|
+
const listeners = Array.from(this.listeners);
|
|
281
|
+
for (const listener of listeners) try {
|
|
282
|
+
listener(newState);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error(`[${this.name}] Error in listener:`, error);
|
|
285
|
+
}
|
|
286
|
+
const stackTrace = this.captureStackTrace();
|
|
287
|
+
globalRegistry.emit("stateChanged", this, previousState, newState, stackTrace);
|
|
288
|
+
this.lastUpdateTimestamp = Date.now();
|
|
289
|
+
}
|
|
290
|
+
setHydrationStatus(status, error) {
|
|
291
|
+
const previousStatus = this._hydrationStatus;
|
|
292
|
+
this._hydrationStatus = status;
|
|
293
|
+
this._hydrationError = error;
|
|
294
|
+
this.emitSystemEvent("hydrationChanged", {
|
|
295
|
+
status,
|
|
296
|
+
previousStatus,
|
|
297
|
+
error,
|
|
298
|
+
changedWhileHydrating: this._changedWhileHydrating
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
ensureHydrationPromise() {
|
|
302
|
+
if (!this.hydrationPromise || this.hydrationPromiseSettled) {
|
|
303
|
+
this.hydrationPromiseSettled = false;
|
|
304
|
+
this.hydrationPromise = new Promise((resolve, reject) => {
|
|
305
|
+
this.resolveHydrationPromise = () => {
|
|
306
|
+
if (this.hydrationPromiseSettled) return;
|
|
307
|
+
this.hydrationPromiseSettled = true;
|
|
308
|
+
resolve();
|
|
309
|
+
};
|
|
310
|
+
this.rejectHydrationPromise = (error) => {
|
|
311
|
+
if (this.hydrationPromiseSettled) return;
|
|
312
|
+
this.hydrationPromiseSettled = true;
|
|
313
|
+
reject(error);
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
this.hydrationPromise.catch(() => {});
|
|
317
|
+
}
|
|
318
|
+
return this.hydrationPromise;
|
|
319
|
+
}
|
|
320
|
+
resolveHydration() {
|
|
321
|
+
this.resolveHydrationPromise?.();
|
|
322
|
+
}
|
|
323
|
+
rejectHydration(error) {
|
|
324
|
+
this.ensureHydrationPromise();
|
|
325
|
+
this.rejectHydrationPromise?.(error);
|
|
326
|
+
}
|
|
214
327
|
emitSystemEvent(event, payload) {
|
|
215
328
|
const handlers = this.systemEventHandlers.get(event);
|
|
216
329
|
if (!handlers) return;
|
|
@@ -221,7 +334,6 @@ var StateContainer = class StateContainer {
|
|
|
221
334
|
}
|
|
222
335
|
}
|
|
223
336
|
};
|
|
224
|
-
|
|
225
337
|
//#endregion
|
|
226
338
|
//#region src/core/Cubit.ts
|
|
227
339
|
/**
|
|
@@ -235,8 +347,14 @@ var Cubit = class extends StateContainer {
|
|
|
235
347
|
super(initialState);
|
|
236
348
|
this.patch = ((partial) => {
|
|
237
349
|
if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
|
|
238
|
-
|
|
239
|
-
|
|
350
|
+
const current = this.state;
|
|
351
|
+
let hasChanges = false;
|
|
352
|
+
for (const key in partial) if (!Object.is(current[key], partial[key])) {
|
|
353
|
+
hasChanges = true;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
if (hasChanges) this.update((c) => ({
|
|
357
|
+
...c,
|
|
240
358
|
...partial
|
|
241
359
|
}));
|
|
242
360
|
});
|
|
@@ -256,7 +374,6 @@ var Cubit = class extends StateContainer {
|
|
|
256
374
|
super["update"](updater);
|
|
257
375
|
}
|
|
258
376
|
};
|
|
259
|
-
|
|
260
377
|
//#endregion
|
|
261
378
|
//#region src/registry/acquire.ts
|
|
262
379
|
function acquire(BlocClass, instanceKey) {
|
|
@@ -265,7 +382,6 @@ function acquire(BlocClass, instanceKey) {
|
|
|
265
382
|
countRef: true
|
|
266
383
|
});
|
|
267
384
|
}
|
|
268
|
-
|
|
269
385
|
//#endregion
|
|
270
386
|
//#region src/registry/borrow.ts
|
|
271
387
|
function borrow(BlocClass, instanceKey) {
|
|
@@ -274,13 +390,11 @@ function borrow(BlocClass, instanceKey) {
|
|
|
274
390
|
function borrowSafe(BlocClass, instanceKey) {
|
|
275
391
|
return globalRegistry.borrowSafe(BlocClass, instanceKey);
|
|
276
392
|
}
|
|
277
|
-
|
|
278
393
|
//#endregion
|
|
279
394
|
//#region src/registry/release.ts
|
|
280
395
|
function release(BlocClass, instanceKey, forceDispose = false) {
|
|
281
396
|
globalRegistry.release(BlocClass, instanceKey, forceDispose);
|
|
282
397
|
}
|
|
283
|
-
|
|
284
398
|
//#endregion
|
|
285
399
|
//#region src/decorators/blac.ts
|
|
286
400
|
/**
|
|
@@ -313,7 +427,7 @@ function blac(options) {
|
|
|
313
427
|
return target;
|
|
314
428
|
};
|
|
315
429
|
}
|
|
316
|
-
|
|
317
430
|
//#endregion
|
|
318
|
-
export { Cubit,
|
|
431
|
+
export { Cubit, PluginManager, StateContainer, TrackedContext, acquire, blac, borrow, borrowSafe, clear, clearAll, createTrackedContext, ensure, forEach, generateIsolatedKey, getAll, getPluginManager, getRefCount, getRegistry, getStats, globalRegistry, hasInstance, instance, instanceId, isIsolatedClass, register, release, setRegistry, tracked, watch };
|
|
432
|
+
|
|
319
433
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/utils/idGenerator.ts","../src/core/StateContainer.ts","../src/core/Cubit.ts","../src/registry/acquire.ts","../src/registry/borrow.ts","../src/registry/release.ts","../src/decorators/blac.ts"],"sourcesContent":["/**\n * Centralized ID Generation\n *\n * Provides consistent, collision-resistant ID generation for all BlaC subsystems.\n * Uses timestamp + counter + random suffix for uniqueness.\n */\n\n// Global counter map for ID generation\nconst globalCounters = new Map<string, number>();\n\n/**\n * Creates an ID generator with isolated counter state\n *\n * @param prefix - Prefix for generated IDs\n * @returns Object with next(), nextSimple(), and reset() methods\n *\n * @example\n * ```ts\n * const generator = createIdGenerator('sub');\n * const id1 = generator.next(); // \"sub:1698765432100_1_a3k9d7f2q\"\n * const id2 = generator.next(); // \"sub:1698765432101_2_b4n8e9g3r\"\n * ```\n */\nexport function createIdGenerator(prefix: string) {\n let counter = 0;\n return {\n next: () =>\n `${prefix}:${Date.now()}_${++counter}_${Math.random().toString(36).substring(2, 11)}`,\n nextSimple: () => `${prefix}:${++counter}`,\n reset: () => {\n counter = 0;\n },\n };\n}\n\n/**\n * Generate ID with timestamp, counter, and random suffix (tree-shakeable)\n *\n * Format: `${prefix}:${timestamp}_${counter}_${random}`\n *\n * @param prefix - Prefix for the ID (e.g., 'sub', 'consumer', 'stage')\n * @returns Branded ID string\n *\n * @example\n * const id = generateId('sub');\n * // Returns: \"sub:1698765432100_1_a3k9d7f2q\"\n */\nexport function generateId(prefix: string): string {\n const counter = (globalCounters.get(prefix) ?? 0) + 1;\n globalCounters.set(prefix, counter);\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 11);\n return `${prefix}:${timestamp}_${counter}_${random}`;\n}\n\n/**\n * Generate simple ID with timestamp and random (no counter tracking)\n *\n * Format: `${prefix}:${timestamp}_${random}`\n *\n * @param prefix - Prefix for the ID\n * @returns Branded ID string\n *\n * @example\n * ```ts\n * const id = generateSimpleId('CounterBloc');\n * // Returns: \"CounterBloc:1698765432100_a3k9d7f2q\"\n * ```\n */\nexport function generateSimpleId(prefix: string, affix?: string): string {\n if (affix) {\n return `${prefix}:${affix}`;\n }\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 11);\n return `${prefix}:${timestamp}_${random}`;\n}\n\n/**\n * Reset all counters for testing purposes\n * @internal\n */\nexport function __resetIdCounters(): void {\n globalCounters.clear();\n}\n\n/**\n * Generate a unique isolated instance key\n * Uses base36 encoding for compact, URL-safe identifiers\n *\n * Format: \"isolated-{9-char-random-string}\"\n * Example: \"isolated-k7x2m9p4q\"\n *\n * @returns A unique isolated instance key\n */\nexport function generateIsolatedKey(): string {\n const randomPart = Math.random().toString(36).slice(2, 11);\n return `isolated-${randomPart}`;\n}\n\n/**\n * Check if a key is an isolated instance key\n * @param key - The instance key to check\n * @returns true if the key is an isolated instance key\n */\nexport function isIsolatedKey(key: string): boolean {\n return key.startsWith('isolated-');\n}\n","import { generateSimpleId } from '../utils/idGenerator';\nimport { BLAC_DEFAULTS } from '../constants';\nimport { globalRegistry } from './StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\n/**\n * Configuration options for initializing a StateContainer instance\n */\nexport interface StateContainerConfig {\n /** Display name for the instance (defaults to class name) */\n name?: string;\n /** Enable debug logging for this instance */\n debug?: boolean;\n /** Custom instance identifier */\n instanceId?: string;\n}\n\n/**\n * Listener function for state changes\n * @internal\n */\ntype StateListener<S> = (state: S) => void;\n\n/**\n * System events emitted by StateContainer lifecycle\n */\nexport type SystemEvent = 'stateChanged' | 'dispose';\n\n/**\n * Payload types for each system event\n * @template S - State type\n */\nexport interface SystemEventPayloads<S> {\n /** Emitted when state changes via emit() or update() */\n stateChanged: { state: S; previousState: S };\n /** Emitted when the instance is disposed */\n dispose: void;\n}\n\n/**\n * Handler function for system events\n * @internal\n */\ntype SystemEventHandler<S, E extends SystemEvent> = (\n payload: SystemEventPayloads<S>[E],\n) => void;\n\nconst EMPTY_DEPS: ReadonlyMap<any, any> = new Map();\n\n/**\n * Abstract base class for all state containers in BlaC.\n * Provides lifecycle management, subscription handling, ref counting,\n * and integration with the global registry.\n *\n * @template S - State type managed by this container\n *\n * @example\n * ```ts\n * class CounterBloc extends StateContainer<{ count: number }> {\n * constructor() {\n * super({ count: 0 });\n * }\n * increment() {\n * this.emit({ count: this.state.count + 1 });\n * }\n * }\n * ```\n */\nexport abstract class StateContainer<S extends object = any> {\n static __excludeFromDevTools = false;\n static enableStackTrace = true;\n\n private _state: S;\n private readonly listeners = new Set<StateListener<S>>();\n private _disposed = false;\n private config: StateContainerConfig = {};\n private readonly systemEventHandlers = new Map<\n SystemEvent,\n Set<SystemEventHandler<S, any>>\n >();\n\n /** Display name for this instance */\n name: string = this.constructor.name;\n /** Whether debug logging is enabled */\n debug: boolean = false;\n /** Unique identifier for this instance */\n instanceId: string = generateSimpleId(this.constructor.name, 'main');\n /** Timestamp when this instance was created */\n createdAt: number = Date.now();\n\n private _dependencies: Map<StateContainerConstructor, string> | null = null;\n\n get dependencies(): ReadonlyMap<StateContainerConstructor, string> {\n return this._dependencies ?? EMPTY_DEPS;\n }\n\n protected depend<T extends StateContainerConstructor>(\n Type: T,\n instanceKey?: string,\n ): () => InstanceType<T> {\n if (!this._dependencies) {\n this._dependencies = new Map();\n }\n this._dependencies.set(\n Type,\n instanceKey ?? BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY,\n );\n return () => globalRegistry.ensure(Type, instanceKey);\n }\n\n constructor(initialState: S) {\n this._state = initialState;\n }\n\n /**\n * Initialize configuration for this instance.\n * Called by the registry after construction.\n * @param config - Configuration options\n */\n initConfig(config: StateContainerConfig): void {\n this.config = { ...config };\n this.name = this.config.name || this.constructor.name;\n this.debug = this.config.debug ?? false;\n this.instanceId = generateSimpleId(\n this.constructor.name,\n this.config.instanceId,\n );\n\n globalRegistry.emit('created', this);\n }\n\n /** Current state value */\n get state(): Readonly<S> {\n return this._state;\n }\n\n /** Whether this instance has been disposed */\n get isDisposed(): boolean {\n return this._disposed;\n }\n\n /**\n * Subscribe to state changes\n * @param listener - Function called when state changes\n * @returns Unsubscribe function\n */\n subscribe(listener: StateListener<S>): () => void {\n if (this._disposed) {\n throw new Error(`Cannot subscribe to disposed container ${this.name}`);\n }\n\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Dispose this instance and clean up resources.\n * Clears all listeners and emits the 'dispose' system event.\n */\n dispose(): void {\n if (this._disposed) return;\n\n if (this.debug) {\n console.log(`[${this.name}] Disposing...`);\n }\n\n this._disposed = true;\n\n this.emitSystemEvent('dispose', undefined as void);\n\n this.listeners.clear();\n this.systemEventHandlers.clear();\n\n globalRegistry.emit('disposed', this);\n\n if (this.debug) {\n console.log(`[${this.name}] Disposed successfully`);\n }\n }\n\n /**\n * Emit a new state value and notify all listeners.\n * @param newState - The new state value\n * @protected\n */\n protected emit(newState: S): void {\n if (this._disposed) {\n throw new Error(`Cannot emit state from disposed container ${this.name}`);\n }\n\n if (this._state === newState) return;\n\n const previousState = this._state;\n this._state = newState;\n\n this.emitSystemEvent('stateChanged', {\n state: newState,\n previousState,\n });\n\n for (const listener of this.listeners) {\n try {\n listener(newState);\n } catch (error) {\n console.error(`[${this.name}] Error in listener:`, error);\n }\n }\n\n const stackTrace = this.captureStackTrace();\n\n globalRegistry.emit(\n 'stateChanged',\n this,\n previousState,\n newState,\n stackTrace,\n );\n this.lastUpdateTimestamp = Date.now();\n }\n\n private captureStackTrace(): string {\n if (\n !StateContainer.enableStackTrace ||\n (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production')\n ) {\n return '';\n }\n\n try {\n const error = new Error();\n const stack = error.stack || '';\n const lines = stack.split('\\n');\n const relevantLines = lines.slice(1);\n const formattedLines: string[] = [];\n\n for (const line of relevantLines) {\n if (!line.trim()) continue;\n\n if (\n line.includes('StateContainer.emit') ||\n line.includes('StateContainer.update') ||\n line.includes('StateContainer.captureStackTrace') ||\n line.includes('Cubit.patch')\n ) {\n continue;\n }\n\n if (\n line.includes('node_modules') ||\n line.includes('react-dom') ||\n line.includes('react_jsx') ||\n line.includes('.vite/deps') ||\n line.includes('executeDispatch') ||\n line.includes('runWithFiber') ||\n line.includes('invokeGuarded') ||\n line.includes('callCallback') ||\n line.includes('processDispatchQueue') ||\n line.includes('dispatchEvent') ||\n line.includes('batchedUpdates')\n ) {\n continue;\n }\n\n const formatted = this.formatStackLine(line);\n if (formatted) {\n formattedLines.push(formatted);\n }\n }\n\n return formattedLines.join('\\n');\n } catch {\n return '';\n }\n }\n\n private formatStackLine(line: string): string | null {\n const match = line.match(/at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)/);\n if (!match) {\n const simpleMatch = line.match(/at\\s+(.+?):(\\d+):(\\d+)/);\n if (simpleMatch) {\n const [, url, lineNum, col] = simpleMatch;\n const cleanPath = this.cleanFilePath(url);\n return ` at ${cleanPath}:${lineNum}:${col}`;\n }\n return null;\n }\n\n const [, functionName, url, lineNum, col] = match;\n const cleanPath = this.cleanFilePath(url);\n\n return ` at ${functionName} (${cleanPath}:${lineNum}:${col})`;\n }\n\n private cleanFilePath(url: string): string {\n let path = url\n .replace(/http:\\/\\/localhost:\\d+\\/@fs/, '')\n .replace(/http:\\/\\/localhost:\\d+\\//, '')\n .replace(/\\?t=\\d+/, '')\n .replace(/\\?v=[a-f0-9]+/, '');\n\n const projectMatch = path.match(/\\/Projects\\/blac\\/(.+)/);\n if (projectMatch) {\n path = projectMatch[1];\n }\n\n const segments = path.split('/');\n if (segments.length > 3) {\n path = segments.slice(-3).join('/');\n }\n\n return path;\n }\n\n /** Timestamp of the last state update */\n lastUpdateTimestamp: number = Date.now();\n\n /**\n * Update state using a transform function.\n * @param updater - Function that receives current state and returns new state\n * @protected\n */\n protected update(updater: (current: S) => S): void {\n if (this._disposed) {\n throw new Error(\n `Cannot update state from disposed container ${this.name}`,\n );\n }\n this.emit(updater(this._state));\n }\n\n /**\n * Subscribe to system lifecycle events.\n * @param event - The event type to listen for\n * @param handler - Handler function called when event occurs\n * @returns Unsubscribe function\n * @protected\n */\n protected onSystemEvent = <E extends SystemEvent>(\n event: E,\n handler: SystemEventHandler<S, E>,\n ): (() => void) => {\n let handlers = this.systemEventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.systemEventHandlers.set(event, handlers);\n }\n handlers.add(handler as SystemEventHandler<S, any>);\n\n return () => {\n handlers?.delete(handler as SystemEventHandler<S, any>);\n };\n };\n\n private emitSystemEvent<E extends SystemEvent>(\n event: E,\n payload: SystemEventPayloads<S>[E],\n ): void {\n const handlers = this.systemEventHandlers.get(event);\n if (!handlers) return;\n\n for (const handler of handlers) {\n try {\n handler(payload);\n } catch (error) {\n console.error(`[${this.name}] Error in system event handler:`, error);\n }\n }\n }\n}\n","import { StateContainer } from './StateContainer';\n\n/**\n * Simple state container with direct state emission.\n * Extends StateContainer with public methods for emitting and updating state.\n *\n * @template S - State type\n */\nexport abstract class Cubit<S extends object = any> extends StateContainer<S> {\n constructor(initialState: S) {\n super(initialState);\n }\n\n /**\n * Replace state with a new value and notify all listeners\n * @param newState - The new state value\n */\n public emit(newState: S): void {\n super['emit'](newState);\n }\n\n /**\n * Transform current state using an updater function and emit the new state\n * @param updater - Function that receives current state and returns new state\n */\n public update(updater: (current: S) => S): void {\n super['update'](updater);\n }\n\n /**\n * Merge partial state changes into current state (only for object states)\n */\n public patch = ((partial: S extends object ? Partial<S> : never): void => {\n if (typeof this.state !== 'object' || this.state === null) {\n throw new Error('patch() is only available for object state types');\n }\n this.update((current) => ({ ...current, ...partial }) as S);\n }) as S extends object ? (partial: Partial<S>) => void : never;\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function acquire<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): InstanceType<T> {\n return globalRegistry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n });\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function borrow<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): InstanceType<T> {\n return globalRegistry.borrow(BlocClass, instanceKey);\n}\n\nexport function borrowSafe<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n):\n | { error: Error; instance: null }\n | { error: null; instance: InstanceType<T> } {\n return globalRegistry.borrowSafe(BlocClass, instanceKey);\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function release<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n forceDispose = false,\n): void {\n globalRegistry.release(BlocClass, instanceKey, forceDispose);\n}\n","import { BLAC_STATIC_PROPS } from '../constants';\n\n/**\n * Configuration options for the @blac decorator.\n * Only one option can be specified at a time (union type).\n */\nexport type BlacOptions =\n /** Mark bloc as isolated (each component gets its own instance) */\n | { isolated: true }\n /** Mark bloc to never be auto-disposed when ref count reaches 0 */\n | { keepAlive: true }\n /** Exclude bloc from DevTools tracking (prevents infinite loops) */\n | { excludeFromDevTools: true };\n\n/**\n * Decorator to configure StateContainer classes.\n *\n * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)\n * ```ts\n * @blac({ isolated: true })\n * class FormBloc extends Cubit<FormState> {}\n *\n * @blac({ keepAlive: true })\n * class AuthBloc extends Cubit<AuthState> {}\n *\n * @blac({ excludeFromDevTools: true })\n * class InternalBloc extends Cubit<InternalState> {}\n * ```\n *\n * @example Function syntax (no decorator support needed)\n * ```ts\n * const FormBloc = blac({ isolated: true })(\n * class extends Cubit<FormState> {}\n * );\n * ```\n */\nexport function blac(options: BlacOptions) {\n return function <T extends new (...args: any[]) => any>(\n target: T,\n _context?: ClassDecoratorContext,\n ): T {\n if ('isolated' in options && options.isolated) {\n (target as any)[BLAC_STATIC_PROPS.ISOLATED] = true;\n }\n if ('keepAlive' in options && options.keepAlive) {\n (target as any)[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;\n }\n if ('excludeFromDevTools' in options && options.excludeFromDevTools) {\n (target as any)[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;\n }\n return target;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/utils/idGenerator.ts","../src/core/StateContainer.ts","../src/core/Cubit.ts","../src/registry/acquire.ts","../src/registry/borrow.ts","../src/registry/release.ts","../src/decorators/blac.ts"],"sourcesContent":["/**\n * Centralized ID Generation\n *\n * Provides consistent, collision-resistant ID generation for all BlaC subsystems.\n * Uses timestamp + counter + random suffix for uniqueness.\n */\n\n// Global counter map for ID generation\nconst globalCounters = new Map<string, number>();\n\n/**\n * Creates an ID generator with isolated counter state\n *\n * @param prefix - Prefix for generated IDs\n * @returns Object with next(), nextSimple(), and reset() methods\n *\n * @example\n * ```ts\n * const generator = createIdGenerator('sub');\n * const id1 = generator.next(); // \"sub:1698765432100_1_a3k9d7f2q\"\n * const id2 = generator.next(); // \"sub:1698765432101_2_b4n8e9g3r\"\n * ```\n */\nexport function createIdGenerator(prefix: string) {\n let counter = 0;\n return {\n next: () =>\n `${prefix}:${Date.now()}_${++counter}_${Math.random().toString(36).substring(2, 11)}`,\n nextSimple: () => `${prefix}:${++counter}`,\n reset: () => {\n counter = 0;\n },\n };\n}\n\n/**\n * Generate ID with timestamp, counter, and random suffix (tree-shakeable)\n *\n * Format: `${prefix}:${timestamp}_${counter}_${random}`\n *\n * @param prefix - Prefix for the ID (e.g., 'sub', 'consumer', 'stage')\n * @returns Branded ID string\n *\n * @example\n * const id = generateId('sub');\n * // Returns: \"sub:1698765432100_1_a3k9d7f2q\"\n */\nexport function generateId(prefix: string): string {\n const counter = (globalCounters.get(prefix) ?? 0) + 1;\n globalCounters.set(prefix, counter);\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 11);\n return `${prefix}:${timestamp}_${counter}_${random}`;\n}\n\n/**\n * Generate simple ID with timestamp and random (no counter tracking)\n *\n * Format: `${prefix}:${timestamp}_${random}`\n *\n * @param prefix - Prefix for the ID\n * @returns Branded ID string\n *\n * @example\n * ```ts\n * const id = generateSimpleId('CounterBloc');\n * // Returns: \"CounterBloc:1698765432100_a3k9d7f2q\"\n * ```\n */\nexport function generateSimpleId(prefix: string, affix?: string): string {\n if (affix) {\n return `${prefix}:${affix}`;\n }\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 11);\n return `${prefix}:${timestamp}_${random}`;\n}\n\n/**\n * Reset all counters for testing purposes\n * @internal\n */\nexport function __resetIdCounters(): void {\n globalCounters.clear();\n}\n\n/**\n * Generate a unique isolated instance key\n * Uses base36 encoding for compact, URL-safe identifiers\n *\n * Format: \"isolated-{9-char-random-string}\"\n * Example: \"isolated-k7x2m9p4q\"\n *\n * @returns A unique isolated instance key\n */\nexport function generateIsolatedKey(): string {\n const randomPart = Math.random().toString(36).slice(2, 11);\n return `isolated-${randomPart}`;\n}\n\n/**\n * Check if a key is an isolated instance key\n * @param key - The instance key to check\n * @returns true if the key is an isolated instance key\n */\nexport function isIsolatedKey(key: string): boolean {\n return key.startsWith('isolated-');\n}\n","import { generateSimpleId } from '../utils/idGenerator';\nimport { BLAC_DEFAULTS } from '../constants';\nimport { globalRegistry } from './StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\n/**\n * Configuration options for initializing a StateContainer instance\n */\nexport interface StateContainerConfig {\n /** Display name for the instance (defaults to class name) */\n name?: string;\n /** Enable debug logging for this instance */\n debug?: boolean;\n /** Custom instance identifier */\n instanceId?: string;\n}\n\nexport type HydrationStatus = 'idle' | 'hydrating' | 'hydrated' | 'error';\n\n/**\n * Listener function for state changes\n * @internal\n */\ntype StateListener<S> = (state: S) => void;\n\n/**\n * System events emitted by StateContainer lifecycle\n */\nexport type SystemEvent = 'stateChanged' | 'dispose' | 'hydrationChanged';\n\n/**\n * Payload types for each system event\n * @template S - State type\n */\nexport interface SystemEventPayloads<S> {\n /** Emitted when state changes via emit() or update() */\n stateChanged: { state: S; previousState: S };\n /** Emitted when the instance is disposed */\n dispose: void;\n /** Emitted when hydration status changes */\n hydrationChanged: {\n status: HydrationStatus;\n previousStatus: HydrationStatus;\n error?: Error;\n changedWhileHydrating: boolean;\n };\n}\n\n/**\n * Handler function for system events\n * @internal\n */\ntype SystemEventHandler<S, E extends SystemEvent> = (\n payload: SystemEventPayloads<S>[E],\n) => void;\n\nconst EMPTY_DEPS: ReadonlyMap<any, any> = new Map();\n\n/**\n * Abstract base class for all state containers in BlaC.\n * Provides lifecycle management, subscription handling, ref counting,\n * and integration with the global registry.\n *\n * @template S - State type managed by this container\n *\n * @example\n * ```ts\n * class CounterBloc extends StateContainer<{ count: number }> {\n * constructor() {\n * super({ count: 0 });\n * }\n * increment() {\n * this.emit({ count: this.state.count + 1 });\n * }\n * }\n * ```\n */\nexport abstract class StateContainer<S extends object = any> {\n static __excludeFromDevTools = false;\n static enableStackTrace = true;\n\n private _state: S;\n private readonly listeners = new Set<StateListener<S>>();\n private _disposed = false;\n private _hydrationStatus: HydrationStatus = 'idle';\n private _hydrationError?: Error;\n private _changedWhileHydrating = false;\n private hydrationPromise: Promise<void> | null = null;\n private resolveHydrationPromise?: () => void;\n private rejectHydrationPromise?: (error: Error) => void;\n private hydrationPromiseSettled = false;\n private config: StateContainerConfig = {};\n private readonly systemEventHandlers = new Map<\n SystemEvent,\n Set<SystemEventHandler<S, any>>\n >();\n\n /** Display name for this instance */\n name: string = this.constructor.name;\n /** Whether debug logging is enabled */\n debug: boolean = false;\n /** Unique identifier for this instance */\n instanceId: string = generateSimpleId(this.constructor.name, 'main');\n /** Timestamp when this instance was created */\n createdAt: number = Date.now();\n\n private _dependencies: Map<StateContainerConstructor, string> | null = null;\n\n get dependencies(): ReadonlyMap<StateContainerConstructor, string> {\n return this._dependencies ?? EMPTY_DEPS;\n }\n\n protected depend<T extends StateContainerConstructor>(\n Type: T,\n instanceKey?: string,\n ): () => InstanceType<T> {\n if (!this._dependencies) {\n this._dependencies = new Map();\n }\n this._dependencies.set(\n Type,\n instanceKey ?? BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY,\n );\n return () => globalRegistry.ensure(Type, instanceKey);\n }\n\n constructor(initialState: S) {\n this._state = initialState;\n }\n\n /**\n * Initialize configuration for this instance.\n * Called by the registry after construction.\n * @param config - Configuration options\n */\n initConfig(config: StateContainerConfig): void {\n this.config = { ...config };\n this.name = this.config.name || this.constructor.name;\n this.debug = this.config.debug ?? false;\n this.instanceId = generateSimpleId(\n this.constructor.name,\n this.config.instanceId,\n );\n\n globalRegistry.emit('created', this);\n }\n\n /** Current state value */\n get state(): Readonly<S> {\n return this._state;\n }\n\n /** Whether this instance has been disposed */\n get isDisposed(): boolean {\n return this._disposed;\n }\n\n /** Current hydration status for this instance */\n get hydrationStatus(): HydrationStatus {\n return this._hydrationStatus;\n }\n\n /** Error from the latest failed hydration, if any */\n get hydrationError(): Error | undefined {\n return this._hydrationError;\n }\n\n /** Whether this instance has completed hydration successfully */\n get isHydrated(): boolean {\n return this._hydrationStatus === 'hydrated';\n }\n\n /** Whether user state changes happened while hydration was pending */\n get changedWhileHydrating(): boolean {\n return this._changedWhileHydrating;\n }\n\n /**\n * Subscribe to state changes\n * @param listener - Function called when state changes\n * @returns Unsubscribe function\n */\n subscribe(listener: StateListener<S>): () => void {\n if (this._disposed) {\n throw new Error(`Cannot subscribe to disposed container ${this.name}`);\n }\n\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Dispose this instance and clean up resources.\n * Clears all listeners and emits the 'dispose' system event.\n */\n dispose(): void {\n if (this._disposed) return;\n\n if (this.debug) {\n console.log(`[${this.name}] Disposing...`);\n }\n\n this._disposed = true;\n\n if (this._hydrationStatus === 'hydrating') {\n this.failHydration(\n new Error(`Hydration cancelled because ${this.name} was disposed`),\n );\n }\n\n this.emitSystemEvent('dispose', undefined as void);\n\n this.listeners.clear();\n this.systemEventHandlers.clear();\n\n globalRegistry.emit('disposed', this);\n\n if (this.debug) {\n console.log(`[${this.name}] Disposed successfully`);\n }\n }\n\n /**\n * Emit a new state value and notify all listeners.\n * @param newState - The new state value\n * @protected\n */\n protected emit(newState: S): void {\n this.applyState(newState, 'default');\n }\n\n /**\n * Mark this instance as hydrating. Plugins should call this before starting\n * asynchronous rehydration work.\n */\n beginHydration(): void {\n if (this._disposed) {\n throw new Error(\n `Cannot begin hydration for disposed container ${this.name}`,\n );\n }\n\n this._changedWhileHydrating = false;\n this._hydrationError = undefined;\n this.hydrationPromise = null;\n this.resolveHydrationPromise = undefined;\n this.rejectHydrationPromise = undefined;\n this.hydrationPromiseSettled = false;\n this.ensureHydrationPromise();\n this.setHydrationStatus('hydrating');\n }\n\n /**\n * Apply hydrated state if no user writes happened since hydration started.\n * Returns false when hydration should be skipped.\n */\n applyHydratedState(newState: S): boolean {\n if (this._disposed) {\n return false;\n }\n\n if (this._hydrationStatus !== 'hydrating' || this._changedWhileHydrating) {\n return false;\n }\n\n this.applyState(newState, 'hydration');\n return true;\n }\n\n /**\n * Mark hydration as completed.\n */\n finishHydration(): void {\n if (this._hydrationStatus !== 'hydrating') {\n if (this._hydrationStatus === 'hydrated') {\n return;\n }\n if (this._hydrationStatus === 'error') {\n this.ensureHydrationPromise();\n }\n }\n\n this.setHydrationStatus('hydrated');\n this.resolveHydration();\n }\n\n /**\n * Mark hydration as failed.\n */\n failHydration(error: Error): void {\n const err =\n error instanceof Error ? error : new Error(`Hydration failed: ${error}`);\n\n this._hydrationError = err;\n this.setHydrationStatus('error', err);\n this.rejectHydration(err);\n }\n\n /**\n * Wait until the current hydration cycle finishes.\n */\n waitForHydration(): Promise<void> {\n if (this._hydrationStatus === 'idle' || this._hydrationStatus === 'hydrated') {\n return Promise.resolve();\n }\n\n if (this._hydrationStatus === 'error') {\n return Promise.reject(\n this._hydrationError ??\n new Error(`Hydration failed for container ${this.name}`),\n );\n }\n\n return this.ensureHydrationPromise();\n }\n\n private captureStackTrace(): string {\n if (\n !StateContainer.enableStackTrace ||\n (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production')\n ) {\n return '';\n }\n\n try {\n const error = new Error();\n const stack = error.stack || '';\n const lines = stack.split('\\n');\n const relevantLines = lines.slice(1);\n const formattedLines: string[] = [];\n\n for (const line of relevantLines) {\n if (!line.trim()) continue;\n\n if (\n line.includes('StateContainer.emit') ||\n line.includes('StateContainer.update') ||\n line.includes('StateContainer.captureStackTrace') ||\n line.includes('Cubit.patch')\n ) {\n continue;\n }\n\n if (\n line.includes('node_modules') ||\n line.includes('react-dom') ||\n line.includes('react_jsx') ||\n line.includes('.vite/deps') ||\n line.includes('executeDispatch') ||\n line.includes('runWithFiber') ||\n line.includes('invokeGuarded') ||\n line.includes('callCallback') ||\n line.includes('processDispatchQueue') ||\n line.includes('dispatchEvent') ||\n line.includes('batchedUpdates')\n ) {\n continue;\n }\n\n const formatted = this.formatStackLine(line);\n if (formatted) {\n formattedLines.push(formatted);\n }\n }\n\n return formattedLines.join('\\n');\n } catch {\n return '';\n }\n }\n\n private formatStackLine(line: string): string | null {\n const match = line.match(/at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)/);\n if (!match) {\n const simpleMatch = line.match(/at\\s+(.+?):(\\d+):(\\d+)/);\n if (simpleMatch) {\n const [, url, lineNum, col] = simpleMatch;\n const cleanPath = this.cleanFilePath(url);\n return ` at ${cleanPath}:${lineNum}:${col}`;\n }\n return null;\n }\n\n const [, functionName, url, lineNum, col] = match;\n const cleanPath = this.cleanFilePath(url);\n\n return ` at ${functionName} (${cleanPath}:${lineNum}:${col})`;\n }\n\n private cleanFilePath(url: string): string {\n let path = url\n .replace(/http:\\/\\/localhost:\\d+\\/@fs/, '')\n .replace(/http:\\/\\/localhost:\\d+\\//, '')\n .replace(/\\?t=\\d+/, '')\n .replace(/\\?v=[a-f0-9]+/, '');\n\n const projectMatch = path.match(/\\/Projects\\/blac\\/(.+)/);\n if (projectMatch) {\n path = projectMatch[1];\n }\n\n const segments = path.split('/');\n if (segments.length > 3) {\n path = segments.slice(-3).join('/');\n }\n\n return path;\n }\n\n /** Timestamp of the last state update */\n lastUpdateTimestamp: number = Date.now();\n\n /**\n * Update state using a transform function.\n * @param updater - Function that receives current state and returns new state\n * @protected\n */\n protected update(updater: (current: S) => S): void {\n if (this._disposed) {\n throw new Error(\n `Cannot update state from disposed container ${this.name}`,\n );\n }\n this.emit(updater(this._state));\n }\n\n private applyState(newState: S, source: 'default' | 'hydration'): void {\n if (this._disposed) {\n throw new Error(`Cannot emit state from disposed container ${this.name}`);\n }\n\n if (this._state === newState) return;\n\n if (this._hydrationStatus === 'hydrating' && source !== 'hydration') {\n this._changedWhileHydrating = true;\n }\n\n const previousState = this._state;\n this._state = newState;\n\n this.emitSystemEvent('stateChanged', {\n state: newState,\n previousState,\n });\n\n const listeners = Array.from(this.listeners);\n for (const listener of listeners) {\n try {\n listener(newState);\n } catch (error) {\n console.error(`[${this.name}] Error in listener:`, error);\n }\n }\n\n const stackTrace = this.captureStackTrace();\n\n globalRegistry.emit(\n 'stateChanged',\n this,\n previousState,\n newState,\n stackTrace,\n );\n this.lastUpdateTimestamp = Date.now();\n }\n\n private setHydrationStatus(status: HydrationStatus, error?: Error): void {\n const previousStatus = this._hydrationStatus;\n this._hydrationStatus = status;\n this._hydrationError = error;\n\n this.emitSystemEvent('hydrationChanged', {\n status,\n previousStatus,\n error,\n changedWhileHydrating: this._changedWhileHydrating,\n });\n }\n\n private ensureHydrationPromise(): Promise<void> {\n if (!this.hydrationPromise || this.hydrationPromiseSettled) {\n this.hydrationPromiseSettled = false;\n this.hydrationPromise = new Promise<void>((resolve, reject) => {\n this.resolveHydrationPromise = () => {\n if (this.hydrationPromiseSettled) return;\n this.hydrationPromiseSettled = true;\n resolve();\n };\n this.rejectHydrationPromise = (error: Error) => {\n if (this.hydrationPromiseSettled) return;\n this.hydrationPromiseSettled = true;\n reject(error);\n };\n });\n this.hydrationPromise.catch(() => {});\n }\n\n return this.hydrationPromise;\n }\n\n private resolveHydration(): void {\n this.resolveHydrationPromise?.();\n }\n\n private rejectHydration(error: Error): void {\n this.ensureHydrationPromise();\n this.rejectHydrationPromise?.(error);\n }\n\n /**\n * Subscribe to system lifecycle events.\n * @param event - The event type to listen for\n * @param handler - Handler function called when event occurs\n * @returns Unsubscribe function\n * @protected\n */\n protected onSystemEvent = <E extends SystemEvent>(\n event: E,\n handler: SystemEventHandler<S, E>,\n ): (() => void) => {\n let handlers = this.systemEventHandlers.get(event);\n if (!handlers) {\n handlers = new Set();\n this.systemEventHandlers.set(event, handlers);\n }\n handlers.add(handler as SystemEventHandler<S, any>);\n\n return () => {\n handlers?.delete(handler as SystemEventHandler<S, any>);\n };\n };\n\n private emitSystemEvent<E extends SystemEvent>(\n event: E,\n payload: SystemEventPayloads<S>[E],\n ): void {\n const handlers = this.systemEventHandlers.get(event);\n if (!handlers) return;\n\n for (const handler of handlers) {\n try {\n handler(payload);\n } catch (error) {\n console.error(`[${this.name}] Error in system event handler:`, error);\n }\n }\n }\n}\n","import { StateContainer } from './StateContainer';\n\n/**\n * Simple state container with direct state emission.\n * Extends StateContainer with public methods for emitting and updating state.\n *\n * @template S - State type\n */\nexport abstract class Cubit<S extends object = any> extends StateContainer<S> {\n constructor(initialState: S) {\n super(initialState);\n }\n\n /**\n * Replace state with a new value and notify all listeners\n * @param newState - The new state value\n */\n public emit(newState: S): void {\n super['emit'](newState);\n }\n\n /**\n * Transform current state using an updater function and emit the new state\n * @param updater - Function that receives current state and returns new state\n */\n public update(updater: (current: S) => S): void {\n super['update'](updater);\n }\n\n /**\n * Merge partial state changes into current state (only for object states)\n */\n public patch = ((partial: S extends object ? Partial<S> : never): void => {\n if (typeof this.state !== 'object' || this.state === null) {\n throw new Error('patch() is only available for object state types');\n }\n const current = this.state;\n let hasChanges = false;\n for (const key in partial) {\n if (!Object.is((current as any)[key], (partial as any)[key])) {\n hasChanges = true;\n break;\n }\n }\n if (hasChanges) {\n this.update((c) => ({ ...c, ...partial }) as S);\n }\n }) as S extends object ? (partial: Partial<S>) => void : never;\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function acquire<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): InstanceType<T> {\n return globalRegistry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n });\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function borrow<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): InstanceType<T> {\n return globalRegistry.borrow(BlocClass, instanceKey);\n}\n\nexport function borrowSafe<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n):\n | { error: Error; instance: null }\n | { error: null; instance: InstanceType<T> } {\n return globalRegistry.borrowSafe(BlocClass, instanceKey);\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function release<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n forceDispose = false,\n): void {\n globalRegistry.release(BlocClass, instanceKey, forceDispose);\n}\n","import { BLAC_STATIC_PROPS } from '../constants';\n\n/**\n * Configuration options for the @blac decorator.\n * Only one option can be specified at a time (union type).\n */\nexport type BlacOptions =\n /** Mark bloc as isolated (each component gets its own instance) */\n | { isolated: true }\n /** Mark bloc to never be auto-disposed when ref count reaches 0 */\n | { keepAlive: true }\n /** Exclude bloc from DevTools tracking (prevents infinite loops) */\n | { excludeFromDevTools: true };\n\n/**\n * Decorator to configure StateContainer classes.\n *\n * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)\n * ```ts\n * @blac({ isolated: true })\n * class FormBloc extends Cubit<FormState> {}\n *\n * @blac({ keepAlive: true })\n * class AuthBloc extends Cubit<AuthState> {}\n *\n * @blac({ excludeFromDevTools: true })\n * class InternalBloc extends Cubit<InternalState> {}\n * ```\n *\n * @example Function syntax (no decorator support needed)\n * ```ts\n * const FormBloc = blac({ isolated: true })(\n * class extends Cubit<FormState> {}\n * );\n * ```\n */\nexport function blac(options: BlacOptions) {\n return function <T extends new (...args: any[]) => any>(\n target: T,\n _context?: ClassDecoratorContext,\n ): T {\n if ('isolated' in options && options.isolated) {\n (target as any)[BLAC_STATIC_PROPS.ISOLATED] = true;\n }\n if ('keepAlive' in options && options.keepAlive) {\n (target as any)[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;\n }\n if ('excludeFromDevTools' in options && options.excludeFromDevTools) {\n (target as any)[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;\n }\n return target;\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { HydrationStatus, StateContainer } from '../core/StateContainer';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata information about a state container instance for debugging and inspection
|
|
4
|
+
*/
|
|
5
|
+
export interface InstanceMetadata {
|
|
6
|
+
/** Unique instance identifier */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Name of the state container class */
|
|
9
|
+
className: string;
|
|
10
|
+
/** Whether the instance has been disposed */
|
|
11
|
+
isDisposed: boolean;
|
|
12
|
+
/** Display name for the instance */
|
|
13
|
+
name: string;
|
|
14
|
+
/** When state last changed (milliseconds) */
|
|
15
|
+
lastStateChangeTimestamp: number;
|
|
16
|
+
/** Current state value */
|
|
17
|
+
state: any;
|
|
18
|
+
/** Timestamp when instance was created (milliseconds) */
|
|
19
|
+
createdAt: number;
|
|
20
|
+
/** Whether this is an isolated (component-scoped) instance */
|
|
21
|
+
isIsolated: boolean;
|
|
22
|
+
/** Stack trace from when instance was created (for debugging) */
|
|
23
|
+
callstack?: string;
|
|
24
|
+
/** Previous state value */
|
|
25
|
+
previousState?: any;
|
|
26
|
+
/** Current state value */
|
|
27
|
+
currentState?: any;
|
|
28
|
+
/** Current hydration status */
|
|
29
|
+
hydrationStatus: HydrationStatus;
|
|
30
|
+
/** Whether hydration completed successfully */
|
|
31
|
+
isHydrated: boolean;
|
|
32
|
+
/** Hydration error, if present */
|
|
33
|
+
hydrationError?: Error;
|
|
34
|
+
/** Whether state changed while hydration was pending */
|
|
35
|
+
changedWhileHydrating: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Safe context API provided to plugins for accessing registry data
|
|
39
|
+
*/
|
|
40
|
+
export interface PluginContext {
|
|
41
|
+
/**
|
|
42
|
+
* Get metadata for a specific instance
|
|
43
|
+
*/
|
|
44
|
+
getInstanceMetadata(instance: StateContainer<any>): InstanceMetadata;
|
|
45
|
+
/**
|
|
46
|
+
* Get current state from a container
|
|
47
|
+
*/
|
|
48
|
+
getState<S extends object = any>(instance: StateContainer<S>): S;
|
|
49
|
+
/**
|
|
50
|
+
* Get hydration status from a container
|
|
51
|
+
*/
|
|
52
|
+
getHydrationStatus(instance: StateContainer<any>): HydrationStatus;
|
|
53
|
+
/**
|
|
54
|
+
* Mark a container as hydrating
|
|
55
|
+
*/
|
|
56
|
+
startHydration(instance: StateContainer<any>): void;
|
|
57
|
+
/**
|
|
58
|
+
* Apply hydrated state if the container has not been mutated while hydrating
|
|
59
|
+
* @returns true when the state was applied
|
|
60
|
+
*/
|
|
61
|
+
applyHydratedState<S extends object = any>(instance: StateContainer<S>, state: S): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Mark hydration as finished
|
|
64
|
+
*/
|
|
65
|
+
finishHydration(instance: StateContainer<any>): void;
|
|
66
|
+
/**
|
|
67
|
+
* Mark hydration as failed
|
|
68
|
+
*/
|
|
69
|
+
failHydration(instance: StateContainer<any>, error: Error): void;
|
|
70
|
+
/**
|
|
71
|
+
* Wait for hydration to settle
|
|
72
|
+
*/
|
|
73
|
+
waitForHydration(instance: StateContainer<any>): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Get all instances of a specific type
|
|
76
|
+
*/
|
|
77
|
+
queryInstances<T extends StateContainer<any>>(typeClass: new (...args: any[]) => T): T[];
|
|
78
|
+
/**
|
|
79
|
+
* Get all registered state container types
|
|
80
|
+
*/
|
|
81
|
+
getAllTypes(): Array<new (...args: any[]) => StateContainer<any>>;
|
|
82
|
+
/**
|
|
83
|
+
* Get registry statistics
|
|
84
|
+
*/
|
|
85
|
+
getStats(): {
|
|
86
|
+
registeredTypes: number;
|
|
87
|
+
totalInstances: number;
|
|
88
|
+
typeBreakdown: Record<string, number>;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Interface for plugins that extend BlaC functionality
|
|
93
|
+
*/
|
|
94
|
+
export interface BlacPlugin {
|
|
95
|
+
/** Unique plugin identifier */
|
|
96
|
+
readonly name: string;
|
|
97
|
+
/** Plugin version identifier */
|
|
98
|
+
readonly version: string;
|
|
99
|
+
/**
|
|
100
|
+
* Called when the plugin is installed (optional)
|
|
101
|
+
*/
|
|
102
|
+
onInstall?(context: PluginContext): void;
|
|
103
|
+
/**
|
|
104
|
+
* Called when the plugin is uninstalled
|
|
105
|
+
*/
|
|
106
|
+
onUninstall?(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Called when a state container instance is created
|
|
109
|
+
*/
|
|
110
|
+
onInstanceCreated?(instance: StateContainer<any>, context: PluginContext): void;
|
|
111
|
+
/**
|
|
112
|
+
* Called when state changes in a container instance
|
|
113
|
+
*/
|
|
114
|
+
onStateChanged?<S extends object = any>(instance: StateContainer<S>, previousState: S, currentState: S, callstack: string | undefined, context: PluginContext): void;
|
|
115
|
+
/**
|
|
116
|
+
* Called when a state container instance is disposed
|
|
117
|
+
*/
|
|
118
|
+
onInstanceDisposed?(instance: StateContainer<any>, context: PluginContext): void;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Plugin interface variant that requires mandatory onInstall hook
|
|
122
|
+
*/
|
|
123
|
+
export interface BlacPluginWithInit extends BlacPlugin {
|
|
124
|
+
/**
|
|
125
|
+
* Required initialization hook called when plugin is installed
|
|
126
|
+
*/
|
|
127
|
+
onInstall(context: PluginContext): void;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Configuration options for plugin installation
|
|
131
|
+
*/
|
|
132
|
+
export interface PluginConfig {
|
|
133
|
+
/** Enable or disable the plugin */
|
|
134
|
+
enabled?: boolean;
|
|
135
|
+
/** Environments where plugin runs */
|
|
136
|
+
environment?: 'development' | 'production' | 'test' | 'all';
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Type guard to check if a plugin has a required onInstall hook
|
|
140
|
+
* @param plugin - The plugin to check
|
|
141
|
+
* @returns true if the plugin implements BlacPluginWithInit
|
|
142
|
+
*/
|
|
143
|
+
export declare function hasInitHook(plugin: BlacPlugin): plugin is BlacPluginWithInit;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { StateContainerRegistry } from '../core/StateContainerRegistry';
|
|
2
|
+
import type { BlacPlugin, PluginConfig } from './BlacPlugin';
|
|
3
|
+
/**
|
|
4
|
+
* Manages plugin lifecycle for the BlaC state management system.
|
|
5
|
+
* Plugins receive notifications about state container lifecycle events.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const manager = createPluginManager(registry);
|
|
10
|
+
* manager.install(myPlugin, { environment: 'development' });
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare class PluginManager {
|
|
14
|
+
private plugins;
|
|
15
|
+
private registry;
|
|
16
|
+
/**
|
|
17
|
+
* Create a new PluginManager
|
|
18
|
+
* @param registry - The StateContainerRegistry to monitor for lifecycle events
|
|
19
|
+
*/
|
|
20
|
+
constructor(registry: StateContainerRegistry);
|
|
21
|
+
/**
|
|
22
|
+
* Install a plugin with optional configuration
|
|
23
|
+
* @param plugin - The plugin to install
|
|
24
|
+
* @param config - Optional plugin configuration
|
|
25
|
+
* @throws Error if plugin is already installed
|
|
26
|
+
*/
|
|
27
|
+
install(plugin: BlacPlugin, config?: PluginConfig): void;
|
|
28
|
+
/**
|
|
29
|
+
* Uninstall a plugin by name
|
|
30
|
+
* @param pluginName - The name of the plugin to uninstall
|
|
31
|
+
* @throws Error if plugin is not installed
|
|
32
|
+
*/
|
|
33
|
+
uninstall(pluginName: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Get an installed plugin by name
|
|
36
|
+
* @param pluginName - The name of the plugin to retrieve
|
|
37
|
+
* @returns The plugin instance or undefined if not found
|
|
38
|
+
*/
|
|
39
|
+
getPlugin(pluginName: string): BlacPlugin | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Get all installed plugins
|
|
42
|
+
* @returns Array of all installed plugins
|
|
43
|
+
*/
|
|
44
|
+
getAllPlugins(): BlacPlugin[];
|
|
45
|
+
/**
|
|
46
|
+
* Check if a plugin is installed
|
|
47
|
+
* @param pluginName - The name of the plugin to check
|
|
48
|
+
* @returns true if the plugin is installed
|
|
49
|
+
*/
|
|
50
|
+
hasPlugin(pluginName: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Uninstall all plugins
|
|
53
|
+
*/
|
|
54
|
+
clear(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Setup lifecycle hooks to notify plugins
|
|
57
|
+
*/
|
|
58
|
+
private setupLifecycleHooks;
|
|
59
|
+
/**
|
|
60
|
+
* Notify all plugins of a lifecycle event
|
|
61
|
+
*/
|
|
62
|
+
private notifyPlugins;
|
|
63
|
+
/**
|
|
64
|
+
* Create plugin context with safe API access
|
|
65
|
+
*/
|
|
66
|
+
private createPluginContext;
|
|
67
|
+
/**
|
|
68
|
+
* Check if plugin should be enabled based on environment
|
|
69
|
+
*/
|
|
70
|
+
private shouldEnablePlugin;
|
|
71
|
+
/**
|
|
72
|
+
* Get current environment
|
|
73
|
+
*/
|
|
74
|
+
private getCurrentEnvironment;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a plugin manager instance
|
|
78
|
+
* @param registry - The StateContainerRegistry to monitor for lifecycle events
|
|
79
|
+
* @returns A new PluginManager instance
|
|
80
|
+
*/
|
|
81
|
+
export declare function createPluginManager(registry: StateContainerRegistry): PluginManager;
|
package/dist/plugins.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_StateContainerRegistry = require("./StateContainerRegistry-CVPTMaHn.cjs");
|
|
3
3
|
exports.PluginManager = require_StateContainerRegistry.PluginManager;
|
|
4
|
-
exports.getPluginManager = require_StateContainerRegistry.getPluginManager;
|
|
4
|
+
exports.getPluginManager = require_StateContainerRegistry.getPluginManager;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugins Subpath Export
|
|
3
|
+
*
|
|
4
|
+
* Plugin system utilities for extending BlaC functionality.
|
|
5
|
+
* Import from '@blac/core/plugins'
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { PluginManager, getPluginManager, type BlacPlugin } from '@blac/core/plugins';
|
|
10
|
+
*
|
|
11
|
+
* const myPlugin: BlacPlugin = {
|
|
12
|
+
* name: 'my-plugin',
|
|
13
|
+
* onInstanceCreated(context) {
|
|
14
|
+
* console.log('Instance created:', context.instance);
|
|
15
|
+
* },
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* getPluginManager().register(myPlugin);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @packageDocumentation
|
|
22
|
+
*/
|
|
23
|
+
export { PluginManager } from './plugin/PluginManager';
|
|
24
|
+
export type { BlacPlugin, BlacPluginWithInit, PluginContext, PluginConfig, InstanceMetadata, } from './plugin/BlacPlugin';
|
|
25
|
+
export { getPluginManager } from './core/StateContainerRegistry';
|