@context-action/core 0.9.2 → 1.0.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 CHANGED
@@ -161,19 +161,16 @@ console.log('Result:', result.successResults);
161
161
  ### Memory Management
162
162
 
163
163
  ```typescript
164
- // Configure handler limits for memory safety (v0.4.1+)
164
+ // A finite limit is opt-in and fails registration explicitly on overflow.
165
165
  const actions = new ActionRegister<MyActions>({
166
166
  registry: {
167
- maxHandlersPerAction: 1000 // Default: 1000, prevents memory issues
168
- // maxHandlersPerAction: 5000 // Higher limit for complex applications
169
- // maxHandlersPerAction: Infinity // Disable limit (use with caution)
167
+ maxHandlersPerAction: 1000
170
168
  }
171
169
  });
172
170
 
173
- // Use cases for different limits:
174
- // - 1000 (default): Most applications
175
- // - 5000-10000: Large enterprise applications
176
- // - Infinity: Only for controlled environments with trusted code
171
+ // Omit maxHandlersPerAction for the default unbounded registry.
172
+ // Use a finite limit only where registration ownership is bounded and overflow
173
+ // should be treated as a programming error.
177
174
  ```
178
175
 
179
176
  ## 🚀 New Features (v0.4.0+)
@@ -232,31 +229,39 @@ actions.register('myAction', handler, {
232
229
  ### Immediate Execution & Queue Control
233
230
 
234
231
  ```typescript
235
- // Bypass queue for immediate execution
236
- await actions.dispatch('urgentAction', data, {
232
+ // Queueing is opt-in for shared mutable state or explicit ordering.
233
+ const queuedActions = new ActionRegister<MyActions>({
234
+ registry: { useConcurrencyQueue: true }
235
+ });
236
+
237
+ // Bypass an enabled queue for immediate execution.
238
+ await queuedActions.dispatch('urgentAction', data, {
237
239
  immediate: true
238
240
  });
239
241
 
240
- // Queue with custom priority
241
- await actions.dispatch('backgroundTask', data, {
242
+ // Prioritize work within an enabled queue.
243
+ await queuedActions.dispatch('backgroundTask', data, {
242
244
  queuePriority: 5
243
245
  });
244
246
 
245
- // Wall-clock timeout (queue wait + retry delay included).
247
+ // Wall-clock timeout (including enabled-queue wait and retry delay).
246
248
  // Rejects with ActionTimeoutError while the internal operation drains safely.
247
249
  await actions.dispatch('timedAction', data, { timeout: 5000 });
248
250
  ```
249
251
 
250
- The default queue is single-slot. When a handler awaits another dispatch on
251
- the **same** register, make that nested call explicit with `{ immediate: true }`
252
- so it can run inside the current queue turn. Likewise, do not set
253
- `queuePriority` on an awaited nested `dispatchWithResult` call. Independent
254
- top-level dispatches should keep the queue defaults.
252
+ Dispatches run independently by default. When queueing is enabled, it is
253
+ single-slot. A handler that awaits another dispatch on the **same** register
254
+ must make that nested call explicit with `{ immediate: true }`; likewise, do
255
+ not set `queuePriority` on an awaited nested `dispatchWithResult` call.
255
256
 
256
257
  Handlers that perform cancellable I/O can observe `controller.signal`. It is
257
258
  aborted for caller cancellation, timeout, provider teardown, and register
258
259
  shutdown.
259
260
 
261
+ A timeout is a caller boundary, not a rollback. A handler that ignores its
262
+ signal can continue while the register drains it for lifecycle cleanup; do not
263
+ retry a mutation after timeout unless its operation is idempotent.
264
+
260
265
  ### Result Collection with Strategies
261
266
 
262
267
  ```typescript
@@ -277,28 +282,31 @@ console.log('Success:', result.success);
277
282
  ### React Integration Helpers
278
283
 
279
284
  ```typescript
280
- import {
281
- useActionHandler,
282
- ReactDevUtils
283
- } from '@context-action/core';
285
+ import {
286
+ createActionContext,
287
+ ReactDevUtils,
288
+ } from '@context-action/react';
289
+
290
+ // `useActionHandler` and `useActionRegister` are returned by the action
291
+ // context factory from `@context-action/react`; the core package is framework-agnostic.
292
+ const { useActionHandler, useActionRegister } = createActionContext<MyActions>('MyActions');
284
293
 
285
294
  // React hook pattern
286
295
  function MyComponent() {
287
296
  const registry = useActionRegister();
288
297
 
289
298
  // Auto-cleanup on unmount, HMR support
290
- const handlerConfig = useActionHandler(
291
- registry,
292
- 'userAction',
299
+ useActionHandler(
300
+ 'userAction',
293
301
  async (payload) => {
294
302
  // Handler logic
295
303
  },
296
- { priority: 10 },
297
- [] // dependencies
304
+ { priority: 10 }
298
305
  );
299
306
 
300
307
  // Direct registry dispatch with error handling
301
308
  const handleDispatch = useCallback(async (action, payload) => {
309
+ if (!registry) return;
302
310
  try {
303
311
  await registry.dispatch(action, payload);
304
312
  } catch (error) {
@@ -321,11 +329,11 @@ const stats = ReactDevUtils.getStats(registry);
321
329
  - **Multiple execution modes** - sequential, parallel, race
322
330
 
323
331
  ### ⚡ Performance & Memory Optimizations
324
- - **Cached environment checks** for better performance
332
+ - **Explicit debug mode** logs are emitted only with `registry.debug: true`
325
333
  - **Optimized handler ID generation** without random numbers
326
334
  - **Smart array filtering** - only copies when needed
327
335
  - **Automatic memory cleanup** with idle handler cleanup
328
- - **Optional concurrency queues** for thread safety
336
+ - **Opt-in concurrency queue** for explicit ordering
329
337
 
330
338
  ### 🔧 Advanced Configuration
331
339
 
@@ -450,12 +458,7 @@ console.log(`Total handlers: ${info.totalHandlers}`);
450
458
  const stats = actions.getActionStats('updateUser');
451
459
  if (stats) {
452
460
  console.log(`Handler count: ${stats.handlerCount}`);
453
- console.log(`Success rate: ${stats.executionStats?.successRate}%`);
454
- console.log(`Average duration: ${stats.executionStats?.averageDuration}ms`);
455
461
  }
456
-
457
- // Clear statistics
458
- actions.clearExecutionStats();
459
462
  ```
460
463
 
461
464
  ### Cleanup & Resource Management
@@ -522,9 +525,9 @@ interface DispatchOptions {
522
525
  throttle?: number;
523
526
  executionMode?: 'sequential' | 'parallel' | 'race';
524
527
  signal?: AbortSignal;
525
- immediate?: boolean; // Bypass queue
526
- queuePriority?: number; // Queue priority
527
- timeout?: number; // Execution timeout
528
+ immediate?: boolean; // Bypass an enabled queue
529
+ queuePriority?: number; // Priority within an enabled queue
530
+ timeout?: number; // Non-negative finite wall-clock timeout
528
531
 
529
532
  retryOnError?: {
530
533
  maxAttempts: number;
@@ -605,7 +608,7 @@ actions.destroy();
605
608
 
606
609
  1. **Use handler IDs** for better debugging and filtering
607
610
  2. **Enable replaceExisting** for React components to prevent duplicates
608
- 3. **Use immediate: false** (default) to benefit from queue optimizations
611
+ 3. **Enable `useConcurrencyQueue` only** when independent dispatches must be ordered
609
612
  4. **Await destroyAsync()** when shutdown completion must be guaranteed
610
613
  5. **Use priority filtering** instead of excludeHandlerIds for better performance
611
614
  6. **Cache ActionRegister instances** - don't create new ones frequently