@blac/core 2.0.0-rc.6 → 2.0.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/framework-adapter.d.ts +114 -0
- package/dist/adapter/framework-adapter.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +8 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/constants.d.ts +82 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/core/Cubit.d.ts +82 -0
- package/dist/core/Cubit.d.ts.map +1 -0
- package/dist/core/StateContainer.d.ts +200 -0
- package/dist/core/StateContainer.d.ts.map +1 -0
- package/dist/core/StateContainerRegistry.d.ts +176 -0
- package/dist/core/StateContainerRegistry.d.ts.map +1 -0
- package/dist/core/Vertex.d.ts +113 -0
- package/dist/core/Vertex.d.ts.map +1 -0
- package/dist/index.cjs +1158 -326
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -885
- package/dist/index.d.ts +19 -884
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1122 -327
- package/dist/index.js.map +1 -1
- package/dist/logging/Logger.d.ts +57 -0
- package/dist/logging/Logger.d.ts.map +1 -0
- package/dist/plugin/BlacPlugin.d.ts +115 -0
- package/dist/plugin/BlacPlugin.d.ts.map +1 -0
- package/dist/plugin/PluginManager.d.ts +63 -0
- package/dist/plugin/PluginManager.d.ts.map +1 -0
- package/dist/proxy/ProxyTracker.d.ts +71 -0
- package/dist/proxy/ProxyTracker.d.ts.map +1 -0
- package/dist/registry/index.d.ts +20 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/test-utils/index.d.ts +43 -0
- package/dist/test-utils/index.d.ts.map +1 -0
- package/dist/tracking/dependency-tracker.d.ts +21 -0
- package/dist/tracking/dependency-tracker.d.ts.map +1 -0
- package/dist/tracking/getter-tracker.d.ts +31 -0
- package/dist/tracking/getter-tracker.d.ts.map +1 -0
- package/dist/tracking/index.d.ts +38 -0
- package/dist/tracking/index.d.ts.map +1 -0
- package/dist/tracking/path-utils.d.ts +39 -0
- package/dist/tracking/path-utils.d.ts.map +1 -0
- package/dist/tracking/proxy-tracker.d.ts +88 -0
- package/dist/tracking/proxy-tracker.d.ts.map +1 -0
- package/dist/types/branded.d.ts +9 -0
- package/dist/types/branded.d.ts.map +1 -0
- package/dist/types/events.d.ts +13 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/utilities.d.ts +18 -0
- package/dist/types/utilities.d.ts.map +1 -0
- package/dist/utils/equality.d.ts +9 -0
- package/dist/utils/equality.d.ts.map +1 -0
- package/dist/utils/idGenerator.d.ts +76 -0
- package/dist/utils/idGenerator.d.ts.map +1 -0
- package/dist/utils/immutable.d.ts +16 -0
- package/dist/utils/immutable.d.ts.map +1 -0
- package/dist/utils/stack-trace.d.ts +19 -0
- package/dist/utils/stack-trace.d.ts.map +1 -0
- package/dist/utils/static-props.d.ts +29 -0
- package/dist/utils/static-props.d.ts.map +1 -0
- package/package.json +17 -4
package/dist/index.js
CHANGED
|
@@ -15,15 +15,15 @@ const globalCounters = /* @__PURE__ */ new Map();
|
|
|
15
15
|
* @example
|
|
16
16
|
* ```ts
|
|
17
17
|
* const generator = createIdGenerator('sub');
|
|
18
|
-
* const id1 = generator.next(); // "
|
|
19
|
-
* const id2 = generator.next(); // "
|
|
18
|
+
* const id1 = generator.next(); // "sub:1698765432100_1_a3k9d7f2q"
|
|
19
|
+
* const id2 = generator.next(); // "sub:1698765432101_2_b4n8e9g3r"
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
function createIdGenerator(prefix) {
|
|
23
23
|
let counter = 0;
|
|
24
24
|
return {
|
|
25
|
-
next: () => `${prefix}
|
|
26
|
-
nextSimple: () => `${prefix}
|
|
25
|
+
next: () => `${prefix}:${Date.now()}_${++counter}_${Math.random().toString(36).substring(2, 11)}`,
|
|
26
|
+
nextSimple: () => `${prefix}:${++counter}`,
|
|
27
27
|
reset: () => {
|
|
28
28
|
counter = 0;
|
|
29
29
|
}
|
|
@@ -32,7 +32,7 @@ function createIdGenerator(prefix) {
|
|
|
32
32
|
/**
|
|
33
33
|
* Generate ID with timestamp, counter, and random suffix (tree-shakeable)
|
|
34
34
|
*
|
|
35
|
-
* Format: `${prefix}
|
|
35
|
+
* Format: `${prefix}:${timestamp}_${counter}_${random}`
|
|
36
36
|
*
|
|
37
37
|
* @param prefix - Prefix for the ID (e.g., 'sub', 'consumer', 'stage')
|
|
38
38
|
* @returns Branded ID string
|
|
@@ -40,18 +40,18 @@ function createIdGenerator(prefix) {
|
|
|
40
40
|
* @example
|
|
41
41
|
* ```ts
|
|
42
42
|
* const id = generateId('sub');
|
|
43
|
-
* // Returns: "
|
|
43
|
+
* // Returns: "sub:1698765432100_1_a3k9d7f2q"
|
|
44
44
|
* ```
|
|
45
45
|
*/
|
|
46
46
|
function generateId(prefix) {
|
|
47
47
|
const counter = (globalCounters.get(prefix) ?? 0) + 1;
|
|
48
48
|
globalCounters.set(prefix, counter);
|
|
49
|
-
return `${prefix}
|
|
49
|
+
return `${prefix}:${Date.now()}_${counter}_${Math.random().toString(36).substring(2, 11)}`;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Generate simple ID with timestamp and random (no counter tracking)
|
|
53
53
|
*
|
|
54
|
-
* Format: `${prefix}
|
|
54
|
+
* Format: `${prefix}:${timestamp}_${random}`
|
|
55
55
|
*
|
|
56
56
|
* @param prefix - Prefix for the ID
|
|
57
57
|
* @returns Branded ID string
|
|
@@ -59,11 +59,12 @@ function generateId(prefix) {
|
|
|
59
59
|
* @example
|
|
60
60
|
* ```ts
|
|
61
61
|
* const id = generateSimpleId('CounterBloc');
|
|
62
|
-
* // Returns: "
|
|
62
|
+
* // Returns: "CounterBloc:1698765432100_a3k9d7f2q"
|
|
63
63
|
* ```
|
|
64
64
|
*/
|
|
65
|
-
function generateSimpleId(prefix) {
|
|
66
|
-
return `${prefix}
|
|
65
|
+
function generateSimpleId(prefix, affix) {
|
|
66
|
+
if (affix) return `${prefix}:${affix}`;
|
|
67
|
+
return `${prefix}:${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
67
68
|
}
|
|
68
69
|
/**
|
|
69
70
|
* Reset all counters for testing purposes
|
|
@@ -72,129 +73,653 @@ function generateSimpleId(prefix) {
|
|
|
72
73
|
function __resetIdCounters() {
|
|
73
74
|
globalCounters.clear();
|
|
74
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Generate a unique isolated instance key
|
|
78
|
+
* Uses base36 encoding for compact, URL-safe identifiers
|
|
79
|
+
*
|
|
80
|
+
* Format: "isolated-{9-char-random-string}"
|
|
81
|
+
* Example: "isolated-k7x2m9p4q"
|
|
82
|
+
*
|
|
83
|
+
* @returns A unique isolated instance key
|
|
84
|
+
*/
|
|
85
|
+
function generateIsolatedKey() {
|
|
86
|
+
return `isolated-${Math.random().toString(36).slice(2, 11)}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if a key is an isolated instance key
|
|
90
|
+
* @param key - The instance key to check
|
|
91
|
+
* @returns true if the key is an isolated instance key
|
|
92
|
+
*/
|
|
93
|
+
function isIsolatedKey(key) {
|
|
94
|
+
return key.startsWith("isolated-");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/plugin/PluginManager.ts
|
|
99
|
+
/**
|
|
100
|
+
* Plugin manager for registering and coordinating plugins
|
|
101
|
+
*/
|
|
102
|
+
var PluginManager = class {
|
|
103
|
+
constructor(registry) {
|
|
104
|
+
this.plugins = /* @__PURE__ */ new Map();
|
|
105
|
+
this.registry = registry;
|
|
106
|
+
this.setupLifecycleHooks();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Install a plugin
|
|
110
|
+
*/
|
|
111
|
+
install(plugin, config = {}) {
|
|
112
|
+
const effectiveConfig = {
|
|
113
|
+
enabled: true,
|
|
114
|
+
environment: "all",
|
|
115
|
+
...config
|
|
116
|
+
};
|
|
117
|
+
if (!this.shouldEnablePlugin(effectiveConfig)) {
|
|
118
|
+
console.log(`[BlaC] Plugin "${plugin.name}" skipped (environment mismatch)`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (this.plugins.has(plugin.name)) throw new Error(`Plugin "${plugin.name}" is already installed`);
|
|
122
|
+
const context = this.createPluginContext();
|
|
123
|
+
this.plugins.set(plugin.name, {
|
|
124
|
+
plugin,
|
|
125
|
+
config: effectiveConfig,
|
|
126
|
+
context
|
|
127
|
+
});
|
|
128
|
+
if (plugin.onInstall) try {
|
|
129
|
+
plugin.onInstall(context);
|
|
130
|
+
} catch (error$1) {
|
|
131
|
+
console.error(`[BlaC] Error installing plugin "${plugin.name}":`, error$1);
|
|
132
|
+
this.plugins.delete(plugin.name);
|
|
133
|
+
throw error$1;
|
|
134
|
+
}
|
|
135
|
+
console.log(`[BlaC] Plugin "${plugin.name}" v${plugin.version} installed`);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Uninstall a plugin
|
|
139
|
+
*/
|
|
140
|
+
uninstall(pluginName) {
|
|
141
|
+
const installed = this.plugins.get(pluginName);
|
|
142
|
+
if (!installed) throw new Error(`Plugin "${pluginName}" is not installed`);
|
|
143
|
+
if (installed.plugin.onUninstall) try {
|
|
144
|
+
installed.plugin.onUninstall();
|
|
145
|
+
} catch (error$1) {
|
|
146
|
+
console.error(`[BlaC] Error uninstalling plugin "${pluginName}":`, error$1);
|
|
147
|
+
}
|
|
148
|
+
this.plugins.delete(pluginName);
|
|
149
|
+
console.log(`[BlaC] Plugin "${pluginName}" uninstalled`);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get installed plugin
|
|
153
|
+
*/
|
|
154
|
+
getPlugin(pluginName) {
|
|
155
|
+
return this.plugins.get(pluginName)?.plugin;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get all installed plugins
|
|
159
|
+
*/
|
|
160
|
+
getAllPlugins() {
|
|
161
|
+
return Array.from(this.plugins.values()).map((p) => p.plugin);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if plugin is installed
|
|
165
|
+
*/
|
|
166
|
+
hasPlugin(pluginName) {
|
|
167
|
+
return this.plugins.has(pluginName);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Clear all plugins (for testing)
|
|
171
|
+
*/
|
|
172
|
+
clear() {
|
|
173
|
+
for (const name of this.plugins.keys()) this.uninstall(name);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Setup lifecycle hooks to notify plugins
|
|
177
|
+
*/
|
|
178
|
+
setupLifecycleHooks() {
|
|
179
|
+
this.registry.on("created", (instance) => {
|
|
180
|
+
this.notifyPlugins("onInstanceCreated", instance);
|
|
181
|
+
});
|
|
182
|
+
this.registry.on("stateChanged", (instance, previousState, currentState, callstack) => {
|
|
183
|
+
this.notifyPlugins("onStateChanged", instance, previousState, currentState, callstack);
|
|
184
|
+
});
|
|
185
|
+
this.registry.on("eventAdded", (vertex, event) => {
|
|
186
|
+
this.notifyPlugins("onEventAdded", vertex, event);
|
|
187
|
+
});
|
|
188
|
+
this.registry.on("disposed", (instance) => {
|
|
189
|
+
this.notifyPlugins("onInstanceDisposed", instance);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Notify all plugins of a lifecycle event
|
|
194
|
+
*/
|
|
195
|
+
notifyPlugins(hookName, ...args) {
|
|
196
|
+
for (const { plugin, config, context } of this.plugins.values()) {
|
|
197
|
+
if (!config.enabled) continue;
|
|
198
|
+
const hook = plugin[hookName];
|
|
199
|
+
if (typeof hook === "function") try {
|
|
200
|
+
hook.apply(plugin, [...args, context]);
|
|
201
|
+
} catch (error$1) {
|
|
202
|
+
console.error(`[BlaC] Error in plugin "${plugin.name}" ${hookName}:`, error$1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Create plugin context with safe API access
|
|
208
|
+
*/
|
|
209
|
+
createPluginContext() {
|
|
210
|
+
return {
|
|
211
|
+
getInstanceMetadata: (instance) => {
|
|
212
|
+
return {
|
|
213
|
+
id: instance.instanceId,
|
|
214
|
+
className: instance.constructor.name,
|
|
215
|
+
isDisposed: instance.isDisposed,
|
|
216
|
+
name: instance.name,
|
|
217
|
+
lastStateChangeTimestamp: instance.lastUpdateTimestamp,
|
|
218
|
+
createdAt: instance.createdAt,
|
|
219
|
+
state: instance.state,
|
|
220
|
+
isIsolated: instance.instanceId.startsWith("isolated-")
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
getState: (instance) => {
|
|
224
|
+
return instance.state;
|
|
225
|
+
},
|
|
226
|
+
queryInstances: (typeClass) => {
|
|
227
|
+
return typeClass.getAll();
|
|
228
|
+
},
|
|
229
|
+
getAllTypes: () => {
|
|
230
|
+
return this.registry.getTypes();
|
|
231
|
+
},
|
|
232
|
+
getStats: () => {
|
|
233
|
+
return this.registry.getStats();
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Check if plugin should be enabled based on environment
|
|
239
|
+
*/
|
|
240
|
+
shouldEnablePlugin(config) {
|
|
241
|
+
if (!config.enabled) return false;
|
|
242
|
+
if (config.environment === "all") return true;
|
|
243
|
+
return this.getCurrentEnvironment() === config.environment;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get current environment
|
|
247
|
+
*/
|
|
248
|
+
getCurrentEnvironment() {
|
|
249
|
+
if (typeof process !== "undefined") {
|
|
250
|
+
if (process.env.NODE_ENV === "test") return "test";
|
|
251
|
+
if (process.env.NODE_ENV === "production") return "production";
|
|
252
|
+
return "development";
|
|
253
|
+
}
|
|
254
|
+
return "development";
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Create a plugin manager instance
|
|
259
|
+
*/
|
|
260
|
+
function createPluginManager(registry) {
|
|
261
|
+
return new PluginManager(registry);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/constants.ts
|
|
266
|
+
/**
|
|
267
|
+
* Default configuration constants for BlaC
|
|
268
|
+
*
|
|
269
|
+
* Centralized location for all magic numbers and default values.
|
|
270
|
+
*/
|
|
271
|
+
/**
|
|
272
|
+
* Default configuration constants for BlaC
|
|
273
|
+
*/
|
|
274
|
+
const BLAC_DEFAULTS = {
|
|
275
|
+
DEFAULT_INSTANCE_KEY: "default",
|
|
276
|
+
MAX_GETTER_DEPTH: 10,
|
|
277
|
+
CLEANUP_INTERVAL_MS: 3e4,
|
|
278
|
+
WEAKREF_CLEANUP_INTERVAL_MS: 1e4,
|
|
279
|
+
MAX_SUBSCRIPTIONS: 1e3,
|
|
280
|
+
MAX_SUBSCRIPTIONS_HIGH_PERF: 1e4,
|
|
281
|
+
PIPELINE_TIMEOUT_MS: 5e3,
|
|
282
|
+
CLEANUP_INTERVAL_HIGH_PERF_MS: 5e3,
|
|
283
|
+
MAX_PIPELINE_STAGES: 30
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Static property names for StateContainer classes
|
|
287
|
+
* Used for feature flags and configuration on bloc classes
|
|
288
|
+
*/
|
|
289
|
+
const BLAC_STATIC_PROPS = {
|
|
290
|
+
ISOLATED: "isolated",
|
|
291
|
+
KEEP_ALIVE: "keepAlive",
|
|
292
|
+
EXCLUDE_FROM_DEVTOOLS: "__excludeFromDevTools"
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* ID generation patterns and constants
|
|
296
|
+
*/
|
|
297
|
+
const BLAC_ID_PATTERNS = {
|
|
298
|
+
ISOLATED_PREFIX: "isolated-",
|
|
299
|
+
ID_LENGTH: 9
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* Standard error message prefix
|
|
303
|
+
*/
|
|
304
|
+
const BLAC_ERROR_PREFIX = "[BlaC]";
|
|
305
|
+
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/tracking/getter-tracker.ts
|
|
308
|
+
const descriptorCache = /* @__PURE__ */ new WeakMap();
|
|
309
|
+
const blocProxyCache = /* @__PURE__ */ new WeakMap();
|
|
310
|
+
const activeTrackerMap = /* @__PURE__ */ new WeakMap();
|
|
311
|
+
const getterExecutionContext = {
|
|
312
|
+
tracker: null,
|
|
313
|
+
currentBloc: null,
|
|
314
|
+
depth: 0,
|
|
315
|
+
visitedBlocs: /* @__PURE__ */ new Set()
|
|
316
|
+
};
|
|
317
|
+
const MAX_GETTER_DEPTH = BLAC_DEFAULTS.MAX_GETTER_DEPTH;
|
|
318
|
+
function getGetterExecutionContext() {
|
|
319
|
+
return getterExecutionContext;
|
|
320
|
+
}
|
|
321
|
+
function getDescriptor(obj, prop) {
|
|
322
|
+
const constructor = obj.constructor;
|
|
323
|
+
let constructorCache = descriptorCache.get(constructor);
|
|
324
|
+
if (constructorCache?.has(prop)) return constructorCache.get(prop);
|
|
325
|
+
let current = obj;
|
|
326
|
+
let descriptor;
|
|
327
|
+
while (current && current !== Object.prototype) {
|
|
328
|
+
descriptor = Object.getOwnPropertyDescriptor(current, prop);
|
|
329
|
+
if (descriptor) break;
|
|
330
|
+
current = Object.getPrototypeOf(current);
|
|
331
|
+
}
|
|
332
|
+
if (!constructorCache) {
|
|
333
|
+
constructorCache = /* @__PURE__ */ new Map();
|
|
334
|
+
descriptorCache.set(constructor, constructorCache);
|
|
335
|
+
}
|
|
336
|
+
constructorCache.set(prop, descriptor);
|
|
337
|
+
return descriptor;
|
|
338
|
+
}
|
|
339
|
+
function isGetter(obj, prop) {
|
|
340
|
+
return getDescriptor(obj, prop)?.get !== void 0;
|
|
341
|
+
}
|
|
342
|
+
function createGetterTracker() {
|
|
343
|
+
return {
|
|
344
|
+
trackedValues: /* @__PURE__ */ new Map(),
|
|
345
|
+
currentlyAccessing: /* @__PURE__ */ new Set(),
|
|
346
|
+
trackedGetters: /* @__PURE__ */ new Set(),
|
|
347
|
+
isTracking: false,
|
|
348
|
+
renderCache: /* @__PURE__ */ new Map(),
|
|
349
|
+
cacheValid: false,
|
|
350
|
+
externalDependencies: /* @__PURE__ */ new Set()
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function setActiveTracker(bloc, tracker) {
|
|
354
|
+
activeTrackerMap.set(bloc, tracker);
|
|
355
|
+
}
|
|
356
|
+
function clearActiveTracker(bloc) {
|
|
357
|
+
activeTrackerMap.delete(bloc);
|
|
358
|
+
}
|
|
359
|
+
function getActiveTracker(bloc) {
|
|
360
|
+
return activeTrackerMap.get(bloc);
|
|
361
|
+
}
|
|
362
|
+
function commitTrackedGetters(tracker) {
|
|
363
|
+
if (tracker.currentlyAccessing.size > 0) tracker.trackedGetters = new Set(tracker.currentlyAccessing);
|
|
364
|
+
tracker.currentlyAccessing.clear();
|
|
365
|
+
}
|
|
366
|
+
function createBlocProxy(bloc) {
|
|
367
|
+
const cached = blocProxyCache.get(bloc);
|
|
368
|
+
if (cached) return cached;
|
|
369
|
+
const proxy = new Proxy(bloc, { get(target, prop, receiver) {
|
|
370
|
+
const tracker = activeTrackerMap.get(target);
|
|
371
|
+
if (tracker?.isTracking && isGetter(target, prop)) {
|
|
372
|
+
tracker.currentlyAccessing.add(prop);
|
|
373
|
+
if (tracker.cacheValid && tracker.renderCache.has(prop)) {
|
|
374
|
+
const cachedValue = tracker.renderCache.get(prop);
|
|
375
|
+
tracker.trackedValues.set(prop, cachedValue);
|
|
376
|
+
return cachedValue;
|
|
377
|
+
}
|
|
378
|
+
if (getterExecutionContext.depth >= MAX_GETTER_DEPTH) {
|
|
379
|
+
console.warn(`${BLAC_ERROR_PREFIX} Maximum getter depth (${MAX_GETTER_DEPTH}) exceeded. Possible circular dependency in getter "${String(prop)}" on ${target.constructor.name}. Returning undefined to prevent stack overflow.`);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (getterExecutionContext.visitedBlocs.has(target)) {
|
|
383
|
+
console.warn(`${BLAC_ERROR_PREFIX} Circular dependency detected: getter "${String(prop)}" on ${target.constructor.name} is calling back into itself. Returning undefined to prevent infinite recursion.`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const prevContext = {
|
|
387
|
+
tracker: getterExecutionContext.tracker,
|
|
388
|
+
currentBloc: getterExecutionContext.currentBloc,
|
|
389
|
+
depth: getterExecutionContext.depth,
|
|
390
|
+
visitedBlocs: new Set(getterExecutionContext.visitedBlocs)
|
|
391
|
+
};
|
|
392
|
+
getterExecutionContext.tracker = tracker;
|
|
393
|
+
getterExecutionContext.currentBloc = target;
|
|
394
|
+
getterExecutionContext.depth++;
|
|
395
|
+
getterExecutionContext.visitedBlocs.add(target);
|
|
396
|
+
try {
|
|
397
|
+
const value = getDescriptor(target, prop).get.call(target);
|
|
398
|
+
tracker.trackedValues.set(prop, value);
|
|
399
|
+
return value;
|
|
400
|
+
} finally {
|
|
401
|
+
getterExecutionContext.tracker = prevContext.tracker;
|
|
402
|
+
getterExecutionContext.currentBloc = prevContext.currentBloc;
|
|
403
|
+
getterExecutionContext.depth = prevContext.depth;
|
|
404
|
+
getterExecutionContext.visitedBlocs = prevContext.visitedBlocs;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return Reflect.get(target, prop, receiver);
|
|
408
|
+
} });
|
|
409
|
+
blocProxyCache.set(bloc, proxy);
|
|
410
|
+
return proxy;
|
|
411
|
+
}
|
|
412
|
+
function hasGetterChanges(bloc, tracker) {
|
|
413
|
+
if (!tracker || tracker.trackedGetters.size === 0) return false;
|
|
414
|
+
tracker.renderCache.clear();
|
|
415
|
+
let hasAnyChange = false;
|
|
416
|
+
for (const prop of tracker.trackedGetters) try {
|
|
417
|
+
const descriptor = getDescriptor(bloc, prop);
|
|
418
|
+
if (!descriptor?.get) continue;
|
|
419
|
+
const newValue = descriptor.get.call(bloc);
|
|
420
|
+
const oldValue = tracker.trackedValues.get(prop);
|
|
421
|
+
tracker.renderCache.set(prop, newValue);
|
|
422
|
+
tracker.trackedValues.set(prop, newValue);
|
|
423
|
+
if (!Object.is(newValue, oldValue)) hasAnyChange = true;
|
|
424
|
+
} catch (error$1) {
|
|
425
|
+
console.warn(`${BLAC_ERROR_PREFIX} Getter "${String(prop)}" threw error during change detection. Stopping tracking for this getter.`, error$1);
|
|
426
|
+
tracker.trackedGetters.delete(prop);
|
|
427
|
+
tracker.trackedValues.delete(prop);
|
|
428
|
+
tracker.cacheValid = false;
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
tracker.cacheValid = true;
|
|
432
|
+
return hasAnyChange;
|
|
433
|
+
}
|
|
434
|
+
function invalidateRenderCache(tracker) {
|
|
435
|
+
tracker.cacheValid = false;
|
|
436
|
+
}
|
|
437
|
+
function clearExternalDependencies(tracker) {
|
|
438
|
+
tracker.externalDependencies.clear();
|
|
439
|
+
}
|
|
440
|
+
function resetGetterTracker(tracker) {
|
|
441
|
+
tracker.trackedValues.clear();
|
|
442
|
+
tracker.currentlyAccessing.clear();
|
|
443
|
+
tracker.trackedGetters.clear();
|
|
444
|
+
tracker.renderCache.clear();
|
|
445
|
+
tracker.cacheValid = false;
|
|
446
|
+
tracker.isTracking = false;
|
|
447
|
+
tracker.externalDependencies.clear();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region src/utils/static-props.ts
|
|
452
|
+
/**
|
|
453
|
+
* Utility functions for accessing static properties on StateContainer classes
|
|
454
|
+
*/
|
|
455
|
+
/**
|
|
456
|
+
* Get a static property from a class constructor
|
|
457
|
+
* Type-safe helper that avoids (Type as any) casts
|
|
458
|
+
*
|
|
459
|
+
* @param Type - The class constructor
|
|
460
|
+
* @param propName - The property name to access
|
|
461
|
+
* @param defaultValue - Optional default value if property is undefined
|
|
462
|
+
* @returns The property value or default
|
|
463
|
+
*/
|
|
464
|
+
function getStaticProp(Type, propName, defaultValue) {
|
|
465
|
+
return Type[propName] ?? defaultValue;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Check if a class is marked as isolated
|
|
469
|
+
* Isolated classes create separate instances per component
|
|
470
|
+
*/
|
|
471
|
+
function isIsolatedClass(Type) {
|
|
472
|
+
return getStaticProp(Type, BLAC_STATIC_PROPS.ISOLATED) === true;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Check if a class is marked as keepAlive
|
|
476
|
+
* KeepAlive classes are never auto-disposed when ref count reaches 0
|
|
477
|
+
*/
|
|
478
|
+
function isKeepAliveClass(Type) {
|
|
479
|
+
return getStaticProp(Type, BLAC_STATIC_PROPS.KEEP_ALIVE) === true;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Check if a class should be excluded from DevTools
|
|
483
|
+
* Used to prevent infinite loops when DevTools tracks itself
|
|
484
|
+
*/
|
|
485
|
+
function isExcludedFromDevTools(Type) {
|
|
486
|
+
return getStaticProp(Type, BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS) === true;
|
|
487
|
+
}
|
|
75
488
|
|
|
76
489
|
//#endregion
|
|
77
490
|
//#region src/core/StateContainerRegistry.ts
|
|
78
491
|
/**
|
|
79
|
-
* Registry for
|
|
492
|
+
* Registry for coordinating StateContainer lifecycle and managing instances
|
|
493
|
+
*
|
|
494
|
+
* Centralizes all instance management using WeakMap to ensure proper isolation.
|
|
80
495
|
*/
|
|
81
496
|
var StateContainerRegistry = class {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
497
|
+
constructor() {
|
|
498
|
+
this.instancesByConstructor = /* @__PURE__ */ new WeakMap();
|
|
499
|
+
this.types = /* @__PURE__ */ new Set();
|
|
500
|
+
this.typeConfigs = /* @__PURE__ */ new Map();
|
|
501
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
502
|
+
}
|
|
85
503
|
/**
|
|
86
|
-
* Register a type
|
|
504
|
+
* Register a type for tracking
|
|
505
|
+
* Called automatically on first instance creation
|
|
506
|
+
*/
|
|
507
|
+
registerType(constructor) {
|
|
508
|
+
this.types.add(constructor);
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Register a type with isolation mode (explicit registration)
|
|
87
512
|
*/
|
|
88
513
|
register(constructor, isolated = false) {
|
|
89
514
|
const className = constructor.name;
|
|
90
|
-
if (!isolated && constructor
|
|
91
|
-
if (this.
|
|
92
|
-
this.
|
|
515
|
+
if (!isolated && isIsolatedClass(constructor)) isolated = true;
|
|
516
|
+
if (this.typeConfigs.has(className)) throw new Error(`${BLAC_ERROR_PREFIX} Type "${className}" is already registered`);
|
|
517
|
+
this.typeConfigs.set(className, { isolated });
|
|
518
|
+
this.registerType(constructor);
|
|
93
519
|
}
|
|
94
520
|
/**
|
|
95
|
-
* Get
|
|
96
|
-
*
|
|
97
|
-
* For shared: singleton per key
|
|
521
|
+
* Get the instances Map for a specific class
|
|
522
|
+
* Creates a new Map on first access.
|
|
98
523
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
524
|
+
ensureInstancesMap(Type) {
|
|
525
|
+
let instances = this.instancesByConstructor.get(Type);
|
|
526
|
+
if (!instances) {
|
|
527
|
+
instances = /* @__PURE__ */ new Map();
|
|
528
|
+
this.instancesByConstructor.set(Type, instances);
|
|
529
|
+
}
|
|
530
|
+
return instances;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Get the instances Map for a specific class (public API for stats/debugging)
|
|
534
|
+
*/
|
|
535
|
+
getInstancesMap(Type) {
|
|
536
|
+
return this.instancesByConstructor.get(Type) || /* @__PURE__ */ new Map();
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Resolve an instance with ref counting (ownership semantics)
|
|
540
|
+
*/
|
|
541
|
+
resolve(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
|
|
542
|
+
const registryConfig = this.typeConfigs.get(Type.name);
|
|
543
|
+
const isolated = isIsolatedClass(Type) || registryConfig?.isolated === true;
|
|
544
|
+
const config = { instanceId: instanceKey };
|
|
545
|
+
if (isolated) {
|
|
546
|
+
const instance$1 = new Type(constructorArgs);
|
|
547
|
+
instance$1.initiConfig(config);
|
|
548
|
+
this.registerType(Type);
|
|
549
|
+
return instance$1;
|
|
106
550
|
}
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return existing.instance;
|
|
551
|
+
const instances = this.ensureInstancesMap(Type);
|
|
552
|
+
const entry = instances.get(instanceKey);
|
|
553
|
+
if (entry) {
|
|
554
|
+
entry.refCount++;
|
|
555
|
+
return entry.instance;
|
|
113
556
|
}
|
|
114
|
-
const instance = new
|
|
115
|
-
|
|
557
|
+
const instance = new Type(constructorArgs);
|
|
558
|
+
instance.initiConfig(config);
|
|
559
|
+
instances.set(instanceKey, {
|
|
116
560
|
instance,
|
|
117
561
|
refCount: 1
|
|
118
562
|
});
|
|
563
|
+
this.registerType(Type);
|
|
119
564
|
return instance;
|
|
120
565
|
}
|
|
121
566
|
/**
|
|
567
|
+
* Get an existing instance without ref counting (borrowing semantics)
|
|
568
|
+
* @throws Error if instance doesn't exist
|
|
569
|
+
*/
|
|
570
|
+
get(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
571
|
+
const entry = this.ensureInstancesMap(Type).get(instanceKey);
|
|
572
|
+
if (!entry) throw new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found.\nUse .resolve() to create and claim ownership, or .getSafe() for conditional access.`);
|
|
573
|
+
const context = getGetterExecutionContext();
|
|
574
|
+
if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
|
|
575
|
+
return entry.instance;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Safely get an existing instance (borrowing semantics with error handling)
|
|
579
|
+
* Returns discriminated union for type-safe conditional access
|
|
580
|
+
*/
|
|
581
|
+
getSafe(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
582
|
+
const entry = this.ensureInstancesMap(Type).get(instanceKey);
|
|
583
|
+
if (!entry) return {
|
|
584
|
+
error: /* @__PURE__ */ new Error(`${BLAC_ERROR_PREFIX} ${Type.name} instance "${instanceKey}" not found`),
|
|
585
|
+
instance: null
|
|
586
|
+
};
|
|
587
|
+
const context = getGetterExecutionContext();
|
|
588
|
+
if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
|
|
589
|
+
return {
|
|
590
|
+
error: null,
|
|
591
|
+
instance: entry.instance
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Connect to an instance with borrowing semantics (for B2B communication)
|
|
596
|
+
* Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
|
|
597
|
+
* Tracks cross-bloc dependency for reactive updates.
|
|
598
|
+
*
|
|
599
|
+
* Use this in bloc-to-bloc communication when you need to ensure an instance exists
|
|
600
|
+
* but don't want to claim ownership (no ref count increment).
|
|
601
|
+
*
|
|
602
|
+
* @param Type - The bloc class constructor
|
|
603
|
+
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
604
|
+
* @param constructorArgs - Constructor arguments (only used if creating new instance)
|
|
605
|
+
* @returns The bloc instance
|
|
606
|
+
*/
|
|
607
|
+
connect(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, constructorArgs) {
|
|
608
|
+
const registryConfig = this.typeConfigs.get(Type.name);
|
|
609
|
+
if (isIsolatedClass(Type) || registryConfig?.isolated === true) throw new Error(`${BLAC_ERROR_PREFIX} Cannot use .connect() with isolated bloc "${Type.name}".\nIsolated blocs are component-scoped. Use .resolve() in components instead.`);
|
|
610
|
+
const instances = this.ensureInstancesMap(Type);
|
|
611
|
+
let entry = instances.get(instanceKey);
|
|
612
|
+
if (!entry) {
|
|
613
|
+
const instance = new Type(constructorArgs);
|
|
614
|
+
const config = { instanceId: instanceKey };
|
|
615
|
+
instance.initiConfig(config);
|
|
616
|
+
entry = {
|
|
617
|
+
instance,
|
|
618
|
+
refCount: 1
|
|
619
|
+
};
|
|
620
|
+
instances.set(instanceKey, entry);
|
|
621
|
+
this.registerType(Type);
|
|
622
|
+
}
|
|
623
|
+
const context = getGetterExecutionContext();
|
|
624
|
+
if (context.tracker && context.currentBloc && context.currentBloc !== entry.instance) context.tracker.externalDependencies.add(entry.instance);
|
|
625
|
+
return entry.instance;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
122
628
|
* Release a reference to an instance
|
|
123
|
-
* Disposes when ref count reaches zero (unless static keepAlive is true)
|
|
124
629
|
*/
|
|
125
|
-
release(
|
|
126
|
-
const
|
|
127
|
-
const entry =
|
|
630
|
+
release(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY, forceDispose = false) {
|
|
631
|
+
const instances = this.ensureInstancesMap(Type);
|
|
632
|
+
const entry = instances.get(instanceKey);
|
|
128
633
|
if (!entry) return;
|
|
129
634
|
if (forceDispose) {
|
|
130
|
-
entry.instance.dispose();
|
|
131
|
-
|
|
635
|
+
if (!entry.instance.isDisposed) entry.instance.dispose();
|
|
636
|
+
instances.delete(instanceKey);
|
|
132
637
|
return;
|
|
133
638
|
}
|
|
134
639
|
entry.refCount--;
|
|
135
|
-
const keepAlive =
|
|
640
|
+
const keepAlive = isKeepAliveClass(Type);
|
|
136
641
|
if (entry.refCount <= 0 && !keepAlive) {
|
|
137
|
-
entry.instance.dispose();
|
|
138
|
-
|
|
642
|
+
if (!entry.instance.isDisposed) entry.instance.dispose();
|
|
643
|
+
instances.delete(instanceKey);
|
|
139
644
|
}
|
|
140
645
|
}
|
|
141
646
|
/**
|
|
142
|
-
* Get all instances of a type
|
|
647
|
+
* Get all instances of a specific type
|
|
143
648
|
*/
|
|
144
|
-
getAll(
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
for (const
|
|
148
|
-
return
|
|
649
|
+
getAll(Type) {
|
|
650
|
+
const instances = this.ensureInstancesMap(Type);
|
|
651
|
+
const result = [];
|
|
652
|
+
for (const entry of instances.values()) result.push(entry.instance);
|
|
653
|
+
return result;
|
|
149
654
|
}
|
|
150
655
|
/**
|
|
151
|
-
*
|
|
656
|
+
* Safely iterate over all instances of a type
|
|
152
657
|
*/
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
if (!
|
|
158
|
-
|
|
658
|
+
forEach(Type, callback) {
|
|
659
|
+
const instances = this.ensureInstancesMap(Type);
|
|
660
|
+
for (const entry of instances.values()) {
|
|
661
|
+
const instance = entry.instance;
|
|
662
|
+
if (!instance.isDisposed) try {
|
|
663
|
+
callback(instance);
|
|
664
|
+
} catch (error$1) {
|
|
665
|
+
console.error(`${BLAC_ERROR_PREFIX} forEach callback error for ${Type.name}:`, error$1);
|
|
666
|
+
}
|
|
159
667
|
}
|
|
160
|
-
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Clear all instances of a specific type
|
|
671
|
+
*/
|
|
672
|
+
clear(Type) {
|
|
673
|
+
const instances = this.ensureInstancesMap(Type);
|
|
674
|
+
for (const entry of instances.values()) if (!entry.instance.isDisposed) entry.instance.dispose();
|
|
675
|
+
instances.clear();
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Get reference count for an instance
|
|
679
|
+
*/
|
|
680
|
+
getRefCount(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
681
|
+
return this.ensureInstancesMap(Type).get(instanceKey)?.refCount ?? 0;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Check if an instance exists
|
|
685
|
+
*/
|
|
686
|
+
hasInstance(Type, instanceKey = BLAC_DEFAULTS.DEFAULT_INSTANCE_KEY) {
|
|
687
|
+
return this.ensureInstancesMap(Type).has(instanceKey);
|
|
161
688
|
}
|
|
162
689
|
/**
|
|
163
690
|
* Clear all instances from all types (for testing)
|
|
691
|
+
*
|
|
692
|
+
* Iterates all registered types and clears their instances.
|
|
693
|
+
* Also clears type tracking to reset the registry state.
|
|
164
694
|
*/
|
|
165
695
|
clearAll() {
|
|
166
|
-
for (const
|
|
167
|
-
this.instances.clear();
|
|
696
|
+
for (const Type of this.types) this.clear(Type);
|
|
168
697
|
this.types.clear();
|
|
698
|
+
this.typeConfigs.clear();
|
|
169
699
|
}
|
|
170
700
|
/**
|
|
171
701
|
* Get registry statistics (for debugging)
|
|
172
702
|
*/
|
|
173
703
|
getStats() {
|
|
174
704
|
const typeBreakdown = {};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
705
|
+
let totalInstances = 0;
|
|
706
|
+
for (const Type of this.types) {
|
|
707
|
+
const typeName = Type.name;
|
|
708
|
+
const count = this.getInstancesMap(Type).size;
|
|
709
|
+
typeBreakdown[typeName] = count;
|
|
710
|
+
totalInstances += count;
|
|
178
711
|
}
|
|
179
712
|
return {
|
|
180
713
|
registeredTypes: this.types.size,
|
|
181
|
-
totalInstances
|
|
714
|
+
totalInstances,
|
|
182
715
|
typeBreakdown
|
|
183
716
|
};
|
|
184
717
|
}
|
|
185
718
|
/**
|
|
186
|
-
* Get
|
|
187
|
-
*/
|
|
188
|
-
getRefCount(constructor, key) {
|
|
189
|
-
const instanceKey = `${constructor.name}:${key || "default"}`;
|
|
190
|
-
return this.instances.get(instanceKey)?.refCount ?? 0;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Check if an instance exists
|
|
719
|
+
* Get all registered types (for plugin system)
|
|
194
720
|
*/
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return this.instances.has(instanceKey);
|
|
721
|
+
getTypes() {
|
|
722
|
+
return Array.from(this.types);
|
|
198
723
|
}
|
|
199
724
|
/**
|
|
200
725
|
* Subscribe to lifecycle events
|
|
@@ -219,7 +744,7 @@ var StateContainerRegistry = class {
|
|
|
219
744
|
for (const listener of listeners) try {
|
|
220
745
|
listener(...args);
|
|
221
746
|
} catch (error$1) {
|
|
222
|
-
console.error(
|
|
747
|
+
console.error(`${BLAC_ERROR_PREFIX} Listener error for '${event}':`, error$1);
|
|
223
748
|
}
|
|
224
749
|
}
|
|
225
750
|
};
|
|
@@ -227,18 +752,51 @@ var StateContainerRegistry = class {
|
|
|
227
752
|
* Global default registry instance
|
|
228
753
|
*/
|
|
229
754
|
const globalRegistry = new StateContainerRegistry();
|
|
755
|
+
/**
|
|
756
|
+
* Global plugin manager (initialized lazily)
|
|
757
|
+
*/
|
|
758
|
+
let _globalPluginManager = null;
|
|
759
|
+
/**
|
|
760
|
+
* Get the global plugin manager
|
|
761
|
+
*/
|
|
762
|
+
function getPluginManager() {
|
|
763
|
+
if (!_globalPluginManager) _globalPluginManager = createPluginManager(globalRegistry);
|
|
764
|
+
return _globalPluginManager;
|
|
765
|
+
}
|
|
230
766
|
|
|
231
767
|
//#endregion
|
|
232
768
|
//#region src/core/StateContainer.ts
|
|
233
769
|
/**
|
|
770
|
+
* StateContainer - Clean, minimal state management container
|
|
771
|
+
*
|
|
772
|
+
* Responsibilities:
|
|
773
|
+
* - State storage and updates
|
|
774
|
+
* - Change notifications to subscribers
|
|
775
|
+
* - Lifecycle management
|
|
776
|
+
*/
|
|
777
|
+
/**
|
|
234
778
|
* Base abstract class for all state containers
|
|
235
779
|
*/
|
|
236
780
|
var StateContainer = class StateContainer {
|
|
237
|
-
static
|
|
781
|
+
static {
|
|
782
|
+
this.__excludeFromDevTools = false;
|
|
783
|
+
}
|
|
784
|
+
static {
|
|
785
|
+
this._registry = globalRegistry;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Get the global registry (mainly for testing)
|
|
789
|
+
*/
|
|
790
|
+
static getRegistry() {
|
|
791
|
+
return StateContainer._registry;
|
|
792
|
+
}
|
|
238
793
|
/**
|
|
239
794
|
* Set a custom registry (mainly for testing)
|
|
795
|
+
*
|
|
796
|
+
* Clears all instances before switching to ensure clean test isolation.
|
|
240
797
|
*/
|
|
241
798
|
static setRegistry(registry) {
|
|
799
|
+
StateContainer._registry.clearAll();
|
|
242
800
|
StateContainer._registry = registry;
|
|
243
801
|
}
|
|
244
802
|
/**
|
|
@@ -248,75 +806,123 @@ var StateContainer = class StateContainer {
|
|
|
248
806
|
StateContainer._registry.register(this, isolated);
|
|
249
807
|
}
|
|
250
808
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
809
|
+
* Resolve an instance with ref counting (ownership semantics)
|
|
810
|
+
*
|
|
811
|
+
* Delegates to the global registry for instance management.
|
|
812
|
+
*
|
|
813
|
+
* @param instanceKey - Optional instance key for shared instances
|
|
814
|
+
* @param constructorArgs - Constructor arguments
|
|
815
|
+
* @returns Instance with incremented ref count
|
|
816
|
+
*/
|
|
817
|
+
static resolve(instanceKey, constructorArgs) {
|
|
818
|
+
return StateContainer._registry.resolve(this, instanceKey, constructorArgs);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Get an existing instance without ref counting (borrowing semantics)
|
|
822
|
+
* Delegates to the global registry.
|
|
254
823
|
*/
|
|
255
|
-
static
|
|
256
|
-
return StateContainer._registry.
|
|
824
|
+
static get(instanceKey) {
|
|
825
|
+
return StateContainer._registry.get(this, instanceKey);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Safely get an existing instance (borrowing semantics with error handling)
|
|
829
|
+
* Delegates to the global registry.
|
|
830
|
+
*/
|
|
831
|
+
static getSafe(instanceKey) {
|
|
832
|
+
return StateContainer._registry.getSafe(this, instanceKey);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Connect to an instance with borrowing semantics (for B2B communication)
|
|
836
|
+
* Gets existing instance OR creates it if it doesn't exist, without incrementing ref count.
|
|
837
|
+
* Tracks cross-bloc dependency for reactive updates.
|
|
838
|
+
*
|
|
839
|
+
* Use this in bloc-to-bloc communication when you need to ensure an instance exists
|
|
840
|
+
* but don't want to claim ownership (no ref count increment).
|
|
841
|
+
*
|
|
842
|
+
* Delegates to the global registry.
|
|
843
|
+
*
|
|
844
|
+
* @param instanceKey - Optional instance key (defaults to 'default')
|
|
845
|
+
* @param constructorArgs - Constructor arguments (only used if creating new instance)
|
|
846
|
+
* @returns The bloc instance
|
|
847
|
+
*/
|
|
848
|
+
static connect(instanceKey, constructorArgs) {
|
|
849
|
+
return StateContainer._registry.connect(this, instanceKey, constructorArgs);
|
|
257
850
|
}
|
|
258
851
|
/**
|
|
259
852
|
* Release a reference to an instance
|
|
260
|
-
*
|
|
261
|
-
* @param forceDispose - Force dispose regardless of ref count or keepAlive
|
|
853
|
+
* Delegates to the global registry.
|
|
262
854
|
*/
|
|
263
|
-
static release(
|
|
264
|
-
StateContainer._registry.release(this,
|
|
855
|
+
static release(instanceKey, forceDispose = false) {
|
|
856
|
+
StateContainer._registry.release(this, instanceKey, forceDispose);
|
|
265
857
|
}
|
|
266
858
|
/**
|
|
267
859
|
* Get all instances of this type
|
|
860
|
+
* Delegates to the global registry.
|
|
268
861
|
*/
|
|
269
862
|
static getAll() {
|
|
270
863
|
return StateContainer._registry.getAll(this);
|
|
271
864
|
}
|
|
272
865
|
/**
|
|
866
|
+
* Safely iterate over all instances of this type
|
|
867
|
+
* Delegates to the global registry.
|
|
868
|
+
*/
|
|
869
|
+
static forEach(callback) {
|
|
870
|
+
StateContainer._registry.forEach(this, callback);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
273
873
|
* Clear all instances of this type
|
|
874
|
+
* Delegates to the global registry.
|
|
274
875
|
*/
|
|
275
876
|
static clear() {
|
|
276
877
|
StateContainer._registry.clear(this);
|
|
277
878
|
}
|
|
278
879
|
/**
|
|
279
880
|
* Clear all instances from all types (for testing)
|
|
881
|
+
* Delegates to the global registry.
|
|
280
882
|
*/
|
|
281
883
|
static clearAllInstances() {
|
|
282
884
|
StateContainer._registry.clearAll();
|
|
283
885
|
}
|
|
284
886
|
/**
|
|
285
887
|
* Get registry statistics (for debugging)
|
|
888
|
+
* Delegates to the global registry.
|
|
286
889
|
*/
|
|
287
890
|
static getStats() {
|
|
288
891
|
return StateContainer._registry.getStats();
|
|
289
892
|
}
|
|
290
893
|
/**
|
|
291
894
|
* Get reference count for an instance
|
|
292
|
-
*
|
|
895
|
+
* Delegates to the global registry.
|
|
293
896
|
*/
|
|
294
|
-
static getRefCount(
|
|
295
|
-
return StateContainer._registry.getRefCount(this,
|
|
897
|
+
static getRefCount(instanceKey) {
|
|
898
|
+
return StateContainer._registry.getRefCount(this, instanceKey);
|
|
296
899
|
}
|
|
297
900
|
/**
|
|
298
901
|
* Check if an instance exists
|
|
299
|
-
*
|
|
902
|
+
* Delegates to the global registry.
|
|
300
903
|
*/
|
|
301
|
-
static hasInstance(
|
|
302
|
-
return StateContainer._registry.hasInstance(this,
|
|
904
|
+
static hasInstance(instanceKey) {
|
|
905
|
+
return StateContainer._registry.hasInstance(this, instanceKey);
|
|
303
906
|
}
|
|
304
|
-
_state;
|
|
305
|
-
listeners = /* @__PURE__ */ new Set();
|
|
306
|
-
_disposed = false;
|
|
307
|
-
config;
|
|
308
|
-
name;
|
|
309
|
-
debug;
|
|
310
|
-
instanceId;
|
|
311
907
|
/**
|
|
312
908
|
* Create a new StateContainer
|
|
313
909
|
*/
|
|
314
|
-
constructor(initialState
|
|
910
|
+
constructor(initialState) {
|
|
911
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
912
|
+
this._disposed = false;
|
|
913
|
+
this.config = {};
|
|
914
|
+
this.name = this.constructor.name;
|
|
915
|
+
this.debug = false;
|
|
916
|
+
this.instanceId = generateSimpleId(this.constructor.name, "main");
|
|
917
|
+
this.createdAt = Date.now();
|
|
918
|
+
this.lastUpdateTimestamp = Date.now();
|
|
315
919
|
this._state = initialState;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
this.
|
|
319
|
-
this.
|
|
920
|
+
}
|
|
921
|
+
initiConfig(config) {
|
|
922
|
+
this.config = { ...config };
|
|
923
|
+
this.name = this.config.name || this.constructor.name;
|
|
924
|
+
this.debug = this.config.debug ?? false;
|
|
925
|
+
this.instanceId = generateSimpleId(this.constructor.name, this.config.instanceId);
|
|
320
926
|
StateContainer._registry.emit("created", this);
|
|
321
927
|
}
|
|
322
928
|
/**
|
|
@@ -366,7 +972,58 @@ var StateContainer = class StateContainer {
|
|
|
366
972
|
} catch (error$1) {
|
|
367
973
|
console.error(`[${this.name}] Error in listener:`, error$1);
|
|
368
974
|
}
|
|
369
|
-
|
|
975
|
+
const stackTrace = this.captureStackTrace();
|
|
976
|
+
StateContainer._registry.emit("stateChanged", this, previousState, newState, stackTrace);
|
|
977
|
+
this.lastUpdateTimestamp = Date.now();
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Capture stack trace for debugging
|
|
981
|
+
* Can be overridden to disable in production
|
|
982
|
+
*/
|
|
983
|
+
captureStackTrace() {
|
|
984
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return "";
|
|
985
|
+
try {
|
|
986
|
+
const relevantLines = ((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(1);
|
|
987
|
+
const formattedLines = [];
|
|
988
|
+
for (const line of relevantLines) {
|
|
989
|
+
if (!line.trim()) continue;
|
|
990
|
+
if (line.includes("StateContainer.emit") || line.includes("StateContainer.update") || line.includes("StateContainer.captureStackTrace") || line.includes("Cubit.patch") || line.includes("Vertex.")) continue;
|
|
991
|
+
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;
|
|
992
|
+
const formatted = this.formatStackLine(line);
|
|
993
|
+
if (formatted) formattedLines.push(formatted);
|
|
994
|
+
}
|
|
995
|
+
return formattedLines.join("\n");
|
|
996
|
+
} catch {
|
|
997
|
+
return "";
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Format a single stack trace line for better readability
|
|
1002
|
+
* Removes URL prefixes, query params, and cleans up the output
|
|
1003
|
+
*/
|
|
1004
|
+
formatStackLine(line) {
|
|
1005
|
+
const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
|
|
1006
|
+
if (!match) {
|
|
1007
|
+
const simpleMatch = line.match(/at\s+(.+?):(\d+):(\d+)/);
|
|
1008
|
+
if (simpleMatch) {
|
|
1009
|
+
const [, url$1, lineNum$1, col$1] = simpleMatch;
|
|
1010
|
+
return ` at ${this.cleanFilePath(url$1)}:${lineNum$1}:${col$1}`;
|
|
1011
|
+
}
|
|
1012
|
+
return null;
|
|
1013
|
+
}
|
|
1014
|
+
const [, functionName, url, lineNum, col] = match;
|
|
1015
|
+
return ` at ${functionName} (${this.cleanFilePath(url)}:${lineNum}:${col})`;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Clean up file path by removing URL prefixes and query params
|
|
1019
|
+
*/
|
|
1020
|
+
cleanFilePath(url) {
|
|
1021
|
+
let path = url.replace(/http:\/\/localhost:\d+\/@fs/, "").replace(/http:\/\/localhost:\d+\//, "").replace(/\?t=\d+/, "").replace(/\?v=[a-f0-9]+/, "");
|
|
1022
|
+
const projectMatch = path.match(/\/Projects\/blac\/(.+)/);
|
|
1023
|
+
if (projectMatch) path = projectMatch[1];
|
|
1024
|
+
const segments = path.split("/");
|
|
1025
|
+
if (segments.length > 3) path = segments.slice(-3).join("/");
|
|
1026
|
+
return path;
|
|
370
1027
|
}
|
|
371
1028
|
/**
|
|
372
1029
|
* Update state using a function
|
|
@@ -380,6 +1037,12 @@ var StateContainer = class StateContainer {
|
|
|
380
1037
|
//#endregion
|
|
381
1038
|
//#region src/core/Cubit.ts
|
|
382
1039
|
/**
|
|
1040
|
+
* Cubit - Simple state container with direct state emission
|
|
1041
|
+
*
|
|
1042
|
+
* This validates that our StateContainer design can support
|
|
1043
|
+
* the simple Cubit pattern from the original BlaC.
|
|
1044
|
+
*/
|
|
1045
|
+
/**
|
|
383
1046
|
* Cubit is a simple state container that allows direct state emission
|
|
384
1047
|
*
|
|
385
1048
|
* Unlike StateContainer (where emit is protected), Cubit exposes emit and update
|
|
@@ -389,10 +1052,16 @@ var Cubit = class extends StateContainer {
|
|
|
389
1052
|
/**
|
|
390
1053
|
* Create a new Cubit
|
|
391
1054
|
* @param initialState Initial state value
|
|
392
|
-
* @param config Configuration options
|
|
393
1055
|
*/
|
|
394
|
-
constructor(initialState
|
|
395
|
-
super(initialState
|
|
1056
|
+
constructor(initialState) {
|
|
1057
|
+
super(initialState);
|
|
1058
|
+
this.patch = ((partial) => {
|
|
1059
|
+
if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
|
|
1060
|
+
this.update((current) => ({
|
|
1061
|
+
...current,
|
|
1062
|
+
...partial
|
|
1063
|
+
}));
|
|
1064
|
+
});
|
|
396
1065
|
}
|
|
397
1066
|
/**
|
|
398
1067
|
* Emit a new state (public override of protected parent method)
|
|
@@ -406,89 +1075,70 @@ var Cubit = class extends StateContainer {
|
|
|
406
1075
|
update(updater) {
|
|
407
1076
|
super["update"](updater);
|
|
408
1077
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Patch state with partial updates (shallow merge)
|
|
411
|
-
* Only available when state is an object type
|
|
412
|
-
* Arrow function to maintain correct 'this' binding in React
|
|
413
|
-
*
|
|
414
|
-
* @example
|
|
415
|
-
* ```typescript
|
|
416
|
-
* // Update single field
|
|
417
|
-
* this.patch({ count: 5 });
|
|
418
|
-
*
|
|
419
|
-
* // Update multiple fields
|
|
420
|
-
* this.patch({ count: 5, name: 'Updated' });
|
|
421
|
-
* ```
|
|
422
|
-
*/
|
|
423
|
-
patch = ((partial) => {
|
|
424
|
-
if (typeof this.state !== "object" || this.state === null) throw new Error("patch() is only available for object state types");
|
|
425
|
-
this.update((current) => ({
|
|
426
|
-
...current,
|
|
427
|
-
...partial
|
|
428
|
-
}));
|
|
429
|
-
});
|
|
430
1078
|
};
|
|
431
1079
|
/**
|
|
432
1080
|
* Example: Counter Cubit
|
|
433
1081
|
*/
|
|
434
1082
|
var CounterCubit = class extends Cubit {
|
|
435
1083
|
constructor() {
|
|
436
|
-
super(0
|
|
1084
|
+
super(0);
|
|
1085
|
+
this.increment = () => {
|
|
1086
|
+
this.emit(this.state + 1);
|
|
1087
|
+
};
|
|
1088
|
+
this.decrement = () => {
|
|
1089
|
+
this.emit(this.state - 1);
|
|
1090
|
+
};
|
|
1091
|
+
this.reset = () => {
|
|
1092
|
+
this.emit(0);
|
|
1093
|
+
};
|
|
1094
|
+
this.addAmount = (amount) => {
|
|
1095
|
+
this.emit(this.state + amount);
|
|
1096
|
+
};
|
|
437
1097
|
}
|
|
438
|
-
increment = () => {
|
|
439
|
-
this.emit(this.state + 1);
|
|
440
|
-
};
|
|
441
|
-
decrement = () => {
|
|
442
|
-
this.emit(this.state - 1);
|
|
443
|
-
};
|
|
444
|
-
reset = () => {
|
|
445
|
-
this.emit(0);
|
|
446
|
-
};
|
|
447
|
-
addAmount = (amount) => {
|
|
448
|
-
this.emit(this.state + amount);
|
|
449
|
-
};
|
|
450
1098
|
};
|
|
451
1099
|
var TodoCubit = class extends Cubit {
|
|
452
|
-
static
|
|
453
|
-
|
|
1100
|
+
static {
|
|
1101
|
+
this.keepAlive = true;
|
|
1102
|
+
}
|
|
454
1103
|
constructor() {
|
|
455
1104
|
super({
|
|
456
1105
|
todos: [],
|
|
457
1106
|
filter: "all",
|
|
458
1107
|
isLoading: false
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
1108
|
+
});
|
|
1109
|
+
this.nextId = 0;
|
|
1110
|
+
this.addTodo = (text) => {
|
|
1111
|
+
this.update((state) => ({
|
|
1112
|
+
...state,
|
|
1113
|
+
todos: [...state.todos, {
|
|
1114
|
+
id: `todo-${Date.now()}-${this.nextId++}`,
|
|
1115
|
+
text,
|
|
1116
|
+
done: false
|
|
1117
|
+
}]
|
|
1118
|
+
}));
|
|
1119
|
+
};
|
|
1120
|
+
this.toggleTodo = (id) => {
|
|
1121
|
+
this.update((state) => ({
|
|
1122
|
+
...state,
|
|
1123
|
+
todos: state.todos.map((todo) => todo.id === id ? {
|
|
1124
|
+
...todo,
|
|
1125
|
+
done: !todo.done
|
|
1126
|
+
} : todo)
|
|
1127
|
+
}));
|
|
1128
|
+
};
|
|
1129
|
+
this.removeTodo = (id) => {
|
|
1130
|
+
this.update((state) => ({
|
|
1131
|
+
...state,
|
|
1132
|
+
todos: state.todos.filter((todo) => todo.id !== id)
|
|
1133
|
+
}));
|
|
1134
|
+
};
|
|
1135
|
+
this.setFilter = (filter) => {
|
|
1136
|
+
this.patch({ filter });
|
|
1137
|
+
};
|
|
1138
|
+
this.setLoading = (isLoading) => {
|
|
1139
|
+
this.patch({ isLoading });
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
492
1142
|
get visibleTodos() {
|
|
493
1143
|
const todos = this.state.todos;
|
|
494
1144
|
switch (this.state.filter) {
|
|
@@ -506,45 +1156,42 @@ var TodoCubit = class extends Cubit {
|
|
|
506
1156
|
//#endregion
|
|
507
1157
|
//#region src/core/Vertex.ts
|
|
508
1158
|
/**
|
|
1159
|
+
* Vertex - Event-driven state container (Bloc pattern)
|
|
1160
|
+
*
|
|
1161
|
+
* Simplified version that includes its own event handling,
|
|
1162
|
+
* since events are only needed for the Vertex pattern.
|
|
1163
|
+
*/
|
|
1164
|
+
/**
|
|
509
1165
|
* Vertex is an event-driven state container (Bloc pattern)
|
|
510
1166
|
*
|
|
511
1167
|
* Includes its own simple event system since events are only needed
|
|
512
1168
|
* for the Vertex pattern, not for the base StateContainer.
|
|
513
1169
|
*/
|
|
514
1170
|
var Vertex = class extends StateContainer {
|
|
515
|
-
eventHandlers = /* @__PURE__ */ new Map();
|
|
516
|
-
isProcessing = false;
|
|
517
|
-
eventQueue = [];
|
|
518
1171
|
/**
|
|
519
1172
|
* Create a new Vertex
|
|
520
1173
|
* @param initialState Initial state value
|
|
521
|
-
* @param config Configuration options
|
|
522
1174
|
*/
|
|
523
|
-
constructor(initialState
|
|
524
|
-
super(initialState
|
|
1175
|
+
constructor(initialState) {
|
|
1176
|
+
super(initialState);
|
|
1177
|
+
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
1178
|
+
this.isProcessing = false;
|
|
1179
|
+
this.eventQueue = [];
|
|
1180
|
+
this.on = (EventClass, handler) => {
|
|
1181
|
+
const className = EventClass.name;
|
|
1182
|
+
const registrations = this.eventHandlers.get(className) || [];
|
|
1183
|
+
registrations.push({
|
|
1184
|
+
eventClass: EventClass,
|
|
1185
|
+
handler
|
|
1186
|
+
});
|
|
1187
|
+
this.eventHandlers.set(className, registrations);
|
|
1188
|
+
};
|
|
1189
|
+
this.add = (event) => {
|
|
1190
|
+
StateContainer._registry.emit("eventAdded", this, event);
|
|
1191
|
+
this.processEvent(event);
|
|
1192
|
+
};
|
|
525
1193
|
}
|
|
526
1194
|
/**
|
|
527
|
-
* Register an event handler
|
|
528
|
-
* Arrow function to maintain correct 'this' binding
|
|
529
|
-
*/
|
|
530
|
-
on = (EventClass, handler) => {
|
|
531
|
-
const className = EventClass.name;
|
|
532
|
-
const registrations = this.eventHandlers.get(className) || [];
|
|
533
|
-
registrations.push({
|
|
534
|
-
eventClass: EventClass,
|
|
535
|
-
handler
|
|
536
|
-
});
|
|
537
|
-
this.eventHandlers.set(className, registrations);
|
|
538
|
-
};
|
|
539
|
-
/**
|
|
540
|
-
* Add an event to be processed
|
|
541
|
-
* Arrow function to maintain correct 'this' binding in React
|
|
542
|
-
*/
|
|
543
|
-
add = (event) => {
|
|
544
|
-
StateContainer._registry.emit("eventAdded", this, event);
|
|
545
|
-
this.processEvent(event);
|
|
546
|
-
};
|
|
547
|
-
/**
|
|
548
1195
|
* Process an event (synchronously)
|
|
549
1196
|
*/
|
|
550
1197
|
processEvent(event) {
|
|
@@ -586,28 +1233,30 @@ var Vertex = class extends StateContainer {
|
|
|
586
1233
|
/**
|
|
587
1234
|
* Hook for handling event processing errors
|
|
588
1235
|
*/
|
|
589
|
-
onEventError(
|
|
1236
|
+
onEventError(_event, _error) {}
|
|
590
1237
|
};
|
|
591
1238
|
/**
|
|
592
1239
|
* Example: Counter Events
|
|
593
1240
|
*/
|
|
594
1241
|
var IncrementEvent = class {
|
|
595
|
-
type = "increment";
|
|
596
|
-
timestamp = Date.now();
|
|
597
1242
|
constructor(amount = 1) {
|
|
598
1243
|
this.amount = amount;
|
|
1244
|
+
this.type = "increment";
|
|
1245
|
+
this.timestamp = Date.now();
|
|
599
1246
|
}
|
|
600
1247
|
};
|
|
601
1248
|
var DecrementEvent = class {
|
|
602
|
-
type = "decrement";
|
|
603
|
-
timestamp = Date.now();
|
|
604
1249
|
constructor(amount = 1) {
|
|
605
1250
|
this.amount = amount;
|
|
1251
|
+
this.type = "decrement";
|
|
1252
|
+
this.timestamp = Date.now();
|
|
606
1253
|
}
|
|
607
1254
|
};
|
|
608
1255
|
var ResetEvent = class {
|
|
609
|
-
|
|
610
|
-
|
|
1256
|
+
constructor() {
|
|
1257
|
+
this.type = "reset";
|
|
1258
|
+
this.timestamp = Date.now();
|
|
1259
|
+
}
|
|
611
1260
|
};
|
|
612
1261
|
/**
|
|
613
1262
|
* Example: Counter Vertex
|
|
@@ -616,6 +1265,15 @@ var ResetEvent = class {
|
|
|
616
1265
|
var CounterVertex = class extends Vertex {
|
|
617
1266
|
constructor() {
|
|
618
1267
|
super(0);
|
|
1268
|
+
this.increment = (amount = 1) => {
|
|
1269
|
+
this.add(new IncrementEvent(amount));
|
|
1270
|
+
};
|
|
1271
|
+
this.decrement = (amount = 1) => {
|
|
1272
|
+
this.add(new DecrementEvent(amount));
|
|
1273
|
+
};
|
|
1274
|
+
this.reset = () => {
|
|
1275
|
+
this.add(new ResetEvent());
|
|
1276
|
+
};
|
|
619
1277
|
this.on(IncrementEvent, (event, emit) => {
|
|
620
1278
|
emit(this.state + event.amount);
|
|
621
1279
|
});
|
|
@@ -626,27 +1284,20 @@ var CounterVertex = class extends Vertex {
|
|
|
626
1284
|
emit(0);
|
|
627
1285
|
});
|
|
628
1286
|
}
|
|
629
|
-
increment = (amount = 1) => {
|
|
630
|
-
this.add(new IncrementEvent(amount));
|
|
631
|
-
};
|
|
632
|
-
decrement = (amount = 1) => {
|
|
633
|
-
this.add(new DecrementEvent(amount));
|
|
634
|
-
};
|
|
635
|
-
reset = () => {
|
|
636
|
-
this.add(new ResetEvent());
|
|
637
|
-
};
|
|
638
1287
|
};
|
|
639
1288
|
var LoginEvent = class {
|
|
640
|
-
type = "login";
|
|
641
|
-
timestamp = Date.now();
|
|
642
1289
|
constructor(email, password) {
|
|
643
1290
|
this.email = email;
|
|
644
1291
|
this.password = password;
|
|
1292
|
+
this.type = "login";
|
|
1293
|
+
this.timestamp = Date.now();
|
|
645
1294
|
}
|
|
646
1295
|
};
|
|
647
1296
|
var LogoutEvent = class {
|
|
648
|
-
|
|
649
|
-
|
|
1297
|
+
constructor() {
|
|
1298
|
+
this.type = "logout";
|
|
1299
|
+
this.timestamp = Date.now();
|
|
1300
|
+
}
|
|
650
1301
|
};
|
|
651
1302
|
var AuthVertex = class extends Vertex {
|
|
652
1303
|
constructor() {
|
|
@@ -656,6 +1307,12 @@ var AuthVertex = class extends Vertex {
|
|
|
656
1307
|
user: null,
|
|
657
1308
|
error: null
|
|
658
1309
|
});
|
|
1310
|
+
this.login = (email, password) => {
|
|
1311
|
+
this.add(new LoginEvent(email, password));
|
|
1312
|
+
};
|
|
1313
|
+
this.logout = () => {
|
|
1314
|
+
this.add(new LogoutEvent());
|
|
1315
|
+
};
|
|
659
1316
|
this.on(LoginEvent, (event, emit) => {
|
|
660
1317
|
emit({
|
|
661
1318
|
...this.state,
|
|
@@ -688,35 +1345,19 @@ var AuthVertex = class extends Vertex {
|
|
|
688
1345
|
});
|
|
689
1346
|
});
|
|
690
1347
|
}
|
|
691
|
-
login = (email, password) => {
|
|
692
|
-
this.add(new LoginEvent(email, password));
|
|
693
|
-
};
|
|
694
|
-
logout = () => {
|
|
695
|
-
this.add(new LogoutEvent());
|
|
696
|
-
};
|
|
697
1348
|
};
|
|
698
1349
|
|
|
699
1350
|
//#endregion
|
|
700
|
-
//#region src/
|
|
1351
|
+
//#region src/plugin/BlacPlugin.ts
|
|
701
1352
|
/**
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
* This module provides a functional approach to proxy-based dependency tracking.
|
|
705
|
-
* Unlike the class-based ProxyTracker, this uses a state object pattern that's
|
|
706
|
-
* easier to integrate with functional programming patterns and framework hooks.
|
|
707
|
-
*/
|
|
708
|
-
/**
|
|
709
|
-
* Array methods that should trigger tracking of the entire array
|
|
1353
|
+
* Type guard for plugins with init
|
|
710
1354
|
*/
|
|
711
|
-
|
|
712
|
-
"
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
"every",
|
|
718
|
-
"reduce"
|
|
719
|
-
]);
|
|
1355
|
+
function hasInitHook(plugin) {
|
|
1356
|
+
return typeof plugin.onInstall === "function";
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
//#endregion
|
|
1360
|
+
//#region src/tracking/proxy-tracker.ts
|
|
720
1361
|
/**
|
|
721
1362
|
* Check if a value can be proxied
|
|
722
1363
|
*
|
|
@@ -780,9 +1421,6 @@ function createArrayProxy(state, target, path, depth = 0) {
|
|
|
780
1421
|
if (typeof prop === "symbol") return Reflect.get(arr, prop);
|
|
781
1422
|
const value = Reflect.get(arr, prop);
|
|
782
1423
|
if (typeof value === "function") {
|
|
783
|
-
if (ARRAY_METHODS.has(prop)) {
|
|
784
|
-
if (state.isTracking && path) state.trackedPaths.add(path);
|
|
785
|
-
}
|
|
786
1424
|
if (!state.boundFunctionsCache) state.boundFunctionsCache = /* @__PURE__ */ new WeakMap();
|
|
787
1425
|
const cached = state.boundFunctionsCache.get(value);
|
|
788
1426
|
if (cached) return cached;
|
|
@@ -790,9 +1428,15 @@ function createArrayProxy(state, target, path, depth = 0) {
|
|
|
790
1428
|
state.boundFunctionsCache.set(value, bound);
|
|
791
1429
|
return bound;
|
|
792
1430
|
}
|
|
1431
|
+
if (prop === "length") {
|
|
1432
|
+
if (state.isTracking) {
|
|
1433
|
+
const fullPath$1 = path ? `${path}.length` : "length";
|
|
1434
|
+
state.trackedPaths.add(fullPath$1);
|
|
1435
|
+
}
|
|
1436
|
+
return value;
|
|
1437
|
+
}
|
|
793
1438
|
let fullPath;
|
|
794
|
-
if (prop === "
|
|
795
|
-
else if (typeof prop === "string") {
|
|
1439
|
+
if (typeof prop === "string") {
|
|
796
1440
|
const index = Number(prop);
|
|
797
1441
|
if (!isNaN(index) && index >= 0) fullPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
798
1442
|
else fullPath = path ? `${path}.${prop}` : prop;
|
|
@@ -834,10 +1478,10 @@ function createProxyInternal(state, target, path = "", depth = 0) {
|
|
|
834
1478
|
return bound;
|
|
835
1479
|
}
|
|
836
1480
|
const fullPath = path ? `${path}.${String(prop)}` : String(prop);
|
|
837
|
-
if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
|
|
838
1481
|
if (typeof prop === "string" && state.isTracking) {
|
|
839
1482
|
if (!prop.startsWith("_") && !prop.startsWith("$$")) state.trackedPaths.add(fullPath);
|
|
840
1483
|
}
|
|
1484
|
+
if (isProxyable(value)) return createProxyInternal(state, value, fullPath, depth + 1);
|
|
841
1485
|
return value;
|
|
842
1486
|
},
|
|
843
1487
|
has: (obj, prop) => {
|
|
@@ -949,13 +1593,45 @@ function shallowEqual(arr1, arr2) {
|
|
|
949
1593
|
|
|
950
1594
|
//#endregion
|
|
951
1595
|
//#region src/tracking/dependency-tracker.ts
|
|
1596
|
+
function isChildPath(child, parent) {
|
|
1597
|
+
if (child === parent) return false;
|
|
1598
|
+
return child.startsWith(parent + ".") || child.startsWith(parent + "[");
|
|
1599
|
+
}
|
|
952
1600
|
/**
|
|
953
|
-
*
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
*
|
|
1601
|
+
* Extract the parent array path from an array child path
|
|
1602
|
+
* Examples:
|
|
1603
|
+
* 'messages.length' -> 'messages'
|
|
1604
|
+
* 'messages[0]' -> 'messages'
|
|
1605
|
+
* 'messages[0].text' -> 'messages'
|
|
1606
|
+
* 'data.items[0]' -> 'data.items'
|
|
958
1607
|
*/
|
|
1608
|
+
function getArrayParentPath(path) {
|
|
1609
|
+
if (path.endsWith(".length")) return path.slice(0, -7);
|
|
1610
|
+
const arrayIndexMatch = path.match(/^(.+?)\[\d+\]/);
|
|
1611
|
+
if (arrayIndexMatch) return arrayIndexMatch[1];
|
|
1612
|
+
return null;
|
|
1613
|
+
}
|
|
1614
|
+
function optimizeTrackedPaths(paths) {
|
|
1615
|
+
if (paths.size === 0) return /* @__PURE__ */ new Set();
|
|
1616
|
+
if (paths.size === 1) return new Set(paths);
|
|
1617
|
+
const sortedPaths = Array.from(paths).sort((a, b) => b.length - a.length);
|
|
1618
|
+
const optimized = /* @__PURE__ */ new Set();
|
|
1619
|
+
for (const path of sortedPaths) {
|
|
1620
|
+
let hasMoreSpecificChild = false;
|
|
1621
|
+
for (const optimizedPath of optimized) if (isChildPath(optimizedPath, path)) {
|
|
1622
|
+
hasMoreSpecificChild = true;
|
|
1623
|
+
break;
|
|
1624
|
+
}
|
|
1625
|
+
if (!hasMoreSpecificChild) optimized.add(path);
|
|
1626
|
+
}
|
|
1627
|
+
const arrayParents = /* @__PURE__ */ new Set();
|
|
1628
|
+
for (const path of optimized) {
|
|
1629
|
+
const arrayParent = getArrayParentPath(path);
|
|
1630
|
+
if (arrayParent) arrayParents.add(arrayParent);
|
|
1631
|
+
}
|
|
1632
|
+
for (const arrayParent of arrayParents) optimized.add(arrayParent);
|
|
1633
|
+
return optimized;
|
|
1634
|
+
}
|
|
959
1635
|
function createTrackerState() {
|
|
960
1636
|
return {
|
|
961
1637
|
proxyTrackerState: createProxyTrackerState(),
|
|
@@ -963,31 +1639,18 @@ function createTrackerState() {
|
|
|
963
1639
|
currentRenderPaths: /* @__PURE__ */ new Set(),
|
|
964
1640
|
pathCache: /* @__PURE__ */ new Map(),
|
|
965
1641
|
lastCheckedState: null,
|
|
966
|
-
lastCheckedValues: /* @__PURE__ */ new Map()
|
|
967
|
-
cleanupCounter: 0
|
|
1642
|
+
lastCheckedValues: /* @__PURE__ */ new Map()
|
|
968
1643
|
};
|
|
969
1644
|
}
|
|
970
|
-
/**
|
|
971
|
-
* Start tracking property accesses
|
|
972
|
-
*/
|
|
973
1645
|
function startTracking(tracker) {
|
|
974
1646
|
startProxyTracking(tracker.proxyTrackerState);
|
|
975
1647
|
}
|
|
976
|
-
/**
|
|
977
|
-
* Create a proxy for the given state
|
|
978
|
-
*/
|
|
979
1648
|
function createProxy(tracker, state) {
|
|
980
1649
|
return createProxyForTarget(tracker.proxyTrackerState, state);
|
|
981
1650
|
}
|
|
982
|
-
/**
|
|
983
|
-
* Capture the paths that were tracked during a render
|
|
984
|
-
*
|
|
985
|
-
* This should be called after each render to record which paths were accessed.
|
|
986
|
-
* It also performs periodic cleanup of unused paths to prevent memory leaks.
|
|
987
|
-
*/
|
|
988
1651
|
function captureTrackedPaths(tracker, state) {
|
|
989
1652
|
tracker.previousRenderPaths = tracker.currentRenderPaths;
|
|
990
|
-
tracker.currentRenderPaths = stopProxyTracking(tracker.proxyTrackerState);
|
|
1653
|
+
tracker.currentRenderPaths = optimizeTrackedPaths(stopProxyTracking(tracker.proxyTrackerState));
|
|
991
1654
|
if (tracker.previousRenderPaths.size === 0 && tracker.currentRenderPaths.size === 0) return;
|
|
992
1655
|
const trackedPathsUnion = new Set(tracker.previousRenderPaths);
|
|
993
1656
|
for (const path of tracker.currentRenderPaths) trackedPathsUnion.add(path);
|
|
@@ -1003,19 +1666,8 @@ function captureTrackedPaths(tracker, state) {
|
|
|
1003
1666
|
const info$1 = tracker.pathCache.get(path);
|
|
1004
1667
|
info$1.value = canReuseCache && tracker.lastCheckedValues.has(path) ? tracker.lastCheckedValues.get(path) : getValueAtPath(state, info$1.segments);
|
|
1005
1668
|
}
|
|
1006
|
-
tracker.cleanupCounter++;
|
|
1007
|
-
if (tracker.cleanupCounter >= CLEANUP_INTERVAL) {
|
|
1008
|
-
tracker.cleanupCounter = 0;
|
|
1009
|
-
for (const path of tracker.pathCache.keys()) if (!trackedPathsUnion.has(path)) tracker.pathCache.delete(path);
|
|
1010
|
-
}
|
|
1011
1669
|
tracker.lastCheckedValues.clear();
|
|
1012
1670
|
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Check if any tracked paths have changed
|
|
1015
|
-
*
|
|
1016
|
-
* This is called when state changes to determine if a re-render is needed.
|
|
1017
|
-
* Returns true if any tracked path has a different value than before.
|
|
1018
|
-
*/
|
|
1019
1671
|
function hasChanges(tracker, state) {
|
|
1020
1672
|
if (tracker.pathCache.size === 0) return true;
|
|
1021
1673
|
tracker.lastCheckedValues.clear();
|
|
@@ -1030,11 +1682,6 @@ function hasChanges(tracker, state) {
|
|
|
1030
1682
|
tracker.lastCheckedState = state;
|
|
1031
1683
|
return false;
|
|
1032
1684
|
}
|
|
1033
|
-
/**
|
|
1034
|
-
* Check if tracker has any tracked data
|
|
1035
|
-
*
|
|
1036
|
-
* Returns true if there are any tracked paths or cached paths.
|
|
1037
|
-
*/
|
|
1038
1685
|
function hasTrackedData(tracker) {
|
|
1039
1686
|
return tracker.proxyTrackerState.trackedPaths.size > 0 || tracker.pathCache.size > 0 || tracker.previousRenderPaths.size > 0;
|
|
1040
1687
|
}
|
|
@@ -1138,6 +1785,12 @@ function configureLogger(opts) {
|
|
|
1138
1785
|
//#endregion
|
|
1139
1786
|
//#region src/proxy/ProxyTracker.ts
|
|
1140
1787
|
/**
|
|
1788
|
+
* ProxyTracker - Automatic dependency tracking through Proxies
|
|
1789
|
+
*
|
|
1790
|
+
* Tracks property access on state objects to automatically determine
|
|
1791
|
+
* which parts of the state a component depends on.
|
|
1792
|
+
*/
|
|
1793
|
+
/**
|
|
1141
1794
|
* Check if a value is an object that can be proxied
|
|
1142
1795
|
*/
|
|
1143
1796
|
function isProxyable$1(value) {
|
|
@@ -1147,10 +1800,12 @@ function isProxyable$1(value) {
|
|
|
1147
1800
|
* Tracks property access on state objects
|
|
1148
1801
|
*/
|
|
1149
1802
|
var ProxyTracker = class {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1803
|
+
constructor() {
|
|
1804
|
+
this.trackedPaths = /* @__PURE__ */ new Set();
|
|
1805
|
+
this.isTracking = false;
|
|
1806
|
+
this.proxyCache = /* @__PURE__ */ new WeakMap();
|
|
1807
|
+
this.maxDepth = 10;
|
|
1808
|
+
}
|
|
1154
1809
|
/**
|
|
1155
1810
|
* Create a proxy that tracks property access
|
|
1156
1811
|
*/
|
|
@@ -1202,22 +1857,12 @@ var ProxyTracker = class {
|
|
|
1202
1857
|
const proxy = new Proxy(target, { get: (arr, prop) => {
|
|
1203
1858
|
const value = Reflect.get(arr, prop);
|
|
1204
1859
|
if (typeof value === "function") {
|
|
1205
|
-
if (
|
|
1206
|
-
|
|
1207
|
-
"
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
"every",
|
|
1212
|
-
"reduce"
|
|
1213
|
-
].includes(String(prop))) {
|
|
1214
|
-
if (this.isTracking && path) {
|
|
1215
|
-
this.trackedPaths.add(path);
|
|
1216
|
-
debug("ProxyTracker", "Tracked array method access", {
|
|
1217
|
-
path,
|
|
1218
|
-
method: String(prop)
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1860
|
+
if (this.isTracking && path && typeof prop === "string") {
|
|
1861
|
+
this.trackedPaths.add(path);
|
|
1862
|
+
debug("ProxyTracker", "Tracked array method access", {
|
|
1863
|
+
path,
|
|
1864
|
+
method: String(prop)
|
|
1865
|
+
});
|
|
1221
1866
|
}
|
|
1222
1867
|
return value.bind(arr);
|
|
1223
1868
|
}
|
|
@@ -1320,25 +1965,175 @@ function trackAccess(state, callback) {
|
|
|
1320
1965
|
}
|
|
1321
1966
|
|
|
1322
1967
|
//#endregion
|
|
1323
|
-
//#region src/
|
|
1968
|
+
//#region src/adapter/framework-adapter.ts
|
|
1324
1969
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
* Centralized location for all magic numbers and default values.
|
|
1970
|
+
* External dependency manager
|
|
1971
|
+
* Handles subscriptions to external blocs accessed in getters
|
|
1328
1972
|
*/
|
|
1973
|
+
var ExternalDependencyManager = class {
|
|
1974
|
+
constructor() {
|
|
1975
|
+
this.subscriptions = [];
|
|
1976
|
+
this.previousDeps = /* @__PURE__ */ new Set();
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Check if dependencies have changed
|
|
1980
|
+
*/
|
|
1981
|
+
areDependenciesEqual(oldDeps, newDeps) {
|
|
1982
|
+
if (oldDeps.size !== newDeps.size) return false;
|
|
1983
|
+
for (const dep of newDeps) if (!oldDeps.has(dep)) return false;
|
|
1984
|
+
return true;
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Update external subscriptions
|
|
1988
|
+
* @param getterTracker - The getter tracker state
|
|
1989
|
+
* @param rawInstance - The raw bloc instance (exclude from subscriptions)
|
|
1990
|
+
* @param onGetterChange - Callback when external getter changes
|
|
1991
|
+
* @returns Whether subscriptions were updated
|
|
1992
|
+
*/
|
|
1993
|
+
updateSubscriptions(getterTracker, rawInstance, onGetterChange) {
|
|
1994
|
+
if (!getterTracker?.externalDependencies) return false;
|
|
1995
|
+
const currentDeps = getterTracker.externalDependencies;
|
|
1996
|
+
if (this.areDependenciesEqual(this.previousDeps, currentDeps)) {
|
|
1997
|
+
clearExternalDependencies(getterTracker);
|
|
1998
|
+
return false;
|
|
1999
|
+
}
|
|
2000
|
+
this.cleanup();
|
|
2001
|
+
for (const externalBloc of currentDeps) {
|
|
2002
|
+
if (externalBloc === rawInstance) continue;
|
|
2003
|
+
const unsub = externalBloc.subscribe(() => {
|
|
2004
|
+
invalidateRenderCache(getterTracker);
|
|
2005
|
+
if (hasGetterChanges(rawInstance, getterTracker)) onGetterChange();
|
|
2006
|
+
});
|
|
2007
|
+
this.subscriptions.push(unsub);
|
|
2008
|
+
}
|
|
2009
|
+
this.previousDeps = new Set(currentDeps);
|
|
2010
|
+
clearExternalDependencies(getterTracker);
|
|
2011
|
+
return true;
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Cleanup all subscriptions
|
|
2015
|
+
*/
|
|
2016
|
+
cleanup() {
|
|
2017
|
+
this.subscriptions.forEach((unsub) => unsub());
|
|
2018
|
+
this.subscriptions = [];
|
|
2019
|
+
}
|
|
2020
|
+
};
|
|
1329
2021
|
/**
|
|
1330
|
-
*
|
|
2022
|
+
* Create a subscription function for auto-tracking mode
|
|
1331
2023
|
*/
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
2024
|
+
function createAutoTrackSubscribe(instance, adapterState) {
|
|
2025
|
+
return (callback) => {
|
|
2026
|
+
return instance.subscribe(() => {
|
|
2027
|
+
const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
|
|
2028
|
+
const hasStateDeps = tracker.pathCache && tracker.pathCache.size > 0;
|
|
2029
|
+
const hasGetterDeps = adapterState.getterTracker && adapterState.getterTracker.trackedGetters.size > 0;
|
|
2030
|
+
const isPrimitiveState = instance.state !== null && typeof instance.state !== "object" && typeof instance.state !== "function";
|
|
2031
|
+
if (!hasStateDeps && !hasGetterDeps && !isPrimitiveState) return;
|
|
2032
|
+
let stateChanged = hasChanges(tracker, instance.state);
|
|
2033
|
+
if (!hasStateDeps && hasGetterDeps) stateChanged = false;
|
|
2034
|
+
if (stateChanged) {
|
|
2035
|
+
callback();
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
if (hasGetterChanges(instance, adapterState.getterTracker)) callback();
|
|
2039
|
+
});
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* Create a subscription function for manual dependencies mode
|
|
2044
|
+
*/
|
|
2045
|
+
function createManualDepsSubscribe(instance, adapterState, config) {
|
|
2046
|
+
return (callback) => {
|
|
2047
|
+
return instance.subscribe(() => {
|
|
2048
|
+
const newDeps = config.dependencies(instance.state, instance);
|
|
2049
|
+
if (!adapterState.manualDepsCache || !shallowEqual(adapterState.manualDepsCache, newDeps)) {
|
|
2050
|
+
adapterState.manualDepsCache = newDeps;
|
|
2051
|
+
callback();
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Create a subscription function for no-tracking mode
|
|
2058
|
+
*/
|
|
2059
|
+
function createNoTrackSubscribe(instance) {
|
|
2060
|
+
return (callback) => instance.subscribe(callback);
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Create a snapshot function for auto-tracking mode
|
|
2064
|
+
*/
|
|
2065
|
+
function createAutoTrackSnapshot(instance, adapterState) {
|
|
2066
|
+
return () => {
|
|
2067
|
+
const tracker = adapterState.tracker || (adapterState.tracker = createTrackerState());
|
|
2068
|
+
if (hasTrackedData(tracker)) captureTrackedPaths(tracker, instance.state);
|
|
2069
|
+
if (adapterState.getterTracker) {
|
|
2070
|
+
invalidateRenderCache(adapterState.getterTracker);
|
|
2071
|
+
commitTrackedGetters(adapterState.getterTracker);
|
|
2072
|
+
adapterState.getterTracker.isTracking = true;
|
|
2073
|
+
setActiveTracker(instance, adapterState.getterTracker);
|
|
2074
|
+
}
|
|
2075
|
+
startTracking(tracker);
|
|
2076
|
+
return createProxy(tracker, instance.state);
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
/**
|
|
2080
|
+
* Create a snapshot function for manual dependencies mode
|
|
2081
|
+
*/
|
|
2082
|
+
function createManualDepsSnapshot(instance, adapterState, config) {
|
|
2083
|
+
return () => {
|
|
2084
|
+
adapterState.manualDepsCache = config.dependencies(instance.state, instance);
|
|
2085
|
+
return instance.state;
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Create a snapshot function for no-tracking mode
|
|
2090
|
+
*/
|
|
2091
|
+
function createNoTrackSnapshot(instance) {
|
|
2092
|
+
return () => instance.state;
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Initialize adapter state for auto-tracking mode
|
|
2096
|
+
*/
|
|
2097
|
+
function initAutoTrackState(instance) {
|
|
2098
|
+
return {
|
|
2099
|
+
tracker: null,
|
|
2100
|
+
manualDepsCache: null,
|
|
2101
|
+
getterTracker: createGetterTracker(),
|
|
2102
|
+
proxiedBloc: createBlocProxy(instance)
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Initialize adapter state for manual dependencies mode
|
|
2107
|
+
*/
|
|
2108
|
+
function initManualDepsState(instance) {
|
|
2109
|
+
return {
|
|
2110
|
+
tracker: null,
|
|
2111
|
+
manualDepsCache: null,
|
|
2112
|
+
getterTracker: null,
|
|
2113
|
+
proxiedBloc: instance
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* Initialize adapter state for no-tracking mode
|
|
2118
|
+
*/
|
|
2119
|
+
function initNoTrackState(instance) {
|
|
2120
|
+
return {
|
|
2121
|
+
tracker: null,
|
|
2122
|
+
manualDepsCache: null,
|
|
2123
|
+
getterTracker: null,
|
|
2124
|
+
proxiedBloc: instance
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* Disable getter tracking (call after render completes)
|
|
2129
|
+
*/
|
|
2130
|
+
function disableGetterTracking(adapterState, rawInstance) {
|
|
2131
|
+
if (adapterState.getterTracker) {
|
|
2132
|
+
adapterState.getterTracker.isTracking = false;
|
|
2133
|
+
clearActiveTracker(rawInstance);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
1341
2136
|
|
|
1342
2137
|
//#endregion
|
|
1343
|
-
export { AuthVertex, BLAC_DEFAULTS, CounterCubit, CounterVertex, Cubit, DecrementEvent, IncrementEvent, LogLevel, LoginEvent, LogoutEvent, ProxyTracker, ResetEvent, StateContainer, StateContainerRegistry, TodoCubit, Vertex, __resetIdCounters, captureTrackedPaths, configureLogger, createArrayProxy, createIdGenerator, createLogger, createProxy, createProxyForTarget, createProxyInternal, createProxyTrackerState, createTrackerState, debug, error, generateId, generateSimpleId, getValueAtPath, globalProxyTracker, globalRegistry, hasChanges, hasTrackedData, info, isProxyable, parsePath, shallowEqual, startProxyTracking, startTracking, stopProxyTracking, trackAccess, warn };
|
|
2138
|
+
export { AuthVertex, BLAC_DEFAULTS, BLAC_ERROR_PREFIX, BLAC_ID_PATTERNS, BLAC_STATIC_PROPS, CounterCubit, CounterVertex, Cubit, DecrementEvent, ExternalDependencyManager, IncrementEvent, LogLevel, LoginEvent, LogoutEvent, PluginManager, ProxyTracker, ResetEvent, StateContainer, StateContainerRegistry, TodoCubit, Vertex, __resetIdCounters, captureTrackedPaths, clearActiveTracker, clearExternalDependencies, commitTrackedGetters, configureLogger, createArrayProxy, createAutoTrackSnapshot, createAutoTrackSubscribe, createBlocProxy, createGetterTracker, createIdGenerator, createLogger, createManualDepsSnapshot, createManualDepsSubscribe, createNoTrackSnapshot, createNoTrackSubscribe, createPluginManager, createProxy, createProxyForTarget, createProxyInternal, createProxyTrackerState, createTrackerState, debug, disableGetterTracking, error, generateId, generateIsolatedKey, generateSimpleId, getActiveTracker, getDescriptor, getGetterExecutionContext, getPluginManager, getStaticProp, getValueAtPath, globalProxyTracker, globalRegistry, hasChanges, hasGetterChanges, hasInitHook, hasTrackedData, info, initAutoTrackState, initManualDepsState, initNoTrackState, invalidateRenderCache, isExcludedFromDevTools, isGetter, isIsolatedClass, isIsolatedKey, isKeepAliveClass, isProxyable, parsePath, resetGetterTracker, setActiveTracker, shallowEqual, startProxyTracking, startTracking, stopProxyTracking, trackAccess, warn };
|
|
1344
2139
|
//# sourceMappingURL=index.js.map
|