tanstack-cacher 1.2.0 → 1.3.1

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
  }
@@ -41,20 +42,27 @@ declare class QueryCacheManager<TData, TItem> {
41
42
  createHandlers(): CacheHandlers<TItem>;
42
43
  }
43
44
 
44
- type CacheOptions<TData = any, TItem = any> = Omit<CacheConfig<TData, TItem>, 'queryClient' | 'queryKey'>;
45
+ type CacheManagerConstructor = new <TData, TItem>(config: CacheConfig<TData, TItem>) => QueryCacheManager<TData, TItem>;
46
+ declare class CacheManagerFactory {
47
+ private managerClass;
48
+ setManagerClass(managerClass: CacheManagerConstructor): void;
49
+ resetManagerClass(): void;
50
+ create<TData, TItem>(config: CacheConfig<TData, TItem>): QueryCacheManager<TData, TItem>;
51
+ getManagerClass(): CacheManagerConstructor;
52
+ }
53
+ declare const cacheManagerFactory: CacheManagerFactory;
45
54
 
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> & {
55
+ type MutationTypes = 'add' | 'invalidate' | 'remove' | 'update';
56
+ interface CacheActions<TData, TItem = unknown> extends Omit<CacheConfig<TData, TItem>, 'queryClient'> {
57
+ type: MutationTypes;
58
+ }
59
+ type CustomMutationOptions<TData, TError, TVariables> = UseMutationOptions<TData, TError, TVariables> & {
53
60
  notify?: boolean;
54
61
  notifyError?: boolean;
55
62
  errorMessage?: string;
56
63
  notifySuccess?: boolean;
57
64
  successMessage?: string;
65
+ cacheActions?: CacheActions<TData>[] | CacheActions<TData>;
58
66
  notificationConfig?: NotificationOptions;
59
67
  getErrorMessage?: (error: TError) => string;
60
68
  };
@@ -63,11 +71,7 @@ interface NotificationOptions {
63
71
  [key: string]: any;
64
72
  }
65
73
 
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>;
74
+ declare const useCustomMutation: <TData, TError, TVariables = void>(options: CustomMutationOptions<TData, TError, TVariables>) => _tanstack_react_query.UseMutationResult<TData, TError, TVariables, unknown>;
71
75
 
72
76
  declare const useQueryCacheManagers: <T extends Record<string, QueryCacheManager<any, any>>>(configs: { [K in keyof T]: {
73
77
  queryKey: readonly unknown[];
@@ -81,7 +85,9 @@ interface NotificationContextType {
81
85
 
82
86
  declare const useNotificationContext: () => NotificationContextType;
83
87
 
88
+ type CacheOptions<TData = any, TItem = any> = Omit<CacheConfig<TData, TItem>, 'queryClient' | 'queryKey'>;
89
+
84
90
  declare const resetCacheManager: (queryKey: QueryKey) => void;
85
91
  declare const resetAllCacheManagers: () => void;
86
92
 
87
- export { type CacheConfig, type CacheHandlers, type CacheOptions, type CustomMutationOptions, type CustomQueryOptions, type InsertPosition, type PaginationConfig, QueryCacheManager, resetAllCacheManagers, resetCacheManager, useCustomMutation, useCustomQuery, useNotificationContext, useQueryCacheManagers };
93
+ 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
  /**
@@ -284,75 +317,25 @@ var QueryCacheManager = class {
284
317
  }
285
318
  };
286
319
 
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"
320
+ // src/managers/QueryCacheManager/CacheManagerFactory.ts
321
+ var CacheManagerFactory = class {
322
+ constructor() {
323
+ this.managerClass = QueryCacheManager;
302
324
  }
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);
325
+ setManagerClass(managerClass) {
326
+ this.managerClass = managerClass;
311
327
  }
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
- );
328
+ resetManagerClass() {
329
+ this.managerClass = QueryCacheManager;
320
330
  }
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
- });
331
+ create(config) {
332
+ return new this.managerClass(config);
350
333
  }
351
- return {
352
- ...queryResult,
353
- cache
354
- };
355
- }
334
+ getManagerClass() {
335
+ return this.managerClass;
336
+ }
337
+ };
338
+ var cacheManagerFactory = new CacheManagerFactory();
356
339
  var NotificationContext = react.createContext(
357
340
  void 0
358
341
  );
@@ -371,6 +354,7 @@ var useCustomMutation = (options) => {
371
354
  const {
372
355
  onError,
373
356
  onSuccess,
357
+ cacheActions,
374
358
  notify = false,
375
359
  notifyError = false,
376
360
  notifySuccess = false,
@@ -380,21 +364,32 @@ var useCustomMutation = (options) => {
380
364
  getErrorMessage,
381
365
  ...rest
382
366
  } = options;
383
- const { showSuccess, showError } = useNotificationContext();
367
+ const notificationContext = useNotificationContext();
384
368
  return reactQuery.useMutation({
385
369
  ...rest,
386
- onSuccess: (data, variables, context, mutation) => {
370
+ onSuccess: (data, variables, mResult, context) => {
387
371
  if (notify || notifySuccess) {
388
- showSuccess(successMessage, notificationConfig);
372
+ notificationContext?.showSuccess?.(successMessage, notificationConfig);
373
+ }
374
+ onSuccess?.(data, variables, mResult, context);
375
+ if (Array.isArray(cacheActions)) {
376
+ cacheActions.forEach((item) => {
377
+ const { type, ...rest2 } = item;
378
+ const manager = cacheManagerFactory.create(rest2);
379
+ runCacheManagers(type, manager, data);
380
+ });
381
+ } else if (cacheActions) {
382
+ const { type, ...rest2 } = cacheActions;
383
+ const manager = cacheManagerFactory.create(rest2);
384
+ runCacheManagers(type, manager, data);
389
385
  }
390
- onSuccess?.(data, variables, context, mutation);
391
386
  },
392
- onError: (apiError, variables, context, mutation) => {
387
+ onError: (apiError, variables, mResult, context) => {
393
388
  const message = getErrorMessage ? getErrorMessage(apiError) : apiError?.error?.message ?? errorMessage;
394
389
  if (notify || notifyError) {
395
- showError(message, notificationConfig);
390
+ notificationContext?.showError(message, notificationConfig);
396
391
  }
397
- onError?.(apiError, variables, context, mutation);
392
+ onError?.(apiError, variables, mResult, context);
398
393
  }
399
394
  });
400
395
  };
@@ -413,11 +408,38 @@ var useQueryCacheManagers = (configs) => {
413
408
  return managers;
414
409
  };
415
410
 
411
+ // src/utils/cacheRegistry.ts
412
+ var cacheConfigRegistry = /* @__PURE__ */ new Map();
413
+ var cacheRegistry = /* @__PURE__ */ new Map();
414
+ var defaultConfigs = {
415
+ paginated: {
416
+ itemsPath: "data.content",
417
+ pagination: {
418
+ totalElementsPath: "data.page.totalElements",
419
+ totalPagesPath: "data.page.totalPages",
420
+ currentPagePath: "data.page.number",
421
+ pageSizePath: "data.page.size"
422
+ }
423
+ },
424
+ nonPaginated: {
425
+ itemsPath: "data"
426
+ }
427
+ };
428
+ Object.entries(defaultConfigs).forEach(([key, config]) => {
429
+ cacheConfigRegistry.set(key, config);
430
+ });
431
+ var resetCacheManager = (queryKey) => {
432
+ cacheRegistry.delete(JSON.stringify(queryKey));
433
+ };
434
+ var resetAllCacheManagers = () => {
435
+ cacheRegistry.clear();
436
+ };
437
+
416
438
  exports.QueryCacheManager = QueryCacheManager;
439
+ exports.cacheManagerFactory = cacheManagerFactory;
417
440
  exports.resetAllCacheManagers = resetAllCacheManagers;
418
441
  exports.resetCacheManager = resetCacheManager;
419
442
  exports.useCustomMutation = useCustomMutation;
420
- exports.useCustomQuery = useCustomQuery;
421
443
  exports.useNotificationContext = useNotificationContext;
422
444
  exports.useQueryCacheManagers = useQueryCacheManagers;
423
445
  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.0",
3
+ "version": "1.3.1",
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": {
@@ -19,7 +19,7 @@
19
19
  "web": "http://github.com/hacagahasanli"
20
20
  }
21
21
  ],
22
- "homepage": "https://github.com/hacagahasanli/tanstack-cacher",
22
+ "homepage": "https://github.com/hacagahasanli/tanstack-cacher#readme",
23
23
  "keywords": [
24
24
  "react",
25
25
  "react-query",
@@ -28,26 +28,30 @@
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",
52
+ "build": "tsup && npm run copy-dist",
53
+ "copy-dist": "cp dist/index.js index.js && cp dist/index.d.ts index.d.ts",
54
+ "locale-build": "tsup",
51
55
  "dev": "tsup --watch",
52
56
  "lint": "eslint src --ext .ts,.tsx",
53
57
  "lint:fix": "eslint src --ext .ts,.tsx --fix",
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 };