@signskart/uploader 1.0.8 → 2.0.2

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
@@ -1,21 +1,41 @@
1
- # @signskart/uploader
1
+ Here is your **fully updated, production-ready README** including:
2
2
 
3
- Production-grade Upload Manager SDK by Signskart.
3
+ * Client + Server usage
4
+ * ✅ Proper S3 presign setup
5
+ * ✅ Next.js backend example
6
+ * ✅ Express backend example
7
+ * ✅ Architecture explanation
8
+ * ✅ Clean professional structure
9
+ * ✅ v2 structure with `/server` export
4
10
 
5
- Features:
11
+ You can replace your entire README with this.
6
12
 
7
- - 🚀 Upload queue
8
- - 🔁 Retry logic with exponential backoff
9
- - 📊 Real-time progress tracking
10
- - ❌ Cancel uploads
11
- - Concurrency control
12
- - ☁ Multi-provider support (S3, Cloudinary)
13
- - 🧠 Fully typed (TypeScript)
14
- - 🌍 Works with React, Vue, Next.js, Vite
13
+ ---
14
+
15
+ # 🚀 @signskart/uploader
16
+
17
+ Production-grade Upload Manager SDK by **Signskart**.
18
+
19
+ A powerful, fully-typed file upload SDK with queue management, retry logic, cancellation support, concurrency control, and multi-provider architecture (S3 + Cloudinary).
20
+
21
+ ---
22
+
23
+ ## ✨ Features
24
+
25
+ * 🚀 Upload queue system
26
+ * 🔁 Automatic retry with exponential backoff
27
+ * 📊 Real-time progress tracking
28
+ * ❌ Cancel uploads (AbortController support)
29
+ * ⚡ Concurrency control
30
+ * ☁ Multi-provider support (Amazon S3, Cloudinary)
31
+ * 🧠 Fully typed (TypeScript)
32
+ * 🌍 Works with React, Vue, Next.js, Vite, vanilla JS
33
+ * 🔐 Secure S3 presigned upload support
34
+ * 🖥 Optional server helper included
15
35
 
16
36
  ---
17
37
 
18
- ## 📦 Installation
38
+ # 📦 Installation
19
39
 
20
40
  ```bash
21
41
  npm install @signskart/uploader
@@ -29,19 +49,37 @@ yarn add @signskart/uploader
29
49
 
30
50
  ---
31
51
 
52
+ # 🏗 Architecture
53
+
54
+ For **Amazon S3**, uploads require a backend to generate presigned URLs.
55
+
56
+ Flow:
57
+
58
+ Frontend → Backend (presign) → S3 → Frontend uploads directly
59
+
60
+ Your AWS credentials NEVER reach the browser.
61
+
62
+ Cloudinary does NOT require backend if using unsigned preset.
63
+
64
+ ---
65
+
32
66
  # 🚀 Quick Start
33
67
 
34
- ## Using Amazon S3 (Presigned Upload)
68
+ ---
69
+
70
+ # ☁ Using Amazon S3
71
+
72
+ ## 1️⃣ Frontend Setup
35
73
 
36
74
  ```ts
37
75
  import { UploadManager, S3Uploader } from '@signskart/uploader';
38
76
 
39
77
  const uploader = new S3Uploader({
40
- apiBaseUrl: 'https://api.yourbackend.com',
78
+ presignEndpoint: '/api/s3/presign-upload',
41
79
  publicUrl: 'https://cdn.yoursite.com'
42
80
  });
43
81
 
44
- const manager = new UploadManager(uploader, 2); // concurrency = 2
82
+ const manager = new UploadManager(uploader, 2);
45
83
 
