@signskart/uploader 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -143,7 +143,9 @@ var S3Uploader = class extends BaseUploader {
|
|
|
143
143
|
contentType: options.file.type
|
|
144
144
|
})
|
|
145
145
|
});
|
|
146
|
-
|
|
146
|
+
if (!presignRes.ok) throw new Error("Failed to get presigned URL");
|
|
147
|
+
const { signedUrl, key } = await presignRes.json();
|
|
148
|
+
if (!signedUrl || !key) throw new Error("Invalid presign response");
|
|
147
149
|
return new Promise((resolve, reject) => {
|
|
148
150
|
const xhr = new XMLHttpRequest();
|
|
149
151
|
xhr.upload.onprogress = (event) => {
|
|
@@ -153,17 +155,21 @@ var S3Uploader = class extends BaseUploader {
|
|
|
153
155
|
};
|
|
154
156
|
xhr.onload = () => {
|
|
155
157
|
if (xhr.status === 200) {
|
|
156
|
-
resolve({
|
|
158
|
+
resolve({
|
|
159
|
+
url: `${this.config.publicUrl}/${key}`,
|
|
160
|
+
provider: "s3",
|
|
161
|
+
key
|
|
162
|
+
});
|
|
157
163
|
} else {
|
|
158
|
-
reject(new Error(
|
|
164
|
+
reject(new Error(`S3 upload failed with status ${xhr.status}`));
|
|
159
165
|
}
|
|
160
166
|
};
|
|
161
|
-
xhr.onerror = () => reject(new Error("S3 upload
|
|
167
|
+
xhr.onerror = () => reject(new Error("S3 upload network error"));
|
|
162
168
|
signal?.addEventListener("abort", () => {
|
|
163
169
|
xhr.abort();
|
|
164
170
|
reject(new Error("Upload cancelled"));
|
|
165
171
|
});
|
|
166
|
-
xhr.open("PUT",
|
|
172
|
+
xhr.open("PUT", signedUrl);
|
|
167
173
|
xhr.setRequestHeader("Content-Type", options.file.type);
|
|
168
174
|
xhr.send(options.file);
|
|
169
175
|
});
|
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 const { uploadUrl, key } = await presignRes.json();\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 if (xhr.status === 200) {\n resolve({ url: `${this.config.publicUrl}/${key}`, provider: 's3' });\n } else {\n reject(new Error('S3 upload failed'));\n }\n };\n\n xhr.onerror = () => reject(new Error('S3 upload failed'));\n\n signal?.addEventListener('abort', () => {\n xhr.abort();\n reject(new Error('Upload cancelled'));\n });\n\n xhr.open('PUT', uploadUrl);\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/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":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -114,7 +114,9 @@ var S3Uploader = class extends BaseUploader {
|
|
|
114
114
|
contentType: options.file.type
|
|
115
115
|
})
|
|
116
116
|
});
|
|
117
|
-
|
|
117
|
+
if (!presignRes.ok) throw new Error("Failed to get presigned URL");
|
|
118
|
+
const { signedUrl, key } = await presignRes.json();
|
|
119
|
+
if (!signedUrl || !key) throw new Error("Invalid presign response");
|
|
118
120
|
return new Promise((resolve, reject) => {
|
|
119
121
|
const xhr = new XMLHttpRequest();
|
|
120
122
|
xhr.upload.onprogress = (event) => {
|
|
@@ -124,17 +126,21 @@ var S3Uploader = class extends BaseUploader {
|
|
|
124
126
|
};
|
|
125
127
|
xhr.onload = () => {
|
|
126
128
|
if (xhr.status === 200) {
|
|
127
|
-
resolve({
|
|
129
|
+
resolve({
|
|
130
|
+
url: `${this.config.publicUrl}/${key}`,
|
|
131
|
+
provider: "s3",
|
|
132
|
+
key
|
|
133
|
+
});
|
|
128
134
|
} else {
|
|
129
|
-
reject(new Error(
|
|
135
|
+
reject(new Error(`S3 upload failed with status ${xhr.status}`));
|
|
130
136
|
}
|
|
131
137
|
};
|
|
132
|
-
xhr.onerror = () => reject(new Error("S3 upload
|
|
138
|
+
xhr.onerror = () => reject(new Error("S3 upload network error"));
|
|
133
139
|
signal?.addEventListener("abort", () => {
|
|
134
140
|
xhr.abort();
|
|
135
141
|
reject(new Error("Upload cancelled"));
|
|
136
142
|
});
|
|
137
|
-
xhr.open("PUT",
|
|
143
|
+
xhr.open("PUT", signedUrl);
|
|
138
144
|
xhr.setRequestHeader("Content-Type", options.file.type);
|
|
139
145
|
xhr.send(options.file);
|
|
140
146
|
});
|
package/dist/index.mjs.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"],"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 const { uploadUrl, key } = await presignRes.json();\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 if (xhr.status === 200) {\n resolve({ url: `${this.config.publicUrl}/${key}`, provider: 's3' });\n } else {\n reject(new Error('S3 upload failed'));\n }\n };\n\n xhr.onerror = () => reject(new Error('S3 upload failed'));\n\n signal?.addEventListener('abort', () => {\n xhr.abort();\n reject(new Error('Upload cancelled'));\n });\n\n xhr.open('PUT', uploadUrl);\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"],"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":";AAEO,IAAM,eAAN,MAAsB;AAAA,EAAtB;AACH,SAAQ,YAA2B,CAAC;AAAA;AAAA,EAEpC,UAAU,UAAuB;AAC7B,SAAK,UAAU,KAAK,QAAQ;AAC5B,WAAO,MAAM;AACT,WAAK,YAAY,KAAK,UAAU,OAAO,OAAK,MAAM,QAAQ;AAAA,IAC9D;AAAA,EACJ;AAAA,EAEA,KAAK,SAAY;AACb,SAAK,UAAU,QAAQ,cAAY,SAAS,OAAO,CAAC;AAAA,EACxD;AACJ;;;ACXO,IAAM,aAAN,MAAiB;AAAA,EAMpB,YACY,UACA,SACA,aAAa,GACb,aAAa,KACvB;AAJU;AACA;AACA;AACA;AARZ,SAAO,SAAS,IAAI,aAA8B;AAClD,SAAQ,kBAAkB,IAAI,gBAAgB;AAC9C,SAAQ,UAAU;AAQd,SAAK,QAAQ;AAAA,MACT,IAAI,OAAO,WAAW;AAAA,MACtB,UAAU;AAAA,MACV,QAAQ;AAAA,IACZ;AAAA,EACJ;AAAA,EAEA,MAAM,QAAuB;AACzB,SAAK,OAAO,EAAE,QAAQ,YAAY,CAAC;AAEnC,WAAO,KAAK,WAAW,KAAK,YAAY;AACpC,UAAI;AACA,cAAM,WAAW,MAAM,KAAK,SAAS;AAAA,UACjC,KAAK;AAAA,UACL,cAAY,KAAK,OAAO,EAAE,SAAS,CAAC;AAAA,UACpC,KAAK,gBAAgB;AAAA,QACzB;AAEA,aAAK,OAAO,EAAE,QAAQ,WAAW,UAAU,KAAK,SAAS,CAAC;AAC1D;AAAA,MACJ,SAAS,OAAgB;AACrB,YAAI,KAAK,gBAAgB,OAAO,SAAS;AACrC,eAAK,OAAO,EAAE,QAAQ,YAAY,CAAC;AACnC;AAAA,QACJ;AAEA,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AAEzD,YAAI,KAAK,WAAW,KAAK,YAAY;AACjC,eAAK,OAAO,EAAE,QAAQ,SAAS,OAAO,QAAQ,CAAC;AAC/C;AAAA,QACJ;AAEA,aAAK;AACL,cAAM,KAAK,MAAM,KAAK,aAAa,KAAK,IAAI,GAAG,KAAK,UAAU,CAAC,CAAC;AAAA,MACpE;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,SAAS;AACL,SAAK,gBAAgB,MAAM;AAC3B,SAAK,OAAO,EAAE,QAAQ,YAAY,CAAC;AAAA,EACvC;AAAA,EAEQ,OAAO,QAAkC;AAC7C,SAAK,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO;AACxC,SAAK,OAAO,KAAK,KAAK,KAAK;AAAA,EAC/B;AAAA,EAEQ,MAAM,IAAY;AACtB,WAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AACJ;;;AChEO,IAAM,gBAAN,MAAoB;AAAA,EAIvB,YAAoB,UAAgC,cAAc,GAAG;AAAjD;AAAgC;AAHpD,SAAQ,QAAsB,CAAC;AAC/B,SAAQ,SAAS;AAAA,EAEsD;AAAA,EAEvE,IAAI,SAAwB;AACxB,UAAM,OAAO,IAAI,WAAW,KAAK,UAAU,OAAO;AAClD,SAAK,MAAM,KAAK,IAAI;AACpB,SAAK,QAAQ;AACb,WAAO;AAAA,EACX;AAAA,EAEA,MAAc,UAAU;AACpB,QAAI,KAAK,UAAU,KAAK,YAAa;AAErC,UAAM,OAAO,KAAK,MAAM,KAAK,OAAK,EAAE,MAAM,WAAW,QAAQ;AAC7D,QAAI,CAAC,KAAM;AAEX,SAAK;AACL,UAAM,KAAK,MAAM;AACjB,SAAK;AACL,SAAK,QAAQ;AAAA,EACjB;AACJ;;;AC1BO,IAAe,eAAf,MAA4B;AAEnC;;;ACIO,IAAM,aAAN,cAAyB,aAAa;AAAA,EACzC,YAAoB,QAAkB;AAClC,UAAM;AADU;AAAA,EAEpB;AAAA,EAEA,MAAM,OACF,SACA,YACA,QACuB;AACvB,UAAM,aAAa,MAAM,MAAM,GAAG,KAAK,OAAO,UAAU,0BAA0B;AAAA,MAC9E,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACjB,UAAU,QAAQ,YAAY,QAAQ,KAAK;AAAA,QAC3C,QAAQ,QAAQ;AAAA,QAChB,aAAa,QAAQ,KAAK;AAAA,MAC9B,CAAC;AAAA,IACL,CAAC;AAED,QAAI,CAAC,WAAW,GAAI,OAAM,IAAI,MAAM,6BAA6B;AAEjE,UAAM,EAAE,WAAW,IAAI,IAAI,MAAM,WAAW,KAAK;AAEjD,QAAI,CAAC,aAAa,CAAC,IAAK,OAAM,IAAI,MAAM,0BAA0B;AAElE,WAAO,IAAI,QAAwB,CAAC,SAAS,WAAW;AACpD,YAAM,MAAM,IAAI,eAAe;AAE/B,UAAI,OAAO,aAAa,WAAS;AAC7B,YAAI,MAAM,kBAAkB;AACxB,qBAAW,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG,CAAC;AAAA,QAC7D;AAAA,MACJ;AAEA,UAAI,SAAS,MAAM;AACf,YAAI,IAAI,WAAW,KAAK;AACpB,kBAAQ;AAAA,YACJ,KAAK,GAAG,KAAK,OAAO,SAAS,IAAI,GAAG;AAAA,YACpC,UAAU;AAAA,YACV;AAAA,UACJ,CAAC;AAAA,QACL,OAAO;AACH,iBAAO,IAAI,MAAM,gCAAgC,IAAI,MAAM,EAAE,CAAC;AAAA,QAClE;AAAA,MACJ;AAEA,UAAI,UAAU,MAAM,OAAO,IAAI,MAAM,yBAAyB,CAAC;AAE/D,cAAQ,iBAAiB,SAAS,MAAM;AACpC,YAAI,MAAM;AACV,eAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA,MACxC,CAAC;AAED,UAAI,KAAK,OAAO,SAAS;AACzB,UAAI,iBAAiB,gBAAgB,QAAQ,KAAK,IAAI;AACtD,UAAI,KAAK,QAAQ,IAAI;AAAA,IACzB,CAAC;AAAA,EACL;AACJ;;;AC3DO,IAAM,qBAAN,cAAiC,aAAa;AAAA,EACjD,YAAoB,QAA0B;AAC1C,UAAM;AADU;AAAA,EAEpB;AAAA,EAEA,MAAM,OACF,SACA,YACuB;AACvB,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,QAAQ,QAAQ,IAAI;AACpC,aAAS,OAAO,iBAAiB,KAAK,OAAO,YAAY;AACzD,aAAS,OAAO,UAAU,QAAQ,MAAM;AAExC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,YAAM,MAAM,IAAI,eAAe;AAE/B,UAAI,OAAO,aAAa,WAAS;AAC7B,YAAI,MAAM,kBAAkB;AACxB,qBAAW,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG,CAAC;AAAA,QAC7D;AAAA,MACJ;AAEA,UAAI,SAAS,MAAM;AACf,cAAM,OAAO,KAAK,MAAM,IAAI,YAAY;AACxC,YAAI,IAAI,WAAW,OAAO,KAAK,YAAY;AACvC,kBAAQ,EAAE,KAAK,KAAK,YAAY,UAAU,aAAa,CAAC;AAAA,QAC5D,OAAO;AACH,iBAAO,IAAI,MAAM,0BAA0B,CAAC;AAAA,QAChD;AAAA,MACJ;AAEA,UAAI,UAAU,MAAM,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAEhE,UAAI,KAAK,QAAQ,mCAAmC,KAAK,OAAO,SAAS,SAAS;AAClF,UAAI,KAAK,QAAQ;AAAA,IACrB,CAAC;AAAA,EACL;AACJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signskart/uploader",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Production-grade upload manager SDK with queue, progress tracking, retry logic, and multi-provider support (S3, Cloudinary).",
|
|
5
5
|
"author": "Signskart",
|
|
6
6
|
"license": "MIT",
|