@signskart/uploader 2.0.12 → 2.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Production-grade Upload Manager SDK by **Signskart**.
4
4
 
5
- A powerful, fully-typed file upload SDK with queue management, retry logic, cancellation support, concurrency control, and multi-provider architecture (S3 + Cloudinary).
5
+ A powerful, fully-typed file upload SDK with queue management, retry logic, cancellation support, concurrency control, and multi-provider architecture (S3 + Cloudinary + Firebase Storage).
6
6
 
7
7
  ---
8
8
 
@@ -13,7 +13,7 @@ A powerful, fully-typed file upload SDK with queue management, retry logic, canc
13
13
  * 📊 Real-time progress tracking
14
14
  * ❌ Cancel uploads (AbortController support)
15
15
  * ⚡ Concurrency control
16
- * ☁ Multi-provider support (Amazon S3, Cloudinary)
16
+ * ☁ Multi-provider support (Amazon S3, Cloudinary, Firebase Storage)
17
17
  * 🧠 Fully typed (TypeScript)
18
18
  * 🌍 Works with React, Vue, Next.js, Vite, vanilla JS
19
19
  * 🔐 Secure S3 presigned upload support
@@ -51,25 +51,22 @@ Cloudinary does NOT require backend if using unsigned preset.
51
51
 
52
52
  # 🚀 Quick Start
53
53
 
54
- ---
55
-
56
- # ☁ Using Amazon S3
57
-
58
- ## 1️⃣ Frontend Setup
54
+ ## 1️⃣ Client (S3)
59
55
 
60
56
  ```ts
61
57
  import { UploadManager, S3Uploader } from '@signskart/uploader';
62
58
 
63
- const uploader = new S3Uploader({
64
- presignEndpoint: '/api/s3/presign-upload',
65
- publicUrl: 'https://cdn.yoursite.com'
66
- });
67
-
68
- const manager = new UploadManager(uploader, 2);
59
+ const manager = new UploadManager(
60
+ new S3Uploader({
61
+ apiBaseUrl: import.meta.env.VITE_APP_API_BASE_URL,
62
+ publicUrl: import.meta.env.VITE_AWS_S3_BASE_URL,
63
+ }),
64
+ 3
65
+ );
69
66
 
70
67
  const task = manager.add({
71
68
  file,
72
- folder: 'designs'
69
+ folder: 'uploads',
73
70
  });
74
71
 
75
72
  task.events.subscribe((state) => {
@@ -77,6 +74,76 @@ task.events.subscribe((state) => {
77
74
  });
78
75
  ```
79
76
 
77
+ ## 2️⃣ Server Presign Endpoint
78
+
79
+ Use `createS3PresignHandler` in your backend and expose:
80
+
81
+ `POST /api/s3/presign-upload`
82
+
83
+ Expected request body:
84
+
85
+ ```json
86
+ {
87
+ "fileName": "logo.png",
88
+ "folder": "uploads",
89
+ "contentType": "image/png"
90
+ }
91
+ ```
92
+
93
+ Expected response body:
94
+
95
+ ```json
96
+ {
97
+ "signedUrl": "https://...",
98
+ "key": "uploads/1710000000000-logo.png",
99
+ "url": "https://cdn.example.com/uploads/1710000000000-logo.png"
100
+ }
101
+ ```
102
+
103
+ ---
104
+
105
+ # ✅ Signskart Admin Pattern (Recommended)
106
+
107
+ Goal: keep upload implementation inside `@signskart/uploader`; in admin only pass env and selected file.
108
+
109
+ ## 1️⃣ Admin wrapper (env only)
110
+
111
+ ```ts
112
+ // apps/signskart-admin/src/lib/uploads/createUploadManager.ts
113
+ import { S3Uploader, UploadManager } from '@signskart/uploader';
114
+
115
+ export const createUploadManager = (concurrency = 3) => {
116
+ return new UploadManager(
117
+ new S3Uploader({
118
+ apiBaseUrl: import.meta.env.VITE_APP_API_BASE_URL,
119
+ publicUrl: import.meta.env.VITE_AWS_S3_BASE_URL,
120
+ }),
121
+ concurrency
122
+ );
123
+ };
124
+ ```
125
+
126
+ ## 2️⃣ Admin component (select file + call manager)
127
+
128
+ ```tsx
129
+ const uploadManager = useMemo(() => createUploadManager(3), []);
130
+
131
+ const handleUpload = (file: File) => {
132
+ const task = uploadManager.add({
133
+ file,
134
+ folder: 'assets/store',
135
+ });
136
+
137
+ task.events.subscribe((state) => {
138
+ if (state.status === 'success') {
139
+ console.log('Uploaded URL:', state.response?.url);
140
+ }
141
+ });
142
+ };
143
+ ```
144
+
145
+ This keeps admin thin: env + selected file only.
146
+
80
147
  ---
81
148
 
82
149
  ## 2️⃣ Backend Setup (Next.js Example)
