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/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 (Direct call to Amplify Client - prevents random ID generation of generic 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
- // Call appropriate model from Amplify Models
89
- const { data: createdItem } = yield (0, client_1.getClient)().models[modelName].create(modelData);
90
- if (createdItem) {
91
- try {
92
- (_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);
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
- (_j = (_h = (_g = baseService.getStore) === null || _g === void 0 ? void 0 : _g.call(baseService)) === null || _h === void 0 ? void 0 : _h.setLoading) === null || _j === void 0 ? void 0 : _j.call(_h, false);
86
+ yield baseService.get(modelId, { forceRefresh: true });
100
87
  }
101
- catch (storeError) {
102
- // Ignore if getStore doesn't exist or call fails
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
- useCurrentHook: () => {
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
- const core = baseService.useItemHook(idForItemHook);
151
- const item = (() => {
152
- var _a;
153
- if (!currentId)
154
- return null;
155
- const raw = core.item;
156
- if (Array.isArray(raw)) {
157
- const match = raw.find((i) => (i === null || i === void 0 ? void 0 : i.id) === currentId);
158
- return match || null;
159
- }
160
- return (_a = raw) !== null && _a !== void 0 ? _a : null;
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
- if (!currentId) {
166
- const { data } = yield refetchId({ throwOnError: false });
167
- if (!data)
168
- return null;
169
- }
170
- return core.refresh();
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
- if (!currentId) {
174
- const { data } = yield refetchId({ throwOnError: false });
175
- if (!data)
176
- return null;
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
- if (!currentId) {
182
- const { data } = yield refetchId({ throwOnError: false });
183
- if (!data)
184
- return false;
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
- useCurrentHook: () => ItemHook<T>;
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
  */