tanstack-cacher 1.2.1 → 1.3.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025
3
+ Copyright (c) 2025 Hagi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { QueryClient, QueryKey, UseQueryOptions, UseMutationOptions, UseQueryResult } from '@tanstack/react-query';
2
+ import { QueryClient, QueryKey, UseMutationOptions } from '@tanstack/react-query';
3
3
  export * from '@tanstack/react-query';
4
4
 
5
5
  interface PaginationConfig {
@@ -9,10 +9,11 @@ interface PaginationConfig {
9
9
  pageSizePath?: string;
10
10
  }
11
11
  interface CacheConfig<TData, TItem> {
12
- queryClient: QueryClient;
12
+ queryClient?: QueryClient;
13
13
  queryKey: QueryKey;
14
- itemsPath: string;
14
+ itemsPath?: string;
15
15
  pagination?: PaginationConfig;
16
+ isPaginated?: boolean;
16
17
  keyExtractor?: (item: TItem) => string | number;
17
18
  initialData?: TData;
18
19
  }
@@ -38,23 +39,31 @@ declare class QueryCacheManager<TData, TItem> {
38
39
  getItemsFromCache(): TItem[];
39
40
  getDataFromCache(): TData | undefined;
40
41
  invalidate(): void;
42
+ refetch(key?: string | string[]): void;
41
43
  createHandlers(): CacheHandlers<TItem>;
42
44
  }
43
45
 
44
- type CacheOptions<TData = any, TItem = any> = Omit<CacheConfig<TData, TItem>, 'queryClient' | 'queryKey'>;
46
+ type CacheManagerConstructor = new <TData, TItem>(config: CacheConfig<TData, TItem>) => QueryCacheManager<TData, TItem>;
47
+ declare class CacheManagerFactory {
48
+ private managerClass;
49
+ setManagerClass(managerClass: CacheManagerConstructor): void;
50
+ resetManagerClass(): void;
51
+ create<TData, TItem>(config: CacheConfig<TData, TItem>): QueryCacheManager<TData, TItem>;
52
+ getManagerClass(): CacheManagerConstructor;
53
+ }
54
+ declare const cacheManagerFactory: CacheManagerFactory;
45
55
 
46
- type CustomQueryOptions<TData, TError = unknown> = UseQueryOptions<TData, TError> & {
47
- queryKey: QueryKey;
48
- cacheType?: string;
49
- queryFn: () => Promise<TData>;
50
- cacheConfig?: CacheOptions;
51
- };
52
- type CustomMutationOptions<TData, TError, TVariables, TContext> = UseMutationOptions<TData, TError, TVariables, TContext> & {
56
+ type MutationTypes = 'add' | 'invalidate' | 'remove' | 'update';
57
+ interface CacheActions<TData, TItem = unknown> extends Omit<CacheConfig<TData, TItem>, 'queryClient'> {
58
+ type: MutationTypes;
59
+ }
60
+ type CustomMutationOptions<TData, TError, TVariables> = UseMutationOptions<TData, TError, TVariables> & {
53
61
  notify?: boolean;
54
62
  notifyError?: boolean;
55
63
  errorMessage?: string;
56
64
  notifySuccess?: boolean;
57
65
  successMessage?: string;
66
+ cacheActions?: CacheActions<TData>[] | CacheActions<TData>;
58
67
  notificationConfig?: NotificationOptions;
59
68
  getErrorMessage?: (error: TError) => string;
60
69
  };
@@ -63,11 +72,7 @@ interface NotificationOptions {
63
72
  [key: string]: any;
64
73
  }
65
74
 
66
- declare function useCustomQuery<TData, TError = unknown, TItem = any>(options: CustomQueryOptions<TData, TError>): UseQueryResult<TData, TError> & {
67
- cache?: QueryCacheManager<TData, TItem>;
68
- };
69
-
70
- declare const useCustomMutation: <TData, TError, TVariables = void, TContext = unknown>(options: CustomMutationOptions<TData, TError, TVariables, TContext>) => _tanstack_react_query.UseMutationResult<TData, TError, TVariables, TContext>;
75
+ declare const useCustomMutation: <TData, TError, TVariables = void>(options: CustomMutationOptions<TData, TError, TVariables>) => _tanstack_react_query.UseMutationResult<TData, TError, TVariables, unknown>;
71
76
 
72
77
  declare const useQueryCacheManagers: <T extends Record<string, QueryCacheManager<any, any>>>(configs: { [K in keyof T]: {
73
78
  queryKey: readonly unknown[];
@@ -81,7 +86,9 @@ interface NotificationContextType {
81
86
 
82
87
  declare const useNotificationContext: () => NotificationContextType;
83
88
 
89
+ type CacheOptions<TData = any, TItem = any> = Omit<CacheConfig<TData, TItem>, 'queryClient' | 'queryKey'>;
90
+
84
91
  declare const resetCacheManager: (queryKey: QueryKey) => void;
85
92
  declare const resetAllCacheManagers: () => void;
86
93
 
87
- export { type CacheConfig, type CacheHandlers, type CacheOptions, type CustomMutationOptions, type CustomQueryOptions, type InsertPosition, type PaginationConfig, QueryCacheManager, resetAllCacheManagers, resetCacheManager, useCustomMutation, useCustomQuery, useNotificationContext, useQueryCacheManagers };
94
+ export { type CacheConfig, type CacheHandlers, type CacheManagerConstructor, type CacheOptions, type CustomMutationOptions, type InsertPosition, type PaginationConfig, QueryCacheManager, cacheManagerFactory, resetAllCacheManagers, resetCacheManager, useCustomMutation, useNotificationContext, useQueryCacheManagers };
@@ -11,7 +11,7 @@ function getAtPath(obj, path, defaultValue) {
11
11
  const keys = path.split(".");
12
12
  let result = obj;
13
13
  for (const key of keys) {
14
- if (result === null || result === void 0) {
14
+ if (result === null) {
15
15
  return defaultValue;
16
16
  }
17
17
  result = result[key];
@@ -40,13 +40,46 @@ function incrementAtPath(obj, path, increment) {
40
40
  const newValue = currentValue + increment;
41
41
  return setAtPath(obj, path, newValue);
42
42
  }
43
+ var runCacheManagers = (type, manager, data) => {
44
+ switch (type) {
45
+ case "invalidate":
46
+ manager.invalidate();
47
+ break;
48
+ case "remove":
49
+ manager.delete(data);
50
+ break;
51
+ case "add":
52
+ manager.add(data);
53
+ break;
54
+ case "update":
55
+ manager.update(data);
56
+ break;
57
+ }
58
+ };
59
+
60
+ // src/managers/QueryCacheManager/QueryCache.consts.ts
61
+ var DEFAULT_PAGINATION_PATHS = {
62
+ totalElementsPath: "data.page.totalElements",
63
+ totalPagesPath: "data.page.totalPages",
64
+ currentPagePath: "data.page.number",
65
+ pageSizePath: "data.page.size"
66
+ };
43
67
 
44
68
  // src/managers/QueryCacheManager/QueryCache.manager.ts
69
+ var defaultQueryClient = new reactQuery.QueryClient();
45
70
  var QueryCacheManager = class {
46
71
  constructor(config) {
72
+ const isPaginated = Boolean(config.pagination);
47
73
  this.config = {
48
74
  ...config,
49
- keyExtractor: config.keyExtractor || ((item) => item.id)
75
+ itemsPath: config.itemsPath ?? "data.content",
76
+ queryClient: config.queryClient ?? defaultQueryClient,
77
+ isPaginated,
78
+ keyExtractor: config.keyExtractor || ((item) => item.id),
79
+ pagination: isPaginated ? {
80
+ ...DEFAULT_PAGINATION_PATHS,
81
+ ...config.pagination
82
+ } : void 0
50
83
  };
51
84
  }
52
85
  /**
@@ -270,6 +303,35 @@ var QueryCacheManager = class {
270
303
  invalidate() {
271
304
  this.config.queryClient.invalidateQueries({ queryKey: this.config.queryKey });
272
305
  }
306
+ /**
307
+ * Refetch query immediately
308
+ */
309
+ refetch(key) {
310
+ try {
311
+ if (!key) {
312
+ this.config.queryClient.refetchQueries({
313
+ queryKey: this.config.queryKey,
314
+ exact: true
315
+ });
316
+ return;
317
+ }
318
+ if (Array.isArray(key)) {
319
+ key.forEach((keyItem) => {
320
+ this.config.queryClient.refetchQueries({
321
+ queryKey: [keyItem],
322
+ exact: true
323
+ });
324
+ });
325
+ } else {
326
+ this.config.queryClient.refetchQueries({
327
+ queryKey: [key],
328
+ exact: true
329
+ });
330
+ }
331
+ } catch (error) {
332
+ console.error("[QueryCacheManager] Refetch failed:", error);
333
+ }
334
+ }
273
335
  /**
274
336
  * Get handlers for use with mutations
275
337
  *
@@ -284,75 +346,25 @@ var QueryCacheManager = class {
284
346
  }
285
347
  };
286
348
 
287
- // src/utils/cacheRegistry.ts
288
- var cacheConfigRegistry = /* @__PURE__ */ new Map();
289
- var cacheRegistry = /* @__PURE__ */ new Map();
290
- var defaultConfigs = {
291
- paginated: {
292
- itemsPath: "data.content",
293
- pagination: {
294
- totalElementsPath: "data.page.totalElements",
295
- totalPagesPath: "data.page.totalPages",
296
- currentPagePath: "data.page.number",
297
- pageSizePath: "data.page.size"
298
- }
299
- },
300
- nonPaginated: {
301
- itemsPath: "data"
349
+ // src/managers/QueryCacheManager/CacheManagerFactory.ts
350
+ var CacheManagerFactory = class {
351
+ constructor() {
352
+ this.managerClass = QueryCacheManager;
302
353
  }
303
- };
304
- Object.entries(defaultConfigs).forEach(([key, config]) => {
305
- cacheConfigRegistry.set(key, config);
306
- });
307
- var getOrCreateCacheManager = (queryKey, queryClient, options) => {
308
- const key = JSON.stringify(queryKey);
309
- if (cacheRegistry.has(key)) {
310
- return cacheRegistry.get(key);
354
+ setManagerClass(managerClass) {
355
+ this.managerClass = managerClass;
311
356
  }
312
- const cacheType = options?.cacheType;
313
- const config = options?.cacheConfig || cacheType && cacheConfigRegistry.get(cacheType);
314
- if (!config) {
315
- throw new Error(
316
- `[CacheManager] Unknown cacheType "${cacheType}". Available: ${[
317
- ...cacheConfigRegistry.keys()
318
- ].join(", ")}`
319
- );
357
+ resetManagerClass() {
358
+ this.managerClass = QueryCacheManager;
320
359
  }
321
- const manager = new QueryCacheManager({
322
- queryClient,
323
- queryKey,
324
- ...config
325
- });
326
- cacheRegistry.set(key, manager);
327
- return manager;
328
- };
329
- var resetCacheManager = (queryKey) => {
330
- cacheRegistry.delete(JSON.stringify(queryKey));
331
- };
332
- var resetAllCacheManagers = () => {
333
- cacheRegistry.clear();
334
- };
335
-
336
- // src/hooks/useCustomQuery.ts
337
- function useCustomQuery(options) {
338
- const queryClient = reactQuery.useQueryClient();
339
- const { queryKey, cacheType, cacheConfig, ...rest } = options;
340
- const queryResult = reactQuery.useQuery({
341
- queryKey,
342
- ...rest
343
- });
344
- let cache;
345
- if (cacheType) {
346
- cache = getOrCreateCacheManager(queryKey, queryClient, {
347
- cacheType,
348
- cacheConfig
349
- });
360
+ create(config) {
361
+ return new this.managerClass(config);
350
362
  }
351
- return {
352
- ...queryResult,
353
- cache
354
- };
355
- }
363
+ getManagerClass() {
364
+ return this.managerClass;
365
+ }
366
+ };
367
+ var cacheManagerFactory = new CacheManagerFactory();
356
368
  var NotificationContext = react.createContext(
357
369
  void 0
358
370
  );
@@ -371,6 +383,7 @@ var useCustomMutation = (options) => {
371
383
  const {
372
384
  onError,
373
385
  onSuccess,
386
+ cacheActions,
374
387
  notify = false,
375
388
  notifyError = false,
376
389
  notifySuccess = false,
@@ -380,21 +393,32 @@ var useCustomMutation = (options) => {
380
393
  getErrorMessage,
381
394
  ...rest
382
395
  } = options;
383
- const { showSuccess, showError } = useNotificationContext();
396
+ const notificationContext = useNotificationContext();
384
397
  return reactQuery.useMutation({
385
398
  ...rest,
386
- onSuccess: (data, variables, context, mutation) => {
399
+ onSuccess: (data, variables, mResult, context) => {
387
400
  if (notify || notifySuccess) {
388
- showSuccess(successMessage, notificationConfig);
401
+ notificationContext?.showSuccess?.(successMessage, notificationConfig);
402
+ }
403
+ onSuccess?.(data, variables, mResult, context);
404
+ if (Array.isArray(cacheActions)) {
405
+ cacheActions.forEach((item) => {
406
+ const { type, ...rest2 } = item;
407
+ const manager = cacheManagerFactory.create(rest2);
408
+ runCacheManagers(type, manager, data);
409
+ });
410
+ } else if (cacheActions) {
411
+ const { type, ...rest2 } = cacheActions;
412
+ const manager = cacheManagerFactory.create(rest2);
413
+ runCacheManagers(type, manager, data);
389
414
  }
390
- onSuccess?.(data, variables, context, mutation);
391
415
  },
392
- onError: (apiError, variables, context, mutation) => {
416
+ onError: (apiError, variables, mResult, context) => {
393
417
  const message = getErrorMessage ? getErrorMessage(apiError) : apiError?.error?.message ?? errorMessage;
394
418
  if (notify || notifyError) {
395
- showError(message, notificationConfig);
419
+ notificationContext?.showError(message, notificationConfig);
396
420
  }
397
- onError?.(apiError, variables, context, mutation);
421
+ onError?.(apiError, variables, mResult, context);
398
422
  }
399
423
  });
400
424
  };
@@ -413,11 +437,38 @@ var useQueryCacheManagers = (configs) => {
413
437
  return managers;
414
438
  };
415
439
 
440
+ // src/utils/cacheRegistry.ts
441
+ var cacheConfigRegistry = /* @__PURE__ */ new Map();
442
+ var cacheRegistry = /* @__PURE__ */ new Map();
443
+ var defaultConfigs = {
444
+ paginated: {
445
+ itemsPath: "data.content",
446
+ pagination: {
447
+ totalElementsPath: "data.page.totalElements",
448
+ totalPagesPath: "data.page.totalPages",
449
+ currentPagePath: "data.page.number",
450
+ pageSizePath: "data.page.size"
451
+ }
452
+ },
453
+ nonPaginated: {
454
+ itemsPath: "data"
455
+ }
456
+ };
457
+ Object.entries(defaultConfigs).forEach(([key, config]) => {
458
+ cacheConfigRegistry.set(key, config);
459
+ });
460
+ var resetCacheManager = (queryKey) => {
461
+ cacheRegistry.delete(JSON.stringify(queryKey));
462
+ };
463
+ var resetAllCacheManagers = () => {
464
+ cacheRegistry.clear();
465
+ };
466
+
416
467
  exports.QueryCacheManager = QueryCacheManager;
468
+ exports.cacheManagerFactory = cacheManagerFactory;
417
469
  exports.resetAllCacheManagers = resetAllCacheManagers;
418
470
  exports.resetCacheManager = resetCacheManager;
419
471
  exports.useCustomMutation = useCustomMutation;
420
- exports.useCustomQuery = useCustomQuery;
421
472
  exports.useNotificationContext = useNotificationContext;
422
473
  exports.useQueryCacheManagers = useQueryCacheManagers;
423
474
  Object.keys(reactQuery).forEach(function (k) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tanstack-cacher",
3
- "version": "1.2.1",
3
+ "version": "1.3.2",
4
4
  "description": "A lightweight cache management utility for TanStack Query that simplifies adding, updating, deleting, and synchronizing cached data",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,32 +28,36 @@
28
28
  "cache-manager",
29
29
  "optimistic-updates"
30
30
  ],
31
- "main": "./dist/index.js",
32
- "module": "./dist/index.mjs",
33
- "types": "./dist/index.d.ts",
31
+ "main": "./index.js",
32
+ "module": "./index.js",
33
+ "types": "./index.d.ts",
34
+ "files": [
35
+ "index.js",
36
+ "index.d.ts",
37
+ "styles",
38
+ "LICENSE",
39
+ "README.md"
40
+ ],
34
41
  "exports": {
35
42
  ".": {
36
- "types": "./dist/index.d.ts",
37
- "import": "./dist/index.mjs",
38
- "require": "./dist/index.js"
39
- }
43
+ "types": "./index.d.ts",
44
+ "import": "./index.js"
45
+ },
46
+ "./package.json": "./package.json"
40
47
  },
41
- "files": [
42
- "dist",
43
- "README.md",
44
- "LICENSE"
45
- ],
46
48
  "engines": {
47
49
  "node": ">=16"
48
50
  },
49
51
  "scripts": {
50
- "build": "tsup",
51
52
  "dev": "tsup --watch",
52
- "lint": "eslint src --ext .ts,.tsx",
53
- "lint:fix": "eslint src --ext .ts,.tsx --fix",
53
+ "locale-build": "tsup",
54
54
  "type-check": "tsc --noEmit",
55
55
  "prepublishOnly": "npm run build",
56
- "test": "echo \"No tests yet\" && exit 0"
56
+ "lint": "eslint src --ext .ts,.tsx",
57
+ "build": "tsup && npm run copy-dist",
58
+ "test": "echo \"No tests yet\" && exit 0",
59
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
60
+ "copy-dist": "cp dist/index.js index.js && cp dist/index.d.ts index.d.ts"
57
61
  },
58
62
  "dependencies": {
59
63
  "@tanstack/react-query": "^5.62.11"
package/dist/index.d.mts DELETED
@@ -1,87 +0,0 @@
1
- import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { QueryClient, QueryKey, UseQueryOptions, UseMutationOptions, UseQueryResult } from '@tanstack/react-query';
3
- export * from '@tanstack/react-query';
4
-
5
- interface PaginationConfig {
6
- totalElementsPath?: string;
7
- totalPagesPath?: string;
8
- currentPagePath?: string;
9
- pageSizePath?: string;
10
- }
11
- interface CacheConfig<TData, TItem> {
12
- queryClient: QueryClient;
13
- queryKey: QueryKey;
14
- itemsPath: string;
15
- pagination?: PaginationConfig;
16
- keyExtractor?: (item: TItem) => string | number;
17
- initialData?: TData;
18
- }
19
- interface CacheHandlers<TItem> {
20
- onAdd: (newItem: TItem, position?: 'start' | 'end') => void;
21
- onUpdate: (updatedItem: Partial<TItem>, matcher?: (item: TItem) => boolean) => void;
22
- onDelete: (itemOrId: TItem | string | number, matcher?: (item: TItem) => boolean) => void;
23
- }
24
- type InsertPosition = 'start' | 'end';
25
-
26
- declare class QueryCacheManager<TData, TItem> {
27
- private config;
28
- constructor(config: CacheConfig<TData, TItem>);
29
- private getItems;
30
- private setItems;
31
- private updatePaginationOnAdd;
32
- private updatePaginationOnRemove;
33
- add(newItem: TItem, position?: InsertPosition): void;
34
- update(updatedItem: Partial<TItem>, matcher?: (item: TItem) => boolean): void;
35
- delete(itemOrId: TItem | string | number, matcher?: (item: TItem) => boolean): void;
36
- replace(newData: TData): void;
37
- clear(): void;
38
- getItemsFromCache(): TItem[];
39
- getDataFromCache(): TData | undefined;
40
- invalidate(): void;
41
- createHandlers(): CacheHandlers<TItem>;
42
- }
43
-
44
- type CacheOptions<TData = any, TItem = any> = Omit<CacheConfig<TData, TItem>, 'queryClient' | 'queryKey'>;
45
-
46
- type CustomQueryOptions<TData, TError = unknown> = UseQueryOptions<TData, TError> & {
47
- queryKey: QueryKey;
48
- cacheType?: string;
49
- queryFn: () => Promise<TData>;
50
- cacheConfig?: CacheOptions;
51
- };
52
- type CustomMutationOptions<TData, TError, TVariables, TContext> = UseMutationOptions<TData, TError, TVariables, TContext> & {
53
- notify?: boolean;
54
- notifyError?: boolean;
55
- errorMessage?: string;
56
- notifySuccess?: boolean;
57
- successMessage?: string;
58
- notificationConfig?: NotificationOptions;
59
- getErrorMessage?: (error: TError) => string;
60
- };
61
- interface NotificationOptions {
62
- duration?: number;
63
- [key: string]: any;
64
- }
65
-
66
- declare function useCustomQuery<TData, TError = unknown, TItem = any>(options: CustomQueryOptions<TData, TError>): UseQueryResult<TData, TError> & {
67
- cache?: QueryCacheManager<TData, TItem>;
68
- };
69
-
70
- declare const useCustomMutation: <TData, TError, TVariables = void, TContext = unknown>(options: CustomMutationOptions<TData, TError, TVariables, TContext>) => _tanstack_react_query.UseMutationResult<TData, TError, TVariables, TContext>;
71
-
72
- declare const useQueryCacheManagers: <T extends Record<string, QueryCacheManager<any, any>>>(configs: { [K in keyof T]: {
73
- queryKey: readonly unknown[];
74
- options?: Partial<Omit<CacheConfig<any, any>, "queryClient">>;
75
- }; }) => T;
76
-
77
- interface NotificationContextType {
78
- showError: (message: string, options?: NotificationOptions) => void;
79
- showSuccess: (message: string, options?: NotificationOptions) => void;
80
- }
81
-
82
- declare const useNotificationContext: () => NotificationContextType;
83
-
84
- declare const resetCacheManager: (queryKey: QueryKey) => void;
85
- declare const resetAllCacheManagers: () => void;
86
-
87
- export { type CacheConfig, type CacheHandlers, type CacheOptions, type CustomMutationOptions, type CustomQueryOptions, type InsertPosition, type PaginationConfig, QueryCacheManager, resetAllCacheManagers, resetCacheManager, useCustomMutation, useCustomQuery, useNotificationContext, useQueryCacheManagers };
package/dist/index.mjs DELETED
@@ -1,415 +0,0 @@
1
- import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
2
- export * from '@tanstack/react-query';
3
- import { createContext, useContext } from 'react';
4
-
5
- // src/index.ts
6
-
7
- // src/managers/QueryCacheManager/QueryCache.utils.ts
8
- function getAtPath(obj, path, defaultValue) {
9
- if (!obj || !path) return defaultValue;
10
- const keys = path.split(".");
11
- let result = obj;
12
- for (const key of keys) {
13
- if (result === null || result === void 0) {
14
- return defaultValue;
15
- }
16
- result = result[key];
17
- }
18
- return result !== void 0 ? result : defaultValue;
19
- }
20
- function setAtPath(obj, path, value) {
21
- if (!path) return obj;
22
- const keys = path.split(".");
23
- const root = obj ? { ...obj } : {};
24
- let current = root;
25
- for (let i = 0; i < keys.length - 1; i++) {
26
- const key = keys[i];
27
- if (!current[key] || typeof current[key] !== "object") {
28
- current[key] = {};
29
- } else {
30
- current[key] = { ...current[key] };
31
- }
32
- current = current[key];
33
- }
34
- current[keys[keys.length - 1]] = value;
35
- return root;
36
- }
37
- function incrementAtPath(obj, path, increment) {
38
- const currentValue = getAtPath(obj, path, 0);
39
- const newValue = currentValue + increment;
40
- return setAtPath(obj, path, newValue);
41
- }
42
-
43
- // src/managers/QueryCacheManager/QueryCache.manager.ts
44
- var QueryCacheManager = class {
45
- constructor(config) {
46
- this.config = {
47
- ...config,
48
- keyExtractor: config.keyExtractor || ((item) => item.id)
49
- };
50
- }
51
- /**
52
- * Get items array from data
53
- * Returns empty array if path doesn't exist or data is null
54
- */
55
- getItems(data) {
56
- if (!data) return [];
57
- if (!this.config.itemsPath) {
58
- return Array.isArray(data) ? data : [];
59
- }
60
- const items = getAtPath(data, this.config.itemsPath, []);
61
- return Array.isArray(items) ? items : [];
62
- }
63
- /**
64
- * Set items array in data
65
- * Creates nested structure if it doesn't exist
66
- */
67
- setItems(data, items) {
68
- if (!data) {
69
- if (this.config.initialData) {
70
- data = this.config.initialData;
71
- } else {
72
- if (!this.config.itemsPath) {
73
- return items;
74
- }
75
- data = {};
76
- }
77
- }
78
- if (!this.config.itemsPath) {
79
- return items;
80
- }
81
- return setAtPath(data, this.config.itemsPath, items);
82
- }
83
- /**
84
- * Update pagination metadata after adding items
85
- */
86
- updatePaginationOnAdd(data, addedCount) {
87
- if (!this.config.pagination) return data;
88
- let result = data;
89
- if (this.config.pagination.totalElementsPath) {
90
- result = incrementAtPath(
91
- result,
92
- this.config.pagination.totalElementsPath,
93
- addedCount
94
- );
95
- }
96
- if (this.config.pagination.totalPagesPath && this.config.pagination.pageSizePath && this.config.pagination.totalElementsPath) {
97
- const pageSize = getAtPath(result, this.config.pagination.pageSizePath, 0);
98
- const totalElements = getAtPath(
99
- result,
100
- this.config.pagination.totalElementsPath,
101
- 0
102
- );
103
- if (pageSize > 0) {
104
- const totalPages = Math.ceil(totalElements / pageSize);
105
- result = setAtPath(
106
- result,
107
- this.config.pagination.totalPagesPath,
108
- totalPages
109
- );
110
- }
111
- }
112
- return result;
113
- }
114
- /**
115
- * Update pagination metadata after removing items
116
- */
117
- updatePaginationOnRemove(data, removedCount) {
118
- if (!this.config.pagination) return data;
119
- let result = data;
120
- if (this.config.pagination.totalElementsPath) {
121
- result = incrementAtPath(
122
- result,
123
- this.config.pagination.totalElementsPath,
124
- -removedCount
125
- );
126
- }
127
- if (this.config.pagination.totalPagesPath && this.config.pagination.pageSizePath && this.config.pagination.totalElementsPath) {
128
- const pageSize = getAtPath(result, this.config.pagination.pageSizePath, 0);
129
- const totalElements = getAtPath(
130
- result,
131
- this.config.pagination.totalElementsPath,
132
- 0
133
- );
134
- if (pageSize > 0) {
135
- const totalPages = Math.ceil(Math.max(0, totalElements) / pageSize);
136
- result = setAtPath(
137
- result,
138
- this.config.pagination.totalPagesPath,
139
- totalPages
140
- );
141
- }
142
- }
143
- return result;
144
- }
145
- /**
146
- * Add item to cache
147
- *
148
- * @param newItem - The item to add
149
- * @param position - Where to add: 'start' or 'end'
150
- */
151
- add(newItem, position = "start") {
152
- try {
153
- this.config.queryClient.setQueryData(this.config.queryKey, (oldData) => {
154
- const items = this.getItems(oldData);
155
- const updatedItems = position === "start" ? [newItem, ...items] : [...items, newItem];
156
- let result = this.setItems(oldData, updatedItems);
157
- result = this.updatePaginationOnAdd(result, 1);
158
- return result;
159
- });
160
- } catch (error) {
161
- console.error("[QueryCacheManager] Add failed:", error);
162
- this.invalidate();
163
- }
164
- }
165
- /**
166
- * Update existing item
167
- *
168
- * @param updatedItem - Partial item data to update
169
- * @param matcher - Optional custom matcher function. Defaults to matching by key
170
- */
171
- update(updatedItem, matcher) {
172
- try {
173
- this.config.queryClient.setQueryData(this.config.queryKey, (oldData) => {
174
- const items = this.getItems(oldData);
175
- const matchFn = matcher || ((item) => this.config.keyExtractor(item) === this.config.keyExtractor(updatedItem));
176
- const updatedItems = items.map(
177
- (item) => matchFn(item) ? { ...item, ...updatedItem } : item
178
- );
179
- return this.setItems(oldData, updatedItems);
180
- });
181
- } catch (error) {
182
- console.error("[QueryCacheManager] Update failed:", error);
183
- this.invalidate();
184
- }
185
- }
186
- /**
187
- * Remove item from cache
188
- *
189
- * @param itemOrId - Item object or ID to remove
190
- * @param matcher - Optional custom matcher function. Defaults to matching by key
191
- */
192
- delete(itemOrId, matcher) {
193
- try {
194
- this.config.queryClient.setQueryData(this.config.queryKey, (oldData) => {
195
- const items = this.getItems(oldData);
196
- const matchFn = matcher || ((item) => {
197
- if (typeof itemOrId === "object") {
198
- return this.config.keyExtractor(item) === this.config.keyExtractor(itemOrId);
199
- }
200
- return this.config.keyExtractor(item) === itemOrId;
201
- });
202
- const originalLength = items.length;
203
- const updatedItems = items.filter((item) => !matchFn(item));
204
- const removedCount = originalLength - updatedItems.length;
205
- let result = this.setItems(oldData, updatedItems);
206
- if (removedCount > 0) {
207
- result = this.updatePaginationOnRemove(result, removedCount);
208
- }
209
- return result;
210
- });
211
- } catch (error) {
212
- console.error("[QueryCacheManager] Delete failed:", error);
213
- this.invalidate();
214
- }
215
- }
216
- /**
217
- * Replace full data
218
- *
219
- * @param newData - Complete new data to replace cache
220
- */
221
- replace(newData) {
222
- try {
223
- this.config.queryClient.setQueryData(this.config.queryKey, newData);
224
- } catch (error) {
225
- console.error("[QueryCacheManager] Replace failed:", error);
226
- this.invalidate();
227
- }
228
- }
229
- /**
230
- * Clear all items (keeps structure, empties array)
231
- */
232
- clear() {
233
- try {
234
- this.config.queryClient.setQueryData(this.config.queryKey, (oldData) => {
235
- let result = this.setItems(oldData, []);
236
- if (this.config.pagination?.totalElementsPath) {
237
- result = setAtPath(result, this.config.pagination.totalElementsPath, 0);
238
- }
239
- if (this.config.pagination?.totalPagesPath) {
240
- result = setAtPath(result, this.config.pagination.totalPagesPath, 0);
241
- }
242
- return result;
243
- });
244
- } catch (error) {
245
- console.error("[QueryCacheManager] Clear failed:", error);
246
- this.invalidate();
247
- }
248
- }
249
- /**
250
- * Get current items from cache
251
- *
252
- * @returns Current items array or empty array if no data
253
- */
254
- getItemsFromCache() {
255
- const data = this.config.queryClient.getQueryData(this.config.queryKey);
256
- return this.getItems(data);
257
- }
258
- /**
259
- * Get full data from cache
260
- *
261
- * @returns Current full data or undefined if no data
262
- */
263
- getDataFromCache() {
264
- return this.config.queryClient.getQueryData(this.config.queryKey);
265
- }
266
- /**
267
- * Invalidate query to trigger refetch
268
- */
269
- invalidate() {
270
- this.config.queryClient.invalidateQueries({ queryKey: this.config.queryKey });
271
- }
272
- /**
273
- * Get handlers for use with mutations
274
- *
275
- * @returns Object with onAdd, onUpdate, onDelete handlers
276
- */
277
- createHandlers() {
278
- return {
279
- onAdd: (newItem, position) => this.add(newItem, position),
280
- onUpdate: (updatedItem, matcher) => this.update(updatedItem, matcher),
281
- onDelete: (itemOrId, matcher) => this.delete(itemOrId, matcher)
282
- };
283
- }
284
- };
285
-
286
- // src/utils/cacheRegistry.ts
287
- var cacheConfigRegistry = /* @__PURE__ */ new Map();
288
- var cacheRegistry = /* @__PURE__ */ new Map();
289
- var defaultConfigs = {
290
- paginated: {
291
- itemsPath: "data.content",
292
- pagination: {
293
- totalElementsPath: "data.page.totalElements",
294
- totalPagesPath: "data.page.totalPages",
295
- currentPagePath: "data.page.number",
296
- pageSizePath: "data.page.size"
297
- }
298
- },
299
- nonPaginated: {
300
- itemsPath: "data"
301
- }
302
- };
303
- Object.entries(defaultConfigs).forEach(([key, config]) => {
304
- cacheConfigRegistry.set(key, config);
305
- });
306
- var getOrCreateCacheManager = (queryKey, queryClient, options) => {
307
- const key = JSON.stringify(queryKey);
308
- if (cacheRegistry.has(key)) {
309
- return cacheRegistry.get(key);
310
- }
311
- const cacheType = options?.cacheType;
312
- const config = options?.cacheConfig || cacheType && cacheConfigRegistry.get(cacheType);
313
- if (!config) {
314
- throw new Error(
315
- `[CacheManager] Unknown cacheType "${cacheType}". Available: ${[
316
- ...cacheConfigRegistry.keys()
317
- ].join(", ")}`
318
- );
319
- }
320
- const manager = new QueryCacheManager({
321
- queryClient,
322
- queryKey,
323
- ...config
324
- });
325
- cacheRegistry.set(key, manager);
326
- return manager;
327
- };
328
- var resetCacheManager = (queryKey) => {
329
- cacheRegistry.delete(JSON.stringify(queryKey));
330
- };
331
- var resetAllCacheManagers = () => {
332
- cacheRegistry.clear();
333
- };
334
-
335
- // src/hooks/useCustomQuery.ts
336
- function useCustomQuery(options) {
337
- const queryClient = useQueryClient();
338
- const { queryKey, cacheType, cacheConfig, ...rest } = options;
339
- const queryResult = useQuery({
340
- queryKey,
341
- ...rest
342
- });
343
- let cache;
344
- if (cacheType) {
345
- cache = getOrCreateCacheManager(queryKey, queryClient, {
346
- cacheType,
347
- cacheConfig
348
- });
349
- }
350
- return {
351
- ...queryResult,
352
- cache
353
- };
354
- }
355
- var NotificationContext = createContext(
356
- void 0
357
- );
358
-
359
- // src/hooks/useNotificationContext.ts
360
- var useNotificationContext = () => {
361
- const context = useContext(NotificationContext);
362
- if (!context) {
363
- throw new Error("useNotificationContext must be used within an NotificationProvider");
364
- }
365
- return context;
366
- };
367
-
368
- // src/hooks/useCustomMutation.ts
369
- var useCustomMutation = (options) => {
370
- const {
371
- onError,
372
- onSuccess,
373
- notify = false,
374
- notifyError = false,
375
- notifySuccess = false,
376
- errorMessage = "Operation failed!",
377
- successMessage = "Operation successfull!",
378
- notificationConfig = { duration: 2 },
379
- getErrorMessage,
380
- ...rest
381
- } = options;
382
- const { showSuccess, showError } = useNotificationContext();
383
- return useMutation({
384
- ...rest,
385
- onSuccess: (data, variables, context, mutation) => {
386
- if (notify || notifySuccess) {
387
- showSuccess(successMessage, notificationConfig);
388
- }
389
- onSuccess?.(data, variables, context, mutation);
390
- },
391
- onError: (apiError, variables, context, mutation) => {
392
- const message = getErrorMessage ? getErrorMessage(apiError) : apiError?.error?.message ?? errorMessage;
393
- if (notify || notifyError) {
394
- showError(message, notificationConfig);
395
- }
396
- onError?.(apiError, variables, context, mutation);
397
- }
398
- });
399
- };
400
- var useQueryCacheManagers = (configs) => {
401
- const queryClient = useQueryClient();
402
- const managers = {};
403
- Object.entries(configs).forEach(([key, config]) => {
404
- const options = config.options ?? {};
405
- managers[key] = new QueryCacheManager({
406
- queryKey: config.queryKey,
407
- queryClient,
408
- ...options,
409
- itemsPath: options.itemsPath ?? ""
410
- });
411
- });
412
- return managers;
413
- };
414
-
415
- export { QueryCacheManager, resetAllCacheManagers, resetCacheManager, useCustomMutation, useCustomQuery, useNotificationContext, useQueryCacheManagers };