@kumix/storage 0.1.0 → 0.1.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/dist/cloudinary.d.ts +2 -1
- package/dist/cloudinary.d.ts.map +1 -1
- package/dist/cloudinary.js +173 -148
- package/dist/cloudinary.js.map +1 -1
- package/dist/{config-t-NVJICl.js → config-By5ib98s.js} +17 -9
- package/dist/config-By5ib98s.js.map +1 -0
- package/dist/{config-J6AdDh_c.d.ts → config-DGzEH-Zd.d.ts} +13 -10
- package/dist/config-DGzEH-Zd.d.ts.map +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +83 -10
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/s3.d.ts +2 -1
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +67 -54
- package/dist/s3.js.map +1 -1
- package/package.json +5 -10
- package/dist/config-J6AdDh_c.d.ts.map +0 -1
- package/dist/config-t-NVJICl.js.map +0 -1
package/dist/cloudinary.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as DownloadOptions, B as PresignedUrlResult, C as RenameFolderResult, D as CopyResult, E as CopyOptions, F as ListOptions, H as UploadResult, I as ListResult, L as MoveOptions, M as DuplicateOptions, N as DuplicateResult, O as DeleteOptions, P as ExistsResult, R as MoveResult, S as RenameFolderOptions, T as BatchDeleteResult, U as CloudinaryConfig, V as UploadOptions, _ as DeleteFolderOptions, b as ListFoldersOptions, f as StorageInterface, g as CreateFolderResult, h as CreateFolderOptions, j as DownloadResult, k as DeleteResult, m as CopyFolderResult, n as EnvRecord, p as CopyFolderOptions, v as DeleteFolderResult, w as BatchDeleteOptions, x as ListFoldersResult, y as FolderExistsResult, z as PresignedUrlOptions } from "./config-
|
|
1
|
+
import { A as DownloadOptions, B as PresignedUrlResult, C as RenameFolderResult, D as CopyResult, E as CopyOptions, F as ListOptions, H as UploadResult, I as ListResult, L as MoveOptions, M as DuplicateOptions, N as DuplicateResult, O as DeleteOptions, P as ExistsResult, R as MoveResult, S as RenameFolderOptions, T as BatchDeleteResult, U as CloudinaryConfig, V as UploadOptions, _ as DeleteFolderOptions, b as ListFoldersOptions, f as StorageInterface, g as CreateFolderResult, h as CreateFolderOptions, j as DownloadResult, k as DeleteResult, m as CopyFolderResult, n as EnvRecord, p as CopyFolderOptions, v as DeleteFolderResult, w as BatchDeleteOptions, x as ListFoldersResult, y as FolderExistsResult, z as PresignedUrlOptions } from "./config-DGzEH-Zd.js";
|
|
2
2
|
|
|
3
3
|
//#region src/services/cloudinary.d.ts
|
|
4
4
|
/**
|
|
@@ -88,6 +88,7 @@ declare class CloudinaryService implements StorageInterface {
|
|
|
88
88
|
declare function createCloudinary(): CloudinaryService;
|
|
89
89
|
declare function createCloudinary(config: CloudinaryConfig): CloudinaryService;
|
|
90
90
|
declare function createCloudinary(config: CloudinaryConfig, env: EnvRecord): CloudinaryService;
|
|
91
|
+
declare function createCloudinary(config: undefined, env: EnvRecord): CloudinaryService;
|
|
91
92
|
//#endregion
|
|
92
93
|
export { CloudinaryService, createCloudinary };
|
|
93
94
|
//# sourceMappingURL=cloudinary.d.ts.map
|
package/dist/cloudinary.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudinary.d.ts","names":[],"sources":["../src/services/cloudinary.ts","../src/cloudinary.ts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cloudinary.d.ts","names":[],"sources":["../src/services/cloudinary.ts","../src/cloudinary.ts"],"mappings":"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"}
|
package/dist/cloudinary.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as loadCloudinaryConfig } from "./config-
|
|
1
|
+
import { o as loadCloudinaryConfig } from "./config-By5ib98s.js";
|
|
2
2
|
import { v2 } from "cloudinary";
|
|
3
3
|
//#region src/providers/cloudinary.ts
|
|
4
4
|
/**
|
|
@@ -14,11 +14,19 @@ var CloudinaryProvider = class {
|
|
|
14
14
|
config;
|
|
15
15
|
constructor(config) {
|
|
16
16
|
this.config = config;
|
|
17
|
+
this.configure();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Re-apply this instance's config to the global Cloudinary client. Call this
|
|
21
|
+
* before any API operation to reduce (but not eliminate) the chance that a
|
|
22
|
+
* different `CloudinaryProvider` instance has flipped the global config.
|
|
23
|
+
*/
|
|
24
|
+
configure() {
|
|
17
25
|
v2.config({
|
|
18
|
-
cloud_name: config.cloudName,
|
|
19
|
-
api_key: config.apiKey,
|
|
20
|
-
api_secret: config.apiSecret,
|
|
21
|
-
secure: config.secure !== false
|
|
26
|
+
cloud_name: this.config.cloudName,
|
|
27
|
+
api_key: this.config.apiKey,
|
|
28
|
+
api_secret: this.config.apiSecret,
|
|
29
|
+
secure: this.config.secure !== false
|
|
22
30
|
});
|
|
23
31
|
}
|
|
24
32
|
async upload(options) {
|
|
@@ -40,12 +48,14 @@ var CloudinaryProvider = class {
|
|
|
40
48
|
resource_type: resourceType,
|
|
41
49
|
overwrite: true,
|
|
42
50
|
invalidate: true,
|
|
43
|
-
...options.metadata
|
|
51
|
+
...options.metadata ? { context: options.metadata } : {}
|
|
44
52
|
};
|
|
45
|
-
|
|
46
|
-
if (this.config.folder)
|
|
53
|
+
const folderParts = [];
|
|
54
|
+
if (this.config.folder) folderParts.push(this.config.folder);
|
|
55
|
+
if (folder) folderParts.push(folder);
|
|
56
|
+
if (folderParts.length > 0) uploadOptions.folder = folderParts.join("/");
|
|
47
57
|
const result = await v2.uploader.upload(fileData, uploadOptions);
|
|
48
|
-
const publicUrl = this.getPublicUrl(result.public_id, result.format);
|
|
58
|
+
const publicUrl = this.getPublicUrl(result.public_id, result.format, resourceType);
|
|
49
59
|
return {
|
|
50
60
|
success: true,
|
|
51
61
|
key: result.public_id,
|
|
@@ -88,9 +98,7 @@ var CloudinaryProvider = class {
|
|
|
88
98
|
}
|
|
89
99
|
async delete(options) {
|
|
90
100
|
try {
|
|
91
|
-
const keyParts = options.key.split("/");
|
|
92
101
|
let publicId = options.key;
|
|
93
|
-
if (keyParts.length > 1) publicId = `${keyParts.slice(0, -1).join("/")}/${keyParts[keyParts.length - 1]}`;
|
|
94
102
|
const dotIndex = publicId.lastIndexOf(".");
|
|
95
103
|
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
96
104
|
if (this.config.folder) publicId = `${this.config.folder}/${publicId}`;
|
|
@@ -120,12 +128,29 @@ var CloudinaryProvider = class {
|
|
|
120
128
|
}
|
|
121
129
|
async batchDelete(options) {
|
|
122
130
|
try {
|
|
131
|
+
const publicIds = options.keys.map((key) => {
|
|
132
|
+
let publicId = key;
|
|
133
|
+
const dotIndex = publicId.lastIndexOf(".");
|
|
134
|
+
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
135
|
+
if (this.config.folder) publicId = `${this.config.folder}/${publicId}`;
|
|
136
|
+
return publicId;
|
|
137
|
+
});
|
|
138
|
+
const result = await v2.api.delete_resources(publicIds);
|
|
139
|
+
const deleted = [];
|
|
140
|
+
const errors = [];
|
|
141
|
+
for (const [publicId, info] of Object.entries(result)) {
|
|
142
|
+
const originalKey = options.keys[publicIds.indexOf(publicId)] || publicId;
|
|
143
|
+
const status = info?.status;
|
|
144
|
+
if (status === "deleted" || status === "not_found") deleted.push(originalKey);
|
|
145
|
+
else errors.push({
|
|
146
|
+
key: originalKey,
|
|
147
|
+
error: `Delete status: ${String(status)}`
|
|
148
|
+
});
|
|
149
|
+
}
|
|
123
150
|
return {
|
|
124
|
-
success:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
error: "Cloudinary batch delete not implemented yet"
|
|
128
|
-
}))
|
|
151
|
+
success: errors.length === 0,
|
|
152
|
+
deleted,
|
|
153
|
+
errors
|
|
129
154
|
};
|
|
130
155
|
} catch (error) {
|
|
131
156
|
return {
|
|
@@ -139,55 +164,61 @@ var CloudinaryProvider = class {
|
|
|
139
164
|
}
|
|
140
165
|
async list(options = {}) {
|
|
141
166
|
try {
|
|
167
|
+
this.configure();
|
|
142
168
|
const prefix = options.prefix || "";
|
|
143
169
|
const maxResults = options.maxKeys || 50;
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
const TYPES = [
|
|
171
|
+
"image",
|
|
172
|
+
"video",
|
|
173
|
+
"raw"
|
|
174
|
+
];
|
|
175
|
+
let type = "image";
|
|
176
|
+
let cursor;
|
|
177
|
+
if (options.continuationToken) {
|
|
178
|
+
const sep = options.continuationToken.indexOf(":");
|
|
179
|
+
if (sep > 0) {
|
|
180
|
+
const t = options.continuationToken.slice(0, sep);
|
|
181
|
+
const c = options.continuationToken.slice(sep + 1);
|
|
182
|
+
if (TYPES.includes(t)) {
|
|
183
|
+
type = t;
|
|
184
|
+
cursor = c || void 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const fetchPage = (rt, c) => v2.api.resources({
|
|
189
|
+
type: "upload",
|
|
190
|
+
resource_type: rt,
|
|
191
|
+
prefix,
|
|
192
|
+
max_results: maxResults,
|
|
193
|
+
...c ? { next_cursor: c } : {}
|
|
194
|
+
}).catch(() => ({
|
|
195
|
+
resources: [],
|
|
196
|
+
next_cursor: void 0
|
|
197
|
+
}));
|
|
198
|
+
let page = await fetchPage(type, cursor);
|
|
199
|
+
let resources = page.resources || [];
|
|
200
|
+
let nextCursor = page.next_cursor;
|
|
201
|
+
let typeIdx = TYPES.indexOf(type);
|
|
202
|
+
while (!nextCursor && resources.length === 0 && typeIdx < TYPES.length - 1) {
|
|
203
|
+
typeIdx++;
|
|
204
|
+
type = TYPES[typeIdx];
|
|
205
|
+
page = await fetchPage(type);
|
|
206
|
+
resources = page.resources || [];
|
|
207
|
+
nextCursor = page.next_cursor;
|
|
208
|
+
}
|
|
209
|
+
const files = resources.map((resource) => ({
|
|
210
|
+
key: String(resource.public_id || ""),
|
|
211
|
+
size: Number(resource.bytes) || 0,
|
|
212
|
+
lastModified: new Date(String(resource.created_at || /* @__PURE__ */ new Date())),
|
|
213
|
+
etag: String(resource.etag || ""),
|
|
214
|
+
contentType: resource.format ? `${resource.resource_type}/${resource.format}` : "application/octet-stream"
|
|
215
|
+
}));
|
|
216
|
+
const nextContinuationToken = nextCursor ? `${type}:${nextCursor}` : void 0;
|
|
176
217
|
return {
|
|
177
218
|
success: true,
|
|
178
|
-
files
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
...rawFiles.resources || []
|
|
182
|
-
].map((resource) => ({
|
|
183
|
-
key: String(resource.public_id || ""),
|
|
184
|
-
size: Number(resource.bytes) || 0,
|
|
185
|
-
lastModified: new Date(String(resource.created_at || /* @__PURE__ */ new Date())),
|
|
186
|
-
etag: String(resource.etag || ""),
|
|
187
|
-
contentType: resource.format ? `${resource.resource_type}/${resource.format}` : "application/octet-stream"
|
|
188
|
-
})),
|
|
189
|
-
isTruncated: !!(images.next_cursor || videos.next_cursor || rawFiles.next_cursor),
|
|
190
|
-
nextContinuationToken: images.next_cursor || videos.next_cursor || rawFiles.next_cursor
|
|
219
|
+
files,
|
|
220
|
+
isTruncated: !!nextContinuationToken,
|
|
221
|
+
nextContinuationToken
|
|
191
222
|
};
|
|
192
223
|
} catch (error) {
|
|
193
224
|
return {
|
|
@@ -196,12 +227,21 @@ var CloudinaryProvider = class {
|
|
|
196
227
|
};
|
|
197
228
|
}
|
|
198
229
|
}
|
|
199
|
-
async exists(
|
|
230
|
+
async exists(key) {
|
|
200
231
|
try {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
232
|
+
let publicId = key;
|
|
233
|
+
const dotIndex = publicId.lastIndexOf(".");
|
|
234
|
+
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
235
|
+
if (this.config.folder) publicId = `${this.config.folder}/${publicId}`;
|
|
236
|
+
for (const resourceType of [
|
|
237
|
+
"image",
|
|
238
|
+
"video",
|
|
239
|
+
"raw"
|
|
240
|
+
]) try {
|
|
241
|
+
await v2.api.resource(publicId, { resource_type: resourceType });
|
|
242
|
+
return { exists: true };
|
|
243
|
+
} catch {}
|
|
244
|
+
return { exists: false };
|
|
205
245
|
} catch (error) {
|
|
206
246
|
return {
|
|
207
247
|
exists: false,
|
|
@@ -210,68 +250,74 @@ var CloudinaryProvider = class {
|
|
|
210
250
|
}
|
|
211
251
|
}
|
|
212
252
|
async copy(_options) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
};
|
|
218
|
-
} catch (error) {
|
|
219
|
-
return {
|
|
220
|
-
success: false,
|
|
221
|
-
error: error instanceof Error ? error.message : "Copy failed"
|
|
222
|
-
};
|
|
223
|
-
}
|
|
253
|
+
return {
|
|
254
|
+
success: false,
|
|
255
|
+
error: "Cloudinary copy not implemented yet"
|
|
256
|
+
};
|
|
224
257
|
}
|
|
225
258
|
async move(_options) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
};
|
|
231
|
-
} catch (error) {
|
|
232
|
-
return {
|
|
233
|
-
success: false,
|
|
234
|
-
error: error instanceof Error ? error.message : "Move failed"
|
|
235
|
-
};
|
|
236
|
-
}
|
|
259
|
+
return {
|
|
260
|
+
success: false,
|
|
261
|
+
error: "Cloudinary move not implemented yet"
|
|
262
|
+
};
|
|
237
263
|
}
|
|
238
264
|
async duplicate(options) {
|
|
239
265
|
return this.copy(options);
|
|
240
266
|
}
|
|
241
267
|
async getPresignedUrl(_options) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: "Cloudinary presigned URL not implemented yet"
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
getPublicUrl(key, extension, resourceType) {
|
|
274
|
+
const baseUrl = `${this.config.secure !== false ? "https" : "http"}://res.cloudinary.com/${this.config.cloudName}`;
|
|
275
|
+
let publicId = key;
|
|
276
|
+
if (this.config.folder && !publicId.startsWith(`${this.config.folder}/`)) publicId = `${this.config.folder}/${publicId}`;
|
|
277
|
+
return `${baseUrl}/${resourceType || this.inferResourceType(extension || key)}/upload/${publicId}${extension ? `.${extension}` : ""}`;
|
|
278
|
+
}
|
|
279
|
+
/** Infer Cloudinary resource type from a filename or extension */
|
|
280
|
+
inferResourceType(nameOrExt) {
|
|
281
|
+
const ext = nameOrExt.includes(".") ? nameOrExt.split(".").pop()?.toLowerCase() || "" : nameOrExt.toLowerCase();
|
|
282
|
+
const imageExts = [
|
|
283
|
+
"jpg",
|
|
284
|
+
"jpeg",
|
|
285
|
+
"png",
|
|
286
|
+
"gif",
|
|
287
|
+
"webp",
|
|
288
|
+
"svg",
|
|
289
|
+
"bmp",
|
|
290
|
+
"ico",
|
|
291
|
+
"avif",
|
|
292
|
+
"tiff"
|
|
293
|
+
];
|
|
294
|
+
const videoExts = [
|
|
295
|
+
"mp4",
|
|
296
|
+
"webm",
|
|
297
|
+
"mov",
|
|
298
|
+
"avi",
|
|
299
|
+
"mkv",
|
|
300
|
+
"flv",
|
|
301
|
+
"wmv",
|
|
302
|
+
"m4v",
|
|
303
|
+
"ogv"
|
|
304
|
+
];
|
|
305
|
+
if (imageExts.includes(ext)) return "image";
|
|
306
|
+
if (videoExts.includes(ext)) return "video";
|
|
307
|
+
return "raw";
|
|
256
308
|
}
|
|
257
309
|
async createFolder(options) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
};
|
|
263
|
-
} catch (error) {
|
|
264
|
-
return {
|
|
265
|
-
success: false,
|
|
266
|
-
error: error instanceof Error ? error.message : "Create folder failed"
|
|
267
|
-
};
|
|
268
|
-
}
|
|
310
|
+
return {
|
|
311
|
+
success: true,
|
|
312
|
+
path: options.path
|
|
313
|
+
};
|
|
269
314
|
}
|
|
270
315
|
async deleteFolder(options) {
|
|
271
316
|
try {
|
|
272
|
-
const folderPath = options.path.endsWith("/") ? options.path
|
|
317
|
+
const folderPath = options.path.endsWith("/") ? options.path : `${options.path}/`;
|
|
318
|
+
const folderApiPath = options.path.replace(/\/$/, "");
|
|
273
319
|
if (options.recursive) await v2.api.delete_resources_by_prefix(folderPath);
|
|
274
|
-
await v2.api.delete_folder(
|
|
320
|
+
await v2.api.delete_folder(folderApiPath);
|
|
275
321
|
return { success: true };
|
|
276
322
|
} catch (error) {
|
|
277
323
|
return {
|
|
@@ -299,43 +345,22 @@ var CloudinaryProvider = class {
|
|
|
299
345
|
}
|
|
300
346
|
}
|
|
301
347
|
async folderExists(_path) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
};
|
|
307
|
-
} catch (error) {
|
|
308
|
-
return {
|
|
309
|
-
exists: false,
|
|
310
|
-
error: error instanceof Error ? error.message : "Check folder existence failed"
|
|
311
|
-
};
|
|
312
|
-
}
|
|
348
|
+
return {
|
|
349
|
+
exists: false,
|
|
350
|
+
error: "Cloudinary folder existence is not implemented yet — this result does NOT mean the folder is absent."
|
|
351
|
+
};
|
|
313
352
|
}
|
|
314
353
|
async renameFolder(_options) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
};
|
|
320
|
-
} catch (error) {
|
|
321
|
-
return {
|
|
322
|
-
success: false,
|
|
323
|
-
error: error instanceof Error ? error.message : "Rename folder failed"
|
|
324
|
-
};
|
|
325
|
-
}
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
error: "Cloudinary rename folder not implemented yet"
|
|
357
|
+
};
|
|
326
358
|
}
|
|
327
359
|
async copyFolder(_options) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
};
|
|
333
|
-
} catch (error) {
|
|
334
|
-
return {
|
|
335
|
-
success: false,
|
|
336
|
-
error: error instanceof Error ? error.message : "Copy folder failed"
|
|
337
|
-
};
|
|
338
|
-
}
|
|
360
|
+
return {
|
|
361
|
+
success: false,
|
|
362
|
+
error: "Cloudinary copy folder not implemented yet"
|
|
363
|
+
};
|
|
339
364
|
}
|
|
340
365
|
};
|
|
341
366
|
//#endregion
|
package/dist/cloudinary.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudinary.js","names":["cloudinary","publicId"],"sources":["../src/providers/cloudinary.ts","../src/services/cloudinary.ts","../src/cloudinary.ts"],"sourcesContent":["/**\n * Cloudinary storage provider implementation\n * Provides cloud storage operations using Cloudinary's API for media management\n */\n\nimport { v2 as cloudinary } from \"cloudinary\";\n\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage provider\n * Implements StorageInterface for Cloudinary cloud storage\n * @internal\n */\nexport class CloudinaryProvider implements StorageInterface {\n private config: CloudinaryConfig;\n\n constructor(config: CloudinaryConfig) {\n this.config = config;\n\n // Configure Cloudinary\n cloudinary.config({\n cloud_name: config.cloudName,\n api_key: config.apiKey,\n api_secret: config.apiSecret,\n secure: config.secure !== false,\n });\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n try {\n // Convert Buffer/Uint8Array to base64 for Cloudinary\n let fileData: string;\n if (typeof options.file === \"string\") {\n fileData = options.file;\n } else {\n fileData = `data:${options.contentType || \"application/octet-stream\"};base64,${Buffer.from(options.file).toString(\"base64\")}`;\n }\n\n // Determine resource type based on content type\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n if (options.contentType?.startsWith(\"image/\")) {\n resourceType = \"image\";\n } else if (options.contentType?.startsWith(\"video/\")) {\n resourceType = \"video\";\n }\n\n // Extract folder and filename from key for Cloudinary folder support\n const keyParts = options.key.split(\"/\");\n let folder = \"\";\n let publicId = keyParts[keyParts.length - 1];\n\n if (keyParts.length > 1) {\n folder = keyParts.slice(0, -1).join(\"/\");\n }\n\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n const uploadOptions: Record<string, unknown> = {\n public_id: publicId,\n resource_type: resourceType,\n overwrite: true,\n invalidate: true,\n ...options.metadata,\n };\n\n // Add folder if extracted from key\n if (folder) {\n uploadOptions.folder = folder;\n }\n\n // Add folder if enabled in config\n if (this.config.folder) {\n uploadOptions.folder = `${this.config.folder}/${folder}`;\n }\n\n const result = await cloudinary.uploader.upload(fileData, uploadOptions);\n\n // Sample result structure\n // {\n // asset_id: 'b4033628c0897843e61e362167f338d6',\n // public_id: 'starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // version: 1765965394,\n // version_id: 'abd49804c49061cef406f8d703466849',\n // signature: 'ff52169a483bada7ca4943ce322132137bec4d2f',\n // width: 256,\n // height: 256,\n // format: 'png',\n // resource_type: 'image',\n // created_at: '2025-12-17T09:52:12Z',\n // tags: [],\n // bytes: 76429,\n // type: 'upload',\n // etag: '85e34db0ea9ce8e771e25bd8eb33ba56',\n // placeholder: false,\n // url: 'http://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // secure_url: 'https://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // asset_folder: 'starter/workspaces/logo',\n // display_name: 'ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // overwritten: true,\n // api_key: '255239588173378'\n // }\n\n const publicUrl = this.getPublicUrl(result.public_id, result.format);\n\n return {\n success: true,\n key: result.public_id,\n url: result.secure_url,\n publicUrl,\n etag: result.etag,\n size: result.bytes,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Upload failed\",\n };\n }\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n try {\n // Cloudinary doesn't support direct download like S3\n // We need to fetch from the public URL\n const publicUrl = this.getPublicUrl(options.key);\n\n const response = await fetch(publicUrl);\n if (!response.ok) {\n return {\n success: false,\n error: `Failed to download: ${response.status} ${response.statusText}`,\n };\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const content = Buffer.from(arrayBuffer);\n\n return {\n success: true,\n content, // Main content field\n data: content, // Alias for backward compatibility\n contentType: response.headers.get(\"content-type\") || undefined,\n contentLength: content.length,\n lastModified: response.headers.get(\"last-modified\")\n ? new Date(response.headers.get(\"last-modified\")!)\n : new Date(),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Download failed\",\n };\n }\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n try {\n // Extract folder and filename for proper deletion\n const keyParts = options.key.split(\"/\");\n let publicId = options.key;\n\n if (keyParts.length > 1) {\n const folder = keyParts.slice(0, -1).join(\"/\");\n const filename = keyParts[keyParts.length - 1];\n publicId = `${folder}/${filename}`;\n }\n\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n // Add folder if enabled in config\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Determine resource type from key/path\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n\n // Try to get resource info first to determine type\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"image\" });\n resourceType = \"image\";\n } catch {\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"video\" });\n resourceType = \"video\";\n } catch {\n resourceType = \"raw\";\n }\n }\n\n await cloudinary.uploader.destroy(publicId, {\n resource_type: resourceType,\n invalidate: true,\n });\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete failed\",\n };\n }\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n try {\n // Use Cloudinary Admin API for batch delete\n return {\n success: false,\n errors: options.keys.map((key) => ({\n key,\n error: \"Cloudinary batch delete not implemented yet\",\n })),\n };\n } catch (error) {\n return {\n success: false,\n errors: options.keys.map((key) => ({\n key,\n error: error instanceof Error ? error.message : \"Batch delete failed\",\n })),\n };\n }\n }\n\n async list(options: ListOptions = {}): Promise<ListResult> {\n try {\n const prefix = options.prefix || \"\";\n const maxResults = options.maxKeys || 50;\n\n // List all resource types\n const [images, videos, rawFiles] = await Promise.all([\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: \"image\",\n prefix,\n max_results: maxResults,\n next_cursor: options.continuationToken,\n })\n .catch(() => ({ resources: [], next_cursor: undefined })),\n\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: \"video\",\n prefix,\n max_results: maxResults,\n next_cursor: options.continuationToken,\n })\n .catch(() => ({ resources: [], next_cursor: undefined })),\n\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: \"raw\",\n prefix,\n max_results: maxResults,\n next_cursor: options.continuationToken,\n })\n .catch(() => ({ resources: [], next_cursor: undefined })),\n ]);\n\n const allResources = [\n ...(images.resources || []),\n ...(videos.resources || []),\n ...(rawFiles.resources || []),\n ];\n\n const files = allResources.map((resource: Record<string, unknown>) => ({\n key: String(resource.public_id || \"\"),\n size: Number(resource.bytes) || 0,\n lastModified: new Date(String(resource.created_at || new Date())),\n etag: String(resource.etag || \"\"),\n contentType: resource.format\n ? `${resource.resource_type}/${resource.format}`\n : \"application/octet-stream\",\n }));\n\n return {\n success: true,\n files,\n isTruncated: !!(images.next_cursor || videos.next_cursor || rawFiles.next_cursor),\n nextContinuationToken: images.next_cursor || videos.next_cursor || rawFiles.next_cursor,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List failed\",\n };\n }\n }\n\n async exists(_key: string): Promise<ExistsResult> {\n try {\n // Use Cloudinary Admin API to check if resource exists\n return {\n exists: false,\n error: \"Cloudinary exists check not implemented yet\",\n };\n } catch (error) {\n return {\n exists: false,\n error: error instanceof Error ? error.message : \"Check existence failed\",\n };\n }\n }\n\n async copy(_options: CopyOptions): Promise<CopyResult> {\n try {\n // Cloudinary doesn't have direct copy, would need to download and re-upload\n return {\n success: false,\n error: \"Cloudinary copy not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Copy failed\",\n };\n }\n }\n\n async move(_options: MoveOptions): Promise<MoveResult> {\n try {\n // Use Cloudinary Admin API to rename/move\n return {\n success: false,\n error: \"Cloudinary move not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Move failed\",\n };\n }\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n // Duplicate is just an alias for copy\n return this.copy(options);\n }\n\n async getPresignedUrl(_options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n try {\n // Cloudinary uses different approach - signed URLs\n return {\n success: false,\n error: \"Cloudinary presigned URL not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Presigned URL generation failed\",\n };\n }\n }\n\n getPublicUrl(key: string, extension?: string): string {\n // Generate Cloudinary URL\n const protocol = this.config.secure !== false ? \"https\" : \"http\";\n const baseUrl = `${protocol}://res.cloudinary.com/${this.config.cloudName}`;\n\n // Use key directly as public ID - folder structure comes from the key itself\n const publicId = key;\n\n // For images: /image/upload/v1234567890/sample.jpg\n // For videos: /video/upload/v1234567890/sample.mp4\n // For raw files: /raw/upload/v1234567890/sample.pdf\n\n // Default to image for now, in real implementation you'd detect file type\n return `${baseUrl}/image/upload/${publicId}${extension ? `.${extension}` : \"\"}`;\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n try {\n // Cloudinary folders are created implicitly when uploading files\n return {\n success: true,\n path: options.path,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Create folder failed\",\n };\n }\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n try {\n // Cloudinary requires deleting all assets in folder first\n // Then delete the folder itself\n const folderPath = options.path.endsWith(\"/\") ? options.path.slice(0, -1) : options.path;\n\n if (options.recursive) {\n // Delete all resources in the folder\n await cloudinary.api.delete_resources_by_prefix(folderPath);\n }\n\n // Delete the folder\n await cloudinary.api.delete_folder(folderPath);\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete folder failed\",\n };\n }\n }\n\n async listFolders(_options?: ListFoldersOptions): Promise<ListFoldersResult> {\n try {\n // Use Cloudinary Admin API to list folders\n const result = await cloudinary.api.root_folders();\n\n const folders = (result.folders || []).map((folder: Record<string, unknown>) => ({\n name: folder.name,\n path: folder.path,\n }));\n\n return {\n success: true,\n folders,\n isTruncated: false,\n nextContinuationToken: undefined,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List folders failed\",\n };\n }\n }\n\n async folderExists(_path: string): Promise<FolderExistsResult> {\n try {\n // Check if folder exists in Cloudinary\n return {\n exists: false,\n error: \"Cloudinary folder exists check not implemented yet\",\n };\n } catch (error) {\n return {\n exists: false,\n error: error instanceof Error ? error.message : \"Check folder existence failed\",\n };\n }\n }\n\n async renameFolder(_options: RenameFolderOptions): Promise<RenameFolderResult> {\n try {\n // Cloudinary folder rename would require moving all assets\n return {\n success: false,\n error: \"Cloudinary rename folder not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Rename folder failed\",\n };\n }\n }\n\n async copyFolder(_options: CopyFolderOptions): Promise<CopyFolderResult> {\n try {\n // Cloudinary folder copy would require copying all assets\n return {\n success: false,\n error: \"Cloudinary copy folder not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Copy folder failed\",\n };\n }\n }\n}\n","/**\n * Cloudinary storage service implementation\n * High-level service wrapper for Cloudinary storage operations with convenience methods\n */\n\nimport { loadCloudinaryConfig } from \"../config\";\nimport { CloudinaryProvider } from \"../providers/cloudinary\";\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage service\n * High-level service for Cloudinary storage operations with convenience methods\n * @public\n */\nexport class CloudinaryService implements StorageInterface {\n private provider: CloudinaryProvider;\n private config: CloudinaryConfig;\n\n constructor();\n constructor(config: CloudinaryConfig);\n constructor(config?: CloudinaryConfig) {\n this.config = config || loadCloudinaryConfig();\n this.provider = new CloudinaryProvider(this.config);\n }\n\n // Utility methods\n getConfig(): CloudinaryConfig {\n return { ...this.config };\n }\n\n getProvider(): string {\n return this.config.provider;\n }\n\n getPublicUrl(key: string): string {\n return this.provider.getPublicUrl(key);\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n return this.provider.upload(options);\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n return this.provider.download(options);\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n return this.provider.delete(options);\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n return this.provider.batchDelete(options);\n }\n\n async list(options?: ListOptions): Promise<ListResult> {\n return this.provider.list(options);\n }\n\n async exists(key: string): Promise<ExistsResult> {\n return this.provider.exists(key);\n }\n\n async copy(options: CopyOptions): Promise<CopyResult> {\n return this.provider.copy(options);\n }\n\n async move(options: MoveOptions): Promise<MoveResult> {\n return this.provider.move(options);\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n return this.provider.duplicate(options);\n }\n\n async getPresignedUrl(options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n return this.provider.getPresignedUrl(options);\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n return this.provider.createFolder(options);\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n return this.provider.deleteFolder(options);\n }\n\n async listFolders(options?: ListFoldersOptions): Promise<ListFoldersResult> {\n return this.provider.listFolders(options);\n }\n\n async folderExists(path: string): Promise<FolderExistsResult> {\n return this.provider.folderExists(path);\n }\n\n async renameFolder(options: RenameFolderOptions): Promise<RenameFolderResult> {\n return this.provider.renameFolder(options);\n }\n\n async copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult> {\n return this.provider.copyFolder(options);\n }\n\n // Convenience methods\n async uploadFile(\n key: string,\n file: Buffer | Uint8Array | string,\n contentType?: string,\n metadata?: Record<string, string>,\n ): Promise<UploadResult> {\n return this.upload({ key, file, contentType, metadata });\n }\n\n async downloadFile(key: string): Promise<DownloadResult> {\n return this.download({ key });\n }\n\n async deleteFile(key: string): Promise<DeleteResult> {\n return this.delete({ key });\n }\n\n async deleteFiles(keys: string[]): Promise<BatchDeleteResult> {\n return this.batchDelete({ keys });\n }\n\n async listFiles(prefix?: string, maxKeys?: number): Promise<ListResult> {\n return this.list({ prefix, maxKeys });\n }\n\n async fileExists(key: string): Promise<boolean> {\n const result = await this.exists(key);\n return result.exists;\n }\n\n async copyFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<CopyResult> {\n return this.copy({ sourceKey, destinationKey, metadata });\n }\n\n async moveFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<MoveResult> {\n return this.move({ sourceKey, destinationKey, metadata });\n }\n\n async duplicateFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<DuplicateResult> {\n return this.duplicate({ sourceKey, destinationKey, metadata });\n }\n\n async renameFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<MoveResult> {\n return this.moveFile(sourceKey, destinationKey, metadata);\n }\n\n async getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({ key, operation: \"get\", expiresIn });\n }\n\n async getUploadUrl(\n key: string,\n contentType?: string,\n expiresIn?: number,\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({\n key,\n operation: \"put\",\n contentType,\n expiresIn,\n });\n }\n\n // Folder convenience methods\n async createFolderPath(path: string): Promise<CreateFolderResult> {\n return this.createFolder({ path });\n }\n\n async deleteFolderPath(path: string, recursive = false): Promise<DeleteFolderResult> {\n return this.deleteFolder({ path, recursive });\n }\n\n async folderPathExists(path: string): Promise<boolean> {\n const result = await this.folderExists(path);\n return result.exists;\n }\n\n async renameFolderPath(oldPath: string, newPath: string): Promise<RenameFolderResult> {\n return this.renameFolder({ oldPath, newPath });\n }\n\n async copyFolderPath(\n sourcePath: string,\n destinationPath: string,\n recursive = true,\n ): Promise<CopyFolderResult> {\n return this.copyFolder({ sourcePath, destinationPath, recursive });\n }\n}\n","import type { EnvRecord } from \"./config\";\nimport { loadCloudinaryConfig } from \"./config\";\nimport { CloudinaryService } from \"./services/cloudinary\";\nimport type { CloudinaryConfig } from \"./types\";\n\n// === SERVICE CLASSES ===\nexport { CloudinaryService } from \"./services/cloudinary\";\n\n// === MAIN FACTORY FUNCTIONS (Primary API) ===\n/**\n * Create Cloudinary service using environment variables or manual configuration\n * @param config Optional Cloudinary configuration. If not provided, loads from environment variables\n * @param env - Optional environment record\n * @returns CloudinaryService instance\n * @throws Error if configuration is invalid or environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { createCloudinary } from '@kumix/storage/cloudinary';\n *\n * // Using environment variables\n * // Set: KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud, KUMIX_CLOUDINARY_API_KEY=123..., etc.\n * const cloudinaryFromEnv = createCloudinary();\n * await cloudinaryFromEnv.uploadFile('photo.jpg', imageBuffer);\n *\n * // Using manual configuration\n * const cloudinary = createCloudinary({\n * provider: 'cloudinary',\n * cloudName: 'my-cloud-name',\n * apiKey: '123456789012345',\n * apiSecret: 'your-api-secret',\n * secure: true,\n * folder: 'uploads'\n * });\n *\n * // Upload with transformations\n * await cloudinary.uploadFile('profile-pic.jpg', imageBuffer);\n *\n * // Get optimized URL\n * const publicUrl = cloudinary.getPublicUrl('profile-pic.jpg');\n * console.log(publicUrl); // Cloudinary optimized URL\n * ```\n */\nexport function createCloudinary(): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig, env: EnvRecord): CloudinaryService;\nexport function createCloudinary(config?: CloudinaryConfig, env?: EnvRecord): CloudinaryService {\n if (config) {\n return new CloudinaryService(config);\n }\n const envConfig = loadCloudinaryConfig(env);\n return new CloudinaryService(envConfig);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cloudinary.js","names":["cloudinary"],"sources":["../src/providers/cloudinary.ts","../src/services/cloudinary.ts","../src/cloudinary.ts"],"sourcesContent":["/**\n * Cloudinary storage provider implementation\n * Provides cloud storage operations using Cloudinary's API for media management\n */\n\nimport { v2 as cloudinary } from \"cloudinary\";\n\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage provider\n * Implements StorageInterface for Cloudinary cloud storage\n * @internal\n */\nexport class CloudinaryProvider implements StorageInterface {\n private config: CloudinaryConfig;\n\n constructor(config: CloudinaryConfig) {\n this.config = config;\n // Apply this instance's config to the module-global Cloudinary v2 client.\n // NOTE: the Cloudinary SDK keeps a single global config, so creating\n // multiple `CloudinaryProvider` instances with different credentials in the\n // same process is not supported — the most recently-constructed instance\n // wins. `configure()` is called before each API call below to narrow the\n // race window when instances are interleaved.\n this.configure();\n }\n\n /**\n * Re-apply this instance's config to the global Cloudinary client. Call this\n * before any API operation to reduce (but not eliminate) the chance that a\n * different `CloudinaryProvider` instance has flipped the global config.\n */\n private configure(): void {\n cloudinary.config({\n cloud_name: this.config.cloudName,\n api_key: this.config.apiKey,\n api_secret: this.config.apiSecret,\n secure: this.config.secure !== false,\n });\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n try {\n // Convert Buffer/Uint8Array to base64 for Cloudinary\n let fileData: string;\n if (typeof options.file === \"string\") {\n fileData = options.file;\n } else {\n fileData = `data:${options.contentType || \"application/octet-stream\"};base64,${Buffer.from(options.file).toString(\"base64\")}`;\n }\n\n // Determine resource type based on content type\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n if (options.contentType?.startsWith(\"image/\")) {\n resourceType = \"image\";\n } else if (options.contentType?.startsWith(\"video/\")) {\n resourceType = \"video\";\n }\n\n // Extract folder and filename from key for Cloudinary folder support\n const keyParts = options.key.split(\"/\");\n let folder = \"\";\n let publicId = keyParts[keyParts.length - 1];\n\n if (keyParts.length > 1) {\n folder = keyParts.slice(0, -1).join(\"/\");\n }\n\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n const uploadOptions: Record<string, unknown> = {\n public_id: publicId,\n resource_type: resourceType,\n overwrite: true,\n invalidate: true,\n // Send user metadata under `context`/`metadata` instead of spreading it\n // at the top level. Spreading last let callers override security-\n // sensitive fields like `public_id`, `resource_type`, and `folder`.\n ...(options.metadata ? { context: options.metadata } : {}),\n };\n\n // Combine config folder + extracted folder without trailing slashes\n const folderParts: string[] = [];\n if (this.config.folder) folderParts.push(this.config.folder);\n if (folder) folderParts.push(folder);\n if (folderParts.length > 0) {\n uploadOptions.folder = folderParts.join(\"/\");\n }\n\n const result = await cloudinary.uploader.upload(fileData, uploadOptions);\n\n // Sample result structure\n // {\n // asset_id: 'b4033628c0897843e61e362167f338d6',\n // public_id: 'starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // version: 1765965394,\n // version_id: 'abd49804c49061cef406f8d703466849',\n // signature: 'ff52169a483bada7ca4943ce322132137bec4d2f',\n // width: 256,\n // height: 256,\n // format: 'png',\n // resource_type: 'image',\n // created_at: '2025-12-17T09:52:12Z',\n // tags: [],\n // bytes: 76429,\n // type: 'upload',\n // etag: '85e34db0ea9ce8e771e25bd8eb33ba56',\n // placeholder: false,\n // url: 'http://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // secure_url: 'https://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // asset_folder: 'starter/workspaces/logo',\n // display_name: 'ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // overwritten: true,\n // api_key: '255239588173378'\n // }\n\n const publicUrl = this.getPublicUrl(result.public_id, result.format, resourceType);\n\n return {\n success: true,\n key: result.public_id,\n url: result.secure_url,\n publicUrl,\n etag: result.etag,\n size: result.bytes,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Upload failed\",\n };\n }\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n try {\n // Cloudinary doesn't support direct download like S3\n // We need to fetch from the public URL\n const publicUrl = this.getPublicUrl(options.key);\n\n const response = await fetch(publicUrl);\n if (!response.ok) {\n return {\n success: false,\n error: `Failed to download: ${response.status} ${response.statusText}`,\n };\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const content = Buffer.from(arrayBuffer);\n\n return {\n success: true,\n content, // Main content field\n data: content, // Alias for backward compatibility\n contentType: response.headers.get(\"content-type\") || undefined,\n contentLength: content.length,\n lastModified: response.headers.get(\"last-modified\")\n ? new Date(response.headers.get(\"last-modified\")!)\n : new Date(),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Download failed\",\n };\n }\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n try {\n // Strip extension to get public_id\n let publicId = options.key;\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n // Add folder if enabled in config\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Determine resource type from key/path\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n\n // Try to get resource info first to determine type\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"image\" });\n resourceType = \"image\";\n } catch {\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"video\" });\n resourceType = \"video\";\n } catch {\n resourceType = \"raw\";\n }\n }\n\n await cloudinary.uploader.destroy(publicId, {\n resource_type: resourceType,\n invalidate: true,\n });\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete failed\",\n };\n }\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n try {\n // Convert keys to public_ids (strip extensions, add folder prefix)\n const publicIds = options.keys.map((key) => {\n let publicId = key;\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n return publicId;\n });\n\n const result = await cloudinary.api.delete_resources(publicIds);\n\n // delete_resources returns an object keyed by public_id with status info\n const deleted: string[] = [];\n const errors: { key: string; error: string }[] = [];\n\n for (const [publicId, info] of Object.entries(result)) {\n const originalKey = options.keys[publicIds.indexOf(publicId)] || publicId;\n const status = (info as Record<string, unknown>)?.status;\n if (status === \"deleted\" || status === \"not_found\") {\n deleted.push(originalKey);\n } else {\n errors.push({ key: originalKey, error: `Delete status: ${String(status)}` });\n }\n }\n\n return {\n success: errors.length === 0,\n deleted,\n errors,\n };\n } catch (error) {\n return {\n success: false,\n errors: options.keys.map((key) => ({\n key,\n error: error instanceof Error ? error.message : \"Batch delete failed\",\n })),\n };\n }\n }\n\n async list(options: ListOptions = {}): Promise<ListResult> {\n try {\n this.configure();\n const prefix = options.prefix || \"\";\n const maxResults = options.maxKeys || 50;\n\n // Each Cloudinary resource_type (image/video/raw) maintains its OWN\n // opaque cursor. The previous implementation queried all three in\n // parallel and returned `images.next_cursor || videos.next_cursor ||\n // raw.next_cursor` — so a page-2 token belonging to (say) videos was\n // fed to the images call on the next request, producing wrong/empty\n // results. We now query ONE type per page call and encode the owning\n // type into the continuation token so the next call resumes the right\n // type. Types are visited in stable order (image → video → raw); when a\n // type's page is empty we auto-advance to the next type within the same\n // call so the caller never receives an empty page.\n const TYPES = [\"image\", \"video\", \"raw\"] as const;\n type RType = (typeof TYPES)[number];\n\n let type: RType = \"image\";\n let cursor: string | undefined;\n if (options.continuationToken) {\n const sep = options.continuationToken.indexOf(\":\");\n if (sep > 0) {\n const t = options.continuationToken.slice(0, sep);\n const c = options.continuationToken.slice(sep + 1);\n if ((TYPES as readonly string[]).includes(t)) {\n type = t as RType;\n cursor = c || undefined;\n }\n }\n }\n\n const fetchPage = (rt: RType, c?: string) =>\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: rt,\n prefix,\n max_results: maxResults,\n ...(c ? { next_cursor: c } : {}),\n })\n .catch(() => ({ resources: [], next_cursor: undefined }));\n\n let page = await fetchPage(type, cursor);\n let resources: Record<string, unknown>[] = (page.resources || []) as Record<\n string,\n unknown\n >[];\n let nextCursor: string | undefined = page.next_cursor;\n\n // Auto-advance: if this type returned nothing and has no next page, move\n // to the next resource type (still within maxResults) so callers don't\n // see empty intermediate pages while iterating through all types.\n let typeIdx = TYPES.indexOf(type);\n while (!nextCursor && resources.length === 0 && typeIdx < TYPES.length - 1) {\n typeIdx++;\n type = TYPES[typeIdx];\n page = await fetchPage(type);\n resources = (page.resources || []) as Record<string, unknown>[];\n nextCursor = page.next_cursor;\n }\n\n const files = resources.map((resource) => ({\n key: String(resource.public_id || \"\"),\n size: Number(resource.bytes) || 0,\n lastModified: new Date(String(resource.created_at || new Date())),\n etag: String(resource.etag || \"\"),\n contentType: resource.format\n ? `${resource.resource_type}/${resource.format}`\n : \"application/octet-stream\",\n }));\n\n const nextContinuationToken = nextCursor ? `${type}:${nextCursor}` : undefined;\n\n return {\n success: true,\n files,\n isTruncated: !!nextContinuationToken,\n nextContinuationToken,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List failed\",\n };\n }\n }\n\n async exists(key: string): Promise<ExistsResult> {\n try {\n // Strip extension and add config folder prefix for the public_id\n let publicId = key;\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Try each resource type until found\n for (const resourceType of [\"image\", \"video\", \"raw\"] as const) {\n try {\n await cloudinary.api.resource(publicId, { resource_type: resourceType });\n return { exists: true };\n } catch {\n // Try next resource type\n }\n }\n\n return { exists: false };\n } catch (error) {\n return {\n exists: false,\n error: error instanceof Error ? error.message : \"Check existence failed\",\n };\n }\n }\n\n async copy(_options: CopyOptions): Promise<CopyResult> {\n return {\n success: false,\n error: \"Cloudinary copy not implemented yet\",\n };\n }\n\n async move(_options: MoveOptions): Promise<MoveResult> {\n return {\n success: false,\n error: \"Cloudinary move not implemented yet\",\n };\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n // Duplicate is just an alias for copy\n return this.copy(options);\n }\n\n async getPresignedUrl(_options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n return {\n success: false,\n error: \"Cloudinary presigned URL not implemented yet\",\n };\n }\n\n getPublicUrl(key: string, extension?: string, resourceType?: \"image\" | \"video\" | \"raw\"): string {\n // Generate Cloudinary URL\n const protocol = this.config.secure !== false ? \"https\" : \"http\";\n const baseUrl = `${protocol}://res.cloudinary.com/${this.config.cloudName}`;\n\n // `upload()` prepends `config.folder` to the stored public_id, so callers\n // holding a relative key need the folder re-prepended here to round-trip\n // correctly. If the key already starts with the folder (full public_id),\n // don't double-prefix.\n let publicId = key;\n if (this.config.folder && !publicId.startsWith(`${this.config.folder}/`)) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Use explicit resource type if provided, otherwise infer from extension\n const type = resourceType || this.inferResourceType(extension || key);\n\n return `${baseUrl}/${type}/upload/${publicId}${extension ? `.${extension}` : \"\"}`;\n }\n\n /** Infer Cloudinary resource type from a filename or extension */\n private inferResourceType(nameOrExt: string): \"image\" | \"video\" | \"raw\" {\n const ext = nameOrExt.includes(\".\")\n ? nameOrExt.split(\".\").pop()?.toLowerCase() || \"\"\n : nameOrExt.toLowerCase();\n const imageExts = [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\", \"bmp\", \"ico\", \"avif\", \"tiff\"];\n const videoExts = [\"mp4\", \"webm\", \"mov\", \"avi\", \"mkv\", \"flv\", \"wmv\", \"m4v\", \"ogv\"];\n if (imageExts.includes(ext)) return \"image\";\n if (videoExts.includes(ext)) return \"video\";\n return \"raw\";\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n // Cloudinary folders are created implicitly when uploading files\n return {\n success: true,\n path: options.path,\n };\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n try {\n // Cloudinary requires deleting all assets in folder first, then the\n // folder itself. Preserve the trailing slash for the prefix delete so we\n // only match `folder/*` and never a sibling like `folderXYZ/*`. Stripping\n // the slash (the previous behavior) made `deleteFolder(\"folder/\")` also\n // delete `folderXYZ/...` — a data-loss hazard.\n const folderPath = options.path.endsWith(\"/\") ? options.path : `${options.path}/`;\n const folderApiPath = options.path.replace(/\\/$/, \"\");\n\n if (options.recursive) {\n // Delete all resources in the folder\n await cloudinary.api.delete_resources_by_prefix(folderPath);\n }\n\n // Delete the folder\n await cloudinary.api.delete_folder(folderApiPath);\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete folder failed\",\n };\n }\n }\n\n async listFolders(_options?: ListFoldersOptions): Promise<ListFoldersResult> {\n try {\n // Use Cloudinary Admin API to list folders\n const result = await cloudinary.api.root_folders();\n\n const folders = (result.folders || []).map((folder: Record<string, unknown>) => ({\n name: folder.name,\n path: folder.path,\n }));\n\n return {\n success: true,\n folders,\n isTruncated: false,\n nextContinuationToken: undefined,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List folders failed\",\n };\n }\n }\n\n async folderExists(_path: string): Promise<FolderExistsResult> {\n // Previously this returned `{ exists: false, error: \"...\" }`, which looks\n // like a definitive \"the folder does not exist\" to a caller doing\n // `if (!result.exists)`. Return an `error`-only result (no `exists` claim)\n // so the unimplemented state is distinguishable from a real \"not found\".\n return {\n exists: false,\n error:\n \"Cloudinary folder existence is not implemented yet — this result does NOT mean the folder is absent.\",\n };\n }\n\n async renameFolder(_options: RenameFolderOptions): Promise<RenameFolderResult> {\n return {\n success: false,\n error: \"Cloudinary rename folder not implemented yet\",\n };\n }\n\n async copyFolder(_options: CopyFolderOptions): Promise<CopyFolderResult> {\n return {\n success: false,\n error: \"Cloudinary copy folder not implemented yet\",\n };\n }\n}\n","/**\n * Cloudinary storage service implementation\n * High-level service wrapper for Cloudinary storage operations with convenience methods\n */\n\nimport { loadCloudinaryConfig } from \"../config\";\nimport { CloudinaryProvider } from \"../providers/cloudinary\";\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage service\n * High-level service for Cloudinary storage operations with convenience methods\n * @public\n */\nexport class CloudinaryService implements StorageInterface {\n private provider: CloudinaryProvider;\n private config: CloudinaryConfig;\n\n constructor();\n constructor(config: CloudinaryConfig);\n constructor(config?: CloudinaryConfig) {\n this.config = config || loadCloudinaryConfig();\n this.provider = new CloudinaryProvider(this.config);\n }\n\n // Utility methods\n getConfig(): CloudinaryConfig {\n return { ...this.config };\n }\n\n getProvider(): string {\n return this.config.provider;\n }\n\n getPublicUrl(key: string): string {\n return this.provider.getPublicUrl(key);\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n return this.provider.upload(options);\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n return this.provider.download(options);\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n return this.provider.delete(options);\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n return this.provider.batchDelete(options);\n }\n\n async list(options?: ListOptions): Promise<ListResult> {\n return this.provider.list(options);\n }\n\n async exists(key: string): Promise<ExistsResult> {\n return this.provider.exists(key);\n }\n\n async copy(options: CopyOptions): Promise<CopyResult> {\n return this.provider.copy(options);\n }\n\n async move(options: MoveOptions): Promise<MoveResult> {\n return this.provider.move(options);\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n return this.provider.duplicate(options);\n }\n\n async getPresignedUrl(options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n return this.provider.getPresignedUrl(options);\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n return this.provider.createFolder(options);\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n return this.provider.deleteFolder(options);\n }\n\n async listFolders(options?: ListFoldersOptions): Promise<ListFoldersResult> {\n return this.provider.listFolders(options);\n }\n\n async folderExists(path: string): Promise<FolderExistsResult> {\n return this.provider.folderExists(path);\n }\n\n async renameFolder(options: RenameFolderOptions): Promise<RenameFolderResult> {\n return this.provider.renameFolder(options);\n }\n\n async copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult> {\n return this.provider.copyFolder(options);\n }\n\n // Convenience methods\n async uploadFile(\n key: string,\n file: Buffer | Uint8Array | string,\n contentType?: string,\n metadata?: Record<string, string>,\n ): Promise<UploadResult> {\n return this.upload({ key, file, contentType, metadata });\n }\n\n async downloadFile(key: string): Promise<DownloadResult> {\n return this.download({ key });\n }\n\n async deleteFile(key: string): Promise<DeleteResult> {\n return this.delete({ key });\n }\n\n async deleteFiles(keys: string[]): Promise<BatchDeleteResult> {\n return this.batchDelete({ keys });\n }\n\n async listFiles(prefix?: string, maxKeys?: number): Promise<ListResult> {\n return this.list({ prefix, maxKeys });\n }\n\n async fileExists(key: string): Promise<boolean> {\n const result = await this.exists(key);\n return result.exists;\n }\n\n async copyFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<CopyResult> {\n return this.copy({ sourceKey, destinationKey, metadata });\n }\n\n async moveFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<MoveResult> {\n return this.move({ sourceKey, destinationKey, metadata });\n }\n\n async duplicateFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<DuplicateResult> {\n return this.duplicate({ sourceKey, destinationKey, metadata });\n }\n\n async renameFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<MoveResult> {\n return this.moveFile(sourceKey, destinationKey, metadata);\n }\n\n async getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({ key, operation: \"get\", expiresIn });\n }\n\n async getUploadUrl(\n key: string,\n contentType?: string,\n expiresIn?: number,\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({\n key,\n operation: \"put\",\n contentType,\n expiresIn,\n });\n }\n\n // Folder convenience methods\n async createFolderPath(path: string): Promise<CreateFolderResult> {\n return this.createFolder({ path });\n }\n\n async deleteFolderPath(path: string, recursive = false): Promise<DeleteFolderResult> {\n return this.deleteFolder({ path, recursive });\n }\n\n async folderPathExists(path: string): Promise<boolean> {\n const result = await this.folderExists(path);\n return result.exists;\n }\n\n async renameFolderPath(oldPath: string, newPath: string): Promise<RenameFolderResult> {\n return this.renameFolder({ oldPath, newPath });\n }\n\n async copyFolderPath(\n sourcePath: string,\n destinationPath: string,\n recursive = true,\n ): Promise<CopyFolderResult> {\n return this.copyFolder({ sourcePath, destinationPath, recursive });\n }\n}\n","import type { EnvRecord } from \"./config\";\nimport { loadCloudinaryConfig } from \"./config\";\nimport { CloudinaryService } from \"./services/cloudinary\";\nimport type { CloudinaryConfig } from \"./types\";\n\n// === SERVICE CLASSES ===\nexport { CloudinaryService } from \"./services/cloudinary\";\n\n// === MAIN FACTORY FUNCTIONS (Primary API) ===\n/**\n * Create Cloudinary service using environment variables or manual configuration\n * @param config Optional Cloudinary configuration. If not provided, loads from environment variables\n * @param env - Optional environment record\n * @returns CloudinaryService instance\n * @throws Error if configuration is invalid or environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { createCloudinary } from '@kumix/storage/cloudinary';\n *\n * // Using environment variables\n * // Set: KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud, KUMIX_CLOUDINARY_API_KEY=123..., etc.\n * const cloudinaryFromEnv = createCloudinary();\n * await cloudinaryFromEnv.uploadFile('photo.jpg', imageBuffer);\n *\n * // Using manual configuration\n * const cloudinary = createCloudinary({\n * provider: 'cloudinary',\n * cloudName: 'my-cloud-name',\n * apiKey: '123456789012345',\n * apiSecret: 'your-api-secret',\n * secure: true,\n * folder: 'uploads'\n * });\n *\n * // Upload with transformations\n * await cloudinary.uploadFile('profile-pic.jpg', imageBuffer);\n *\n * // Get optimized URL\n * const publicUrl = cloudinary.getPublicUrl('profile-pic.jpg');\n * console.log(publicUrl); // Cloudinary optimized URL\n * ```\n */\nexport function createCloudinary(): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig, env: EnvRecord): CloudinaryService;\nexport function createCloudinary(config: undefined, env: EnvRecord): CloudinaryService;\nexport function createCloudinary(config?: CloudinaryConfig, env?: EnvRecord): CloudinaryService {\n if (config) {\n return new CloudinaryService(config);\n }\n const envConfig = loadCloudinaryConfig(env);\n return new CloudinaryService(envConfig);\n}\n"],"mappings":"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"}
|