@@ -162,6 +229,196 @@ const task = manager.add({
162
229
 
163
230
  ---
164
231
 
232
+ # 🔥 Firebase Storage Full Examples (Next.js + Node.js)
233
+
234
+ Firebase mode is different from S3 mode:
235
+
236
+ * S3: frontend uploads via backend presign endpoint.
237
+ * Firebase: frontend can upload directly with Firebase client SDK.
238
+
239
+ ## 1️⃣ Next.js Client Example (using uploader package)
240
+
241
+ Install:
242
+
243
+ ```bash
244
+ npm install @signskart/uploader firebase
245
+ ```
246
+
247
+ Create env file:
248
+
249
+ ```bash
250
+ # .env.local
251
+ NEXT_PUBLIC_FIREBASE_API_KEY=...
252
+ NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=...
253
+ NEXT_PUBLIC_FIREBASE_PROJECT_ID=...
254
+ NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=...
255
+ NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=...
256
+ NEXT_PUBLIC_FIREBASE_APP_ID=...
257
+ NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=...
258
+ ```
259
+
260
+ Create uploader helper:
261
+
262
+ ```ts
263
+ // lib/firebaseUploader.ts
264
+ import { FirebaseStorageUploader, UploadManager } from '@signskart/uploader';
265
+
266
+ export const createFirebaseUploadManager = (concurrency = 3) => {
267
+ return new UploadManager(
268
+ new FirebaseStorageUploader({
269
+ firebaseConfig: {
270
+ apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
271
+ authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
272
+ projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
273
+ storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
274
+ messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
275
+ appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
276
+ measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
277
+ },
278
+ appName: 'signskart-nextjs-uploader',
279
+ }),
280
+ concurrency
281
+ );
282
+ };
283
+ ```
284
+
285
+ Use in a client component:
286
+
287
+ ```tsx
288
+ // app/upload/page.tsx
289
+ 'use client';
290
+
291
+ import { useMemo, useState } from 'react';
292
+ import { createFirebaseUploadManager } from '@/lib/firebaseUploader';
293
+
294
+ export default function UploadPage() {
295
+ const [progress, setProgress] = useState(0);
296
+ const [uploadedUrl, setUploadedUrl] = useState('');
297
+ const [status, setStatus] = useState('idle');
298
+
299
+ const manager = useMemo(() => createFirebaseUploadManager(3), []);
300
+
301
+ const onSelectFile = async (event: React.ChangeEvent<HTMLInputElement>) => {
302
+ const file = event.target.files?.[0];
303
+ if (!file) return;
304
+
305
+ const task = manager.add({
306
+ file,
307
+ folder: 'uploads/nextjs',
308
+ metadata: { source: 'nextjs-client' },
309
+ });
310
+
311
+ task.events.subscribe((state) => {
312
+ setProgress(state.progress);
313
+ setStatus(state.status);
314
+
315
+ if (state.status === 'success') {
316
+ setUploadedUrl(state.response?.url || '');
317
+ }
318
+ });
319
+
320
+ try {
321
+ await task.start();
322
+ } catch (error) {
323
+ console.error('Upload failed:', error);
324
+ }
325
+ };
326
+
327
+ return (
328
+ <main style={{ padding: 24 }}>
329
+ <h1>Firebase Upload</h1>
330
+ <input type="file" onChange={onSelectFile} />
331
+ <p>Status: {status}</p>
332
+ <p>Progress: {progress}%</p>
333
+ {uploadedUrl ? (
334
+ <p>
335
+ File URL: <a href={uploadedUrl} target="_blank" rel="noreferrer">{uploadedUrl}</a>
336
+ </p>
337
+ ) : null}
338
+ </main>
339
+ );
340
+ }
341
+ ```
342
+
343
+ ## 2️⃣ Node.js Example (server upload flow)
344
+
345
+ `FirebaseStorageUploader` in this package is browser-oriented (uses browser upload APIs).
346
+ For pure Node.js backend upload, use Firebase Admin SDK.
347
+
348
+ Install:
349
+
350
+ ```bash
351
+ npm install express multer firebase-admin
352
+ ```
353
+
354
+ Server example:
355
+
356
+ ```ts
357
+ // server.ts
358
+ import express from 'express';
359
+ import multer from 'multer';
360
+ import admin from 'firebase-admin';
361
+
362
+ const app = express();
363
+ const upload = multer({ storage: multer.memoryStorage() });
364
+
365
+ admin.initializeApp({
366
+ credential: admin.credential.cert({
367
+ projectId: process.env.FIREBASE_PROJECT_ID,
368
+ clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
369
+ privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
370
+ }),
371
+ storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
372
+ });
373
+
374
+ const bucket = admin.storage().bucket();
375
+
376
+ app.post('/api/firebase/upload', upload.single('file'), async (req, res) => {
377
+ try {
378
+ if (!req.file) {
379
+ return res.status(400).json({ error: 'File is required' });
380
+ }
381
+
382
+ const folder = String(req.body.folder || 'uploads/node');
383
+ const objectName = `${folder}/${Date.now()}-${req.file.originalname}`;
384
+ const object = bucket.file(objectName);
385
+
386
+ await object.save(req.file.buffer, {
387
+ contentType: req.file.mimetype,
388
+ resumable: false,
389
+ metadata: {
390
+ metadata: {
391
+ source: 'node-api',
392
+ },
393
+ },
394
+ });
395
+
396
+ await object.makePublic();
397
+ const url = `https://storage.googleapis.com/${bucket.name}/${objectName}`;
398
+
399
+ return res.json({ url, key: objectName, provider: 'firebase' });
400
+ } catch (error) {
401
+ console.error(error);
402
+ return res.status(500).json({ error: 'Upload failed' });
403
+ }
404
+ });
405
+
406
+ app.listen(3000, () => {
407
+ console.log('Server running on http://localhost:3000');
408
+ });
409
+ ```
410
+
411
+ Node.js env example:
412
+
413
+ ```bash
414
+ FIREBASE_PROJECT_ID=...
415
+ FIREBASE_CLIENT_EMAIL=...
416
+ FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
417
+ FIREBASE_STORAGE_BUCKET=your-project-id.appspot.com
418
+ ```
419
+
420
+ ---
421
+
165
422
  # 🧠 API Reference
166
423
 
167
424
  ---
package/dist/index.d.mts CHANGED
@@ -1,4 +1,6 @@
1
- type UploadProvider = 's3' | 'cloudinary';
1
+ import { FirebaseOptions } from 'firebase/app';
2
+
3
+ type UploadProvider = 's3' | 'cloudinary' | 'firebase';
2
4
  interface UploadOptions {
3
5
  file: File;
4
6
  folder: string;
@@ -76,6 +78,36 @@ declare class CloudinaryUploader extends BaseUploader {
76
78
  upload(options: UploadOptions, onProgress: (percent: number) => void): Promise<UploadResponse>;
77
79
  }
78
80
 
81
+ interface FirebaseStorageConfig {
82
+ firebaseConfig: FirebaseOptions;
83
+ bucketUrl?: string;
84
+ appName?: string;
85
+ }
86
+ declare class FirebaseStorageUploader extends BaseUploader {
87
+ private config;
88
+ private app;
89
+ constructor(config: FirebaseStorageConfig);
90
+ upload(options: UploadOptions, onProgress: (percent: number) => void, signal?: AbortSignal): Promise<UploadResponse>;
91
+ private buildObjectPath;
92
+ private getOrCreateApp;
93
+ private toStringRecord;
94
+ }
95
+
96
+ interface CreateClientUploadManagerConfig {
97
+ provider?: UploadProvider | string;
98
+ concurrency?: number;
99
+ s3?: {
100
+ apiBaseUrl?: string;
101
+ publicUrl?: string;
102
+ };
103
+ firebase?: {
104
+ firebaseConfig?: FirebaseOptions;
105
+ bucketUrl?: string;
106
+ appName?: string;
107
+ };
108
+ }
109
+ declare const createClientUploadManager: (config?: CreateClientUploadManagerConfig) => UploadManager;
110
+
79
111
  interface PresignConfig {
80
112
  region: string;
81
113
  accessKeyId: string;
@@ -95,4 +127,4 @@ declare function createS3PresignHandler(config: PresignConfig): ({ fileName, con
95
127
  publicUrl: string;
96
128
  }>;
97
129
 
98
- export { CloudinaryUploader, type PresignConfig, type PresignRequest, S3Uploader, UploadManager, type UploadOptions, type UploadProvider, type UploadResponse, type UploadStatus, UploadTask, type UploadTaskState, createS3PresignHandler };
130
+ export { CloudinaryUploader, type CreateClientUploadManagerConfig, type FirebaseStorageConfig, FirebaseStorageUploader, type PresignConfig, type PresignRequest, S3Uploader, UploadManager, type UploadOptions, type UploadProvider, type UploadResponse, type UploadStatus, UploadTask, type UploadTaskState, createClientUploadManager, createS3PresignHandler };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- type UploadProvider = 's3' | 'cloudinary';
1
+ import { FirebaseOptions } from 'firebase/app';
2
+
3
+ type UploadProvider = 's3' | 'cloudinary' | 'firebase';
2
4
  interface UploadOptions {
3
5
  file: File;
4
6
  folder: string;
@@ -76,6 +78,36 @@ declare class CloudinaryUploader extends BaseUploader {
76
78
  upload(options: UploadOptions, onProgress: (percent: number) => void): Promise<UploadResponse>;
77
79
  }
78
80
 
81
+ interface FirebaseStorageConfig {
82
+ firebaseConfig: FirebaseOptions;
83
+ bucketUrl?: string;
84
+ appName?: string;
85
+ }
86
+ declare class FirebaseStorageUploader extends BaseUploader {
87
+ private config;
88
+ private app;
89
+ constructor(config: FirebaseStorageConfig);
90
+ upload(options: UploadOptions, onProgress: (percent: number) => void, signal?: AbortSignal): Promise<UploadResponse>;
91
+ private buildObjectPath;
92
+ private getOrCreateApp;
93
+ private toStringRecord;
94
+ }
95
+
96
+ interface CreateClientUploadManagerConfig {
97
+ provider?: UploadProvider | string;
98
+ concurrency?: number;
99
+ s3?: {
100
+ apiBaseUrl?: string;
101
+ publicUrl?: string;
102
+ };
103
+ firebase?: {
104
+ firebaseConfig?: FirebaseOptions;
105
+ bucketUrl?: string;
106
+ appName?: string;
107
+ };
108
+ }
109
+ declare const createClientUploadManager: (config?: CreateClientUploadManagerConfig) => UploadManager;
110
+
79
111
  interface PresignConfig {
80
112
  region: string;
81
113
  accessKeyId: string;
@@ -95,4 +127,4 @@ declare function createS3PresignHandler(config: PresignConfig): ({ fileName, con
95
127
  publicUrl: string;
96
128
  }>;
97
129
 
98
- export { CloudinaryUploader, type PresignConfig, type PresignRequest, S3Uploader, UploadManager, type UploadOptions, type UploadProvider, type UploadResponse, type UploadStatus, UploadTask, type UploadTaskState, createS3PresignHandler };
130
+ export { CloudinaryUploader, type CreateClientUploadManagerConfig, type FirebaseStorageConfig, FirebaseStorageUploader, type PresignConfig, type PresignRequest, S3Uploader, UploadManager, type UploadOptions, type UploadProvider, type UploadResponse, type UploadStatus, UploadTask, type UploadTaskState, createClientUploadManager, createS3PresignHandler };
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var app = require('firebase/app');
4
+ var storage = require('firebase/storage');
3
5
  var clientS3 = require('@aws-sdk/client-s3');
4
6
  var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
5
7
 
@@ -184,6 +186,119 @@ var CloudinaryUploader = class extends BaseUploader {
184
186
  });
185
187
  }
186
188
  };
189
+ var FirebaseStorageUploader = class extends BaseUploader {
190
+ constructor(config) {
191
+ super();
192
+ this.config = config;
193
+ this.app = this.getOrCreateApp(config.firebaseConfig, config.appName);
194
+ }
195
+ async upload(options, onProgress, signal) {
196
+ const storage$1 = storage.getStorage(this.app, this.config.bucketUrl);
197
+ const objectPath = this.buildObjectPath(options.folder, options.fileName || options.file.name);
198
+ const storageRef = storage.ref(storage$1, objectPath);
199
+ const metadata = {
200
+ contentType: options.file.type,
201
+ customMetadata: this.toStringRecord(options.metadata)
202
+ };
203
+ return new Promise((resolve, reject) => {
204
+ const uploadTask = storage.uploadBytesResumable(storageRef, options.file, metadata);
205
+ let isSettled = false;
206
+ const settleReject = (error) => {
207
+ if (isSettled) return;
208
+ isSettled = true;
209
+ reject(error);
210
+ };
211
+ const settleResolve = (response) => {
212
+ if (isSettled) return;
213
+ isSettled = true;
214
+ resolve(response);
215
+ };
216
+ const abortUpload = () => {
217
+ uploadTask.cancel();
218
+ settleReject(new Error("Upload cancelled"));
219
+ };
220
+ if (signal?.aborted) {
221
+ abortUpload();
222
+ return;
223
+ }
224
+ signal?.addEventListener("abort", abortUpload, { once: true });
225
+ uploadTask.on(
226
+ "state_changed",
227
+ (snapshot) => {
228
+ const percent = snapshot.totalBytes ? Math.round(snapshot.bytesTransferred / snapshot.totalBytes * 100) : 0;
229
+ onProgress(percent);
230
+ },
231
+ (error) => {
232
+ signal?.removeEventListener("abort", abortUpload);
233
+ settleReject(error);
234
+ },
235
+ async () => {
236
+ signal?.removeEventListener("abort", abortUpload);
237
+ try {
238
+ const url = await storage.getDownloadURL(uploadTask.snapshot.ref);
239
+ settleResolve({
240
+ url,
241
+ provider: "firebase",
242
+ key: uploadTask.snapshot.ref.fullPath
243
+ });
244
+ } catch (error) {
245
+ settleReject(error);
246
+ }
247
+ }
248
+ );
249
+ });
250
+ }
251
+ buildObjectPath(folder, fileName) {
252
+ return [folder, fileName].filter(Boolean).join("/").replace(/\/{2,}/g, "/").replace(/^\//, "");
253
+ }
254
+ getOrCreateApp(firebaseConfig, appName) {
255
+ if (!appName) {
256
+ return app.getApps().length > 0 ? app.getApp() : app.initializeApp(firebaseConfig);
257
+ }
258
+ const existingApp = app.getApps().find((app) => app.name === appName);
259
+ return existingApp || app.initializeApp(firebaseConfig, appName);
260
+ }
261
+ toStringRecord(metadata) {
262
+ if (!metadata) return void 0;
263
+ return Object.entries(metadata).reduce((result, [key, value]) => {
264
+ if (value !== void 0 && value !== null) {
265
+ result[key] = String(value);
266
+ }
267
+ return result;
268
+ }, {});
269
+ }
270
+ };
271
+
272
+ // src/factories/createClientUploadManager.ts
273
+ var normalizeProvider = (provider) => (provider || "s3").toLowerCase();
274
+ var hasFirebaseConfig = (config) => Boolean(config?.apiKey && config?.authDomain && config?.projectId && config?.storageBucket && config?.appId);
275
+ var createClientUploadManager = (config = {}) => {
276
+ const provider = normalizeProvider(config.provider);
277
+ const concurrency = config.concurrency ?? 3;
278
+ if (provider === "firebase") {
279
+ if (!hasFirebaseConfig(config.firebase?.firebaseConfig)) {
280
+ throw new Error('Missing required Firebase configuration for upload provider "firebase".');
281
+ }
282
+ return new UploadManager(
283
+ new FirebaseStorageUploader({
284
+ firebaseConfig: config.firebase.firebaseConfig,
285
+ bucketUrl: config.firebase?.bucketUrl,
286
+ appName: config.firebase?.appName
287
+ }),
288
+ concurrency
289
+ );
290
+ }
291
+ if (!config.s3?.apiBaseUrl || !config.s3?.publicUrl) {
292
+ throw new Error('Missing required S3 configuration: "apiBaseUrl" and "publicUrl" are required.');
293
+ }
294
+ return new UploadManager(
295
+ new S3Uploader({
296
+ apiBaseUrl: config.s3.apiBaseUrl,
297
+ publicUrl: config.s3.publicUrl
298
+ }),
299
+ concurrency
300
+ );
301
+ };
187
302
  function createS3PresignHandler(config) {
188
303
  const s3 = new clientS3.S3Client({
189
304
  region: config.region,
@@ -219,9 +334,11 @@ function createS3PresignHandler(config) {
219
334
  }
220
335
 
221
336
  exports.CloudinaryUploader = CloudinaryUploader;
337
+ exports.FirebaseStorageUploader = FirebaseStorageUploader;
222
338
  exports.S3Uploader = S3Uploader;
223
339
  exports.UploadManager = UploadManager;
224
340
  exports.UploadTask = UploadTask;
341
+ exports.createClientUploadManager = createClientUploadManager;
225
342
  exports.createS3PresignHandler = createS3PresignHandler;
226
343
  //# sourceMappingURL=index.js.map
227
344
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/EventEmitter.ts","../src/core/UploadTask.ts","../src/core/UploadManager.ts","../src/core/BaseUploader.ts","../src/providers/S3Uploader.ts","../src/providers/CloudinaryUploader.ts","../src/server/createS3PresignHandler.ts"],"names":["S3Client","PutObjectCommand","getSignedUrl"],"mappings":"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","file":"index.js","sourcesContent":["type Listener<T> = (payload: T) => void;\n\nexport class EventEmitter<T> {\n private listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>) {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n emit(payload: T) {\n this.listeners.forEach(listener => listener(payload));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions, UploadTaskState } from './types';\nimport { EventEmitter } from './EventEmitter';\n\nexport class UploadTask {\n public state: UploadTaskState;\n public events = new EventEmitter<UploadTaskState>();\n private abortController = new AbortController();\n private retries = 0;\n\n constructor(\n private uploader: BaseUploader,\n private options: UploadOptions,\n private maxRetries = 2,\n private retryDelay = 500\n ) {\n this.state = {\n id: crypto.randomUUID(),\n progress: 0,\n status: 'queued'\n };\n }\n\n async start(): Promise<void> {\n this.update({ status: 'uploading' });\n\n while (this.retries <= this.maxRetries) {\n try {\n const response = await this.uploader.upload(\n this.options,\n progress => this.update({ progress }),\n this.abortController.signal\n );\n\n this.update({ status: 'success', progress: 100, response });\n return;\n } catch (error: unknown) {\n if (this.abortController.signal.aborted) {\n this.update({ status: 'cancelled' });\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unknown upload error';\n\n if (this.retries >= this.maxRetries) {\n this.update({ status: 'error', error: message });\n return;\n }\n\n this.retries++;\n await this.sleep(this.retryDelay * Math.pow(2, this.retries - 1));\n }\n }\n }\n\n cancel() {\n this.abortController.abort();\n this.update({ status: 'cancelled' });\n }\n\n private update(update: Partial<UploadTaskState>) {\n this.state = { ...this.state, ...update };\n this.events.emit(this.state);\n }\n\n private sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions } from './types';\nimport { UploadTask } from './UploadTask';\n\nexport class UploadManager {\n private queue: UploadTask[] = [];\n private active = 0;\n\n constructor(private uploader: BaseUploader, private concurrency = 3) { }\n\n add(options: UploadOptions) {\n const task = new UploadTask(this.uploader, options);\n this.queue.push(task);\n this.process();\n return task;\n }\n\n private async process() {\n if (this.active >= this.concurrency) return;\n\n const next = this.queue.find(t => t.state.status === 'queued');\n if (!next) return;\n\n this.active++;\n await next.start();\n this.active--;\n this.process();\n }\n}","import { UploadOptions, UploadResponse } from './types';\n\nexport abstract class BaseUploader {\n abstract upload(options: UploadOptions, onProgress: (percent: number) => void, signal?: AbortSignal): Promise<UploadResponse>;\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface S3Config {\n apiBaseUrl: string;\n publicUrl: string;\n}\n\nexport class S3Uploader extends BaseUploader {\n constructor(private config: S3Config) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void,\n signal?: AbortSignal\n ): Promise<UploadResponse> {\n const presignRes = await fetch(`${this.config.apiBaseUrl}/api/s3/presign-upload`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n fileName: options.fileName || options.file.name,\n folder: options.folder,\n contentType: options.file.type\n })\n });\n\n if (!presignRes.ok) throw new Error('Failed to get presigned URL');\n\n const { signedUrl, key } = await presignRes.json();\n\n if (!signedUrl || !key) throw new Error('Invalid presign response');\n\n return new Promise<UploadResponse>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n if (xhr.status === 200) {\n resolve({\n url: `${this.config.publicUrl}/${key}`,\n provider: 's3',\n key,\n });\n } else {\n reject(new Error(`S3 upload failed with status ${xhr.status}`));\n }\n };\n\n xhr.onerror = () => reject(new Error('S3 upload network error'));\n\n signal?.addEventListener('abort', () => {\n xhr.abort();\n reject(new Error('Upload cancelled'));\n });\n\n xhr.open('PUT', signedUrl);\n xhr.setRequestHeader('Content-Type', options.file.type);\n xhr.send(options.file);\n });\n }\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface CloudinaryConfig {\n cloudName: string;\n uploadPreset: string;\n}\n\nexport class CloudinaryUploader extends BaseUploader {\n constructor(private config: CloudinaryConfig) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void\n ): Promise<UploadResponse> {\n const formData = new FormData();\n formData.append('file', options.file);\n formData.append('upload_preset', this.config.uploadPreset);\n formData.append('folder', options.folder);\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n const data = JSON.parse(xhr.responseText);\n if (xhr.status === 200 && data.secure_url) {\n resolve({ url: data.secure_url, provider: 'cloudinary' });\n } else {\n reject(new Error('Cloudinary upload failed'));\n }\n };\n\n xhr.onerror = () => reject(new Error('Cloudinary upload failed'));\n\n xhr.open('POST', `https://api.cloudinary.com/v1_1/${this.config.cloudName}/upload`);\n xhr.send(formData);\n });\n }\n}","import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n\nexport interface PresignConfig {\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n bucket: string;\n publicUrl: string;\n expiresIn?: number;\n}\n\nexport interface PresignRequest {\n fileName: string;\n contentType: string;\n folder?: string;\n}\n\nexport function createS3PresignHandler(config: PresignConfig) {\n const s3 = new S3Client({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n\n return async function generatePresignedUpload({\n fileName,\n contentType,\n folder,\n }: PresignRequest) {\n if (!fileName || !contentType) {\n throw new Error('fileName and contentType are required');\n }\n\n const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n const key = `${folder || 'uploads'}/${Date.now()}-${safeFileName}`;\n\n const command = new PutObjectCommand({\n Bucket: config.bucket,\n Key: key,\n ContentType: contentType,\n });\n\n const signedUrl = await getSignedUrl(s3, command, {\n expiresIn: config.expiresIn ?? 300,\n });\n\n return {\n signedUrl,\n key,\n publicUrl: `${config.publicUrl}/${key}`,\n };\n };\n}"]}
1
+ {"version":3,"sources":["../src/core/EventEmitter.ts","../src/core/UploadTask.ts","../src/core/UploadManager.ts","../src/core/BaseUploader.ts","../src/providers/S3Uploader.ts","../src/providers/CloudinaryUploader.ts","../src/providers/FirebaseStorageUploader.ts","../src/factories/createClientUploadManager.ts","../src/server/createS3PresignHandler.ts"],"names":["storage","getStorage","ref","uploadBytesResumable","getDownloadURL","getApps","getApp","initializeApp","S3Client","PutObjectCommand","getSignedUrl"],"mappings":"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","file":"index.js","sourcesContent":["type Listener<T> = (payload: T) => void;\n\nexport class EventEmitter<T> {\n private listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>) {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n emit(payload: T) {\n this.listeners.forEach(listener => listener(payload));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions, UploadTaskState } from './types';\nimport { EventEmitter } from './EventEmitter';\n\nexport class UploadTask {\n public state: UploadTaskState;\n public events = new EventEmitter<UploadTaskState>();\n private abortController = new AbortController();\n private retries = 0;\n\n constructor(\n private uploader: BaseUploader,\n private options: UploadOptions,\n private maxRetries = 2,\n private retryDelay = 500\n ) {\n this.state = {\n id: crypto.randomUUID(),\n progress: 0,\n status: 'queued'\n };\n }\n\n async start(): Promise<void> {\n this.update({ status: 'uploading' });\n\n while (this.retries <= this.maxRetries) {\n try {\n const response = await this.uploader.upload(\n this.options,\n progress => this.update({ progress }),\n this.abortController.signal\n );\n\n this.update({ status: 'success', progress: 100, response });\n return;\n } catch (error: unknown) {\n if (this.abortController.signal.aborted) {\n this.update({ status: 'cancelled' });\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unknown upload error';\n\n if (this.retries >= this.maxRetries) {\n this.update({ status: 'error', error: message });\n return;\n }\n\n this.retries++;\n await this.sleep(this.retryDelay * Math.pow(2, this.retries - 1));\n }\n }\n }\n\n cancel() {\n this.abortController.abort();\n this.update({ status: 'cancelled' });\n }\n\n private update(update: Partial<UploadTaskState>) {\n this.state = { ...this.state, ...update };\n this.events.emit(this.state);\n }\n\n private sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions } from './types';\nimport { UploadTask } from './UploadTask';\n\nexport class UploadManager {\n private queue: UploadTask[] = [];\n private active = 0;\n\n constructor(private uploader: BaseUploader, private concurrency = 3) { }\n\n add(options: UploadOptions) {\n const task = new UploadTask(this.uploader, options);\n this.queue.push(task);\n this.process();\n return task;\n }\n\n private async process() {\n if (this.active >= this.concurrency) return;\n\n const next = this.queue.find(t => t.state.status === 'queued');\n if (!next) return;\n\n this.active++;\n await next.start();\n this.active--;\n this.process();\n }\n}","import { UploadOptions, UploadResponse } from './types';\n\nexport abstract class BaseUploader {\n abstract upload(options: UploadOptions, onProgress: (percent: number) => void, signal?: AbortSignal): Promise<UploadResponse>;\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface S3Config {\n apiBaseUrl: string;\n publicUrl: string;\n}\n\nexport class S3Uploader extends BaseUploader {\n constructor(private config: S3Config) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void,\n signal?: AbortSignal\n ): Promise<UploadResponse> {\n const presignRes = await fetch(`${this.config.apiBaseUrl}/api/s3/presign-upload`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n fileName: options.fileName || options.file.name,\n folder: options.folder,\n contentType: options.file.type\n })\n });\n\n if (!presignRes.ok) throw new Error('Failed to get presigned URL');\n\n const { signedUrl, key } = await presignRes.json();\n\n if (!signedUrl || !key) throw new Error('Invalid presign response');\n\n return new Promise<UploadResponse>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n if (xhr.status === 200) {\n resolve({\n url: `${this.config.publicUrl}/${key}`,\n provider: 's3',\n key,\n });\n } else {\n reject(new Error(`S3 upload failed with status ${xhr.status}`));\n }\n };\n\n xhr.onerror = () => reject(new Error('S3 upload network error'));\n\n signal?.addEventListener('abort', () => {\n xhr.abort();\n reject(new Error('Upload cancelled'));\n });\n\n xhr.open('PUT', signedUrl);\n xhr.setRequestHeader('Content-Type', options.file.type);\n xhr.send(options.file);\n });\n }\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface CloudinaryConfig {\n cloudName: string;\n uploadPreset: string;\n}\n\nexport class CloudinaryUploader extends BaseUploader {\n constructor(private config: CloudinaryConfig) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void\n ): Promise<UploadResponse> {\n const formData = new FormData();\n formData.append('file', options.file);\n formData.append('upload_preset', this.config.uploadPreset);\n formData.append('folder', options.folder);\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n const data = JSON.parse(xhr.responseText);\n if (xhr.status === 200 && data.secure_url) {\n resolve({ url: data.secure_url, provider: 'cloudinary' });\n } else {\n reject(new Error('Cloudinary upload failed'));\n }\n };\n\n xhr.onerror = () => reject(new Error('Cloudinary upload failed'));\n\n xhr.open('POST', `https://api.cloudinary.com/v1_1/${this.config.cloudName}/upload`);\n xhr.send(formData);\n });\n }\n}","import { getApp, getApps, initializeApp, type FirebaseApp, type FirebaseOptions } from 'firebase/app';\nimport { getDownloadURL, getStorage, ref, uploadBytesResumable, type UploadMetadata } from 'firebase/storage';\nimport { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\nexport interface FirebaseStorageConfig {\n firebaseConfig: FirebaseOptions;\n bucketUrl?: string;\n appName?: string;\n}\n\nexport class FirebaseStorageUploader extends BaseUploader {\n private app: FirebaseApp;\n\n constructor(private config: FirebaseStorageConfig) {\n super();\n this.app = this.getOrCreateApp(config.firebaseConfig, config.appName);\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void,\n signal?: AbortSignal\n ): Promise<UploadResponse> {\n const storage = getStorage(this.app, this.config.bucketUrl);\n const objectPath = this.buildObjectPath(options.folder, options.fileName || options.file.name);\n const storageRef = ref(storage, objectPath);\n const metadata: UploadMetadata = {\n contentType: options.file.type,\n customMetadata: this.toStringRecord(options.metadata),\n };\n\n return new Promise<UploadResponse>((resolve, reject) => {\n const uploadTask = uploadBytesResumable(storageRef, options.file, metadata);\n let isSettled = false;\n\n const settleReject = (error: Error) => {\n if (isSettled) return;\n isSettled = true;\n reject(error);\n };\n\n const settleResolve = (response: UploadResponse) => {\n if (isSettled) return;\n isSettled = true;\n resolve(response);\n };\n\n const abortUpload = () => {\n uploadTask.cancel();\n settleReject(new Error('Upload cancelled'));\n };\n\n if (signal?.aborted) {\n abortUpload();\n return;\n }\n\n signal?.addEventListener('abort', abortUpload, { once: true });\n\n uploadTask.on(\n 'state_changed',\n snapshot => {\n const percent = snapshot.totalBytes\n ? Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100)\n : 0;\n\n onProgress(percent);\n },\n error => {\n signal?.removeEventListener('abort', abortUpload);\n settleReject(error);\n },\n async () => {\n signal?.removeEventListener('abort', abortUpload);\n\n try {\n const url = await getDownloadURL(uploadTask.snapshot.ref);\n\n settleResolve({\n url,\n provider: 'firebase',\n key: uploadTask.snapshot.ref.fullPath,\n });\n } catch (error) {\n settleReject(error as Error);\n }\n }\n );\n });\n }\n\n private buildObjectPath(folder: string, fileName: string) {\n return [folder, fileName]\n .filter(Boolean)\n .join('/')\n .replace(/\\/{2,}/g, '/')\n .replace(/^\\//, '');\n }\n\n private getOrCreateApp(firebaseConfig: FirebaseOptions, appName?: string) {\n if (!appName) {\n return getApps().length > 0 ? getApp() : initializeApp(firebaseConfig);\n }\n\n const existingApp = getApps().find(app => app.name === appName);\n return existingApp || initializeApp(firebaseConfig, appName);\n }\n\n private toStringRecord(metadata?: Record<string, any>) {\n if (!metadata) return undefined;\n\n return Object.entries(metadata).reduce<Record<string, string>>((result, [key, value]) => {\n if (value !== undefined && value !== null) {\n result[key] = String(value);\n }\n\n return result;\n }, {});\n }\n}\n","import type { FirebaseOptions } from 'firebase/app';\nimport { UploadManager } from '../core/UploadManager';\nimport type { UploadProvider } from '../core/types';\nimport { FirebaseStorageUploader } from '../providers/FirebaseStorageUploader';\nimport { S3Uploader } from '../providers/S3Uploader';\n\nexport interface CreateClientUploadManagerConfig {\n provider?: UploadProvider | string;\n concurrency?: number;\n s3?: {\n apiBaseUrl?: string;\n publicUrl?: string;\n };\n firebase?: {\n firebaseConfig?: FirebaseOptions;\n bucketUrl?: string;\n appName?: string;\n };\n}\n\nconst normalizeProvider = (provider: CreateClientUploadManagerConfig['provider']) =>\n (provider || 's3').toLowerCase();\n\nconst hasFirebaseConfig = (config?: FirebaseOptions) =>\n Boolean(config?.apiKey && config?.authDomain && config?.projectId && config?.storageBucket && config?.appId);\n\nexport const createClientUploadManager = (config: CreateClientUploadManagerConfig = {}) => {\n const provider = normalizeProvider(config.provider);\n const concurrency = config.concurrency ?? 3;\n\n if (provider === 'firebase') {\n if (!hasFirebaseConfig(config.firebase?.firebaseConfig)) {\n throw new Error('Missing required Firebase configuration for upload provider \"firebase\".');\n }\n\n return new UploadManager(\n new FirebaseStorageUploader({\n firebaseConfig: config.firebase!.firebaseConfig!,\n bucketUrl: config.firebase?.bucketUrl,\n appName: config.firebase?.appName,\n }),\n concurrency\n );\n }\n\n if (!config.s3?.apiBaseUrl || !config.s3?.publicUrl) {\n throw new Error('Missing required S3 configuration: \"apiBaseUrl\" and \"publicUrl\" are required.');\n }\n\n return new UploadManager(\n new S3Uploader({\n apiBaseUrl: config.s3.apiBaseUrl,\n publicUrl: config.s3.publicUrl,\n }),\n concurrency\n );\n};\n","import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n\nexport interface PresignConfig {\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n bucket: string;\n publicUrl: string;\n expiresIn?: number;\n}\n\nexport interface PresignRequest {\n fileName: string;\n contentType: string;\n folder?: string;\n}\n\nexport function createS3PresignHandler(config: PresignConfig) {\n const s3 = new S3Client({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n\n return async function generatePresignedUpload({\n fileName,\n contentType,\n folder,\n }: PresignRequest) {\n if (!fileName || !contentType) {\n throw new Error('fileName and contentType are required');\n }\n\n const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n const key = `${folder || 'uploads'}/${Date.now()}-${safeFileName}`;\n\n const command = new PutObjectCommand({\n Bucket: config.bucket,\n Key: key,\n ContentType: contentType,\n });\n\n const signedUrl = await getSignedUrl(s3, command, {\n expiresIn: config.expiresIn ?? 300,\n });\n\n return {\n signedUrl,\n key,\n publicUrl: `${config.publicUrl}/${key}`,\n };\n };\n}"]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { getApps, getApp, initializeApp } from 'firebase/app';
2
+ import { getStorage, ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage';
1
3
  import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
2
4
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
5
 
@@ -182,6 +184,119 @@ var CloudinaryUploader = class extends BaseUploader {
182
184
  });
183
185
  }
