@qiaopeng/tanstack-query-plus 0.2.7 → 0.2.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"consistency.d.ts","sourceRoot":"","sources":["../../src/utils/consistency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAA2O;AACxT,MAAM,MAAM,gBAAgB,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAA;CAAE,CAAC;AAC1R,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASnG;AACD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAe7F;AACD,eAAO,MAAM,mBAAmB,EAAE,gBAA4J,CAAC;AAC/L,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAqBvK;AAED,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,QAAQ,EACtB,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAC7B,KAAK,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC,CA6CtD"}
1
+ {"version":3,"file":"consistency.d.ts","sourceRoot":"","sources":["../../src/utils/consistency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAiDvD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAgB5E;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnF,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;CAClD,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiBnG;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAe7F;AAED,eAAO,MAAM,mBAAmB,EAAE,gBAMjC,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,QAAQ,EACtB,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAC7B,IAAI,CAiDN;AAED,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,QAAQ,EACtB,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAC7B,KAAK,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC,CAkDtD"}
@@ -1,21 +1,65 @@
1
1
  import { listUpdater } from "./optimisticUtils.js";
2
- function ensureArray(key) { return Array.isArray(key) ? key : [key]; }
3
- function deepEqual(a, b) { if (a === b)
4
- return true; if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) {
5
- try {
6
- return JSON.stringify(a) === JSON.stringify(b);
2
+ function ensureArray(key) {
3
+ return Array.isArray(key) ? key : [key];
4
+ }
5
+ /**
6
+ * Robust deep equality check that handles object key order and common types
7
+ */
8
+ function deepEqual(a, b) {
9
+ if (a === b)
10
+ return true;
11
+ if (a && b && typeof a === "object" && typeof b === "object") {
12
+ if (Array.isArray(a)) {
13
+ if (!Array.isArray(b) || a.length !== b.length)
14
+ return false;
15
+ for (let i = 0; i < a.length; i++) {
16
+ if (!deepEqual(a[i], b[i]))
17
+ return false;
18
+ }
19
+ return true;
20
+ }
21
+ // Handle plain objects
22
+ const keysA = Object.keys(a);
23
+ const keysB = Object.keys(b);
24
+ if (keysA.length !== keysB.length)
25
+ return false;
26
+ // Check if all keys in A exist in B and values are deep equal
27
+ // Key order doesn't matter here because we look up by key
28
+ for (const key of keysA) {
29
+ if (!Object.prototype.hasOwnProperty.call(b, key))
30
+ return false;
31
+ if (!deepEqual(a[key], b[key]))
32
+ return false;
33
+ }
34
+ return true;
7
35
  }
8
- catch {
36
+ return false;
37
+ }
38
+ /**
39
+ * Loose equality check for IDs (string vs number)
40
+ */
41
+ function idsAreEqual(a, b) {
42
+ if (a === b)
43
+ return true;
44
+ if (a === undefined || a === null || b === undefined || b === null)
9
45
  return false;
10
- }
11
- } return false; }
12
- export function startsWithKeyPrefix(key, prefix) { const k = ensureArray(key); const p = ensureArray(prefix); if (p.length > k.length)
13
- return false; for (let i = 0; i < p.length; i++) {
14
- const a = k[i];
15
- const b = p[i];
16
- if (!(a === b || deepEqual(a, b)))
46
+ return String(a) === String(b);
47
+ }
48
+ export function startsWithKeyPrefix(key, prefix) {
49
+ const k = ensureArray(key);
50
+ const p = ensureArray(prefix);
51
+ if (p.length > k.length)
17
52
  return false;
18
- } return true; }
53
+ for (let i = 0; i < p.length; i++) {
54
+ const a = k[i];
55
+ const b = p[i];
56
+ // Check for equality using robust deepEqual
57
+ if (!deepEqual(a, b)) {
58
+ return false;
59
+ }
60
+ }
61
+ return true;
62
+ }
19
63
  export function defaultListSelector(data) {
20
64
  if (!data)
21
65
  return null;
@@ -24,9 +68,15 @@ export function defaultListSelector(data) {
24
68
  if (typeof data === "object") {
25
69
  const obj = data;
26
70
  if (Array.isArray(obj.items))
27
- return { items: obj.items, total: typeof obj.total === "number" ? obj.total : undefined };
71
+ return {
72
+ items: obj.items,
73
+ total: typeof obj.total === "number" ? obj.total : undefined
74
+ };
28
75
  if (Array.isArray(obj.Rows))
29
- return { items: obj.Rows, total: typeof obj.Total === "number" ? obj.Total : undefined };
76
+ return {
77
+ items: obj.Rows,
78
+ total: typeof obj.Total === "number" ? obj.Total : undefined
79
+ };
30
80
  }
31
81
  return null;
32
82
  }
@@ -48,7 +98,13 @@ export function defaultWriteBack(old, items, total) {
48
98
  }
49
99
  return { items, total };
50
100
  }
51
- export const DEFAULT_FAMILY_SYNC = { idField: "id", listSelector: defaultListSelector, writeBack: defaultWriteBack, maxKeys: 50, enableForOperations: ["update", "delete"] };
101
+ export const DEFAULT_FAMILY_SYNC = {
102
+ idField: "id",
103
+ listSelector: defaultListSelector,
104
+ writeBack: defaultWriteBack,
105
+ maxKeys: 50,
106
+ enableForOperations: ["update", "delete"]
107
+ };
52
108
  export function syncEntityAcrossFamily(queryClient, familyPrefix, cfg, operation, payload) {
53
109
  const enabledOps = cfg.enableForOperations ?? ["update", "delete"];
54
110
  const op = String(operation).toLowerCase();
@@ -67,10 +123,28 @@ export function syncEntityAcrossFamily(queryClient, familyPrefix, cfg, operation
67
123
  const idValue = payload?.[idField];
68
124
  let items = picked.items;
69
125
  if (op === "update" && idValue !== undefined) {
70
- items = listUpdater.update(items, { ...payload, id: idValue });
126
+ // Use custom listUpdater logic but ensure ID matching is robust if listUpdater uses strict equality
127
+ // Since listUpdater is imported, we should ideally modify it too or handle it here.
128
+ // For now, let's modify how we find the item index if we were doing it manually,
129
+ // but listUpdater is a black box here.
130
+ // Assuming listUpdater uses strict equality, we might have a problem if we pass '1' and item has 1.
131
+ // But listUpdater.update usually takes the whole object.
132
+ // To be safe, we should ensure the payload ID matches the type of the item ID in the list if possible.
133
+ // Or, we rely on listUpdater being robust.
134
+ // Let's look at listUpdater implementation later. For now, let's assume we need to fix the ID type in payload if possible.
135
+ // Actually, the safer way is to find the item using loose equality, get its strict ID, and use that in payload.
136
+ const existingItem = items.find(item => idsAreEqual(item[idField], idValue));
137
+ if (existingItem) {
138
+ const strictId = existingItem[idField];
139
+ items = listUpdater.update(items, { ...payload, [idField]: strictId });
140
+ }
71
141
  }
72
142
  else if (op === "delete" && idValue !== undefined) {
73
- items = listUpdater.remove(items, idValue);
143
+ const existingItem = items.find(item => idsAreEqual(item[idField], idValue));
144
+ if (existingItem) {
145
+ const strictId = existingItem[idField];
146
+ items = listUpdater.remove(items, strictId);
147
+ }
74
148
  }
75
149
  const total = typeof picked.total === "number" && op === "delete" ? Math.max(0, picked.total - 1) : picked.total;
76
150
  const next = (cfg.writeBack ?? defaultWriteBack)(old, items, total);
@@ -97,16 +171,20 @@ export function syncEntityAcrossFamilyOptimistic(queryClient, familyPrefix, cfg,
97
171
  let items = picked.items;
98
172
  let shouldUpdate = false;
99
173
  if (op === "update" && idValue !== undefined) {
100
- const exists = items.some(item => item[idField] === idValue);
101
- if (exists) {
102
- items = listUpdater.update(items, { ...payload, id: idValue });
174
+ // Robust ID check
175
+ const existingItem = items.find(item => idsAreEqual(item[idField], idValue));
176
+ if (existingItem) {
177
+ const strictId = existingItem[idField];
178
+ // Ensure payload uses the correct ID type from the store
179
+ items = listUpdater.update(items, { ...payload, [idField]: strictId });
103
180
  shouldUpdate = true;
104
181
  }
105
182
  }
106
183
  else if (op === "delete" && idValue !== undefined) {
107
- const exists = items.some(item => item[idField] === idValue);
108
- if (exists) {
109
- items = listUpdater.remove(items, idValue);
184
+ const existingItem = items.find(item => idsAreEqual(item[idField], idValue));
185
+ if (existingItem) {
186
+ const strictId = existingItem[idField];
187
+ items = listUpdater.remove(items, strictId);
110
188
  shouldUpdate = true;
111
189
  }
112
190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qiaopeng/tanstack-query-plus",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Enhanced TanStack Query toolkit: defaults, hooks, persistence, offline, utils",
5
5
  "author": "qiaopeng",
6
6
  "license": "MIT",