46
84
  const task = manager.add({
47
85
  file,
@@ -55,7 +93,70 @@ task.events.subscribe((state) => {
55
93
 
56
94
  ---
57
95
 
58
- ## Using Cloudinary
96
+ ## 2️⃣ Backend Setup (Next.js Example)
97
+
98
+ ```ts
99
+ // app/api/s3/presign-upload/route.ts
100
+
101
+ import { NextRequest, NextResponse } from 'next/server';
102
+ import { createS3PresignHandler } from '@signskart/uploader/server';
103
+
104
+ const generatePresignedUpload = createS3PresignHandler({
105
+ region: process.env.AWS_REGION!,
106
+ accessKeyId: process.env.AWS_KEY!,
107
+ secretAccessKey: process.env.AWS_SECRET!,
108
+ bucket: process.env.AWS_S3_BUCKET!,
109
+ publicUrl: process.env.NEXT_PUBLIC_S3_PUBLIC_URL!,
110
+ });
111
+
112
+ export async function POST(req: NextRequest) {
113
+ try {
114
+ const body = await req.json();
115
+ const result = await generatePresignedUpload(body);
116
+ return NextResponse.json(result);
117
+ } catch (error) {
118
+ return NextResponse.json(
119
+ { error: 'Failed to generate presigned URL' },
120
+ { status: 500 }
121
+ );
122
+ }
123
+ }
124
+ ```
125
+
126
+ ---
127
+
128
+ ## 3️⃣ Backend Setup (Express Example)
129
+
130
+ ```ts
131
+ import express from 'express';
132
+ import { createS3PresignHandler } from '@signskart/uploader/server';
133
+
134
+ const app = express();
135
+ app.use(express.json());
136
+
137
+ const generatePresignedUpload = createS3PresignHandler({
138
+ region: process.env.AWS_REGION!,
139
+ accessKeyId: process.env.AWS_KEY!,
140
+ secretAccessKey: process.env.AWS_SECRET!,
141
+ bucket: process.env.AWS_S3_BUCKET!,
142
+ publicUrl: process.env.S3_PUBLIC_URL!,
143
+ });
144
+
145
+ app.post('/api/s3/presign-upload', async (req, res) => {
146
+ try {
147
+ const result = await generatePresignedUpload(req.body);
148
+ res.json(result);
149
+ } catch (error) {
150
+ res.status(500).json({ error: 'Failed to generate presigned URL' });
151
+ }
152
+ });
153
+
154
+ app.listen(3000);
155
+ ```
156
+
157
+ ---
158
+
159
+ # ☁ Using Cloudinary (No Backend Required)
59
160
 
60
161
  ```ts
61
162
  import { UploadManager, CloudinaryUploader } from '@signskart/uploader';
@@ -77,18 +178,20 @@ const task = manager.add({
77
178
 
78
179
  # 🧠 API Reference
79
180
 
181
+ ---
182
+
80
183
  ## UploadManager
81
184
 
82
185
  ```ts
83
186
  new UploadManager(uploader, concurrency?)
84
187
  ```
85
188
 
86
- ### Parameters:
189
+ ### Parameters
87
190
 
88
- | Parameter | Type | Default |
89
- |-----------|------|----------|
90
- | uploader | BaseUploader | required |
91
- | concurrency | number | 3 |
191
+ | Parameter | Type | Default |
192
+ | ----------- | ------------ | -------- |
193
+ | uploader | BaseUploader | required |
194
+ | concurrency | number | 3 |
92
195
 
93
196
  ---
94
197
 
@@ -100,12 +203,12 @@ Returned from:
100
203
  const task = manager.add(options);
101
204
  ```
102
205
 
103
- ### Properties:
206
+ ### Properties
104
207
 
105
- - `task.state`
106
- - `task.events.subscribe()`
208
+ * `task.state`
209
+ * `task.events.subscribe()`
107
210
 
108
- ### Methods:
211
+ ### Methods
109
212
 
110
213
  ```ts
111
214
  task.start()
@@ -129,9 +232,10 @@ task.cancel()
129
232
 
130
233
  # 🔁 Retry Logic
131
234
 
132
- - Automatic retry
133
- - Exponential backoff
134
- - Default max retries: 2
235
+ * Automatic retry
236
+ * Exponential backoff
237
+ * Default max retries: 2
238
+ * Cancels if AbortController triggered
135
239
 
136
240
  ---
137
241
 
@@ -147,13 +251,25 @@ task.cancel();
147
251
 
148
252
  ```ts
149
253
  task.events.subscribe((state) => {
150
- console.log(state.progress);
254
+ console.log(state.progress, state.status);
151
255
  });
152
256
  ```
153
257
 
258
+ State structure:
259
+
260
+ ```ts
261
+ {
262
+ id: string;
263
+ progress: number;
264
+ status: 'queued' | 'uploading' | 'success' | 'error' | 'cancelled';
265
+ error?: string;
266
+ response?: UploadResponse;
267
+ }
268
+ ```
269
+
154
270
  ---
155
271
 
156
- # 🏗 Example React Usage
272
+ # Example React Usage
157
273
 
158
274
  ```tsx
159
275
  const handleUpload = (file: File) => {
@@ -167,13 +283,60 @@ const handleUpload = (file: File) => {
167
283
 
168
284
  ---
169
285
 
286
+ # 🔐 Security Notes (Important)
287
+
288
+ * AWS credentials must NEVER be exposed to frontend.
289
+ * Always generate presigned URLs server-side.
290
+ * Cloudinary unsigned preset must have restricted permissions.
291
+ * Set proper S3 bucket CORS configuration.
292
+
293
+ ---
294
+
170
295
  # 🛠 Requirements
171
296
 
172
- - Modern browser (AbortController support)
173
- - Backend endpoint for S3 presign (if using S3)
297
+ * Modern browser (AbortController support)
298
+ * Node.js 18+ for server helper
299
+ * Backend endpoint for S3 presign (if using S3)
300
+
301
+ ---
302
+
303
+ # 📦 Package Exports
304
+
305
+ Client:
306
+
307
+ ```ts
308
+ import { UploadManager } from '@signskart/uploader';
309
+ ```
310
+
311
+ Server helper:
312
+
313
+ ```ts
314
+ import { createS3PresignHandler } from '@signskart/uploader/server';
315
+ ```
316
+
317
+ ---
318
+
319
+ # 📈 Roadmap
320
+
321
+ * Multipart uploads (100MB+)
322
+ * Image compression plugin
323
+ * Validation middleware
324
+ * Signed download URLs
325
+ * React hooks wrapper
174
326
 
175
327
  ---
176
328
 
177
329
  # 📜 License
178
330
 
179
- MIT © Signskart
331
+ MIT © Signskart
332
+
333
+ ---
334
+
335
+ If you want next, I can:
336
+
337
+ * Turn this into an elite-level open source README (badges, diagrams, examples, comparison table)
338
+ * Prepare it for GitHub launch
339
+ * Write marketing description for npm
340
+ * Add proper semantic versioning strategy
341
+
342
+ Tell me your next move.
package/dist/index.js CHANGED
@@ -1,31 +1,4 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- CloudinaryUploader: () => CloudinaryUploader,
24
- S3Uploader: () => S3Uploader,
25
- UploadManager: () => UploadManager,
26
- UploadTask: () => UploadTask
27
- });
28
- module.exports = __toCommonJS(index_exports);
1
+ 'use strict';
29
2
 
30
3
  // src/core/EventEmitter.ts
31
4
  var EventEmitter = class {
@@ -208,11 +181,10 @@ var CloudinaryUploader = class extends BaseUploader {
208
181
  });
209
182
  }
210
183
  };
211
- // Annotate the CommonJS export names for ESM import in node:
212
- 0 && (module.exports = {
213
- CloudinaryUploader,
214
- S3Uploader,
215
- UploadManager,
216
- UploadTask
217
- });
184
+
185
+ exports.CloudinaryUploader = CloudinaryUploader;
186
+ exports.S3Uploader = S3Uploader;
187
+ exports.UploadManager = UploadManager;
188
+ exports.UploadTask = UploadTask;
189
+ //# sourceMappingURL=index.js.map
218
190
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/EventEmitter.ts","../src/core/UploadTask.ts","../src/core/UploadManager.ts","../src/core/BaseUploader.ts","../src/providers/S3Uploader.ts","../src/providers/CloudinaryUploader.ts"],"sourcesContent":["export * from './core/types';\nexport * from './core/UploadManager';\nexport * from './core/UploadTask';\nexport * from './providers/S3Uploader';\nexport * from './providers/CloudinaryUploader';","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}"],"mappings":"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","names":[]}
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"],"names":[],"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}"]}
package/dist/index.mjs CHANGED
@@ -179,10 +179,7 @@ var CloudinaryUploader = class extends BaseUploader {
179
179
  });
