amplifyquery 1.0.0

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/utils.js ADDED
@@ -0,0 +1,437 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __rest = (this && this.__rest) || function (s, e) {
45
+ var t = {};
46
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
47
+ t[p] = s[p];
48
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
49
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
50
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
51
+ t[p[i]] = s[p[i]];
52
+ }
53
+ return t;
54
+ };
55
+ Object.defineProperty(exports, "__esModule", { value: true });
56
+ exports.AuthService = exports.StorageService = exports.Utils = void 0;
57
+ exports.setAppUrl = setAppUrl;
58
+ exports.getAppUrl = getAppUrl;
59
+ exports.createRelationalHook = createRelationalHook;
60
+ const react_query_1 = require("@tanstack/react-query");
61
+ const auth_1 = require("aws-amplify/auth");
62
+ const storage_1 = require("aws-amplify/storage");
63
+ const expo_crypto_1 = require("expo-crypto");
64
+ const FileSystem = __importStar(require("expo-file-system"));
65
+ const react_1 = require("react");
66
+ const react_native_mmkv_1 = require("react-native-mmkv");
67
+ // Create MMKV storage instance
68
+ const storage = new react_native_mmkv_1.MMKV({ id: "mmkv.amplify-query" });
69
+ // Key for managing app URL
70
+ const APP_URL_KEY = "amplify_query_app_url";
71
+ /**
72
+ * Set the base URL for the app
73
+ * @param url Base URL for the app
74
+ */
75
+ function setAppUrl(url) {
76
+ storage.set(APP_URL_KEY, url);
77
+ }
78
+ /**
79
+ * Get the base URL for the app
80
+ * @returns Configured app URL or empty string
81
+ */
82
+ function getAppUrl() {
83
+ return storage.getString(APP_URL_KEY) || "";
84
+ }
85
+ /**
86
+ * Utility functions
87
+ */
88
+ exports.Utils = {
89
+ /**
90
+ * Get the ID of the currently logged-in user.
91
+ */
92
+ getUserId: () => __awaiter(void 0, void 0, void 0, function* () {
93
+ try {
94
+ const user = yield (0, auth_1.getCurrentUser)();
95
+ return user.userId;
96
+ }
97
+ catch (e) {
98
+ console.error("Failed to get user ID:", e);
99
+ throw new Error("Could not retrieve user authentication information.");
100
+ }
101
+ }),
102
+ /**
103
+ * Timestamp formatting function
104
+ */
105
+ formatTimestamp: (date = new Date()) => {
106
+ return date.toISOString();
107
+ },
108
+ /**
109
+ * Extract YYYY-MM-DD format from date string
110
+ */
111
+ getDateString: (dateStr) => {
112
+ if (!dateStr)
113
+ return "";
114
+ return dateStr.split("T")[0];
115
+ },
116
+ /**
117
+ * Utility function to remove the owner field and print a warning
118
+ * @param data Data object to process
119
+ * @param operation Operation being performed (create or update)
120
+ * @returns Data object with owner field removed
121
+ */
122
+ removeOwnerField: (data, operation) => {
123
+ const { owner } = data, dataWithoutOwner = __rest(data, ["owner"]);
124
+ if (owner) {
125
+ console.warn(`The owner field exists. This field is added automatically and will be excluded from the ${operation} operation.`);
126
+ }
127
+ return dataWithoutOwner;
128
+ },
129
+ };
130
+ /**
131
+ * Storage related utilities
132
+ */
133
+ exports.StorageService = {
134
+ // URL cache internal type definition
135
+ _types: {
136
+ // URL cache interface (used internally only)
137
+ Item: {},
138
+ // Cache data structure (stored as key-value)
139
+ CacheData: {},
140
+ },
141
+ // Memory cache (for fast access)
142
+ _urlCache: new Map(),
143
+ // MMKV cache storage key
144
+ _CACHE_KEY: "storage_url_cache",
145
+ // Initialization status
146
+ _initialized: false,
147
+ /**
148
+ * Initialize the memory cache.
149
+ * Loads URL cache from MMKV storage.
150
+ */
151
+ _initCache: () => {
152
+ if (exports.StorageService._initialized)
153
+ return;
154
+ try {
155
+ // Retrieve URL cache data from MMKV
156
+ const cachedData = storage.getString(exports.StorageService._CACHE_KEY);
157
+ // Restore to memory cache
158
+ if (cachedData && cachedData.length > 0) {
159
+ const parsedData = JSON.parse(cachedData);
160
+ const now = Date.now();
161
+ // Add only non-expired items to memory cache
162
+ Object.entries(parsedData).forEach(([key, item]) => {
163
+ if (item.expiresAt > now) {
164
+ exports.StorageService._urlCache.set(key, {
165
+ url: item.url,
166
+ expiresAt: item.expiresAt,
167
+ });
168
+ }
169
+ });
170
+ }
171
+ exports.StorageService._initialized = true;
172
+ }
173
+ catch (error) {
174
+ console.error("URL cache initialization error:", error);
175
+ // Clear cache on error
176
+ exports.StorageService.clearUrlCache();
177
+ exports.StorageService._initialized = true;
178
+ }
179
+ },
180
+ /**
181
+ * Save cache to MMKV storage.
182
+ */
183
+ _saveCache: () => {
184
+ try {
185
+ const cacheData = {};
186
+ // Convert memory cache to persistent storage format
187
+ exports.StorageService._urlCache.forEach((item, key) => {
188
+ cacheData[key] = {
189
+ url: item.url,
190
+ expiresAt: item.expiresAt,
191
+ };
192
+ });
193
+ // Save to MMKV
194
+ storage.set(exports.StorageService._CACHE_KEY, JSON.stringify(cacheData));
195
+ }
196
+ catch (error) {
197
+ console.error("URL cache save error:", error);
198
+ }
199
+ },
200
+ /**
201
+ * Upload an image file to Storage.
202
+ * @param file File to upload (Blob or File object)
203
+ * @param key Path and filename to save as (auto-generated if not specified)
204
+ * @returns Key of the uploaded file
205
+ */
206
+ uploadImage: (file, key) => __awaiter(void 0, void 0, void 0, function* () {
207
+ try {
208
+ const fileKey = key || `images/${(0, expo_crypto_1.randomUUID)()}.${file.type.split("/")[1] || "jpg"}`;
209
+ const result = yield (0, storage_1.uploadData)({
210
+ path: fileKey,
211
+ data: file,
212
+ options: {
213
+ contentType: file.type,
214
+ },
215
+ }).result;
216
+ return result.path;
217
+ }
218
+ catch (error) {
219
+ console.error("File upload failed:", error);
220
+ throw error;
221
+ }
222
+ }),
223
+ /**
224
+ * Get the URL of a stored file. (Auto-caching)
225
+ * @param key File key
226
+ * @param options Caching options (forceRefresh: ignore cache and fetch new URL)
227
+ * @returns File URL
228
+ */
229
+ getFileUrl: (key, options) => __awaiter(void 0, void 0, void 0, function* () {
230
+ try {
231
+ // Initialize cache (only runs on first call)
232
+ if (!exports.StorageService._initialized) {
233
+ exports.StorageService._initCache();
234
+ }
235
+ // If not ignoring cache and URL is in cache, return from cache
236
+ const cachedItem = exports.StorageService._urlCache.get(key);
237
+ const now = Date.now();
238
+ // If cached URL exists, is not expired, and not forced refresh
239
+ if (cachedItem && cachedItem.expiresAt > now && !(options === null || options === void 0 ? void 0 : options.forceRefresh)) {
240
+ return cachedItem.url;
241
+ }
242
+ // If not in cache, expired, or forced refresh, get new URL
243
+ // Number of seconds till the URL expires.
244
+ // The expiration time of the presigned url is dependent on the session and will max out at 1 hour.
245
+ const result = yield (0, storage_1.getUrl)({
246
+ path: key,
247
+ options: {
248
+ // Default 1 hour expiration, adjustable if needed
249
+ expiresIn: 60 * 60, // 1 hour
250
+ },
251
+ });
252
+ const url = result.url.toString();
253
+ // Convert expiration time to timestamp
254
+ const expiresAt = result.expiresAt.getTime();
255
+ // Save to cache
256
+ exports.StorageService._urlCache.set(key, {
257
+ url,
258
+ expiresAt,
259
+ });
260
+ // Also save to persistent storage
261
+ exports.StorageService._saveCache();
262
+ return url;
263
+ }
264
+ catch (error) {
265
+ console.error("Failed to get file URL:", error);
266
+ throw error;
267
+ }
268
+ }),
269
+ /**
270
+ * Delete a stored file.
271
+ * @param key Key of the file to delete
272
+ */
273
+ deleteFile: (key) => __awaiter(void 0, void 0, void 0, function* () {
274
+ try {
275
+ yield (0, storage_1.remove)({ path: key });
276
+ // Remove from cache on file deletion
277
+ exports.StorageService._urlCache.delete(key);
278
+ // Update persistent storage
279
+ exports.StorageService._saveCache();
280
+ }
281
+ catch (error) {
282
+ console.error("File deletion failed:", error);
283
+ throw error;
284
+ }
285
+ }),
286
+ /**
287
+ * Clear the URL cache.
288
+ */
289
+ clearUrlCache: () => {
290
+ exports.StorageService._urlCache.clear();
291
+ storage.delete(exports.StorageService._CACHE_KEY);
292
+ },
293
+ /**
294
+ * Remove a specific key's URL cache.
295
+ * @param key Key of the URL to remove
296
+ */
297
+ clearUrlCacheForKey: (key) => {
298
+ exports.StorageService._urlCache.delete(key);
299
+ // Update persistent storage
300
+ exports.StorageService._saveCache();
301
+ },
302
+ /**
303
+ * Remove only expired URL caches.
304
+ */
305
+ clearExpiredUrlCache: () => {
306
+ const now = Date.now();
307
+ for (const [key, item] of exports.StorageService._urlCache.entries()) {
308
+ if (item.expiresAt <= now) {
309
+ exports.StorageService._urlCache.delete(key);
310
+ }
311
+ }
312
+ // Update persistent storage
313
+ exports.StorageService._saveCache();
314
+ },
315
+ /**
316
+ * Download an audio file.
317
+ * @param audioKey Key of the audio file to download
318
+ * @returns Local file system path of the downloaded file
319
+ */
320
+ downloadAudioFile: (audioKey) => __awaiter(void 0, void 0, void 0, function* () {
321
+ try {
322
+ // Create directory path where the file will be saved
323
+ const audioDir = `${FileSystem.cacheDirectory}sounds/`;
324
+ const localFilePath = `${audioDir}${audioKey}`;
325
+ // Check if directory exists, create if not
326
+ const dirInfo = yield FileSystem.getInfoAsync(audioDir);
327
+ if (!dirInfo.exists) {
328
+ yield FileSystem.makeDirectoryAsync(audioDir, { intermediates: true });
329
+ }
330
+ // Check if file already exists
331
+ const fileInfo = yield FileSystem.getInfoAsync(localFilePath);
332
+ if (fileInfo.exists) {
333
+ console.log("Audio file already exists locally:", localFilePath);
334
+ return localFilePath;
335
+ }
336
+ // Get file URL from S3
337
+ const s3Url = yield (0, storage_1.getUrl)({ path: `public/sound/${audioKey}` });
338
+ // Download file
339
+ const downloadResult = yield FileSystem.downloadAsync(s3Url.url.toString(), localFilePath);
340
+ console.log("Audio file downloaded successfully:", downloadResult.uri);
341
+ return downloadResult.uri;
342
+ }
343
+ catch (error) {
344
+ console.error("Audio file download failed:", error);
345
+ throw error;
346
+ }
347
+ }),
348
+ };
349
+ /**
350
+ * Authentication related utilities
351
+ */
352
+ exports.AuthService = {
353
+ /**
354
+ * Get information about the currently logged-in user.
355
+ */
356
+ getCurrentUserInfo: () => __awaiter(void 0, void 0, void 0, function* () {
357
+ try {
358
+ const user = yield (0, auth_1.getCurrentUser)();
359
+ return {
360
+ userId: user.userId,
361
+ username: user.username,
362
+ };
363
+ }
364
+ catch (e) {
365
+ console.error("Error getting current user info:", e);
366
+ throw new Error("Could not retrieve user authentication information.");
367
+ }
368
+ }),
369
+ };
370
+ /**
371
+ * Utility to create a relational query hook.
372
+ * Creates a hook to query related items based on a specific foreign key.
373
+ *
374
+ * @param service Base service object
375
+ * @param relationName Name of the relation (e.g., Daily, User)
376
+ * @param queryName API query name (e.g., listMissionsByDaily)
377
+ * @param idParamName ID parameter name (e.g., dailyId, userId)
378
+ * @returns Relational query hook function
379
+ */
380
+ function createRelationalHook(service, relationName, queryName, idParamName = `${relationName.toLowerCase()}Id`) {
381
+ return (id) => {
382
+ // Create query key - set dedicated cache key for specific relation ID
383
+ const queryKey = [
384
+ service.modelName,
385
+ relationName,
386
+ id, // Explicitly include relation ID in query key for cache separation
387
+ "query",
388
+ queryName,
389
+ JSON.stringify({ [idParamName]: id }),
390
+ ];
391
+ // Get only CRUD methods from existing hook
392
+ const baseHook = service.useHook({
393
+ initialFetchOptions: { fetch: false },
394
+ });
395
+ // Execute query to fetch actual data
396
+ const { data = [], isLoading, error, refetch, } = (0, react_query_1.useQuery)({
397
+ queryKey,
398
+ queryFn: () => service.customList(queryName, { [idParamName]: id }),
399
+ enabled: !!id, // Enable query only when ID exists
400
+ // Configure caching for optimized data per relation ID
401
+ staleTime: 1000 * 30, // Keep fresh for 30 seconds
402
+ gcTime: 1000 * 60 * 5, // Keep in cache for 5 minutes
403
+ });
404
+ // Custom CRUD methods specific to the ID
405
+ const createItem = (0, react_1.useCallback)((data) => __awaiter(this, void 0, void 0, function* () {
406
+ try {
407
+ // Add relation ID field
408
+ const dataWithRelation = Object.assign(Object.assign({}, data), { [idParamName]: id });
409
+ // Auto-update cache per relation ID after creation
410
+ const result = yield service.create(dataWithRelation);
411
+ // Force refresh list per relation ID
412
+ yield refetch();
413
+ return result;
414
+ }
415
+ catch (error) {
416
+ console.error(`🍬 ${service.modelName} relational create error:`, error);
417
+ throw error;
418
+ }
419
+ }), [service, id, refetch]);
420
+ // Integrate data and loading state from useQuery, methods from baseHook
421
+ return {
422
+ items: data,
423
+ isLoading,
424
+ error,
425
+ getItem: baseHook.getItem,
426
+ refresh: () => __awaiter(this, void 0, void 0, function* () {
427
+ console.log(`🍬 ${service.modelName} relational refresh called`, id);
428
+ const { data } = yield refetch({ throwOnError: true });
429
+ return data || [];
430
+ }),
431
+ create: createItem, // ID-specific method
432
+ update: baseHook.update,
433
+ delete: baseHook.delete,
434
+ customList: baseHook.customList,
435
+ };
436
+ };
437
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "amplifyquery",
3
+ "version": "1.0.0",
4
+ "description": "Amplify+Query",
5
+ "keywords": [
6
+ "Amplify",
7
+ "React Query",
8
+ "TypeScript"
9
+ ],
10
+ "homepage": "https://github.com/Sun-Woo-Kim/AmplifyQuery#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/Sun-Woo-Kim/AmplifyQuery/issues"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Sun-Woo-Kim/AmplifyQuery.git"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Sun-Woo-Kim",
20
+ "type": "commonjs",
21
+ "main": "dist/index.js",
22
+ "types": "dist/index.d.ts",
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "clean": "rm -rf dist",
29
+ "dev": "tsc --watch",
30
+ "prepare": "npm run build",
31
+ "test": "echo \"Error: no test specified\" && exit 1"
32
+ },
33
+ "dependencies": {
34
+ "@aws-amplify/api": "^6.3.11",
35
+ "@aws-amplify/core": "^6.11.4",
36
+ "@aws-amplify/datastore": "^5.0.82",
37
+ "@babel/core": "^7.27.1",
38
+ "@react-native-async-storage/async-storage": "^2.1.2",
39
+ "@tanstack/react-query": "^5.76.1",
40
+ "@tanstack/react-query-persist-client": "^5.76.1",
41
+ "@types/react": "^19.1.4",
42
+ "amplify": "^0.0.11",
43
+ "aws-amplify": "^6.14.4",
44
+ "expo": "^53.0.9",
45
+ "expo-crypto": "^14.1.4",
46
+ "expo-file-system": "^18.1.10",
47
+ "react": "^19.1.0",
48
+ "react-native": "^0.79.2",
49
+ "react-native-mmkv": "^3.2.0",
50
+ "typescript": "^5.8.3",
51
+ "zustand": "^5.0.4"
52
+ }
53
+ }