amplifyquery 1.0.16 β†’ 1.0.18

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/config.d.ts CHANGED
@@ -11,6 +11,10 @@ export declare function setModelOwnerQueryMap(queryMap: Record<string, string>):
11
11
  * @returns The global model owner query mapping or undefined if not set
12
12
  */
13
13
  export declare function getModelOwnerQueryMap(): Record<string, string> | undefined;
14
+ /**
15
+ * Set the global selection set mapping
16
+ * @param selectionSetMap Mapping of model names to selection sets
17
+ */
14
18
  /**
15
19
  * Get owner query name for a specific model
16
20
  * @param modelName The model name
package/dist/config.js CHANGED
@@ -26,6 +26,10 @@ function setModelOwnerQueryMap(queryMap) {
26
26
  function getModelOwnerQueryMap() {
27
27
  return globalConfig.modelOwnerQueryMap;
28
28
  }
29
+ /**
30
+ * Set the global selection set mapping
31
+ * @param selectionSetMap Mapping of model names to selection sets
32
+ */
29
33
  /**
30
34
  * Get owner query name for a specific model
31
35
  * @param modelName The model name
package/dist/service.js CHANGED
@@ -1122,7 +1122,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1122
1122
  },
1123
1123
  // React Hook returning method - Reimplemented based on TanStack Query
1124
1124
  useHook: (options) => {
1125
- var _a, _b, _c, _d, _e, _f, _g, _h;
1125
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
1126
1126
  const hookQueryClient = (0, react_query_1.useQueryClient)();
1127
1127
  // Determine query key
1128
1128
  const queryKey = (0, react_1.useMemo)(() => {
@@ -1170,16 +1170,124 @@ function createAmplifyService(modelName, defaultAuthMode) {
1170
1170
  (_g = options === null || options === void 0 ? void 0 : options.customList) === null || _g === void 0 ? void 0 : _g.forceRefresh,
1171
1171
  service,
1172
1172
  ]);
1173
+ const realtimeEnabled = ((_h = options === null || options === void 0 ? void 0 : options.realtime) === null || _h === void 0 ? void 0 : _h.enabled) === true;
1174
+ 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"];
1173
1175
  const queryOptions = {
1174
1176
  queryKey,
1175
1177
  queryFn,
1176
- enabled: ((_h = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _h === void 0 ? void 0 : _h.fetch) !== false,
1178
+ enabled: ((_l = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _l === void 0 ? void 0 : _l.fetch) !== false && !realtimeEnabled,
1177
1179
  staleTime: 1000 * 30, // Keep fresh for 30 seconds (refresh more frequently)
1178
1180
  refetchOnMount: true, // Refetch on component mount
1179
1181
  refetchOnWindowFocus: false, // Don't auto-refetch on window focus
1180
1182
  refetchOnReconnect: true, // Refetch on network reconnect
1181
1183
  };
1184
+ const [isSynced, setIsSynced] = (0, react_1.useState)(undefined);
1182
1185
  const { data: items = [], isLoading, error, refetch, } = (0, react_query_1.useQuery)(queryOptions);
1186
+ (0, react_1.useEffect)(() => {
1187
+ var _a, _b, _c, _d, _e, _f;
1188
+ if (!realtimeEnabled)
1189
+ return;
1190
+ if (options === null || options === void 0 ? void 0 : options.customList) {
1191
+ const client = (0, client_1.getClient)();
1192
+ const model = (_a = client.models) === null || _a === void 0 ? void 0 : _a[modelName];
1193
+ if (!model) {
1194
+ console.warn(`🍬 ${modelName} useHook realtime: model not available.`);
1195
+ return;
1196
+ }
1197
+ const observeOptions = Object.assign({}, (((_b = options === null || options === void 0 ? void 0 : options.realtime) === null || _b === void 0 ? void 0 : _b.observeOptions) || {}));
1198
+ if (observeOptions.filter === undefined &&
1199
+ ((_c = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _c === void 0 ? void 0 : _c.filter)) {
1200
+ observeOptions.filter = options.initialFetchOptions.filter;
1201
+ }
1202
+ const subscriptions = [
1203
+ realtimeEvents.includes("create") && model.onCreate
1204
+ ? model.onCreate(observeOptions).subscribe({
1205
+ next: () => hookQueryClient.invalidateQueries({
1206
+ queryKey,
1207
+ refetchType: "active",
1208
+ }),
1209
+ error: (err) => console.error(`🍬 ${modelName} useHook realtime onCreate error:`, err),
1210
+ })
1211
+ : null,
1212
+ realtimeEvents.includes("update") && model.onUpdate
1213
+ ? model.onUpdate(observeOptions).subscribe({
1214
+ next: () => hookQueryClient.invalidateQueries({
1215
+ queryKey,
1216
+ refetchType: "active",
1217
+ }),
1218
+ error: (err) => console.error(`🍬 ${modelName} useHook realtime onUpdate error:`, err),
1219
+ })
1220
+ : null,
1221
+ realtimeEvents.includes("delete") && model.onDelete
1222
+ ? model.onDelete(observeOptions).subscribe({
1223
+ next: () => hookQueryClient.invalidateQueries({
1224
+ queryKey,
1225
+ refetchType: "active",
1226
+ }),
1227
+ error: (err) => console.error(`🍬 ${modelName} useHook realtime onDelete error:`, err),
1228
+ })
1229
+ : null,
1230
+ ].filter(Boolean);
1231
+ return () => {
1232
+ subscriptions.forEach((sub) => { var _a; return (_a = sub === null || sub === void 0 ? void 0 : sub.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(sub); });
1233
+ };
1234
+ }
1235
+ const client = (0, client_1.getClient)();
1236
+ const model = (_d = client.models) === null || _d === void 0 ? void 0 : _d[modelName];
1237
+ if (!(model === null || model === void 0 ? void 0 : model.observeQuery)) {
1238
+ console.warn(`🍬 ${modelName} useHook realtime: observeQuery not available.`);
1239
+ return;
1240
+ }
1241
+ const observeOptions = Object.assign({}, (((_e = options === null || options === void 0 ? void 0 : options.realtime) === null || _e === void 0 ? void 0 : _e.observeOptions) || {}));
1242
+ if (observeOptions.filter === undefined &&
1243
+ ((_f = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _f === void 0 ? void 0 : _f.filter)) {
1244
+ observeOptions.filter = options.initialFetchOptions.filter;
1245
+ }
1246
+ let isMounted = true;
1247
+ const subscription = model.observeQuery(observeOptions).subscribe({
1248
+ next: ({ items: nextItems, isSynced: synced }) => {
1249
+ if (!isMounted)
1250
+ return;
1251
+ const safeItems = Array.isArray(nextItems)
1252
+ ? nextItems.filter(Boolean)
1253
+ : [];
1254
+ 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
+ });
1265
+ hookQueryClient.setQueryData(queryKey, safeItems);
1266
+ safeItems.forEach((item) => {
1267
+ if (item === null || item === void 0 ? void 0 : item.id) {
1268
+ hookQueryClient.setQueryData(itemKey(modelName, item.id), item);
1269
+ }
1270
+ });
1271
+ setIsSynced(Boolean(synced));
1272
+ },
1273
+ error: (err) => {
1274
+ console.error(`🍬 ${modelName} useHook realtime subscribe error:`, err);
1275
+ },
1276
+ });
1277
+ return () => {
1278
+ var _a;
1279
+ isMounted = false;
1280
+ (_a = subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(subscription);
1281
+ };
1282
+ }, [
1283
+ realtimeEnabled,
1284
+ modelName,
1285
+ queryKey,
1286
+ hookQueryClient,
1287
+ options === null || options === void 0 ? void 0 : options.customList,
1288
+ (_m = options === null || options === void 0 ? void 0 : options.initialFetchOptions) === null || _m === void 0 ? void 0 : _m.filter,
1289
+ (_o = options === null || options === void 0 ? void 0 : options.realtime) === null || _o === void 0 ? void 0 : _o.observeOptions,
1290
+ ]);
1183
1291
  // Interface functions implementation
1184
1292
  const getItem = (0, react_1.useCallback)((id) => {
1185
1293
  // Use useQueryData to get latest single item from current cache
@@ -1282,6 +1390,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1282
1390
  items,
1283
1391
  isLoading,
1284
1392
  error: error,
1393
+ isSynced,
1285
1394
  getItem,
1286
1395
  refresh,
1287
1396
  create: createItem,
@@ -1291,9 +1400,11 @@ function createAmplifyService(modelName, defaultAuthMode) {
1291
1400
  };
1292
1401
  },
1293
1402
  // Hook for managing single item - Reimplemented based on TanStack Query
1294
- useItemHook: (id) => {
1403
+ useItemHook: (id, options) => {
1404
+ var _a, _b;
1295
1405
  const hookQueryClient = (0, react_query_1.useQueryClient)();
1296
1406
  const singleItemQueryKey = itemKey(modelName, id);
1407
+ const realtimeEnabled = ((_a = options === null || options === void 0 ? void 0 : options.realtime) === null || _a === void 0 ? void 0 : _a.enabled) === true;
1297
1408
  // First check data from cache
1298
1409
  const rawCachedData = hookQueryClient.getQueryData(singleItemQueryKey);
1299
1410
  // πŸ”§ 버그 μˆ˜μ •: 배열이 μΊμ‹œλ˜μ–΄ μžˆλŠ” 경우 처리
@@ -1325,8 +1436,83 @@ function createAmplifyService(modelName, defaultAuthMode) {
1325
1436
  staleTime: 1000 * 60, // Keep data "fresh" for 1 minute
1326
1437
  refetchOnMount: cachedData ? false : true, // Only refetch if no cached data
1327
1438
  refetchOnWindowFocus: false, // Disable window focus refetch to prevent loops
1328
- enabled: !!id, // Only enable query when id exists
1439
+ enabled: !!id && !realtimeEnabled, // Only enable query when id exists
1329
1440
  });
1441
+ const [isSynced, setIsSynced] = (0, react_1.useState)(undefined);
1442
+ (0, react_1.useEffect)(() => {
1443
+ var _a, _b;
1444
+ if (!realtimeEnabled || !id)
1445
+ return;
1446
+ const client = (0, client_1.getClient)();
1447
+ const model = (_a = client.models) === null || _a === void 0 ? void 0 : _a[modelName];
1448
+ if (!(model === null || model === void 0 ? void 0 : model.observeQuery)) {
1449
+ console.warn(`🍬 ${modelName} useItemHook realtime: observeQuery not available.`);
1450
+ return;
1451
+ }
1452
+ const observeOptions = Object.assign({}, (((_b = options === null || options === void 0 ? void 0 : options.realtime) === null || _b === void 0 ? void 0 : _b.observeOptions) || {}));
1453
+ if (!observeOptions.filter) {
1454
+ observeOptions.filter = { id: { eq: id } };
1455
+ }
1456
+ let isMounted = true;
1457
+ const subscription = model.observeQuery(observeOptions).subscribe({
1458
+ next: ({ items: nextItems, isSynced: synced }) => {
1459
+ if (!isMounted)
1460
+ return;
1461
+ const safeItems = Array.isArray(nextItems)
1462
+ ? nextItems.filter(Boolean)
1463
+ : [];
1464
+ const nextItem = safeItems.find((entry) => (entry === null || entry === void 0 ? void 0 : entry.id) === id) || null;
1465
+ const relatedQueryKeys = findRelatedQueryKeys(modelName, hookQueryClient);
1466
+ if (nextItem) {
1467
+ hookQueryClient.setQueryData(singleItemQueryKey, nextItem);
1468
+ relatedQueryKeys.forEach((queryKey) => {
1469
+ if (isItemKeyForModel(modelName, queryKey)) {
1470
+ return;
1471
+ }
1472
+ hookQueryClient.setQueryData(queryKey, (oldData) => {
1473
+ const oldItems = Array.isArray(oldData) ? oldData : [];
1474
+ const hasItem = oldItems.some((entry) => (entry === null || entry === void 0 ? void 0 : entry.id) === id);
1475
+ if (hasItem) {
1476
+ return oldItems.map((entry) => (entry === null || entry === void 0 ? void 0 : entry.id) === id ? nextItem : entry);
1477
+ }
1478
+ if (queryKey.length === 1) {
1479
+ return [...oldItems, nextItem];
1480
+ }
1481
+ return oldItems;
1482
+ });
1483
+ });
1484
+ }
1485
+ else {
1486
+ hookQueryClient.setQueryData(singleItemQueryKey, null);
1487
+ relatedQueryKeys.forEach((queryKey) => {
1488
+ if (isItemKeyForModel(modelName, queryKey)) {
1489
+ return;
1490
+ }
1491
+ hookQueryClient.setQueryData(queryKey, (oldData) => {
1492
+ const oldItems = Array.isArray(oldData) ? oldData : [];
1493
+ return oldItems.filter((entry) => (entry === null || entry === void 0 ? void 0 : entry.id) !== id);
1494
+ });
1495
+ });
1496
+ }
1497
+ setIsSynced(Boolean(synced));
1498
+ },
1499
+ error: (err) => {
1500
+ console.error(`🍬 ${modelName} useItemHook realtime subscribe error:`, err);
1501
+ },
1502
+ });
1503
+ return () => {
1504
+ var _a;
1505
+ isMounted = false;
1506
+ (_a = subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(subscription);
1507
+ };
1508
+ }, [
1509
+ realtimeEnabled,
1510
+ id,
1511
+ modelName,
1512
+ hookQueryClient,
1513
+ singleItemQueryKey,
1514
+ (_b = options === null || options === void 0 ? void 0 : options.realtime) === null || _b === void 0 ? void 0 : _b.observeOptions,
1515
+ ]);
1330
1516
  // useMutation hooks call service methods,
1331
1517
  // Service methods handle optimistic updates and cache updates/rollbacks internally.
1332
1518
  // Mutations inside useHook only serve to call service methods.
@@ -1396,6 +1582,7 @@ function createAmplifyService(modelName, defaultAuthMode) {
1396
1582
  item: item || null,
1397
1583
  isLoading: effectiveLoading, // Not loading if cached data exists
1398
1584
  error: error, // Explicitly specify error type
1585
+ isSynced,
1399
1586
  refresh: refreshItem,
1400
1587
  update: updateItem,
1401
1588
  delete: deleteItem,
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?: {
@@ -40,6 +41,7 @@ export type ModelHook<T> = {
40
41
  export type ItemHook<T> = {
41
42
  item: T | null;
42
43
  isLoading: boolean;
44
+ isSynced?: boolean;
43
45
  error: Error | null;
44
46
  refresh: () => Promise<T | null>;
45
47
  update: (data: Partial<T>) => Promise<T | null>;
@@ -92,8 +94,18 @@ export interface AmplifyDataService<T> {
92
94
  args: Record<string, any>;
93
95
  forceRefresh?: boolean;
94
96
  };
97
+ realtime?: {
98
+ enabled?: boolean;
99
+ observeOptions?: Record<string, any>;
100
+ events?: Array<"create" | "update" | "delete">;
101
+ };
95
102
  }) => ModelHook<T>;
96
- useItemHook: (id: string) => ItemHook<T>;
103
+ useItemHook: (id: string, options?: {
104
+ realtime?: {
105
+ enabled?: boolean;
106
+ observeOptions?: Record<string, any>;
107
+ };
108
+ }) => ItemHook<T>;
97
109
  modelName: string;
98
110
  withExtensions: <E>(extensions: E) => AmplifyDataService<T> & E;
99
111
  setAuthMode: (authMode: AuthMode) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amplifyquery",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Amplify+Query",
5
5
  "keywords": [
6
6
  "Amplify",