@mohasinac/appkit 3.2.9 → 3.2.10

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.
@@ -10,6 +10,7 @@
10
10
  * <AsyncFacetSection loadOptions={load} ... />
11
11
  */
12
12
  import { apiClient } from "../../../../http/ApiClient";
13
+ import { normalizeError } from "../../../../errors/normalize";
13
14
  const PAGE_SIZE = 25;
14
15
  function buildUrl(base, q, page, extra) {
15
16
  const params = new URLSearchParams({
@@ -29,7 +30,7 @@ async function fetchPage(url, mapItem) {
29
30
  };
30
31
  }
31
32
  catch (_err) {
32
- // PaginatedSelect degrades to an empty page on load error — caller can retry by re-typing
33
+ void normalizeError(_err); // PaginatedSelect degrades to empty page; caller can retry by re-typing
33
34
  return { items: [], hasMore: false };
34
35
  }
35
36
  }
@@ -20,6 +20,9 @@
20
20
  * ```
21
21
  */
22
22
  import { getAdminStorageLite } from "../db-firebase/admin-storage-lite";
23
+ import { withRetry } from "../../http/retry";
24
+ import { normalizeError } from "../../errors/normalize";
25
+ import { serverLogger } from "../../monitoring/server-logger";
23
26
  function getBucket() {
24
27
  const bucketName = process.env.FIREBASE_ADMIN_STORAGE_BUCKET?.trim() ??
25
28
  process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET?.trim();
@@ -29,75 +32,126 @@ function getBucket() {
29
32
  }
30
33
  export const firebaseStorageProvider = {
31
34
  async upload(file, storagePath, options) {
32
- const bucket = getBucket();
33
- const fileRef = bucket.file(storagePath);
34
- await fileRef.save(file, {
35
- metadata: {
36
- contentType: options?.contentType ?? "application/octet-stream",
37
- cacheControl: options?.cacheControl ?? "public, max-age=31536000",
38
- metadata: options?.metadata ?? {},
39
- },
40
- public: options?.isPublic !== false,
41
- });
42
- const [metadata] = await fileRef.getMetadata();
43
- const url = options?.isPublic !== false
44
- ? `https://storage.googleapis.com/${bucket.name}/${storagePath}`
45
- : await fileRef
46
- .getSignedUrl({
47
- action: "read",
48
- expires: Date.now() + 60 * 60 * 1000, // 1 hour
49
- })
50
- .then(([u]) => u);
51
- return {
52
- path: storagePath,
53
- url,
54
- contentType: metadata.contentType,
55
- size: Number(metadata.size),
56
- updatedAt: metadata.updated,
57
- };
35
+ try {
36
+ const bucket = getBucket();
37
+ const fileRef = bucket.file(storagePath);
38
+ await withRetry(() => fileRef.save(file, {
39
+ metadata: {
40
+ contentType: options?.contentType ?? "application/octet-stream",
41
+ cacheControl: options?.cacheControl ?? "public, max-age=31536000",
42
+ metadata: options?.metadata ?? {},
43
+ },
44
+ public: options?.isPublic !== false,
45
+ }), 2, 300);
46
+ const [metadata] = await withRetry(() => fileRef.getMetadata(), 2, 300);
47
+ const url = options?.isPublic !== false
48
+ ? `https://storage.googleapis.com/${bucket.name}/${storagePath}`
49
+ : await withRetry(() => fileRef
50
+ .getSignedUrl({
51
+ action: "read",
52
+ expires: Date.now() + 60 * 60 * 1000,
53
+ })
54
+ .then(([u]) => u), 2, 300);
55
+ return {
56
+ path: storagePath,
57
+ url,
58
+ contentType: metadata.contentType,
59
+ size: Number(metadata.size),
60
+ updatedAt: metadata.updated,
61
+ };
62
+ }
63
+ catch (err) {
64
+ void normalizeError(err);
65
+ serverLogger.error("storage-provider: upload failed", {
66
+ storagePath,
67
+ error: err instanceof Error ? err.message : String(err),
68
+ });
69
+ throw err;
70
+ }
58
71
  },
