@hamak/ui-store-impl 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- $ tsc -p tsconfig.json
1
+ $ tsc -p tsconfig.json && tsc -p tsconfig.es2015.json
@@ -0,0 +1,115 @@
1
+ $ vitest run
2
+
3
+ RUN v2.1.9 /Users/amah/Devs/projects/app-framework/packages/ui-store/ui-store-impl
4
+
5
+ stderr | src/core/middleware-registry.test.ts > MiddlewareRegistry > register > should replace existing middleware with same id
6
+ [MiddlewareRegistry] Middleware "test-middleware" already registered, overwriting.
7
+
8
+ ✓ src/core/middleware-registry.test.ts (20 tests) 16ms
9
+ stdout | src/core/store-manager.test.ts > StoreManager > initialization > should initialize store
10
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
11
+
12
+ stdout | src/core/store-manager.test.ts > StoreManager > initialization > should throw error when initializing twice
13
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
14
+
15
+ stdout | src/core/store-manager.test.ts > StoreManager > initialization > should lock middleware registry after initialization
16
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
17
+
18
+ stdout | src/core/store-manager.test.ts > StoreManager > middleware registration > should apply middleware in priority order
19
+ [StoreManager] Store initialized with: {
20
+ reducers: [],
21
+ middleware: [
22
+ { id: 'high', priority: 100, plugin: undefined },
23
+ { id: 'medium', priority: 50, plugin: undefined },
24
+ { id: 'low', priority: 10, plugin: undefined }
25
+ ],
26
+ devTools: true
27
+ }
28
+
29
+ stdout | src/core/store-manager.test.ts > StoreManager > reducer registration > should combine multiple reducers
30
+ [StoreManager] Store initialized with: { reducers: [ 'counter', 'name' ], middleware: [], devTools: true }
31
+
32
+ stdout | src/core/store-manager.test.ts > StoreManager > reducer registration > should support hot reducer replacement
33
+ [StoreManager] Store initialized with: { reducers: [ 'feature' ], middleware: [], devTools: true }
34
+
35
+ stdout | src/core/store-manager.test.ts > StoreManager > store operations > should dispatch actions
36
+ [StoreManager] Store initialized with: { reducers: [ 'counter' ], middleware: [], devTools: true }
37
+
38
+ stdout | src/core/store-manager.test.ts > StoreManager > store operations > should get state
39
+ [StoreManager] Store initialized with: { reducers: [ 'data' ], middleware: [], devTools: true }
40
+
41
+ stdout | src/core/store-manager.test.ts > StoreManager > store operations > should subscribe to changes
42
+ [StoreManager] Store initialized with: { reducers: [ 'counter' ], middleware: [], devTools: true }
43
+
44
+ stdout | src/core/store-manager.test.ts > StoreManager > store operations > should replace reducer
45
+ [StoreManager] Store initialized with: { reducers: [ 'data' ], middleware: [], devTools: true }
46
+
47
+ stdout | src/core/store-manager.test.ts > StoreManager > preloaded state > should initialize with preloaded state
48
+ [StoreManager] Store initialized with: { reducers: [ 'counter' ], middleware: [], devTools: true }
49
+
50
+ stdout | src/core/store-manager.test.ts > StoreManager > destroy > should destroy store
51
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
52
+ [StoreManager] Store destroyed
53
+
54
+ stdout | src/core/store-manager.test.ts > StoreManager > destroy > should allow re-initialization after destroy
55
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
56
+ [StoreManager] Store destroyed
57
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
58
+
59
+ stdout | src/core/store-manager.test.ts > StoreManager > DevTools configuration > should accept devTools configuration
60
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: false }
61
+
62
+ stdout | src/core/store-manager.test.ts > StoreManager > DevTools configuration > should accept enhancers
63
+ [StoreManager] Store initialized with: { reducers: [], middleware: [], devTools: true }
64
+
65
+ ✓ src/core/store-manager.test.ts (19 tests) 229ms
66
+ stderr | src/core/reducer-registry.test.ts > ReducerRegistry > register > should warn when replacing without replace flag
67
+ [ReducerRegistry] Reducer "test" already registered. Use replace=true to override.
68
+
69
+ ✓ src/core/reducer-registry.test.ts (19 tests) 130ms
70
+ stdout | src/middleware/logger-middleware.test.ts > createLoggerMiddleware > should log actions with type
71
+ [Redux] INCREMENT
72
+ Prev State: { count: 0 }
73
+ Action: { type: 'INCREMENT' }
74
+ Next State: { count: 1 }
75
+
76
+ stdout | src/middleware/logger-middleware.test.ts > createLoggerMiddleware > should pass action through middleware chain
77
+ [Redux] TEST
78
+ Prev State: {}
79
+ Action: { type: 'TEST' }
80
+ Next State: {}
81
+
82
+ stdout | src/middleware/logger-middleware.test.ts > createLoggerMiddleware > should return action result
83
+ [Redux] TEST
84
+ Prev State: {}
85
+ Action: { type: 'TEST', payload: 123 }
86
+ Next State: {}
87
+
88
+ stdout | src/middleware/logger-middleware.test.ts > createLoggerMiddleware > should log state before and after action
89
+ [Redux] SET_VALUE
90
+ Prev State: { value: 0 }
91
+ Action: { type: 'SET_VALUE', payload: 42 }
92
+ Next State: { value: 42 }
93
+
94
+ stdout | src/middleware/logger-middleware.test.ts > createLoggerMiddleware > should work with multiple actions
95
+ [Redux] INCREMENT
96
+ Prev State: { count: 0 }
97
+ Action: { type: 'INCREMENT' }
98
+ Next State: { count: 1 }
99
+ [Redux] INCREMENT
100
+ Prev State: { count: 1 }
101
+ Action: { type: 'INCREMENT' }
102
+ Next State: { count: 2 }
103
+ [Redux] DECREMENT
104
+ Prev State: { count: 2 }
105
+ Action: { type: 'DECREMENT' }
106
+ Next State: { count: 1 }
107
+
108
+ ✓ src/middleware/logger-middleware.test.ts (8 tests) 160ms
109
+ ✓ src/middleware/event-bridge-middleware.test.ts (8 tests) 73ms
110
+
111
+ Test Files 5 passed (5)
112
+ Tests 74 passed (74)
113
+ Start at 21:18:15
114
+ Duration 3.29s (transform 279ms, setup 0ms, collect 376ms, tests 608ms, environment 1ms, prepare 2.75s)
115
+
@@ -0,0 +1,3 @@
1
+ export * from './store-manager';
2
+ export * from './middleware-registry';
3
+ export * from './reducer-registry';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Middleware Registry Implementation
3
+ */
4
+ export class MiddlewareRegistry {
5
+ constructor() {
6
+ this.registrations = new Map();
7
+ this.locked = false;
8
+ }
9
+ register(registration) {
10
+ if (this.locked) {
11
+ throw new Error(`Cannot register middleware "${registration.id}" - store already created. ` +
12
+ `Register middleware during plugin initialization phase.`);
13
+ }
14
+ if (this.registrations.has(registration.id)) {
15
+ console.warn(`[MiddlewareRegistry] Middleware "${registration.id}" already registered, overwriting.`);
16
+ }
17
+ this.registrations.set(registration.id, Object.assign({ priority: 0 }, registration));
18
+ }
19
+ unregister(id) {
20
+ if (this.locked) {
21
+ throw new Error(`Cannot unregister middleware after store creation`);
22
+ }
23
+ this.registrations.delete(id);
24
+ }
25
+ getAll() {
26
+ const sorted = Array.from(this.registrations.values())
27
+ .sort((a, b) => (b.priority || 0) - (a.priority || 0));
28
+ return sorted.map(reg => reg.middleware);
29
+ }
30
+ has(id) {
31
+ return this.registrations.has(id);
32
+ }
33
+ getInfo(id) {
34
+ return this.registrations.get(id);
35
+ }
36
+ getAllRegistrations() {
37
+ return Array.from(this.registrations.values())
38
+ .sort((a, b) => (b.priority || 0) - (a.priority || 0));
39
+ }
40
+ /**
41
+ * Lock the registry (called when store is created)
42
+ * @internal
43
+ */
44
+ lock() {
45
+ this.locked = true;
46
+ }
47
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Reducer Registry Implementation
3
+ */
4
+ import { combineReducers } from 'redux';
5
+ export class ReducerRegistry {
6
+ constructor(onReducerChange) {
7
+ this.reducers = new Map();
8
+ this.onReducerChange = onReducerChange;
9
+ }
10
+ register(key, reducer, replace = false) {
11
+ if (!replace && this.reducers.has(key)) {
12
+ console.warn(`[ReducerRegistry] Reducer "${key}" already registered. Use replace=true to override.`);
13
+ return;
14
+ }
15
+ this.reducers.set(key, {
16
+ key,
17
+ reducer,
18
+ registeredAt: new Date(),
19
+ });
20
+ // Notify about reducer change for hot replacement
21
+ if (this.onReducerChange) {
22
+ this.onReducerChange(this.getCombinedReducer());
23
+ }
24
+ }
25
+ unregister(key) {
26
+ this.reducers.delete(key);
27
+ if (this.onReducerChange) {
28
+ this.onReducerChange(this.getCombinedReducer());
29
+ }
30
+ }
31
+ getAll() {
32
+ const map = {};
33
+ this.reducers.forEach((registration, key) => {
34
+ map[key] = registration.reducer;
35
+ });
36
+ return map;
37
+ }
38
+ getCombinedReducer() {
39
+ const reducerMap = this.getAll();
40
+ if (Object.keys(reducerMap).length === 0) {
41
+ return (state = {}) => state;
42
+ }
43
+ return combineReducers(reducerMap);
44
+ }
45
+ has(key) {
46
+ return this.reducers.has(key);
47
+ }
48
+ getInfo(key) {
49
+ return this.reducers.get(key);
50
+ }
51
+ getAllRegistrations() {
52
+ return Array.from(this.reducers.values());
53
+ }
54
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Store Manager Implementation
3
+ */
4
+ import { createStore, applyMiddleware, compose } from 'redux';
5
+ import { MiddlewareRegistry } from './middleware-registry';
6
+ import { ReducerRegistry } from './reducer-registry';
7
+ export class StoreManager {
8
+ constructor() {
9
+ this.store = null;
10
+ this.initialized = false;
11
+ this.config = null;
12
+ this.middlewareRegistry = new MiddlewareRegistry();
13
+ this.reducerRegistry = new ReducerRegistry((rootReducer) => {
14
+ // Hot replacement callback
15
+ if (this.store) {
16
+ this.store.replaceReducer(rootReducer);
17
+ }
18
+ });
19
+ }
20
+ getMiddlewareRegistry() {
21
+ return this.middlewareRegistry;
22
+ }
23
+ getReducerRegistry() {
24
+ return this.reducerRegistry;
25
+ }
26
+ isInitialized() {
27
+ return this.initialized;
28
+ }
29
+ initialize(config = {}) {
30
+ if (this.initialized) {
31
+ throw new Error('[StoreManager] Store already initialized');
32
+ }
33
+ this.config = config;
34
+ // Get all middleware in priority order
35
+ const middleware = this.middlewareRegistry.getAll();
36
+ // Lock the middleware registry
37
+ this.middlewareRegistry.lock();
38
+ // Get combined reducer
39
+ const rootReducer = this.reducerRegistry.getCombinedReducer();
40
+ // Create enhancers
41
+ const enhancers = config.enhancers || [];
42
+ // Setup Redux DevTools
43
+ let composeEnhancers = compose;
44
+ if (config.devTools !== false && typeof window !== 'undefined') {
45
+ const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
46
+ if (devToolsExtension) {
47
+ composeEnhancers = devToolsExtension({
48
+ trace: true,
49
+ traceLimit: 25,
50
+ });
51
+ }
52
+ }
53
+ // Create store
54
+ const enhancer = composeEnhancers(applyMiddleware(...middleware), ...enhancers);
55
+ this.store = createStore(rootReducer, config.preloadedState, enhancer);
56
+ this.initialized = true;
57
+ console.log('[StoreManager] Store initialized with:', {
58
+ reducers: this.reducerRegistry.getAllRegistrations().map(r => r.key),
59
+ middleware: this.middlewareRegistry.getAllRegistrations().map(m => ({
60
+ id: m.id,
61
+ priority: m.priority,
62
+ plugin: m.plugin,
63
+ })),
64
+ devTools: config.devTools !== false,
65
+ });
66
+ return this.store;
67
+ }
68
+ getStore() {
69
+ if (!this.store) {
70
+ throw new Error('[StoreManager] Store not initialized. Call initialize() first.');
71
+ }
72
+ return this.store;
73
+ }
74
+ dispatch(action) {
75
+ return this.getStore().dispatch(action);
76
+ }
77
+ getState() {
78
+ return this.getStore().getState();
79
+ }
80
+ subscribe(listener) {
81
+ return this.getStore().subscribe(listener);
82
+ }
83
+ replaceReducer(nextReducer) {
84
+ this.getStore().replaceReducer(nextReducer);
85
+ }
86
+ destroy() {
87
+ this.store = null;
88
+ this.initialized = false;
89
+ console.log('[StoreManager] Store destroyed');
90
+ }
91
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Store extensions collector
3
+ */
4
+ /**
5
+ * Collects middleware and reducer contributions until the store boots
6
+ */
7
+ export class StoreExtensionsCollector {
8
+ constructor() {
9
+ this.entries = [];
10
+ }
11
+ register(source, extensions) {
12
+ var _a;
13
+ const hasMiddleware = Boolean((_a = extensions.middleware) === null || _a === void 0 ? void 0 : _a.length);
14
+ const hasReducers = Boolean(extensions.reducers && Object.keys(extensions.reducers).length);
15
+ if (!hasMiddleware && !hasReducers) {
16
+ return;
17
+ }
18
+ this.entries.push({
19
+ source,
20
+ extensions: {
21
+ middleware: extensions.middleware,
22
+ reducers: extensions.reducers,
23
+ },
24
+ });
25
+ }
26
+ /**
27
+ * Drain collected extensions (used internally by the plugin)
28
+ */
29
+ drain() {
30
+ const snapshot = this.entries;
31
+ this.entries = [];
32
+ return snapshot;
33
+ }
34
+ }
35
+ function withSourceMetadata(middleware, source) {
36
+ var _a;
37
+ if (!middleware) {
38
+ return undefined;
39
+ }
40
+ return Object.assign({ plugin: (_a = middleware.plugin) !== null && _a !== void 0 ? _a : source }, middleware);
41
+ }
42
+ export function applyStoreExtensions(collector, middlewareRegistry, reducerRegistry) {
43
+ const entries = collector.drain();
44
+ entries.forEach(({ source, extensions }) => {
45
+ var _a;
46
+ (_a = extensions.middleware) === null || _a === void 0 ? void 0 : _a.forEach((mw) => {
47
+ const middleware = withSourceMetadata(mw, source);
48
+ if (middleware) {
49
+ middlewareRegistry.register(middleware);
50
+ }
51
+ });
52
+ if (extensions.reducers) {
53
+ Object.entries(extensions.reducers).forEach(([key, reducer]) => {
54
+ reducerRegistry.register(key, reducer);
55
+ });
56
+ }
57
+ });
58
+ }
59
+ export function createStoreExtensionsCollector() {
60
+ return new StoreExtensionsCollector();
61
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * UI Store Implementation
3
+ * Concrete implementations of Redux store management
4
+ */
5
+ export * from './core';
6
+ export * from './middleware';
7
+ export * from './plugin';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Event Bridge Middleware
3
+ * Bridges Redux actions to microkernel event bus
4
+ */
5
+ export function createEventBridgeMiddleware(hooks) {
6
+ return (store) => (next) => (action) => {
7
+ // Emit before action
8
+ hooks === null || hooks === void 0 ? void 0 : hooks.emit('redux:action:before', { action, state: store.getState() });
9
+ // Execute action
10
+ const result = next(action);
11
+ // Emit after action
12
+ hooks === null || hooks === void 0 ? void 0 : hooks.emit('redux:action:after', { action, state: store.getState() });
13
+ // Emit specific action type event
14
+ if (action === null || action === void 0 ? void 0 : action.type) {
15
+ hooks === null || hooks === void 0 ? void 0 : hooks.emit(`redux:action:${action.type}`, action);
16
+ }
17
+ return result;
18
+ };
19
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Middleware Export
3
+ */
4
+ export * from './event-bridge-middleware';
5
+ export * from './logger-middleware';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Logger Middleware
3
+ * Development logger for Redux actions
4
+ */
5
+ export function createLoggerMiddleware() {
6
+ return (store) => (next) => (action) => {
7
+ if (typeof (action === null || action === void 0 ? void 0 : action.type) === 'string') {
8
+ console.group(`[Redux] ${action.type}`);
9
+ console.log('Prev State:', store.getState());
10
+ console.log('Action:', action);
11
+ const result = next(action);
12
+ console.log('Next State:', store.getState());
13
+ console.groupEnd();
14
+ return result;
15
+ }
16
+ return next(action);
17
+ };
18
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Plugin Export
3
+ */
4
+ export * from './store-plugin-factory';
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Store Plugin Factory
3
+ * Creates a microkernel plugin for Redux store management
4
+ */
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
14
+ import { STORE_MANAGER_TOKEN, MIDDLEWARE_REGISTRY_TOKEN, REDUCER_REGISTRY_TOKEN, STORE_EXTENSIONS_TOKEN, } from '@hamak/ui-store-api';
15
+ import { StoreManager } from '../core/store-manager';
16
+ import { createEventBridgeMiddleware, createLoggerMiddleware, } from '../middleware';
17
+ import { applyStoreExtensions, createStoreExtensionsCollector, } from '../extensions/store-extensions';
18
+ export function createStorePlugin(config = {}) {
19
+ const storeManager = new StoreManager();
20
+ const middlewareRegistry = storeManager.getMiddlewareRegistry();
21
+ const reducerRegistry = storeManager.getReducerRegistry();
22
+ const extensionsCollector = createStoreExtensionsCollector();
23
+ const registerDefaultMiddleware = (hooks) => {
24
+ extensionsCollector.register('ui-store:event-bridge', {
25
+ middleware: [
26
+ {
27
+ id: 'event-bridge',
28
+ middleware: createEventBridgeMiddleware(hooks),
29
+ priority: 1000,
30
+ plugin: 'ui-store',
31
+ description: 'Bridges Redux actions to microkernel events',
32
+ },
33
+ ],
34
+ });
35
+ if (config.logger !== false &&
36
+ process.env.NODE_ENV === 'development') {
37
+ extensionsCollector.register('ui-store:logger', {
38
+ middleware: [
39
+ {
40
+ id: 'logger',
41
+ middleware: createLoggerMiddleware(),
42
+ priority: -1000,
43
+ plugin: 'ui-store',
44
+ description: 'Development logger',
45
+ },
46
+ ],
47
+ });
48
+ }
49
+ };
50
+ const registerConfigExtensions = () => {
51
+ var _a;
52
+ if ((_a = config.middleware) === null || _a === void 0 ? void 0 : _a.length) {
53
+ const middleware = config.middleware.map((mw) => {
54
+ var _a;
55
+ return (Object.assign(Object.assign({}, mw), { plugin: (_a = mw.plugin) !== null && _a !== void 0 ? _a : 'ui-store-config' }));
56
+ });
57
+ extensionsCollector.register('ui-store-config:middleware', {
58
+ middleware,
59
+ });
60
+ }
61
+ if (config.reducers && Object.keys(config.reducers).length) {
62
+ extensionsCollector.register('ui-store-config:reducers', {
63
+ reducers: config.reducers,
64
+ });
65
+ }
66
+ };
67
+ return {
68
+ initialize(ctx) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ // Register services via DI
71
+ ctx.provide({ provide: STORE_MANAGER_TOKEN, useValue: storeManager });
72
+ ctx.provide({
73
+ provide: MIDDLEWARE_REGISTRY_TOKEN,
74
+ useValue: middlewareRegistry,
75
+ });
76
+ ctx.provide({
77
+ provide: REDUCER_REGISTRY_TOKEN,
78
+ useValue: reducerRegistry,
79
+ });
80
+ ctx.provide({
81
+ provide: STORE_EXTENSIONS_TOKEN,
82
+ useValue: extensionsCollector,
83
+ });
84
+ registerDefaultMiddleware(ctx.hooks);
85
+ registerConfigExtensions();
86
+ console.log('[ui-store] Plugin initialized');
87
+ });
88
+ },
89
+ activate(ctx) {
90
+ return __awaiter(this, void 0, void 0, function* () {
91
+ applyStoreExtensions(extensionsCollector, middlewareRegistry, reducerRegistry);
92
+ // Initialize the store after all plugins have registered middleware/reducers
93
+ const store = storeManager.initialize({
94
+ devTools: config.devTools,
95
+ });
96
+ // Bridge Redux state changes to microkernel events
97
+ store.subscribe(() => {
98
+ ctx.hooks.emit('redux:state-changed', store.getState());
99
+ });
100
+ // Emit ready event
101
+ ctx.hooks.emit('ui-store:ready', { store });
102
+ console.log('[ui-store] Plugin activated');
103
+ });
104
+ },
105
+ deactivate() {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ storeManager.destroy();
108
+ console.log('[ui-store] Plugin deactivated');
109
+ });
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Store extensions collector
3
+ */
4
+ import type { IMiddlewareRegistry, IReducerRegistry, StoreExtensionsRegistry, StorePluginExtensions } from '@hamak/ui-store-api';
5
+ interface StoreExtensionsEntry {
6
+ source: string;
7
+ extensions: StorePluginExtensions;
8
+ }
9
+ /**
10
+ * Collects middleware and reducer contributions until the store boots
11
+ */
12
+ export declare class StoreExtensionsCollector implements StoreExtensionsRegistry {
13
+ private entries;
14
+ register(source: string, extensions: StorePluginExtensions): void;
15
+ /**
16
+ * Drain collected extensions (used internally by the plugin)
17
+ */
18
+ drain(): StoreExtensionsEntry[];
19
+ }
20
+ export declare function applyStoreExtensions(collector: StoreExtensionsCollector, middlewareRegistry: IMiddlewareRegistry, reducerRegistry: IReducerRegistry): void;
21
+ export declare function createStoreExtensionsCollector(): StoreExtensionsCollector;
22
+ export {};
23
+ //# sourceMappingURL=store-extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-extensions.d.ts","sourceRoot":"","sources":["../../src/extensions/store-extensions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAE7B,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,wBAAyB,YAAW,uBAAuB;IACtE,OAAO,CAAC,OAAO,CAA8B;IAE7C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,qBAAqB,GAAG,IAAI;IAiBjE;;OAEG;IACH,KAAK,IAAI,oBAAoB,EAAE;CAKhC;AAgBD,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,wBAAwB,EACnC,kBAAkB,EAAE,mBAAmB,EACvC,eAAe,EAAE,gBAAgB,GAChC,IAAI,CAiBN;AAED,wBAAgB,8BAA8B,IAAI,wBAAwB,CAEzE"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Store extensions collector
3
+ */
4
+ /**
5
+ * Collects middleware and reducer contributions until the store boots
6
+ */
7
+ export class StoreExtensionsCollector {
8
+ constructor() {
9
+ this.entries = [];
10
+ }
11
+ register(source, extensions) {
12
+ const hasMiddleware = Boolean(extensions.middleware?.length);
13
+ const hasReducers = Boolean(extensions.reducers && Object.keys(extensions.reducers).length);
14
+ if (!hasMiddleware && !hasReducers) {
15
+ return;
16
+ }
17
+ this.entries.push({
18
+ source,
19
+ extensions: {
20
+ middleware: extensions.middleware,
21
+ reducers: extensions.reducers,
22
+ },
23
+ });
24
+ }
25
+ /**
26
+ * Drain collected extensions (used internally by the plugin)
27
+ */
28
+ drain() {
29
+ const snapshot = this.entries;
30
+ this.entries = [];
31
+ return snapshot;
32
+ }
33
+ }
34
+ function withSourceMetadata(middleware, source) {
35
+ if (!middleware) {
36
+ return undefined;
37
+ }
38
+ return {
39
+ plugin: middleware.plugin ?? source,
40
+ ...middleware,
41
+ };
42
+ }
43
+ export function applyStoreExtensions(collector, middlewareRegistry, reducerRegistry) {
44
+ const entries = collector.drain();
45
+ entries.forEach(({ source, extensions }) => {
46
+ extensions.middleware?.forEach((mw) => {
47
+ const middleware = withSourceMetadata(mw, source);
48
+ if (middleware) {
49
+ middlewareRegistry.register(middleware);
50
+ }
51
+ });
52
+ if (extensions.reducers) {
53
+ Object.entries(extensions.reducers).forEach(([key, reducer]) => {
54
+ reducerRegistry.register(key, reducer);
55
+ });
56
+ }
57
+ });
58
+ }
59
+ export function createStoreExtensionsCollector() {
60
+ return new StoreExtensionsCollector();
61
+ }
@@ -3,20 +3,12 @@
3
3
  * Creates a microkernel plugin for Redux store management
4
4
  */
5
5
  import type { PluginModule } from '@hamak/microkernel-spi';
6
- import type { Reducer } from 'redux';
7
- export interface StorePluginConfig {
6
+ import { type StorePluginExtensions } from '@hamak/ui-store-api';
7
+ export interface StorePluginConfig extends StorePluginExtensions {
8
8
  /** Enable Redux DevTools integration */
9
9
  devTools?: boolean;
10
10
  /** Enable logger middleware in development */
11
11
  logger?: boolean;
12
- /** Initial middleware to register */
13
- middleware?: Array<{
14
- id: string;
15
- middleware: any;
16
- priority?: number;
17
- }>;
18
- /** Initial reducers to register */
19
- reducers?: Record<string, Reducer>;
20
12
  }
21
13
  export declare function createStorePlugin(config?: StorePluginConfig): PluginModule;
22
14
  //# sourceMappingURL=store-plugin-factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-plugin-factory.d.ts","sourceRoot":"","sources":["../../src/plugin/store-plugin-factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAYrC,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,qCAAqC;IACrC,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,GAAG,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAEH,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,iBAAsB,GAC7B,YAAY,CAqFd"}
1
+ {"version":3,"file":"store-plugin-factory.d.ts","sourceRoot":"","sources":["../../src/plugin/store-plugin-factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAML,KAAK,qBAAqB,EAC3B,MAAM,qBAAqB,CAAC;AAW7B,MAAM,WAAW,iBAAkB,SAAQ,qBAAqB;IAC9D,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,iBAAsB,GAC7B,YAAY,CA2Gd"}
@@ -2,13 +2,58 @@
2
2
  * Store Plugin Factory
3
3
  * Creates a microkernel plugin for Redux store management
4
4
  */
5
- import { STORE_MANAGER_TOKEN, MIDDLEWARE_REGISTRY_TOKEN, REDUCER_REGISTRY_TOKEN, } from '@hamak/ui-store-api';
5
+ import { STORE_MANAGER_TOKEN, MIDDLEWARE_REGISTRY_TOKEN, REDUCER_REGISTRY_TOKEN, STORE_EXTENSIONS_TOKEN, } from '@hamak/ui-store-api';
6
6
  import { StoreManager } from '../core/store-manager';
7
7
  import { createEventBridgeMiddleware, createLoggerMiddleware, } from '../middleware';
8
+ import { applyStoreExtensions, createStoreExtensionsCollector, } from '../extensions/store-extensions';
8
9
  export function createStorePlugin(config = {}) {
9
10
  const storeManager = new StoreManager();
10
11
  const middlewareRegistry = storeManager.getMiddlewareRegistry();
11
12
  const reducerRegistry = storeManager.getReducerRegistry();
13
+ const extensionsCollector = createStoreExtensionsCollector();
14
+ const registerDefaultMiddleware = (hooks) => {
15
+ extensionsCollector.register('ui-store:event-bridge', {
16
+ middleware: [
17
+ {
18
+ id: 'event-bridge',
19
+ middleware: createEventBridgeMiddleware(hooks),
20
+ priority: 1000,
21
+ plugin: 'ui-store',
22
+ description: 'Bridges Redux actions to microkernel events',
23
+ },
24
+ ],
25
+ });
26
+ if (config.logger !== false &&
27
+ process.env.NODE_ENV === 'development') {
28
+ extensionsCollector.register('ui-store:logger', {
29
+ middleware: [
30
+ {
31
+ id: 'logger',
32
+ middleware: createLoggerMiddleware(),
33
+ priority: -1000,
34
+ plugin: 'ui-store',
35
+ description: 'Development logger',
36
+ },
37
+ ],
38
+ });
39
+ }
40
+ };
41
+ const registerConfigExtensions = () => {
42
+ if (config.middleware?.length) {
43
+ const middleware = config.middleware.map((mw) => ({
44
+ ...mw,
45
+ plugin: mw.plugin ?? 'ui-store-config',
46
+ }));
47
+ extensionsCollector.register('ui-store-config:middleware', {
48
+ middleware,
49
+ });
50
+ }
51
+ if (config.reducers && Object.keys(config.reducers).length) {
52
+ extensionsCollector.register('ui-store-config:reducers', {
53
+ reducers: config.reducers,
54
+ });
55
+ }
56
+ };
12
57
  return {
13
58
  async initialize(ctx) {
14
59
  // Register services via DI
@@ -21,46 +66,16 @@ export function createStorePlugin(config = {}) {
21
66
  provide: REDUCER_REGISTRY_TOKEN,
22
67
  useValue: reducerRegistry,
23
68
  });
24
- // Register event bridge middleware (high priority)
25
- middlewareRegistry.register({
26
- id: 'event-bridge',
27
- middleware: createEventBridgeMiddleware(ctx.hooks),
28
- priority: 1000,
29
- plugin: 'ui-store',
30
- description: 'Bridges Redux actions to microkernel events',
31
- });
32
- // Register logger middleware in development (low priority - runs last)
33
- if (config.logger !== false &&
34
- process.env.NODE_ENV === 'development') {
35
- middlewareRegistry.register({
36
- id: 'logger',
37
- middleware: createLoggerMiddleware(),
38
- priority: -1000,
39
- plugin: 'ui-store',
40
- description: 'Development logger',
41
- });
42
- }
43
- // Register user-provided middleware
44
- config.middleware?.forEach((mw) => {
45
- middlewareRegistry.register({
46
- ...mw,
47
- plugin: 'ui-store-config',
48
- });
69
+ ctx.provide({
70
+ provide: STORE_EXTENSIONS_TOKEN,
71
+ useValue: extensionsCollector,
49
72
  });
50
- // Register user-provided reducers
51
- if (config.reducers) {
52
- console.log('[ui-store] Registering reducers:', Object.keys(config.reducers));
53
- Object.entries(config.reducers).forEach(([key, reducer]) => {
54
- reducerRegistry.register(key, reducer);
55
- console.log('[ui-store] Registered reducer:', key);
56
- });
57
- }
58
- else {
59
- console.log('[ui-store] No reducers provided in config');
60
- }
73
+ registerDefaultMiddleware(ctx.hooks);
74
+ registerConfigExtensions();
61
75
  console.log('[ui-store] Plugin initialized');
62
76
  },
63
77
  async activate(ctx) {
78
+ applyStoreExtensions(extensionsCollector, middlewareRegistry, reducerRegistry);
64
79
  // Initialize the store after all plugins have registered middleware/reducers
65
80
  const store = storeManager.initialize({
66
81
  devTools: config.devTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamak/ui-store-impl",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "UI Store Implementation - Redux store implementation with middleware",
@@ -16,7 +16,7 @@
16
16
  "access": "public"
17
17
  },
18
18
  "scripts": {
19
- "build": "tsc -p tsconfig.json",
19
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.es2015.json",
20
20
  "clean": "rm -rf dist",
21
21
  "test": "vitest run",
22
22
  "test:watch": "vitest"
@@ -24,14 +24,20 @@
24
24
  "exports": {
25
25
  ".": {
26
26
  "types": "./dist/index.d.ts",
27
- "import": "./dist/index.js"
27
+ "import": "./dist/index.js",
28
+ "default": "./dist/index.js",
29
+ "legacy": "./dist/es2015/index.js"
30
+ },
31
+ "./es2015": {
32
+ "import": "./dist/es2015/index.js",
33
+ "default": "./dist/es2015/index.js"
28
34
  }
29
35
  },
30
36
  "dependencies": {
31
- "@hamak/ui-store-api": "0.2.1",
32
- "@hamak/ui-store-spi": "0.2.1",
33
- "@hamak/microkernel-api": "0.2.1",
34
- "@hamak/microkernel-spi": "0.2.1",
37
+ "@hamak/ui-store-api": "0.2.2",
38
+ "@hamak/ui-store-spi": "0.2.2",
39
+ "@hamak/microkernel-api": "0.2.2",
40
+ "@hamak/microkernel-spi": "0.2.2",
35
41
  "redux": "^5.0.1",
36
42
  "redux-thunk": "^3.1.0"
37
43
  },
@@ -2,7 +2,7 @@
2
2
  * MiddlewareRegistry Tests
3
3
  */
4
4
 
5
- import { describe, test, expect, beforeEach } from 'bun:test';
5
+ import { describe, test, expect, beforeEach, vi } from 'vitest';
6
6
  import { MiddlewareRegistry } from './middleware-registry';
7
7
  import type { Middleware } from 'redux';
8
8
  import type { MiddlewareRegistration } from '@hamak/ui-store-api';
@@ -2,16 +2,16 @@
2
2
  * ReducerRegistry Tests
3
3
  */
4
4
 
5
- import { describe, test, expect, beforeEach, mock } from 'bun:test';
5
+ import { describe, test, expect, beforeEach, vi } from 'vitest';
6
6
  import { ReducerRegistry } from './reducer-registry';
7
7
  import type { Reducer } from 'redux';
8
8
 
9
9
  describe('ReducerRegistry', () => {
10
10
  let registry: ReducerRegistry;
11
- let onReducerChange: ReturnType<typeof mock>;
11
+ let onReducerChange: ReturnType<typeof vi.fn>;
12
12
 
13
13
  beforeEach(() => {
14
- onReducerChange = mock(() => {});
14
+ onReducerChange = vi.fn(() => {});
15
15
  registry = new ReducerRegistry(onReducerChange);
16
16
  });
17
17
 
@@ -2,7 +2,7 @@
2
2
  * StoreManager Tests
3
3
  */
4
4
 
5
- import { describe, test, expect, beforeEach, mock } from 'bun:test';
5
+ import { describe, test, expect, beforeEach, vi } from 'vitest';
6
6
  import { StoreManager } from './store-manager';
7
7
  import type { Middleware, Reducer } from 'redux';
8
8
 
@@ -202,7 +202,7 @@ describe('StoreManager', () => {
202
202
  manager.getReducerRegistry().register('counter', reducer);
203
203
  manager.initialize();
204
204
 
205
- const listener = mock(() => {});
205
+ const listener = vi.fn(() => {});
206
206
  const unsubscribe = manager.subscribe(listener);
207
207
 
208
208
  manager.dispatch({ type: 'INCREMENT' });
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Store extensions collector
3
+ */
4
+
5
+ import type {
6
+ IMiddlewareRegistry,
7
+ IReducerRegistry,
8
+ StoreExtensionsRegistry,
9
+ StorePluginExtensions,
10
+ StoreMiddlewareExtension,
11
+ } from '@hamak/ui-store-api';
12
+
13
+ interface StoreExtensionsEntry {
14
+ source: string;
15
+ extensions: StorePluginExtensions;
16
+ }
17
+
18
+ /**
19
+ * Collects middleware and reducer contributions until the store boots
20
+ */
21
+ export class StoreExtensionsCollector implements StoreExtensionsRegistry {
22
+ private entries: StoreExtensionsEntry[] = [];
23
+
24
+ register(source: string, extensions: StorePluginExtensions): void {
25
+ const hasMiddleware = Boolean(extensions.middleware?.length);
26
+ const hasReducers = Boolean(extensions.reducers && Object.keys(extensions.reducers).length);
27
+
28
+ if (!hasMiddleware && !hasReducers) {
29
+ return;
30
+ }
31
+
32
+ this.entries.push({
33
+ source,
34
+ extensions: {
35
+ middleware: extensions.middleware,
36
+ reducers: extensions.reducers,
37
+ },
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Drain collected extensions (used internally by the plugin)
43
+ */
44
+ drain(): StoreExtensionsEntry[] {
45
+ const snapshot = this.entries;
46
+ this.entries = [];
47
+ return snapshot;
48
+ }
49
+ }
50
+
51
+ function withSourceMetadata(
52
+ middleware: StoreMiddlewareExtension | undefined,
53
+ source: string
54
+ ): StoreMiddlewareExtension | undefined {
55
+ if (!middleware) {
56
+ return undefined;
57
+ }
58
+
59
+ return {
60
+ plugin: middleware.plugin ?? source,
61
+ ...middleware,
62
+ };
63
+ }
64
+
65
+ export function applyStoreExtensions(
66
+ collector: StoreExtensionsCollector,
67
+ middlewareRegistry: IMiddlewareRegistry,
68
+ reducerRegistry: IReducerRegistry
69
+ ): void {
70
+ const entries = collector.drain();
71
+
72
+ entries.forEach(({ source, extensions }) => {
73
+ extensions.middleware?.forEach((mw) => {
74
+ const middleware = withSourceMetadata(mw, source);
75
+ if (middleware) {
76
+ middlewareRegistry.register(middleware);
77
+ }
78
+ });
79
+
80
+ if (extensions.reducers) {
81
+ Object.entries(extensions.reducers).forEach(([key, reducer]) => {
82
+ reducerRegistry.register(key, reducer);
83
+ });
84
+ }
85
+ });
86
+ }
87
+
88
+ export function createStoreExtensionsCollector(): StoreExtensionsCollector {
89
+ return new StoreExtensionsCollector();
90
+ }
@@ -2,13 +2,13 @@
2
2
  * Event Bridge Middleware Tests
3
3
  */
4
4
 
5
- import { describe, test, expect, mock } from 'bun:test';
5
+ import { describe, test, expect, vi } from 'vitest';
6
6
  import { createEventBridgeMiddleware } from './event-bridge-middleware';
7
7
  import { createStore, applyMiddleware } from 'redux';
8
8
 
9
9
  describe('createEventBridgeMiddleware', () => {
10
10
  test('should create middleware', () => {
11
- const hooks = { emit: mock(() => {}) };
11
+ const hooks = { emit: vi.fn(() => {}) };
12
12
  const middleware = createEventBridgeMiddleware(hooks);
13
13
 
14
14
  expect(middleware).toBeDefined();
@@ -16,7 +16,7 @@ describe('createEventBridgeMiddleware', () => {
16
16
  });
17
17
 
18
18
  test('should emit before and after action events', () => {
19
- const hooks = { emit: mock(() => {}) };
19
+ const hooks = { emit: vi.fn(() => {}) };
20
20
  const middleware = createEventBridgeMiddleware(hooks);
21
21
 
22
22
  const reducer = (state = {}) => state;
@@ -37,7 +37,7 @@ describe('createEventBridgeMiddleware', () => {
37
37
  });
38
38
 
39
39
  test('should emit specific action type event', () => {
40
- const hooks = { emit: mock(() => {}) };
40
+ const hooks = { emit: vi.fn(() => {}) };
41
41
  const middleware = createEventBridgeMiddleware(hooks);
42
42
 
43
43
  const reducer = (state = {}) => state;
@@ -64,10 +64,10 @@ describe('createEventBridgeMiddleware', () => {
64
64
  });
65
65
 
66
66
  test('should pass action through middleware chain', () => {
67
- const hooks = { emit: mock(() => {}) };
67
+ const hooks = { emit: vi.fn(() => {}) };
68
68
  const middleware = createEventBridgeMiddleware(hooks);
69
69
 
70
- const reducer = mock((state = { count: 0 }, action: any) => {
70
+ const reducer = vi.fn((state = { count: 0 }, action: any) => {
71
71
  if (action.type === 'INCREMENT') {
72
72
  return { count: state.count + 1 };
73
73
  }
@@ -85,7 +85,7 @@ describe('createEventBridgeMiddleware', () => {
85
85
  test('should emit events in correct order', () => {
86
86
  const eventOrder: string[] = [];
87
87
  const hooks = {
88
- emit: mock((event: string) => {
88
+ emit: vi.fn((event: string) => {
89
89
  eventOrder.push(event);
90
90
  }),
91
91
  };
@@ -104,7 +104,7 @@ describe('createEventBridgeMiddleware', () => {
104
104
  });
105
105
 
106
106
  test('should include state in before/after events', () => {
107
- const hooks = { emit: mock(() => {}) };
107
+ const hooks = { emit: vi.fn(() => {}) };
108
108
  const middleware = createEventBridgeMiddleware(hooks);
109
109
 
110
110
  const reducer = (state = { value: 0 }, action: any) => {
@@ -2,7 +2,7 @@
2
2
  * Logger Middleware Tests
3
3
  */
4
4
 
5
- import { describe, test, expect, mock, spyOn } from 'bun:test';
5
+ import { describe, test, expect, vi } from 'vitest';
6
6
  import { createLoggerMiddleware } from './logger-middleware';
7
7
  import { createStore, applyMiddleware } from 'redux';
8
8
 
@@ -15,9 +15,9 @@ describe('createLoggerMiddleware', () => {
15
15
  });
16
16
 
17
17
  test('should log actions with type', () => {
18
- const consoleGroupSpy = spyOn(console, 'group');
19
- const consoleLogSpy = spyOn(console, 'log');
20
- const consoleGroupEndSpy = spyOn(console, 'groupEnd');
18
+ const consoleGroupSpy = vi.spyOn(console, 'group');
19
+ const consoleLogSpy = vi.spyOn(console, 'log');
20
+ const consoleGroupEndSpy = vi.spyOn(console, 'groupEnd');
21
21
 
22
22
  const middleware = createLoggerMiddleware();
23
23
  const reducer = (state = { count: 0 }, action: any) => {
@@ -42,7 +42,7 @@ describe('createLoggerMiddleware', () => {
42
42
  });
43
43
 
44
44
  test('should not log actions without string type', () => {
45
- const consoleGroupSpy = spyOn(console, 'group');
45
+ const consoleGroupSpy = vi.spyOn(console, 'group');
46
46
  const middleware = createLoggerMiddleware();
47
47
 
48
48
  const reducer = (state = {}) => state;
@@ -56,7 +56,7 @@ describe('createLoggerMiddleware', () => {
56
56
 
57
57
  test('should pass action through middleware chain', () => {
58
58
  const middleware = createLoggerMiddleware();
59
- const reducer = mock((state = {}) => state);
59
+ const reducer = vi.fn((state = {}) => state);
60
60
 
61
61
  const store = createStore(reducer, applyMiddleware(middleware));
62
62
  store.dispatch({ type: 'TEST' });
@@ -81,7 +81,7 @@ describe('createLoggerMiddleware', () => {
81
81
  });
82
82
 
83
83
  test('should log state before and after action', () => {
84
- const consoleLogSpy = spyOn(console, 'log');
84
+ const consoleLogSpy = vi.spyOn(console, 'log');
85
85
  const middleware = createLoggerMiddleware();
86
86
 
87
87
  const reducer = (state = { value: 0 }, action: any) => {
@@ -101,7 +101,7 @@ describe('createLoggerMiddleware', () => {
101
101
  });
102
102
 
103
103
  test('should work with multiple actions', () => {
104
- const consoleGroupSpy = spyOn(console, 'group');
104
+ const consoleGroupSpy = vi.spyOn(console, 'group');
105
105
  const middleware = createLoggerMiddleware();
106
106
 
107
107
  const reducer = (state = { count: 0 }, action: any) => {
@@ -4,34 +4,30 @@
4
4
  */
5
5
 
6
6
  import type { PluginModule } from '@hamak/microkernel-spi';
7
- import type { Reducer } from 'redux';
8
7
  import {
9
8
  STORE_MANAGER_TOKEN,
10
9
  MIDDLEWARE_REGISTRY_TOKEN,
11
10
  REDUCER_REGISTRY_TOKEN,
11
+ STORE_EXTENSIONS_TOKEN,
12
+ type StoreMiddlewareExtension,
13
+ type StorePluginExtensions,
12
14
  } from '@hamak/ui-store-api';
13
15
  import { StoreManager } from '../core/store-manager';
14
16
  import {
15
17
  createEventBridgeMiddleware,
16
18
  createLoggerMiddleware,
17
19
  } from '../middleware';
20
+ import {
21
+ applyStoreExtensions,
22
+ createStoreExtensionsCollector,
23
+ } from '../extensions/store-extensions';
18
24
 
19
- export interface StorePluginConfig {
25
+ export interface StorePluginConfig extends StorePluginExtensions {
20
26
  /** Enable Redux DevTools integration */
21
27
  devTools?: boolean;
22
28
 
23
29
  /** Enable logger middleware in development */
24
30
  logger?: boolean;
25
-
26
- /** Initial middleware to register */
27
- middleware?: Array<{
28
- id: string;
29
- middleware: any;
30
- priority?: number;
31
- }>;
32
-
33
- /** Initial reducers to register */
34
- reducers?: Record<string, Reducer>;
35
31
  }
36
32
 
37
33
  export function createStorePlugin(
@@ -40,6 +36,57 @@ export function createStorePlugin(
40
36
  const storeManager = new StoreManager();
41
37
  const middlewareRegistry = storeManager.getMiddlewareRegistry();
42
38
  const reducerRegistry = storeManager.getReducerRegistry();
39
+ const extensionsCollector = createStoreExtensionsCollector();
40
+
41
+ const registerDefaultMiddleware = (hooks: any) => {
42
+ extensionsCollector.register('ui-store:event-bridge', {
43
+ middleware: [
44
+ {
45
+ id: 'event-bridge',
46
+ middleware: createEventBridgeMiddleware(hooks),
47
+ priority: 1000,
48
+ plugin: 'ui-store',
49
+ description: 'Bridges Redux actions to microkernel events',
50
+ },
51
+ ],
52
+ });
53
+
54
+ if (
55
+ config.logger !== false &&
56
+ process.env.NODE_ENV === 'development'
57
+ ) {
58
+ extensionsCollector.register('ui-store:logger', {
59
+ middleware: [
60
+ {
61
+ id: 'logger',
62
+ middleware: createLoggerMiddleware(),
63
+ priority: -1000,
64
+ plugin: 'ui-store',
65
+ description: 'Development logger',
66
+ },
67
+ ],
68
+ });
69
+ }
70
+ };
71
+
72
+ const registerConfigExtensions = () => {
73
+ if (config.middleware?.length) {
74
+ const middleware: StoreMiddlewareExtension[] = config.middleware.map((mw) => ({
75
+ ...mw,
76
+ plugin: mw.plugin ?? 'ui-store-config',
77
+ }));
78
+
79
+ extensionsCollector.register('ui-store-config:middleware', {
80
+ middleware,
81
+ });
82
+ }
83
+
84
+ if (config.reducers && Object.keys(config.reducers).length) {
85
+ extensionsCollector.register('ui-store-config:reducers', {
86
+ reducers: config.reducers,
87
+ });
88
+ }
89
+ };
43
90
 
44
91
  return {
45
92
  async initialize(ctx) {
@@ -53,53 +100,24 @@ export function createStorePlugin(
53
100
  provide: REDUCER_REGISTRY_TOKEN,
54
101
  useValue: reducerRegistry,
55
102
  });
56
-
57
- // Register event bridge middleware (high priority)
58
- middlewareRegistry.register({
59
- id: 'event-bridge',
60
- middleware: createEventBridgeMiddleware(ctx.hooks),
61
- priority: 1000,
62
- plugin: 'ui-store',
63
- description: 'Bridges Redux actions to microkernel events',
64
- });
65
-
66
- // Register logger middleware in development (low priority - runs last)
67
- if (
68
- config.logger !== false &&
69
- process.env.NODE_ENV === 'development'
70
- ) {
71
- middlewareRegistry.register({
72
- id: 'logger',
73
- middleware: createLoggerMiddleware(),
74
- priority: -1000,
75
- plugin: 'ui-store',
76
- description: 'Development logger',
77
- });
78
- }
79
-
80
- // Register user-provided middleware
81
- config.middleware?.forEach((mw) => {
82
- middlewareRegistry.register({
83
- ...mw,
84
- plugin: 'ui-store-config',
85
- });
103
+ ctx.provide({
104
+ provide: STORE_EXTENSIONS_TOKEN,
105
+ useValue: extensionsCollector,
86
106
  });
87
107
 
88
- // Register user-provided reducers
89
- if (config.reducers) {
90
- console.log('[ui-store] Registering reducers:', Object.keys(config.reducers));
91
- Object.entries(config.reducers).forEach(([key, reducer]) => {
92
- reducerRegistry.register(key, reducer);
93
- console.log('[ui-store] Registered reducer:', key);
94
- });
95
- } else {
96
- console.log('[ui-store] No reducers provided in config');
97
- }
108
+ registerDefaultMiddleware(ctx.hooks);
109
+ registerConfigExtensions();
98
110
 
99
111
  console.log('[ui-store] Plugin initialized');
100
112
  },
101
113
 
102
114
  async activate(ctx) {
115
+ applyStoreExtensions(
116
+ extensionsCollector,
117
+ middlewareRegistry,
118
+ reducerRegistry
119
+ );
120
+
103
121
  // Initialize the store after all plugins have registered middleware/reducers
104
122
  const store = storeManager.initialize({
105
123
  devTools: config.devTools,
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "target": "ES2015",
5
+ "lib": [
6
+ "ES2015",
7
+ "DOM"
8
+ ],
9
+ "outDir": "./dist/es2015",
10
+ "declaration": false,
11
+ "declarationMap": false,
12
+ "sourceMap": false,
13
+ "downlevelIteration": true,
14
+ "composite": false
15
+ },
16
+ "include": [
17
+ "src/**/*"
18
+ ],
19
+ "exclude": [
20
+ "node_modules",
21
+ "dist",
22
+ "**/*.test.ts"
23
+ ]
24
+ }