180
180
  }
181
181
  };
182
- export {
183
- CloudinaryUploader,
184
- S3Uploader,
185
- UploadManager,
186
- UploadTask
187
- };
182
+
183
+ export { CloudinaryUploader, S3Uploader, UploadManager, UploadTask };
184
+ //# sourceMappingURL=index.mjs.map
188
185
  //# 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"],"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}"],"mappings":"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","names":[]}
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"],"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}"]}
@@ -0,0 +1,20 @@
1
+ interface PresignConfig {
2
+ region: string;
3
+ accessKeyId: string;
4
+ secretAccessKey: string;
5
+ bucket: string;
6
+ publicUrl: string;
7
+ expiresIn?: number;
8
+ }
9
+ interface PresignRequest {
10
+ fileName: string;
11
+ contentType: string;
12
+ folder?: string;
13
+ }
14
+ declare function createS3PresignHandler(config: PresignConfig): ({ fileName, contentType, folder, }: PresignRequest) => Promise<{
15
+ signedUrl: string;
16
+ key: string;
17
+ publicUrl: string;
18
+ }>;
19
+
20
+ export { type PresignConfig, type PresignRequest, createS3PresignHandler };
@@ -0,0 +1,20 @@
1
+ interface PresignConfig {
2
+ region: string;
3
+ accessKeyId: string;
4
+ secretAccessKey: string;
5
+ bucket: string;
6
+ publicUrl: string;
7
+ expiresIn?: number;
8
+ }
9
+ interface PresignRequest {
10
+ fileName: string;
11
+ contentType: string;
12
+ folder?: string;
13
+ }
14
+ declare function createS3PresignHandler(config: PresignConfig): ({ fileName, contentType, folder, }: PresignRequest) => Promise<{
15
+ signedUrl: string;
16
+ key: string;
17
+ publicUrl: string;
18
+ }>;
19
+
20
+ export { type PresignConfig, type PresignRequest, createS3PresignHandler };
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ var clientS3 = require('@aws-sdk/client-s3');
4
+ var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
5
+
6
+ // src/server/createS3PresignHandler.ts
7
+ function createS3PresignHandler(config) {
8
+ const s3 = new clientS3.S3Client({
9
+ region: config.region,
10
+ credentials: {
11
+ accessKeyId: config.accessKeyId,
12
+ secretAccessKey: config.secretAccessKey
13
+ }
14
+ });
15
+ return async function generatePresignedUpload({
16
+ fileName,
17
+ contentType,
18
+ folder
19
+ }) {
20
+ if (!fileName || !contentType) {
21
+ throw new Error("fileName and contentType are required");
22
+ }
23
+ const key = `${folder || "uploads"}/${Date.now()}-${fileName}`;
24
+ const command = new clientS3.PutObjectCommand({
25
+ Bucket: config.bucket,
26
+ Key: key,
27
+ ContentType: contentType
28
+ });
29
+ const signedUrl = await s3RequestPresigner.getSignedUrl(s3, command, {
30
+ expiresIn: config.expiresIn ?? 300
31
+ });
32
+ return {
33
+ signedUrl,
34
+ key,
35
+ publicUrl: `${config.publicUrl}/${key}`
36
+ };
37
+ };
38
+ }
39
+
40
+ exports.createS3PresignHandler = createS3PresignHandler;
41
+ //# sourceMappingURL=index.js.map
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/createS3PresignHandler.ts"],"names":["S3Client","PutObjectCommand","getSignedUrl"],"mappings":";;;;;;AAkBO,SAAS,uBAAuB,MAAA,EAAuB;AAC1D,EAAA,MAAM,EAAA,GAAK,IAAIA,iBAAA,CAAS;AAAA,IACpB,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,WAAA,EAAa;AAAA,MACT,aAAa,MAAA,CAAO,WAAA;AAAA,MACpB,iBAAiB,MAAA,CAAO;AAAA;AAC5B,GACH,CAAA;AAED,EAAA,OAAO,eAAe,uBAAA,CAAwB;AAAA,IAC1C,QAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACJ,EAAmB;AACf,IAAA,IAAI,CAAC,QAAA,IAAY,CAAC,WAAA,EAAa;AAC3B,MAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,IAC3D;AAEA,IAAA,MAAM,GAAA,GAAM,GAAG,MAAA,IAAU,SAAS,IAAI,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AAE5D,IAAA,MAAM,OAAA,GAAU,IAAIC,yBAAA,CAAiB;AAAA,MACjC,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,GAAA,EAAK,GAAA;AAAA,MACL,WAAA,EAAa;AAAA,KAChB,CAAA;AAED,IAAA,MAAM,SAAA,GAAY,MAAMC,+BAAA,CAAa,EAAA,EAAI,OAAA,EAAS;AAAA,MAC9C,SAAA,EAAW,OAAO,SAAA,IAAa;AAAA,KAClC,CAAA;AAED,IAAA,OAAO;AAAA,MACH,SAAA;AAAA,MACA,GAAA;AAAA,MACA,SAAA,EAAW,CAAA,EAAG,MAAA,CAAO,SAAS,IAAI,GAAG,CAAA;AAAA,KACzC;AAAA,EACJ,CAAA;AACJ","file":"index.js","sourcesContent":["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 key = `${folder || 'uploads'}/${Date.now()}-${fileName}`;\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}"]}
@@ -0,0 +1,40 @@
1
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
2
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
+
4
+ // src/server/createS3PresignHandler.ts
5
+ function createS3PresignHandler(config) {
6
+ const s3 = new S3Client({
7
+ region: config.region,
8
+ credentials: {
9
+ accessKeyId: config.accessKeyId,
10
+ secretAccessKey: config.secretAccessKey
11
+ }
12
+ });
13
+ return async function generatePresignedUpload({
14
+ fileName,
15
+ contentType,
16
+ folder
17
+ }) {
18
+ if (!fileName || !contentType) {
19
+ throw new Error("fileName and contentType are required");
20
+ }
21
+ const key = `${folder || "uploads"}/${Date.now()}-${fileName}`;
22
+ const command = new PutObjectCommand({
23
+ Bucket: config.bucket,
24
+ Key: key,
25
+ ContentType: contentType
26
+ });
27
+ const signedUrl = await getSignedUrl(s3, command, {
28
+ expiresIn: config.expiresIn ?? 300
29
+ });
30
+ return {
31
+ signedUrl,
32
+ key,
33
+ publicUrl: `${config.publicUrl}/${key}`
34
+ };
35
+ };
36
+ }
37
+
38
+ export { createS3PresignHandler };
39
+ //# sourceMappingURL=index.mjs.map
40
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/createS3PresignHandler.ts"],"names":[],"mappings":";;;;AAkBO,SAAS,uBAAuB,MAAA,EAAuB;AAC1D,EAAA,MAAM,EAAA,GAAK,IAAI,QAAA,CAAS;AAAA,IACpB,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,WAAA,EAAa;AAAA,MACT,aAAa,MAAA,CAAO,WAAA;AAAA,MACpB,iBAAiB,MAAA,CAAO;AAAA;AAC5B,GACH,CAAA;AAED,EAAA,OAAO,eAAe,uBAAA,CAAwB;AAAA,IAC1C,QAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACJ,EAAmB;AACf,IAAA,IAAI,CAAC,QAAA,IAAY,CAAC,WAAA,EAAa;AAC3B,MAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,IAC3D;AAEA,IAAA,MAAM,GAAA,GAAM,GAAG,MAAA,IAAU,SAAS,IAAI,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AAE5D,IAAA,MAAM,OAAA,GAAU,IAAI,gBAAA,CAAiB;AAAA,MACjC,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,GAAA,EAAK,GAAA;AAAA,MACL,WAAA,EAAa;AAAA,KAChB,CAAA;AAED,IAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,EAAA,EAAI,OAAA,EAAS;AAAA,MAC9C,SAAA,EAAW,OAAO,SAAA,IAAa;AAAA,KAClC,CAAA;AAED,IAAA,OAAO;AAAA,MACH,SAAA;AAAA,MACA,GAAA;AAAA,MACA,SAAA,EAAW,CAAA,EAAG,MAAA,CAAO,SAAS,IAAI,GAAG,CAAA;AAAA,KACzC;AAAA,EACJ,CAAA;AACJ","file":"index.mjs","sourcesContent":["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 key = `${folder || 'uploads'}/${Date.now()}-${fileName}`;\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,7 +1,7 @@
1
1
  {
2
2
  "name": "@signskart/uploader",
3
- "version": "1.0.8",
4
- "description": "Production-grade upload manager SDK with queue, progress tracking, retry logic, and multi-provider support (S3, Cloudinary).",
3
+ "version": "2.0.2",
4
+ "description": "Production-grade upload manager SDK with queue, progress tracking, retry logic, cancellation, and multi-provider support (S3, Cloudinary).",
5
5
  "author": "Signskart",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.cjs",
@@ -14,12 +14,24 @@
14
14
  ".": {
15
15
  "import": "./dist/index.js",
16
16
  "require": "./dist/index.cjs"
17
+ },
18
+ "./server": {
19
+ "import": "./dist/server/index.js",
20
+ "require": "./dist/server/index.cjs"
17
21
  }
18
22
  },
19
23
  "scripts": {
20
24
  "build": "tsup",
21
25
  "prepublishOnly": "npm run build"
22
26
  },
27
+ "dependencies": {
28
+ "@aws-sdk/client-s3": "^3.540.0",
29
+ "@aws-sdk/s3-request-presigner": "^3.540.0"
30
+ },
31
+ "devDependencies": {
32
+ "tsup": "^8.0.0",
33
+ "typescript": "^5.0.0"
34
+ },
23
35
  "keywords": [
24
36
  "signskart",
25
37
  "upload",
@@ -29,9 +41,5 @@
29
41
  "typescript",
30
42
  "file-upload",
31
43
  "sdk"
32
- ],
33
- "devDependencies": {
34
- "tsup": "^8.0.0",
35
- "typescript": "^5.0.0"
36
- }
44
+ ]
37
45
  }