184
186
  };
187
+ var FirebaseStorageUploader = class extends BaseUploader {
188
+ constructor(config) {
189
+ super();
190
+ this.config = config;
191
+ this.app = this.getOrCreateApp(config.firebaseConfig, config.appName);
192
+ }
193
+ async upload(options, onProgress, signal) {
194
+ const storage = getStorage(this.app, this.config.bucketUrl);
195
+ const objectPath = this.buildObjectPath(options.folder, options.fileName || options.file.name);
196
+ const storageRef = ref(storage, objectPath);
197
+ const metadata = {
198
+ contentType: options.file.type,
199
+ customMetadata: this.toStringRecord(options.metadata)
200
+ };
201
+ return new Promise((resolve, reject) => {
202
+ const uploadTask = uploadBytesResumable(storageRef, options.file, metadata);
203
+ let isSettled = false;
204
+ const settleReject = (error) => {
205
+ if (isSettled) return;
206
+ isSettled = true;
207
+ reject(error);
208
+ };
209
+ const settleResolve = (response) => {
210
+ if (isSettled) return;
211
+ isSettled = true;
212
+ resolve(response);
213
+ };
214
+ const abortUpload = () => {
215
+ uploadTask.cancel();
216
+ settleReject(new Error("Upload cancelled"));
217
+ };
218
+ if (signal?.aborted) {
219
+ abortUpload();
220
+ return;
221
+ }
222
+ signal?.addEventListener("abort", abortUpload, { once: true });
223
+ uploadTask.on(
224
+ "state_changed",
225
+ (snapshot) => {
226
+ const percent = snapshot.totalBytes ? Math.round(snapshot.bytesTransferred / snapshot.totalBytes * 100) : 0;
227
+ onProgress(percent);
228
+ },
229
+ (error) => {
230
+ signal?.removeEventListener("abort", abortUpload);
231
+ settleReject(error);
232
+ },
233
+ async () => {
234
+ signal?.removeEventListener("abort", abortUpload);
235
+ try {
236
+ const url = await getDownloadURL(uploadTask.snapshot.ref);
237
+ settleResolve({
238
+ url,
239
+ provider: "firebase",
240
+ key: uploadTask.snapshot.ref.fullPath
241
+ });
242
+ } catch (error) {
243
+ settleReject(error);
244
+ }
245
+ }
246
+ );
247
+ });
248
+ }
249
+ buildObjectPath(folder, fileName) {
250
+ return [folder, fileName].filter(Boolean).join("/").replace(/\/{2,}/g, "/").replace(/^\//, "");
251
+ }
252
+ getOrCreateApp(firebaseConfig, appName) {
253
+ if (!appName) {
254
+ return getApps().length > 0 ? getApp() : initializeApp(firebaseConfig);
255
+ }
256
+ const existingApp = getApps().find((app) => app.name === appName);
257
+ return existingApp || initializeApp(firebaseConfig, appName);
258
+ }
259
+ toStringRecord(metadata) {
260
+ if (!metadata) return void 0;
261
+ return Object.entries(metadata).reduce((result, [key, value]) => {
262
+ if (value !== void 0 && value !== null) {
263
+ result[key] = String(value);
264
+ }
265
+ return result;
266
+ }, {});
267
+ }
268
+ };
269
+
270
+ // src/factories/createClientUploadManager.ts
271
+ var normalizeProvider = (provider) => (provider || "s3").toLowerCase();
272
+ var hasFirebaseConfig = (config) => Boolean(config?.apiKey && config?.authDomain && config?.projectId && config?.storageBucket && config?.appId);
273
+ var createClientUploadManager = (config = {}) => {
274
+ const provider = normalizeProvider(config.provider);
275
+ const concurrency = config.concurrency ?? 3;
276
+ if (provider === "firebase") {
277
+ if (!hasFirebaseConfig(config.firebase?.firebaseConfig)) {
278
+ throw new Error('Missing required Firebase configuration for upload provider "firebase".');
279
+ }
280
+ return new UploadManager(
281
+ new FirebaseStorageUploader({
282
+ firebaseConfig: config.firebase.firebaseConfig,
283
+ bucketUrl: config.firebase?.bucketUrl,
284
+ appName: config.firebase?.appName
285
+ }),
286
+ concurrency
287
+ );
288
+ }
289
+ if (!config.s3?.apiBaseUrl || !config.s3?.publicUrl) {
290
+ throw new Error('Missing required S3 configuration: "apiBaseUrl" and "publicUrl" are required.');
291
+ }
292
+ return new UploadManager(
293
+ new S3Uploader({
294
+ apiBaseUrl: config.s3.apiBaseUrl,
295
+ publicUrl: config.s3.publicUrl
296
+ }),
297
+ concurrency
298
+ );
299
+ };
185
300
  function createS3PresignHandler(config) {
186
301
  const s3 = new S3Client({
187
302
  region: config.region,
@@ -216,6 +331,6 @@ function createS3PresignHandler(config) {
216
331
  };
217
332
  }
218
333
 
219
- export { CloudinaryUploader, S3Uploader, UploadManager, UploadTask, createS3PresignHandler };
334
+ export { CloudinaryUploader, FirebaseStorageUploader, S3Uploader, UploadManager, UploadTask, createClientUploadManager, createS3PresignHandler };
220
335
  //# sourceMappingURL=index.mjs.map
221
336
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/EventEmitter.ts","../src/core/UploadTask.ts","../src/core/UploadManager.ts","../src/core/BaseUploader.ts","../src/providers/S3Uploader.ts","../src/providers/CloudinaryUploader.ts","../src/server/createS3PresignHandler.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["type Listener<T> = (payload: T) => void;\n\nexport class EventEmitter<T> {\n private listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>) {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n emit(payload: T) {\n this.listeners.forEach(listener => listener(payload));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions, UploadTaskState } from './types';\nimport { EventEmitter } from './EventEmitter';\n\nexport class UploadTask {\n public state: UploadTaskState;\n public events = new EventEmitter<UploadTaskState>();\n private abortController = new AbortController();\n private retries = 0;\n\n constructor(\n private uploader: BaseUploader,\n private options: UploadOptions,\n private maxRetries = 2,\n private retryDelay = 500\n ) {\n this.state = {\n id: crypto.randomUUID(),\n progress: 0,\n status: 'queued'\n };\n }\n\n async start(): Promise<void> {\n this.update({ status: 'uploading' });\n\n while (this.retries <= this.maxRetries) {\n try {\n const response = await this.uploader.upload(\n this.options,\n progress => this.update({ progress }),\n this.abortController.signal\n );\n\n this.update({ status: 'success', progress: 100, response });\n return;\n } catch (error: unknown) {\n if (this.abortController.signal.aborted) {\n this.update({ status: 'cancelled' });\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unknown upload error';\n\n if (this.retries >= this.maxRetries) {\n this.update({ status: 'error', error: message });\n return;\n }\n\n this.retries++;\n await this.sleep(this.retryDelay * Math.pow(2, this.retries - 1));\n }\n }\n }\n\n cancel() {\n this.abortController.abort();\n this.update({ status: 'cancelled' });\n }\n\n private update(update: Partial<UploadTaskState>) {\n this.state = { ...this.state, ...update };\n this.events.emit(this.state);\n }\n\n private sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions } from './types';\nimport { UploadTask } from './UploadTask';\n\nexport class UploadManager {\n private queue: UploadTask[] = [];\n private active = 0;\n\n constructor(private uploader: BaseUploader, private concurrency = 3) { }\n\n add(options: UploadOptions) {\n const task = new UploadTask(this.uploader, options);\n this.queue.push(task);\n this.process();\n return task;\n }\n\n private async process() {\n if (this.active >= this.concurrency) return;\n\n const next = this.queue.find(t => t.state.status === 'queued');\n if (!next) return;\n\n this.active++;\n await next.start();\n this.active--;\n this.process();\n }\n}","import { UploadOptions, UploadResponse } from './types';\n\nexport abstract class BaseUploader {\n abstract upload(options: UploadOptions, onProgress: (percent: number) => void, signal?: AbortSignal): Promise<UploadResponse>;\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface S3Config {\n apiBaseUrl: string;\n publicUrl: string;\n}\n\nexport class S3Uploader extends BaseUploader {\n constructor(private config: S3Config) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void,\n signal?: AbortSignal\n ): Promise<UploadResponse> {\n const presignRes = await fetch(`${this.config.apiBaseUrl}/api/s3/presign-upload`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n fileName: options.fileName || options.file.name,\n folder: options.folder,\n contentType: options.file.type\n })\n });\n\n if (!presignRes.ok) throw new Error('Failed to get presigned URL');\n\n const { signedUrl, key } = await presignRes.json();\n\n if (!signedUrl || !key) throw new Error('Invalid presign response');\n\n return new Promise<UploadResponse>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n if (xhr.status === 200) {\n resolve({\n url: `${this.config.publicUrl}/${key}`,\n provider: 's3',\n key,\n });\n } else {\n reject(new Error(`S3 upload failed with status ${xhr.status}`));\n }\n };\n\n xhr.onerror = () => reject(new Error('S3 upload network error'));\n\n signal?.addEventListener('abort', () => {\n xhr.abort();\n reject(new Error('Upload cancelled'));\n });\n\n xhr.open('PUT', signedUrl);\n xhr.setRequestHeader('Content-Type', options.file.type);\n xhr.send(options.file);\n });\n }\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface CloudinaryConfig {\n cloudName: string;\n uploadPreset: string;\n}\n\nexport class CloudinaryUploader extends BaseUploader {\n constructor(private config: CloudinaryConfig) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void\n ): Promise<UploadResponse> {\n const formData = new FormData();\n formData.append('file', options.file);\n formData.append('upload_preset', this.config.uploadPreset);\n formData.append('folder', options.folder);\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n const data = JSON.parse(xhr.responseText);\n if (xhr.status === 200 && data.secure_url) {\n resolve({ url: data.secure_url, provider: 'cloudinary' });\n } else {\n reject(new Error('Cloudinary upload failed'));\n }\n };\n\n xhr.onerror = () => reject(new Error('Cloudinary upload failed'));\n\n xhr.open('POST', `https://api.cloudinary.com/v1_1/${this.config.cloudName}/upload`);\n xhr.send(formData);\n });\n }\n}","import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n\nexport interface PresignConfig {\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n bucket: string;\n publicUrl: string;\n expiresIn?: number;\n}\n\nexport interface PresignRequest {\n fileName: string;\n contentType: string;\n folder?: string;\n}\n\nexport function createS3PresignHandler(config: PresignConfig) {\n const s3 = new S3Client({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n\n return async function generatePresignedUpload({\n fileName,\n contentType,\n folder,\n }: PresignRequest) {\n if (!fileName || !contentType) {\n throw new Error('fileName and contentType are required');\n }\n\n const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n const key = `${folder || 'uploads'}/${Date.now()}-${safeFileName}`;\n\n const command = new PutObjectCommand({\n Bucket: config.bucket,\n Key: key,\n ContentType: contentType,\n });\n\n const signedUrl = await getSignedUrl(s3, command, {\n expiresIn: config.expiresIn ?? 300,\n });\n\n return {\n signedUrl,\n key,\n publicUrl: `${config.publicUrl}/${key}`,\n };\n };\n}"]}
1
+ {"version":3,"sources":["../src/core/EventEmitter.ts","../src/core/UploadTask.ts","../src/core/UploadManager.ts","../src/core/BaseUploader.ts","../src/providers/S3Uploader.ts","../src/providers/CloudinaryUploader.ts","../src/providers/FirebaseStorageUploader.ts","../src/factories/createClientUploadManager.ts","../src/server/createS3PresignHandler.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["type Listener<T> = (payload: T) => void;\n\nexport class EventEmitter<T> {\n private listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>) {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter(l => l !== listener);\n };\n }\n\n emit(payload: T) {\n this.listeners.forEach(listener => listener(payload));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions, UploadTaskState } from './types';\nimport { EventEmitter } from './EventEmitter';\n\nexport class UploadTask {\n public state: UploadTaskState;\n public events = new EventEmitter<UploadTaskState>();\n private abortController = new AbortController();\n private retries = 0;\n\n constructor(\n private uploader: BaseUploader,\n private options: UploadOptions,\n private maxRetries = 2,\n private retryDelay = 500\n ) {\n this.state = {\n id: crypto.randomUUID(),\n progress: 0,\n status: 'queued'\n };\n }\n\n async start(): Promise<void> {\n this.update({ status: 'uploading' });\n\n while (this.retries <= this.maxRetries) {\n try {\n const response = await this.uploader.upload(\n this.options,\n progress => this.update({ progress }),\n this.abortController.signal\n );\n\n this.update({ status: 'success', progress: 100, response });\n return;\n } catch (error: unknown) {\n if (this.abortController.signal.aborted) {\n this.update({ status: 'cancelled' });\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unknown upload error';\n\n if (this.retries >= this.maxRetries) {\n this.update({ status: 'error', error: message });\n return;\n }\n\n this.retries++;\n await this.sleep(this.retryDelay * Math.pow(2, this.retries - 1));\n }\n }\n }\n\n cancel() {\n this.abortController.abort();\n this.update({ status: 'cancelled' });\n }\n\n private update(update: Partial<UploadTaskState>) {\n this.state = { ...this.state, ...update };\n this.events.emit(this.state);\n }\n\n private sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}","import { BaseUploader } from './BaseUploader';\nimport { UploadOptions } from './types';\nimport { UploadTask } from './UploadTask';\n\nexport class UploadManager {\n private queue: UploadTask[] = [];\n private active = 0;\n\n constructor(private uploader: BaseUploader, private concurrency = 3) { }\n\n add(options: UploadOptions) {\n const task = new UploadTask(this.uploader, options);\n this.queue.push(task);\n this.process();\n return task;\n }\n\n private async process() {\n if (this.active >= this.concurrency) return;\n\n const next = this.queue.find(t => t.state.status === 'queued');\n if (!next) return;\n\n this.active++;\n await next.start();\n this.active--;\n this.process();\n }\n}","import { UploadOptions, UploadResponse } from './types';\n\nexport abstract class BaseUploader {\n abstract upload(options: UploadOptions, onProgress: (percent: number) => void, signal?: AbortSignal): Promise<UploadResponse>;\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface S3Config {\n apiBaseUrl: string;\n publicUrl: string;\n}\n\nexport class S3Uploader extends BaseUploader {\n constructor(private config: S3Config) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void,\n signal?: AbortSignal\n ): Promise<UploadResponse> {\n const presignRes = await fetch(`${this.config.apiBaseUrl}/api/s3/presign-upload`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n fileName: options.fileName || options.file.name,\n folder: options.folder,\n contentType: options.file.type\n })\n });\n\n if (!presignRes.ok) throw new Error('Failed to get presigned URL');\n\n const { signedUrl, key } = await presignRes.json();\n\n if (!signedUrl || !key) throw new Error('Invalid presign response');\n\n return new Promise<UploadResponse>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n if (xhr.status === 200) {\n resolve({\n url: `${this.config.publicUrl}/${key}`,\n provider: 's3',\n key,\n });\n } else {\n reject(new Error(`S3 upload failed with status ${xhr.status}`));\n }\n };\n\n xhr.onerror = () => reject(new Error('S3 upload network error'));\n\n signal?.addEventListener('abort', () => {\n xhr.abort();\n reject(new Error('Upload cancelled'));\n });\n\n xhr.open('PUT', signedUrl);\n xhr.setRequestHeader('Content-Type', options.file.type);\n xhr.send(options.file);\n });\n }\n}","import { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\ninterface CloudinaryConfig {\n cloudName: string;\n uploadPreset: string;\n}\n\nexport class CloudinaryUploader extends BaseUploader {\n constructor(private config: CloudinaryConfig) {\n super();\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void\n ): Promise<UploadResponse> {\n const formData = new FormData();\n formData.append('file', options.file);\n formData.append('upload_preset', this.config.uploadPreset);\n formData.append('folder', options.folder);\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.onprogress = event => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n\n xhr.onload = () => {\n const data = JSON.parse(xhr.responseText);\n if (xhr.status === 200 && data.secure_url) {\n resolve({ url: data.secure_url, provider: 'cloudinary' });\n } else {\n reject(new Error('Cloudinary upload failed'));\n }\n };\n\n xhr.onerror = () => reject(new Error('Cloudinary upload failed'));\n\n xhr.open('POST', `https://api.cloudinary.com/v1_1/${this.config.cloudName}/upload`);\n xhr.send(formData);\n });\n }\n}","import { getApp, getApps, initializeApp, type FirebaseApp, type FirebaseOptions } from 'firebase/app';\nimport { getDownloadURL, getStorage, ref, uploadBytesResumable, type UploadMetadata } from 'firebase/storage';\nimport { BaseUploader } from '../core/BaseUploader';\nimport { UploadOptions, UploadResponse } from '../core/types';\n\nexport interface FirebaseStorageConfig {\n firebaseConfig: FirebaseOptions;\n bucketUrl?: string;\n appName?: string;\n}\n\nexport class FirebaseStorageUploader extends BaseUploader {\n private app: FirebaseApp;\n\n constructor(private config: FirebaseStorageConfig) {\n super();\n this.app = this.getOrCreateApp(config.firebaseConfig, config.appName);\n }\n\n async upload(\n options: UploadOptions,\n onProgress: (percent: number) => void,\n signal?: AbortSignal\n ): Promise<UploadResponse> {\n const storage = getStorage(this.app, this.config.bucketUrl);\n const objectPath = this.buildObjectPath(options.folder, options.fileName || options.file.name);\n const storageRef = ref(storage, objectPath);\n const metadata: UploadMetadata = {\n contentType: options.file.type,\n customMetadata: this.toStringRecord(options.metadata),\n };\n\n return new Promise<UploadResponse>((resolve, reject) => {\n const uploadTask = uploadBytesResumable(storageRef, options.file, metadata);\n let isSettled = false;\n\n const settleReject = (error: Error) => {\n if (isSettled) return;\n isSettled = true;\n reject(error);\n };\n\n const settleResolve = (response: UploadResponse) => {\n if (isSettled) return;\n isSettled = true;\n resolve(response);\n };\n\n const abortUpload = () => {\n uploadTask.cancel();\n settleReject(new Error('Upload cancelled'));\n };\n\n if (signal?.aborted) {\n abortUpload();\n return;\n }\n\n signal?.addEventListener('abort', abortUpload, { once: true });\n\n uploadTask.on(\n 'state_changed',\n snapshot => {\n const percent = snapshot.totalBytes\n ? Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100)\n : 0;\n\n onProgress(percent);\n },\n error => {\n signal?.removeEventListener('abort', abortUpload);\n settleReject(error);\n },\n async () => {\n signal?.removeEventListener('abort', abortUpload);\n\n try {\n const url = await getDownloadURL(uploadTask.snapshot.ref);\n\n settleResolve({\n url,\n provider: 'firebase',\n key: uploadTask.snapshot.ref.fullPath,\n });\n } catch (error) {\n settleReject(error as Error);\n }\n }\n );\n });\n }\n\n private buildObjectPath(folder: string, fileName: string) {\n return [folder, fileName]\n .filter(Boolean)\n .join('/')\n .replace(/\\/{2,}/g, '/')\n .replace(/^\\//, '');\n }\n\n private getOrCreateApp(firebaseConfig: FirebaseOptions, appName?: string) {\n if (!appName) {\n return getApps().length > 0 ? getApp() : initializeApp(firebaseConfig);\n }\n\n const existingApp = getApps().find(app => app.name === appName);\n return existingApp || initializeApp(firebaseConfig, appName);\n }\n\n private toStringRecord(metadata?: Record<string, any>) {\n if (!metadata) return undefined;\n\n return Object.entries(metadata).reduce<Record<string, string>>((result, [key, value]) => {\n if (value !== undefined && value !== null) {\n result[key] = String(value);\n }\n\n return result;\n }, {});\n }\n}\n","import type { FirebaseOptions } from 'firebase/app';\nimport { UploadManager } from '../core/UploadManager';\nimport type { UploadProvider } from '../core/types';\nimport { FirebaseStorageUploader } from '../providers/FirebaseStorageUploader';\nimport { S3Uploader } from '../providers/S3Uploader';\n\nexport interface CreateClientUploadManagerConfig {\n provider?: UploadProvider | string;\n concurrency?: number;\n s3?: {\n apiBaseUrl?: string;\n publicUrl?: string;\n };\n firebase?: {\n firebaseConfig?: FirebaseOptions;\n bucketUrl?: string;\n appName?: string;\n };\n}\n\nconst normalizeProvider = (provider: CreateClientUploadManagerConfig['provider']) =>\n (provider || 's3').toLowerCase();\n\nconst hasFirebaseConfig = (config?: FirebaseOptions) =>\n Boolean(config?.apiKey && config?.authDomain && config?.projectId && config?.storageBucket && config?.appId);\n\nexport const createClientUploadManager = (config: CreateClientUploadManagerConfig = {}) => {\n const provider = normalizeProvider(config.provider);\n const concurrency = config.concurrency ?? 3;\n\n if (provider === 'firebase') {\n if (!hasFirebaseConfig(config.firebase?.firebaseConfig)) {\n throw new Error('Missing required Firebase configuration for upload provider \"firebase\".');\n }\n\n return new UploadManager(\n new FirebaseStorageUploader({\n firebaseConfig: config.firebase!.firebaseConfig!,\n bucketUrl: config.firebase?.bucketUrl,\n appName: config.firebase?.appName,\n }),\n concurrency\n );\n }\n\n if (!config.s3?.apiBaseUrl || !config.s3?.publicUrl) {\n throw new Error('Missing required S3 configuration: \"apiBaseUrl\" and \"publicUrl\" are required.');\n }\n\n return new UploadManager(\n new S3Uploader({\n apiBaseUrl: config.s3.apiBaseUrl,\n publicUrl: config.s3.publicUrl,\n }),\n concurrency\n );\n};\n","import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n\nexport interface PresignConfig {\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n bucket: string;\n publicUrl: string;\n expiresIn?: number;\n}\n\nexport interface PresignRequest {\n fileName: string;\n contentType: string;\n folder?: string;\n}\n\nexport function createS3PresignHandler(config: PresignConfig) {\n const s3 = new S3Client({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n\n return async function generatePresignedUpload({\n fileName,\n contentType,\n folder,\n }: PresignRequest) {\n if (!fileName || !contentType) {\n throw new Error('fileName and contentType are required');\n }\n\n const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n const key = `${folder || 'uploads'}/${Date.now()}-${safeFileName}`;\n\n const command = new PutObjectCommand({\n Bucket: config.bucket,\n Key: key,\n ContentType: contentType,\n });\n\n const signedUrl = await getSignedUrl(s3, command, {\n expiresIn: config.expiresIn ?? 300,\n });\n\n return {\n signedUrl,\n key,\n publicUrl: `${config.publicUrl}/${key}`,\n };\n };\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signskart/uploader",
3
- "version": "2.0.12",
3
+ "version": "2.0.13",
4
4
  "description": "Upload manager SDK for Node.js, React, Vite, and Next.js",
5
5
  "author": "Signskart",
6
6
  "license": "MIT",
@@ -24,12 +24,14 @@
24
24
  }
25
25
  },
26
26
  "scripts": {
27
+ "dev": "tsup --watch",
27
28
  "build": "tsup",
28
29
  "prepublishOnly": "npm run build"
29
30
  },
30
31
  "dependencies": {
31
32
  "@aws-sdk/client-s3": "^3.540.0",
32
- "@aws-sdk/s3-request-presigner": "^3.540.0"
33
+ "@aws-sdk/s3-request-presigner": "^3.540.0",
34
+ "firebase": "^12.12.1"
33
35
  },
34
36
  "devDependencies": {
35
37
  "tsup": "^8.0.0",