@feardread/feature-factory 4.0.1 → 4.0.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.
package/README.md ADDED
@@ -0,0 +1,677 @@
1
+ # Redux Factory Suite
2
+
3
+ A comprehensive collection of factory utilities for Redux applications, providing standardized patterns for creating slices, async thunks, state management, and caching. This suite eliminates boilerplate code and enforces consistent patterns across your Redux application.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Installation](#installation)
9
+ - [Core Components](#core-components)
10
+ - [Quick Start](#quick-start)
11
+ - [API Reference](#api-reference)
12
+ - [Examples](#examples)
13
+ - [Best Practices](#best-practices)
14
+ - [Contributing](#contributing)
15
+
16
+ ## Overview
17
+
18
+ The Redux Factory Suite consists of five main components:
19
+
20
+ 1. **FeatureFactory** - Creates complete Redux features with slices, async actions, and reducers
21
+ 2. **ThunkFactory** - Generates standardized async thunks for API operations
22
+ 3. **StateFactory** - Creates comprehensive initial state structures
23
+ 4. **CacheFactory** - Provides browser storage utilities with fallback support
24
+ 5. **ApiFactory** - Creates RTK Query APIs with standardized CRUD operations and caching
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install @feardread/feature-factory
30
+ # The factory utilities are included in your project source
31
+ ```
32
+
33
+ dependencies:
34
+ ```bash
35
+ npm install @reduxjs/toolkit react-redux
36
+ ```
37
+
38
+ ## Core Components
39
+
40
+ ### FeatureFactory
41
+
42
+ Creates complete Redux features with standardized structure including slices, async actions, and reducers.
43
+
44
+ ```javascript
45
+ import { FeatureFactory } from './FeatureFactory';
46
+
47
+ const userFeature = FeatureFactory('users', {
48
+ setCurrentUser: (state, action) => {
49
+ state.currentUser = action.payload;
50
+ }
51
+ });
52
+
53
+ const { slice, asyncActions } = userFeature.create();
54
+ ```
55
+
56
+ ### ThunkFactory
57
+
58
+ Factory for creating Redux async thunks with standardized patterns for HTTP operations.
59
+
60
+ ```javascript
61
+ import ThunkFactory from './ThunkFactory';
62
+
63
+ // Create individual thunks
64
+ const fetchUsers = ThunkFactory.create('users', 'all');
65
+ const createUser = ThunkFactory.post('users', 'create');
66
+
67
+ // Create full CRUD suite
68
+ const userThunks = ThunkFactory.createCrud('users');
69
+ ```
70
+
71
+ ### StateFactory
72
+
73
+ Creates comprehensive initial state structures with optional features like pagination, filtering, and validation.
74
+
75
+ ```javascript
76
+ import StateFactory from './StateFactory';
77
+
78
+ // Full-featured state
79
+ const initialState = StateFactory('users');
80
+
81
+ // Minimal state
82
+ const minimalState = StateFactory.minimal('users');
83
+
84
+ // List-optimized state
85
+ const listState = StateFactory.forList('users');
86
+ ```
87
+
88
+ ### CacheFactory
89
+
90
+ Browser storage utilities with automatic fallback to memory storage.
91
+
92
+ ```javascript
93
+ import CacheFactory from './CacheFactory';
94
+
95
+ const cache = CacheFactory({ type: 'local', prefix: 'myapp_' });
96
+
97
+ await cache.set('user', userData);
98
+ const user = await cache.get('user');
99
+ ```
100
+
101
+ ### ApiFactory
102
+
103
+ Creates RTK Query APIs with standardized CRUD operations, caching, and tag invalidation.
104
+
105
+ ```javascript
106
+ import ApiFactory from './ApiFactory';
107
+
108
+ // Create complete API with standard CRUD endpoints
109
+ const usersApi = ApiFactory.createComplete('users');
110
+
111
+ // Create custom API
112
+ const customApi = ApiFactory('products')
113
+ .inject('getFeatured', {
114
+ method: 'GET',
115
+ url: 'products/featured',
116
+ providesTags: ['Product']
117
+ })
118
+ .create();
119
+ ```
120
+
121
+ ## Quick Start
122
+
123
+ Here's how to create a complete feature using all components:
124
+
125
+ ```javascript
126
+ import { FeatureFactory } from './FeatureFactory';
127
+ import StateFactory from './StateFactory';
128
+ import ThunkFactory from './ThunkFactory';
129
+ import ApiFactory from './ApiFactory';
130
+
131
+ // 1. Create RTK Query API (recommended for new projects)
132
+ const productsApi = ApiFactory.createComplete('products', {
133
+ tagTypes: ['Product', 'Category']
134
+ });
135
+
136
+ // OR use traditional async thunks
137
+ // 1. Create initial state
138
+ const initialState = StateFactory.forList('products');
139
+
140
+ // 2. Create custom async actions
141
+ const customActions = {
142
+ searchProducts: ThunkFactory.create('products', 'search'),
143
+ getPopularProducts: ThunkFactory.custom('products', 'popular', {
144
+ method: 'GET',
145
+ customUrl: 'products/popular'
146
+ })
147
+ };
148
+
149
+ // 3. Create the feature
150
+ const productFeature = FeatureFactory('products', {
151
+ toggleFavorite: (state, action) => {
152
+ const product = state.products.find(p => p.id === action.payload);
153
+ if (product) {
154
+ product.isFavorite = !product.isFavorite;
155
+ }
156
+ }
157
+ });
158
+
159
+ // 4. Generate slice and actions
160
+ const { slice, asyncActions } = productFeature.create({
161
+ service: customActions,
162
+ initialState
163
+ });
164
+
165
+ export const productReducer = slice.reducer;
166
+ export const productActions = { ...slice.actions, ...asyncActions };
167
+
168
+ // For RTK Query approach, use generated hooks:
169
+ // const { data, error, isLoading } = productsApi.useGetAllQuery();
170
+ ```
171
+
172
+ ## API Reference
173
+
174
+ ### FeatureFactory API
175
+
176
+ #### `FeatureFactory(entity, reducers, endpoints)`
177
+
178
+ Creates a feature factory instance.
179
+
180
+ **Parameters:**
181
+ - `entity` (string): Entity name for the feature
182
+ - `reducers` (Object): Custom reducers to include in the slice
183
+ - `endpoints` (Object|null): API endpoints (currently unused)
184
+
185
+ **Returns:** Feature factory instance
186
+
187
+ #### Methods
188
+
189
+ - `create(options)`: Creates slice and async actions
190
+ - `manager(initialReducers)`: Creates dynamic reducer manager
191
+ - `inject(source, destination)`: Merges objects
192
+ - `getEntityName()`: Returns entity name
193
+ - `getAdapter()`: Returns RTK entity adapter
194
+
195
+ ### ThunkFactory API
196
+
197
+ #### Static Methods
198
+
199
+ - `create(entity, prefix)`: Creates GET request thunk
200
+ - `post(entity, prefix)`: Creates POST request thunk
201
+ - `put(entity, prefix)`: Creates PUT request thunk
202
+ - `patch(entity, prefix)`: Creates PATCH request thunk
203
+ - `delete(entity, prefix)`: Creates DELETE request thunk
204
+ - `custom(entity, prefix, options)`: Creates custom configured thunk
205
+ - `createCrud(entity)`: Creates complete CRUD thunk suite
206
+
207
+ #### Standard Operations
208
+
209
+ - `all`: GET /entity/all
210
+ - `one`: GET /entity/:id
211
+ - `search`: GET /entity/search?params
212
+ - `create`: POST /entity/create
213
+ - `update`: PUT /entity/:id
214
+ - `patch`: PATCH /entity/:id
215
+ - `delete`: DELETE /entity/:id
216
+
217
+ ### StateFactory API
218
+
219
+ #### `StateFactory(namespace, options)`
220
+
221
+ Creates comprehensive initial state.
222
+
223
+ **Parameters:**
224
+ - `namespace` (string): Entity namespace
225
+ - `options` (Object): Configuration options
226
+
227
+ **Options:**
228
+ - `includeEntityState` (boolean): Include entity-specific state
229
+ - `includePagination` (boolean): Include pagination state
230
+ - `includeFiltering` (boolean): Include filtering state
231
+ - `includeSorting` (boolean): Include sorting state
232
+ - `includeSelection` (boolean): Include selection state
233
+ - `includeValidation` (boolean): Include validation state
234
+ - `includeMetadata` (boolean): Include metadata state
235
+ - `customFields` (Object): Custom fields to add
236
+
237
+ #### Preset Methods
238
+
239
+ - `StateFactory.minimal(namespace)`: Minimal state structure
240
+ - `StateFactory.forList(namespace)`: List/table optimized state
241
+ - `StateFactory.forForm(namespace)`: Form optimized state
242
+ - `StateFactory.forRealTime(namespace)`: Real-time data state
243
+ - `StateFactory.normalized(namespace)`: Normalized entities state
244
+ - `StateFactory.withOperations(namespace, operations)`: Custom operations tracking
245
+
246
+ ### CacheFactory API
247
+
248
+ #### `CacheFactory(options)`
249
+
250
+ Creates cache instance.
251
+
252
+ **Options:**
253
+ - `type` (string): 'local' or 'session'
254
+ - `prefix` (string): Key prefix for namespacing
255
+ - `enableLogging` (boolean): Enable debug logging
256
+ - `fallbackToMemory` (boolean): Use memory storage fallback
257
+ - `maxRetries` (number): Maximum retry attempts
258
+
259
+ #### Methods
260
+
261
+ - `set(key, value)`: Store value
262
+ - `get(key, defaultValue)`: Retrieve value
263
+ - `remove(key)`: Remove value
264
+ - `clear()`: Clear all values
265
+ - `keys()`: Get all keys
266
+ - `has(key)`: Check if key exists
267
+ - `getStats()`: Get storage statistics
268
+ - `bulk.set(items)`: Set multiple items
269
+ - `bulk.get(keys)`: Get multiple items
270
+ - `bulk.remove(keys)`: Remove multiple items
271
+
272
+ ### ApiFactory API
273
+
274
+ #### `ApiFactory(sliceName, options)`
275
+
276
+ Creates an RTK Query API factory instance.
277
+
278
+ **Parameters:**
279
+ - `sliceName` (string): Entity/slice name
280
+ - `options` (Object): Configuration options
281
+
282
+ **Options:**
283
+ - `tagTypes` (string[]): Cache tag types for invalidation
284
+ - `baseQuery` (Object): Custom base query configuration
285
+ - `includeStandardEndpoints` (boolean): Include CRUD endpoints
286
+
287
+ #### Static Methods
288
+
289
+ - `createComplete(sliceName, options)`: Creates API with all standard endpoints
290
+ - `createCustom(sliceName, endpoints, options)`: Creates API with only custom endpoints
291
+ - `getStandardEndpoints()`: Returns standard endpoint configurations
292
+ - `getHttpMethods()`: Returns HTTP methods constants
293
+
294
+ #### Instance Methods
295
+
296
+ - `inject(name, config)`: Inject single endpoint
297
+ - `injectMany(endpoints)`: Inject multiple endpoints
298
+ - `create()`: Create the final API
299
+ - `createStandard()`: Add standard CRUD endpoints
300
+ - `createBulk()`: Add bulk operation endpoints
301
+ - `createCustom(name, config)`: Add custom endpoint
302
+ - `withAuth(token)`: Create API with authentication
303
+ - `withBaseUrl(url)`: Create API with custom base URL
304
+
305
+ #### Standard Endpoints Generated
306
+
307
+ - `getAll`: GET /entity/all
308
+ - `getById`: GET /entity/:id
309
+ - `create`: POST /entity
310
+ - `update`: PUT /entity/:id
311
+ - `patch`: PATCH /entity/:id
312
+ - `delete`: DELETE /entity/:id
313
+ - `search`: GET /entity/search
314
+
315
+ ## Examples
316
+
317
+ ### Complete User Management Feature
318
+
319
+ ```javascript
320
+ // userFeature.js
321
+ import { FeatureFactory } from './FeatureFactory';
322
+ import StateFactory from './StateFactory';
323
+ import ThunkFactory from './ThunkFactory';
324
+
325
+ // Create state optimized for user lists
326
+ const initialState = StateFactory.forList('users');
327
+
328
+ // Create custom service actions
329
+ const userService = {
330
+ fetchProfile: ThunkFactory.custom('users', 'profile', {
331
+ method: 'GET',
332
+ customUrl: 'auth/profile'
333
+ }),
334
+ updateProfile: ThunkFactory.put('users', 'profile'),
335
+ changePassword: ThunkFactory.post('users', 'change-password')
336
+ };
337
+
338
+ // Create feature factory
339
+ const userFeature = FeatureFactory('users', {
340
+ setActiveUser: (state, action) => {
341
+ state.activeUser = action.payload;
342
+ },
343
+ clearUsers: (state) => {
344
+ state.data = [];
345
+ state.usersList = [];
346
+ }
347
+ });
348
+
349
+ // Generate the complete feature
350
+ const { slice, asyncActions } = userFeature.create({
351
+ service: userService,
352
+ initialState
353
+ });
354
+
355
+ export const userReducer = slice.reducer;
356
+ export const userActions = { ...slice.actions, ...asyncActions };
357
+ ```
358
+
359
+ ### Using the Cache System
360
+
361
+ ```javascript
362
+ // cacheService.js
363
+ import CacheFactory from './CacheFactory';
364
+
365
+ // Create application cache
366
+ const appCache = CacheFactory({
367
+ type: 'local',
368
+ prefix: 'myapp_',
369
+ enableLogging: process.env.NODE_ENV === 'development'
370
+ });
371
+
372
+ // Cache service
373
+ export const cacheService = {
374
+ async cacheUserData(userId, userData) {
375
+ await appCache.set(`user_${userId}`, userData);
376
+ },
377
+
378
+ async getUserData(userId) {
379
+ return await appCache.get(`user_${userId}`);
380
+ },
381
+
382
+ async clearUserCache(userId) {
383
+ await appCache.remove(`user_${userId}`);
384
+ },
385
+
386
+ async cacheUserPreferences(preferences) {
387
+ await appCache.set('user_preferences', preferences);
388
+ }
389
+ };
390
+ ```
391
+
392
+ ### RTK Query API with ApiFactory
393
+
394
+ ```javascript
395
+ // productsApi.js
396
+ import ApiFactory from './ApiFactory';
397
+
398
+ // Create complete API with all CRUD operations
399
+ export const productsApi = ApiFactory.createComplete('products', {
400
+ tagTypes: ['Product', 'Category', 'Review'],
401
+ baseQuery: {
402
+ baseUrl: process.env.REACT_APP_API_URL,
403
+ prepareHeaders: (headers, { getState }) => {
404
+ const token = getState().auth.token;
405
+ if (token) {
406
+ headers.set('authorization', `Bearer ${token}`);
407
+ }
408
+ return headers;
409
+ }
410
+ }
411
+ });
412
+
413
+ // Add custom endpoints
414
+ const enhancedApi = productsApi.injectEndpoints({
415
+ endpoints: (builder) => ({
416
+ getFeaturedProducts: builder.query({
417
+ query: () => 'products/featured',
418
+ providesTags: ['Product'],
419
+ }),
420
+ getProductsByCategory: builder.query({
421
+ query: (categoryId) => `products/category/${categoryId}`,
422
+ providesTags: (result, error, categoryId) => [
423
+ { type: 'Product', id: `CATEGORY_${categoryId}` },
424
+ ],
425
+ }),
426
+ toggleProductFavorite: builder.mutation({
427
+ query: ({ id, isFavorite }) => ({
428
+ url: `products/${id}/favorite`,
429
+ method: 'PATCH',
430
+ body: { isFavorite },
431
+ }),
432
+ invalidatesTags: (result, error, { id }) => [
433
+ { type: 'Product', id },
434
+ ],
435
+ }),
436
+ }),
437
+ });
438
+
439
+ // Export hooks for use in components
440
+ export const {
441
+ useGetAllQuery,
442
+ useGetByIdQuery,
443
+ useCreateMutation,
444
+ useUpdateMutation,
445
+ useDeleteMutation,
446
+ useGetFeaturedProductsQuery,
447
+ useGetProductsByCategoryQuery,
448
+ useToggleProductFavoriteMutation,
449
+ } = enhancedApi;
450
+ ```
451
+
452
+ ### Using ApiFactory in Components
453
+
454
+ ```javascript
455
+ // ProductList.jsx
456
+ import React from 'react';
457
+ import {
458
+ useGetAllQuery,
459
+ useDeleteMutation,
460
+ useGetFeaturedProductsQuery
461
+ } from '../api/productsApi';
462
+
463
+ const ProductList = () => {
464
+ const {
465
+ data: products = [],
466
+ error,
467
+ isLoading
468
+ } = useGetAllQuery();
469
+
470
+ const {
471
+ data: featuredProducts = []
472
+ } = useGetFeaturedProductsQuery();
473
+
474
+ const [deleteProduct, { isLoading: isDeleting }] = useDeleteMutation();
475
+
476
+ const handleDelete = async (productId) => {
477
+ try {
478
+ await deleteProduct({ id: productId }).unwrap();
479
+ // Success - cache automatically invalidated
480
+ } catch (error) {
481
+ console.error('Failed to delete product:', error);
482
+ }
483
+ };
484
+
485
+ if (isLoading) return <div>Loading products...</div>;
486
+ if (error) return <div>Error: {error.message}</div>;
487
+
488
+ return (
489
+ <div>
490
+ <section>
491
+ <h2>Featured Products</h2>
492
+ {featuredProducts.map(product => (
493
+ <ProductCard key={product.id} product={product} />
494
+ ))}
495
+ </section>
496
+
497
+ <section>
498
+ <h2>All Products</h2>
499
+ {products.map(product => (
500
+ <ProductCard
501
+ key={product.id}
502
+ product={product}
503
+ onDelete={() => handleDelete(product.id)}
504
+ isDeleting={isDeleting}
505
+ />
506
+ ))}
507
+ </section>
508
+ </div>
509
+ );
510
+ };
511
+ ```
512
+
513
+ ### Advanced State Management
514
+
515
+ ```javascript
516
+ // advancedState.js
517
+ import StateFactory from './StateFactory';
518
+
519
+ // Create state for a complex dashboard
520
+ const dashboardState = StateFactory('dashboard', {
521
+ includeMetadata: true,
522
+ includePagination: true,
523
+ customFields: {
524
+ widgets: [],
525
+ layout: 'grid',
526
+ theme: 'light',
527
+ notifications: [],
528
+ isFullscreen: false,
529
+ lastRefresh: null
530
+ }
531
+ });
532
+
533
+ // Create form state with validation
534
+ const userFormState = StateFactory.forForm('userForm');
535
+
536
+ // Create real-time chat state
537
+ const chatState = StateFactory.forRealTime('chat');
538
+ ```
539
+
540
+ ## Best Practices
541
+
542
+ ### 1. Entity Naming
543
+ Use consistent, descriptive entity names:
544
+ ```javascript
545
+ // Good
546
+ const userFeature = FeatureFactory('users');
547
+ const productFeature = FeatureFactory('products');
548
+
549
+ // Avoid
550
+ const feature1 = FeatureFactory('u');
551
+ const myFeature = FeatureFactory('thing');
552
+ ```
553
+
554
+ ### 2. State Organization
555
+ Choose appropriate state factories for your use case:
556
+ ```javascript
557
+ // For data tables/lists
558
+ const listState = StateFactory.forList('products');
559
+
560
+ // For forms
561
+ const formState = StateFactory.forForm('userForm');
562
+
563
+ // For simple data
564
+ const minimalState = StateFactory.minimal('settings');
565
+ ```
566
+
567
+ ### 3. Async Actions
568
+ Use descriptive prefixes for custom actions:
569
+ ```javascript
570
+ const customActions = {
571
+ fetchUserProfile: ThunkFactory.create('users', 'profile'),
572
+ searchActiveUsers: ThunkFactory.create('users', 'active-search'),
573
+ bulkUpdateUsers: ThunkFactory.post('users', 'bulk-update')
574
+ };
575
+ ```
576
+
577
+ ### 4. Error Handling
578
+ Always handle async action states in components:
579
+ ```javascript
580
+ const { data, loading, error } = useSelector(state => state.users);
581
+
582
+ if (loading) return <LoadingSpinner />;
583
+ if (error) return <ErrorMessage error={error} />;
584
+ ```
585
+
586
+ ### 5. Caching Strategy
587
+ Use appropriate cache types and prefixes:
588
+ ```javascript
589
+ // Session data (cleared on browser close)
590
+ const sessionCache = CacheFactory({ type: 'session' });
591
+
592
+ // Persistent data
593
+ const persistentCache = CacheFactory({
594
+ type: 'local',
595
+ prefix: 'app_v1_'
596
+ });
597
+ ```
598
+
599
+ ### 6. RTK Query vs Async Thunks
600
+ Choose the appropriate data fetching approach:
601
+
602
+ ```javascript
603
+ // RTK Query - Recommended for new projects (automatic caching, background refetching)
604
+ const usersApi = ApiFactory.createComplete('users');
605
+
606
+ // Async Thunks - Better for complex business logic or existing codebases
607
+ const userFeature = FeatureFactory('users').create({
608
+ service: ThunkFactory.createCrud('users')
609
+ });
610
+ ```
611
+
612
+ ### 7. API Configuration
613
+ Configure APIs with proper error handling and authentication:
614
+ ```javascript
615
+ const api = ApiFactory('products', {
616
+ baseQuery: {
617
+ baseUrl: process.env.REACT_APP_API_URL,
618
+ prepareHeaders: (headers, { getState }) => {
619
+ const token = getState().auth.token;
620
+ if (token) {
621
+ headers.set('authorization', `Bearer ${token}`);
622
+ }
623
+ return headers;
624
+ },
625
+ },
626
+ tagTypes: ['Product', 'Category']
627
+ });
628
+ ```
629
+
630
+ ## TypeScript Support
631
+
632
+ The factories can be used with TypeScript by defining interfaces:
633
+
634
+ ```typescript
635
+ interface User {
636
+ id: number;
637
+ name: string;
638
+ email: string;
639
+ }
640
+
641
+ interface UserState {
642
+ users: User[];
643
+ currentUser: User | null;
644
+ loading: boolean;
645
+ error: string | null;
646
+ }
647
+
648
+ // Use with proper typing
649
+ const userFeature = FeatureFactory<UserState>('users', reducers);
650
+ ```
651
+
652
+ ## Contributing
653
+
654
+ 1. Fork the repository
655
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
656
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
657
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
658
+ 5. Open a Pull Request
659
+
660
+ ## License
661
+
662
+ This project is licensed under the MIT License - see the LICENSE file for details.
663
+
664
+ ## Dependencies
665
+
666
+ - `@reduxjs/toolkit` - Required for Redux functionality
667
+ - Modern browser with ES6+ support
668
+ - Node.js 14+ for development
669
+
670
+ ## Browser Support
671
+
672
+ - Chrome 60+
673
+ - Firefox 55+
674
+ - Safari 12+
675
+ - Edge 79+
676
+
677
+ For older browsers, the CacheFactory will automatically fallback to memory storage if localStorage/sessionStorage is unavailable.