@rplx/core 0.2.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/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @rplx/core
2
+
3
+ A re-frame inspired state management library for TypeScript.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rplx/core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { createStore } from '@rplx/core'
15
+
16
+ // Define your state
17
+ interface AppState {
18
+ count: number
19
+ }
20
+
21
+ // Define your coeffects (optional)
22
+ interface AppCoeffects {
23
+ uuid: string
24
+ now: Date
25
+ }
26
+
27
+ // Create store with coeffect providers
28
+ const store = createStore<AppState, AppCoeffects>({
29
+ initialState: { count: 0 },
30
+ coeffects: {
31
+ uuid: () => crypto.randomUUID(),
32
+ now: () => new Date()
33
+ }
34
+ })
35
+
36
+ // Register event handlers - fully type-safe!
37
+ store.registerEvent('increment', (context, payload) => {
38
+ // context.db is AppState
39
+ // context.uuid is string
40
+ // context.now is Date
41
+ return [
42
+ { count: context.db.count + 1 },
43
+ {} // no effects
44
+ ]
45
+ })
46
+
47
+ // Dispatch events
48
+ await store.dispatch('increment')
49
+
50
+ // Get state
51
+ const state = store.getState()
52
+ ```
53
+
54
+ ## Core Concepts
55
+
56
+ ### Events
57
+ Events are dispatched to trigger state changes. Each event has a handler that receives the current state (via context) and a payload, and returns a new state and effects.
58
+
59
+ ### Coeffects (Context)
60
+ Coeffects are the **inputs** to event handlers. Define your coeffect types and provide functions to compute them:
61
+
62
+ ```typescript
63
+ interface MyCoeffects {
64
+ uuid: string
65
+ now: Date
66
+ }
67
+
68
+ const store = createStore<AppState, MyCoeffects>({
69
+ initialState,
70
+ coeffects: {
71
+ uuid: () => crypto.randomUUID(),
72
+ now: () => new Date()
73
+ }
74
+ })
75
+
76
+ // Handlers get type-safe access
77
+ store.registerEvent('create', (context, data) => {
78
+ const { db, uuid, now } = context // ✅ Fully typed!
79
+ return [newState, effects]
80
+ })
81
+ ```
82
+
83
+ **Benefits:**
84
+ - ✅ Full type safety
85
+ - ✅ Easy to test (mock coeffect providers)
86
+ - ✅ Lazy evaluation
87
+
88
+ ### Effects
89
+ Effects are side effects that can be triggered by event handlers. Common effects include:
90
+ - `dispatch` - Dispatch another event
91
+ - `dispatch-n` - Dispatch multiple events
92
+ - Custom effects (HTTP requests, localStorage, etc.)
93
+
94
+ ### Interceptors
95
+ Interceptors wrap event handlers to add cross-cutting concerns. They have `:before` and `:after` phases that run in opposite order (like middleware):
96
+
97
+ ```typescript
98
+ import { path, debug } from '@rplx/core'
99
+
100
+ store.registerEventWithInterceptors(
101
+ 'update-todo',
102
+ [path(['todos']), debug()],
103
+ handler
104
+ )
105
+ ```
106
+
107
+ ## API
108
+
109
+ ### `createStore<State, Cofx>(config)`
110
+
111
+ Factory function to create a store instance. Generic over State and Coeffects.
112
+
113
+ **Parameters:**
114
+ - `config.initialState` - Initial application state
115
+ - `config.coeffects` - Optional coeffect providers
116
+ - `config.tracing` - Optional tracing configuration
117
+
118
+ **Returns:** A store instance with the following methods:
119
+ - `registerEventDb<Payload>(eventKey, handler, interceptors?)` - Register an event handler that returns state
120
+ - `registerEvent<Payload>(eventKey, handler, interceptors?)` - Register an event handler that returns effects
121
+ - `registerEffect<Config>(effectType, handler)` - Register an effect handler
122
+ - `dispatch<Payload>(eventKey, payload)` - Dispatch an event
123
+ - `getState()` - Get current state
124
+ - `flush()` - Flush event queue (for testing)
125
+ - `registerSubscription(key, config)` - Register a subscription
126
+ - `subscribe(key, params, callback)` - Subscribe to state changes
127
+ - `query(key, params)` - Query a subscription once
128
+
129
+ ## License
130
+
131
+ MIT
132
+
@@ -0,0 +1,237 @@
1
+ interface EffectMap {
2
+ db?: any;
3
+ dispatch?: {
4
+ event: string;
5
+ payload: any;
6
+ };
7
+ 'dispatch-n'?: Array<{
8
+ event: string;
9
+ payload: any;
10
+ }>;
11
+ 'dispatch-later'?: Array<{
12
+ ms: number;
13
+ event: string;
14
+ payload: any;
15
+ }>;
16
+ fx?: Array<[string, any] | null>;
17
+ 'deregister-event-handler'?: string | string[];
18
+ }
19
+ type Context<State, Cofx = {}> = {
20
+ db: State;
21
+ event?: any;
22
+ } & Cofx;
23
+ type EventHandlerDb<State, Cofx = {}, Payload = any> = (context: Context<State, Cofx>, payload: Payload) => State;
24
+ type EventHandlerFx<State, Cofx = {}, Payload = any> = (context: Context<State, Cofx>, payload: Payload) => EffectMap;
25
+ interface ErrorContext {
26
+ eventKey: string;
27
+ payload: any;
28
+ phase: 'interceptor' | 'effect' | 'subscription';
29
+ interceptor?: {
30
+ id?: string;
31
+ direction: 'before' | 'after';
32
+ };
33
+ }
34
+ interface ErrorHandlerConfig {
35
+ /** Whether to re-throw the error after handling (default: false) */
36
+ rethrow?: boolean;
37
+ }
38
+ type ErrorHandler = (error: Error, context: ErrorContext, config: ErrorHandlerConfig) => void | Promise<void>;
39
+ type EffectHandler<Config = any> = (config: Config, store: any) => Promise<void> | void;
40
+ type CoeffectProviders<Cofx> = {
41
+ [K in keyof Cofx]: () => Cofx[K];
42
+ };
43
+ interface EffectExecutionTrace {
44
+ effectType: string;
45
+ config: any;
46
+ start: number;
47
+ end: number;
48
+ duration: number;
49
+ error?: Error;
50
+ }
51
+ interface EventTrace<State = any> {
52
+ id: number;
53
+ eventKey: string;
54
+ payload: any;
55
+ timestamp: number;
56
+ stateBefore: State;
57
+ stateAfter: State;
58
+ interceptors: Array<{
59
+ id?: string;
60
+ order: number;
61
+ }>;
62
+ effectMap: EffectMap;
63
+ effectsExecuted: EffectExecutionTrace[];
64
+ duration: number;
65
+ error?: Error;
66
+ }
67
+ type TraceCallback<State = any> = (traces: EventTrace<State>[]) => void;
68
+ interface StoreConfig<State, Cofx = {}> {
69
+ initialState: State;
70
+ coeffects?: CoeffectProviders<Cofx>;
71
+ onStateChange?: (state: State) => void;
72
+ /** Error handler configuration */
73
+ errorHandler?: {
74
+ handler?: ErrorHandler;
75
+ rethrow?: boolean;
76
+ };
77
+ /** Tracing configuration */
78
+ tracing?: {
79
+ enabled?: boolean;
80
+ debounceTime?: number;
81
+ };
82
+ }
83
+ interface QueuedEvent {
84
+ eventKey: string;
85
+ payload: any;
86
+ }
87
+
88
+ interface InterceptorContext<State, Cofx = {}> {
89
+ coeffects: Context<State, Cofx>;
90
+ effects: EffectMap;
91
+ queue: Interceptor<State, Cofx>[];
92
+ stack: Interceptor<State, Cofx>[];
93
+ }
94
+ interface Interceptor<State, Cofx = {}> {
95
+ id?: string;
96
+ before?: (context: InterceptorContext<State, Cofx>) => InterceptorContext<State, Cofx>;
97
+ after?: (context: InterceptorContext<State, Cofx>) => InterceptorContext<State, Cofx>;
98
+ }
99
+ /**
100
+ * Path interceptor - focus handler on a path in state
101
+ */
102
+ declare function path<State, Cofx = {}>(pathKeys: (keyof State)[]): Interceptor<State, Cofx>;
103
+ /**
104
+ * Debug interceptor - log events
105
+ */
106
+ declare function debug<State, Cofx = {}>(): Interceptor<State, Cofx>;
107
+ /**
108
+ * After interceptor - run side effect after handler
109
+ */
110
+ declare function after<State, Cofx = {}>(fn: (db: State, effects: EffectMap) => void): Interceptor<State, Cofx>;
111
+ /**
112
+ * Inject coeffect interceptor - adds a dynamic coeffect value
113
+ * Note: With the new coeffect provider system, this is rarely needed
114
+ * Use it only for one-off dynamic values that aren't in your Cofx type
115
+ */
116
+ declare function injectCofx<State, Cofx = {}>(key: string, value: any): Interceptor<State, Cofx>;
117
+ /**
118
+ * Validation interceptor
119
+ */
120
+ declare function validate<State, Cofx = {}>(schema: (state: State) => boolean | string): Interceptor<State, Cofx>;
121
+
122
+ /**
123
+ * Subscription system
124
+ */
125
+ type SubscriptionFn<State, Result, Params extends any[] = []> = (state: State, ...params: Params) => Result;
126
+ type SubscriptionConfig<State, Result, Params extends any[] = [], Deps extends any[] = any[]> = {
127
+ compute?: SubscriptionFn<State, Result, Params>;
128
+ deps?: string[];
129
+ combine?: (deps: Deps, ...params: Params) => Result;
130
+ };
131
+ /**
132
+ * Shared subscription object - same instance for same key+params
133
+ * This enables React memoization and matches re-frame behavior
134
+ */
135
+ declare class Subscription<State = any, Result = any, Params extends any[] = []> {
136
+ readonly key: string;
137
+ readonly params: Params;
138
+ constructor(key: string, params: Params);
139
+ }
140
+ type SubscriptionErrorHandler = (error: Error, key: string, params: any[]) => void;
141
+ declare class SubscriptionRegistry<State> {
142
+ private subscriptions;
143
+ private subscriptionCache;
144
+ private resultCache;
145
+ private refCounts;
146
+ private listeners;
147
+ register<Result, Params extends any[] = [], Deps extends any[] = any[]>(key: string, config: SubscriptionConfig<State, Result, Params, Deps>): void;
148
+ /**
149
+ * Get or create a shared Subscription object for the given key+params
150
+ * Returns the same object for the same key+params (like re-frame)
151
+ */
152
+ getSubscription<Result, Params extends any[]>(key: string, params: Params): Subscription<State, Result, Params>;
153
+ subscribe<Result, Params extends any[]>(state: State, key: string, params: Params, callback: (result: Result) => void, onError?: SubscriptionErrorHandler): () => void;
154
+ query<Result, Params extends any[]>(state: State, key: string, params: Params, onError?: SubscriptionErrorHandler): Result;
155
+ notifyListeners(newState: State): void;
156
+ private getCacheKey;
157
+ private deepEqual;
158
+ }
159
+
160
+ /**
161
+ * Store Factory Module
162
+ * Creates a store instance by composing all modules together
163
+ *
164
+ * This is the primary API for creating stores (replaces the Store class)
165
+ */
166
+
167
+ /**
168
+ * Store API interface - all public methods available on a store instance
169
+ */
170
+ interface StoreAPI<State, Cofx = {}> {
171
+ registerEventDb<Payload = any>(eventKey: string, handler: EventHandlerDb<State, Cofx, Payload>, interceptors?: Interceptor<State, Cofx>[]): void;
172
+ registerEvent<Payload = any>(eventKey: string, handler: EventHandlerFx<State, Cofx, Payload>, interceptors?: Interceptor<State, Cofx>[]): void;
173
+ deregisterEvent(eventKey: string): void;
174
+ registerEffect<Config = any>(effectType: string, handler: EffectHandler<Config>): void;
175
+ dispatch<Payload = any>(eventKey: string, payload: Payload): Promise<void>;
176
+ flush(): Promise<void>;
177
+ getState(): Readonly<State>;
178
+ getInterceptors(eventKey: string): Interceptor<State, Cofx>[] | undefined;
179
+ registerErrorHandler(handler: ErrorHandler, config?: ErrorHandlerConfig): void;
180
+ registerSubscription<Result, Params extends any[] = [], Deps extends any[] = any[]>(key: string, config: SubscriptionConfig<State, Result, Params, Deps>): void;
181
+ subscribe<Result, Params extends any[]>(key: string, params: Params, callback: (result: Result) => void): () => void;
182
+ query<Result, Params extends any[]>(key: string, params: Params): Result;
183
+ getSubscription<Result, Params extends any[]>(key: string, params: Params): any;
184
+ registerTraceCallback(key: string, callback: TraceCallback<State>): void;
185
+ removeTraceCallback(key: string): void;
186
+ }
187
+ /**
188
+ * Create a new store instance by composing all modules
189
+ *
190
+ * This is the recommended way to create a store (replaces `new Store()`)
191
+ *
192
+ * @param config - Store configuration
193
+ * @returns Store API instance
194
+ */
195
+ declare function createStore<State, Cofx = {}>(config: StoreConfig<State, Cofx>): StoreAPI<State, Cofx>;
196
+
197
+ /**
198
+ * Error Handler Module
199
+ * Handles error registration and execution
200
+ */
201
+
202
+ /**
203
+ * Default error handler
204
+ * Logs error details to console
205
+ */
206
+ declare function defaultErrorHandler(error: Error, context: ErrorContext, config: ErrorHandlerConfig): void;
207
+
208
+ /**
209
+ * Effects Module
210
+ * Handles effect execution and built-in effects
211
+ * Inspired by re-frame's fx.cljc
212
+ */
213
+
214
+ /**
215
+ * Merge multiple effect maps into a single effect map
216
+ *
217
+ * Handles special merging rules for:
218
+ * - `dispatch-n`: Arrays are concatenated
219
+ * - `dispatch-later`: Arrays are concatenated
220
+ * - `fx`: Arrays are concatenated
221
+ * - `deregister-event-handler`: Arrays or strings are merged
222
+ * - Other effects: Last one wins
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * import { mergeEffects } from '@rplx/core'
227
+ *
228
+ * const effects = mergeEffects(
229
+ * { db: newState },
230
+ * { dispatch: { event: 'save', payload: {} } },
231
+ * { 'dispatch-n': [{ event: 'log', payload: {} }] }
232
+ * )
233
+ * ```
234
+ */
235
+ declare function mergeEffects(...effects: EffectMap[]): EffectMap;
236
+
237
+ export { type CoeffectProviders, type Context, type EffectExecutionTrace, type EffectHandler, type EffectMap, type ErrorContext, type ErrorHandler, type ErrorHandlerConfig, type EventHandlerDb, type EventHandlerFx, type EventTrace, type Interceptor, type InterceptorContext, type QueuedEvent, type StoreAPI, type StoreConfig, Subscription, type SubscriptionConfig, type SubscriptionErrorHandler, type SubscriptionFn, SubscriptionRegistry, type TraceCallback, after, createStore, debug, defaultErrorHandler, injectCofx, mergeEffects, path, validate };
@@ -0,0 +1,237 @@
1
+ interface EffectMap {
2
+ db?: any;
3
+ dispatch?: {
4
+ event: string;
5
+ payload: any;
6
+ };
7
+ 'dispatch-n'?: Array<{
8
+ event: string;
9
+ payload: any;
10
+ }>;
11
+ 'dispatch-later'?: Array<{
12
+ ms: number;
13
+ event: string;
14
+ payload: any;
15
+ }>;
16
+ fx?: Array<[string, any] | null>;
17
+ 'deregister-event-handler'?: string | string[];
18
+ }
19
+ type Context<State, Cofx = {}> = {
20
+ db: State;
21
+ event?: any;
22
+ } & Cofx;
23
+ type EventHandlerDb<State, Cofx = {}, Payload = any> = (context: Context<State, Cofx>, payload: Payload) => State;
24
+ type EventHandlerFx<State, Cofx = {}, Payload = any> = (context: Context<State, Cofx>, payload: Payload) => EffectMap;
25
+ interface ErrorContext {
26
+ eventKey: string;
27
+ payload: any;
28
+ phase: 'interceptor' | 'effect' | 'subscription';
29
+ interceptor?: {
30
+ id?: string;
31
+ direction: 'before' | 'after';
32
+ };
33
+ }
34
+ interface ErrorHandlerConfig {
35
+ /** Whether to re-throw the error after handling (default: false) */
36
+ rethrow?: boolean;
37
+ }
38
+ type ErrorHandler = (error: Error, context: ErrorContext, config: ErrorHandlerConfig) => void | Promise<void>;
39
+ type EffectHandler<Config = any> = (config: Config, store: any) => Promise<void> | void;
40
+ type CoeffectProviders<Cofx> = {
41
+ [K in keyof Cofx]: () => Cofx[K];
42
+ };
43
+ interface EffectExecutionTrace {
44
+ effectType: string;
45
+ config: any;
46
+ start: number;
47
+ end: number;
48
+ duration: number;
49
+ error?: Error;
50
+ }
51
+ interface EventTrace<State = any> {
52
+ id: number;
53
+ eventKey: string;
54
+ payload: any;
55
+ timestamp: number;
56
+ stateBefore: State;
57
+ stateAfter: State;
58
+ interceptors: Array<{
59
+ id?: string;
60
+ order: number;
61
+ }>;
62
+ effectMap: EffectMap;
63
+ effectsExecuted: EffectExecutionTrace[];
64
+ duration: number;
65
+ error?: Error;
66
+ }
67
+ type TraceCallback<State = any> = (traces: EventTrace<State>[]) => void;
68
+ interface StoreConfig<State, Cofx = {}> {
69
+ initialState: State;
70
+ coeffects?: CoeffectProviders<Cofx>;
71
+ onStateChange?: (state: State) => void;
72
+ /** Error handler configuration */
73
+ errorHandler?: {
74
+ handler?: ErrorHandler;
75
+ rethrow?: boolean;
76
+ };
77
+ /** Tracing configuration */
78
+ tracing?: {
79
+ enabled?: boolean;
80
+ debounceTime?: number;
81
+ };
82
+ }
83
+ interface QueuedEvent {
84
+ eventKey: string;
85
+ payload: any;
86
+ }
87
+
88
+ interface InterceptorContext<State, Cofx = {}> {
89
+ coeffects: Context<State, Cofx>;
90
+ effects: EffectMap;
91
+ queue: Interceptor<State, Cofx>[];
92
+ stack: Interceptor<State, Cofx>[];
93
+ }
94
+ interface Interceptor<State, Cofx = {}> {
95
+ id?: string;
96
+ before?: (context: InterceptorContext<State, Cofx>) => InterceptorContext<State, Cofx>;
97
+ after?: (context: InterceptorContext<State, Cofx>) => InterceptorContext<State, Cofx>;
98
+ }
99
+ /**
100
+ * Path interceptor - focus handler on a path in state
101
+ */
102
+ declare function path<State, Cofx = {}>(pathKeys: (keyof State)[]): Interceptor<State, Cofx>;
103
+ /**
104
+ * Debug interceptor - log events
105
+ */
106
+ declare function debug<State, Cofx = {}>(): Interceptor<State, Cofx>;
107
+ /**
108
+ * After interceptor - run side effect after handler
109
+ */
110
+ declare function after<State, Cofx = {}>(fn: (db: State, effects: EffectMap) => void): Interceptor<State, Cofx>;
111
+ /**
112
+ * Inject coeffect interceptor - adds a dynamic coeffect value
113
+ * Note: With the new coeffect provider system, this is rarely needed
114
+ * Use it only for one-off dynamic values that aren't in your Cofx type
115
+ */
116
+ declare function injectCofx<State, Cofx = {}>(key: string, value: any): Interceptor<State, Cofx>;
117
+ /**
118
+ * Validation interceptor
119
+ */
120
+ declare function validate<State, Cofx = {}>(schema: (state: State) => boolean | string): Interceptor<State, Cofx>;
121
+
122
+ /**
123
+ * Subscription system
124
+ */
125
+ type SubscriptionFn<State, Result, Params extends any[] = []> = (state: State, ...params: Params) => Result;
126
+ type SubscriptionConfig<State, Result, Params extends any[] = [], Deps extends any[] = any[]> = {
127
+ compute?: SubscriptionFn<State, Result, Params>;
128
+ deps?: string[];
129
+ combine?: (deps: Deps, ...params: Params) => Result;
130
+ };
131
+ /**
132
+ * Shared subscription object - same instance for same key+params
133
+ * This enables React memoization and matches re-frame behavior
134
+ */
135
+ declare class Subscription<State = any, Result = any, Params extends any[] = []> {
136
+ readonly key: string;
137
+ readonly params: Params;
138
+ constructor(key: string, params: Params);
139
+ }
140
+ type SubscriptionErrorHandler = (error: Error, key: string, params: any[]) => void;
141
+ declare class SubscriptionRegistry<State> {
142
+ private subscriptions;
143
+ private subscriptionCache;
144
+ private resultCache;
145
+ private refCounts;
146
+ private listeners;
147
+ register<Result, Params extends any[] = [], Deps extends any[] = any[]>(key: string, config: SubscriptionConfig<State, Result, Params, Deps>): void;
148
+ /**
149
+ * Get or create a shared Subscription object for the given key+params
150
+ * Returns the same object for the same key+params (like re-frame)
151
+ */
152
+ getSubscription<Result, Params extends any[]>(key: string, params: Params): Subscription<State, Result, Params>;
153
+ subscribe<Result, Params extends any[]>(state: State, key: string, params: Params, callback: (result: Result) => void, onError?: SubscriptionErrorHandler): () => void;
154
+ query<Result, Params extends any[]>(state: State, key: string, params: Params, onError?: SubscriptionErrorHandler): Result;
155
+ notifyListeners(newState: State): void;
156
+ private getCacheKey;
157
+ private deepEqual;
158
+ }
159
+
160
+ /**
161
+ * Store Factory Module
162
+ * Creates a store instance by composing all modules together
163
+ *
164
+ * This is the primary API for creating stores (replaces the Store class)
165
+ */
166
+
167
+ /**
168
+ * Store API interface - all public methods available on a store instance
169
+ */
170
+ interface StoreAPI<State, Cofx = {}> {
171
+ registerEventDb<Payload = any>(eventKey: string, handler: EventHandlerDb<State, Cofx, Payload>, interceptors?: Interceptor<State, Cofx>[]): void;
172
+ registerEvent<Payload = any>(eventKey: string, handler: EventHandlerFx<State, Cofx, Payload>, interceptors?: Interceptor<State, Cofx>[]): void;
173
+ deregisterEvent(eventKey: string): void;
174
+ registerEffect<Config = any>(effectType: string, handler: EffectHandler<Config>): void;
175
+ dispatch<Payload = any>(eventKey: string, payload: Payload): Promise<void>;
176
+ flush(): Promise<void>;
177
+ getState(): Readonly<State>;
178
+ getInterceptors(eventKey: string): Interceptor<State, Cofx>[] | undefined;
179
+ registerErrorHandler(handler: ErrorHandler, config?: ErrorHandlerConfig): void;
180
+ registerSubscription<Result, Params extends any[] = [], Deps extends any[] = any[]>(key: string, config: SubscriptionConfig<State, Result, Params, Deps>): void;
181
+ subscribe<Result, Params extends any[]>(key: string, params: Params, callback: (result: Result) => void): () => void;
182
+ query<Result, Params extends any[]>(key: string, params: Params): Result;
183
+ getSubscription<Result, Params extends any[]>(key: string, params: Params): any;
184
+ registerTraceCallback(key: string, callback: TraceCallback<State>): void;
185
+ removeTraceCallback(key: string): void;
186
+ }
187
+ /**
188
+ * Create a new store instance by composing all modules
189
+ *
190
+ * This is the recommended way to create a store (replaces `new Store()`)
191
+ *
192
+ * @param config - Store configuration
193
+ * @returns Store API instance
194
+ */
195
+ declare function createStore<State, Cofx = {}>(config: StoreConfig<State, Cofx>): StoreAPI<State, Cofx>;
196
+
197
+ /**
198
+ * Error Handler Module
199
+ * Handles error registration and execution
200
+ */
201
+
202
+ /**
203
+ * Default error handler
204
+ * Logs error details to console
205
+ */
206
+ declare function defaultErrorHandler(error: Error, context: ErrorContext, config: ErrorHandlerConfig): void;
207
+
208
+ /**
209
+ * Effects Module
210
+ * Handles effect execution and built-in effects
211
+ * Inspired by re-frame's fx.cljc
212
+ */
213
+
214
+ /**
215
+ * Merge multiple effect maps into a single effect map
216
+ *
217
+ * Handles special merging rules for:
218
+ * - `dispatch-n`: Arrays are concatenated
219
+ * - `dispatch-later`: Arrays are concatenated
220
+ * - `fx`: Arrays are concatenated
221
+ * - `deregister-event-handler`: Arrays or strings are merged
222
+ * - Other effects: Last one wins
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * import { mergeEffects } from '@rplx/core'
227
+ *
228
+ * const effects = mergeEffects(
229
+ * { db: newState },
230
+ * { dispatch: { event: 'save', payload: {} } },
231
+ * { 'dispatch-n': [{ event: 'log', payload: {} }] }
232
+ * )
233
+ * ```
234
+ */
235
+ declare function mergeEffects(...effects: EffectMap[]): EffectMap;
236
+
237
+ export { type CoeffectProviders, type Context, type EffectExecutionTrace, type EffectHandler, type EffectMap, type ErrorContext, type ErrorHandler, type ErrorHandlerConfig, type EventHandlerDb, type EventHandlerFx, type EventTrace, type Interceptor, type InterceptorContext, type QueuedEvent, type StoreAPI, type StoreConfig, Subscription, type SubscriptionConfig, type SubscriptionErrorHandler, type SubscriptionFn, SubscriptionRegistry, type TraceCallback, after, createStore, debug, defaultErrorHandler, injectCofx, mergeEffects, path, validate };