amplifyquery 1.0.19 → 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/README.md +11 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +57 -3
- package/dist/index.d.ts +0 -62
- package/dist/index.js +10 -4
- package/dist/query.js +60 -29
- package/dist/service.js +347 -97
- package/dist/singleton.js +104 -88
- package/dist/types.d.ts +33 -1
- package/dist/utils.d.ts +0 -72
- package/dist/utils.js +20 -262
- package/package.json +14 -17
package/dist/singleton.js
CHANGED
|
@@ -11,9 +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");
|
|
16
|
+
const config_1 = require("./config");
|
|
17
|
+
const react_1 = require("react");
|
|
18
|
+
const config_2 = require("./config");
|
|
17
19
|
/**
|
|
18
20
|
* Function to create an extension service for singleton models
|
|
19
21
|
* @param baseService Base service
|
|
@@ -27,93 +29,68 @@ function createSingletonService(baseService, getModelId) {
|
|
|
27
29
|
const singletonService = Object.assign(Object.assign({}, baseService), {
|
|
28
30
|
// Add singleton instance management methods
|
|
29
31
|
getCurrent: (options) => __awaiter(this, void 0, void 0, function* () {
|
|
30
|
-
var _a, _b, _c;
|
|
31
32
|
try {
|
|
32
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
|
+
});
|
|
33
38
|
return baseService.get(modelId, options);
|
|
34
39
|
}
|
|
35
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.)
|
|
36
43
|
// console.error(`${modelName} singleton instance lookup error:`, error);
|
|
37
|
-
// Safely call getStore
|
|
38
|
-
try {
|
|
39
|
-
(_c = (_b = (_a = baseService
|
|
40
|
-
.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)));
|
|
41
|
-
}
|
|
42
|
-
catch (storeError) {
|
|
43
|
-
// Ignore if getStore doesn't exist or call fails
|
|
44
|
-
}
|
|
45
44
|
return null;
|
|
46
45
|
}
|
|
47
46
|
}), updateCurrent: (data) => __awaiter(this, void 0, void 0, function* () {
|
|
48
|
-
var _a, _b, _c;
|
|
49
47
|
try {
|
|
50
48
|
const modelId = yield getModelId();
|
|
51
49
|
return baseService.update(Object.assign(Object.assign({}, data), { id: modelId }));
|
|
52
50
|
}
|
|
53
51
|
catch (error) {
|
|
54
52
|
console.error(`${modelName} singleton instance update error:`, error);
|
|
55
|
-
// Safely call getStore
|
|
56
|
-
try {
|
|
57
|
-
(_c = (_b = (_a = baseService
|
|
58
|
-
.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)));
|
|
59
|
-
}
|
|
60
|
-
catch (storeError) {
|
|
61
|
-
// Ignore if getStore doesn't exist or call fails
|
|
62
|
-
}
|
|
63
53
|
return null;
|
|
64
54
|
}
|
|
65
55
|
}), upsertCurrent: (data) => __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
67
56
|
try {
|
|
68
57
|
const modelId = yield getModelId();
|
|
69
58
|
// Check the latest status by forced refresh
|
|
59
|
+
(0, config_2.debugLog)(`🍬 ${modelName} singleton.upsertCurrent check existing`, {
|
|
60
|
+
modelId,
|
|
61
|
+
});
|
|
70
62
|
const existingItem = yield baseService.get(modelId, {
|
|
71
63
|
forceRefresh: true,
|
|
72
64
|
});
|
|
73
65
|
if (existingItem) {
|
|
74
66
|
// Update
|
|
67
|
+
(0, config_2.debugLog)(`🍬 ${modelName} singleton.upsertCurrent -> update`, {
|
|
68
|
+
modelId,
|
|
69
|
+
});
|
|
75
70
|
return baseService.update(Object.assign(Object.assign({}, data), { id: modelId }));
|
|
76
71
|
}
|
|
77
72
|
else {
|
|
78
|
-
// 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
|
+
});
|
|
79
78
|
const modelData = Object.assign(Object.assign({}, data), { id: modelId });
|
|
80
|
-
// Safely call getStore
|
|
81
|
-
try {
|
|
82
|
-
(_c = (_b = (_a = baseService.getStore) === null || _a === void 0 ? void 0 : _a.call(baseService)) === null || _b === void 0 ? void 0 : _b.setLoading) === null || _c === void 0 ? void 0 : _c.call(_b, true);
|
|
83
|
-
}
|
|
84
|
-
catch (storeError) {
|
|
85
|
-
// Ignore if getStore doesn't exist or call fails
|
|
86
|
-
}
|
|
87
79
|
try {
|
|
88
|
-
//
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
catch (storeError) {
|
|
95
|
-
// Ignore if getStore doesn't exist or call fails
|
|
96
|
-
}
|
|
97
|
-
}
|
|
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
|
|
98
85
|
try {
|
|
99
|
-
|
|
86
|
+
yield baseService.get(modelId, { forceRefresh: true });
|
|
100
87
|
}
|
|
101
|
-
catch (
|
|
102
|
-
//
|
|
88
|
+
catch (_a) {
|
|
89
|
+
// ignore cache sync failures
|
|
103
90
|
}
|
|
104
91
|
return createdItem !== null && createdItem !== void 0 ? createdItem : null;
|
|
105
92
|
}
|
|
106
93
|
catch (apiError) {
|
|
107
|
-
try {
|
|
108
|
-
(_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);
|
|
109
|
-
(_q = (_p = (_o = baseService
|
|
110
|
-
.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
|
|
111
|
-
? apiError
|
|
112
|
-
: new Error(String(apiError)));
|
|
113
|
-
}
|
|
114
|
-
catch (storeError) {
|
|
115
|
-
// Ignore if getStore doesn't exist or call fails
|
|
116
|
-
}
|
|
117
94
|
console.error(`${modelName} singleton instance Upsert error:`, apiError);
|
|
118
95
|
throw apiError; // Propagate error upwards
|
|
119
96
|
}
|
|
@@ -125,21 +102,15 @@ function createSingletonService(baseService, getModelId) {
|
|
|
125
102
|
}
|
|
126
103
|
}),
|
|
127
104
|
// React hook to manage the current singleton item
|
|
128
|
-
|
|
105
|
+
useSigletoneHook: (options) => {
|
|
129
106
|
const { data: currentId, isLoading: isIdLoading, error: idError, refetch: refetchId, } = (0, react_query_1.useQuery)({
|
|
130
107
|
queryKey: [modelName, "currentId"],
|
|
131
108
|
queryFn: () => __awaiter(this, void 0, void 0, function* () {
|
|
132
|
-
var _a, _b, _c;
|
|
133
109
|
try {
|
|
134
110
|
const id = yield getModelId();
|
|
135
111
|
return id || null;
|
|
136
112
|
}
|
|
137
113
|
catch (error) {
|
|
138
|
-
try {
|
|
139
|
-
(_c = (_b = (_a = baseService
|
|
140
|
-
.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)));
|
|
141
|
-
}
|
|
142
|
-
catch (_storeError) { }
|
|
143
114
|
return null;
|
|
144
115
|
}
|
|
145
116
|
}),
|
|
@@ -147,46 +118,91 @@ function createSingletonService(baseService, getModelId) {
|
|
|
147
118
|
refetchOnWindowFocus: false,
|
|
148
119
|
});
|
|
149
120
|
const idForItemHook = currentId !== null && currentId !== void 0 ? currentId : "";
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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;
|
|
162
134
|
const isLoading = isIdLoading || core.isLoading;
|
|
163
135
|
const error = idError || core.error || null;
|
|
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
|
+
});
|
|
145
|
+
if (!currentId)
|
|
146
|
+
return;
|
|
147
|
+
if (isLoading)
|
|
148
|
+
return;
|
|
149
|
+
if (!autoCreateEnabled)
|
|
150
|
+
return;
|
|
151
|
+
if (attemptedAutoCreateForIdRef.current === currentId)
|
|
152
|
+
return;
|
|
153
|
+
if (error)
|
|
154
|
+
return;
|
|
155
|
+
if (item)
|
|
156
|
+
return;
|
|
157
|
+
attemptedAutoCreateForIdRef.current = currentId;
|
|
158
|
+
// Best-effort: create { id } if missing.
|
|
159
|
+
void (() => __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
try {
|
|
161
|
+
(0, config_2.debugWarn)(`🍬 ${modelName} useSigletoneHook auto-create starting`, {
|
|
162
|
+
currentId,
|
|
163
|
+
});
|
|
164
|
+
yield singletonService.upsertCurrent({});
|
|
165
|
+
yield singletonService.getCurrent({ forceRefresh: true });
|
|
166
|
+
(0, config_2.debugLog)(`🍬 ${modelName} useSigletoneHook auto-create done`, {
|
|
167
|
+
currentId,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
attemptedAutoCreateForIdRef.current = null; // allow retry later
|
|
172
|
+
(0, config_2.debugWarn)(`🍬 ${modelName} useSigletoneHook auto-create failed:`, e);
|
|
173
|
+
}
|
|
174
|
+
}))();
|
|
175
|
+
}, [currentId, isLoading, item, modelName, autoCreateEnabled, error]);
|
|
164
176
|
const refresh = () => __awaiter(this, void 0, void 0, function* () {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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;
|
|
171
186
|
});
|
|
172
187
|
const update = (data) => __awaiter(this, void 0, void 0, function* () {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
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;
|
|
179
192
|
});
|
|
180
193
|
const remove = () => __awaiter(this, void 0, void 0, function* () {
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
try {
|
|
195
|
+
const modelId = yield getModelId();
|
|
196
|
+
return baseService.delete(modelId);
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
return false;
|
|
185
200
|
}
|
|
186
|
-
return core.delete();
|
|
187
201
|
});
|
|
188
202
|
return { item, isLoading, error, refresh, update, delete: remove };
|
|
189
|
-
}
|
|
203
|
+
},
|
|
204
|
+
// Backward-compatible alias
|
|
205
|
+
useCurrentHook: (options) => singletonService.useSigletoneHook(options) });
|
|
190
206
|
return singletonService;
|
|
191
207
|
}
|
|
192
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,27 @@ 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>;
|
|
121
|
-
|
|
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
|
+
*/
|
|
138
|
+
useCurrentHook: (options?: {
|
|
139
|
+
autoCreate?: boolean;
|
|
140
|
+
realtime?: {
|
|
141
|
+
enabled?: boolean;
|
|
142
|
+
observeOptions?: Record<string, any>;
|
|
143
|
+
};
|
|
144
|
+
}) => ItemHook<T>;
|
|
122
145
|
}
|
|
123
146
|
export interface BaseModel {
|
|
124
147
|
id: string;
|
|
@@ -141,7 +164,16 @@ import { QueryClientConfig } from "@tanstack/react-query";
|
|
|
141
164
|
export interface AmplifyQueryConfig {
|
|
142
165
|
client: GraphQLClient;
|
|
143
166
|
defaultAuthMode?: AuthMode;
|
|
167
|
+
/**
|
|
168
|
+
* Enable AmplifyQuery internal debug logs.
|
|
169
|
+
* Default: false.
|
|
170
|
+
*/
|
|
171
|
+
debug?: boolean;
|
|
144
172
|
modelOwnerQueryMap?: Record<string, string>;
|
|
173
|
+
singletonAutoCreate?: {
|
|
174
|
+
enabled?: boolean;
|
|
175
|
+
models?: string[];
|
|
176
|
+
};
|
|
145
177
|
isCachingEnabled?: boolean;
|
|
146
178
|
queryClientConfig?: QueryClientConfig;
|
|
147
179
|
storage?: {
|
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
|
*/
|