amplifyquery 1.0.15 β†’ 1.0.17

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
@@ -559,7 +559,10 @@ function createAmplifyService(modelName, defaultAuthMode) {
559
559
  // Check cache first (if forceRefresh is false)
560
560
  if (!options.forceRefresh) {
561
561
  const cachedItems = query_1.queryClient.getQueryData(queryKey);
562
- if (cachedItems && cachedItems.length > 0) {
562
+ const queryState = query_1.queryClient.getQueryState(queryKey);
563
+ if (cachedItems &&
564
+ cachedItems.length > 0 &&
565
+ !(queryState === null || queryState === void 0 ? void 0 : queryState.isInvalidated)) {
563
566
  console.log(`🍬 ${modelName} list using cache`, queryKey);
564
567
  return cachedItems.filter((item) => item !== null);
565
568
  }
@@ -1119,7 +1122,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1119
1122
  },
1120
1123
  // React Hook returning method - Reimplemented based on TanStack Query
1121
1124
  useHook: (options) => {
1122
- var _a, _b, _c, _d, _e, _f, _g, _h;
1125
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1123
1126
  const hookQueryClient = (0, react_query_1.useQueryClient)();
1124
1127
  // Determine query key
1125
1128
  const queryKey = (0, react_1.useMemo)(() => {
@@ -1167,16 +1170,82 @@ function createAmplifyService(modelName, defaultAuthMode) {
1167
1170
  (_g = options === null || options === void 0 ? void 0 : options.customList) === null || _g === void 0 ? void 0 : _g.forceRefresh,
1168
1171
  service,
1169
1172
  ]);
1173
+ const realtimeEnabled = ((_h = options === null || options === void 0 ? void 0 : options.realtime) === null || _h === void 0 ? void 0 : _h.enabled) === true;
1170
1174
  const queryOptions = {
1171
1175
  queryKey,
1172
1176
  queryFn,
1173
- enabled: ((_h = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _h === void 0 ? void 0 : _h.fetch) !== false,
1177
+ enabled: ((_j = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _j === void 0 ? void 0 : _j.fetch) !== false && !realtimeEnabled,
1174
1178
  staleTime: 1000 * 30, // Keep fresh for 30 seconds (refresh more frequently)
1175
1179
  refetchOnMount: true, // Refetch on component mount
1176
1180
  refetchOnWindowFocus: false, // Don't auto-refetch on window focus
1177
1181
  refetchOnReconnect: true, // Refetch on network reconnect
1178
1182
  };
1183
+ const [isSynced, setIsSynced] = (0, react_1.useState)(undefined);
1179
1184
  const { data: items = [], isLoading, error, refetch, } = (0, react_query_1.useQuery)(queryOptions);
1185
+ (0, react_1.useEffect)(() => {
1186
+ var _a, _b, _c;
1187
+ if (!realtimeEnabled)
1188
+ return;
1189
+ if (options === null || options === void 0 ? void 0 : options.customList) {
1190
+ console.warn(`🍬 ${modelName} useHook realtime: customList is not supported.`);
1191
+ return;
1192
+ }
1193
+ const client = (0, client_1.getClient)();
1194
+ const model = (_a = client.models) === null || _a === void 0 ? void 0 : _a[modelName];
1195
+ if (!(model === null || model === void 0 ? void 0 : model.observeQuery)) {
1196
+ console.warn(`🍬 ${modelName} useHook realtime: observeQuery not available.`);
1197
+ return;
1198
+ }
1199
+ const observeOptions = Object.assign({}, (((_b = options === null || options === void 0 ? void 0 : options.realtime) === null || _b === void 0 ? void 0 : _b.observeOptions) || {}));
1200
+ if (observeOptions.filter === undefined &&
1201
+ ((_c = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _c === void 0 ? void 0 : _c.filter)) {
1202
+ observeOptions.filter = options.initialFetchOptions.filter;
1203
+ }
1204
+ let isMounted = true;
1205
+ const subscription = model.observeQuery(observeOptions).subscribe({
1206
+ next: ({ items: nextItems, isSynced: synced }) => {
1207
+ if (!isMounted)
1208
+ return;
1209
+ const safeItems = Array.isArray(nextItems)
1210
+ ? nextItems.filter(Boolean)
1211
+ : [];
1212
+ const previousItems = hookQueryClient.getQueryData(queryKey) || [];
1213
+ const previousIds = new Set(previousItems.map((item) => item === null || item === void 0 ? void 0 : item.id).filter(Boolean));
1214
+ const nextIds = new Set(safeItems.map((item) => item === null || item === void 0 ? void 0 : item.id).filter(Boolean));
1215
+ previousIds.forEach((id) => {
1216
+ if (!nextIds.has(id)) {
1217
+ hookQueryClient.removeQueries({
1218
+ queryKey: itemKey(modelName, id),
1219
+ exact: true,
1220
+ });
1221
+ }
1222
+ });
1223
+ hookQueryClient.setQueryData(queryKey, safeItems);
1224
+ safeItems.forEach((item) => {
1225
+ if (item === null || item === void 0 ? void 0 : item.id) {
1226
+ hookQueryClient.setQueryData(itemKey(modelName, item.id), item);
1227
+ }
1228
+ });
1229
+ setIsSynced(Boolean(synced));
1230
+ },
1231
+ error: (err) => {
1232
+ console.error(`🍬 ${modelName} useHook realtime subscribe error:`, err);
1233
+ },
1234
+ });
1235
+ return () => {
1236
+ var _a;
1237
+ isMounted = false;
1238
+ (_a = subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(subscription);
1239
+ };
1240
+ }, [
1241
+ realtimeEnabled,
1242
+ modelName,
1243
+ queryKey,
1244
+ hookQueryClient,
1245
+ options === null || options === void 0 ? void 0 : options.customList,
1246
+ (_k = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _k === void 0 ? void 0 : _k.filter,
1247
+ (_l = options === null || options === void 0 ? void 0 : options.realtime) === null || _l === void 0 ? void 0 : _l.observeOptions,
1248
+ ]);
1180
1249
  // Interface functions implementation
1181
1250
  const getItem = (0, react_1.useCallback)((id) => {
1182
1251
  // Use useQueryData to get latest single item from current cache
@@ -1197,6 +1266,17 @@ function createAmplifyService(modelName, defaultAuthMode) {
1197
1266
  const createItem = (0, react_1.useCallback)((data) => __awaiter(this, void 0, void 0, function* () {
1198
1267
  try {
1199
1268
  const result = yield service.create(data);
1269
+ if (result) {
1270
+ // Keep hook cache in sync immediately
1271
+ hookQueryClient.setQueryData(queryKey, (oldData) => {
1272
+ const oldItems = Array.isArray(oldData) ? oldData : [];
1273
+ if (oldItems.some((item) => (item === null || item === void 0 ? void 0 : item.id) === result.id)) {
1274
+ return oldItems;
1275
+ }
1276
+ return [...oldItems, result];
1277
+ });
1278
+ hookQueryClient.setQueryData(itemKey(modelName, result.id), result);
1279
+ }
1200
1280
  // Automatically refresh list after successful create
1201
1281
  yield refetch();
1202
1282
  return result;
@@ -1210,6 +1290,14 @@ function createAmplifyService(modelName, defaultAuthMode) {
1210
1290
  const updateItem = (0, react_1.useCallback)((data) => __awaiter(this, void 0, void 0, function* () {
1211
1291
  try {
1212
1292
  const result = yield service.update(data);
1293
+ if (result) {
1294
+ // Keep hook cache in sync immediately
1295
+ hookQueryClient.setQueryData(queryKey, (oldData) => {
1296
+ const oldItems = Array.isArray(oldData) ? oldData : [];
1297
+ return oldItems.map((item) => item && item.id === result.id ? result : item);
1298
+ });
1299
+ hookQueryClient.setQueryData(itemKey(modelName, result.id), result);
1300
+ }
1213
1301
  // Automatically refresh list after successful update
1214
1302
  yield refetch();
1215
1303
  return result;
@@ -1223,6 +1311,14 @@ function createAmplifyService(modelName, defaultAuthMode) {
1223
1311
  const deleteItem = (0, react_1.useCallback)((id) => __awaiter(this, void 0, void 0, function* () {
1224
1312
  try {
1225
1313
  const result = yield service.delete(id);
1314
+ if (result) {
1315
+ // Keep hook cache in sync immediately
1316
+ hookQueryClient.setQueryData(queryKey, (oldData) => {
1317
+ const oldItems = Array.isArray(oldData) ? oldData : [];
1318
+ return oldItems.filter((item) => (item === null || item === void 0 ? void 0 : item.id) !== id);
1319
+ });
1320
+ hookQueryClient.setQueryData(itemKey(modelName, id), null);
1321
+ }
1226
1322
  // Automatically refresh list after successful delete
1227
1323
  yield refetch();
1228
1324
  return result;
@@ -1252,6 +1348,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1252
1348
  items,
1253
1349
  isLoading,
1254
1350
  error: error,
1351
+ isSynced,
1255
1352
  getItem,
1256
1353
  refresh,
1257
1354
  create: createItem,
@@ -1330,13 +1427,25 @@ function createAmplifyService(modelName, defaultAuthMode) {
1330
1427
  const deleteItem = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
1331
1428
  try {
1332
1429
  const result = yield deleteMutation.mutateAsync();
1430
+ if (result) {
1431
+ // Ensure the hook's query cache reflects deletion immediately
1432
+ hookQueryClient.setQueryData(singleItemQueryKey, null);
1433
+ hookQueryClient.invalidateQueries({
1434
+ queryKey: [modelName],
1435
+ refetchType: "active",
1436
+ });
1437
+ hookQueryClient.invalidateQueries({
1438
+ queryKey: singleItemQueryKey,
1439
+ refetchType: "active",
1440
+ });
1441
+ }
1333
1442
  return result;
1334
1443
  }
1335
1444
  catch (error) {
1336
1445
  console.error(`🍬 ${modelName} useItemHook delete error:`, error);
1337
1446
  throw error;
1338
1447
  }
1339
- }), [deleteMutation]);
1448
+ }), [deleteMutation, hookQueryClient, modelName, singleItemQueryKey]);
1340
1449
  // Change loading state to false when isLoading is true and cached data exists
1341
1450
  const effectiveLoading = isLoading && !cachedData;
1342
1451
  // μΊμ‹œ 정리λ₯Ό μœ„ν•œ 효과 μ΅œμ ν™” (ν•œ 번만 μ‹€ν–‰)
package/dist/types.d.ts CHANGED
@@ -20,6 +20,7 @@ export type StoreState<T> = {
20
20
  export type ModelHook<T> = {
21
21
  items: T[];
22
22
  isLoading: boolean;
23
+ isSynced?: boolean;
23
24
  error: Error | null;
24
25
  getItem: (id: string) => T | null | undefined;
25
26
  refresh: (options?: {
@@ -92,6 +93,10 @@ export interface AmplifyDataService<T> {
92
93
  args: Record<string, any>;
93
94
  forceRefresh?: boolean;
94
95
  };
96
+ realtime?: {
97
+ enabled?: boolean;
98
+ observeOptions?: Record<string, any>;
99
+ };
95
100
  }) => ModelHook<T>;
96
101
  useItemHook: (id: string) => ItemHook<T>;
97
102
  modelName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amplifyquery",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "Amplify+Query",
5
5
  "keywords": [
6
6
  "Amplify",