@blac/core 2.0.0-rc-16 → 2.0.0-rc.0

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.
Files changed (157) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/Blac.d.ts +246 -0
  3. package/dist/Blac.d.ts.map +1 -0
  4. package/dist/Blac.js +625 -0
  5. package/dist/Blac.js.map +1 -0
  6. package/dist/Bloc.d.ts +41 -0
  7. package/dist/Bloc.d.ts.map +1 -0
  8. package/dist/Bloc.js +140 -0
  9. package/dist/Bloc.js.map +1 -0
  10. package/dist/BlocBase.d.ts +110 -0
  11. package/dist/BlocBase.d.ts.map +1 -0
  12. package/dist/BlocBase.js +287 -0
  13. package/dist/BlocBase.js.map +1 -0
  14. package/dist/Cubit.d.ts +25 -0
  15. package/dist/Cubit.d.ts.map +1 -0
  16. package/dist/Cubit.js +64 -0
  17. package/dist/Cubit.js.map +1 -0
  18. package/dist/adapter/BlacAdapter.d.ts +71 -0
  19. package/dist/adapter/BlacAdapter.d.ts.map +1 -0
  20. package/dist/adapter/BlacAdapter.js +373 -0
  21. package/dist/adapter/BlacAdapter.js.map +1 -0
  22. package/dist/adapter/ProxyFactory.d.ts +50 -0
  23. package/dist/adapter/ProxyFactory.d.ts.map +1 -0
  24. package/dist/adapter/ProxyFactory.js +150 -0
  25. package/dist/adapter/ProxyFactory.js.map +1 -0
  26. package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
  27. package/dist/adapter/index.d.ts.map +1 -0
  28. package/dist/adapter/index.js +19 -0
  29. package/dist/adapter/index.js.map +1 -0
  30. package/dist/errors/BlacError.d.ts +38 -0
  31. package/dist/errors/BlacError.d.ts.map +1 -0
  32. package/dist/errors/BlacError.js +55 -0
  33. package/dist/errors/BlacError.js.map +1 -0
  34. package/dist/errors/ErrorManager.d.ts +39 -0
  35. package/dist/errors/ErrorManager.d.ts.map +1 -0
  36. package/dist/errors/ErrorManager.js +91 -0
  37. package/dist/errors/ErrorManager.js.map +1 -0
  38. package/dist/errors/handleError.d.ts +24 -0
  39. package/dist/errors/handleError.d.ts.map +1 -0
  40. package/dist/errors/handleError.js +62 -0
  41. package/dist/errors/handleError.js.map +1 -0
  42. package/dist/errors/index.d.ts +5 -0
  43. package/dist/errors/index.d.ts.map +1 -0
  44. package/dist/errors/index.js +13 -0
  45. package/dist/errors/index.js.map +1 -0
  46. package/{src/events.ts → dist/events.d.ts} +4 -2
  47. package/dist/events.d.ts.map +1 -0
  48. package/dist/events.js +17 -0
  49. package/dist/events.js.map +1 -0
  50. package/dist/index.cjs +38 -0
  51. package/{src/index.ts → dist/index.d.ts} +1 -10
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +38 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
  56. package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
  57. package/dist/lifecycle/BlocLifecycle.js +123 -0
  58. package/dist/lifecycle/BlocLifecycle.js.map +1 -0
  59. package/dist/lifecycle/index.d.ts +2 -0
  60. package/dist/lifecycle/index.d.ts.map +1 -0
  61. package/dist/lifecycle/index.js +18 -0
  62. package/dist/lifecycle/index.js.map +1 -0
  63. package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
  64. package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
  65. package/dist/plugins/BlocPluginRegistry.js +201 -0
  66. package/dist/plugins/BlocPluginRegistry.js.map +1 -0
  67. package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
  68. package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
  69. package/dist/plugins/SystemPluginRegistry.js +184 -0
  70. package/dist/plugins/SystemPluginRegistry.js.map +1 -0
  71. package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
  72. package/dist/plugins/index.d.ts.map +1 -0
  73. package/dist/plugins/index.js +26 -0
  74. package/dist/plugins/index.js.map +1 -0
  75. package/dist/plugins/types.d.ts +103 -0
  76. package/dist/plugins/types.d.ts.map +1 -0
  77. package/dist/plugins/types.js +3 -0
  78. package/dist/plugins/types.js.map +1 -0
  79. package/dist/subscription/SubscriptionManager.d.ts +67 -0
  80. package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
  81. package/dist/subscription/SubscriptionManager.js +317 -0
  82. package/dist/subscription/SubscriptionManager.js.map +1 -0
  83. package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
  84. package/dist/subscription/index.d.ts.map +1 -0
  85. package/dist/subscription/index.js +19 -0
  86. package/dist/subscription/index.js.map +1 -0
  87. package/dist/subscription/types.d.ts +65 -0
  88. package/dist/subscription/types.d.ts.map +1 -0
  89. package/dist/subscription/types.js +3 -0
  90. package/dist/subscription/types.js.map +1 -0
  91. package/dist/testing.d.ts +85 -0
  92. package/dist/testing.d.ts.map +1 -0
  93. package/dist/testing.js +186 -0
  94. package/dist/testing.js.map +1 -0
  95. package/{src/types.ts → dist/types.d.ts} +11 -28
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +3 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/utils/BatchingManager.d.ts +28 -0
  100. package/dist/utils/BatchingManager.d.ts.map +1 -0
  101. package/dist/utils/BatchingManager.js +48 -0
  102. package/dist/utils/BatchingManager.js.map +1 -0
  103. package/dist/utils/RerenderLogger.d.ts +60 -0
  104. package/dist/utils/RerenderLogger.d.ts.map +1 -0
  105. package/dist/utils/RerenderLogger.js +205 -0
  106. package/dist/utils/RerenderLogger.js.map +1 -0
  107. package/dist/utils/generateInstanceId.d.ts +11 -0
  108. package/dist/utils/generateInstanceId.d.ts.map +1 -0
  109. package/dist/utils/generateInstanceId.js +50 -0
  110. package/dist/utils/generateInstanceId.js.map +1 -0
  111. package/dist/utils/shallowEqual.d.ts +9 -0
  112. package/dist/utils/shallowEqual.d.ts.map +1 -0
  113. package/dist/utils/shallowEqual.js +35 -0
  114. package/dist/utils/shallowEqual.js.map +1 -0
  115. package/dist/utils/uuid.d.ts +10 -0
  116. package/dist/utils/uuid.d.ts.map +1 -0
  117. package/dist/utils/uuid.js +24 -0
  118. package/dist/utils/uuid.js.map +1 -0
  119. package/package.json +17 -6
  120. package/src/Blac.ts +0 -802
  121. package/src/Bloc.ts +0 -180
  122. package/src/BlocBase.ts +0 -398
  123. package/src/Cubit.ts +0 -68
  124. package/src/__tests__/Blac.config.test.ts +0 -79
  125. package/src/__tests__/Bloc.event.test.ts +0 -394
  126. package/src/__tests__/BlocBase.subscription.test.ts +0 -349
  127. package/src/__tests__/Cubit.test.ts +0 -462
  128. package/src/__tests__/memory-leaks.test.ts +0 -383
  129. package/src/__tests__/plugins.test.ts +0 -464
  130. package/src/adapter/BlacAdapter.ts +0 -467
  131. package/src/adapter/ProxyFactory.ts +0 -249
  132. package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
  133. package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
  134. package/src/errors/BlacError.ts +0 -69
  135. package/src/errors/ErrorManager.ts +0 -101
  136. package/src/errors/handleError.ts +0 -114
  137. package/src/errors/index.ts +0 -13
  138. package/src/lifecycle/BlocLifecycle.ts +0 -156
  139. package/src/lifecycle/index.ts +0 -1
  140. package/src/plugins/BlocPluginRegistry.ts +0 -240
  141. package/src/plugins/SystemPluginRegistry.ts +0 -242
  142. package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
  143. package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
  144. package/src/plugins/types.ts +0 -129
  145. package/src/subscription/README.md +0 -132
  146. package/src/subscription/SubscriptionManager.ts +0 -395
  147. package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
  148. package/src/subscription/types.ts +0 -90
  149. package/src/testing.ts +0 -246
  150. package/src/utils/BatchingManager.ts +0 -59
  151. package/src/utils/RerenderLogger.ts +0 -265
  152. package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
  153. package/src/utils/__tests__/shallowEqual.test.ts +0 -171
  154. package/src/utils/__tests__/uuid.test.ts +0 -144
  155. package/src/utils/generateInstanceId.ts +0 -54
  156. package/src/utils/shallowEqual.ts +0 -40
  157. package/src/utils/uuid.ts +0 -22
