@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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/Blac.d.ts +246 -0
- package/dist/Blac.d.ts.map +1 -0
- package/dist/Blac.js +625 -0
- package/dist/Blac.js.map +1 -0
- package/dist/Bloc.d.ts +41 -0
- package/dist/Bloc.d.ts.map +1 -0
- package/dist/Bloc.js +140 -0
- package/dist/Bloc.js.map +1 -0
- package/dist/BlocBase.d.ts +110 -0
- package/dist/BlocBase.d.ts.map +1 -0
- package/dist/BlocBase.js +287 -0
- package/dist/BlocBase.js.map +1 -0
- package/dist/Cubit.d.ts +25 -0
- package/dist/Cubit.d.ts.map +1 -0
- package/dist/Cubit.js +64 -0
- package/dist/Cubit.js.map +1 -0
- package/dist/adapter/BlacAdapter.d.ts +71 -0
- package/dist/adapter/BlacAdapter.d.ts.map +1 -0
- package/dist/adapter/BlacAdapter.js +373 -0
- package/dist/adapter/BlacAdapter.js.map +1 -0
- package/dist/adapter/ProxyFactory.d.ts +50 -0
- package/dist/adapter/ProxyFactory.d.ts.map +1 -0
- package/dist/adapter/ProxyFactory.js +150 -0
- package/dist/adapter/ProxyFactory.js.map +1 -0
- package/{src/adapter/index.ts → dist/adapter/index.d.ts} +1 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +19 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/errors/BlacError.d.ts +38 -0
- package/dist/errors/BlacError.d.ts.map +1 -0
- package/dist/errors/BlacError.js +55 -0
- package/dist/errors/BlacError.js.map +1 -0
- package/dist/errors/ErrorManager.d.ts +39 -0
- package/dist/errors/ErrorManager.d.ts.map +1 -0
- package/dist/errors/ErrorManager.js +91 -0
- package/dist/errors/ErrorManager.js.map +1 -0
- package/dist/errors/handleError.d.ts +24 -0
- package/dist/errors/handleError.d.ts.map +1 -0
- package/dist/errors/handleError.js +62 -0
- package/dist/errors/handleError.js.map +1 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/{src/events.ts → dist/events.d.ts} +4 -2
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/index.cjs +38 -0
- package/{src/index.ts → dist/index.d.ts} +1 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/BlocLifecycle.d.ts +51 -0
- package/dist/lifecycle/BlocLifecycle.d.ts.map +1 -0
- package/dist/lifecycle/BlocLifecycle.js +123 -0
- package/dist/lifecycle/BlocLifecycle.js.map +1 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +18 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/plugins/BlocPluginRegistry.d.ts +58 -0
- package/dist/plugins/BlocPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/BlocPluginRegistry.js +201 -0
- package/dist/plugins/BlocPluginRegistry.js.map +1 -0
- package/dist/plugins/SystemPluginRegistry.d.ts +79 -0
- package/dist/plugins/SystemPluginRegistry.d.ts.map +1 -0
- package/dist/plugins/SystemPluginRegistry.js +184 -0
- package/dist/plugins/SystemPluginRegistry.js.map +1 -0
- package/{src/plugins/index.ts → dist/plugins/index.d.ts} +1 -5
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +26 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/types.d.ts +103 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +3 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/subscription/SubscriptionManager.d.ts +67 -0
- package/dist/subscription/SubscriptionManager.d.ts.map +1 -0
- package/dist/subscription/SubscriptionManager.js +317 -0
- package/dist/subscription/SubscriptionManager.js.map +1 -0
- package/{src/subscription/index.ts → dist/subscription/index.d.ts} +1 -0
- package/dist/subscription/index.d.ts.map +1 -0
- package/dist/subscription/index.js +19 -0
- package/dist/subscription/index.js.map +1 -0
- package/dist/subscription/types.d.ts +65 -0
- package/dist/subscription/types.d.ts.map +1 -0
- package/dist/subscription/types.js +3 -0
- package/dist/subscription/types.js.map +1 -0
- package/dist/testing.d.ts +85 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +186 -0
- package/dist/testing.js.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +11 -28
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/BatchingManager.d.ts +28 -0
- package/dist/utils/BatchingManager.d.ts.map +1 -0
- package/dist/utils/BatchingManager.js +48 -0
- package/dist/utils/BatchingManager.js.map +1 -0
- package/dist/utils/RerenderLogger.d.ts +60 -0
- package/dist/utils/RerenderLogger.d.ts.map +1 -0
- package/dist/utils/RerenderLogger.js +205 -0
- package/dist/utils/RerenderLogger.js.map +1 -0
- package/dist/utils/generateInstanceId.d.ts +11 -0
- package/dist/utils/generateInstanceId.d.ts.map +1 -0
- package/dist/utils/generateInstanceId.js +50 -0
- package/dist/utils/generateInstanceId.js.map +1 -0
- package/dist/utils/shallowEqual.d.ts +9 -0
- package/dist/utils/shallowEqual.d.ts.map +1 -0
- package/dist/utils/shallowEqual.js +35 -0
- package/dist/utils/shallowEqual.js.map +1 -0
- package/dist/utils/uuid.d.ts +10 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +24 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +17 -6
- package/src/Blac.ts +0 -802
- package/src/Bloc.ts +0 -180
- package/src/BlocBase.ts +0 -398
- package/src/Cubit.ts +0 -68
- package/src/__tests__/Blac.config.test.ts +0 -79
- package/src/__tests__/Bloc.event.test.ts +0 -394
- package/src/__tests__/BlocBase.subscription.test.ts +0 -349
- package/src/__tests__/Cubit.test.ts +0 -462
- package/src/__tests__/memory-leaks.test.ts +0 -383
- package/src/__tests__/plugins.test.ts +0 -464
- package/src/adapter/BlacAdapter.ts +0 -467
- package/src/adapter/ProxyFactory.ts +0 -249
- package/src/adapter/__tests__/BlacAdapter.test.ts +0 -555
- package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
- package/src/errors/BlacError.ts +0 -69
- package/src/errors/ErrorManager.ts +0 -101
- package/src/errors/handleError.ts +0 -114
- package/src/errors/index.ts +0 -13
- package/src/lifecycle/BlocLifecycle.ts +0 -156
- package/src/lifecycle/index.ts +0 -1
- package/src/plugins/BlocPluginRegistry.ts +0 -240
- package/src/plugins/SystemPluginRegistry.ts +0 -242
- package/src/plugins/__tests__/BlocPluginRegistry.test.ts +0 -450
- package/src/plugins/__tests__/SystemPluginRegistry.test.ts +0 -404
- package/src/plugins/types.ts +0 -129
- package/src/subscription/README.md +0 -132
- package/src/subscription/SubscriptionManager.ts +0 -395
- package/src/subscription/__tests__/SubscriptionManager.test.ts +0 -355
- package/src/subscription/types.ts +0 -90
- package/src/testing.ts +0 -246
- package/src/utils/BatchingManager.ts +0 -59
- package/src/utils/RerenderLogger.ts +0 -265
- package/src/utils/__tests__/generateInstanceId.test.ts +0 -104
- package/src/utils/__tests__/shallowEqual.test.ts +0 -171
- package/src/utils/__tests__/uuid.test.ts +0 -144
- package/src/utils/generateInstanceId.ts +0 -54
- package/src/utils/shallowEqual.ts +0 -40
- package/src/utils/uuid.ts +0 -22
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BlacPlugin,
|
|
3
|
-
PluginRegistry,
|
|
4
|
-
PluginMetrics,
|
|
5
|
-
ErrorContext,
|
|
6
|
-
AdapterMetadata,
|
|
7
|
-
} from './types';
|
|
8
|
-
import { BlocBase } from '../BlocBase';
|
|
9
|
-
import { Bloc } from '../Bloc';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Registry for system-wide plugins
|
|
13
|
-
*/
|
|
14
|
-
export class SystemPluginRegistry implements PluginRegistry<BlacPlugin> {
|
|
15
|
-
private plugins = new Map<string, BlacPlugin>();
|
|
16
|
-
private metrics = new Map<string, Map<string, PluginMetrics>>();
|
|
17
|
-
private executionOrder: string[] = [];
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Add a system plugin
|
|
21
|
-
*/
|
|
22
|
-
add(plugin: BlacPlugin): void {
|
|
23
|
-
if (this.plugins.has(plugin.name)) {
|
|
24
|
-
throw new Error(`Plugin '${plugin.name}' is already registered`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
this.plugins.set(plugin.name, plugin);
|
|
28
|
-
this.executionOrder.push(plugin.name);
|
|
29
|
-
this.initializeMetrics(plugin.name);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Remove a system plugin
|
|
34
|
-
*/
|
|
35
|
-
remove(pluginName: string): boolean {
|
|
36
|
-
const plugin = this.plugins.get(pluginName);
|
|
37
|
-
if (!plugin) return false;
|
|
38
|
-
|
|
39
|
-
this.plugins.delete(pluginName);
|
|
40
|
-
this.metrics.delete(pluginName);
|
|
41
|
-
this.executionOrder = this.executionOrder.filter(
|
|
42
|
-
(name) => name !== pluginName,
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Get a plugin by name
|
|
50
|
-
*/
|
|
51
|
-
get(pluginName: string): BlacPlugin | undefined {
|
|
52
|
-
return this.plugins.get(pluginName);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Get all plugins in execution order
|
|
57
|
-
*/
|
|
58
|
-
getAll(): ReadonlyArray<BlacPlugin> {
|
|
59
|
-
return this.executionOrder.map((name) => this.plugins.get(name)!);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Clear all plugins
|
|
64
|
-
*/
|
|
65
|
-
clear(): void {
|
|
66
|
-
this.plugins.clear();
|
|
67
|
-
this.metrics.clear();
|
|
68
|
-
this.executionOrder = [];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Execute a hook on all plugins
|
|
73
|
-
*/
|
|
74
|
-
executeHook(
|
|
75
|
-
hookName: keyof BlacPlugin,
|
|
76
|
-
args: unknown[],
|
|
77
|
-
errorHandler?: (error: Error, plugin: BlacPlugin) => void,
|
|
78
|
-
): void {
|
|
79
|
-
for (const pluginName of this.executionOrder) {
|
|
80
|
-
const plugin = this.plugins.get(pluginName)!;
|
|
81
|
-
const hook = plugin[hookName] as Function | undefined;
|
|
82
|
-
|
|
83
|
-
if (typeof hook !== 'function') continue;
|
|
84
|
-
|
|
85
|
-
const startTime = performance.now();
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
hook.apply(plugin, args);
|
|
89
|
-
this.recordSuccess(pluginName, hookName as string, startTime);
|
|
90
|
-
} catch (error) {
|
|
91
|
-
this.recordError(pluginName, hookName as string, error as Error);
|
|
92
|
-
|
|
93
|
-
if (errorHandler) {
|
|
94
|
-
errorHandler(error as Error, plugin);
|
|
95
|
-
} else {
|
|
96
|
-
// Default: log and continue
|
|
97
|
-
console.error(
|
|
98
|
-
`Plugin '${pluginName}' error in hook '${hookName as string}':`,
|
|
99
|
-
error,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Bootstrap all plugins
|
|
108
|
-
*/
|
|
109
|
-
bootstrap(): void {
|
|
110
|
-
this.executeHook('beforeBootstrap', []);
|
|
111
|
-
this.executeHook('afterBootstrap', []);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Shutdown all plugins
|
|
116
|
-
*/
|
|
117
|
-
shutdown(): void {
|
|
118
|
-
this.executeHook('beforeShutdown', []);
|
|
119
|
-
this.executeHook('afterShutdown', []);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Notify plugins of bloc creation
|
|
124
|
-
*/
|
|
125
|
-
notifyBlocCreated(bloc: BlocBase<any>): void {
|
|
126
|
-
this.executeHook('onBlocCreated', [bloc]);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Notify plugins of bloc disposal
|
|
131
|
-
*/
|
|
132
|
-
notifyBlocDisposed(bloc: BlocBase<any>): void {
|
|
133
|
-
this.executeHook('onBlocDisposed', [bloc]);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Notify plugins of state change
|
|
138
|
-
*/
|
|
139
|
-
notifyStateChanged(
|
|
140
|
-
bloc: BlocBase<any>,
|
|
141
|
-
previousState: any,
|
|
142
|
-
currentState: any,
|
|
143
|
-
): void {
|
|
144
|
-
this.executeHook('onStateChanged', [bloc, previousState, currentState]);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Notify plugins of event addition
|
|
149
|
-
*/
|
|
150
|
-
notifyEventAdded(bloc: Bloc<any, any>, event: any): void {
|
|
151
|
-
this.executeHook('onEventAdded', [bloc, event]);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Notify plugins of errors
|
|
156
|
-
*/
|
|
157
|
-
notifyError(
|
|
158
|
-
error: Error,
|
|
159
|
-
bloc: BlocBase<unknown>,
|
|
160
|
-
context: ErrorContext,
|
|
161
|
-
): void {
|
|
162
|
-
this.executeHook('onError', [error, bloc, context], (hookError, plugin) => {
|
|
163
|
-
// Double fault protection - if error handler fails, just log
|
|
164
|
-
console.error(`Plugin '${plugin.name}' error handler failed:`, hookError);
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Notify plugins of adapter lifecycle events
|
|
170
|
-
*/
|
|
171
|
-
notifyAdapterCreated(adapter: any, metadata: AdapterMetadata): void {
|
|
172
|
-
this.executeHook('onAdapterCreated', [adapter, metadata]);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
notifyAdapterMount(adapter: any, metadata: AdapterMetadata): void {
|
|
176
|
-
this.executeHook('onAdapterMount', [adapter, metadata]);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
notifyAdapterUnmount(adapter: any, metadata: AdapterMetadata): void {
|
|
180
|
-
this.executeHook('onAdapterUnmount', [adapter, metadata]);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
notifyAdapterRender(adapter: any, metadata: AdapterMetadata): void {
|
|
184
|
-
this.executeHook('onAdapterRender', [adapter, metadata]);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
notifyAdapterDisposed(adapter: any, metadata: AdapterMetadata): void {
|
|
188
|
-
this.executeHook('onAdapterDisposed', [adapter, metadata]);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Get metrics for a plugin
|
|
193
|
-
*/
|
|
194
|
-
getMetrics(pluginName: string): Map<string, PluginMetrics> | undefined {
|
|
195
|
-
return this.metrics.get(pluginName);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private initializeMetrics(pluginName: string): void {
|
|
199
|
-
this.metrics.set(pluginName, new Map());
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private recordSuccess(
|
|
203
|
-
pluginName: string,
|
|
204
|
-
hookName: string,
|
|
205
|
-
startTime: number,
|
|
206
|
-
): void {
|
|
207
|
-
const pluginMetrics = this.metrics.get(pluginName)!;
|
|
208
|
-
const hookMetrics = pluginMetrics.get(hookName) || {
|
|
209
|
-
executionTime: 0,
|
|
210
|
-
executionCount: 0,
|
|
211
|
-
errorCount: 0,
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const executionTime = performance.now() - startTime;
|
|
215
|
-
|
|
216
|
-
pluginMetrics.set(hookName, {
|
|
217
|
-
...hookMetrics,
|
|
218
|
-
executionTime: hookMetrics.executionTime + executionTime,
|
|
219
|
-
executionCount: hookMetrics.executionCount + 1,
|
|
220
|
-
lastExecutionTime: executionTime,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
private recordError(
|
|
225
|
-
pluginName: string,
|
|
226
|
-
hookName: string,
|
|
227
|
-
error: Error,
|
|
228
|
-
): void {
|
|
229
|
-
const pluginMetrics = this.metrics.get(pluginName)!;
|
|
230
|
-
const hookMetrics = pluginMetrics.get(hookName) || {
|
|
231
|
-
executionTime: 0,
|
|
232
|
-
executionCount: 0,
|
|
233
|
-
errorCount: 0,
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
pluginMetrics.set(hookName, {
|
|
237
|
-
...hookMetrics,
|
|
238
|
-
errorCount: hookMetrics.errorCount + 1,
|
|
239
|
-
lastError: error,
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { BlocPluginRegistry } from '../BlocPluginRegistry';
|
|
3
|
-
import { BlocPlugin } from '../types';
|
|
4
|
-
|
|
5
|
-
// Mock bloc for testing
|
|
6
|
-
class MockBloc {
|
|
7
|
-
state = { count: 0 };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Sample plugins for testing
|
|
11
|
-
const createTestPlugin = (
|
|
12
|
-
name: string,
|
|
13
|
-
overrides?: Partial<BlocPlugin<any, any>>,
|
|
14
|
-
): BlocPlugin<any, any> => ({
|
|
15
|
-
name,
|
|
16
|
-
version: '1.0.0',
|
|
17
|
-
...overrides,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe('BlocPluginRegistry', () => {
|
|
21
|
-
let registry: BlocPluginRegistry<any, any>;
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
registry = new BlocPluginRegistry();
|
|
25
|
-
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('Plugin Management', () => {
|
|
29
|
-
it('should add plugins successfully', () => {
|
|
30
|
-
const plugin = createTestPlugin('test-plugin');
|
|
31
|
-
|
|
32
|
-
registry.add(plugin);
|
|
33
|
-
|
|
34
|
-
expect(registry.get('test-plugin')).toBe(plugin);
|
|
35
|
-
expect(registry.getAll()).toContain(plugin);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should throw error when adding duplicate plugin', () => {
|
|
39
|
-
const plugin = createTestPlugin('test-plugin');
|
|
40
|
-
|
|
41
|
-
registry.add(plugin);
|
|
42
|
-
|
|
43
|
-
expect(() => registry.add(plugin)).toThrow(
|
|
44
|
-
"Plugin 'test-plugin' is already registered",
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should maintain execution order', () => {
|
|
49
|
-
const plugin1 = createTestPlugin('plugin-1');
|
|
50
|
-
const plugin2 = createTestPlugin('plugin-2');
|
|
51
|
-
const plugin3 = createTestPlugin('plugin-3');
|
|
52
|
-
|
|
53
|
-
registry.add(plugin1);
|
|
54
|
-
registry.add(plugin2);
|
|
55
|
-
registry.add(plugin3);
|
|
56
|
-
|
|
57
|
-
const all = registry.getAll();
|
|
58
|
-
expect(all[0]).toBe(plugin1);
|
|
59
|
-
expect(all[1]).toBe(plugin2);
|
|
60
|
-
expect(all[2]).toBe(plugin3);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should remove plugins', () => {
|
|
64
|
-
const plugin = createTestPlugin('test-plugin');
|
|
65
|
-
|
|
66
|
-
registry.add(plugin);
|
|
67
|
-
expect(registry.get('test-plugin')).toBe(plugin);
|
|
68
|
-
|
|
69
|
-
const removed = registry.remove('test-plugin');
|
|
70
|
-
expect(removed).toBe(true);
|
|
71
|
-
expect(registry.get('test-plugin')).toBeUndefined();
|
|
72
|
-
expect(registry.getAll()).not.toContain(plugin);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should return false when removing non-existent plugin', () => {
|
|
76
|
-
const removed = registry.remove('non-existent');
|
|
77
|
-
expect(removed).toBe(false);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should clear all plugins', () => {
|
|
81
|
-
registry.add(createTestPlugin('plugin-1'));
|
|
82
|
-
registry.add(createTestPlugin('plugin-2'));
|
|
83
|
-
registry.add(createTestPlugin('plugin-3'));
|
|
84
|
-
|
|
85
|
-
expect(registry.getAll().length).toBe(3);
|
|
86
|
-
|
|
87
|
-
registry.clear();
|
|
88
|
-
|
|
89
|
-
expect(registry.getAll().length).toBe(0);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('Capability Validation', () => {
|
|
94
|
-
it('should validate transformState requires readState', () => {
|
|
95
|
-
const plugin = createTestPlugin('test-plugin', {
|
|
96
|
-
capabilities: {
|
|
97
|
-
readState: false,
|
|
98
|
-
transformState: true,
|
|
99
|
-
interceptEvents: false,
|
|
100
|
-
persistData: false,
|
|
101
|
-
accessMetadata: false,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
expect(() => registry.add(plugin)).toThrow(
|
|
106
|
-
"Plugin 'test-plugin': transformState requires readState capability",
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should validate interceptEvents requires readState', () => {
|
|
111
|
-
const plugin = createTestPlugin('test-plugin', {
|
|
112
|
-
capabilities: {
|
|
113
|
-
readState: false,
|
|
114
|
-
transformState: false,
|
|
115
|
-
interceptEvents: true,
|
|
116
|
-
persistData: false,
|
|
117
|
-
accessMetadata: false,
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
expect(() => registry.add(plugin)).toThrow(
|
|
122
|
-
"Plugin 'test-plugin': interceptEvents requires readState capability",
|
|
123
|
-
);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should allow valid capability combinations', () => {
|
|
127
|
-
const plugin = createTestPlugin('test-plugin', {
|
|
128
|
-
capabilities: {
|
|
129
|
-
readState: true,
|
|
130
|
-
transformState: true,
|
|
131
|
-
interceptEvents: true,
|
|
132
|
-
persistData: false,
|
|
133
|
-
accessMetadata: false,
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
expect(() => registry.add(plugin)).not.toThrow();
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('Lifecycle Hooks', () => {
|
|
142
|
-
it('should call onAttach for all plugins', () => {
|
|
143
|
-
const onAttach1 = vi.fn();
|
|
144
|
-
const onAttach2 = vi.fn();
|
|
145
|
-
|
|
146
|
-
registry.add(createTestPlugin('plugin-1', { onAttach: onAttach1 }));
|
|
147
|
-
registry.add(createTestPlugin('plugin-2', { onAttach: onAttach2 }));
|
|
148
|
-
|
|
149
|
-
const bloc = new MockBloc();
|
|
150
|
-
registry.attach(bloc);
|
|
151
|
-
|
|
152
|
-
expect(onAttach1).toHaveBeenCalledWith(bloc);
|
|
153
|
-
expect(onAttach2).toHaveBeenCalledWith(bloc);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('should throw error when attaching twice', () => {
|
|
157
|
-
registry.add(createTestPlugin('plugin-1'));
|
|
158
|
-
|
|
159
|
-
const bloc = new MockBloc();
|
|
160
|
-
registry.attach(bloc);
|
|
161
|
-
|
|
162
|
-
expect(() => registry.attach(bloc)).toThrow(
|
|
163
|
-
'Plugins are already attached',
|
|
164
|
-
);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should remove plugin if onAttach fails', () => {
|
|
168
|
-
const failingPlugin = createTestPlugin('failing-plugin', {
|
|
169
|
-
onAttach: () => {
|
|
170
|
-
throw new Error('Attach failed');
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
registry.add(failingPlugin);
|
|
175
|
-
registry.add(createTestPlugin('good-plugin'));
|
|
176
|
-
|
|
177
|
-
const bloc = new MockBloc();
|
|
178
|
-
registry.attach(bloc);
|
|
179
|
-
|
|
180
|
-
expect(registry.get('failing-plugin')).toBeUndefined();
|
|
181
|
-
expect(registry.get('good-plugin')).toBeDefined();
|
|
182
|
-
expect(console.error).toHaveBeenCalled();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('should call onDetach when removing attached plugin', () => {
|
|
186
|
-
const onDetach = vi.fn();
|
|
187
|
-
const plugin = createTestPlugin('test-plugin', { onDetach });
|
|
188
|
-
|
|
189
|
-
registry.add(plugin);
|
|
190
|
-
registry.attach(new MockBloc());
|
|
191
|
-
registry.remove('test-plugin');
|
|
192
|
-
|
|
193
|
-
expect(onDetach).toHaveBeenCalled();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should call onDetach for all plugins when clearing', () => {
|
|
197
|
-
const onDetach1 = vi.fn();
|
|
198
|
-
const onDetach2 = vi.fn();
|
|
199
|
-
|
|
200
|
-
registry.add(createTestPlugin('plugin-1', { onDetach: onDetach1 }));
|
|
201
|
-
registry.add(createTestPlugin('plugin-2', { onDetach: onDetach2 }));
|
|
202
|
-
|
|
203
|
-
registry.attach(new MockBloc());
|
|
204
|
-
registry.clear();
|
|
205
|
-
|
|
206
|
-
expect(onDetach1).toHaveBeenCalled();
|
|
207
|
-
expect(onDetach2).toHaveBeenCalled();
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe('State Transformation', () => {
|
|
212
|
-
it('should transform state through all plugins', () => {
|
|
213
|
-
const plugin1 = createTestPlugin('plugin-1', {
|
|
214
|
-
capabilities: {
|
|
215
|
-
readState: true,
|
|
216
|
-
transformState: true,
|
|
217
|
-
interceptEvents: false,
|
|
218
|
-
persistData: false,
|
|
219
|
-
accessMetadata: false,
|
|
220
|
-
},
|
|
221
|
-
transformState: (prev, next) => ({ ...next, modified1: true }),
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const plugin2 = createTestPlugin('plugin-2', {
|
|
225
|
-
capabilities: {
|
|
226
|
-
readState: true,
|
|
227
|
-
transformState: true,
|
|
228
|
-
interceptEvents: false,
|
|
229
|
-
persistData: false,
|
|
230
|
-
accessMetadata: false,
|
|
231
|
-
},
|
|
232
|
-
transformState: (prev, next) => ({ ...next, modified2: true }),
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
registry.add(plugin1);
|
|
236
|
-
registry.add(plugin2);
|
|
237
|
-
|
|
238
|
-
const result = registry.transformState({ count: 0 }, { count: 1 });
|
|
239
|
-
|
|
240
|
-
expect(result).toEqual({
|
|
241
|
-
count: 1,
|
|
242
|
-
modified1: true,
|
|
243
|
-
modified2: true,
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('should skip transformation for plugins without capability', () => {
|
|
248
|
-
const plugin = createTestPlugin('plugin-1', {
|
|
249
|
-
capabilities: {
|
|
250
|
-
readState: true,
|
|
251
|
-
transformState: false,
|
|
252
|
-
interceptEvents: false,
|
|
253
|
-
persistData: false,
|
|
254
|
-
accessMetadata: false,
|
|
255
|
-
},
|
|
256
|
-
transformState: vi.fn(),
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
registry.add(plugin);
|
|
260
|
-
|
|
261
|
-
registry.transformState({ count: 0 }, { count: 1 });
|
|
262
|
-
|
|
263
|
-
expect(plugin.transformState).not.toHaveBeenCalled();
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('should continue on transformation error', () => {
|
|
267
|
-
const plugin1 = createTestPlugin('plugin-1', {
|
|
268
|
-
transformState: () => {
|
|
269
|
-
throw new Error('Transform failed');
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
const plugin2 = createTestPlugin('plugin-2', {
|
|
274
|
-
transformState: (prev, next) => ({ ...next, modified: true }),
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
registry.add(plugin1);
|
|
278
|
-
registry.add(plugin2);
|
|
279
|
-
|
|
280
|
-
const result = registry.transformState({ count: 0 }, { count: 1 });
|
|
281
|
-
|
|
282
|
-
expect(result).toEqual({ count: 1, modified: true });
|
|
283
|
-
expect(console.error).toHaveBeenCalled();
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
describe('Event Transformation', () => {
|
|
288
|
-
it('should transform event through all plugins', () => {
|
|
289
|
-
const plugin1 = createTestPlugin('plugin-1', {
|
|
290
|
-
capabilities: {
|
|
291
|
-
readState: true,
|
|
292
|
-
transformState: false,
|
|
293
|
-
interceptEvents: true,
|
|
294
|
-
persistData: false,
|
|
295
|
-
accessMetadata: false,
|
|
296
|
-
},
|
|
297
|
-
transformEvent: (event) => ({ ...event, modified1: true }),
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const plugin2 = createTestPlugin('plugin-2', {
|
|
301
|
-
capabilities: {
|
|
302
|
-
readState: true,
|
|
303
|
-
transformState: false,
|
|
304
|
-
interceptEvents: true,
|
|
305
|
-
persistData: false,
|
|
306
|
-
accessMetadata: false,
|
|
307
|
-
},
|
|
308
|
-
transformEvent: (event) => ({ ...event, modified2: true }),
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
registry.add(plugin1);
|
|
312
|
-
registry.add(plugin2);
|
|
313
|
-
|
|
314
|
-
const result = registry.transformEvent({ type: 'TEST' });
|
|
315
|
-
|
|
316
|
-
expect(result).toEqual({
|
|
317
|
-
type: 'TEST',
|
|
318
|
-
modified1: true,
|
|
319
|
-
modified2: true,
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('should stop transformation chain if event becomes null', () => {
|
|
324
|
-
const plugin1 = createTestPlugin('plugin-1', {
|
|
325
|
-
transformEvent: () => null,
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
const plugin2 = createTestPlugin('plugin-2', {
|
|
329
|
-
transformEvent: vi.fn(),
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
registry.add(plugin1);
|
|
333
|
-
registry.add(plugin2);
|
|
334
|
-
|
|
335
|
-
const result = registry.transformEvent({ type: 'TEST' });
|
|
336
|
-
|
|
337
|
-
expect(result).toBeNull();
|
|
338
|
-
expect(plugin2.transformEvent).not.toHaveBeenCalled();
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
describe('Notifications', () => {
|
|
343
|
-
it('should notify plugins of state changes', () => {
|
|
344
|
-
const onStateChange1 = vi.fn();
|
|
345
|
-
const onStateChange2 = vi.fn();
|
|
346
|
-
|
|
347
|
-
registry.add(
|
|
348
|
-
createTestPlugin('plugin-1', { onStateChange: onStateChange1 }),
|
|
349
|
-
);
|
|
350
|
-
registry.add(
|
|
351
|
-
createTestPlugin('plugin-2', { onStateChange: onStateChange2 }),
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
const prevState = { count: 0 };
|
|
355
|
-
const currState = { count: 1 };
|
|
356
|
-
|
|
357
|
-
registry.notifyStateChange(prevState, currState);
|
|
358
|
-
|
|
359
|
-
expect(onStateChange1).toHaveBeenCalledWith(prevState, currState);
|
|
360
|
-
expect(onStateChange2).toHaveBeenCalledWith(prevState, currState);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('should notify plugins of events', () => {
|
|
364
|
-
const onEvent1 = vi.fn();
|
|
365
|
-
const onEvent2 = vi.fn();
|
|
366
|
-
|
|
367
|
-
registry.add(createTestPlugin('plugin-1', { onEvent: onEvent1 }));
|
|
368
|
-
registry.add(createTestPlugin('plugin-2', { onEvent: onEvent2 }));
|
|
369
|
-
|
|
370
|
-
const event = { type: 'TEST_EVENT' };
|
|
371
|
-
|
|
372
|
-
registry.notifyEvent(event);
|
|
373
|
-
|
|
374
|
-
expect(onEvent1).toHaveBeenCalledWith(event);
|
|
375
|
-
expect(onEvent2).toHaveBeenCalledWith(event);
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it('should notify plugins of errors', () => {
|
|
379
|
-
const onError1 = vi.fn();
|
|
380
|
-
const onError2 = vi.fn();
|
|
381
|
-
|
|
382
|
-
registry.add(createTestPlugin('plugin-1', { onError: onError1 }));
|
|
383
|
-
registry.add(createTestPlugin('plugin-2', { onError: onError2 }));
|
|
384
|
-
|
|
385
|
-
const error = new Error('Test error');
|
|
386
|
-
const context = {
|
|
387
|
-
phase: 'state-change' as const,
|
|
388
|
-
operation: 'test-operation',
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
registry.notifyError(error, context);
|
|
392
|
-
|
|
393
|
-
expect(onError1).toHaveBeenCalledWith(error, context);
|
|
394
|
-
expect(onError2).toHaveBeenCalledWith(error, context);
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
it('should continue notifying on plugin errors', () => {
|
|
398
|
-
const onStateChange1 = vi.fn(() => {
|
|
399
|
-
throw new Error('Plugin error');
|
|
400
|
-
});
|
|
401
|
-
const onStateChange2 = vi.fn();
|
|
402
|
-
|
|
403
|
-
registry.add(
|
|
404
|
-
createTestPlugin('plugin-1', { onStateChange: onStateChange1 }),
|
|
405
|
-
);
|
|
406
|
-
registry.add(
|
|
407
|
-
createTestPlugin('plugin-2', { onStateChange: onStateChange2 }),
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
const prevState = { count: 0 };
|
|
411
|
-
const currState = { count: 1 };
|
|
412
|
-
|
|
413
|
-
registry.notifyStateChange(prevState, currState);
|
|
414
|
-
|
|
415
|
-
expect(onStateChange2).toHaveBeenCalled();
|
|
416
|
-
expect(console.error).toHaveBeenCalled();
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
describe('Capability Checks', () => {
|
|
421
|
-
it('should respect readState capability for notifications', () => {
|
|
422
|
-
const plugin = createTestPlugin('plugin-1', {
|
|
423
|
-
capabilities: {
|
|
424
|
-
readState: false,
|
|
425
|
-
transformState: false,
|
|
426
|
-
interceptEvents: false,
|
|
427
|
-
persistData: false,
|
|
428
|
-
accessMetadata: false,
|
|
429
|
-
},
|
|
430
|
-
onStateChange: vi.fn(),
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
registry.add(plugin);
|
|
434
|
-
registry.notifyStateChange({ count: 0 }, { count: 1 });
|
|
435
|
-
|
|
436
|
-
expect(plugin.onStateChange).not.toHaveBeenCalled();
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
it('should use default capabilities when not specified', () => {
|
|
440
|
-
const plugin = createTestPlugin('plugin-1', {
|
|
441
|
-
onStateChange: vi.fn(),
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
registry.add(plugin);
|
|
445
|
-
registry.notifyStateChange({ count: 0 }, { count: 1 });
|
|
446
|
-
|
|
447
|
-
expect(plugin.onStateChange).toHaveBeenCalled();
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
});
|