@feardread/feature-factory 3.0.2 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.min.js +2 -2
- package/dist/index.esm.js +3 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/factory/api.js +521 -56
- package/src/factory/cache.js +479 -27
- package/src/factory/index.js +171 -100
- package/src/factory/service.js +412 -18
- package/src/factory/state.js +430 -5
- package/src/factory/thunk.js +264 -38
- package/src/factories/api.js +0 -72
- package/src/factories/api.ts +0 -72
- package/src/factories/cache.js +0 -71
- package/src/factories/cache.ts +0 -71
- package/src/factories/factory.js +0 -149
- package/src/factories/factory.ts +0 -158
- package/src/factories/service.js +0 -28
- package/src/factories/service.ts +0 -28
- package/src/factories/state.js +0 -10
- package/src/factories/state.ts +0 -10
- package/src/factories/thunk.js +0 -53
- package/src/factories/thunk.ts +0 -53
- package/src/index.ts +0 -7
- package/tsconfig.json +0 -11
package/src/factory/service.js
CHANGED
|
@@ -1,28 +1,422 @@
|
|
|
1
|
-
// src/services/myApi.js
|
|
2
1
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
|
3
|
-
const USERS_URL = "auth"
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Default configuration for the API factory
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
baseUrl: process.env.REACT_APP_API_BASE_URL || 'http://localhost:4000/fear/api/',
|
|
8
|
+
timeout: 30000,
|
|
9
|
+
credentials: 'include',
|
|
10
|
+
prepareHeaders: (headers, { getState }) => {
|
|
11
|
+
// Add common headers here
|
|
12
|
+
headers.set('Content-Type', 'application/json');
|
|
13
|
+
|
|
14
|
+
// Add auth token if available
|
|
15
|
+
const token = getState()?.auth?.token;
|
|
16
|
+
if (token) {
|
|
17
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return headers;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Standard HTTP methods
|
|
26
|
+
*/
|
|
27
|
+
const HTTP_METHODS = {
|
|
28
|
+
GET: 'GET',
|
|
29
|
+
POST: 'POST',
|
|
30
|
+
PUT: 'PUT',
|
|
31
|
+
PATCH: 'PATCH',
|
|
32
|
+
DELETE: 'DELETE',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Standard endpoint configurations for common CRUD operations
|
|
37
|
+
*/
|
|
38
|
+
const STANDARD_ENDPOINTS = {
|
|
39
|
+
getAll: {
|
|
40
|
+
method: HTTP_METHODS.GET,
|
|
41
|
+
url: (entity) => `${entity}/all`,
|
|
42
|
+
providesTags: (entity) => [{ type: entity, id: 'LIST' }],
|
|
43
|
+
},
|
|
44
|
+
getById: {
|
|
45
|
+
method: HTTP_METHODS.GET,
|
|
46
|
+
url: (entity, id) => `${entity}/${id}`,
|
|
47
|
+
providesTags: (entity, id) => [{ type: entity, id }],
|
|
48
|
+
},
|
|
49
|
+
create: {
|
|
50
|
+
method: HTTP_METHODS.POST,
|
|
51
|
+
url: (entity) => `${entity}`,
|
|
52
|
+
invalidatesTags: (entity) => [{ type: entity, id: 'LIST' }],
|
|
53
|
+
},
|
|
54
|
+
update: {
|
|
55
|
+
method: HTTP_METHODS.PUT,
|
|
56
|
+
url: (entity, id) => `${entity}/${id}`,
|
|
57
|
+
invalidatesTags: (entity, id) => [
|
|
58
|
+
{ type: entity, id },
|
|
59
|
+
{ type: entity, id: 'LIST' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
patch: {
|
|
63
|
+
method: HTTP_METHODS.PATCH,
|
|
64
|
+
url: (entity, id) => `${entity}/${id}`,
|
|
65
|
+
invalidatesTags: (entity, id) => [
|
|
66
|
+
{ type: entity, id },
|
|
67
|
+
{ type: entity, id: 'LIST' },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
delete: {
|
|
71
|
+
method: HTTP_METHODS.DELETE,
|
|
72
|
+
url: (entity, id) => `${entity}/${id}`,
|
|
73
|
+
invalidatesTags: (entity, id) => [
|
|
74
|
+
{ type: entity, id },
|
|
75
|
+
{ type: entity, id: 'LIST' },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
search: {
|
|
79
|
+
method: HTTP_METHODS.GET,
|
|
80
|
+
url: (entity) => `${entity}/search`,
|
|
81
|
+
providesTags: (entity) => [{ type: entity, id: 'SEARCH' }],
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validates input parameters
|
|
87
|
+
* @param {string} sliceName - The slice/entity name
|
|
88
|
+
* @throws {Error} If validation fails
|
|
89
|
+
*/
|
|
90
|
+
const validateParams = (sliceName) => {
|
|
91
|
+
if (!sliceName || typeof sliceName !== 'string') {
|
|
92
|
+
throw new Error('SliceName must be a non-empty string');
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Transforms entity name to proper case for tag types
|
|
98
|
+
* @param {string} entity - Entity name
|
|
99
|
+
* @returns {string} Capitalized entity name
|
|
100
|
+
*/
|
|
101
|
+
const toTagType = (entity) => {
|
|
102
|
+
return entity.charAt(0).toUpperCase() + entity.slice(1);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Creates a standardized error handler
|
|
107
|
+
* @param {Object} error - Error object from RTK Query
|
|
108
|
+
* @returns {Object} Formatted error
|
|
109
|
+
*/
|
|
110
|
+
const handleError = (error) => {
|
|
111
|
+
if (error.status) {
|
|
112
|
+
return {
|
|
113
|
+
status: error.status,
|
|
114
|
+
message: error.data?.message || `HTTP Error ${error.status}`,
|
|
115
|
+
data: error.data,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
status: 'FETCH_ERROR',
|
|
121
|
+
message: error.message || 'Network error occurred',
|
|
122
|
+
error: error.error,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates query configuration for RTK Query
|
|
128
|
+
* @param {Object} config - Endpoint configuration
|
|
129
|
+
* @param {string} entity - Entity name
|
|
130
|
+
* @param {any} args - Arguments passed to the query
|
|
131
|
+
* @returns {Object} RTK Query configuration
|
|
132
|
+
*/
|
|
133
|
+
const createQueryConfig = (config, entity, args = {}) => {
|
|
134
|
+
const { method, url: urlFn } = config;
|
|
135
|
+
|
|
136
|
+
let queryConfig = {
|
|
137
|
+
method,
|
|
138
|
+
url: typeof urlFn === 'function' ? urlFn(entity, args.id, args) : urlFn,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Add body for mutations
|
|
142
|
+
if (method !== HTTP_METHODS.GET && method !== HTTP_METHODS.DELETE) {
|
|
143
|
+
queryConfig.body = args.data || args;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add params for GET requests with parameters
|
|
147
|
+
if (method === HTTP_METHODS.GET && args.params) {
|
|
148
|
+
queryConfig.params = args.params;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return queryConfig;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Factory function to create RTK Query APIs with standardized patterns
|
|
156
|
+
* @param {string} sliceName - The slice/entity name
|
|
157
|
+
* @param {Object} options - Configuration options
|
|
158
|
+
* @param {string[]} options.tagTypes - Additional tag types beyond the entity
|
|
159
|
+
* @param {Object} options.baseQuery - Custom base query configuration
|
|
160
|
+
* @param {boolean} options.includeStandardEndpoints - Whether to include CRUD endpoints
|
|
161
|
+
* @returns {Object} ApiFactory instance with methods to build and configure the API
|
|
162
|
+
*/
|
|
163
|
+
const ApiFactory = (sliceName, options = {}) => {
|
|
164
|
+
validateParams(sliceName);
|
|
165
|
+
|
|
166
|
+
const config = {
|
|
167
|
+
tagTypes: ['Product', 'Order', 'User', 'Category'],
|
|
168
|
+
baseQuery: {},
|
|
169
|
+
includeStandardEndpoints: true,
|
|
170
|
+
...options,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Ensure the entity tag type is included
|
|
174
|
+
const entityTagType = toTagType(sliceName);
|
|
175
|
+
if (!config.tagTypes.includes(entityTagType)) {
|
|
176
|
+
config.tagTypes.push(entityTagType);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Create base query with merged configuration
|
|
180
|
+
const baseQueryConfig = {
|
|
181
|
+
...DEFAULT_CONFIG,
|
|
182
|
+
...config.baseQuery,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Create the base API
|
|
186
|
+
const baseApi = createApi({
|
|
187
|
+
reducerPath: `${sliceName}Api`,
|
|
188
|
+
tagTypes: config.tagTypes,
|
|
189
|
+
baseQuery: fetchBaseQuery(baseQueryConfig),
|
|
190
|
+
endpoints: () => ({}),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Injects a single endpoint into the API
|
|
195
|
+
* @param {string} name - Endpoint name
|
|
196
|
+
* @param {Object} endpointConfig - Endpoint configuration
|
|
197
|
+
* @param {string} endpointConfig.method - HTTP method
|
|
198
|
+
* @param {string|Function} endpointConfig.url - URL or URL generator function
|
|
199
|
+
* @param {string} endpointConfig.type - 'query' or 'mutation'
|
|
200
|
+
* @param {Function} endpointConfig.providesTags - Tags provided by this endpoint
|
|
201
|
+
* @param {Function} endpointConfig.invalidatesTags - Tags invalidated by this endpoint
|
|
202
|
+
* @param {Function} endpointConfig.transformResponse - Response transformer
|
|
203
|
+
* @param {Function} endpointConfig.transformErrorResponse - Error transformer
|
|
204
|
+
* @returns {Object} Enhanced API with injected endpoint
|
|
205
|
+
*/
|
|
206
|
+
const injectEndpoint = (name, endpointConfig) => {
|
|
207
|
+
const {
|
|
208
|
+
method = HTTP_METHODS.GET,
|
|
209
|
+
url,
|
|
210
|
+
type = method === HTTP_METHODS.GET ? 'query' : 'mutation',
|
|
211
|
+
providesTags,
|
|
212
|
+
invalidatesTags,
|
|
213
|
+
transformResponse,
|
|
214
|
+
transformErrorResponse = handleError,
|
|
215
|
+
} = endpointConfig;
|
|
216
|
+
|
|
217
|
+
return baseApi.injectEndpoints({
|
|
218
|
+
endpoints: (builder) => ({
|
|
219
|
+
[name]: builder[type]({
|
|
220
|
+
query: (args = {}) => createQueryConfig({ method, url }, sliceName, args),
|
|
221
|
+
providesTags: providesTags
|
|
222
|
+
? (result, error, args) => providesTags(sliceName, args?.id, result, error, args)
|
|
223
|
+
: undefined,
|
|
224
|
+
invalidatesTags: invalidatesTags
|
|
225
|
+
? (result, error, args) => invalidatesTags(sliceName, args?.id, result, error, args)
|
|
226
|
+
: undefined,
|
|
227
|
+
transformResponse,
|
|
228
|
+
transformErrorResponse,
|
|
229
|
+
}),
|
|
230
|
+
}),
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Injects multiple endpoints at once
|
|
236
|
+
* @param {Object} endpoints - Object with endpoint configurations
|
|
237
|
+
* @returns {Object} Enhanced API with all injected endpoints
|
|
238
|
+
*/
|
|
239
|
+
const injectEndpoints = (endpoints) => {
|
|
240
|
+
return baseApi.injectEndpoints({
|
|
241
|
+
endpoints: (builder) => {
|
|
242
|
+
const builtEndpoints = {};
|
|
9
243
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
244
|
+
Object.entries(endpoints).forEach(([name, config]) => {
|
|
245
|
+
const {
|
|
246
|
+
method = HTTP_METHODS.GET,
|
|
247
|
+
url,
|
|
248
|
+
type = method === HTTP_METHODS.GET ? 'query' : 'mutation',
|
|
249
|
+
providesTags,
|
|
250
|
+
invalidatesTags,
|
|
251
|
+
transformResponse,
|
|
252
|
+
transformErrorResponse = handleError,
|
|
253
|
+
} = config;
|
|
254
|
+
|
|
255
|
+
builtEndpoints[name] = builder[type]({
|
|
256
|
+
query: (args = {}) => createQueryConfig({ method, url }, sliceName, args),
|
|
257
|
+
providesTags: providesTags
|
|
258
|
+
? (result, error, args) => providesTags(sliceName, args?.id, result, error, args)
|
|
259
|
+
: undefined,
|
|
260
|
+
invalidatesTags: invalidatesTags
|
|
261
|
+
? (result, error, args) => invalidatesTags(sliceName, args?.id, result, error, args)
|
|
262
|
+
: undefined,
|
|
263
|
+
transformResponse,
|
|
264
|
+
transformErrorResponse,
|
|
265
|
+
});
|
|
15
266
|
});
|
|
267
|
+
|
|
268
|
+
return builtEndpoints;
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
};
|
|
16
272
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Creates standard CRUD endpoints for the entity
|
|
275
|
+
* @returns {Object} Enhanced API with standard endpoints
|
|
276
|
+
*/
|
|
277
|
+
const createStandardEndpoints = () => {
|
|
278
|
+
const endpoints = {};
|
|
279
|
+
|
|
280
|
+
Object.entries(STANDARD_ENDPOINTS).forEach(([name, config]) => {
|
|
281
|
+
endpoints[name] = {
|
|
282
|
+
method: config.method,
|
|
283
|
+
url: config.url,
|
|
284
|
+
providesTags: config.providesTags,
|
|
285
|
+
invalidatesTags: config.invalidatesTags,
|
|
286
|
+
};
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
return injectEndpoints(endpoints);
|
|
290
|
+
};
|
|
21
291
|
|
|
22
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Creates the final API with all configured endpoints
|
|
294
|
+
* @returns {Object} Complete RTK Query API
|
|
295
|
+
*/
|
|
296
|
+
const create = () => {
|
|
297
|
+
let api = baseApi;
|
|
298
|
+
|
|
299
|
+
if (config.includeStandardEndpoints) {
|
|
300
|
+
api = createStandardEndpoints();
|
|
23
301
|
}
|
|
302
|
+
|
|
303
|
+
return api;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Creates a custom endpoint with advanced configuration
|
|
308
|
+
* @param {string} name - Endpoint name
|
|
309
|
+
* @param {Object} config - Advanced endpoint configuration
|
|
310
|
+
* @returns {Object} Enhanced API with custom endpoint
|
|
311
|
+
*/
|
|
312
|
+
const createCustomEndpoint = (name, config) => {
|
|
313
|
+
return injectEndpoint(name, config);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Creates endpoints for bulk operations
|
|
318
|
+
* @returns {Object} Enhanced API with bulk endpoints
|
|
319
|
+
*/
|
|
320
|
+
const createBulkEndpoints = () => {
|
|
321
|
+
const bulkEndpoints = {
|
|
322
|
+
bulkCreate: {
|
|
323
|
+
method: HTTP_METHODS.POST,
|
|
324
|
+
url: `${sliceName}/bulk`,
|
|
325
|
+
invalidatesTags: () => [{ type: entityTagType, id: 'LIST' }],
|
|
326
|
+
},
|
|
327
|
+
bulkUpdate: {
|
|
328
|
+
method: HTTP_METHODS.PUT,
|
|
329
|
+
url: `${sliceName}/bulk`,
|
|
330
|
+
invalidatesTags: () => [{ type: entityTagType, id: 'LIST' }],
|
|
331
|
+
},
|
|
332
|
+
bulkDelete: {
|
|
333
|
+
method: HTTP_METHODS.DELETE,
|
|
334
|
+
url: `${sliceName}/bulk`,
|
|
335
|
+
invalidatesTags: () => [{ type: entityTagType, id: 'LIST' }],
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
return injectEndpoints(bulkEndpoints);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// Public API
|
|
343
|
+
return {
|
|
344
|
+
// Core properties
|
|
345
|
+
sliceName,
|
|
346
|
+
entityTagType,
|
|
347
|
+
baseApi,
|
|
348
|
+
|
|
349
|
+
// Methods
|
|
350
|
+
inject: injectEndpoint,
|
|
351
|
+
injectMany: injectEndpoints,
|
|
352
|
+
create,
|
|
353
|
+
createStandard: createStandardEndpoints,
|
|
354
|
+
createBulk: createBulkEndpoints,
|
|
355
|
+
createCustom: createCustomEndpoint,
|
|
356
|
+
|
|
357
|
+
// Utilities
|
|
358
|
+
getTagTypes: () => [...config.tagTypes],
|
|
359
|
+
getBaseUrl: () => baseQueryConfig.baseUrl,
|
|
360
|
+
getReducerPath: () => baseApi.reducerPath,
|
|
361
|
+
|
|
362
|
+
// Advanced methods
|
|
363
|
+
withAuth: (token) => {
|
|
364
|
+
return ApiFactory(sliceName, {
|
|
365
|
+
...config,
|
|
366
|
+
baseQuery: {
|
|
367
|
+
...baseQueryConfig,
|
|
368
|
+
prepareHeaders: (headers, api) => {
|
|
369
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
370
|
+
return DEFAULT_CONFIG.prepareHeaders(headers, api);
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
withBaseUrl: (baseUrl) => {
|
|
377
|
+
return ApiFactory(sliceName, {
|
|
378
|
+
...config,
|
|
379
|
+
baseQuery: {
|
|
380
|
+
...baseQueryConfig,
|
|
381
|
+
baseUrl,
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Creates a complete API factory with all standard endpoints
|
|
390
|
+
* @param {string} sliceName - Entity name
|
|
391
|
+
* @param {Object} options - Configuration options
|
|
392
|
+
* @returns {Object} Complete RTK Query API
|
|
393
|
+
*/
|
|
394
|
+
ApiFactory.createComplete = (sliceName, options = {}) => {
|
|
395
|
+
return ApiFactory(sliceName, options).create();
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Creates an API factory with only custom endpoints
|
|
400
|
+
* @param {string} sliceName - Entity name
|
|
401
|
+
* @param {Object} endpoints - Custom endpoints
|
|
402
|
+
* @param {Object} options - Configuration options
|
|
403
|
+
* @returns {Object} RTK Query API with custom endpoints
|
|
404
|
+
*/
|
|
405
|
+
ApiFactory.createCustom = (sliceName, endpoints, options = {}) => {
|
|
406
|
+
return ApiFactory(sliceName, { ...options, includeStandardEndpoints: false })
|
|
407
|
+
.injectMany(endpoints);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Utility to get standard endpoint configurations
|
|
412
|
+
* @returns {Object} Standard endpoint configurations
|
|
413
|
+
*/
|
|
414
|
+
ApiFactory.getStandardEndpoints = () => ({ ...STANDARD_ENDPOINTS });
|
|
24
415
|
|
|
25
|
-
|
|
26
|
-
|
|
416
|
+
/**
|
|
417
|
+
* Utility to get HTTP methods
|
|
418
|
+
* @returns {Object} HTTP methods
|
|
419
|
+
*/
|
|
420
|
+
ApiFactory.getHttpMethods = () => ({ ...HTTP_METHODS });
|
|
27
421
|
|
|
28
422
|
export default ApiFactory;
|