@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 +106 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +106 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
});
|
|
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
|
*/
|
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":["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
|
-
|
|
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
|
-
});
|
|
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":"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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":["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"]}
|