amplifyquery 1.0.20 → 2.0.1
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/README.md +11 -0
- package/dist/config.d.ts +8 -1
- package/dist/config.js +36 -6
- package/dist/index.d.ts +0 -62
- package/dist/index.js +6 -4
- package/dist/query.js +60 -29
- package/dist/service.js +336 -112
- package/dist/singleton.js +85 -95
- package/dist/types.d.ts +23 -0
- package/dist/utils.d.ts +0 -72
- package/dist/utils.js +20 -262
- package/package.json +14 -17
package/dist/singleton.js
CHANGED
|
@@ -11,11 +11,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.getModelIds = void 0;
|
|
13
13
|
exports.createSingletonService = createSingletonService;
|
|
14
|
-
const client_1 = require("./client");
|
|
15
14
|
const auth_1 = require("aws-amplify/auth");
|
|
16
15
|
const react_query_1 = require("@tanstack/react-query");
|
|
17
16
|
const config_1 = require("./config");
|
|
18
17
|
const react_1 = require("react");
|
|
18
|
+
const config_2 = require("./config");
|
|
19
19
|
/**
|
|
20
20
|
* Function to create an extension service for singleton models
|
|
21
21
|
* @param baseService Base service
|
|
@@ -29,93 +29,68 @@ function createSingletonService(baseService, getModelId) {
|
|
|
29
29
|
const singletonService = Object.assign(Object.assign({}, baseService), {
|
|
30
30
|
// Add singleton instance management methods
|
|
31
31
|
getCurrent: (options) => __awaiter(this, void 0, void 0, function* () {
|
|
32
|
-
var _a, _b, _c;
|
|
33
32
|
try {
|
|
34
33
|
const modelId = yield getModelId();
|
|
34
|
+
(0, config_2.debugLog)(`🍬 ${modelName} singleton.getCurrent`, {
|
|
35
|
+
modelId,
|
|
36
|
+
forceRefresh: (options === null || options === void 0 ? void 0 : options.forceRefresh) === true,
|
|
37
|
+
});
|
|
35
38
|
return baseService.get(modelId, options);
|
|
36
39
|
}
|
|
37
40
|
catch (error) {
|
|
41
|
+
// Keep singleton reads soft-failing (null) like base get().
|
|
42
|
+
// (Do not call getStore here: TanStack Query service doesn't support it.)
|
|
38
43
|
// console.error(`${modelName} singleton instance lookup error:`, error);
|
|
39
|
-
// Safely call getStore
|
|
40
|
-
try {
|
|
41
|
-
(_c = (_b = (_a = baseService
|
|
42
|
-
.getStore) === null || _a === void 0 ? void 0 : _a.call(baseService)) === null || _b === void 0 ? void 0 : _b.setError) === null || _c === void 0 ? void 0 : _c.call(_b, error instanceof Error ? error : new Error(String(error)));
|
|
43
|
-
}
|
|
44
|
-
catch (storeError) {
|
|
45
|
-
// Ignore if getStore doesn't exist or call fails
|
|
46
|
-
}
|
|
47
44
|
return null;
|
|
48
45
|
}
|
|
49
46
|
}), updateCurrent: (data) => __awaiter(this, void 0, void 0, function* () {
|
|
50
|
-
var _a, _b, _c;
|
|
51
47
|
try {
|
|
52
48
|
const modelId = yield getModelId();
|
|
53
49
|
return baseService.update(Object.assign(Object.assign({}, data), { id: modelId }));
|
|
54
50
|
}
|
|
55
51
|
catch (error) {
|
|
56
52
|
console.error(`${modelName} singleton instance update error:`, error);
|
|
57
|
-
// Safely call getStore
|
|
58
|
-
try {
|
|
59
|
-
(_c = (_b = (_a = baseService
|
|
60
|
-
.getStore) === null || _a === void 0 ? void 0 : _a.call(baseService)) === null || _b === void 0 ? void 0 : _b.setError) === null || _c === void 0 ? void 0 : _c.call(_b, error instanceof Error ? error : new Error(String(error)));
|
|
61
|
-
}
|
|
62
|
-
catch (storeError) {
|
|
63
|
-
// Ignore if getStore doesn't exist or call fails
|
|
64
|
-
}
|
|
65
53
|
return null;
|
|
66
54
|
}
|
|
67
55
|
}), upsertCurrent: (data) => __awaiter(this, void 0, void 0, function* () {
|
|
68
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
69
56
|
try {
|
|
70
57
|
const modelId = yield getModelId();
|
|
71
58
|
// Check the latest status by forced refresh
|
|
59
|
+
(0, config_2.debugLog)(`🍬 ${modelName} singleton.upsertCurrent check existing`, {
|
|
60
|
+
modelId,
|
|
61
|
+
});
|
|
72
62
|
const existingItem = yield baseService.get(modelId, {
|
|
73
63
|
forceRefresh: true,
|
|
74
64
|
});
|
|
75
65
|
if (existingItem) {
|
|
76
66
|
// Update
|
|
67
|
+
(0, config_2.debugLog)(`🍬 ${modelName} singleton.upsertCurrent -> update`, {
|
|
68
|
+
modelId,
|
|
69
|
+
});
|
|
77
70
|
return baseService.update(Object.assign(Object.assign({}, data), { id: modelId }));
|
|
78
71
|
}
|
|
79
72
|
else {
|
|
80
|
-
// Create
|
|
73
|
+
// Create using baseService so authMode/owner handling stays consistent.
|
|
74
|
+
// (We still pass a fixed id to guarantee singleton identity.)
|
|
75
|
+
(0, config_2.debugWarn)(`🍬 ${modelName} singleton.upsertCurrent missing -> create`, {
|
|
76
|
+
modelId,
|
|
77
|
+
});
|
|
81
78
|
const modelData = Object.assign(Object.assign({}, data), { id: modelId });
|
|
82
|
-
// Safely call getStore
|
|
83
79
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// Call appropriate model from Amplify Models
|
|
91
|
-
const { data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(modelData);
|
|
92
|
-
if (createdItem) {
|
|
93
|
-
try {
|
|
94
|
-
(_f = (_e = (_d = baseService.getStore) === null || _d === void 0 ? void 0 : _d.call(baseService)) === null || _e === void 0 ? void 0 : _e.setItem) === null || _f === void 0 ? void 0 : _f.call(_e, createdItem);
|
|
95
|
-
}
|
|
96
|
-
catch (storeError) {
|
|
97
|
-
// Ignore if getStore doesn't exist or call fails
|
|
98
|
-
}
|
|
99
|
-
}
|
|
80
|
+
// Use service.create to keep cache + auth mode consistent
|
|
81
|
+
const createdItem = yield baseService.create(modelData, {
|
|
82
|
+
authMode: baseService.getAuthMode(),
|
|
83
|
+
});
|
|
84
|
+
// Ensure cache is synced to latest server state
|
|
100
85
|
try {
|
|
101
|
-
|
|
86
|
+
yield baseService.get(modelId, { forceRefresh: true });
|
|
102
87
|
}
|
|
103
|
-
catch (
|
|
104
|
-
//
|
|
88
|
+
catch (_a) {
|
|
89
|
+
// ignore cache sync failures
|
|
105
90
|
}
|
|
106
91
|
return createdItem !== null && createdItem !== void 0 ? createdItem : null;
|
|
107
92
|
}
|
|
108
93
|
catch (apiError) {
|
|
109
|
-
try {
|
|
110
|
-
(_m = (_l = (_k = baseService.getStore) === null || _k === void 0 ? void 0 : _k.call(baseService)) === null || _l === void 0 ? void 0 : _l.setLoading) === null || _m === void 0 ? void 0 : _m.call(_l, false);
|
|
111
|
-
(_q = (_p = (_o = baseService
|
|
112
|
-
.getStore) === null || _o === void 0 ? void 0 : _o.call(baseService)) === null || _p === void 0 ? void 0 : _p.setError) === null || _q === void 0 ? void 0 : _q.call(_p, apiError instanceof Error
|
|
113
|
-
? apiError
|
|
114
|
-
: new Error(String(apiError)));
|
|
115
|
-
}
|
|
116
|
-
catch (storeError) {
|
|
117
|
-
// Ignore if getStore doesn't exist or call fails
|
|
118
|
-
}
|
|
119
94
|
console.error(`${modelName} singleton instance Upsert error:`, apiError);
|
|
120
95
|
throw apiError; // Propagate error upwards
|
|
121
96
|
}
|
|
@@ -127,21 +102,15 @@ function createSingletonService(baseService, getModelId) {
|
|
|
127
102
|
}
|
|
128
103
|
}),
|
|
129
104
|
// React hook to manage the current singleton item
|
|
130
|
-
|
|
105
|
+
useSigletoneHook: (options) => {
|
|
131
106
|
const { data: currentId, isLoading: isIdLoading, error: idError, refetch: refetchId, } = (0, react_query_1.useQuery)({
|
|
132
107
|
queryKey: [modelName, "currentId"],
|
|
133
108
|
queryFn: () => __awaiter(this, void 0, void 0, function* () {
|
|
134
|
-
var _a, _b, _c;
|
|
135
109
|
try {
|
|
136
110
|
const id = yield getModelId();
|
|
137
111
|
return id || null;
|
|
138
112
|
}
|
|
139
113
|
catch (error) {
|
|
140
|
-
try {
|
|
141
|
-
(_c = (_b = (_a = baseService
|
|
142
|
-
.getStore) === null || _a === void 0 ? void 0 : _a.call(baseService)) === null || _b === void 0 ? void 0 : _b.setError) === null || _c === void 0 ? void 0 : _c.call(_b, error instanceof Error ? error : new Error(String(error)));
|
|
143
|
-
}
|
|
144
|
-
catch (_storeError) { }
|
|
145
114
|
return null;
|
|
146
115
|
}
|
|
147
116
|
}),
|
|
@@ -149,70 +118,91 @@ function createSingletonService(baseService, getModelId) {
|
|
|
149
118
|
refetchOnWindowFocus: false,
|
|
150
119
|
});
|
|
151
120
|
const idForItemHook = currentId !== null && currentId !== void 0 ? currentId : "";
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
121
|
+
(0, config_2.debugLog)(`🍬 ${modelName} useSigletoneHook currentId`, {
|
|
122
|
+
currentId,
|
|
123
|
+
idForItemHook,
|
|
124
|
+
});
|
|
125
|
+
const core = baseService.useItemHook(idForItemHook, (options === null || options === void 0 ? void 0 : options.realtime) ? { realtime: options.realtime } : undefined);
|
|
126
|
+
const attemptedAutoCreateForIdRef = (0, react_1.useRef)(null);
|
|
127
|
+
const cfg = (0, config_1.getSingletonAutoCreate)();
|
|
128
|
+
const autoCreateEnabled = typeof (options === null || options === void 0 ? void 0 : options.autoCreate) === "boolean"
|
|
129
|
+
? options.autoCreate
|
|
130
|
+
: cfg
|
|
131
|
+
? (0, config_1.isSingletonAutoCreateEnabledForModel)(modelName)
|
|
132
|
+
: true;
|
|
133
|
+
const item = currentId ? core.item : null;
|
|
165
134
|
const isLoading = isIdLoading || core.isLoading;
|
|
166
135
|
const error = idError || core.error || null;
|
|
167
136
|
(0, react_1.useEffect)(() => {
|
|
137
|
+
(0, config_2.debugLog)(`🍬 ${modelName} useSigletoneHook effect check`, {
|
|
138
|
+
currentId,
|
|
139
|
+
isLoading,
|
|
140
|
+
autoCreateEnabled,
|
|
141
|
+
attemptedFor: attemptedAutoCreateForIdRef.current,
|
|
142
|
+
hasError: Boolean(error),
|
|
143
|
+
hasItem: Boolean(item),
|
|
144
|
+
});
|
|
168
145
|
if (!currentId)
|
|
169
146
|
return;
|
|
170
147
|
if (isLoading)
|
|
171
148
|
return;
|
|
172
|
-
if (!
|
|
149
|
+
if (!autoCreateEnabled)
|
|
173
150
|
return;
|
|
174
|
-
if (
|
|
151
|
+
if (attemptedAutoCreateForIdRef.current === currentId)
|
|
152
|
+
return;
|
|
153
|
+
if (error)
|
|
175
154
|
return;
|
|
176
155
|
if (item)
|
|
177
156
|
return;
|
|
178
|
-
|
|
157
|
+
attemptedAutoCreateForIdRef.current = currentId;
|
|
179
158
|
// Best-effort: create { id } if missing.
|
|
180
159
|
void (() => __awaiter(this, void 0, void 0, function* () {
|
|
181
160
|
try {
|
|
161
|
+
(0, config_2.debugWarn)(`🍬 ${modelName} useSigletoneHook auto-create starting`, {
|
|
162
|
+
currentId,
|
|
163
|
+
});
|
|
182
164
|
yield singletonService.upsertCurrent({});
|
|
183
|
-
yield
|
|
165
|
+
yield singletonService.getCurrent({ forceRefresh: true });
|
|
166
|
+
(0, config_2.debugLog)(`🍬 ${modelName} useSigletoneHook auto-create done`, {
|
|
167
|
+
currentId,
|
|
168
|
+
});
|
|
184
169
|
}
|
|
185
170
|
catch (e) {
|
|
186
|
-
|
|
171
|
+
attemptedAutoCreateForIdRef.current = null; // allow retry later
|
|
172
|
+
(0, config_2.debugWarn)(`🍬 ${modelName} useSigletoneHook auto-create failed:`, e);
|
|
187
173
|
}
|
|
188
174
|
}))();
|
|
189
|
-
}, [currentId, isLoading, item, modelName]);
|
|
175
|
+
}, [currentId, isLoading, item, modelName, autoCreateEnabled, error]);
|
|
190
176
|
const refresh = () => __awaiter(this, void 0, void 0, function* () {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
177
|
+
var _a;
|
|
178
|
+
const latest = yield singletonService.getCurrent({ forceRefresh: true });
|
|
179
|
+
if (latest)
|
|
180
|
+
return latest;
|
|
181
|
+
if (!autoCreateEnabled)
|
|
182
|
+
return null;
|
|
183
|
+
// If missing, create then re-fetch to sync cache.
|
|
184
|
+
yield singletonService.upsertCurrent({});
|
|
185
|
+
return (_a = (yield singletonService.getCurrent({ forceRefresh: true }))) !== null && _a !== void 0 ? _a : null;
|
|
197
186
|
});
|
|
198
187
|
const update = (data) => __awaiter(this, void 0, void 0, function* () {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
return core.update(data);
|
|
188
|
+
var _a;
|
|
189
|
+
// Upsert to satisfy "create if missing" behavior.
|
|
190
|
+
yield singletonService.upsertCurrent(data);
|
|
191
|
+
return (_a = (yield singletonService.getCurrent({ forceRefresh: true }))) !== null && _a !== void 0 ? _a : null;
|
|
205
192
|
});
|
|
206
193
|
const remove = () => __awaiter(this, void 0, void 0, function* () {
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
194
|
+
try {
|
|
195
|
+
const modelId = yield getModelId();
|
|
196
|
+
return baseService.delete(modelId);
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
return false;
|
|
211
200
|
}
|
|
212
|
-
return core.delete();
|
|
213
201
|
});
|
|
214
202
|
return { item, isLoading, error, refresh, update, delete: remove };
|
|
215
|
-
}
|
|
203
|
+
},
|
|
204
|
+
// Backward-compatible alias
|
|
205
|
+
useCurrentHook: (options) => singletonService.useSigletoneHook(options) });
|
|
216
206
|
return singletonService;
|
|
217
207
|
}
|
|
218
208
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export type ModelHook<T> = {
|
|
|
33
33
|
delete: (id: string) => Promise<boolean>;
|
|
34
34
|
customList: (queryName: string, args: Record<string, any>, options?: {
|
|
35
35
|
forceRefresh?: boolean;
|
|
36
|
+
throwOnError?: boolean;
|
|
36
37
|
}) => Promise<T[]>;
|
|
37
38
|
};
|
|
38
39
|
/**
|
|
@@ -65,10 +66,12 @@ export interface AmplifyDataService<T> {
|
|
|
65
66
|
filter?: Record<string, any>;
|
|
66
67
|
forceRefresh?: boolean;
|
|
67
68
|
authMode?: AuthMode;
|
|
69
|
+
throwOnError?: boolean;
|
|
68
70
|
}) => Promise<T[]>;
|
|
69
71
|
customList: (queryName: string, args: Record<string, any>, options?: {
|
|
70
72
|
forceRefresh?: boolean;
|
|
71
73
|
authMode?: AuthMode;
|
|
74
|
+
throwOnError?: boolean;
|
|
72
75
|
}) => Promise<T[]>;
|
|
73
76
|
update: (data: Partial<T> & {
|
|
74
77
|
id: string;
|
|
@@ -118,7 +121,22 @@ export interface SingletonAmplifyService<T> extends AmplifyDataService<T> {
|
|
|
118
121
|
}) => Promise<T | null>;
|
|
119
122
|
updateCurrent: (data: Partial<T>) => Promise<T | null>;
|
|
120
123
|
upsertCurrent: (data: Partial<T>) => Promise<T | null>;
|
|
124
|
+
useSigletoneHook: (options?: {
|
|
125
|
+
/**
|
|
126
|
+
* When true (default), if the singleton item does not exist it will be created.
|
|
127
|
+
* You can set false to disable auto-create for this hook call.
|
|
128
|
+
*/
|
|
129
|
+
autoCreate?: boolean;
|
|
130
|
+
realtime?: {
|
|
131
|
+
enabled?: boolean;
|
|
132
|
+
observeOptions?: Record<string, any>;
|
|
133
|
+
};
|
|
134
|
+
}) => ItemHook<T>;
|
|
135
|
+
/**
|
|
136
|
+
* @deprecated Use useSigletoneHook() instead.
|
|
137
|
+
*/
|
|
121
138
|
useCurrentHook: (options?: {
|
|
139
|
+
autoCreate?: boolean;
|
|
122
140
|
realtime?: {
|
|
123
141
|
enabled?: boolean;
|
|
124
142
|
observeOptions?: Record<string, any>;
|
|
@@ -146,6 +164,11 @@ import { QueryClientConfig } from "@tanstack/react-query";
|
|
|
146
164
|
export interface AmplifyQueryConfig {
|
|
147
165
|
client: GraphQLClient;
|
|
148
166
|
defaultAuthMode?: AuthMode;
|
|
167
|
+
/**
|
|
168
|
+
* Enable AmplifyQuery internal debug logs.
|
|
169
|
+
* Default: false.
|
|
170
|
+
*/
|
|
171
|
+
debug?: boolean;
|
|
149
172
|
modelOwnerQueryMap?: Record<string, string>;
|
|
150
173
|
singletonAutoCreate?: {
|
|
151
174
|
enabled?: boolean;
|
package/dist/utils.d.ts
CHANGED
|
@@ -33,78 +33,6 @@ export declare const Utils: {
|
|
|
33
33
|
*/
|
|
34
34
|
removeOwnerField: <T extends Record<string, any>>(data: T, operation: "create" | "update" | "upsert") => Omit<T, "owner">;
|
|
35
35
|
};
|
|
36
|
-
/**
|
|
37
|
-
* Storage related utilities
|
|
38
|
-
*/
|
|
39
|
-
export declare const StorageService: {
|
|
40
|
-
_types: {
|
|
41
|
-
Item: {
|
|
42
|
-
url: string;
|
|
43
|
-
expiresAt: number;
|
|
44
|
-
};
|
|
45
|
-
CacheData: {
|
|
46
|
-
[key: string]: {
|
|
47
|
-
url: string;
|
|
48
|
-
expiresAt: number;
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
_urlCache: Map<string, {
|
|
53
|
-
url: string;
|
|
54
|
-
expiresAt: number;
|
|
55
|
-
}>;
|
|
56
|
-
_CACHE_KEY: string;
|
|
57
|
-
_initialized: boolean;
|
|
58
|
-
/**
|
|
59
|
-
* Initialize the memory cache.
|
|
60
|
-
* Loads URL cache from MMKV storage.
|
|
61
|
-
*/
|
|
62
|
-
_initCache: () => void;
|
|
63
|
-
/**
|
|
64
|
-
* Save cache to MMKV storage.
|
|
65
|
-
*/
|
|
66
|
-
_saveCache: () => void;
|
|
67
|
-
/**
|
|
68
|
-
* Upload an image file to Storage.
|
|
69
|
-
* @param file File to upload (Blob or File object)
|
|
70
|
-
* @param key Path and filename to save as (auto-generated if not specified)
|
|
71
|
-
* @returns Key of the uploaded file
|
|
72
|
-
*/
|
|
73
|
-
uploadImage: (file: Blob | File, key?: string) => Promise<string>;
|
|
74
|
-
/**
|
|
75
|
-
* Get the URL of a stored file. (Auto-caching)
|
|
76
|
-
* @param key File key
|
|
77
|
-
* @param options Caching options (forceRefresh: ignore cache and fetch new URL)
|
|
78
|
-
* @returns File URL
|
|
79
|
-
*/
|
|
80
|
-
getFileUrl: (key: string, options?: {
|
|
81
|
-
forceRefresh?: boolean;
|
|
82
|
-
}) => Promise<string>;
|
|
83
|
-
/**
|
|
84
|
-
* Delete a stored file.
|
|
85
|
-
* @param key Key of the file to delete
|
|
86
|
-
*/
|
|
87
|
-
deleteFile: (key: string) => Promise<void>;
|
|
88
|
-
/**
|
|
89
|
-
* Clear the URL cache.
|
|
90
|
-
*/
|
|
91
|
-
clearUrlCache: () => void;
|
|
92
|
-
/**
|
|
93
|
-
* Remove a specific key's URL cache.
|
|
94
|
-
* @param key Key of the URL to remove
|
|
95
|
-
*/
|
|
96
|
-
clearUrlCacheForKey: (key: string) => void;
|
|
97
|
-
/**
|
|
98
|
-
* Remove only expired URL caches.
|
|
99
|
-
*/
|
|
100
|
-
clearExpiredUrlCache: () => void;
|
|
101
|
-
/**
|
|
102
|
-
* Download an audio file.
|
|
103
|
-
* @param audioKey Key of the audio file to download
|
|
104
|
-
* @returns Local file system path of the downloaded file
|
|
105
|
-
*/
|
|
106
|
-
downloadAudioFile: (audioKey: string) => Promise<string>;
|
|
107
|
-
};
|
|
108
36
|
/**
|
|
109
37
|
* Authentication related utilities
|
|
110
38
|
*/
|