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 +4 -0
- package/dist/config.js +4 -0
- package/dist/service.js +191 -4
- package/dist/types.d.ts +13 -1
- package/package.json +1 -1
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: ((
|
|
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
|
|
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;
|