@@ -1,395 +0,0 @@
1
- import { BlocBase } from '../BlocBase';
2
- import { Blac } from '../Blac';
3
- import { generateUUID } from '../utils/uuid';
4
- import {
5
- Subscription,
6
- SubscriptionOptions,
7
- SubscriptionManagerStats,
8
- } from './types';
9
-
10
- /**
11
- * Unified subscription manager that replaces the dual consumer/observer system.
12
- * Handles all state change subscriptions with a single, consistent interface.
13
- */
14
- export class SubscriptionManager<S = unknown> {
15
- private subscriptions = new Map<string, Subscription<S>>();
16
- private pathToSubscriptions = new Map<string, Set<string>>();
17
- private weakRefCleanupScheduled = false;
18
- private totalNotifications = 0;
19
-
20
- constructor(private bloc: BlocBase<S>) {}
21
-
22
- /**
23
- * Subscribe to state changes
24
- */
25
- subscribe(options: SubscriptionOptions<S>): () => void {
26
- const id = `${options.type}-${generateUUID()}`;
27
-
28
- const subscription: Subscription<S> = {
29
- id,
30
- type: options.type,
31
- selector: options.selector,
32
- equalityFn: options.equalityFn || Object.is,
33
- notify: options.notify,
34
- priority: options.priority ?? 0,
35
- weakRef: options.weakRef,
36
- dependencies: new Set(),
37
- metadata: {
38
- lastNotified: Date.now(),
39
- hasRendered: false,
40
- accessCount: 0,
41
- firstAccessTime: Date.now(),
42
- },
43
- };
44
-
45
- // Initialize selector value if provided
46
- if (subscription.selector) {
47
- try {
48
- subscription.lastValue = subscription.selector(
49
- this.bloc.state,
50
- this.bloc,
51
- );
52
- } catch (error) {
53
- Blac.error(`SubscriptionManager: Error in selector for ${id}:`, error);
54
- }
55
- }
56
-
57
- this.subscriptions.set(id, subscription);
58
-
59
- Blac.log(
60
- `[${this.bloc._name}:${this.bloc._id}] Subscription added: ${id}. Total: ${this.subscriptions.size}`,
61
- );
62
-
63
- // Cancel disposal if bloc is in disposal_requested state
64
- (this.bloc as any)._cancelDisposalIfRequested();
65
-
66
- // Return unsubscribe function
67
- return () => this.unsubscribe(id);
68
- }
69
-
70
- /**
71
- * Unsubscribe by ID
72
- */
73
- unsubscribe(id: string): void {
74
- const subscription = this.subscriptions.get(id);
75
- if (!subscription) return;
76
-
77
- // Remove from path dependencies
78
- if (subscription.dependencies) {
79
- for (const path of subscription.dependencies) {
80
- const subs = this.pathToSubscriptions.get(path);
81
- if (subs) {
82
- subs.delete(id);
83
- if (subs.size === 0) {
84
- this.pathToSubscriptions.delete(path);
85
- }
86
- }
87
- }
88
- }
89
-
90
- this.subscriptions.delete(id);
91
-
92
- Blac.log(
93
- `[${this.bloc._name}:${this.bloc._id}] Subscription removed: ${id}. Remaining: ${this.subscriptions.size}`,
94
- );
95
-
96
- // Check if bloc should be disposed
97
- this.bloc.checkDisposal();
98
- }
99
-
100
- /**
101
- * Notify all subscriptions of state change
102
- */
103
- notify(newState: S, oldState: S, action?: unknown): void {
104
- // Clean up dead weak references if needed
105
- this.cleanupDeadReferences();
106
-
107
- // Sort subscriptions by priority (descending)
108
- const sortedSubscriptions = Array.from(this.subscriptions.values()).sort(
109
- (a, b) => (b.priority ?? 0) - (a.priority ?? 0),
110
- );
111
-
112
- for (const subscription of sortedSubscriptions) {
113
- // Check if WeakRef is still alive
114
- if (subscription.weakRef && !subscription.weakRef.deref()) {
115
- this.scheduleWeakRefCleanup();
116
- continue;
117
- }
118
-
119
- let shouldNotify = false;
120
- let newValue: unknown;
121
- let oldValue: unknown;
122
-
123
- if (subscription.selector) {
124
- // Use selector to determine if notification is needed
125
- try {
126
- newValue = subscription.selector(newState, this.bloc);
127
- oldValue = subscription.lastValue;
128
-
129
- const equalityFn = subscription.equalityFn || Object.is;
130
- shouldNotify = !equalityFn(oldValue, newValue);
131
-
132
- if (shouldNotify) {
133
- subscription.lastValue = newValue;
134
- }
135
- } catch (error) {
136
- Blac.error(
137
- `SubscriptionManager: Error in selector for ${subscription.id}:`,
138
- error,
139
- );
140
- continue;
141
- }
142
- } else {
143
- // No selector - check if tracked dependencies changed
144
- if (subscription.dependencies && subscription.dependencies.size > 0) {
145
- // Check which paths changed between old and new state
146
- const changedPaths = this.getChangedPaths(oldState, newState);
147
- shouldNotify = this.shouldNotifyForPaths(
148
- subscription.id,
149
- changedPaths,
150
- );
151
- if ((Blac.config as any).logLevel === 'debug') {
152
- Blac.log(
153
- `[SubscriptionManager] Subscription ${subscription.id} dependencies:`,
154
- Array.from(subscription.dependencies),
155
- );
156
- Blac.log(
157
- `[SubscriptionManager] Changed paths:`,
158
- Array.from(changedPaths),
159
- );
160
- Blac.log(`[SubscriptionManager] Should notify:`, shouldNotify);
161
- }
162
- } else {
163
- // No tracked dependencies, always notify
164
- shouldNotify = true;
165
- }
166
- newValue = newState;
167
- oldValue = oldState;
168
- }
169
-
170
- if (shouldNotify) {
171
- try {
172
- subscription.notify(newValue, oldValue, action);
173
- this.totalNotifications++;
174
-
175
- if (subscription.metadata) {
176
- subscription.metadata.lastNotified = Date.now();
177
- subscription.metadata.hasRendered = true;
178
- }
179
- } catch (error) {
180
- Blac.error(
181
- `SubscriptionManager: Error in notify for ${subscription.id}:`,
182
- error,
183
- );
184
- }
185
- }
186
- }
187
- }
188
-
189
- /**
190
- * Track a path access for a subscription
191
- */
192
- trackAccess(subscriptionId: string, path: string, value?: unknown): void {
193
- const subscription = this.subscriptions.get(subscriptionId);
194
- if (!subscription) return;
195
-
196
- // Update dependencies
197
- if (!subscription.dependencies) {
198
- subscription.dependencies = new Set();
199
- }
200
- subscription.dependencies.add(path);
201
-
202
- // Update path-to-subscription mapping
203
- let subs = this.pathToSubscriptions.get(path);
204
- if (!subs) {
205
- subs = new Set();
206
- this.pathToSubscriptions.set(path, subs);
207
- }
208
- subs.add(subscriptionId);
209
-
210
- // Update metadata
211
- if (subscription.metadata) {
212
- subscription.metadata.accessCount =
213
- (subscription.metadata.accessCount || 0) + 1;
214
- subscription.metadata.lastAccessTime = Date.now();
215
- }
216
- }
217
-
218
- /**
219
- * Get the paths that changed between two states
220
- */
221
- private getChangedPaths(
222
- oldState: any,
223
- newState: any,
224
- path = '',
225
- ): Set<string> {
226
- const changedPaths = new Set<string>();
227
-
228
- if (oldState === newState) return changedPaths;
229
-
230
- // Handle primitives
231
- if (
232
- typeof oldState !== 'object' ||
233
- typeof newState !== 'object' ||
234
- oldState === null ||
235
- newState === null
236
- ) {
237
- if (path) changedPaths.add(path);
238
- return changedPaths;
239
- }
240
-
241
- // Get all keys from both objects
242
- const allKeys = new Set([
243
- ...Object.keys(oldState),
244
- ...Object.keys(newState),
245
- ]);
246
-
247
- for (const key of allKeys) {
248
- const fullPath = path ? `${path}.${key}` : key;
249
- const oldValue = oldState[key];
250
- const newValue = newState[key];
251
-
252
- if (oldValue !== newValue) {
253
- changedPaths.add(fullPath);
254
-
255
- // For nested objects, get all nested changed paths
256
- if (
257
- typeof oldValue === 'object' &&
258
- typeof newValue === 'object' &&
259
- oldValue !== null &&
260
- newValue !== null
261
- ) {
262
- const nestedChanges = this.getChangedPaths(
263
- oldValue,
264
- newValue,
265
- fullPath,
266
- );
267
- nestedChanges.forEach((p) => changedPaths.add(p));
268
- }
269
- }
270
- }
271
-
272
- return changedPaths;
273
- }
274
-
275
- /**
276
- * Check if a subscription should be notified based on changed paths
277
- */
278
- shouldNotifyForPaths(
279
- subscriptionId: string,
280
- changedPaths: Set<string>,
281
- ): boolean {
282
- const subscription = this.subscriptions.get(subscriptionId);
283
- if (!subscription || !subscription.dependencies) return true;
284
-
285
- // Check if any tracked dependencies match changed paths
286
- for (const trackedPath of subscription.dependencies) {
287
- // Handle class getter dependencies (_class.propertyName)
288
- if (trackedPath.startsWith('_class.')) {
289
- // For class getters, we need to notify on any state change
290
- // since we can't determine which state properties the getter depends on
291
- // This is conservative but ensures correctness
292
- if (changedPaths.size > 0) {
293
- return true;
294
- }
295
- continue;
296
- }
297
-
298
- // Check direct path matches
299
- if (changedPaths.has(trackedPath)) return true;
300
-
301
- // Check nested paths
302
- for (const changedPath of changedPaths) {
303
- if (
304
- changedPath.startsWith(trackedPath + '.') ||
305
- trackedPath.startsWith(changedPath + '.')
306
- ) {
307
- return true;
308
- }
309
- }
310
- }
311
-
312
- return false;
313
- }
314
-
315
- /**
316
- * Get statistics about subscriptions
317
- */
318
- getStats(): SubscriptionManagerStats {
319
- let consumerCount = 0;
320
- let observerCount = 0;
321
-
322
- for (const sub of this.subscriptions.values()) {
323
- if (sub.type === 'consumer') consumerCount++;
324
- else observerCount++;
325
- }
326
-
327
- return {
328
- activeSubscriptions: this.subscriptions.size,
329
- consumerCount,
330
- observerCount,
331
- totalNotifications: this.totalNotifications,
332
- trackedDependencies: this.pathToSubscriptions.size,
333
- };
334
- }
335
-
336
- /**
337
- * Clear all subscriptions
338
- */
339
- clear(): void {
340
- this.subscriptions.clear();
341
- this.pathToSubscriptions.clear();
342
- }
343
-
344
- /**
345
- * Get subscription by ID
346
- */
347
- getSubscription(id: string): Subscription<S> | undefined {
348
- return this.subscriptions.get(id);
349
- }
350
-
351
- /**
352
- * Check if there are any active subscriptions
353
- */
354
- get hasSubscriptions(): boolean {
355
- return this.subscriptions.size > 0;
356
- }
357
-
358
- /**
359
- * Get count of active subscriptions
360
- */
361
- get size(): number {
362
- return this.subscriptions.size;
363
- }
364
-
365
- /**
366
- * Clean up subscriptions with dead weak references
367
- */
368
- private cleanupDeadReferences(): void {
369
- if (!this.weakRefCleanupScheduled) return;
370
-
371
- const deadIds: string[] = [];
372
-
373
- for (const [id, subscription] of this.subscriptions) {
374
- if (subscription.weakRef && !subscription.weakRef.deref()) {
375
- deadIds.push(id);
376
- }
377
- }
378
-
379
- for (const id of deadIds) {
380
- this.unsubscribe(id);
381
- }
382
-
383
- this.weakRefCleanupScheduled = false;
384
- }
385
-
386
- /**
387
- * Schedule weak reference cleanup for next tick
388
- */
389
- private scheduleWeakRefCleanup(): void {
390
- if (this.weakRefCleanupScheduled) return;
391
-
392
- this.weakRefCleanupScheduled = true;
393
- queueMicrotask(() => this.cleanupDeadReferences());
394
- }
395
- }