amplifyquery 1.0.20 → 1.0.21

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/service.js CHANGED
@@ -27,6 +27,37 @@ function itemKey(modelName, id) {
27
27
  function isItemKeyForModel(modelName, key) {
28
28
  return Array.isArray(key) && key[0] === modelName && key[1] === "item";
29
29
  }
30
+ function signatureForModelItem(item) {
31
+ const id = typeof (item === null || item === void 0 ? void 0 : item.id) === "string" ? item.id : "";
32
+ const updatedAt = typeof (item === null || item === void 0 ? void 0 : item.updatedAt) === "string" ? item.updatedAt : "";
33
+ const createdAt = typeof (item === null || item === void 0 ? void 0 : item.createdAt) === "string" ? item.createdAt : "";
34
+ return `${id}::${updatedAt || createdAt}`;
35
+ }
36
+ function areItemArraysEquivalentById(a, b) {
37
+ if (a === b)
38
+ return true;
39
+ if (!Array.isArray(a) || !Array.isArray(b))
40
+ return false;
41
+ if (a.length !== b.length)
42
+ return false;
43
+ const mapA = new Map();
44
+ for (const item of a) {
45
+ const sig = signatureForModelItem(item);
46
+ const [id, ts] = sig.split("::");
47
+ if (!id)
48
+ continue;
49
+ mapA.set(id, ts || "");
50
+ }
51
+ for (const item of b) {
52
+ const sig = signatureForModelItem(item);
53
+ const [id, ts] = sig.split("::");
54
+ if (!id)
55
+ return false;
56
+ if (mapA.get(id) !== (ts || ""))
57
+ return false;
58
+ }
59
+ return true;
60
+ }
30
61
  /**
31
62
  * Utility function to get owner value based on authentication mode
32
63
  * Sets owner value only in userPool auth mode, returns empty string for other auth modes
@@ -37,11 +68,22 @@ function isItemKeyForModel(modelName, key) {
37
68
  function getOwnerByAuthMode(authMode) {
38
69
  return __awaiter(this, void 0, void 0, function* () {
39
70
  let owner = "";
71
+ let ownerCandidates = [];
40
72
  // Set owner value only in userPool auth mode
41
73
  if (authMode === "userPool") {
42
74
  try {
43
75
  const { username, userId } = yield (0, auth_1.getCurrentUser)();
44
- owner = userId + "::" + username;
76
+ // Canonical "owner" in Amplify is typically the Cognito user sub / userId.
77
+ // Some legacy codepaths used `${userId}::${username}`. We keep it as a fallback
78
+ // candidate so list-by-owner can still find older records.
79
+ const canonicalOwner = userId;
80
+ // Avoid useless legacy candidates like `${userId}::${userId}` (can happen depending on username config).
81
+ const legacyOwner = username && userId && username !== userId ? `${userId}::${username}` : "";
82
+ owner = canonicalOwner;
83
+ ownerCandidates = [canonicalOwner].filter(Boolean);
84
+ if (legacyOwner && legacyOwner !== canonicalOwner) {
85
+ ownerCandidates.push(legacyOwner);
86
+ }
45
87
  }
46
88
  catch (error) {
47
89
  console.error("Error getting user authentication info:", error);
@@ -51,6 +93,7 @@ function getOwnerByAuthMode(authMode) {
51
93
  // Return with auth mode parameters
52
94
  return {
53
95
  owner,
96
+ ownerCandidates,
54
97
  authModeParams: { authMode },
55
98
  };
56
99
  });
@@ -66,13 +109,16 @@ function findRelatedQueryKeys(modelName, queryClient) {
66
109
  return queryClient
67
110
  .getQueryCache()
68
111
  .findAll({
69
- predicate: ({ queryKey }) => {
112
+ predicate: (query) => {
113
+ const { queryKey } = query;
70
114
  // Find all query keys for the model, but EXCLUDE single-item keys
71
115
  // Examples kept: [model], [model, 'filter', ...], [model, 'query', ...], [model, Relation, id, ...]
72
116
  // Excluded: [model, 'item', id]
73
117
  return (Array.isArray(queryKey) &&
74
118
  queryKey[0] === modelName &&
75
- !isItemKeyForModel(modelName, queryKey));
119
+ !isItemKeyForModel(modelName, queryKey) &&
120
+ // Also exclude internal singleton key(s) like ["User", "currentId"]
121
+ queryKey[1] !== "currentId");
76
122
  },
77
123
  })
78
124
  .map((query) => query.queryKey);
@@ -197,6 +243,14 @@ function rollbackCache(queryClient, previousDataMap) {
197
243
  queryClient.setQueryData(queryKey, previousData);
198
244
  });
199
245
  }
246
+ function isOwnerNotInSchemaError(err) {
247
+ const msg = typeof (err === null || err === void 0 ? void 0 : err.message) === "string"
248
+ ? err.message
249
+ : String(err);
250
+ // Typical GraphQL validation/input errors mention the field and that it isn't defined.
251
+ return (/owner/i.test(msg) &&
252
+ /(not defined|does not exist|unknown|not a valid|not allowed|defined for input)/i.test(msg));
253
+ }
200
254
  /**
201
255
  * Create model-specific Amplify service
202
256
  * @param modelName Model name
@@ -213,7 +267,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
213
267
  // Set authentication mode method
214
268
  setAuthMode: (authMode) => {
215
269
  currentAuthMode = authMode;
216
- console.log(`🔐 ${modelName} service auth mode changed: ${authMode}`);
270
+ (0, config_1.debugLog)(`🔐 ${modelName} service auth mode changed: ${authMode}`);
217
271
  },
218
272
  // Get current authentication mode
219
273
  getAuthMode: () => {
@@ -235,7 +289,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
235
289
  // Determine auth mode (use provided option if available)
236
290
  const authMode = (options === null || options === void 0 ? void 0 : options.authMode) || currentAuthMode;
237
291
  // Get owner and parameters based on auth mode
238
- const { authModeParams } = yield getOwnerByAuthMode(authMode);
292
+ const { owner, authModeParams } = yield getOwnerByAuthMode(authMode);
239
293
  const dataWithoutOwner = utils_1.Utils.removeOwnerField(data, "create");
240
294
  const cleanedData = {};
241
295
  Object.entries(dataWithoutOwner).forEach(([key, value]) => {
@@ -286,8 +340,8 @@ function createAmplifyService(modelName, defaultAuthMode) {
286
340
  });
287
341
  }
288
342
  }
289
- else if (queryKey.length < 3) {
290
- // Regular list query
343
+ else if (queryKey.length === 1) {
344
+ // Regular list query (e.g. ["User"]). Avoid internal keys like ["User","currentId"].
291
345
  const data = query_1.queryClient.getQueryData(queryKey);
292
346
  if (data) {
293
347
  previousDataMap.set(queryKey, data);
@@ -300,8 +354,22 @@ function createAmplifyService(modelName, defaultAuthMode) {
300
354
  });
301
355
  try {
302
356
  // Attempt API call - apply auth mode
303
- console.log(`🍬 ${modelName} creation attempt [Auth: ${authMode}]:`, newItem.id);
304
- const { data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(newItem, authModeParams);
357
+ (0, config_1.debugLog)(`🍬 ${modelName} creation attempt [Auth: ${authMode}]:`, newItem.id);
358
+ // Many schemas use an owner field for auth. Prefer adding owner when available,
359
+ // but retry without it if the model doesn't define it.
360
+ const createPayload = owner ? Object.assign(Object.assign({}, newItem), { owner }) : newItem;
361
+ let createdItem = null;
362
+ try {
363
+ ({ data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(createPayload, authModeParams));
364
+ }
365
+ catch (e) {
366
+ if (owner && isOwnerNotInSchemaError(e)) {
367
+ ({ data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(newItem, authModeParams));
368
+ }
369
+ else {
370
+ throw e;
371
+ }
372
+ }
305
373
  if (createdItem) {
306
374
  // Update cache on API success
307
375
  query_1.queryClient.setQueryData(singleItemQueryKey, createdItem);
@@ -322,7 +390,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
322
390
  queryKey: [modelName],
323
391
  refetchType: "active",
324
392
  });
325
- console.log(`🍬 ${modelName} creation successful:`, newItem.id);
393
+ (0, config_1.debugLog)(`🍬 ${modelName} creation successful:`, newItem.id);
326
394
  return createdItem;
327
395
  }
328
396
  // Keep optimistic update data even if no API response
@@ -351,7 +419,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
351
419
  // Determine auth mode (use provided option if available)
352
420
  const authMode = (options === null || options === void 0 ? void 0 : options.authMode) || currentAuthMode;
353
421
  // Get owner and parameters based on auth mode
354
- const { authModeParams } = yield getOwnerByAuthMode(authMode);
422
+ const { owner, authModeParams } = yield getOwnerByAuthMode(authMode);
355
423
  const preparedItems = dataList
356
424
  .map((data) => {
357
425
  if (!data)
@@ -378,7 +446,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
378
446
  }
379
447
  }
380
448
  }
381
- console.log(`🍬 ${modelName} batch creation attempt: ${preparedItems.length} items`);
449
+ (0, config_1.debugLog)(`🍬 ${modelName} batch creation attempt: ${preparedItems.length} items`);
382
450
  // Batch optimistic update - with relation filtering
383
451
  relatedQueryKeys.forEach((queryKey) => {
384
452
  // Check if this query key is a relational query (e.g., ["Mission", "Daily", "daily-id", ...])
@@ -403,8 +471,8 @@ function createAmplifyService(modelName, defaultAuthMode) {
403
471
  return oldItems; // No change if no items match relation ID
404
472
  });
405
473
  }
406
- else if (queryKey.length < 3) {
407
- // Regular list query - add all items
474
+ else if (queryKey.length === 1) {
475
+ // Regular list query - add all items (e.g. ["User"]). Avoid internal keys like ["User","currentId"].
408
476
  query_1.queryClient.setQueryData(queryKey, (oldData) => {
409
477
  const oldItems = Array.isArray(oldData) ? oldData : [];
410
478
  return [...oldItems, ...preparedItems];
@@ -422,7 +490,20 @@ function createAmplifyService(modelName, defaultAuthMode) {
422
490
  // Parallel API calls - apply auth mode
423
491
  const createPromises = preparedItems.map((newItem) => __awaiter(this, void 0, void 0, function* () {
424
492
  try {
425
- const { data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(newItem, authModeParams);
493
+ const createPayload = owner
494
+ ? Object.assign(Object.assign({}, newItem), { owner }) : newItem;
495
+ let createdItem = null;
496
+ try {
497
+ ({ data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(createPayload, authModeParams));
498
+ }
499
+ catch (e) {
500
+ if (owner && isOwnerNotInSchemaError(e)) {
501
+ ({ data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(newItem, authModeParams));
502
+ }
503
+ else {
504
+ throw e;
505
+ }
506
+ }
426
507
  // Update individual item cache on API success
427
508
  if (createdItem) {
428
509
  const itemId = createdItem === null || createdItem === void 0 ? void 0 : createdItem.id;
@@ -442,7 +523,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
442
523
  }
443
524
  }));
444
525
  const results = yield Promise.all(createPromises);
445
- console.log(`🍬 ${modelName} batch creation completed: ${results.length} items`);
526
+ (0, config_1.debugLog)(`🍬 ${modelName} batch creation completed: ${results.length} items`);
446
527
  // After creation, invalidate all related queries to ensure exact server data
447
528
  // This is important as it prevents side effects of optimistic updates by invalidating related queries
448
529
  for (const [field, value] of relationFields) {
@@ -549,8 +630,8 @@ function createAmplifyService(modelName, defaultAuthMode) {
549
630
  }
550
631
  }),
551
632
  // Batch get items
552
- list: (...args_1) => __awaiter(this, [...args_1], void 0, function* (options = { filter: undefined, forceRefresh: false }) {
553
- var _a, _b, _c, _d;
633
+ list: (...args_1) => __awaiter(this, [...args_1], void 0, function* (options = { filter: undefined, forceRefresh: false, throwOnError: false }) {
634
+ var _a, _b, _c, _d, _e, _f, _g;
554
635
  try {
555
636
  // Determine query key
556
637
  const queryKey = options.filter
@@ -563,27 +644,55 @@ function createAmplifyService(modelName, defaultAuthMode) {
563
644
  if (cachedItems &&
564
645
  cachedItems.length > 0 &&
565
646
  !(queryState === null || queryState === void 0 ? void 0 : queryState.isInvalidated)) {
566
- console.log(`🍬 ${modelName} list using cache`, queryKey);
647
+ (0, config_1.debugLog)(`🍬 ${modelName} list using cache`, queryKey);
567
648
  return cachedItems.filter((item) => item !== null);
568
649
  }
569
650
  }
570
651
  // Determine auth mode (use provided option if available)
571
652
  const authMode = (options === null || options === void 0 ? void 0 : options.authMode) || currentAuthMode;
572
653
  // Get owner and parameters based on auth mode
573
- const { owner, authModeParams } = yield getOwnerByAuthMode(authMode);
654
+ const { owner, ownerCandidates, authModeParams } = yield getOwnerByAuthMode(authMode);
574
655
  // Get owner-based query name from global config
575
656
  const ownerQueryName = (0, config_1.getOwnerQueryName)(modelName);
576
657
  // Try query call
577
658
  try {
578
- console.log(`🍬 ${modelName} list API call`, queryKey, `by ${ownerQueryName}`, `[Auth: ${authMode}]`);
579
- // Debug: Check if model and query exist
659
+ (0, config_1.debugLog)(`🍬 ${modelName} list API call`, queryKey, `by ${ownerQueryName}`, `[Auth: ${authMode}]`);
660
+ if ((0, config_1.isDebugEnabled)()) {
661
+ // Debug: Check if model and query exist
662
+ const client = (0, client_1.getClient)();
663
+ (0, config_1.debugLog)(`🍬 Debug - client.models exists:`, !!client.models);
664
+ (0, config_1.debugLog)(`🍬 Debug - client.models[${modelName}] exists:`, !!client.models[modelName]);
665
+ (0, config_1.debugLog)(`🍬 Debug - client.models[${modelName}][${ownerQueryName}] exists:`, !!((_a = client.models[modelName]) === null || _a === void 0 ? void 0 : _a[ownerQueryName]));
666
+ (0, config_1.debugLog)(`🍬 Debug - Available methods for ${modelName}:`, Object.keys(client.models[modelName] || {}));
667
+ }
580
668
  const client = (0, client_1.getClient)();
581
- console.log(`🍬 Debug - client.models exists:`, !!client.models);
582
- console.log(`🍬 Debug - client.models[${modelName}] exists:`, !!client.models[modelName]);
583
- console.log(`🍬 Debug - client.models[${modelName}][${ownerQueryName}] exists:`, !!((_a = client.models[modelName]) === null || _a === void 0 ? void 0 : _a[ownerQueryName]));
584
- console.log(`🍬 Debug - Available methods for ${modelName}:`, Object.keys(client.models[modelName] || {}));
585
- // Execute owner query
586
- const { data: result } = yield (0, client_1.getClient)().models[modelName][ownerQueryName]({ owner, authMode }, authModeParams);
669
+ const model = (_b = client.models) === null || _b === void 0 ? void 0 : _b[modelName];
670
+ // Execute owner query (try canonical owner first, then legacy owner formats)
671
+ const ownersToTry = Array.isArray(ownerCandidates) && ownerCandidates.length > 0
672
+ ? ownerCandidates
673
+ : owner
674
+ ? [owner]
675
+ : [];
676
+ if (!(model === null || model === void 0 ? void 0 : model[ownerQueryName]) || ownersToTry.length === 0) {
677
+ throw new Error(`owner query not available or owner missing: ${modelName}.${ownerQueryName}`);
678
+ }
679
+ let result = null;
680
+ let usedOwner = null;
681
+ for (const candidateOwner of ownersToTry) {
682
+ (0, config_1.debugLog)(`🍬 ${modelName} list owner-query attempt`, `[${ownerQueryName}]`, { owner: candidateOwner, authMode });
683
+ // Generated secondary index query typically expects { owner } only.
684
+ const { data } = yield model[ownerQueryName]({ owner: candidateOwner }, authModeParams);
685
+ const items = ((data === null || data === void 0 ? void 0 : data.items) || (data === null || data === void 0 ? void 0 : data.data) || data || []).filter((item) => item !== null);
686
+ if (items.length > 0) {
687
+ result = data;
688
+ usedOwner = candidateOwner;
689
+ break;
690
+ }
691
+ // If empty, try next candidate (legacy owner format)
692
+ }
693
+ if (usedOwner && usedOwner !== ownersToTry[0]) {
694
+ (0, config_1.debugWarn)(`🍬 ${modelName} list: owner-query returned results only for legacy owner format`, { tried: ownersToTry, usedOwner });
695
+ }
587
696
  // Extract result data + filter null values
588
697
  const items = ((result === null || result === void 0 ? void 0 : result.items) || (result === null || result === void 0 ? void 0 : result.data) || result || []).filter((item) => item !== null);
589
698
  // Apply filter (if client-side filtering needed)
@@ -631,9 +740,11 @@ function createAmplifyService(modelName, defaultAuthMode) {
631
740
  }
632
741
  catch (error) {
633
742
  // Check if the error is because the owner query doesn't exist
634
- if (((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes("not found")) ||
635
- ((_c = error === null || error === void 0 ? void 0 : error.message) === null || _c === void 0 ? void 0 : _c.includes("is not a function")) ||
636
- ((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.includes("is undefined"))) {
743
+ if (((_c = error === null || error === void 0 ? void 0 : error.message) === null || _c === void 0 ? void 0 : _c.includes("not found")) ||
744
+ ((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.includes("is not a function")) ||
745
+ ((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.includes("is undefined")) ||
746
+ ((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.includes("owner query not available")) ||
747
+ ((_g = error === null || error === void 0 ? void 0 : error.message) === null || _g === void 0 ? void 0 : _g.includes("owner missing"))) {
637
748
  console.warn(`🍬 ${ownerQueryName} query not found. Trying default list query...`);
638
749
  // Try default list query if owner query not found
639
750
  const { data: result } = yield (0, client_1.getClient)().models[modelName].list({}, authModeParams);
@@ -688,7 +799,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
688
799
  }
689
800
  }
690
801
  catch (error) {
691
- console.log("🍬 error", error);
802
+ (0, config_1.debugLog)("🍬 error", error);
692
803
  console.error(`🍬 ${modelName} list error:`, error);
693
804
  // Invalidate list query cache on error
694
805
  const queryKey = [
@@ -697,6 +808,8 @@ function createAmplifyService(modelName, defaultAuthMode) {
697
808
  JSON.stringify(options.filter),
698
809
  ];
699
810
  query_1.queryClient.invalidateQueries({ queryKey });
811
+ if (options === null || options === void 0 ? void 0 : options.throwOnError)
812
+ throw error;
700
813
  return [];
701
814
  }
702
815
  }),
@@ -726,7 +839,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
726
839
  const previousDataMap = yield performOptimisticUpdate(query_1.queryClient, modelName, relatedQueryKeys, itemId, cleanedData);
727
840
  try {
728
841
  // Attempt API call - apply auth mode
729
- console.log(`🍬 ${modelName} update attempt [Auth: ${authMode}]:`, itemId);
842
+ (0, config_1.debugLog)(`🍬 ${modelName} update attempt [Auth: ${authMode}]:`, itemId);
730
843
  const { data: updatedItem } = yield (0, client_1.getClient)().models[modelName].update(cleanedData, authModeParams);
731
844
  if (updatedItem) {
732
845
  // Update cache on API success
@@ -736,7 +849,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
736
849
  queryKey: [modelName],
737
850
  refetchType: "active",
738
851
  });
739
- console.log(`🍬 ${modelName} update success:`, itemId);
852
+ (0, config_1.debugLog)(`🍬 ${modelName} update success:`, itemId);
740
853
  return updatedItem;
741
854
  }
742
855
  else {
@@ -794,9 +907,9 @@ function createAmplifyService(modelName, defaultAuthMode) {
794
907
  });
795
908
  try {
796
909
  // API call - apply auth mode
797
- console.log(`🍬 ${modelName} delete attempt [Auth: ${authMode}]:`, id);
910
+ (0, config_1.debugLog)(`🍬 ${modelName} delete attempt [Auth: ${authMode}]:`, id);
798
911
  yield (0, client_1.getClient)().models[modelName].delete({ id }, authModeParams);
799
- console.log(`🍬 ${modelName} delete success:`, id);
912
+ (0, config_1.debugLog)(`🍬 ${modelName} delete success:`, id);
800
913
  // On API success, invalidate all related queries to automatically refresh
801
914
  relatedQueryKeys.forEach((queryKey) => query_1.queryClient.invalidateQueries({
802
915
  queryKey,
@@ -863,7 +976,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
863
976
  });
864
977
  });
865
978
  try {
866
- console.log(`🍬 ${modelName} batch delete attempt [Auth: ${authMode}]: ${ids.length} items`);
979
+ (0, config_1.debugLog)(`🍬 ${modelName} batch delete attempt [Auth: ${authMode}]: ${ids.length} items`);
867
980
  // Parallel API calls - apply auth mode
868
981
  const deletePromises = ids.map((id) => __awaiter(this, void 0, void 0, function* () {
869
982
  try {
@@ -912,7 +1025,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
912
1025
  // Invalidate all related queries to force refresh (safety mechanism)
913
1026
  relatedQueryKeys.forEach((queryKey) => query_1.queryClient.invalidateQueries({ queryKey }));
914
1027
  ids.forEach((id) => query_1.queryClient.invalidateQueries({ queryKey: itemKey(modelName, id) }));
915
- console.log(`🍬 ${modelName} batch delete: ${results.success.length} items deleted, ${results.failed.length} items failed`);
1028
+ (0, config_1.debugLog)(`🍬 ${modelName} batch delete: ${results.success.length} items deleted, ${results.failed.length} items failed`);
916
1029
  return results;
917
1030
  }
918
1031
  catch (generalError) {
@@ -959,11 +1072,11 @@ function createAmplifyService(modelName, defaultAuthMode) {
959
1072
  try {
960
1073
  if (existingItem) {
961
1074
  // Use update logic if item exists - apply auth mode
962
- console.log(`🍬 ${modelName} upsert(update) attempt [Auth: ${authMode}]:`, data.id);
1075
+ (0, config_1.debugLog)(`🍬 ${modelName} upsert(update) attempt [Auth: ${authMode}]:`, data.id);
963
1076
  const { data: updatedItem } = yield (0, client_1.getClient)().models[modelName].update(cleanedData, authModeParams);
964
1077
  if (updatedItem) {
965
1078
  handleCacheUpdateOnSuccess(query_1.queryClient, modelName, relatedQueryKeys, data.id, updatedItem);
966
- console.log(`🍬 ${modelName} upsert(update) success:`, data.id);
1079
+ (0, config_1.debugLog)(`🍬 ${modelName} upsert(update) success:`, data.id);
967
1080
  return updatedItem;
968
1081
  }
969
1082
  else {
@@ -975,11 +1088,11 @@ function createAmplifyService(modelName, defaultAuthMode) {
975
1088
  }
976
1089
  else {
977
1090
  // Use create logic if item doesn't exist - apply auth mode
978
- console.log(`🍬 ${modelName} upsert(create) attempt [Auth: ${authMode}]:`, data.id);
1091
+ (0, config_1.debugLog)(`🍬 ${modelName} upsert(create) attempt [Auth: ${authMode}]:`, data.id);
979
1092
  const { data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(cleanedData, authModeParams);
980
1093
  if (createdItem) {
981
1094
  handleCacheUpdateOnSuccess(query_1.queryClient, modelName, relatedQueryKeys, data.id, createdItem);
982
- console.log(`🍬 ${modelName} upsert(create) success:`, data.id);
1095
+ (0, config_1.debugLog)(`🍬 ${modelName} upsert(create) success:`, data.id);
983
1096
  return createdItem;
984
1097
  }
985
1098
  else {
@@ -1003,7 +1116,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1003
1116
  }
1004
1117
  }),
1005
1118
  // Index-based query (added method)
1006
- customList: (queryName_1, args_1, ...args_2) => __awaiter(this, [queryName_1, args_1, ...args_2], void 0, function* (queryName, args, options = { forceRefresh: false }) {
1119
+ customList: (queryName_1, args_1, ...args_2) => __awaiter(this, [queryName_1, args_1, ...args_2], void 0, function* (queryName, args, options = { forceRefresh: false, throwOnError: false }) {
1007
1120
  var _a;
1008
1121
  try {
1009
1122
  // Determine auth mode (use provided options if available)
@@ -1043,11 +1156,11 @@ function createAmplifyService(modelName, defaultAuthMode) {
1043
1156
  if (!options.forceRefresh) {
1044
1157
  const cachedItems = query_1.queryClient.getQueryData(queryKey);
1045
1158
  if (cachedItems && cachedItems.length > 0) {
1046
- console.log(`🍬 ${modelName} ${queryName} using cache`);
1159
+ (0, config_1.debugLog)(`🍬 ${modelName} ${queryName} using cache`);
1047
1160
  return cachedItems.filter((item) => item !== null);
1048
1161
  }
1049
1162
  }
1050
- console.log(`🍬 ${modelName} customList call [Auth: ${authMode}]:`, queryName, enhancedArgs);
1163
+ (0, config_1.debugLog)(`🍬 ${modelName} customList call [Auth: ${authMode}]:`, queryName, enhancedArgs);
1051
1164
  // Check if index query method exists
1052
1165
  if (!((_a = (0, client_1.getClient)().models[modelName]) === null || _a === void 0 ? void 0 : _a[queryName])) {
1053
1166
  throw new Error(`🍬 Query ${queryName} does not exist.`);
@@ -1056,7 +1169,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1056
1169
  const { data: result } = yield (0, client_1.getClient)().models[modelName][queryName](enhancedArgs, authModeParams);
1057
1170
  // Extract result data
1058
1171
  const items = (result === null || result === void 0 ? void 0 : result.items) || (result === null || result === void 0 ? void 0 : result.data) || result || [];
1059
- console.log(`🍬 ${modelName} ${queryName} result:`, items.length, "items");
1172
+ (0, config_1.debugLog)(`🍬 ${modelName} ${queryName} result:`, items.length, "items");
1060
1173
  // Filter null values
1061
1174
  const filteredItems = items.filter((item) => item !== null);
1062
1175
  // Update cache
@@ -1104,6 +1217,8 @@ function createAmplifyService(modelName, defaultAuthMode) {
1104
1217
  query_1.queryClient.invalidateQueries({ queryKey });
1105
1218
  }
1106
1219
  }
1220
+ if (options === null || options === void 0 ? void 0 : options.throwOnError)
1221
+ throw error;
1107
1222
  return [];
1108
1223
  }
1109
1224
  }),
@@ -1151,24 +1266,37 @@ function createAmplifyService(modelName, defaultAuthMode) {
1151
1266
  ]);
1152
1267
  // Determine query function
1153
1268
  const queryFn = (0, react_1.useCallback)((context) => __awaiter(this, void 0, void 0, function* () {
1154
- var _a;
1269
+ var _a, _b, _c;
1270
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook queryFn called`, {
1271
+ queryKey,
1272
+ isRefetch: (_a = context.meta) === null || _a === void 0 ? void 0 : _a.refetch,
1273
+ customList: (_b = options === null || options === void 0 ? void 0 : options.customList) === null || _b === void 0 ? void 0 : _b.queryName,
1274
+ });
1155
1275
  if (options === null || options === void 0 ? void 0 : options.customList) {
1156
- console.log(`🍬 ${modelName} useHook customList call:`, options.customList.queryName, options.customList.args, options.customList.forceRefresh);
1157
- return service.customList(options.customList.queryName, options.customList.args, { forceRefresh: options.customList.forceRefresh });
1276
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook customList call:`, options.customList.queryName, options.customList.args, options.customList.forceRefresh);
1277
+ const result = yield service.customList(options.customList.queryName, options.customList.args, { forceRefresh: options.customList.forceRefresh });
1278
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook customList result:`, (result === null || result === void 0 ? void 0 : result.length) || 0, "items");
1279
+ return result;
1158
1280
  }
1159
- if ((_a = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _a === void 0 ? void 0 : _a.filter) {
1160
- return service.list({
1281
+ if ((_c = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _c === void 0 ? void 0 : _c.filter) {
1282
+ const result = yield service.list({
1161
1283
  filter: options.initialFetchOptions.filter,
1162
1284
  forceRefresh: true,
1163
1285
  });
1286
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook list (filtered) result:`, (result === null || result === void 0 ? void 0 : result.length) || 0, "items");
1287
+ return result;
1164
1288
  }
1165
- return service.list();
1289
+ const result = yield service.list();
1290
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook list result:`, (result === null || result === void 0 ? void 0 : result.length) || 0, "items");
1291
+ return result;
1166
1292
  }), [
1167
1293
  (_d = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _d === void 0 ? void 0 : _d.filter,
1168
1294
  (_e = options === null || options === void 0 ? void 0 : options.customList) === null || _e === void 0 ? void 0 : _e.queryName,
1169
1295
  (_f = options === null || options === void 0 ? void 0 : options.customList) === null || _f === void 0 ? void 0 : _f.args,
1170
1296
  (_g = options === null || options === void 0 ? void 0 : options.customList) === null || _g === void 0 ? void 0 : _g.forceRefresh,
1171
1297
  service,
1298
+ modelName,
1299
+ queryKey,
1172
1300
  ]);
1173
1301
  const realtimeEnabled = ((_h = options === null || options === void 0 ? void 0 : options.realtime) === null || _h === void 0 ? void 0 : _h.enabled) === true;
1174
1302
  const realtimeEvents = (_k = (_j = options === null || options === void 0 ? void 0 : options.realtime) === null || _j === void 0 ? void 0 : _j.events) !== null && _k !== void 0 ? _k : ["create", "update", "delete"];
@@ -1177,12 +1305,39 @@ function createAmplifyService(modelName, defaultAuthMode) {
1177
1305
  queryFn,
1178
1306
  enabled: ((_l = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _l === void 0 ? void 0 : _l.fetch) !== false && !realtimeEnabled,
1179
1307
  staleTime: 1000 * 30, // Keep fresh for 30 seconds (refresh more frequently)
1308
+ gcTime: 1000 * 60 * 5, // Keep in cache for 5 minutes (prevents data from being garbage collected)
1180
1309
  refetchOnMount: true, // Refetch on component mount
1181
1310
  refetchOnWindowFocus: false, // Don't auto-refetch on window focus
1182
1311
  refetchOnReconnect: true, // Refetch on network reconnect
1312
+ // Keep previous data during refetch to prevent UI flicker
1313
+ // This ensures data persists during refetch operations
1314
+ placeholderData: (previousData) => {
1315
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook placeholderData called - previous data:`, (previousData === null || previousData === void 0 ? void 0 : previousData.length) || 0, "items");
1316
+ return previousData;
1317
+ },
1183
1318
  };
1184
1319
  const [isSynced, setIsSynced] = (0, react_1.useState)(undefined);
1185
1320
  const { data: items = [], isLoading, error, refetch, } = (0, react_query_1.useQuery)(queryOptions);
1321
+ // Log data changes (debug only) - avoid spamming when nothing changed
1322
+ const lastDebugStateRef = (0, react_1.useRef)(null);
1323
+ (0, react_1.useEffect)(() => {
1324
+ if (!(0, config_1.isDebugEnabled)())
1325
+ return;
1326
+ const nextState = {
1327
+ itemsCount: (items === null || items === void 0 ? void 0 : items.length) || 0,
1328
+ isLoading: Boolean(isLoading),
1329
+ hasError: Boolean(error),
1330
+ };
1331
+ const prev = lastDebugStateRef.current;
1332
+ if (prev &&
1333
+ prev.itemsCount === nextState.itemsCount &&
1334
+ prev.isLoading === nextState.isLoading &&
1335
+ prev.hasError === nextState.hasError) {
1336
+ return;
1337
+ }
1338
+ lastDebugStateRef.current = nextState;
1339
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook data changed:`, Object.assign(Object.assign({}, nextState), { queryKey }));
1340
+ }, [items === null || items === void 0 ? void 0 : items.length, isLoading, error, modelName, queryKey]);
1186
1341
  (0, react_1.useEffect)(() => {
1187
1342
  var _a, _b, _c, _d, _e, _f;
1188
1343
  if (!realtimeEnabled)
@@ -1244,6 +1399,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1244
1399
  observeOptions.filter = options.initialFetchOptions.filter;
1245
1400
  }
1246
1401
  let isMounted = true;
1402
+ let lastSynced = undefined;
1247
1403
  const subscription = model.observeQuery(observeOptions).subscribe({
1248
1404
  next: ({ items: nextItems, isSynced: synced }) => {
1249
1405
  if (!isMounted)
@@ -1252,23 +1408,21 @@ function createAmplifyService(modelName, defaultAuthMode) {
1252
1408
  ? nextItems.filter(Boolean)
1253
1409
  : [];
1254
1410
  const previousItems = hookQueryClient.getQueryData(queryKey) || [];
1255
- const previousIds = new Set(previousItems.map((item) => item === null || item === void 0 ? void 0 : item.id).filter(Boolean));
1256
- const nextIds = new Set(safeItems.map((item) => item === null || item === void 0 ? void 0 : item.id).filter(Boolean));
1257
- previousIds.forEach((id) => {
1258
- if (!nextIds.has(id)) {
1259
- hookQueryClient.removeQueries({
1260
- queryKey: itemKey(modelName, id),
1261
- exact: true,
1262
- });
1263
- }
1264
- });
1411
+ const nextSynced = Boolean(synced);
1412
+ if (lastSynced !== nextSynced) {
1413
+ lastSynced = nextSynced;
1414
+ setIsSynced(nextSynced);
1415
+ }
1416
+ // Avoid cache thrash: observeQuery can emit repeatedly even when data didn't change.
1417
+ if (areItemArraysEquivalentById(previousItems, safeItems)) {
1418
+ return;
1419
+ }
1265
1420
  hookQueryClient.setQueryData(queryKey, safeItems);
1266
1421
  safeItems.forEach((item) => {
1267
1422
  if (item === null || item === void 0 ? void 0 : item.id) {
1268
1423
  hookQueryClient.setQueryData(itemKey(modelName, item.id), item);
1269
1424
  }
1270
1425
  });
1271
- setIsSynced(Boolean(synced));
1272
1426
  },
1273
1427
  error: (err) => {
1274
1428
  console.error(`🍬 ${modelName} useHook realtime subscribe error:`, err);
@@ -1372,32 +1526,57 @@ function createAmplifyService(modelName, defaultAuthMode) {
1372
1526
  }), [service, refetch] // refetch dependency added
1373
1527
  );
1374
1528
  const refresh = (0, react_1.useCallback)((refreshOptions) => __awaiter(this, void 0, void 0, function* () {
1375
- var _a, _b;
1376
- console.log(`🍬 ${modelName} useHook refresh called`, queryKey);
1377
- // IMPORTANT:
1378
- // TanStack Query's refetch() re-runs queryFn, but our service.list/customList can short-circuit
1379
- // and return cached data unless forceRefresh is true.
1380
- // refresh() must guarantee a network fetch to reflect server-side updates.
1529
+ var _a;
1530
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook refresh called`, queryKey);
1531
+ // IMPORTANT: refresh must always fetch from server (no cache).
1532
+ // We do this by calling service.list/customList with forceRefresh: true.
1533
+ const currentData = hookQueryClient.getQueryData(queryKey);
1534
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook refresh - current data before server refresh:`, (currentData === null || currentData === void 0 ? void 0 : currentData.length) || 0, "items");
1381
1535
  try {
1536
+ // If filter is provided and different from current filter, we'd need a new query key.
1537
+ // For now, we ignore refreshOptions.filter and refresh the current hook query only.
1538
+ if (refreshOptions === null || refreshOptions === void 0 ? void 0 : refreshOptions.filter) {
1539
+ console.warn(`🍬 ${modelName} useHook refresh: refreshOptions.filter is currently ignored (queryKey is fixed per hook instance).`);
1540
+ }
1541
+ let newData = [];
1382
1542
  if (options === null || options === void 0 ? void 0 : options.customList) {
1383
- return yield service.customList(options.customList.queryName, options.customList.args, { forceRefresh: true });
1543
+ newData = yield service.customList(options.customList.queryName, options.customList.args, { forceRefresh: true, throwOnError: true });
1544
+ }
1545
+ else if ((_a = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _a === void 0 ? void 0 : _a.filter) {
1546
+ newData = yield service.list({
1547
+ filter: options.initialFetchOptions.filter,
1548
+ forceRefresh: true,
1549
+ throwOnError: true,
1550
+ });
1384
1551
  }
1385
- const mergedFilter = (_a = refreshOptions === null || refreshOptions === void 0 ? void 0 : refreshOptions.filter) !== null && _a !== void 0 ? _a : (_b = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _b === void 0 ? void 0 : _b.filter;
1386
- if (mergedFilter) {
1387
- return yield service.list({
1388
- filter: mergedFilter,
1552
+ else {
1553
+ newData = yield service.list({
1389
1554
  forceRefresh: true,
1555
+ throwOnError: true,
1390
1556
  });
1391
1557
  }
1392
- return yield service.list({ forceRefresh: true });
1558
+ (0, config_1.debugLog)(`🍬 ${modelName} useHook refresh - server refresh result:`, (newData === null || newData === void 0 ? void 0 : newData.length) || 0, "items");
1559
+ // Keep hook cache in sync with hook's queryKey.
1560
+ hookQueryClient.setQueryData(queryKey, newData);
1561
+ return newData || [];
1393
1562
  }
1394
1563
  catch (e) {
1395
- // Keep previous behavior of surfacing errors via refetch when desired
1396
- // while still logging consistently.
1397
1564
  console.error(`🍬 ${modelName} useHook refresh error:`, e);
1398
- throw e;
1565
+ // On error, restore previous data if available (prevents list flashing empty)
1566
+ if (currentData && currentData.length > 0) {
1567
+ hookQueryClient.setQueryData(queryKey, currentData);
1568
+ return currentData;
1569
+ }
1570
+ return [];
1399
1571
  }
1400
- }), [modelName, options === null || options === void 0 ? void 0 : options.customList, (_p = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _p === void 0 ? void 0 : _p.filter, queryKey, service]);
1572
+ }), [
1573
+ modelName,
1574
+ queryKey,
1575
+ hookQueryClient,
1576
+ service,
1577
+ options === null || options === void 0 ? void 0 : options.customList,
1578
+ (_p = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _p === void 0 ? void 0 : _p.filter,
1579
+ ]);
1401
1580
  const customListFn = (0, react_1.useCallback)((queryName, args, options) => __awaiter(this, void 0, void 0, function* () {
1402
1581
  try {
1403
1582
  const result = yield service.customList(queryName, args, options);
@@ -1425,7 +1604,10 @@ function createAmplifyService(modelName, defaultAuthMode) {
1425
1604
  useItemHook: (id, options) => {
1426
1605
  var _a, _b;
1427
1606
  const hookQueryClient = (0, react_query_1.useQueryClient)();
1428
- const singleItemQueryKey = itemKey(modelName, id);
1607
+ // Runtime safety: avoid non-string ids creating broken query keys like
1608
+ // ["User","item",[...]] which can cause cache thrash/flicker.
1609
+ const safeId = typeof id === "string" ? id : "";
1610
+ const singleItemQueryKey = itemKey(modelName, safeId);
1429
1611
  const realtimeEnabled = ((_a = options === null || options === void 0 ? void 0 : options.realtime) === null || _a === void 0 ? void 0 : _a.enabled) === true;
1430
1612
  // First check data from cache
1431
1613
  const rawCachedData = hookQueryClient.getQueryData(singleItemQueryKey);
@@ -1433,32 +1615,27 @@ function createAmplifyService(modelName, defaultAuthMode) {
1433
1615
  let cachedData;
1434
1616
  if (Array.isArray(rawCachedData)) {
1435
1617
  console.warn(`🍬 ${modelName} useItemHook: Cache contains array instead of single item. Finding matching item.`);
1436
- const matchingItem = rawCachedData.find((item) => (item === null || item === void 0 ? void 0 : item.id) === id);
1618
+ const matchingItem = rawCachedData.find((item) => (item === null || item === void 0 ? void 0 : item.id) === safeId);
1437
1619
  cachedData = matchingItem || undefined;
1438
- // 배열이 캐시되어 있으면 조용히 캐시를 제거 (무한 루프 방지)
1439
- setTimeout(() => {
1440
- hookQueryClient.removeQueries({
1441
- queryKey: singleItemQueryKey,
1442
- exact: true,
1443
- });
1444
- }, 0);
1620
+ // Normalize the cache in-place instead of deleting it (deleting causes UI flicker).
1621
+ hookQueryClient.setQueryData(singleItemQueryKey, matchingItem !== null && matchingItem !== void 0 ? matchingItem : null);
1445
1622
  }
1446
1623
  else if (rawCachedData && (rawCachedData === null || rawCachedData === void 0 ? void 0 : rawCachedData.id) === id) {
1447
1624
  cachedData = rawCachedData;
1448
1625
  }
1449
1626
  else if (rawCachedData) {
1450
- console.warn(`🍬 ${modelName} useItemHook: Cache ID mismatch! Requested: ${id}, Cached: ${rawCachedData === null || rawCachedData === void 0 ? void 0 : rawCachedData.id}. Ignoring cached data.`);
1627
+ console.warn(`🍬 ${modelName} useItemHook: Cache ID mismatch! Requested: ${safeId}, Cached: ${rawCachedData === null || rawCachedData === void 0 ? void 0 : rawCachedData.id}. Ignoring cached data.`);
1451
1628
  cachedData = undefined;
1452
1629
  }
1453
1630
  // Single item query
1454
1631
  const { data: item, isLoading, error, refetch, } = (0, react_query_1.useQuery)({
1455
1632
  queryKey: singleItemQueryKey,
1456
- queryFn: () => service.get(id),
1633
+ queryFn: () => service.get(safeId),
1457
1634
  initialData: cachedData, // Use cached data as initial value if available
1458
1635
  staleTime: 1000 * 60, // Keep data "fresh" for 1 minute
1459
1636
  refetchOnMount: cachedData ? false : true, // Only refetch if no cached data
1460
1637
  refetchOnWindowFocus: false, // Disable window focus refetch to prevent loops
1461
- enabled: !!id && !realtimeEnabled, // Only enable query when id exists
1638
+ enabled: !!safeId && !realtimeEnabled, // Only enable query when id exists
1462
1639
  });
1463
1640
  const [isSynced, setIsSynced] = (0, react_1.useState)(undefined);
1464
1641
  (0, react_1.useEffect)(() => {
@@ -1476,6 +1653,11 @@ function createAmplifyService(modelName, defaultAuthMode) {
1476
1653
  observeOptions.filter = { id: { eq: id } };
1477
1654
  }
1478
1655
  let isMounted = true;
1656
+ let lastSynced = undefined;
1657
+ // observeQuery can temporarily emit empty items during sync/reconnect, even when the item
1658
+ // still exists on the server. If we immediately set the cache to null, UI flickers.
1659
+ // When observeQuery reports "missing", confirm with a server get (forceRefresh) before clearing.
1660
+ let lastMissingConfirmAt = 0;
1479
1661
  const subscription = model.observeQuery(observeOptions).subscribe({
1480
1662
  next: ({ items: nextItems, isSynced: synced }) => {
1481
1663
  if (!isMounted)
@@ -1484,6 +1666,44 @@ function createAmplifyService(modelName, defaultAuthMode) {
1484
1666
  ? nextItems.filter(Boolean)
1485
1667
  : [];
1486
1668
  const nextItem = safeItems.find((entry) => (entry === null || entry === void 0 ? void 0 : entry.id) === id) || null;
1669
+ const nextSynced = Boolean(synced);
1670
+ if (lastSynced !== nextSynced) {
1671
+ lastSynced = nextSynced;
1672
+ setIsSynced(nextSynced);
1673
+ }
1674
+ const prevItem = hookQueryClient.getQueryData(singleItemQueryKey);
1675
+ if (!nextItem) {
1676
+ // Never clear immediately. During sync/reconnect observeQuery can be temporarily empty.
1677
+ if (!nextSynced)
1678
+ return;
1679
+ const now = Date.now();
1680
+ // Throttle server confirmations to avoid loops.
1681
+ if (now - lastMissingConfirmAt < 2000)
1682
+ return;
1683
+ lastMissingConfirmAt = now;
1684
+ void (() => __awaiter(this, void 0, void 0, function* () {
1685
+ try {
1686
+ const latest = yield service.get(id, { forceRefresh: true });
1687
+ if (!isMounted)
1688
+ return;
1689
+ if (latest) {
1690
+ hookQueryClient.setQueryData(singleItemQueryKey, latest);
1691
+ }
1692
+ else {
1693
+ hookQueryClient.setQueryData(singleItemQueryKey, null);
1694
+ }
1695
+ }
1696
+ catch (e) {
1697
+ // If confirm fails, keep existing cache (avoid flicker)
1698
+ (0, config_1.debugWarn)(`🍬 ${modelName} useItemHook realtime missing confirm error:`, e);
1699
+ }
1700
+ }))();
1701
+ return;
1702
+ }
1703
+ if (signatureForModelItem(prevItem) === signatureForModelItem(nextItem)) {
1704
+ // Avoid thrashing caches when observeQuery emits identical item repeatedly
1705
+ return;
1706
+ }
1487
1707
  const relatedQueryKeys = findRelatedQueryKeys(modelName, hookQueryClient);
1488
1708
  if (nextItem) {
1489
1709
  hookQueryClient.setQueryData(singleItemQueryKey, nextItem);
@@ -1504,19 +1724,6 @@ function createAmplifyService(modelName, defaultAuthMode) {
1504
1724
  });
1505
1725
  });
1506
1726
  }
1507
- else {
1508
- hookQueryClient.setQueryData(singleItemQueryKey, null);
1509
- relatedQueryKeys.forEach((queryKey) => {
1510
- if (isItemKeyForModel(modelName, queryKey)) {
1511
- return;
1512
- }
1513
- hookQueryClient.setQueryData(queryKey, (oldData) => {
1514
- const oldItems = Array.isArray(oldData) ? oldData : [];
1515
- return oldItems.filter((entry) => (entry === null || entry === void 0 ? void 0 : entry.id) !== id);
1516
- });
1517
- });
1518
- }
1519
- setIsSynced(Boolean(synced));
1520
1727
  },
1521
1728
  error: (err) => {
1522
1729
  console.error(`🍬 ${modelName} useItemHook realtime subscribe error:`, err);
@@ -1548,14 +1755,31 @@ function createAmplifyService(modelName, defaultAuthMode) {
1548
1755
  });
1549
1756
  // Interface function implementations
1550
1757
  const refreshItem = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
1551
- console.log(`🍬 ${modelName} useItemHook refresh called`, singleItemQueryKey);
1552
- // IMPORTANT:
1553
- // refetch() re-runs queryFn which calls service.get(id) (default forceRefresh=false).
1554
- // If cache exists, service.get will return cached data and never hit the network.
1555
- // refresh() must guarantee a network fetch to reflect server-side updates.
1556
- const latest = yield service.get(id, { forceRefresh: true });
1557
- return latest || null;
1558
- }), [id, modelName, service, singleItemQueryKey]); // Added id/modelName for correctness
1758
+ (0, config_1.debugLog)(`🍬 ${modelName} useItemHook refresh called`, singleItemQueryKey);
1759
+ // IMPORTANT: Use hookQueryClient.refetchQueries() or refetch() instead of service.get()
1760
+ // to ensure we're updating the same QueryClient instance that useQuery uses.
1761
+ // This prevents data from disappearing during refetch.
1762
+ try {
1763
+ // First, fetch fresh data with forceRefresh
1764
+ const latest = yield service.get(id, { forceRefresh: true });
1765
+ // Update the hookQueryClient cache to sync with the fetched data
1766
+ if (latest) {
1767
+ hookQueryClient.setQueryData(singleItemQueryKey, latest);
1768
+ }
1769
+ else {
1770
+ hookQueryClient.setQueryData(singleItemQueryKey, null);
1771
+ }
1772
+ // Also trigger refetch to ensure UI updates
1773
+ yield refetch();
1774
+ return latest || null;
1775
+ }
1776
+ catch (error) {
1777
+ console.error(`🍬 ${modelName} useItemHook refresh error:`, error);
1778
+ // On error, still try to refetch to get current state
1779
+ const refetchResult = yield refetch();
1780
+ return refetchResult.data || null;
1781
+ }
1782
+ }), [id, modelName, service, singleItemQueryKey, hookQueryClient, refetch]);
1559
1783
  const updateItem = (0, react_1.useCallback)((data) => __awaiter(this, void 0, void 0, function* () {
1560
1784
  // No additional work needed here as cache is already updated in service.update
1561
1785
  try {