@blac/core 2.0.0 → 2.0.3
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 +137 -177
- package/dist/StateContainerRegistry.cjs.map +1 -0
- package/dist/StateContainerRegistry.js.map +1 -0
- package/dist/debug.cjs +27 -0
- package/dist/debug.cjs.map +1 -0
- package/dist/debug.js +19 -0
- package/dist/debug.js.map +1 -0
- package/dist/dependency-manager.cjs.map +1 -0
- package/dist/dependency-manager.js.map +1 -0
- package/dist/index.cjs +346 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +794 -0
- package/dist/index.d.ts +675 -386
- package/dist/index.js +310 -0
- package/dist/index.js.map +1 -0
- package/dist/management.cjs.map +1 -0
- package/dist/management.js.map +1 -0
- package/dist/plugins.cjs +4 -0
- package/dist/plugins.js +3 -0
- package/dist/tracking/dependency-manager.d.ts +37 -0
- package/dist/tracking/index.d.ts +12 -0
- package/dist/tracking/path-utils.d.ts +52 -0
- package/dist/tracking/tracked.d.ts +70 -0
- package/dist/tracking/tracking-proxy.d.ts +197 -0
- package/dist/tracking-proxy.cjs.map +1 -0
- package/dist/tracking-proxy.js.map +1 -0
- package/dist/tracking.cjs +19 -0
- package/dist/tracking.d.cts +9 -0
- package/dist/tracking.d.ts +9 -0
- package/dist/tracking.js +4 -0
- package/dist/types.cjs +14 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +8 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/dist/watch-entry.cjs +111 -0
- package/dist/watch-entry.cjs.map +1 -0
- package/dist/watch-entry.js +107 -0
- package/dist/watch-entry.js.map +1 -0
- package/dist/watch.cjs.map +1 -0
- package/dist/watch.js.map +1 -0
- package/package.json +122 -19
- package/dist/index.cjs.js +0 -2
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -670
- package/dist/index.es.js.map +0 -1
- package/src/Blac.ts +0 -483
- package/src/BlacEvent.ts +0 -5
- package/src/BlacObservable.ts +0 -88
- package/src/BlacObserver.ts +0 -129
- package/src/BlacPlugin.ts +0 -13
- package/src/Bloc.ts +0 -109
- package/src/BlocBase.ts +0 -248
- package/src/Cubit.ts +0 -69
- package/src/addons/BlacAddon.ts +0 -17
- package/src/addons/Persist.ts +0 -105
- package/src/addons/index.ts +0 -1
- package/src/index.ts +0 -8
- package/src/types.ts +0 -63
package/dist/index.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { i as PluginManager, n as globalRegistry, r as isIsolatedClass, t as getPluginManager } from "./StateContainerRegistry.js";
|
|
2
|
+
import { _ as setActiveTracker, a as createBlocProxy, c as createGetterState, g as invalidateRenderCache, h as hasTrackedData, i as commitTrackedGetters, m as hasGetterChanges, n as clearActiveTracker, o as createDependencyProxy, p as hasDependencyChanges, r as clearExternalDependencies, s as createDependencyState, t as capturePaths, v as startDependency, w as BLAC_STATIC_PROPS, x as shallowEqual } from "./tracking-proxy.js";
|
|
3
|
+
import { a as getAll, i as forEach, n as clearAll, o as getRefCount, s as hasInstance, t as clear } from "./management.js";
|
|
4
|
+
import { n as watch, r as ensure, t as instance } from "./watch.js";
|
|
5
|
+
import { t as DependencyManager } from "./dependency-manager.js";
|
|
6
|
+
import "./tracking.js";
|
|
7
|
+
|
|
8
|
+
//#region src/utils/idGenerator.ts
|
|
9
|
+
/**
|
|
10
|
+
* Generate simple ID with timestamp and random (no counter tracking)
|
|
11
|
+
*
|
|
12
|
+
* Format: `${prefix}:${timestamp}_${random}`
|
|
13
|
+
*
|
|
14
|
+
* @param prefix - Prefix for the ID
|
|
15
|
+
* @returns Branded ID string
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const id = generateSimpleId('CounterBloc');
|
|
20
|
+
* // Returns: "CounterBloc:1698765432100_a3k9d7f2q"
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function generateSimpleId(prefix, affix) {
|
|
24
|
+
if (affix) return `${prefix}:${affix}`;
|
|
25
|
+
return `${prefix}:${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate a unique isolated instance key
|
|
29
|
+
* Uses base36 encoding for compact, URL-safe identifiers
|
|
30
|
+
*
|
|
31
|
+
* Format: "isolated-{9-char-random-string}"
|
|
32
|
+
* Example: "isolated-k7x2m9p4q"
|
|
33
|
+
*
|
|
34
|
+
* @returns A unique isolated instance key
|
|
35
|
+
*/
|
|
36
|
+
function generateIsolatedKey() {
|
|
37
|
+
return `isolated-${Math.random().toString(36).slice(2, 11)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/core/StateContainer.ts
|
|
42
|
+
/**
|
|
43
|
+
* Abstract base class for all state containers in BlaC.
|
|
44
|
+
* Provides lifecycle management, subscription handling, ref counting,
|
|
45
|
+
* and integration with the global registry.
|
|
46
|
+
*
|
|
47
|
+
* @template S - State type managed by this container
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* class CounterBloc extends StateContainer<{ count: number }> {
|
|
52
|
+
* constructor() {
|
|
53
|
+
* super({ count: 0 });
|
|
54
|
+
* }
|
|
55
|
+
* increment() {
|
|
56
|
+
* this.emit({ count: this.state.count + 1 });
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
var StateContainer = class StateContainer {
|
|
62
|
+
static {
|
|
63
|
+
this.__excludeFromDevTools = false;
|
|
64
|
+
}
|
|
65
|
+
static {
|
|
66
|
+
this.enableStackTrace = true;
|
|
67
|
+
}
|
|
68
|
+
constructor(initialState) {
|
|
69
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
70
|
+
this._disposed = false;
|
|
71
|
+
this.config = {};
|
|
72
|
+
this.systemEventHandlers = /* @__PURE__ */ new Map();
|
|
73
|
+
this.name = this.constructor.name;
|
|
74
|
+
this.debug = false;
|
|
75
|
+
this.instanceId = generateSimpleId(this.constructor.name, "main");
|
|
76
|
+
this.createdAt = Date.now();
|
|
77
|
+
this.lastUpdateTimestamp = Date.now();
|
|
78
|
+
this.onSystemEvent = (event, handler) => {
|
|
79
|
+
let handlers = this.systemEventHandlers.get(event);
|
|
80
|
+
if (!handlers) {
|
|
81
|
+
handlers = /* @__PURE__ */ new Set();
|
|
82
|
+
this.systemEventHandlers.set(event, handlers);
|
|
83
|
+
}
|
|
84
|
+
handlers.add(handler);
|
|
85
|
+
return () => {
|
|
86
|
+
handlers?.delete(handler);
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
this._state = initialState;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Initialize configuration for this instance.
|
|
93
|
+
* Called by the registry after construction.
|
|
94
|
+
* @param config - Configuration options
|
|
95
|
+
*/
|
|
96
|
+
initConfig(config) {
|
|
97
|
+
this.config = { ...config };
|
|
98
|
+
this.name = this.config.name || this.constructor.name;
|
|
99
|
+
this.debug = this.config.debug ?? false;
|
|
100
|
+
this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
|
|
101
|
+
globalRegistry.emit("created", this);
|
|
102
|
+
}
|
|
103
|
+
/** Current state value */
|
|
104
|
+
get state() {
|
|
105
|
+
return this._state;
|
|
106
|
+
}
|
|
107
|
+
/** Whether this instance has been disposed */
|
|
108
|
+
get isDisposed() {
|
|
109
|
+
return this._disposed;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Subscribe to state changes
|
|
113
|
+
* @param listener - Function called when state changes
|
|
114
|
+
* @returns Unsubscribe function
|
|
115
|
+
*/
|
|
116
|
+
subscribe(listener) {
|
|
117
|
+
if (this._disposed) throw new Error(`Cannot subscribe to disposed container ${this.name}`);
|
|
118
|
+
this.listeners.add(listener);
|
|
119
|
+
return () => this.listeners.delete(listener);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Dispose this instance and clean up resources.
|
|
123
|
+
* Clears all listeners and emits the 'dispose' system event.
|
|
124
|
+
*/
|
|
125
|
+
dispose() {
|
|
126
|
+
if (this._disposed) return;
|
|
127
|
+
if (this.debug) console.log(`[${this.name}] Disposing...`);
|
|
128
|
+
this._disposed = true;
|
|
129
|
+
this.emitSystemEvent("dispose", void 0);
|
|
130
|
+
this.listeners.clear();
|
|
131
|
+
this.systemEventHandlers.clear();
|
|
132
|
+
globalRegistry.emit("disposed", this);
|
|
133
|
+
if (this.debug) console.log(`[${this.name}] Disposed successfully`);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Emit a new state value and notify all listeners.
|
|
137
|
+
* @param newState - The new state value
|
|
138
|
+
* @protected
|
|
139
|
+
*/
|
|
140
|
+
emit(newState) {
|
|
141
|
+
if (this._disposed) throw new Error(`Cannot emit state from disposed container ${this.name}`);
|
|
142
|
+
if (this._state === newState) return;
|
|
143
|
+
const previousState = this._state;
|
|
144
|
+
this._state = newState;
|
|
145
|
+
this.emitSystemEvent("stateChanged", {
|
|
146
|
+
state: newState,
|
|
147
|
+
previousState
|
|
148
|
+
});
|
|
149
|
+
for (const listener of this.listeners) try {
|
|
150
|
+
listener(newState);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`[${this.name}] Error in listener:`, error);
|
|
153
|
+
}
|
|
154
|
+
const stackTrace = this.captureStackTrace();
|
|
155
|
+
globalRegistry.emit("stateChanged", this, previousState, newState, stackTrace);
|
|
156
|
+
this.lastUpdateTimestamp = Date.now();
|
|
157
|
+
}
|
|
158
|
+
captureStackTrace() {
|
|
159
|
+
if (!StateContainer.enableStackTrace || typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
|
|
160
|
+
try {
|
|
161
|
+
const relevantLines = ((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(1);
|
|
162
|
+
const formattedLines = [];
|
|
163
|
+
for (const line of relevantLines) {
|
|
164
|
+
if (!line.trim()) continue;
|
|
165
|
+
if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("Cubit.patch")) continue;
|
|
166
|
+
if (line.includes("node_modules") || line.includes("react-dom") || line.includes("react_jsx") || line.includes(".vite/deps") || line.includes("executeDispatch") || line.includes("runWithFiber") || line.includes("invokeGuarded") || line.includes("callCallback") || line.includes("processDispatchQueue") || line.includes("dispatchEvent") || line.includes("batchedUpdates")) continue;
|
|
167
|
+
const formatted = this.formatStackLine(line);
|
|
168
|
+
if (formatted) formattedLines.push(formatted);
|
|
169
|
+
}
|
|
170
|
+
return formattedLines.join("\n");
|
|
171
|
+
} catch {
|
|
172
|
+
return "";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
formatStackLine(line) {
|
|
176
|
+
const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
|
|
177
|
+
if (!match) {
|
|
178
|
+
const simpleMatch = line.match(/at\s+(.+?):(\d+):(\d+)/);
|
|
179
|
+
if (simpleMatch) {
|
|
180
|
+
const [, url, lineNum, col] = simpleMatch;
|
|
181
|
+
return ` at ${this.cleanFilePath(url)}:${lineNum}:${col}`;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const [, functionName, url, lineNum, col] = match;
|
|
186
|
+
return ` at ${functionName} (${this.cleanFilePath(url)}:${lineNum}:${col})`;
|
|
187
|
+
}
|
|
188
|
+
cleanFilePath(url) {
|
|
189
|
+
let path = url.replace(/http:\/\/localhost:\d+\/@fs/, "").replace(/http:\/\/localhost:\d+\//, "").replace(/\?t=\d+/, "").replace(/\?v=[a-f0-9]+/, "");
|
|
190
|
+
const projectMatch = path.match(/\/Projects\/blac\/(.+)/);
|
|
191
|
+
if (projectMatch) path = projectMatch[1];
|
|
192
|
+
const segments = path.split("/");
|
|
193
|
+
if (segments.length > 3) path = segments.slice(-3).join("/");
|
|
194
|
+
return path;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Update state using a transform function.
|
|
198
|
+
* @param updater - Function that receives current state and returns new state
|
|
199
|
+
* @protected
|
|
200
|
+
*/
|
|
201
|
+
update(updater) {
|
|
202
|
+
if (this._disposed) throw new Error(`Cannot update state from disposed container ${this.name}`);
|
|
203
|
+
this.emit(updater(this._state));
|
|
204
|
+
}
|
|
205
|
+
emitSystemEvent(event, payload) {
|
|
206
|
+
const handlers = this.systemEventHandlers.get(event);
|
|
207
|
+
if (!handlers) return;
|
|
208
|
+
for (const handler of handlers) try {
|
|
209
|
+
handler(payload);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error(`[${this.name}] Error in system event handler:`, error);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/core/Cubit.ts
|
|
218
|
+
/**
|
|
219
|
+
* Simple state container with direct state emission.
|
|
220
|
+
* Extends StateContainer with public methods for emitting and updating state.
|
|
221
|
+
*
|
|
222
|
+
* @template S - State type
|
|
223
|
+
*/
|
|
224
|
+
var Cubit = class extends StateContainer {
|
|
225
|
+
constructor(initialState) {
|
|
226
|
+
super(initialState);
|
|
227
|
+
this.patch = ((partial) => {
|
|
228
|
+
if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
|
|
229
|
+
this.update((current) => ({
|
|
230
|
+
...current,
|
|
231
|
+
...partial
|
|
232
|
+
}));
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Replace state with a new value and notify all listeners
|
|
237
|
+
* @param newState - The new state value
|
|
238
|
+
*/
|
|
239
|
+
emit(newState) {
|
|
240
|
+
super["emit"](newState);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Transform current state using an updater function and emit the new state
|
|
244
|
+
* @param updater - Function that receives current state and returns new state
|
|
245
|
+
*/
|
|
246
|
+
update(updater) {
|
|
247
|
+
super["update"](updater);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/registry/acquire.ts
|
|
253
|
+
function acquire(BlocClass, instanceKey) {
|
|
254
|
+
return globalRegistry.acquire(BlocClass, instanceKey, {
|
|
255
|
+
canCreate: true,
|
|
256
|
+
countRef: true
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/registry/borrow.ts
|
|
262
|
+
function borrow(BlocClass, instanceKey) {
|
|
263
|
+
return globalRegistry.borrow(BlocClass, instanceKey);
|
|
264
|
+
}
|
|
265
|
+
function borrowSafe(BlocClass, instanceKey) {
|
|
266
|
+
return globalRegistry.borrowSafe(BlocClass, instanceKey);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/registry/release.ts
|
|
271
|
+
function release(BlocClass, instanceKey, forceDispose = false) {
|
|
272
|
+
globalRegistry.release(BlocClass, instanceKey, forceDispose);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/decorators/blac.ts
|
|
277
|
+
/**
|
|
278
|
+
* Decorator to configure StateContainer classes.
|
|
279
|
+
*
|
|
280
|
+
* @example Decorator syntax (requires experimentalDecorators or TC39 decorators)
|
|
281
|
+
* ```ts
|
|
282
|
+
* @blac({ isolated: true })
|
|
283
|
+
* class FormBloc extends Cubit<FormState> {}
|
|
284
|
+
*
|
|
285
|
+
* @blac({ keepAlive: true })
|
|
286
|
+
* class AuthBloc extends Cubit<AuthState> {}
|
|
287
|
+
*
|
|
288
|
+
* @blac({ excludeFromDevTools: true })
|
|
289
|
+
* class InternalBloc extends Cubit<InternalState> {}
|
|
290
|
+
* ```
|
|
291
|
+
*
|
|
292
|
+
* @example Function syntax (no decorator support needed)
|
|
293
|
+
* ```ts
|
|
294
|
+
* const FormBloc = blac({ isolated: true })(
|
|
295
|
+
* class extends Cubit<FormState> {}
|
|
296
|
+
* );
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
function blac(options) {
|
|
300
|
+
return function(target, _context) {
|
|
301
|
+
if ("isolated" in options && options.isolated) target[BLAC_STATIC_PROPS.ISOLATED] = true;
|
|
302
|
+
if ("keepAlive" in options && options.keepAlive) target[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;
|
|
303
|
+
if ("excludeFromDevTools" in options && options.excludeFromDevTools) target[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;
|
|
304
|
+
return target;
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
//#endregion
|
|
309
|
+
export { Cubit, DependencyManager, PluginManager, StateContainer, acquire, blac, borrow, borrowSafe, capturePaths, clear, clearActiveTracker, clearAll, clearExternalDependencies, commitTrackedGetters, createBlocProxy, createDependencyProxy, createDependencyState, createGetterState, ensure, forEach, generateIsolatedKey, getAll, getPluginManager, getRefCount, globalRegistry, hasDependencyChanges, hasGetterChanges, hasInstance, hasTrackedData, instance, invalidateRenderCache, isIsolatedClass, release, setActiveTracker, shallowEqual, startDependency, watch };
|
|
310
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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 { globalRegistry } from './StateContainerRegistry';\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\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 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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"management.cjs","names":["globalRegistry"],"sources":["../src/registry/queries.ts","../src/registry/management.ts"],"sourcesContent":["import { globalRegistry } from '../core/StateContainerRegistry';\nimport type {\n StateContainerConstructor,\n InstanceReadonlyState,\n} from '../types/utilities';\n\nexport function hasInstance<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): boolean {\n return globalRegistry.hasInstance(BlocClass, instanceKey);\n}\n\nexport function getRefCount<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): number {\n return globalRegistry.getRefCount(BlocClass, instanceKey);\n}\n\nexport function getAll<T extends StateContainerConstructor>(\n BlocClass: T,\n): InstanceReadonlyState<T>[] {\n return globalRegistry.getAll(BlocClass);\n}\n\nexport function forEach<T extends StateContainerConstructor>(\n BlocClass: T,\n callback: (instance: InstanceReadonlyState<T>) => void,\n): void {\n globalRegistry.forEach(BlocClass, callback);\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function clear<T extends StateContainerConstructor>(BlocClass: T): void {\n globalRegistry.clear(BlocClass);\n}\n\nexport function clearAll(): void {\n globalRegistry.clearAll();\n}\n\nexport function register<T extends StateContainerConstructor>(\n BlocClass: T,\n isolated = false,\n): void {\n globalRegistry.register(BlocClass, isolated);\n}\n"],"mappings":";;;AAMA,SAAgB,YACd,WACA,aACS;AACT,QAAOA,8CAAe,YAAY,WAAW,YAAY;;AAG3D,SAAgB,YACd,WACA,aACQ;AACR,QAAOA,8CAAe,YAAY,WAAW,YAAY;;AAG3D,SAAgB,OACd,WAC4B;AAC5B,QAAOA,8CAAe,OAAO,UAAU;;AAGzC,SAAgB,QACd,WACA,UACM;AACN,+CAAe,QAAQ,WAAW,SAAS;;;;;AC3B7C,SAAgB,MAA2C,WAAoB;AAC7E,+CAAe,MAAM,UAAU;;AAGjC,SAAgB,WAAiB;AAC/B,+CAAe,UAAU;;AAG3B,SAAgB,SACd,WACA,WAAW,OACL;AACN,+CAAe,SAAS,WAAW,SAAS"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"management.js","names":[],"sources":["../src/registry/queries.ts","../src/registry/management.ts"],"sourcesContent":["import { globalRegistry } from '../core/StateContainerRegistry';\nimport type {\n StateContainerConstructor,\n InstanceReadonlyState,\n} from '../types/utilities';\n\nexport function hasInstance<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): boolean {\n return globalRegistry.hasInstance(BlocClass, instanceKey);\n}\n\nexport function getRefCount<T extends StateContainerConstructor>(\n BlocClass: T,\n instanceKey?: string,\n): number {\n return globalRegistry.getRefCount(BlocClass, instanceKey);\n}\n\nexport function getAll<T extends StateContainerConstructor>(\n BlocClass: T,\n): InstanceReadonlyState<T>[] {\n return globalRegistry.getAll(BlocClass);\n}\n\nexport function forEach<T extends StateContainerConstructor>(\n BlocClass: T,\n callback: (instance: InstanceReadonlyState<T>) => void,\n): void {\n globalRegistry.forEach(BlocClass, callback);\n}\n","import { globalRegistry } from '../core/StateContainerRegistry';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function clear<T extends StateContainerConstructor>(BlocClass: T): void {\n globalRegistry.clear(BlocClass);\n}\n\nexport function clearAll(): void {\n globalRegistry.clearAll();\n}\n\nexport function register<T extends StateContainerConstructor>(\n BlocClass: T,\n isolated = false,\n): void {\n globalRegistry.register(BlocClass, isolated);\n}\n"],"mappings":";;;AAMA,SAAgB,YACd,WACA,aACS;AACT,QAAO,eAAe,YAAY,WAAW,YAAY;;AAG3D,SAAgB,YACd,WACA,aACQ;AACR,QAAO,eAAe,YAAY,WAAW,YAAY;;AAG3D,SAAgB,OACd,WAC4B;AAC5B,QAAO,eAAe,OAAO,UAAU;;AAGzC,SAAgB,QACd,WACA,UACM;AACN,gBAAe,QAAQ,WAAW,SAAS;;;;;AC3B7C,SAAgB,MAA2C,WAAoB;AAC7E,gBAAe,MAAM,UAAU;;AAGjC,SAAgB,WAAiB;AAC/B,gBAAe,UAAU;;AAG3B,SAAgB,SACd,WACA,WAAW,OACL;AACN,gBAAe,SAAS,WAAW,SAAS"}
|
package/dist/plugins.cjs
ADDED
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { StateContainerInstance } from '../types/utilities';
|
|
2
|
+
/**
|
|
3
|
+
* Manages subscriptions to state container dependencies.
|
|
4
|
+
* Provides efficient sync mechanism to add/remove subscriptions
|
|
5
|
+
* as dependencies change between callback invocations.
|
|
6
|
+
*/
|
|
7
|
+
export declare class DependencyManager {
|
|
8
|
+
private subscriptions;
|
|
9
|
+
private currentDeps;
|
|
10
|
+
/**
|
|
11
|
+
* Sync subscriptions with a new set of dependencies.
|
|
12
|
+
* Adds subscriptions for new deps, removes subscriptions for stale deps.
|
|
13
|
+
*
|
|
14
|
+
* @param newDeps - The new set of dependencies to subscribe to
|
|
15
|
+
* @param onChange - Callback to invoke when any dependency changes
|
|
16
|
+
* @param exclude - Optional instance to exclude from subscriptions (e.g., primary bloc)
|
|
17
|
+
* @returns true if the dependency set changed, false if unchanged
|
|
18
|
+
*/
|
|
19
|
+
sync(newDeps: Set<StateContainerInstance>, onChange: () => void, exclude?: StateContainerInstance): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Add a single dependency subscription.
|
|
22
|
+
*/
|
|
23
|
+
add(dep: StateContainerInstance, onChange: () => void): void;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a dependency is currently subscribed.
|
|
26
|
+
*/
|
|
27
|
+
has(dep: StateContainerInstance): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Get the current set of dependencies.
|
|
30
|
+
*/
|
|
31
|
+
getDependencies(): Set<StateContainerInstance>;
|
|
32
|
+
/**
|
|
33
|
+
* Clean up all active subscriptions.
|
|
34
|
+
*/
|
|
35
|
+
cleanup(): void;
|
|
36
|
+
private areSetsEqual;
|
|
37
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Tracking
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic dependency tracking utilities for automatic reactivity.
|
|
5
|
+
* These utilities are used by framework adapters (React, Vue, Angular, etc.)
|
|
6
|
+
* to track property accesses and detect changes.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { type DependencyState, type PathInfo, createDependencyState, startDependency, createDependencyProxy, capturePaths, hasDependencyChanges, hasTrackedData, type GetterState, createGetterState, createBlocProxy, hasGetterChanges, setActiveTracker, clearActiveTracker, commitTrackedGetters, invalidateRenderCache, clearExternalDependencies, } from './tracking-proxy';
|
|
11
|
+
export { shallowEqual } from './path-utils';
|
|
12
|
+
export { DependencyManager } from './dependency-manager';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path utilities for dependency tracking
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for parsing property paths and extracting values
|
|
5
|
+
* from nested objects using path strings.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Parse a property path string into an array of segments
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*
|
|
14
|
+
* Handles both dot notation (a.b.c) and bracket notation (a[0].b)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* parsePath('user.name') // ['user', 'name']
|
|
19
|
+
* parsePath('items[0].name') // ['items', '0', 'name']
|
|
20
|
+
* parsePath('data.users[2].address.city') // ['data', 'users', '2', 'address', 'city']
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function parsePath(path: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Get a value from an object using a path of segments
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const obj = { user: { name: 'Alice', age: 30 } }
|
|
30
|
+
* getValueAtPath(obj, ['user', 'name']) // 'Alice'
|
|
31
|
+
* getValueAtPath(obj, ['user', 'age']) // 30
|
|
32
|
+
* getValueAtPath(obj, ['user', 'missing']) // undefined
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export declare function getValueAtPath(obj: any, segments: string[]): any;
|
|
38
|
+
/**
|
|
39
|
+
* Shallow equality comparison for arrays
|
|
40
|
+
*
|
|
41
|
+
* Compares two arrays element-by-element using Object.is
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* shallowEqual([1, 2, 3], [1, 2, 3]) // true
|
|
46
|
+
* shallowEqual([1, 2, 3], [1, 2, 4]) // false
|
|
47
|
+
* shallowEqual([1, 2], [1, 2, 3]) // false
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export declare function shallowEqual(arr1: unknown[], arr2: unknown[]): boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { StateContainerInstance } from '../types/utilities';
|
|
2
|
+
import { DependencyManager } from './dependency-manager';
|
|
3
|
+
/**
|
|
4
|
+
* Result of running a tracked callback.
|
|
5
|
+
*/
|
|
6
|
+
export interface TrackedResult<T> {
|
|
7
|
+
result: T;
|
|
8
|
+
dependencies: Set<StateContainerInstance>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Options for the tracked() function.
|
|
12
|
+
*/
|
|
13
|
+
export interface TrackedOptions {
|
|
14
|
+
exclude?: StateContainerInstance;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run a callback while tracking all bloc dependencies accessed.
|
|
18
|
+
* Returns both the result and the set of discovered dependencies.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const { result, dependencies } = tracked(() => {
|
|
23
|
+
* const user = ensure(UserBloc);
|
|
24
|
+
* return user.fullName; // getter that may access other blocs
|
|
25
|
+
* });
|
|
26
|
+
* // dependencies contains UserBloc + any blocs accessed in fullName getter
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function tracked<T>(callback: () => T, options?: TrackedOptions): TrackedResult<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Context for running tracked callbacks with bloc proxies.
|
|
32
|
+
* Provides methods to create proxies and check for changes.
|
|
33
|
+
*/
|
|
34
|
+
export declare class TrackedContext {
|
|
35
|
+
private tracker;
|
|
36
|
+
private proxiedBlocs;
|
|
37
|
+
private primaryBlocs;
|
|
38
|
+
constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Get a tracking proxy for a bloc instance.
|
|
41
|
+
* The proxy will track state and getter accesses.
|
|
42
|
+
*/
|
|
43
|
+
proxy<T extends StateContainerInstance>(bloc: T): T;
|
|
44
|
+
/**
|
|
45
|
+
* Start tracking for a new callback execution.
|
|
46
|
+
*/
|
|
47
|
+
start(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Stop tracking and get discovered dependencies.
|
|
50
|
+
* Excludes primary blocs (those explicitly proxied via proxy()).
|
|
51
|
+
*/
|
|
52
|
+
stop(): Set<StateContainerInstance>;
|
|
53
|
+
/**
|
|
54
|
+
* Check if any tracked state or getters have changed.
|
|
55
|
+
*/
|
|
56
|
+
changed(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get all primary blocs (those explicitly proxied).
|
|
59
|
+
*/
|
|
60
|
+
getPrimaryBlocs(): Set<StateContainerInstance>;
|
|
61
|
+
/**
|
|
62
|
+
* Reset the context for reuse.
|
|
63
|
+
*/
|
|
64
|
+
reset(): void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create a new tracked context for manual control over tracking.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createTrackedContext(): TrackedContext;
|
|
70
|
+
export { DependencyManager };
|