59
72
  async delete(storagePath) {
60
- await getBucket().file(storagePath).delete({ ignoreNotFound: true });
73
+ try {
74
+ await withRetry(() => getBucket().file(storagePath).delete({ ignoreNotFound: true }), 2, 300);
75
+ }
76
+ catch (err) {
77
+ void normalizeError(err);
78
+ serverLogger.error("storage-provider: delete failed", {
79
+ storagePath,
80
+ error: err instanceof Error ? err.message : String(err),
81
+ });
82
+ throw err;
83
+ }
61
84
  },
62
85
  getPublicUrl(storagePath) {
63
86
  const bucket = getBucket();
64
87
  return `https://storage.googleapis.com/${bucket.name}/${storagePath}`;
65
88
  },
66
89
  async getSignedUrl(storagePath, expiresInSeconds = 3600) {
67
- const [url] = await getBucket()
68
- .file(storagePath)
69
- .getSignedUrl({
70
- action: "read",
71
- expires: Date.now() + expiresInSeconds * 1000,
72
- });
73
- return url;
90
+ try {
91
+ const [url] = await withRetry(() => getBucket()
92
+ .file(storagePath)
93
+ .getSignedUrl({
94
+ action: "read",
95
+ expires: Date.now() + expiresInSeconds * 1000,
96
+ }), 2, 300);
97
+ return url;
98
+ }
99
+ catch (err) {
100
+ void normalizeError(err);
101
+ serverLogger.error("storage-provider: getSignedUrl failed", {
102
+ storagePath,
103
+ error: err instanceof Error ? err.message : String(err),
104
+ });
105
+ throw err;
106
+ }
74
107
  },
75
108
  async copy(from, to) {
76
- const bucket = getBucket();
77
- const src = bucket.file(from);
78
- const dest = bucket.file(to);
79
- await src.copy(dest);
80
- const [metadata] = await dest.getMetadata();
81
- return {
82
- path: to,
83
- url: `https://storage.googleapis.com/${bucket.name}/${to}`,
84
- contentType: metadata.contentType,
85
- size: Number(metadata.size),
86
- updatedAt: metadata.updated,
87
- };
88
- },
89
- async list(prefix) {
90
- const bucket = getBucket();
91
- const [files] = await bucket.getFiles({ prefix });
92
- return Promise.all(files.map(async (f) => {
93
- const [metadata] = await f.getMetadata();
109
+ try {
110
+ const bucket = getBucket();
111
+ const src = bucket.file(from);
112
+ const dest = bucket.file(to);
113
+ await withRetry(() => src.copy(dest), 2, 300);
114
+ const [metadata] = await withRetry(() => dest.getMetadata(), 2, 300);
94
115
  return {
95
- path: f.name,
96
- url: `https://storage.googleapis.com/${bucket.name}/${f.name}`,
116
+ path: to,
117
+ url: `https://storage.googleapis.com/${bucket.name}/${to}`,
97
118
  contentType: metadata.contentType,
98
119
  size: Number(metadata.size),
99
120
  updatedAt: metadata.updated,
100
121
  };
101
- }));
122
+ }
123
+ catch (err) {
124
+ void normalizeError(err);
125
+ serverLogger.error("storage-provider: copy failed", {
126
+ from,
127
+ to,
128
+ error: err instanceof Error ? err.message : String(err),
129
+ });
130
+ throw err;
131
+ }
132
+ },
133
+ async list(prefix) {
134
+ try {
135
+ const bucket = getBucket();
136
+ const [files] = await withRetry(() => bucket.getFiles({ prefix }), 2, 300);
137
+ return Promise.all(files.map(async (f) => {
138
+ const [metadata] = await withRetry(() => f.getMetadata(), 2, 300);
139
+ return {
140
+ path: f.name,
141
+ url: `https://storage.googleapis.com/${bucket.name}/${f.name}`,
142
+ contentType: metadata.contentType,
143
+ size: Number(metadata.size),
144
+ updatedAt: metadata.updated,
145
+ };
146
+ }));
147
+ }
148
+ catch (err) {
149
+ void normalizeError(err);
150
+ serverLogger.error("storage-provider: list failed", {
151
+ prefix,
152
+ error: err instanceof Error ? err.message : String(err),
153
+ });
154
+ throw err;
155
+ }
102
156
  },
103
157
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "3.2.9",
3
+ "version": "3.2.10",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"