@marlinjai/storage-brain-sdk 0.1.0 → 0.3.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 CHANGED
@@ -184,10 +184,15 @@ var StorageBrain = class {
184
184
  webhookUrl
185
185
  });
186
186
  onProgress?.(10);
187
- await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {
187
+ const uploadResult = await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {
188
188
  onProgress?.(10 + Math.round(progress * 0.8));
189
189
  }, signal);
190
190
  onProgress?.(90);
191
+ if (uploadResult?.processingStatus === "completed" && uploadResult?.metadata) {
192
+ onProgress?.(100);
193
+ const fileInfo2 = await this.getFile(handshake.fileId);
194
+ return fileInfo2;
195
+ }
191
196
  const fileInfo = await this.waitForProcessing(handshake.fileId, signal);
192
197
  onProgress?.(100);
193
198
  return fileInfo;
@@ -198,44 +203,31 @@ var StorageBrain = class {
198
203
  async requestUpload(params) {
199
204
  return this.request("POST", "/api/v1/upload/request", params);
200
205
  }
206
+ /**
207
+ * Check if running in a browser environment
208
+ */
209
+ isBrowser() {
210
+ return typeof window !== "undefined" && typeof XMLHttpRequest !== "undefined";
211
+ }
201
212
  /**
202
213
  * Upload file content to presigned URL
214
+ * Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js
215
+ * Returns upload response which may include immediate OCR results for small files
203
216
  */
204
217
  async uploadToPresignedUrl(presignedUrl, file, contentType, onProgress, signal) {
205
218
  const uploadUrl = presignedUrl.startsWith("/") ? `${this.baseUrl}${presignedUrl}` : presignedUrl;
219
+ const headers = {
220
+ "Content-Type": contentType
221
+ };
222
+ if (presignedUrl.startsWith("/")) {
223
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
224
+ }
206
225
  try {
207
- await new Promise((resolve, reject) => {
208
- const xhr = new XMLHttpRequest();
209
- xhr.upload.addEventListener("progress", (event) => {
210
- if (event.lengthComputable && onProgress) {
211
- onProgress(Math.round(event.loaded / event.total * 100));
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
- });
226
+ if (this.isBrowser()) {
227
+ return await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);
228
+ } else {
229
+ return await this.uploadWithFetch(uploadUrl, file, headers, onProgress, signal);
230
+ }
239
231
  } catch (error) {
240
232
  if (error instanceof StorageBrainError) {
241
233
  throw error;
@@ -246,6 +238,87 @@ var StorageBrain = class {
246
238
  );
247
239
  }
248
240
  }
241
+ /**
242
+ * Upload using XMLHttpRequest (browser only, supports progress)
243
+ * Returns the parsed JSON response if available
244
+ */
245
+ uploadWithXHR(url, file, headers, onProgress, signal) {
246
+ return new Promise((resolve, reject) => {
247
+ const xhr = new XMLHttpRequest();
248
+ xhr.upload.addEventListener("progress", (event) => {
249
+ if (event.lengthComputable && onProgress) {
250
+ onProgress(Math.round(event.loaded / event.total * 100));
251
+ }
252
+ });
253
+ xhr.addEventListener("load", () => {
254
+ if (xhr.status >= 200 && xhr.status < 300) {
255
+ try {
256
+ const response = JSON.parse(xhr.responseText);
257
+ resolve(response);
258
+ } catch {
259
+ resolve(null);
260
+ }
261
+ } else {
262
+ reject(new UploadError(`Upload failed with status ${xhr.status}`));
263
+ }
264
+ });
265
+ xhr.addEventListener("error", () => {
266
+ reject(new NetworkError("Network error during upload"));
267
+ });
268
+ xhr.addEventListener("abort", () => {
269
+ reject(new UploadError("Upload was cancelled"));
270
+ });
271
+ if (signal) {
272
+ signal.addEventListener("abort", () => {
273
+ xhr.abort();
274
+ });
275
+ }
276
+ xhr.open("PUT", url);
277
+ for (const [key, value] of Object.entries(headers)) {
278
+ xhr.setRequestHeader(key, value);
279
+ }
280
+ xhr.send(file);
281
+ });
282
+ }
283
+ /**
284
+ * Upload using fetch (works in Node.js and browsers, limited progress support)
285
+ * Returns the parsed JSON response if available
286
+ */
287
+ async uploadWithFetch(url, file, headers, onProgress, signal) {
288
+ onProgress?.(0);
289
+ const body = await this.blobToBuffer(file);
290
+ const response = await fetch(url, {
291
+ method: "PUT",
292
+ headers,
293
+ body,
294
+ signal
295
+ });
296
+ if (!response.ok) {
297
+ const errorText = await response.text().catch(() => "Unknown error");
298
+ throw new UploadError(`Upload failed with status ${response.status}: ${errorText}`);
299
+ }
300
+ onProgress?.(100);
301
+ try {
302
+ const result = await response.json();
303
+ return result;
304
+ } catch {
305
+ return null;
306
+ }
307
+ }
308
+ /**
309
+ * Convert Blob to ArrayBuffer for Node.js fetch compatibility
310
+ */
311
+ async blobToBuffer(blob) {
312
+ if (typeof blob.arrayBuffer === "function") {
313
+ return blob.arrayBuffer();
314
+ }
315
+ return new Promise((resolve, reject) => {
316
+ const reader = new FileReader();
317
+ reader.onload = () => resolve(reader.result);
318
+ reader.onerror = () => reject(new Error("Failed to read file"));
319
+ reader.readAsArrayBuffer(blob);
320
+ });
321
+ }
249
322
  /**
250
323
  * Wait for file processing to complete
251
324
  */
@@ -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":["fileInfo"],"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 - may return immediate results for small files\n const uploadResult = 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 // Check if processing was completed synchronously (small files)\n if (uploadResult?.processingStatus === 'completed' && uploadResult?.metadata) {\n onProgress?.(100);\n // Fetch full file info (includes URL and other fields)\n const fileInfo = await this.getFile(handshake.fileId);\n return fileInfo;\n }\n\n // Poll for file status (processing may take time for large files)\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 * Returns upload response which may include immediate OCR results for small files\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<{ processingStatus?: string; metadata?: Record<string, unknown> } | null> {\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 return await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);\n } else {\n // Use fetch in Node.js (no granular progress, but works universally)\n return 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 * Returns the parsed JSON response if available\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<{ processingStatus?: string; metadata?: Record<string, unknown> } | null> {\n return new Promise((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 // Try to parse JSON response\n try {\n const response = JSON.parse(xhr.responseText);\n resolve(response);\n } catch {\n resolve(null);\n }\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 * Returns the parsed JSON response if available\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<{ processingStatus?: string; metadata?: Record<string, unknown> } | null> {\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 // Try to parse JSON response\n try {\n const result = await response.json();\n return result as { processingStatus?: string; metadata?: Record<string, unknown> };\n } catch {\n return null;\n }\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,30 @@ 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
271
+ * Returns upload response which may include immediate OCR results for small files
266
272
  */
267
273
  private uploadToPresignedUrl;
274
+ /**
275
+ * Upload using XMLHttpRequest (browser only, supports progress)
276
+ * Returns the parsed JSON response if available
277
+ */
278
+ private uploadWithXHR;
279
+ /**
280
+ * Upload using fetch (works in Node.js and browsers, limited progress support)
281
+ * Returns the parsed JSON response if available
282
+ */
283
+ private uploadWithFetch;
284
+ /**
285
+ * Convert Blob to ArrayBuffer for Node.js fetch compatibility
286
+ */
287
+ private blobToBuffer;
268
288
  /**
269
289
  * Wait for file processing to complete
270
290
  */
package/dist/index.d.ts CHANGED
@@ -261,10 +261,30 @@ 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
271
+ * Returns upload response which may include immediate OCR results for small files
266
272
  */
267
273
  private uploadToPresignedUrl;
274
+ /**
275
+ * Upload using XMLHttpRequest (browser only, supports progress)
276
+ * Returns the parsed JSON response if available
277
+ */
278
+ private uploadWithXHR;
279
+ /**
280
+ * Upload using fetch (works in Node.js and browsers, limited progress support)
281
+ * Returns the parsed JSON response if available
282
+ */
283
+ private uploadWithFetch;
284
+ /**
285
+ * Convert Blob to ArrayBuffer for Node.js fetch compatibility
286
+ */
287
+ private blobToBuffer;
268
288
  /**
269
289
  * Wait for file processing to complete
270
290
  */
package/dist/index.js CHANGED
@@ -180,10 +180,15 @@ var StorageBrain = class {
180
180
  webhookUrl
181
181
  });
182
182
  onProgress?.(10);
183
- await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {
183
+ const uploadResult = await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {
184
184
  onProgress?.(10 + Math.round(progress * 0.8));
185
185
  }, signal);
186
186
  onProgress?.(90);
187
+ if (uploadResult?.processingStatus === "completed" && uploadResult?.metadata) {
188
+ onProgress?.(100);
189
+ const fileInfo2 = await this.getFile(handshake.fileId);
190
+ return fileInfo2;
191
+ }
187
192
  const fileInfo = await this.waitForProcessing(handshake.fileId, signal);
188
193
  onProgress?.(100);
189
194
  return fileInfo;
@@ -194,44 +199,31 @@ var StorageBrain = class {
194
199
  async requestUpload(params) {
195
200
  return this.request("POST", "/api/v1/upload/request", params);
196
201
  }
202
+ /**
203
+ * Check if running in a browser environment
204
+ */
205
+ isBrowser() {
206
+ return typeof window !== "undefined" && typeof XMLHttpRequest !== "undefined";
207
+ }
197
208
  /**
198
209
  * Upload file content to presigned URL
210
+ * Uses XMLHttpRequest in browsers (for progress tracking) and fetch in Node.js
211
+ * Returns upload response which may include immediate OCR results for small files
199
212
  */
200
213
  async uploadToPresignedUrl(presignedUrl, file, contentType, onProgress, signal) {
201
214
  const uploadUrl = presignedUrl.startsWith("/") ? `${this.baseUrl}${presignedUrl}` : presignedUrl;
215
+ const headers = {
216
+ "Content-Type": contentType
217
+ };
218
+ if (presignedUrl.startsWith("/")) {
219
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
220
+ }
202
221
  try {
203
- await new Promise((resolve, reject) => {
204
- const xhr = new XMLHttpRequest();
205
- xhr.upload.addEventListener("progress", (event) => {
206
- if (event.lengthComputable && onProgress) {
207
- onProgress(Math.round(event.loaded / event.total * 100));
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
- });
222
+ if (this.isBrowser()) {
223
+ return await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);
224
+ } else {
225
+ return await this.uploadWithFetch(uploadUrl, file, headers, onProgress, signal);
226
+ }
235
227
  } catch (error) {
236
228
  if (error instanceof StorageBrainError) {
237
229
  throw error;
@@ -242,6 +234,87 @@ var StorageBrain = class {
242
234
  );
243
235
  }
244
236
  }
237
+ /**
238
+ * Upload using XMLHttpRequest (browser only, supports progress)
239
+ * Returns the parsed JSON response if available
240
+ */
241
+ uploadWithXHR(url, file, headers, onProgress, signal) {
242
+ return new Promise((resolve, reject) => {
243
+ const xhr = new XMLHttpRequest();
244
+ xhr.upload.addEventListener("progress", (event) => {
245
+ if (event.lengthComputable && onProgress) {
246
+ onProgress(Math.round(event.loaded / event.total * 100));
247
+ }
248
+ });
249
+ xhr.addEventListener("load", () => {
250
+ if (xhr.status >= 200 && xhr.status < 300) {
251
+ try {
252
+ const response = JSON.parse(xhr.responseText);
253
+ resolve(response);
254
+ } catch {
255
+ resolve(null);
256
+ }
257
+ } else {
258
+ reject(new UploadError(`Upload failed with status ${xhr.status}`));
259
+ }
260
+ });
261
+ xhr.addEventListener("error", () => {
262
+ reject(new NetworkError("Network error during upload"));
263
+ });
264
+ xhr.addEventListener("abort", () => {
265
+ reject(new UploadError("Upload was cancelled"));
266
+ });
267
+ if (signal) {
268
+ signal.addEventListener("abort", () => {
269
+ xhr.abort();
270
+ });
271
+ }
272
+ xhr.open("PUT", url);
273
+ for (const [key, value] of Object.entries(headers)) {
274
+ xhr.setRequestHeader(key, value);
275
+ }
276
+ xhr.send(file);
277
+ });
278
+ }
279
+ /**
280
+ * Upload using fetch (works in Node.js and browsers, limited progress support)
281
+ * Returns the parsed JSON response if available
282
+ */
283
+ async uploadWithFetch(url, file, headers, onProgress, signal) {
284
+ onProgress?.(0);
285
+ const body = await this.blobToBuffer(file);
286
+ const response = await fetch(url, {
287
+ method: "PUT",
288
+ headers,
289
+ body,
290
+ signal
291
+ });
292
+ if (!response.ok) {
293
+ const errorText = await response.text().catch(() => "Unknown error");
294
+ throw new UploadError(`Upload failed with status ${response.status}: ${errorText}`);
295
+ }
296
+ onProgress?.(100);
297
+ try {
298
+ const result = await response.json();
299
+ return result;
300
+ } catch {
301
+ return null;
302
+ }
303
+ }
304
+ /**
305
+ * Convert Blob to ArrayBuffer for Node.js fetch compatibility
306
+ */
307
+ async blobToBuffer(blob) {
308
+ if (typeof blob.arrayBuffer === "function") {
309
+ return blob.arrayBuffer();
310
+ }
311
+ return new Promise((resolve, reject) => {
312
+ const reader = new FileReader();
313
+ reader.onload = () => resolve(reader.result);
314
+ reader.onerror = () => reject(new Error("Failed to read file"));
315
+ reader.readAsArrayBuffer(blob);
316
+ });
317
+ }
245
318
  /**
246
319
  * Wait for file processing to complete
247
320
  */
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts"],"names":[],"mappings":";AAGO,IAAM,kBAAA,GAAqB;AAAA;AAAA,EAEhC,YAAA,EAAc,EAAE,SAAA,EAAW,KAAA,EAAO,UAAU,OAAA,EAAQ;AAAA,EACpD,WAAA,EAAa,EAAE,SAAA,EAAW,KAAA,EAAO,UAAU,OAAA,EAAQ;AAAA,EACnD,YAAA,EAAc,EAAE,SAAA,EAAW,MAAA,EAAQ,UAAU,OAAA,EAAQ;AAAA,EACrD,WAAA,EAAa,EAAE,SAAA,EAAW,KAAA,EAAO,UAAU,OAAA,EAAQ;AAAA,EACnD,YAAA,EAAc,EAAE,SAAA,EAAW,MAAA,EAAQ,UAAU,OAAA,EAAQ;AAAA;AAAA,EAErD,iBAAA,EAAmB,EAAE,SAAA,EAAW,KAAA,EAAO,UAAU,UAAA;AACnD;AAIO,IAAM,kBAAA,GAAqB,MAAA,CAAO,IAAA,CAAK,kBAAkB;AAEzD,IAAM,mBAAmB,kBAAA,CAAmB,MAAA;AAAA,EACjD,CAAC,IAAA,KAAS,kBAAA,CAAmB,IAAI,EAAE,QAAA,KAAa;AAClD;AAEO,IAAM,sBAAsB,kBAAA,CAAmB,MAAA;AAAA,EACpD,CAAC,IAAA,KAAS,kBAAA,CAAmB,IAAI,EAAE,QAAA,KAAa;AAClD;AAKO,IAAM,mBAAA,GAAsB,CAAC,YAAA,EAAc,SAAA,EAAW,eAAe,SAAS;AAM9E,IAAM,mBAAA,GAAsB,MAAM,IAAA,GAAO;AAKzC,IAAM,eAAA,GAAkB;AAAA,EAC7B,KAAA,EAAO,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,GAAA,EAAI;AAAA,EACjC,MAAA,EAAQ,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,GAAA,EAAI;AAAA,EAClC,KAAA,EAAO,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,GAAA;AAC/B;AAOO,IAAM,mBAAA,GAAsB,CAAC,SAAA,EAAW,YAAA,EAAc,aAAa,QAAQ;AAM3E,IAAM,YAAA,GAAe;AAAA,EAE1B,cAAA,EAAgB,GAAA;AAAA,EAChB,UAAA,EAAY,GAAA;AAAA,EACZ,iBAAA,EAAmB;AACrB,CAAA;;;AC3DO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EAC3C,WAAA,CACE,OAAA,EACO,IAAA,EACA,UAAA,EACA,OAAA,EACP;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAJN,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAGP,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF;AAKO,IAAM,mBAAA,GAAN,cAAkC,iBAAA,CAAkB;AAAA,EACzD,WAAA,CAAY,UAAU,uBAAA,EAAyB;AAC7C,IAAA,KAAA,CAAM,OAAA,EAAS,wBAAwB,GAAG,CAAA;AAC1C,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAKO,IAAM,kBAAA,GAAN,cAAiC,iBAAA,CAAkB;AAAA,EACxD,WAAA,CACE,OAAA,GAAU,wBAAA,EACH,UAAA,EACA,SAAA,EACP;AACA,IAAA,KAAA,CAAM,SAAS,gBAAA,EAAkB,GAAA,EAAK,EAAE,UAAA,EAAY,WAAW,CAAA;AAHxD,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAGP,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AAAA,EACd;AACF;AAKO,IAAM,oBAAA,GAAN,cAAmC,iBAAA,CAAkB;AAAA,EAC1D,WAAA,CACE,UACA,YAAA,EACA;AACA,IAAA,KAAA;AAAA,MACE,cAAc,QAAQ,CAAA,gBAAA,CAAA;AAAA,MACtB,mBAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAE,UAAU,YAAA;AAAa,KAC3B;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AAAA,EACd;AACF;AAKO,IAAM,iBAAA,GAAN,cAAgC,iBAAA,CAAkB;AAAA,EACvD,WAAA,CACE,UACA,OAAA,EACA;AACA,IAAA,KAAA;AAAA,MACE,CAAA,UAAA,EAAa,QAAQ,CAAA,0BAAA,EAA6B,OAAO,CAAA,MAAA,CAAA;AAAA,MACzD,gBAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAE,UAAU,OAAA;AAAQ,KACtB;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF;AAKO,IAAM,iBAAA,GAAN,cAAgC,iBAAA,CAAkB;AAAA,EACvD,YAAY,MAAA,EAAgB;AAC1B,IAAA,KAAA,CAAM,mBAAmB,MAAM,CAAA,CAAA,EAAI,kBAAkB,GAAA,EAAK,EAAE,QAAQ,CAAA;AACpE,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF;AAKO,IAAM,YAAA,GAAN,cAA2B,iBAAA,CAAkB;AAAA,EAClD,WAAA,CAAY,OAAA,GAAU,wBAAA,EAAiC,aAAA,EAAuB;AAC5E,IAAA,KAAA,CAAM,SAAS,eAAA,EAAiB,MAAA,EAAW,EAAE,aAAA,EAAe,aAAA,EAAe,SAAS,CAAA;AAD/B,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AAErD,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AAAA,EACd;AACF;AAKO,IAAM,WAAA,GAAN,cAA0B,iBAAA,CAAkB;AAAA,EACjD,WAAA,CAAY,SAAwB,aAAA,EAAuB;AACzD,IAAA,KAAA,CAAM,SAAS,cAAA,EAAgB,MAAA,EAAW,EAAE,aAAA,EAAe,aAAA,EAAe,SAAS,CAAA;AADjD,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AAElC,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AAAA,EACd;AACF;AAKO,IAAM,eAAA,GAAN,cAA8B,iBAAA,CAAkB;AAAA,EACrD,WAAA,CACE,SACO,MAAA,EACP;AACA,IAAA,KAAA,CAAM,OAAA,EAAS,kBAAA,EAAoB,GAAA,EAAK,EAAE,QAAQ,CAAA;AAF3C,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAGP,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAKO,SAAS,aAAA,CACd,YACA,QAAA,EACmB;AACnB,EAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,SAAQ,GAAI,QAAA,CAAS,SAAS,EAAC;AAEtD,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,cAAA;AACH,MAAA,OAAO,IAAI,oBAAoB,OAAO,CAAA;AAAA,IACxC,KAAK,gBAAA;AACH,MAAA,OAAO,IAAI,kBAAA;AAAA,QACT,OAAA;AAAA,QACA,OAAA,EAAS,UAAA;AAAA,QACT,OAAA,EAAS;AAAA,OACX;AAAA,IACF,KAAK,mBAAA;AACH,MAAA,OAAO,IAAI,oBAAA;AAAA,QACT,OAAA,EAAS,QAAA;AAAA,QACT,OAAA,EAAS;AAAA,OACX;AAAA,IACF,KAAK,gBAAA;AACH,MAAA,OAAO,IAAI,iBAAA;AAAA,QACT,OAAA,EAAS,QAAA;AAAA,QACT,OAAA,EAAS;AAAA,OACX;AAAA,IACF,KAAK,gBAAA;AAAA,IACL,KAAK,WAAA;AACH,MAAA,OAAO,IAAI,iBAAA,CAAkB,OAAA,EAAS,MAAA,IAAoB,SAAS,CAAA;AAAA,IACrE,KAAK,kBAAA;AACH,MAAA,OAAO,IAAI,eAAA;AAAA,QACT,OAAA,IAAW,mBAAA;AAAA,QACX,OAAA,EAAS;AAAA,OACX;AAAA,IACF;AACE,MAAA,OAAO,IAAI,iBAAA;AAAA,QACT,OAAA,IAAW,mBAAA;AAAA,QACX,IAAA,IAAQ,eAAA;AAAA,QACR,UAAA;AAAA,QACA;AAAA,OACF;AAAA;AAEN;;;AC9IA,IAAM,gBAAA,GAAmB,mDAAA;AACzB,IAAM,eAAA,GAAkB,GAAA;AACxB,IAAM,mBAAA,GAAsB,CAAA;AAoBrB,IAAM,eAAN,MAAmB;AAAA,EACP,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EAEjB,YAAY,MAAA,EAA4B;AACtC,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,MAAM,IAAI,iBAAA,CAAkB,qBAAA,EAAuB,qBAAqB,CAAA;AAAA,IAC1E;AAEA,IAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA;AACrB,IAAA,IAAA,CAAK,WAAW,MAAA,CAAO,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrE,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,eAAA;AACjC,IAAA,IAAA,CAAK,UAAA,GAAa,OAAO,UAAA,IAAc,mBAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,CAAO,IAAA,EAAmB,OAAA,EAA2C;AACzE,IAAA,MAAM,EAAE,OAAA,EAAS,IAAA,EAAM,UAAA,EAAY,UAAA,EAAY,QAAO,GAAI,OAAA;AAG1D,IAAA,MAAM,QAAA,GAAW,IAAA,YAAgB,IAAA,GAAO,IAAA,CAAK,IAAA,GAAO,MAAA;AACpD,IAAA,MAAM,WAAW,IAAA,CAAK,IAAA;AACtB,IAAA,MAAM,WAAW,IAAA,CAAK,IAAA;AAGtB,IAAA,IAAI,CAAC,kBAAA,CAAmB,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC1C,MAAA,MAAM,IAAI,oBAAA,CAAqB,QAAA,EAAU,CAAC,GAAG,kBAAkB,CAAC,CAAA;AAAA,IAClE;AAGA,IAAA,MAAM,SAAA,GAAY,MAAM,IAAA,CAAK,aAAA,CAAc;AAAA,MACzC,QAAA;AAAA,MACA,QAAA;AAAA,MACA,aAAA,EAAe,QAAA;AAAA,MACf,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,UAAA,GAAa,EAAE,CAAA;AAGf,IAAA,MAAM,KAAK,oBAAA,CAAqB,SAAA,CAAU,cAAc,IAAA,EAAM,QAAA,EAAU,CAAC,QAAA,KAAa;AAEpF,MAAA,UAAA,GAAa,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,GAAG,CAAC,CAAA;AAAA,IAC9C,GAAG,MAAM,CAAA;AAET,IAAA,UAAA,GAAa,EAAE,CAAA;AAGf,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,iBAAA,CAAkB,SAAA,CAAU,QAAQ,MAAM,CAAA;AAEtE,IAAA,UAAA,GAAa,GAAG,CAAA;AAEhB,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,MAAA,EAOC;AAC3B,IAAA,OAAO,IAAA,CAAK,OAAA,CAAyB,MAAA,EAAQ,wBAAA,EAA0B,MAAM,CAAA;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBAAA,CACZ,YAAA,EACA,IAAA,EACA,WAAA,EACA,YACA,MAAA,EACe;AAEf,IAAA,MAAM,SAAA,GAAY,YAAA,CAAa,UAAA,CAAW,GAAG,CAAA,GACzC,GAAG,IAAA,CAAK,OAAO,CAAA,EAAG,YAAY,CAAA,CAAA,GAC9B,YAAA;AAEJ,IAAA,IAAI;AAEF,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,QAAA,MAAM,GAAA,GAAM,IAAI,cAAA,EAAe;AAE/B,QAAA,GAAA,CAAI,MAAA,CAAO,gBAAA,CAAiB,UAAA,EAAY,CAAC,KAAA,KAAU;AACjD,UAAA,IAAI,KAAA,CAAM,oBAAoB,UAAA,EAAY;AACxC,YAAA,UAAA,CAAW,KAAK,KAAA,CAAO,KAAA,CAAM,SAAS,KAAA,CAAM,KAAA,GAAS,GAAG,CAAC,CAAA;AAAA,UAC3D;AAAA,QACF,CAAC,CAAA;AAED,QAAA,GAAA,CAAI,gBAAA,CAAiB,QAAQ,MAAM;AACjC,UAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AACzC,YAAA,OAAA,EAAQ;AAAA,UACV,CAAA,MAAO;AACL,YAAA,MAAA,CAAO,IAAI,WAAA,CAAY,CAAA,0BAAA,EAA6B,GAAA,CAAI,MAAM,EAAE,CAAC,CAAA;AAAA,UACnE;AAAA,QACF,CAAC,CAAA;AAED,QAAA,GAAA,CAAI,gBAAA,CAAiB,SAAS,MAAM;AAClC,UAAA,MAAA,CAAO,IAAI,YAAA,CAAa,6BAA6B,CAAC,CAAA;AAAA,QACxD,CAAC,CAAA;AAED,QAAA,GAAA,CAAI,gBAAA,CAAiB,SAAS,MAAM;AAClC,UAAA,MAAA,CAAO,IAAI,WAAA,CAAY,sBAAsB,CAAC,CAAA;AAAA,QAChD,CAAC,CAAA;AAED,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AACrC,YAAA,GAAA,CAAI,KAAA,EAAM;AAAA,UACZ,CAAC,CAAA;AAAA,QACH;AAEA,QAAA,GAAA,CAAI,IAAA,CAAK,OAAO,SAAS,CAAA;AACzB,QAAA,GAAA,CAAI,gBAAA,CAAiB,gBAAgB,WAAW,CAAA;AAGhD,QAAA,IAAI,YAAA,CAAa,UAAA,CAAW,GAAG,CAAA,EAAG;AAChC,UAAA,GAAA,CAAI,gBAAA,CAAiB,eAAA,EAAiB,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAE,CAAA;AAAA,QAC/D;AAEA,QAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,MACf,CAAC,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,iBAAA,EAAmB;AACtC,QAAA,MAAM,KAAA;AAAA,MACR;AACA,MAAA,MAAM,IAAI,WAAA;AAAA,QACR,uBAAA;AAAA,QACA,KAAA,YAAiB,QAAQ,KAAA,GAAQ;AAAA,OACnC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAA,CACZ,MAAA,EACA,QACA,SAAA,GAAY,GAAA,EACZ,iBAAiB,GAAA,EACE;AACnB,IAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA,GAAY,SAAA,EAAW;AACzC,MAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,QAAA,MAAM,IAAI,YAAY,yBAAyB,CAAA;AAAA,MACjD;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA;AAEtC,MAAA,IAAI,IAAA,CAAK,gBAAA,KAAqB,WAAA,IAAe,IAAA,CAAK,qBAAqB,QAAA,EAAU;AAC/E,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,cAAc,CAAC,CAAA;AAAA,IACpE;AAGA,IAAA,OAAO,IAAA,CAAK,QAAQ,MAAM,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,MAAA,EAAmC;AAC/C,IAAA,OAAO,IAAA,CAAK,OAAA,CAAkB,KAAA,EAAO,CAAA,cAAA,EAAiB,MAAM,CAAA,CAAE,CAAA;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,OAAA,EAAsD;AACpE,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AAEnC,IAAA,IAAI,OAAA,EAAS,OAAO,MAAA,CAAO,GAAA,CAAI,SAAS,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAA;AAChE,IAAA,IAAI,SAAS,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,QAAQ,MAAM,CAAA;AACxD,IAAA,IAAI,SAAS,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,QAAQ,OAAO,CAAA;AAC3D,IAAA,IAAI,SAAS,QAAA,EAAU,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,QAAQ,QAAQ,CAAA;AAE9D,IAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,EAAS;AAC9B,IAAA,MAAM,IAAA,GAAO,KAAA,GAAQ,CAAA,cAAA,EAAiB,KAAK,CAAA,CAAA,GAAK,eAAA;AAEhD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAyB,KAAA,EAAO,IAAI,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,MAAA,EAA+B;AAC9C,IAAA,MAAM,IAAA,CAAK,OAAA,CAA8B,QAAA,EAAU,CAAA,cAAA,EAAiB,MAAM,CAAA,CAAE,CAAA;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAA,GAA+B;AACnC,IAAA,OAAO,IAAA,CAAK,OAAA,CAAmB,KAAA,EAAO,sBAAsB,CAAA;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAA,GAAqC;AACzC,IAAA,OAAO,IAAA,CAAK,OAAA,CAAoB,KAAA,EAAO,qBAAqB,CAAA;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,OAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,IAAI,CAAA,CAAA;AAClC,IAAA,IAAI,SAAA;AAEJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,IAAA,CAAK,YAAY,OAAA,EAAA,EAAW;AAC1D,MAAA,IAAI;AACF,QAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,QAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,OAAO,CAAA;AAEnE,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,UAChC,MAAA;AAAA,UACA,OAAA,EAAS;AAAA,YACP,cAAA,EAAgB,kBAAA;AAAA,YAChB,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA;AAAA,WACtC;AAAA,UACA,IAAA,EAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,KAAA,CAAA;AAAA,UACpC,QAAQ,UAAA,CAAW;AAAA,SACpB,CAAA;AAED,QAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,QAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,UAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,GAAO,KAAA,CAAM,OAAO,EAAC,CAAE,CAAA;AACxD,UAAA,MAAM,aAAA,CAAc,QAAA,CAAS,MAAA,EAAQ,SAA+F,CAAA;AAAA,QACtI;AAEA,QAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAAA,MAC7B,SAAS,KAAA,EAAO;AACd,QAAA,SAAA,GAAY,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAGpE,QAAA,IAAI,iBAAiB,iBAAA,IAAqB,KAAA,CAAM,UAAA,IAAc,KAAA,CAAM,aAAa,GAAA,EAAK;AACpF,UAAA,MAAM,KAAA;AAAA,QACR;AAGA,QAAA,IAAI,OAAA,GAAU,IAAA,CAAK,UAAA,GAAa,CAAA,EAAG;AACjC,UAAA,MAAM,QAAQ,IAAA,CAAK,GAAA;AAAA,YACjB,aAAa,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,YAAA,CAAa,mBAAmB,OAAO,CAAA;AAAA,YAC9E,YAAA,CAAa;AAAA,WACf;AACA,UAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,KAAK,CAAC,CAAA;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAEA,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,CAAA,qBAAA,EAAwB,KAAK,UAAU,CAAA,SAAA,CAAA;AAAA,MACvC;AAAA,KACF;AAAA,EACF;AACF","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":["fileInfo"],"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 - may return immediate results for small files\n const uploadResult = 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 // Check if processing was completed synchronously (small files)\n if (uploadResult?.processingStatus === 'completed' && uploadResult?.metadata) {\n onProgress?.(100);\n // Fetch full file info (includes URL and other fields)\n const fileInfo = await this.getFile(handshake.fileId);\n return fileInfo;\n }\n\n // Poll for file status (processing may take time for large files)\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 * Returns upload response which may include immediate OCR results for small files\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<{ processingStatus?: string; metadata?: Record<string, unknown> } | null> {\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 return await this.uploadWithXHR(uploadUrl, file, headers, onProgress, signal);\n } else {\n // Use fetch in Node.js (no granular progress, but works universally)\n return 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 * Returns the parsed JSON response if available\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<{ processingStatus?: string; metadata?: Record<string, unknown> } | null> {\n return new Promise((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 // Try to parse JSON response\n try {\n const response = JSON.parse(xhr.responseText);\n resolve(response);\n } catch {\n resolve(null);\n }\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 * Returns the parsed JSON response if available\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<{ processingStatus?: string; metadata?: Record<string, unknown> } | null> {\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 // Try to parse JSON response\n try {\n const result = await response.json();\n return result as { processingStatus?: string; metadata?: Record<string, unknown> };\n } catch {\n return null;\n }\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marlinjai/storage-brain-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript SDK for Storage Brain - edge-native file storage service",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",