@marlinjai/storage-brain-sdk 0.1.0 → 0.2.0
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.cjs +86 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +86 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -198,44 +198,30 @@ var StorageBrain = class {
|
|
|
198
198
|
async requestUpload(params) {
|
|
199
199
|
return this.request("POST", "/api/v1/upload/request", params);
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Check if running in a browser environment
|
|
203
|
+
*/
|
|
204
|
+
isBrowser() {
|
|
205
|
+
return typeof window !== "undefined" && typeof XMLHttpRequest !== "undefined";
|
|
206
|
+
}
|
|
201
207
|
/**
|
|
202
208
|
* Upload file content to presigned URL
|
|
209
|
+
* Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js
|
|
203
210
|
*/
|
|
204
211
|
async uploadToPresignedUrl(presignedUrl, file, contentType, onProgress, signal) {
|
|
205
212
|
const uploadUrl = presignedUrl.startsWith("/") ? `${this.baseUrl}${presignedUrl}` : presignedUrl;
|
|
213
|
+
const headers = {
|
|
214
|
+
"Content-Type": contentType
|
|
215
|
+
};
|
|
216
|
+
if (presignedUrl.startsWith("/")) {
|
|
217
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
218
|
+
}
|
|
206
219
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
xhr.addEventListener("load", () => {
|
|
215
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
216
|
-
resolve();
|
|
217
|
-
} else {
|
|
218
|
-
reject(new UploadError(`Upload failed with status ${xhr.status}`));
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
xhr.addEventListener("error", () => {
|
|
222
|
-
reject(new NetworkError("Network error during upload"));
|
|
223
|
-
});
|
|
224
|
-
xhr.addEventListener("abort", () => {
|
|
225
|
-
reject(new UploadError("Upload was cancelled"));
|
|
226
|
-
});
|
|
227
|
-
if (signal) {
|
|
228
|
-
signal.addEventListener("abort", () => {
|
|
229
|
-
xhr.abort();
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
xhr.open("PUT", uploadUrl);
|
|
233
|
-
xhr.setRequestHeader("Content-Type", contentType);
|
|
234
|
-
if (presignedUrl.startsWith("/")) {
|
|
235
|
-
xhr.setRequestHeader("Authorization", `Bearer ${this.apiKey}`);
|
|
236
|
-
}
|
|
237
|
-
xhr.send(file);
|
|
238
|
-
});
|
|
220
|
+
if (this.isBrowser()) {
|
|
221
|
+
await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);
|
|
222
|
+
} else {
|
|
223
|
+
await this.uploadWithFetch(uploadUrl, file, headers, onProgress, signal);
|
|
224
|
+
}
|
|
239
225
|
} catch (error) {
|
|
240
226
|
if (error instanceof StorageBrainError) {
|
|
241
227
|
throw error;
|
|
@@ -246,6 +232,74 @@ var StorageBrain = class {
|
|
|
246
232
|
);
|
|
247
233
|
}
|
|
248
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Upload using XMLHttpRequest (browser only, supports progress)
|
|
237
|
+
*/
|
|
238
|
+
uploadWithXHR(url, file, headers, onProgress, signal) {
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
const xhr = new XMLHttpRequest();
|
|
241
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
242
|
+
if (event.lengthComputable && onProgress) {
|
|
243
|
+
onProgress(Math.round(event.loaded / event.total * 100));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
xhr.addEventListener("load", () => {
|
|
247
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
248
|
+
resolve();
|
|
249
|
+
} else {
|
|
250
|
+
reject(new UploadError(`Upload failed with status ${xhr.status}`));
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
xhr.addEventListener("error", () => {
|
|
254
|
+
reject(new NetworkError("Network error during upload"));
|
|
255
|
+
});
|
|
256
|
+
xhr.addEventListener("abort", () => {
|
|
257
|
+
reject(new UploadError("Upload was cancelled"));
|
|
258
|
+
});
|
|
259
|
+
if (signal) {
|
|
260
|
+
signal.addEventListener("abort", () => {
|
|
261
|
+
xhr.abort();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
xhr.open("PUT", url);
|
|
265
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
266
|
+
xhr.setRequestHeader(key, value);
|
|
267
|
+
}
|
|
268
|
+
xhr.send(file);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Upload using fetch (works in Node.js and browsers, limited progress support)
|
|
273
|
+
*/
|
|
274
|
+
async uploadWithFetch(url, file, headers, onProgress, signal) {
|
|
275
|
+
onProgress?.(0);
|
|
276
|
+
const body = await this.blobToBuffer(file);
|
|
277
|
+
const response = await fetch(url, {
|
|
278
|
+
method: "PUT",
|
|
279
|
+
headers,
|
|
280
|
+
body,
|
|
281
|
+
signal
|
|
282
|
+
});
|
|
283
|
+
if (!response.ok) {
|
|
284
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
285
|
+
throw new UploadError(`Upload failed with status ${response.status}: ${errorText}`);
|
|
286
|
+
}
|
|
287
|
+
onProgress?.(100);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Convert Blob to ArrayBuffer for Node.js fetch compatibility
|
|
291
|
+
*/
|
|
292
|
+
async blobToBuffer(blob) {
|
|
293
|
+
if (typeof blob.arrayBuffer === "function") {
|
|
294
|
+
return blob.arrayBuffer();
|
|
295
|
+
}
|
|
296
|
+
return new Promise((resolve, reject) => {
|
|
297
|
+
const reader = new FileReader();
|
|
298
|
+
reader.onload = () => resolve(reader.result);
|
|
299
|
+
reader.onerror = () => reject(new Error("Failed to read file"));
|
|
300
|
+
reader.readAsArrayBuffer(blob);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
249
303
|
/**
|
|
250
304
|
* Wait for file processing to complete
|
|
251
305
|
*/
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Allowed MIME types for file uploads\n */\nexport const ALLOWED_FILE_TYPES = {\n // Images\n 'image/jpeg': { extension: 'jpg', category: 'image' },\n 'image/png': { extension: 'png', category: 'image' },\n 'image/webp': { extension: 'webp', category: 'image' },\n 'image/gif': { extension: 'gif', category: 'image' },\n 'image/avif': { extension: 'avif', category: 'image' },\n // Documents\n 'application/pdf': { extension: 'pdf', category: 'document' },\n} as const;\n\nexport type AllowedMimeType = keyof typeof ALLOWED_FILE_TYPES;\n\nexport const ALLOWED_MIME_TYPES = Object.keys(ALLOWED_FILE_TYPES) as AllowedMimeType[];\n\nexport const IMAGE_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'image'\n);\n\nexport const DOCUMENT_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'document'\n);\n\n/**\n * Processing contexts\n */\nexport const PROCESSING_CONTEXTS = ['newsletter', 'invoice', 'framer-site', 'default'] as const;\nexport type ProcessingContext = (typeof PROCESSING_CONTEXTS)[number];\n\n/**\n * File size limits\n */\nexport const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024; // 100MB per file\n\n/**\n * Thumbnail sizes\n */\nexport const THUMBNAIL_SIZES = {\n thumb: { width: 200, height: 200 },\n medium: { width: 400, height: 400 },\n large: { width: 800, height: 800 },\n} as const;\n\nexport type ThumbnailSize = keyof typeof THUMBNAIL_SIZES;\n\n/**\n * Processing statuses\n */\nexport const PROCESSING_STATUSES = ['pending', 'processing', 'completed', 'failed'] as const;\nexport type ProcessingStatus = (typeof PROCESSING_STATUSES)[number];\n\n/**\n * Retry configuration\n */\nexport const RETRY_CONFIG = {\n maxAttempts: 3,\n initialDelayMs: 1000,\n maxDelayMs: 10000,\n backoffMultiplier: 2,\n} as const;\n","/**\n * Base error class for Storage Brain SDK\n */\nexport class StorageBrainError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'StorageBrainError';\n }\n}\n\n/**\n * Authentication error - invalid or missing API key\n */\nexport class AuthenticationError extends StorageBrainError {\n constructor(message = 'Authentication failed') {\n super(message, 'AUTHENTICATION_ERROR', 401);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Quota exceeded error\n */\nexport class QuotaExceededError extends StorageBrainError {\n constructor(\n message = 'Storage quota exceeded',\n public quotaBytes?: number,\n public usedBytes?: number\n ) {\n super(message, 'QUOTA_EXCEEDED', 403, { quotaBytes, usedBytes });\n this.name = 'QuotaExceededError';\n }\n}\n\n/**\n * Invalid file type error\n */\nexport class InvalidFileTypeError extends StorageBrainError {\n constructor(\n fileType: string,\n allowedTypes?: string[]\n ) {\n super(\n `File type '${fileType}' is not allowed`,\n 'INVALID_FILE_TYPE',\n 400,\n { fileType, allowedTypes }\n );\n this.name = 'InvalidFileTypeError';\n }\n}\n\n/**\n * File too large error\n */\nexport class FileTooLargeError extends StorageBrainError {\n constructor(\n fileSize: number,\n maxSize: number\n ) {\n super(\n `File size ${fileSize} bytes exceeds maximum of ${maxSize} bytes`,\n 'FILE_TOO_LARGE',\n 400,\n { fileSize, maxSize }\n );\n this.name = 'FileTooLargeError';\n }\n}\n\n/**\n * File not found error\n */\nexport class FileNotFoundError extends StorageBrainError {\n constructor(fileId: string) {\n super(`File not found: ${fileId}`, 'FILE_NOT_FOUND', 404, { fileId });\n this.name = 'FileNotFoundError';\n }\n}\n\n/**\n * Network error - connection issues\n */\nexport class NetworkError extends StorageBrainError {\n constructor(message = 'Network error occurred', public originalError?: Error) {\n super(message, 'NETWORK_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Upload error - file upload failed\n */\nexport class UploadError extends StorageBrainError {\n constructor(message: string, public originalError?: Error) {\n super(message, 'UPLOAD_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'UploadError';\n }\n}\n\n/**\n * Validation error - request validation failed\n */\nexport class ValidationError extends StorageBrainError {\n constructor(\n message: string,\n public errors?: Array<{ path: string; message: string }>\n ) {\n super(message, 'VALIDATION_ERROR', 400, { errors });\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Parse API error response into appropriate error class\n */\nexport function parseApiError(\n statusCode: number,\n response: { error?: { code?: string; message?: string; details?: Record<string, unknown> } }\n): StorageBrainError {\n const { code, message, details } = response.error ?? {};\n\n switch (code) {\n case 'UNAUTHORIZED':\n return new AuthenticationError(message);\n case 'QUOTA_EXCEEDED':\n return new QuotaExceededError(\n message,\n details?.quotaBytes as number,\n details?.usedBytes as number\n );\n case 'INVALID_FILE_TYPE':\n return new InvalidFileTypeError(\n details?.fileType as string,\n details?.allowedTypes as string[]\n );\n case 'FILE_TOO_LARGE':\n return new FileTooLargeError(\n details?.fileSize as number,\n details?.maxSize as number\n );\n case 'FILE_NOT_FOUND':\n case 'NOT_FOUND':\n return new FileNotFoundError(details?.fileId as string ?? 'unknown');\n case 'VALIDATION_ERROR':\n return new ValidationError(\n message ?? 'Validation failed',\n details?.errors as Array<{ path: string; message: string }>\n );\n default:\n return new StorageBrainError(\n message ?? 'An error occurred',\n code ?? 'UNKNOWN_ERROR',\n statusCode,\n details\n );\n }\n}\n","import type { AllowedMimeType } from './constants';\nimport { ALLOWED_MIME_TYPES, RETRY_CONFIG } from './constants';\nimport type {\n StorageBrainConfig,\n UploadOptions,\n FileInfo,\n ListFilesOptions,\n ListFilesResult,\n QuotaInfo,\n TenantInfo,\n UploadHandshake,\n} from './types';\nimport {\n StorageBrainError,\n NetworkError,\n UploadError,\n InvalidFileTypeError,\n parseApiError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://storage-brain-api.marlin-pohl.workers.dev';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_MAX_RETRIES = 3;\n\n/**\n * Storage Brain SDK Client\n *\n * @example\n * ```typescript\n * const storage = new StorageBrain({\n * apiKey: 'sk_live_...',\n * });\n *\n * // Upload a file\n * const file = await storage.upload(fileBlob, {\n * context: 'newsletter',\n * onProgress: (p) => console.log(`${p}%`),\n * });\n *\n * console.log(file.url);\n * ```\n */\nexport class StorageBrain {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly maxRetries: number;\n\n constructor(config: StorageBrainConfig) {\n if (!config.apiKey) {\n throw new StorageBrainError('API key is required', 'CONFIGURATION_ERROR');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n }\n\n /**\n * Upload a file\n */\n async upload(file: File | Blob, options: UploadOptions): Promise<FileInfo> {\n const { context, tags, onProgress, webhookUrl, signal } = options;\n\n // Get file info\n const fileName = file instanceof File ? file.name : 'file';\n const fileType = file.type as AllowedMimeType;\n const fileSize = file.size;\n\n // Validate file type\n if (!ALLOWED_MIME_TYPES.includes(fileType)) {\n throw new InvalidFileTypeError(fileType, [...ALLOWED_MIME_TYPES]);\n }\n\n // Request upload handshake\n const handshake = await this.requestUpload({\n fileType,\n fileName,\n fileSizeBytes: fileSize,\n context,\n tags,\n webhookUrl,\n });\n\n onProgress?.(10);\n\n // Upload file to presigned URL\n await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {\n // Map upload progress to 10-90%\n onProgress?.(10 + Math.round(progress * 0.8));\n }, signal);\n\n onProgress?.(90);\n\n // Poll for file status (processing may take time)\n const fileInfo = await this.waitForProcessing(handshake.fileId, signal);\n\n onProgress?.(100);\n\n return fileInfo;\n }\n\n /**\n * Request an upload handshake\n */\n private async requestUpload(params: {\n fileType: AllowedMimeType;\n fileName: string;\n fileSizeBytes: number;\n context: string;\n tags?: Record<string, string>;\n webhookUrl?: string;\n }): Promise<UploadHandshake> {\n return this.request<UploadHandshake>('POST', '/api/v1/upload/request', params);\n }\n\n /**\n * Upload file content to presigned URL\n */\n private async uploadToPresignedUrl(\n presignedUrl: string,\n file: File | Blob,\n contentType: string,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n // Determine if URL is relative (our internal endpoint) or absolute (direct R2)\n const uploadUrl = presignedUrl.startsWith('/')\n ? `${this.baseUrl}${presignedUrl}`\n : presignedUrl;\n\n try {\n // Use XMLHttpRequest for progress tracking\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && onProgress) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new UploadError(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new NetworkError('Network error during upload'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new UploadError('Upload was cancelled'));\n });\n\n if (signal) {\n signal.addEventListener('abort', () => {\n xhr.abort();\n });\n }\n\n xhr.open('PUT', uploadUrl);\n xhr.setRequestHeader('Content-Type', contentType);\n\n // Add auth header for internal endpoint\n if (presignedUrl.startsWith('/')) {\n xhr.setRequestHeader('Authorization', `Bearer ${this.apiKey}`);\n }\n\n xhr.send(file);\n });\n } catch (error) {\n if (error instanceof StorageBrainError) {\n throw error;\n }\n throw new UploadError(\n 'Failed to upload file',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Wait for file processing to complete\n */\n private async waitForProcessing(\n fileId: string,\n signal?: AbortSignal,\n maxWaitMs = 60000,\n pollIntervalMs = 1000\n ): Promise<FileInfo> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitMs) {\n if (signal?.aborted) {\n throw new UploadError('Operation was cancelled');\n }\n\n const file = await this.getFile(fileId);\n\n if (file.processingStatus === 'completed' || file.processingStatus === 'failed') {\n return file;\n }\n\n // Wait before polling again\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n // Return current state even if not fully processed\n return this.getFile(fileId);\n }\n\n /**\n * Get a file by ID\n */\n async getFile(fileId: string): Promise<FileInfo> {\n return this.request<FileInfo>('GET', `/api/v1/files/${fileId}`);\n }\n\n /**\n * List files\n */\n async listFiles(options?: ListFilesOptions): Promise<ListFilesResult> {\n const params = new URLSearchParams();\n\n if (options?.limit) params.set('limit', options.limit.toString());\n if (options?.cursor) params.set('cursor', options.cursor);\n if (options?.context) params.set('context', options.context);\n if (options?.fileType) params.set('fileType', options.fileType);\n\n const query = params.toString();\n const path = query ? `/api/v1/files?${query}` : '/api/v1/files';\n\n return this.request<ListFilesResult>('GET', path);\n }\n\n /**\n * Delete a file\n */\n async deleteFile(fileId: string): Promise<void> {\n await this.request<{ success: boolean }>('DELETE', `/api/v1/files/${fileId}`);\n }\n\n /**\n * Get quota usage\n */\n async getQuota(): Promise<QuotaInfo> {\n return this.request<QuotaInfo>('GET', '/api/v1/tenant/quota');\n }\n\n /**\n * Get tenant information\n */\n async getTenantInfo(): Promise<TenantInfo> {\n return this.request<TenantInfo>('GET', '/api/v1/tenant/info');\n }\n\n /**\n * Make an authenticated API request with retry logic\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < this.maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({}));\n throw parseApiError(response.status, errorBody as { error?: { code?: string; message?: string; details?: Record<string, unknown> } });\n }\n\n return await response.json() as T;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on client errors (4xx) or specific errors\n if (error instanceof StorageBrainError && error.statusCode && error.statusCode < 500) {\n throw error;\n }\n\n // Exponential backoff for retries\n if (attempt < this.maxRetries - 1) {\n const delay = Math.min(\n RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt),\n RETRY_CONFIG.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw new NetworkError(\n `Request failed after ${this.maxRetries} attempts`,\n lastError\n );\n }\n}\n\n// Default export for convenience\nexport default StorageBrain;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Allowed MIME types for file uploads\n */\nexport const ALLOWED_FILE_TYPES = {\n // Images\n 'image/jpeg': { extension: 'jpg', category: 'image' },\n 'image/png': { extension: 'png', category: 'image' },\n 'image/webp': { extension: 'webp', category: 'image' },\n 'image/gif': { extension: 'gif', category: 'image' },\n 'image/avif': { extension: 'avif', category: 'image' },\n // Documents\n 'application/pdf': { extension: 'pdf', category: 'document' },\n} as const;\n\nexport type AllowedMimeType = keyof typeof ALLOWED_FILE_TYPES;\n\nexport const ALLOWED_MIME_TYPES = Object.keys(ALLOWED_FILE_TYPES) as AllowedMimeType[];\n\nexport const IMAGE_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'image'\n);\n\nexport const DOCUMENT_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'document'\n);\n\n/**\n * Processing contexts\n */\nexport const PROCESSING_CONTEXTS = ['newsletter', 'invoice', 'framer-site', 'default'] as const;\nexport type ProcessingContext = (typeof PROCESSING_CONTEXTS)[number];\n\n/**\n * File size limits\n */\nexport const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024; // 100MB per file\n\n/**\n * Thumbnail sizes\n */\nexport const THUMBNAIL_SIZES = {\n thumb: { width: 200, height: 200 },\n medium: { width: 400, height: 400 },\n large: { width: 800, height: 800 },\n} as const;\n\nexport type ThumbnailSize = keyof typeof THUMBNAIL_SIZES;\n\n/**\n * Processing statuses\n */\nexport const PROCESSING_STATUSES = ['pending', 'processing', 'completed', 'failed'] as const;\nexport type ProcessingStatus = (typeof PROCESSING_STATUSES)[number];\n\n/**\n * Retry configuration\n */\nexport const RETRY_CONFIG = {\n maxAttempts: 3,\n initialDelayMs: 1000,\n maxDelayMs: 10000,\n backoffMultiplier: 2,\n} as const;\n","/**\n * Base error class for Storage Brain SDK\n */\nexport class StorageBrainError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'StorageBrainError';\n }\n}\n\n/**\n * Authentication error - invalid or missing API key\n */\nexport class AuthenticationError extends StorageBrainError {\n constructor(message = 'Authentication failed') {\n super(message, 'AUTHENTICATION_ERROR', 401);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Quota exceeded error\n */\nexport class QuotaExceededError extends StorageBrainError {\n constructor(\n message = 'Storage quota exceeded',\n public quotaBytes?: number,\n public usedBytes?: number\n ) {\n super(message, 'QUOTA_EXCEEDED', 403, { quotaBytes, usedBytes });\n this.name = 'QuotaExceededError';\n }\n}\n\n/**\n * Invalid file type error\n */\nexport class InvalidFileTypeError extends StorageBrainError {\n constructor(\n fileType: string,\n allowedTypes?: string[]\n ) {\n super(\n `File type '${fileType}' is not allowed`,\n 'INVALID_FILE_TYPE',\n 400,\n { fileType, allowedTypes }\n );\n this.name = 'InvalidFileTypeError';\n }\n}\n\n/**\n * File too large error\n */\nexport class FileTooLargeError extends StorageBrainError {\n constructor(\n fileSize: number,\n maxSize: number\n ) {\n super(\n `File size ${fileSize} bytes exceeds maximum of ${maxSize} bytes`,\n 'FILE_TOO_LARGE',\n 400,\n { fileSize, maxSize }\n );\n this.name = 'FileTooLargeError';\n }\n}\n\n/**\n * File not found error\n */\nexport class FileNotFoundError extends StorageBrainError {\n constructor(fileId: string) {\n super(`File not found: ${fileId}`, 'FILE_NOT_FOUND', 404, { fileId });\n this.name = 'FileNotFoundError';\n }\n}\n\n/**\n * Network error - connection issues\n */\nexport class NetworkError extends StorageBrainError {\n constructor(message = 'Network error occurred', public originalError?: Error) {\n super(message, 'NETWORK_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Upload error - file upload failed\n */\nexport class UploadError extends StorageBrainError {\n constructor(message: string, public originalError?: Error) {\n super(message, 'UPLOAD_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'UploadError';\n }\n}\n\n/**\n * Validation error - request validation failed\n */\nexport class ValidationError extends StorageBrainError {\n constructor(\n message: string,\n public errors?: Array<{ path: string; message: string }>\n ) {\n super(message, 'VALIDATION_ERROR', 400, { errors });\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Parse API error response into appropriate error class\n */\nexport function parseApiError(\n statusCode: number,\n response: { error?: { code?: string; message?: string; details?: Record<string, unknown> } }\n): StorageBrainError {\n const { code, message, details } = response.error ?? {};\n\n switch (code) {\n case 'UNAUTHORIZED':\n return new AuthenticationError(message);\n case 'QUOTA_EXCEEDED':\n return new QuotaExceededError(\n message,\n details?.quotaBytes as number,\n details?.usedBytes as number\n );\n case 'INVALID_FILE_TYPE':\n return new InvalidFileTypeError(\n details?.fileType as string,\n details?.allowedTypes as string[]\n );\n case 'FILE_TOO_LARGE':\n return new FileTooLargeError(\n details?.fileSize as number,\n details?.maxSize as number\n );\n case 'FILE_NOT_FOUND':\n case 'NOT_FOUND':\n return new FileNotFoundError(details?.fileId as string ?? 'unknown');\n case 'VALIDATION_ERROR':\n return new ValidationError(\n message ?? 'Validation failed',\n details?.errors as Array<{ path: string; message: string }>\n );\n default:\n return new StorageBrainError(\n message ?? 'An error occurred',\n code ?? 'UNKNOWN_ERROR',\n statusCode,\n details\n );\n }\n}\n","import type { AllowedMimeType } from './constants';\nimport { ALLOWED_MIME_TYPES, RETRY_CONFIG } from './constants';\nimport type {\n StorageBrainConfig,\n UploadOptions,\n FileInfo,\n ListFilesOptions,\n ListFilesResult,\n QuotaInfo,\n TenantInfo,\n UploadHandshake,\n} from './types';\nimport {\n StorageBrainError,\n NetworkError,\n UploadError,\n InvalidFileTypeError,\n parseApiError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://storage-brain-api.marlin-pohl.workers.dev';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_MAX_RETRIES = 3;\n\n/**\n * Storage Brain SDK Client\n *\n * @example\n * ```typescript\n * const storage = new StorageBrain({\n * apiKey: 'sk_live_...',\n * });\n *\n * // Upload a file\n * const file = await storage.upload(fileBlob, {\n * context: 'newsletter',\n * onProgress: (p) => console.log(`${p}%`),\n * });\n *\n * console.log(file.url);\n * ```\n */\nexport class StorageBrain {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly maxRetries: number;\n\n constructor(config: StorageBrainConfig) {\n if (!config.apiKey) {\n throw new StorageBrainError('API key is required', 'CONFIGURATION_ERROR');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n }\n\n /**\n * Upload a file\n */\n async upload(file: File | Blob, options: UploadOptions): Promise<FileInfo> {\n const { context, tags, onProgress, webhookUrl, signal } = options;\n\n // Get file info\n const fileName = file instanceof File ? file.name : 'file';\n const fileType = file.type as AllowedMimeType;\n const fileSize = file.size;\n\n // Validate file type\n if (!ALLOWED_MIME_TYPES.includes(fileType)) {\n throw new InvalidFileTypeError(fileType, [...ALLOWED_MIME_TYPES]);\n }\n\n // Request upload handshake\n const handshake = await this.requestUpload({\n fileType,\n fileName,\n fileSizeBytes: fileSize,\n context,\n tags,\n webhookUrl,\n });\n\n onProgress?.(10);\n\n // Upload file to presigned URL\n await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {\n // Map upload progress to 10-90%\n onProgress?.(10 + Math.round(progress * 0.8));\n }, signal);\n\n onProgress?.(90);\n\n // Poll for file status (processing may take time)\n const fileInfo = await this.waitForProcessing(handshake.fileId, signal);\n\n onProgress?.(100);\n\n return fileInfo;\n }\n\n /**\n * Request an upload handshake\n */\n private async requestUpload(params: {\n fileType: AllowedMimeType;\n fileName: string;\n fileSizeBytes: number;\n context: string;\n tags?: Record<string, string>;\n webhookUrl?: string;\n }): Promise<UploadHandshake> {\n return this.request<UploadHandshake>('POST', '/api/v1/upload/request', params);\n }\n\n /**\n * Check if running in a browser environment\n */\n private isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof XMLHttpRequest !== 'undefined';\n }\n\n /**\n * Upload file content to presigned URL\n * Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js\n */\n private async uploadToPresignedUrl(\n presignedUrl: string,\n file: File | Blob,\n contentType: string,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n // Determine if URL is relative (our internal endpoint) or absolute (direct R2)\n const uploadUrl = presignedUrl.startsWith('/')\n ? `${this.baseUrl}${presignedUrl}`\n : presignedUrl;\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': contentType,\n };\n\n // Add auth header for internal endpoint\n if (presignedUrl.startsWith('/')) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n try {\n if (this.isBrowser()) {\n // Use XMLHttpRequest in browsers for progress tracking\n await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);\n } else {\n // Use fetch in Node.js (no granular progress, but works universally)\n await this.uploadWithFetch(uploadUrl, file, headers, onProgress, signal);\n }\n } catch (error) {\n if (error instanceof StorageBrainError) {\n throw error;\n }\n throw new UploadError(\n 'Failed to upload file',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Upload using XMLHttpRequest (browser only, supports progress)\n */\n private uploadWithXHR(\n url: string,\n file: File | Blob,\n headers: Record<string, string>,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && onProgress) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new UploadError(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new NetworkError('Network error during upload'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new UploadError('Upload was cancelled'));\n });\n\n if (signal) {\n signal.addEventListener('abort', () => {\n xhr.abort();\n });\n }\n\n xhr.open('PUT', url);\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n xhr.send(file);\n });\n }\n\n /**\n * Upload using fetch (works in Node.js and browsers, limited progress support)\n */\n private async uploadWithFetch(\n url: string,\n file: File | Blob,\n headers: Record<string, string>,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n // In Node.js, we can't track granular upload progress with fetch\n // Report 0% at start and 100% on completion\n onProgress?.(0);\n\n // Convert Blob to buffer for Node.js compatibility\n const body = await this.blobToBuffer(file);\n\n const response = await fetch(url, {\n method: 'PUT',\n headers,\n body,\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new UploadError(`Upload failed with status ${response.status}: ${errorText}`);\n }\n\n onProgress?.(100);\n }\n\n /**\n * Convert Blob to ArrayBuffer for Node.js fetch compatibility\n */\n private async blobToBuffer(blob: File | Blob): Promise<ArrayBuffer> {\n if (typeof blob.arrayBuffer === 'function') {\n return blob.arrayBuffer();\n }\n // Fallback for older environments\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsArrayBuffer(blob);\n });\n }\n\n /**\n * Wait for file processing to complete\n */\n private async waitForProcessing(\n fileId: string,\n signal?: AbortSignal,\n maxWaitMs = 60000,\n pollIntervalMs = 1000\n ): Promise<FileInfo> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitMs) {\n if (signal?.aborted) {\n throw new UploadError('Operation was cancelled');\n }\n\n const file = await this.getFile(fileId);\n\n if (file.processingStatus === 'completed' || file.processingStatus === 'failed') {\n return file;\n }\n\n // Wait before polling again\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n // Return current state even if not fully processed\n return this.getFile(fileId);\n }\n\n /**\n * Get a file by ID\n */\n async getFile(fileId: string): Promise<FileInfo> {\n return this.request<FileInfo>('GET', `/api/v1/files/${fileId}`);\n }\n\n /**\n * List files\n */\n async listFiles(options?: ListFilesOptions): Promise<ListFilesResult> {\n const params = new URLSearchParams();\n\n if (options?.limit) params.set('limit', options.limit.toString());\n if (options?.cursor) params.set('cursor', options.cursor);\n if (options?.context) params.set('context', options.context);\n if (options?.fileType) params.set('fileType', options.fileType);\n\n const query = params.toString();\n const path = query ? `/api/v1/files?${query}` : '/api/v1/files';\n\n return this.request<ListFilesResult>('GET', path);\n }\n\n /**\n * Delete a file\n */\n async deleteFile(fileId: string): Promise<void> {\n await this.request<{ success: boolean }>('DELETE', `/api/v1/files/${fileId}`);\n }\n\n /**\n * Get quota usage\n */\n async getQuota(): Promise<QuotaInfo> {\n return this.request<QuotaInfo>('GET', '/api/v1/tenant/quota');\n }\n\n /**\n * Get tenant information\n */\n async getTenantInfo(): Promise<TenantInfo> {\n return this.request<TenantInfo>('GET', '/api/v1/tenant/info');\n }\n\n /**\n * Make an authenticated API request with retry logic\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < this.maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({}));\n throw parseApiError(response.status, errorBody as { error?: { code?: string; message?: string; details?: Record<string, unknown> } });\n }\n\n return await response.json() as T;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on client errors (4xx) or specific errors\n if (error instanceof StorageBrainError && error.statusCode && error.statusCode < 500) {\n throw error;\n }\n\n // Exponential backoff for retries\n if (attempt < this.maxRetries - 1) {\n const delay = Math.min(\n RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt),\n RETRY_CONFIG.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw new NetworkError(\n `Request failed after ${this.maxRetries} attempts`,\n lastError\n );\n }\n}\n\n// Default export for convenience\nexport default StorageBrain;\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -261,10 +261,27 @@ declare class StorageBrain {
|
|
|
261
261
|
* Request an upload handshake
|
|
262
262
|
*/
|
|
263
263
|
private requestUpload;
|
|
264
|
+
/**
|
|
265
|
+
* Check if running in a browser environment
|
|
266
|
+
*/
|
|
267
|
+
private isBrowser;
|
|
264
268
|
/**
|
|
265
269
|
* Upload file content to presigned URL
|
|
270
|
+
* Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js
|
|
266
271
|
*/
|
|
267
272
|
private uploadToPresignedUrl;
|
|
273
|
+
/**
|
|
274
|
+
* Upload using XMLHttpRequest (browser only, supports progress)
|
|
275
|
+
*/
|
|
276
|
+
private uploadWithXHR;
|
|
277
|
+
/**
|
|
278
|
+
* Upload using fetch (works in Node.js and browsers, limited progress support)
|
|
279
|
+
*/
|
|
280
|
+
private uploadWithFetch;
|
|
281
|
+
/**
|
|
282
|
+
* Convert Blob to ArrayBuffer for Node.js fetch compatibility
|
|
283
|
+
*/
|
|
284
|
+
private blobToBuffer;
|
|
268
285
|
/**
|
|
269
286
|
* Wait for file processing to complete
|
|
270
287
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -261,10 +261,27 @@ declare class StorageBrain {
|
|
|
261
261
|
* Request an upload handshake
|
|
262
262
|
*/
|
|
263
263
|
private requestUpload;
|
|
264
|
+
/**
|
|
265
|
+
* Check if running in a browser environment
|
|
266
|
+
*/
|
|
267
|
+
private isBrowser;
|
|
264
268
|
/**
|
|
265
269
|
* Upload file content to presigned URL
|
|
270
|
+
* Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js
|
|
266
271
|
*/
|
|
267
272
|
private uploadToPresignedUrl;
|
|
273
|
+
/**
|
|
274
|
+
* Upload using XMLHttpRequest (browser only, supports progress)
|
|
275
|
+
*/
|
|
276
|
+
private uploadWithXHR;
|
|
277
|
+
/**
|
|
278
|
+
* Upload using fetch (works in Node.js and browsers, limited progress support)
|
|
279
|
+
*/
|
|
280
|
+
private uploadWithFetch;
|
|
281
|
+
/**
|
|
282
|
+
* Convert Blob to ArrayBuffer for Node.js fetch compatibility
|
|
283
|
+
*/
|
|
284
|
+
private blobToBuffer;
|
|
268
285
|
/**
|
|
269
286
|
* Wait for file processing to complete
|
|
270
287
|
*/
|
package/dist/index.js
CHANGED
|
@@ -194,44 +194,30 @@ var StorageBrain = class {
|
|
|
194
194
|
async requestUpload(params) {
|
|
195
195
|
return this.request("POST", "/api/v1/upload/request", params);
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Check if running in a browser environment
|
|
199
|
+
*/
|
|
200
|
+
isBrowser() {
|
|
201
|
+
return typeof window !== "undefined" && typeof XMLHttpRequest !== "undefined";
|
|
202
|
+
}
|
|
197
203
|
/**
|
|
198
204
|
* Upload file content to presigned URL
|
|
205
|
+
* Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js
|
|
199
206
|
*/
|
|
200
207
|
async uploadToPresignedUrl(presignedUrl, file, contentType, onProgress, signal) {
|
|
201
208
|
const uploadUrl = presignedUrl.startsWith("/") ? `${this.baseUrl}${presignedUrl}` : presignedUrl;
|
|
209
|
+
const headers = {
|
|
210
|
+
"Content-Type": contentType
|
|
211
|
+
};
|
|
212
|
+
if (presignedUrl.startsWith("/")) {
|
|
213
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
214
|
+
}
|
|
202
215
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
xhr.addEventListener("load", () => {
|
|
211
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
212
|
-
resolve();
|
|
213
|
-
} else {
|
|
214
|
-
reject(new UploadError(`Upload failed with status ${xhr.status}`));
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
xhr.addEventListener("error", () => {
|
|
218
|
-
reject(new NetworkError("Network error during upload"));
|
|
219
|
-
});
|
|
220
|
-
xhr.addEventListener("abort", () => {
|
|
221
|
-
reject(new UploadError("Upload was cancelled"));
|
|
222
|
-
});
|
|
223
|
-
if (signal) {
|
|
224
|
-
signal.addEventListener("abort", () => {
|
|
225
|
-
xhr.abort();
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
xhr.open("PUT", uploadUrl);
|
|
229
|
-
xhr.setRequestHeader("Content-Type", contentType);
|
|
230
|
-
if (presignedUrl.startsWith("/")) {
|
|
231
|
-
xhr.setRequestHeader("Authorization", `Bearer ${this.apiKey}`);
|
|
232
|
-
}
|
|
233
|
-
xhr.send(file);
|
|
234
|
-
});
|
|
216
|
+
if (this.isBrowser()) {
|
|
217
|
+
await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);
|
|
218
|
+
} else {
|
|
219
|
+
await this.uploadWithFetch(uploadUrl, file, headers, onProgress, signal);
|
|
220
|
+
}
|
|
235
221
|
} catch (error) {
|
|
236
222
|
if (error instanceof StorageBrainError) {
|
|
237
223
|
throw error;
|
|
@@ -242,6 +228,74 @@ var StorageBrain = class {
|
|
|
242
228
|
);
|
|
243
229
|
}
|
|
244
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Upload using XMLHttpRequest (browser only, supports progress)
|
|
233
|
+
*/
|
|
234
|
+
uploadWithXHR(url, file, headers, onProgress, signal) {
|
|
235
|
+
return new Promise((resolve, reject) => {
|
|
236
|
+
const xhr = new XMLHttpRequest();
|
|
237
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
238
|
+
if (event.lengthComputable && onProgress) {
|
|
239
|
+
onProgress(Math.round(event.loaded / event.total * 100));
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
xhr.addEventListener("load", () => {
|
|
243
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
244
|
+
resolve();
|
|
245
|
+
} else {
|
|
246
|
+
reject(new UploadError(`Upload failed with status ${xhr.status}`));
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
xhr.addEventListener("error", () => {
|
|
250
|
+
reject(new NetworkError("Network error during upload"));
|
|
251
|
+
});
|
|
252
|
+
xhr.addEventListener("abort", () => {
|
|
253
|
+
reject(new UploadError("Upload was cancelled"));
|
|
254
|
+
});
|
|
255
|
+
if (signal) {
|
|
256
|
+
signal.addEventListener("abort", () => {
|
|
257
|
+
xhr.abort();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
xhr.open("PUT", url);
|
|
261
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
262
|
+
xhr.setRequestHeader(key, value);
|
|
263
|
+
}
|
|
264
|
+
xhr.send(file);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Upload using fetch (works in Node.js and browsers, limited progress support)
|
|
269
|
+
*/
|
|
270
|
+
async uploadWithFetch(url, file, headers, onProgress, signal) {
|
|
271
|
+
onProgress?.(0);
|
|
272
|
+
const body = await this.blobToBuffer(file);
|
|
273
|
+
const response = await fetch(url, {
|
|
274
|
+
method: "PUT",
|
|
275
|
+
headers,
|
|
276
|
+
body,
|
|
277
|
+
signal
|
|
278
|
+
});
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
281
|
+
throw new UploadError(`Upload failed with status ${response.status}: ${errorText}`);
|
|
282
|
+
}
|
|
283
|
+
onProgress?.(100);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Convert Blob to ArrayBuffer for Node.js fetch compatibility
|
|
287
|
+
*/
|
|
288
|
+
async blobToBuffer(blob) {
|
|
289
|
+
if (typeof blob.arrayBuffer === "function") {
|
|
290
|
+
return blob.arrayBuffer();
|
|
291
|
+
}
|
|
292
|
+
return new Promise((resolve, reject) => {
|
|
293
|
+
const reader = new FileReader();
|
|
294
|
+
reader.onload = () => resolve(reader.result);
|
|
295
|
+
reader.onerror = () => reject(new Error("Failed to read file"));
|
|
296
|
+
reader.readAsArrayBuffer(blob);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
245
299
|
/**
|
|
246
300
|
* Wait for file processing to complete
|
|
247
301
|
*/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Allowed MIME types for file uploads\n */\nexport const ALLOWED_FILE_TYPES = {\n // Images\n 'image/jpeg': { extension: 'jpg', category: 'image' },\n 'image/png': { extension: 'png', category: 'image' },\n 'image/webp': { extension: 'webp', category: 'image' },\n 'image/gif': { extension: 'gif', category: 'image' },\n 'image/avif': { extension: 'avif', category: 'image' },\n // Documents\n 'application/pdf': { extension: 'pdf', category: 'document' },\n} as const;\n\nexport type AllowedMimeType = keyof typeof ALLOWED_FILE_TYPES;\n\nexport const ALLOWED_MIME_TYPES = Object.keys(ALLOWED_FILE_TYPES) as AllowedMimeType[];\n\nexport const IMAGE_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'image'\n);\n\nexport const DOCUMENT_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'document'\n);\n\n/**\n * Processing contexts\n */\nexport const PROCESSING_CONTEXTS = ['newsletter', 'invoice', 'framer-site', 'default'] as const;\nexport type ProcessingContext = (typeof PROCESSING_CONTEXTS)[number];\n\n/**\n * File size limits\n */\nexport const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024; // 100MB per file\n\n/**\n * Thumbnail sizes\n */\nexport const THUMBNAIL_SIZES = {\n thumb: { width: 200, height: 200 },\n medium: { width: 400, height: 400 },\n large: { width: 800, height: 800 },\n} as const;\n\nexport type ThumbnailSize = keyof typeof THUMBNAIL_SIZES;\n\n/**\n * Processing statuses\n */\nexport const PROCESSING_STATUSES = ['pending', 'processing', 'completed', 'failed'] as const;\nexport type ProcessingStatus = (typeof PROCESSING_STATUSES)[number];\n\n/**\n * Retry configuration\n */\nexport const RETRY_CONFIG = {\n maxAttempts: 3,\n initialDelayMs: 1000,\n maxDelayMs: 10000,\n backoffMultiplier: 2,\n} as const;\n","/**\n * Base error class for Storage Brain SDK\n */\nexport class StorageBrainError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'StorageBrainError';\n }\n}\n\n/**\n * Authentication error - invalid or missing API key\n */\nexport class AuthenticationError extends StorageBrainError {\n constructor(message = 'Authentication failed') {\n super(message, 'AUTHENTICATION_ERROR', 401);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Quota exceeded error\n */\nexport class QuotaExceededError extends StorageBrainError {\n constructor(\n message = 'Storage quota exceeded',\n public quotaBytes?: number,\n public usedBytes?: number\n ) {\n super(message, 'QUOTA_EXCEEDED', 403, { quotaBytes, usedBytes });\n this.name = 'QuotaExceededError';\n }\n}\n\n/**\n * Invalid file type error\n */\nexport class InvalidFileTypeError extends StorageBrainError {\n constructor(\n fileType: string,\n allowedTypes?: string[]\n ) {\n super(\n `File type '${fileType}' is not allowed`,\n 'INVALID_FILE_TYPE',\n 400,\n { fileType, allowedTypes }\n );\n this.name = 'InvalidFileTypeError';\n }\n}\n\n/**\n * File too large error\n */\nexport class FileTooLargeError extends StorageBrainError {\n constructor(\n fileSize: number,\n maxSize: number\n ) {\n super(\n `File size ${fileSize} bytes exceeds maximum of ${maxSize} bytes`,\n 'FILE_TOO_LARGE',\n 400,\n { fileSize, maxSize }\n );\n this.name = 'FileTooLargeError';\n }\n}\n\n/**\n * File not found error\n */\nexport class FileNotFoundError extends StorageBrainError {\n constructor(fileId: string) {\n super(`File not found: ${fileId}`, 'FILE_NOT_FOUND', 404, { fileId });\n this.name = 'FileNotFoundError';\n }\n}\n\n/**\n * Network error - connection issues\n */\nexport class NetworkError extends StorageBrainError {\n constructor(message = 'Network error occurred', public originalError?: Error) {\n super(message, 'NETWORK_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Upload error - file upload failed\n */\nexport class UploadError extends StorageBrainError {\n constructor(message: string, public originalError?: Error) {\n super(message, 'UPLOAD_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'UploadError';\n }\n}\n\n/**\n * Validation error - request validation failed\n */\nexport class ValidationError extends StorageBrainError {\n constructor(\n message: string,\n public errors?: Array<{ path: string; message: string }>\n ) {\n super(message, 'VALIDATION_ERROR', 400, { errors });\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Parse API error response into appropriate error class\n */\nexport function parseApiError(\n statusCode: number,\n response: { error?: { code?: string; message?: string; details?: Record<string, unknown> } }\n): StorageBrainError {\n const { code, message, details } = response.error ?? {};\n\n switch (code) {\n case 'UNAUTHORIZED':\n return new AuthenticationError(message);\n case 'QUOTA_EXCEEDED':\n return new QuotaExceededError(\n message,\n details?.quotaBytes as number,\n details?.usedBytes as number\n );\n case 'INVALID_FILE_TYPE':\n return new InvalidFileTypeError(\n details?.fileType as string,\n details?.allowedTypes as string[]\n );\n case 'FILE_TOO_LARGE':\n return new FileTooLargeError(\n details?.fileSize as number,\n details?.maxSize as number\n );\n case 'FILE_NOT_FOUND':\n case 'NOT_FOUND':\n return new FileNotFoundError(details?.fileId as string ?? 'unknown');\n case 'VALIDATION_ERROR':\n return new ValidationError(\n message ?? 'Validation failed',\n details?.errors as Array<{ path: string; message: string }>\n );\n default:\n return new StorageBrainError(\n message ?? 'An error occurred',\n code ?? 'UNKNOWN_ERROR',\n statusCode,\n details\n );\n }\n}\n","import type { AllowedMimeType } from './constants';\nimport { ALLOWED_MIME_TYPES, RETRY_CONFIG } from './constants';\nimport type {\n StorageBrainConfig,\n UploadOptions,\n FileInfo,\n ListFilesOptions,\n ListFilesResult,\n QuotaInfo,\n TenantInfo,\n UploadHandshake,\n} from './types';\nimport {\n StorageBrainError,\n NetworkError,\n UploadError,\n InvalidFileTypeError,\n parseApiError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://storage-brain-api.marlin-pohl.workers.dev';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_MAX_RETRIES = 3;\n\n/**\n * Storage Brain SDK Client\n *\n * @example\n * ```typescript\n * const storage = new StorageBrain({\n * apiKey: 'sk_live_...',\n * });\n *\n * // Upload a file\n * const file = await storage.upload(fileBlob, {\n * context: 'newsletter',\n * onProgress: (p) => console.log(`${p}%`),\n * });\n *\n * console.log(file.url);\n * ```\n */\nexport class StorageBrain {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly maxRetries: number;\n\n constructor(config: StorageBrainConfig) {\n if (!config.apiKey) {\n throw new StorageBrainError('API key is required', 'CONFIGURATION_ERROR');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n }\n\n /**\n * Upload a file\n */\n async upload(file: File | Blob, options: UploadOptions): Promise<FileInfo> {\n const { context, tags, onProgress, webhookUrl, signal } = options;\n\n // Get file info\n const fileName = file instanceof File ? file.name : 'file';\n const fileType = file.type as AllowedMimeType;\n const fileSize = file.size;\n\n // Validate file type\n if (!ALLOWED_MIME_TYPES.includes(fileType)) {\n throw new InvalidFileTypeError(fileType, [...ALLOWED_MIME_TYPES]);\n }\n\n // Request upload handshake\n const handshake = await this.requestUpload({\n fileType,\n fileName,\n fileSizeBytes: fileSize,\n context,\n tags,\n webhookUrl,\n });\n\n onProgress?.(10);\n\n // Upload file to presigned URL\n await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {\n // Map upload progress to 10-90%\n onProgress?.(10 + Math.round(progress * 0.8));\n }, signal);\n\n onProgress?.(90);\n\n // Poll for file status (processing may take time)\n const fileInfo = await this.waitForProcessing(handshake.fileId, signal);\n\n onProgress?.(100);\n\n return fileInfo;\n }\n\n /**\n * Request an upload handshake\n */\n private async requestUpload(params: {\n fileType: AllowedMimeType;\n fileName: string;\n fileSizeBytes: number;\n context: string;\n tags?: Record<string, string>;\n webhookUrl?: string;\n }): Promise<UploadHandshake> {\n return this.request<UploadHandshake>('POST', '/api/v1/upload/request', params);\n }\n\n /**\n * Upload file content to presigned URL\n */\n private async uploadToPresignedUrl(\n presignedUrl: string,\n file: File | Blob,\n contentType: string,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n // Determine if URL is relative (our internal endpoint) or absolute (direct R2)\n const uploadUrl = presignedUrl.startsWith('/')\n ? `${this.baseUrl}${presignedUrl}`\n : presignedUrl;\n\n try {\n // Use XMLHttpRequest for progress tracking\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && onProgress) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new UploadError(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new NetworkError('Network error during upload'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new UploadError('Upload was cancelled'));\n });\n\n if (signal) {\n signal.addEventListener('abort', () => {\n xhr.abort();\n });\n }\n\n xhr.open('PUT', uploadUrl);\n xhr.setRequestHeader('Content-Type', contentType);\n\n // Add auth header for internal endpoint\n if (presignedUrl.startsWith('/')) {\n xhr.setRequestHeader('Authorization', `Bearer ${this.apiKey}`);\n }\n\n xhr.send(file);\n });\n } catch (error) {\n if (error instanceof StorageBrainError) {\n throw error;\n }\n throw new UploadError(\n 'Failed to upload file',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Wait for file processing to complete\n */\n private async waitForProcessing(\n fileId: string,\n signal?: AbortSignal,\n maxWaitMs = 60000,\n pollIntervalMs = 1000\n ): Promise<FileInfo> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitMs) {\n if (signal?.aborted) {\n throw new UploadError('Operation was cancelled');\n }\n\n const file = await this.getFile(fileId);\n\n if (file.processingStatus === 'completed' || file.processingStatus === 'failed') {\n return file;\n }\n\n // Wait before polling again\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n // Return current state even if not fully processed\n return this.getFile(fileId);\n }\n\n /**\n * Get a file by ID\n */\n async getFile(fileId: string): Promise<FileInfo> {\n return this.request<FileInfo>('GET', `/api/v1/files/${fileId}`);\n }\n\n /**\n * List files\n */\n async listFiles(options?: ListFilesOptions): Promise<ListFilesResult> {\n const params = new URLSearchParams();\n\n if (options?.limit) params.set('limit', options.limit.toString());\n if (options?.cursor) params.set('cursor', options.cursor);\n if (options?.context) params.set('context', options.context);\n if (options?.fileType) params.set('fileType', options.fileType);\n\n const query = params.toString();\n const path = query ? `/api/v1/files?${query}` : '/api/v1/files';\n\n return this.request<ListFilesResult>('GET', path);\n }\n\n /**\n * Delete a file\n */\n async deleteFile(fileId: string): Promise<void> {\n await this.request<{ success: boolean }>('DELETE', `/api/v1/files/${fileId}`);\n }\n\n /**\n * Get quota usage\n */\n async getQuota(): Promise<QuotaInfo> {\n return this.request<QuotaInfo>('GET', '/api/v1/tenant/quota');\n }\n\n /**\n * Get tenant information\n */\n async getTenantInfo(): Promise<TenantInfo> {\n return this.request<TenantInfo>('GET', '/api/v1/tenant/info');\n }\n\n /**\n * Make an authenticated API request with retry logic\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < this.maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({}));\n throw parseApiError(response.status, errorBody as { error?: { code?: string; message?: string; details?: Record<string, unknown> } });\n }\n\n return await response.json() as T;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on client errors (4xx) or specific errors\n if (error instanceof StorageBrainError && error.statusCode && error.statusCode < 500) {\n throw error;\n }\n\n // Exponential backoff for retries\n if (attempt < this.maxRetries - 1) {\n const delay = Math.min(\n RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt),\n RETRY_CONFIG.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw new NetworkError(\n `Request failed after ${this.maxRetries} attempts`,\n lastError\n );\n }\n}\n\n// Default export for convenience\nexport default StorageBrain;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Allowed MIME types for file uploads\n */\nexport const ALLOWED_FILE_TYPES = {\n // Images\n 'image/jpeg': { extension: 'jpg', category: 'image' },\n 'image/png': { extension: 'png', category: 'image' },\n 'image/webp': { extension: 'webp', category: 'image' },\n 'image/gif': { extension: 'gif', category: 'image' },\n 'image/avif': { extension: 'avif', category: 'image' },\n // Documents\n 'application/pdf': { extension: 'pdf', category: 'document' },\n} as const;\n\nexport type AllowedMimeType = keyof typeof ALLOWED_FILE_TYPES;\n\nexport const ALLOWED_MIME_TYPES = Object.keys(ALLOWED_FILE_TYPES) as AllowedMimeType[];\n\nexport const IMAGE_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'image'\n);\n\nexport const DOCUMENT_MIME_TYPES = ALLOWED_MIME_TYPES.filter(\n (type) => ALLOWED_FILE_TYPES[type].category === 'document'\n);\n\n/**\n * Processing contexts\n */\nexport const PROCESSING_CONTEXTS = ['newsletter', 'invoice', 'framer-site', 'default'] as const;\nexport type ProcessingContext = (typeof PROCESSING_CONTEXTS)[number];\n\n/**\n * File size limits\n */\nexport const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024; // 100MB per file\n\n/**\n * Thumbnail sizes\n */\nexport const THUMBNAIL_SIZES = {\n thumb: { width: 200, height: 200 },\n medium: { width: 400, height: 400 },\n large: { width: 800, height: 800 },\n} as const;\n\nexport type ThumbnailSize = keyof typeof THUMBNAIL_SIZES;\n\n/**\n * Processing statuses\n */\nexport const PROCESSING_STATUSES = ['pending', 'processing', 'completed', 'failed'] as const;\nexport type ProcessingStatus = (typeof PROCESSING_STATUSES)[number];\n\n/**\n * Retry configuration\n */\nexport const RETRY_CONFIG = {\n maxAttempts: 3,\n initialDelayMs: 1000,\n maxDelayMs: 10000,\n backoffMultiplier: 2,\n} as const;\n","/**\n * Base error class for Storage Brain SDK\n */\nexport class StorageBrainError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'StorageBrainError';\n }\n}\n\n/**\n * Authentication error - invalid or missing API key\n */\nexport class AuthenticationError extends StorageBrainError {\n constructor(message = 'Authentication failed') {\n super(message, 'AUTHENTICATION_ERROR', 401);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Quota exceeded error\n */\nexport class QuotaExceededError extends StorageBrainError {\n constructor(\n message = 'Storage quota exceeded',\n public quotaBytes?: number,\n public usedBytes?: number\n ) {\n super(message, 'QUOTA_EXCEEDED', 403, { quotaBytes, usedBytes });\n this.name = 'QuotaExceededError';\n }\n}\n\n/**\n * Invalid file type error\n */\nexport class InvalidFileTypeError extends StorageBrainError {\n constructor(\n fileType: string,\n allowedTypes?: string[]\n ) {\n super(\n `File type '${fileType}' is not allowed`,\n 'INVALID_FILE_TYPE',\n 400,\n { fileType, allowedTypes }\n );\n this.name = 'InvalidFileTypeError';\n }\n}\n\n/**\n * File too large error\n */\nexport class FileTooLargeError extends StorageBrainError {\n constructor(\n fileSize: number,\n maxSize: number\n ) {\n super(\n `File size ${fileSize} bytes exceeds maximum of ${maxSize} bytes`,\n 'FILE_TOO_LARGE',\n 400,\n { fileSize, maxSize }\n );\n this.name = 'FileTooLargeError';\n }\n}\n\n/**\n * File not found error\n */\nexport class FileNotFoundError extends StorageBrainError {\n constructor(fileId: string) {\n super(`File not found: ${fileId}`, 'FILE_NOT_FOUND', 404, { fileId });\n this.name = 'FileNotFoundError';\n }\n}\n\n/**\n * Network error - connection issues\n */\nexport class NetworkError extends StorageBrainError {\n constructor(message = 'Network error occurred', public originalError?: Error) {\n super(message, 'NETWORK_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Upload error - file upload failed\n */\nexport class UploadError extends StorageBrainError {\n constructor(message: string, public originalError?: Error) {\n super(message, 'UPLOAD_ERROR', undefined, { originalError: originalError?.message });\n this.name = 'UploadError';\n }\n}\n\n/**\n * Validation error - request validation failed\n */\nexport class ValidationError extends StorageBrainError {\n constructor(\n message: string,\n public errors?: Array<{ path: string; message: string }>\n ) {\n super(message, 'VALIDATION_ERROR', 400, { errors });\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Parse API error response into appropriate error class\n */\nexport function parseApiError(\n statusCode: number,\n response: { error?: { code?: string; message?: string; details?: Record<string, unknown> } }\n): StorageBrainError {\n const { code, message, details } = response.error ?? {};\n\n switch (code) {\n case 'UNAUTHORIZED':\n return new AuthenticationError(message);\n case 'QUOTA_EXCEEDED':\n return new QuotaExceededError(\n message,\n details?.quotaBytes as number,\n details?.usedBytes as number\n );\n case 'INVALID_FILE_TYPE':\n return new InvalidFileTypeError(\n details?.fileType as string,\n details?.allowedTypes as string[]\n );\n case 'FILE_TOO_LARGE':\n return new FileTooLargeError(\n details?.fileSize as number,\n details?.maxSize as number\n );\n case 'FILE_NOT_FOUND':\n case 'NOT_FOUND':\n return new FileNotFoundError(details?.fileId as string ?? 'unknown');\n case 'VALIDATION_ERROR':\n return new ValidationError(\n message ?? 'Validation failed',\n details?.errors as Array<{ path: string; message: string }>\n );\n default:\n return new StorageBrainError(\n message ?? 'An error occurred',\n code ?? 'UNKNOWN_ERROR',\n statusCode,\n details\n );\n }\n}\n","import type { AllowedMimeType } from './constants';\nimport { ALLOWED_MIME_TYPES, RETRY_CONFIG } from './constants';\nimport type {\n StorageBrainConfig,\n UploadOptions,\n FileInfo,\n ListFilesOptions,\n ListFilesResult,\n QuotaInfo,\n TenantInfo,\n UploadHandshake,\n} from './types';\nimport {\n StorageBrainError,\n NetworkError,\n UploadError,\n InvalidFileTypeError,\n parseApiError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://storage-brain-api.marlin-pohl.workers.dev';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_MAX_RETRIES = 3;\n\n/**\n * Storage Brain SDK Client\n *\n * @example\n * ```typescript\n * const storage = new StorageBrain({\n * apiKey: 'sk_live_...',\n * });\n *\n * // Upload a file\n * const file = await storage.upload(fileBlob, {\n * context: 'newsletter',\n * onProgress: (p) => console.log(`${p}%`),\n * });\n *\n * console.log(file.url);\n * ```\n */\nexport class StorageBrain {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly maxRetries: number;\n\n constructor(config: StorageBrainConfig) {\n if (!config.apiKey) {\n throw new StorageBrainError('API key is required', 'CONFIGURATION_ERROR');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n }\n\n /**\n * Upload a file\n */\n async upload(file: File | Blob, options: UploadOptions): Promise<FileInfo> {\n const { context, tags, onProgress, webhookUrl, signal } = options;\n\n // Get file info\n const fileName = file instanceof File ? file.name : 'file';\n const fileType = file.type as AllowedMimeType;\n const fileSize = file.size;\n\n // Validate file type\n if (!ALLOWED_MIME_TYPES.includes(fileType)) {\n throw new InvalidFileTypeError(fileType, [...ALLOWED_MIME_TYPES]);\n }\n\n // Request upload handshake\n const handshake = await this.requestUpload({\n fileType,\n fileName,\n fileSizeBytes: fileSize,\n context,\n tags,\n webhookUrl,\n });\n\n onProgress?.(10);\n\n // Upload file to presigned URL\n await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {\n // Map upload progress to 10-90%\n onProgress?.(10 + Math.round(progress * 0.8));\n }, signal);\n\n onProgress?.(90);\n\n // Poll for file status (processing may take time)\n const fileInfo = await this.waitForProcessing(handshake.fileId, signal);\n\n onProgress?.(100);\n\n return fileInfo;\n }\n\n /**\n * Request an upload handshake\n */\n private async requestUpload(params: {\n fileType: AllowedMimeType;\n fileName: string;\n fileSizeBytes: number;\n context: string;\n tags?: Record<string, string>;\n webhookUrl?: string;\n }): Promise<UploadHandshake> {\n return this.request<UploadHandshake>('POST', '/api/v1/upload/request', params);\n }\n\n /**\n * Check if running in a browser environment\n */\n private isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof XMLHttpRequest !== 'undefined';\n }\n\n /**\n * Upload file content to presigned URL\n * Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js\n */\n private async uploadToPresignedUrl(\n presignedUrl: string,\n file: File | Blob,\n contentType: string,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n // Determine if URL is relative (our internal endpoint) or absolute (direct R2)\n const uploadUrl = presignedUrl.startsWith('/')\n ? `${this.baseUrl}${presignedUrl}`\n : presignedUrl;\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': contentType,\n };\n\n // Add auth header for internal endpoint\n if (presignedUrl.startsWith('/')) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n try {\n if (this.isBrowser()) {\n // Use XMLHttpRequest in browsers for progress tracking\n await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);\n } else {\n // Use fetch in Node.js (no granular progress, but works universally)\n await this.uploadWithFetch(uploadUrl, file, headers, onProgress, signal);\n }\n } catch (error) {\n if (error instanceof StorageBrainError) {\n throw error;\n }\n throw new UploadError(\n 'Failed to upload file',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Upload using XMLHttpRequest (browser only, supports progress)\n */\n private uploadWithXHR(\n url: string,\n file: File | Blob,\n headers: Record<string, string>,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && onProgress) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new UploadError(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new NetworkError('Network error during upload'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new UploadError('Upload was cancelled'));\n });\n\n if (signal) {\n signal.addEventListener('abort', () => {\n xhr.abort();\n });\n }\n\n xhr.open('PUT', url);\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n xhr.send(file);\n });\n }\n\n /**\n * Upload using fetch (works in Node.js and browsers, limited progress support)\n */\n private async uploadWithFetch(\n url: string,\n file: File | Blob,\n headers: Record<string, string>,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal\n ): Promise<void> {\n // In Node.js, we can't track granular upload progress with fetch\n // Report 0% at start and 100% on completion\n onProgress?.(0);\n\n // Convert Blob to buffer for Node.js compatibility\n const body = await this.blobToBuffer(file);\n\n const response = await fetch(url, {\n method: 'PUT',\n headers,\n body,\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new UploadError(`Upload failed with status ${response.status}: ${errorText}`);\n }\n\n onProgress?.(100);\n }\n\n /**\n * Convert Blob to ArrayBuffer for Node.js fetch compatibility\n */\n private async blobToBuffer(blob: File | Blob): Promise<ArrayBuffer> {\n if (typeof blob.arrayBuffer === 'function') {\n return blob.arrayBuffer();\n }\n // Fallback for older environments\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsArrayBuffer(blob);\n });\n }\n\n /**\n * Wait for file processing to complete\n */\n private async waitForProcessing(\n fileId: string,\n signal?: AbortSignal,\n maxWaitMs = 60000,\n pollIntervalMs = 1000\n ): Promise<FileInfo> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWaitMs) {\n if (signal?.aborted) {\n throw new UploadError('Operation was cancelled');\n }\n\n const file = await this.getFile(fileId);\n\n if (file.processingStatus === 'completed' || file.processingStatus === 'failed') {\n return file;\n }\n\n // Wait before polling again\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n // Return current state even if not fully processed\n return this.getFile(fileId);\n }\n\n /**\n * Get a file by ID\n */\n async getFile(fileId: string): Promise<FileInfo> {\n return this.request<FileInfo>('GET', `/api/v1/files/${fileId}`);\n }\n\n /**\n * List files\n */\n async listFiles(options?: ListFilesOptions): Promise<ListFilesResult> {\n const params = new URLSearchParams();\n\n if (options?.limit) params.set('limit', options.limit.toString());\n if (options?.cursor) params.set('cursor', options.cursor);\n if (options?.context) params.set('context', options.context);\n if (options?.fileType) params.set('fileType', options.fileType);\n\n const query = params.toString();\n const path = query ? `/api/v1/files?${query}` : '/api/v1/files';\n\n return this.request<ListFilesResult>('GET', path);\n }\n\n /**\n * Delete a file\n */\n async deleteFile(fileId: string): Promise<void> {\n await this.request<{ success: boolean }>('DELETE', `/api/v1/files/${fileId}`);\n }\n\n /**\n * Get quota usage\n */\n async getQuota(): Promise<QuotaInfo> {\n return this.request<QuotaInfo>('GET', '/api/v1/tenant/quota');\n }\n\n /**\n * Get tenant information\n */\n async getTenantInfo(): Promise<TenantInfo> {\n return this.request<TenantInfo>('GET', '/api/v1/tenant/info');\n }\n\n /**\n * Make an authenticated API request with retry logic\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < this.maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({}));\n throw parseApiError(response.status, errorBody as { error?: { code?: string; message?: string; details?: Record<string, unknown> } });\n }\n\n return await response.json() as T;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on client errors (4xx) or specific errors\n if (error instanceof StorageBrainError && error.statusCode && error.statusCode < 500) {\n throw error;\n }\n\n // Exponential backoff for retries\n if (attempt < this.maxRetries - 1) {\n const delay = Math.min(\n RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt),\n RETRY_CONFIG.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw new NetworkError(\n `Request failed after ${this.maxRetries} attempts`,\n lastError\n );\n }\n}\n\n// Default export for convenience\nexport default StorageBrain;\n"]}
|