@marlinjai/storage-brain-sdk 0.1.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.
@@ -0,0 +1,367 @@
1
+ /**
2
+ * Allowed MIME types for file uploads
3
+ */
4
+ declare const ALLOWED_FILE_TYPES: {
5
+ readonly 'image/jpeg': {
6
+ readonly extension: "jpg";
7
+ readonly category: "image";
8
+ };
9
+ readonly 'image/png': {
10
+ readonly extension: "png";
11
+ readonly category: "image";
12
+ };
13
+ readonly 'image/webp': {
14
+ readonly extension: "webp";
15
+ readonly category: "image";
16
+ };
17
+ readonly 'image/gif': {
18
+ readonly extension: "gif";
19
+ readonly category: "image";
20
+ };
21
+ readonly 'image/avif': {
22
+ readonly extension: "avif";
23
+ readonly category: "image";
24
+ };
25
+ readonly 'application/pdf': {
26
+ readonly extension: "pdf";
27
+ readonly category: "document";
28
+ };
29
+ };
30
+ type AllowedMimeType = keyof typeof ALLOWED_FILE_TYPES;
31
+ declare const ALLOWED_MIME_TYPES: AllowedMimeType[];
32
+ declare const IMAGE_MIME_TYPES: ("image/jpeg" | "image/png" | "image/webp" | "image/gif" | "image/avif" | "application/pdf")[];
33
+ declare const DOCUMENT_MIME_TYPES: ("image/jpeg" | "image/png" | "image/webp" | "image/gif" | "image/avif" | "application/pdf")[];
34
+ /**
35
+ * Processing contexts
36
+ */
37
+ declare const PROCESSING_CONTEXTS: readonly ["newsletter", "invoice", "framer-site", "default"];
38
+ type ProcessingContext = (typeof PROCESSING_CONTEXTS)[number];
39
+ /**
40
+ * File size limits
41
+ */
42
+ declare const MAX_FILE_SIZE_BYTES: number;
43
+ /**
44
+ * Thumbnail sizes
45
+ */
46
+ declare const THUMBNAIL_SIZES: {
47
+ readonly thumb: {
48
+ readonly width: 200;
49
+ readonly height: 200;
50
+ };
51
+ readonly medium: {
52
+ readonly width: 400;
53
+ readonly height: 400;
54
+ };
55
+ readonly large: {
56
+ readonly width: 800;
57
+ readonly height: 800;
58
+ };
59
+ };
60
+ type ThumbnailSize = keyof typeof THUMBNAIL_SIZES;
61
+ /**
62
+ * Processing statuses
63
+ */
64
+ declare const PROCESSING_STATUSES: readonly ["pending", "processing", "completed", "failed"];
65
+ type ProcessingStatus = (typeof PROCESSING_STATUSES)[number];
66
+
67
+ /**
68
+ * File metadata (stored as JSON)
69
+ */
70
+ interface FileMetadata {
71
+ thumbnailUrls?: ThumbnailUrls;
72
+ ocrData?: OcrResult;
73
+ imageInfo?: ImageInfo;
74
+ processingError?: string;
75
+ [key: string]: unknown;
76
+ }
77
+ /**
78
+ * Thumbnail URLs for different sizes
79
+ */
80
+ type ThumbnailUrls = {
81
+ [K in ThumbnailSize]?: string;
82
+ };
83
+ /**
84
+ * OCR result from Google Cloud Vision
85
+ */
86
+ interface OcrResult {
87
+ fullText: string;
88
+ confidence: number;
89
+ blocks: OcrBlock[];
90
+ }
91
+ interface OcrBlock {
92
+ text: string;
93
+ confidence: number;
94
+ boundingBox: BoundingBox;
95
+ }
96
+ interface BoundingBox {
97
+ x: number;
98
+ y: number;
99
+ width: number;
100
+ height: number;
101
+ }
102
+ /**
103
+ * Image information extracted from EXIF
104
+ */
105
+ interface ImageInfo {
106
+ width: number;
107
+ height: number;
108
+ format: string;
109
+ colorSpace?: string;
110
+ hasAlpha?: boolean;
111
+ }
112
+ /**
113
+ * Configuration for Storage Brain client
114
+ */
115
+ interface StorageBrainConfig {
116
+ /** API key for authentication (sk_live_... or sk_test_...) */
117
+ apiKey: string;
118
+ /** Base URL of the Storage Brain API (defaults to production) */
119
+ baseUrl?: string;
120
+ /** Request timeout in milliseconds (default: 30000) */
121
+ timeout?: number;
122
+ /** Number of retry attempts for failed requests (default: 3) */
123
+ maxRetries?: number;
124
+ }
125
+ /**
126
+ * Options for uploading a file
127
+ */
128
+ interface UploadOptions {
129
+ /** Processing context for the file */
130
+ context: ProcessingContext;
131
+ /** Optional tags for the file */
132
+ tags?: Record<string, string>;
133
+ /** Progress callback (0-100) */
134
+ onProgress?: (progress: number) => void;
135
+ /** Optional webhook URL to call after processing */
136
+ webhookUrl?: string;
137
+ /** Abort signal for cancellation */
138
+ signal?: AbortSignal;
139
+ }
140
+ /**
141
+ * File information returned from the API
142
+ */
143
+ interface FileInfo {
144
+ /** Unique file identifier */
145
+ id: string;
146
+ /** Public URL to access the file */
147
+ url: string;
148
+ /** Original file name */
149
+ originalName: string;
150
+ /** MIME type */
151
+ fileType: AllowedMimeType;
152
+ /** File size in bytes */
153
+ sizeBytes: number;
154
+ /** Processing context */
155
+ context: ProcessingContext;
156
+ /** User-defined tags */
157
+ tags: Record<string, string> | null;
158
+ /** Processing results and metadata */
159
+ metadata: FileMetadata | null;
160
+ /** Current processing status */
161
+ processingStatus: ProcessingStatus;
162
+ /** ISO 8601 timestamp */
163
+ createdAt: string;
164
+ }
165
+ /**
166
+ * Options for listing files
167
+ */
168
+ interface ListFilesOptions {
169
+ /** Maximum number of files to return (1-100, default: 20) */
170
+ limit?: number;
171
+ /** Cursor for pagination */
172
+ cursor?: string;
173
+ /** Filter by processing context */
174
+ context?: ProcessingContext;
175
+ /** Filter by file type */
176
+ fileType?: AllowedMimeType;
177
+ }
178
+ /**
179
+ * Result of listing files
180
+ */
181
+ interface ListFilesResult {
182
+ /** Array of file information */
183
+ files: FileInfo[];
184
+ /** Cursor for next page, null if no more pages */
185
+ nextCursor: string | null;
186
+ /** Total number of files matching the query */
187
+ total: number;
188
+ }
189
+ /**
190
+ * Quota usage information
191
+ */
192
+ interface QuotaInfo {
193
+ /** Total quota in bytes */
194
+ quotaBytes: number;
195
+ /** Used storage in bytes */
196
+ usedBytes: number;
197
+ /** Available storage in bytes */
198
+ availableBytes: number;
199
+ /** Usage percentage (0-100) */
200
+ usagePercent: number;
201
+ }
202
+ /**
203
+ * Tenant information
204
+ */
205
+ interface TenantInfo {
206
+ /** Tenant ID */
207
+ id: string;
208
+ /** Tenant name */
209
+ name: string;
210
+ /** Allowed file types for this tenant */
211
+ allowedFileTypes: AllowedMimeType[] | null;
212
+ /** ISO 8601 timestamp */
213
+ createdAt: string;
214
+ }
215
+ /**
216
+ * Upload handshake response
217
+ */
218
+ interface UploadHandshake {
219
+ /** File ID assigned to this upload */
220
+ fileId: string;
221
+ /** Presigned URL for uploading */
222
+ presignedUrl: string;
223
+ /** Expiration timestamp (ISO 8601) */
224
+ expiresAt: string;
225
+ /** Upload constraints */
226
+ uploadMetadata: {
227
+ maxSizeBytes: number;
228
+ allowedTypes: AllowedMimeType[];
229
+ };
230
+ }
231
+
232
+ /**
233
+ * Storage Brain SDK Client
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * const storage = new StorageBrain({
238
+ * apiKey: 'sk_live_...',
239
+ * });
240
+ *
241
+ * // Upload a file
242
+ * const file = await storage.upload(fileBlob, {
243
+ * context: 'newsletter',
244
+ * onProgress: (p) => console.log(`${p}%`),
245
+ * });
246
+ *
247
+ * console.log(file.url);
248
+ * ```
249
+ */
250
+ declare class StorageBrain {
251
+ private readonly apiKey;
252
+ private readonly baseUrl;
253
+ private readonly timeout;
254
+ private readonly maxRetries;
255
+ constructor(config: StorageBrainConfig);
256
+ /**
257
+ * Upload a file
258
+ */
259
+ upload(file: File | Blob, options: UploadOptions): Promise<FileInfo>;
260
+ /**
261
+ * Request an upload handshake
262
+ */
263
+ private requestUpload;
264
+ /**
265
+ * Upload file content to presigned URL
266
+ */
267
+ private uploadToPresignedUrl;
268
+ /**
269
+ * Wait for file processing to complete
270
+ */
271
+ private waitForProcessing;
272
+ /**
273
+ * Get a file by ID
274
+ */
275
+ getFile(fileId: string): Promise<FileInfo>;
276
+ /**
277
+ * List files
278
+ */
279
+ listFiles(options?: ListFilesOptions): Promise<ListFilesResult>;
280
+ /**
281
+ * Delete a file
282
+ */
283
+ deleteFile(fileId: string): Promise<void>;
284
+ /**
285
+ * Get quota usage
286
+ */
287
+ getQuota(): Promise<QuotaInfo>;
288
+ /**
289
+ * Get tenant information
290
+ */
291
+ getTenantInfo(): Promise<TenantInfo>;
292
+ /**
293
+ * Make an authenticated API request with retry logic
294
+ */
295
+ private request;
296
+ }
297
+
298
+ /**
299
+ * Base error class for Storage Brain SDK
300
+ */
301
+ declare class StorageBrainError extends Error {
302
+ code: string;
303
+ statusCode?: number | undefined;
304
+ details?: Record<string, unknown> | undefined;
305
+ constructor(message: string, code: string, statusCode?: number | undefined, details?: Record<string, unknown> | undefined);
306
+ }
307
+ /**
308
+ * Authentication error - invalid or missing API key
309
+ */
310
+ declare class AuthenticationError extends StorageBrainError {
311
+ constructor(message?: string);
312
+ }
313
+ /**
314
+ * Quota exceeded error
315
+ */
316
+ declare class QuotaExceededError extends StorageBrainError {
317
+ quotaBytes?: number | undefined;
318
+ usedBytes?: number | undefined;
319
+ constructor(message?: string, quotaBytes?: number | undefined, usedBytes?: number | undefined);
320
+ }
321
+ /**
322
+ * Invalid file type error
323
+ */
324
+ declare class InvalidFileTypeError extends StorageBrainError {
325
+ constructor(fileType: string, allowedTypes?: string[]);
326
+ }
327
+ /**
328
+ * File too large error
329
+ */
330
+ declare class FileTooLargeError extends StorageBrainError {
331
+ constructor(fileSize: number, maxSize: number);
332
+ }
333
+ /**
334
+ * File not found error
335
+ */
336
+ declare class FileNotFoundError extends StorageBrainError {
337
+ constructor(fileId: string);
338
+ }
339
+ /**
340
+ * Network error - connection issues
341
+ */
342
+ declare class NetworkError extends StorageBrainError {
343
+ originalError?: Error | undefined;
344
+ constructor(message?: string, originalError?: Error | undefined);
345
+ }
346
+ /**
347
+ * Upload error - file upload failed
348
+ */
349
+ declare class UploadError extends StorageBrainError {
350
+ originalError?: Error | undefined;
351
+ constructor(message: string, originalError?: Error | undefined);
352
+ }
353
+ /**
354
+ * Validation error - request validation failed
355
+ */
356
+ declare class ValidationError extends StorageBrainError {
357
+ errors?: Array<{
358
+ path: string;
359
+ message: string;
360
+ }> | undefined;
361
+ constructor(message: string, errors?: Array<{
362
+ path: string;
363
+ message: string;
364
+ }> | undefined);
365
+ }
366
+
367
+ export { ALLOWED_FILE_TYPES, ALLOWED_MIME_TYPES, type AllowedMimeType, AuthenticationError, type BoundingBox, DOCUMENT_MIME_TYPES, type FileInfo, type FileMetadata, FileNotFoundError, FileTooLargeError, IMAGE_MIME_TYPES, type ImageInfo, InvalidFileTypeError, type ListFilesOptions, type ListFilesResult, MAX_FILE_SIZE_BYTES, NetworkError, type OcrBlock, type OcrResult, PROCESSING_CONTEXTS, PROCESSING_STATUSES, type ProcessingContext, type ProcessingStatus, QuotaExceededError, type QuotaInfo, StorageBrain, type StorageBrainConfig, StorageBrainError, THUMBNAIL_SIZES, type TenantInfo, type ThumbnailSize, type ThumbnailUrls, UploadError, type UploadHandshake, type UploadOptions, ValidationError, StorageBrain as default };
package/dist/index.js ADDED
@@ -0,0 +1,347 @@
1
+ // src/constants.ts
2
+ var ALLOWED_FILE_TYPES = {
3
+ // Images
4
+ "image/jpeg": { extension: "jpg", category: "image" },
5
+ "image/png": { extension: "png", category: "image" },
6
+ "image/webp": { extension: "webp", category: "image" },
7
+ "image/gif": { extension: "gif", category: "image" },
8
+ "image/avif": { extension: "avif", category: "image" },
9
+ // Documents
10
+ "application/pdf": { extension: "pdf", category: "document" }
11
+ };
12
+ var ALLOWED_MIME_TYPES = Object.keys(ALLOWED_FILE_TYPES);
13
+ var IMAGE_MIME_TYPES = ALLOWED_MIME_TYPES.filter(
14
+ (type) => ALLOWED_FILE_TYPES[type].category === "image"
15
+ );
16
+ var DOCUMENT_MIME_TYPES = ALLOWED_MIME_TYPES.filter(
17
+ (type) => ALLOWED_FILE_TYPES[type].category === "document"
18
+ );
19
+ var PROCESSING_CONTEXTS = ["newsletter", "invoice", "framer-site", "default"];
20
+ var MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
21
+ var THUMBNAIL_SIZES = {
22
+ thumb: { width: 200, height: 200 },
23
+ medium: { width: 400, height: 400 },
24
+ large: { width: 800, height: 800 }
25
+ };
26
+ var PROCESSING_STATUSES = ["pending", "processing", "completed", "failed"];
27
+ var RETRY_CONFIG = {
28
+ initialDelayMs: 1e3,
29
+ maxDelayMs: 1e4,
30
+ backoffMultiplier: 2
31
+ };
32
+
33
+ // src/errors.ts
34
+ var StorageBrainError = class extends Error {
35
+ constructor(message, code, statusCode, details) {
36
+ super(message);
37
+ this.code = code;
38
+ this.statusCode = statusCode;
39
+ this.details = details;
40
+ this.name = "StorageBrainError";
41
+ }
42
+ };
43
+ var AuthenticationError = class extends StorageBrainError {
44
+ constructor(message = "Authentication failed") {
45
+ super(message, "AUTHENTICATION_ERROR", 401);
46
+ this.name = "AuthenticationError";
47
+ }
48
+ };
49
+ var QuotaExceededError = class extends StorageBrainError {
50
+ constructor(message = "Storage quota exceeded", quotaBytes, usedBytes) {
51
+ super(message, "QUOTA_EXCEEDED", 403, { quotaBytes, usedBytes });
52
+ this.quotaBytes = quotaBytes;
53
+ this.usedBytes = usedBytes;
54
+ this.name = "QuotaExceededError";
55
+ }
56
+ };
57
+ var InvalidFileTypeError = class extends StorageBrainError {
58
+ constructor(fileType, allowedTypes) {
59
+ super(
60
+ `File type '${fileType}' is not allowed`,
61
+ "INVALID_FILE_TYPE",
62
+ 400,
63
+ { fileType, allowedTypes }
64
+ );
65
+ this.name = "InvalidFileTypeError";
66
+ }
67
+ };
68
+ var FileTooLargeError = class extends StorageBrainError {
69
+ constructor(fileSize, maxSize) {
70
+ super(
71
+ `File size ${fileSize} bytes exceeds maximum of ${maxSize} bytes`,
72
+ "FILE_TOO_LARGE",
73
+ 400,
74
+ { fileSize, maxSize }
75
+ );
76
+ this.name = "FileTooLargeError";
77
+ }
78
+ };
79
+ var FileNotFoundError = class extends StorageBrainError {
80
+ constructor(fileId) {
81
+ super(`File not found: ${fileId}`, "FILE_NOT_FOUND", 404, { fileId });
82
+ this.name = "FileNotFoundError";
83
+ }
84
+ };
85
+ var NetworkError = class extends StorageBrainError {
86
+ constructor(message = "Network error occurred", originalError) {
87
+ super(message, "NETWORK_ERROR", void 0, { originalError: originalError?.message });
88
+ this.originalError = originalError;
89
+ this.name = "NetworkError";
90
+ }
91
+ };
92
+ var UploadError = class extends StorageBrainError {
93
+ constructor(message, originalError) {
94
+ super(message, "UPLOAD_ERROR", void 0, { originalError: originalError?.message });
95
+ this.originalError = originalError;
96
+ this.name = "UploadError";
97
+ }
98
+ };
99
+ var ValidationError = class extends StorageBrainError {
100
+ constructor(message, errors) {
101
+ super(message, "VALIDATION_ERROR", 400, { errors });
102
+ this.errors = errors;
103
+ this.name = "ValidationError";
104
+ }
105
+ };
106
+ function parseApiError(statusCode, response) {
107
+ const { code, message, details } = response.error ?? {};
108
+ switch (code) {
109
+ case "UNAUTHORIZED":
110
+ return new AuthenticationError(message);
111
+ case "QUOTA_EXCEEDED":
112
+ return new QuotaExceededError(
113
+ message,
114
+ details?.quotaBytes,
115
+ details?.usedBytes
116
+ );
117
+ case "INVALID_FILE_TYPE":
118
+ return new InvalidFileTypeError(
119
+ details?.fileType,
120
+ details?.allowedTypes
121
+ );
122
+ case "FILE_TOO_LARGE":
123
+ return new FileTooLargeError(
124
+ details?.fileSize,
125
+ details?.maxSize
126
+ );
127
+ case "FILE_NOT_FOUND":
128
+ case "NOT_FOUND":
129
+ return new FileNotFoundError(details?.fileId ?? "unknown");
130
+ case "VALIDATION_ERROR":
131
+ return new ValidationError(
132
+ message ?? "Validation failed",
133
+ details?.errors
134
+ );
135
+ default:
136
+ return new StorageBrainError(
137
+ message ?? "An error occurred",
138
+ code ?? "UNKNOWN_ERROR",
139
+ statusCode,
140
+ details
141
+ );
142
+ }
143
+ }
144
+
145
+ // src/client.ts
146
+ var DEFAULT_BASE_URL = "https://storage-brain-api.marlin-pohl.workers.dev";
147
+ var DEFAULT_TIMEOUT = 3e4;
148
+ var DEFAULT_MAX_RETRIES = 3;
149
+ var StorageBrain = class {
150
+ apiKey;
151
+ baseUrl;
152
+ timeout;
153
+ maxRetries;
154
+ constructor(config) {
155
+ if (!config.apiKey) {
156
+ throw new StorageBrainError("API key is required", "CONFIGURATION_ERROR");
157
+ }
158
+ this.apiKey = config.apiKey;
159
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
160
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
161
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
162
+ }
163
+ /**
164
+ * Upload a file
165
+ */
166
+ async upload(file, options) {
167
+ const { context, tags, onProgress, webhookUrl, signal } = options;
168
+ const fileName = file instanceof File ? file.name : "file";
169
+ const fileType = file.type;
170
+ const fileSize = file.size;
171
+ if (!ALLOWED_MIME_TYPES.includes(fileType)) {
172
+ throw new InvalidFileTypeError(fileType, [...ALLOWED_MIME_TYPES]);
173
+ }
174
+ const handshake = await this.requestUpload({
175
+ fileType,
176
+ fileName,
177
+ fileSizeBytes: fileSize,
178
+ context,
179
+ tags,
180
+ webhookUrl
181
+ });
182
+ onProgress?.(10);
183
+ await this.uploadToPresignedUrl(handshake.presignedUrl, file, fileType, (progress) => {
184
+ onProgress?.(10 + Math.round(progress * 0.8));
185
+ }, signal);
186
+ onProgress?.(90);
187
+ const fileInfo = await this.waitForProcessing(handshake.fileId, signal);
188
+ onProgress?.(100);
189
+ return fileInfo;
190
+ }
191
+ /**
192
+ * Request an upload handshake
193
+ */
194
+ async requestUpload(params) {
195
+ return this.request("POST", "/api/v1/upload/request", params);
196
+ }
197
+ /**
198
+ * Upload file content to presigned URL
199
+ */
200
+ async uploadToPresignedUrl(presignedUrl, file, contentType, onProgress, signal) {
201
+ const uploadUrl = presignedUrl.startsWith("/") ? `${this.baseUrl}${presignedUrl}` : presignedUrl;
202
+ 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
+ });
235
+ } catch (error) {
236
+ if (error instanceof StorageBrainError) {
237
+ throw error;
238
+ }
239
+ throw new UploadError(
240
+ "Failed to upload file",
241
+ error instanceof Error ? error : void 0
242
+ );
243
+ }
244
+ }
245
+ /**
246
+ * Wait for file processing to complete
247
+ */
248
+ async waitForProcessing(fileId, signal, maxWaitMs = 6e4, pollIntervalMs = 1e3) {
249
+ const startTime = Date.now();
250
+ while (Date.now() - startTime < maxWaitMs) {
251
+ if (signal?.aborted) {
252
+ throw new UploadError("Operation was cancelled");
253
+ }
254
+ const file = await this.getFile(fileId);
255
+ if (file.processingStatus === "completed" || file.processingStatus === "failed") {
256
+ return file;
257
+ }
258
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
259
+ }
260
+ return this.getFile(fileId);
261
+ }
262
+ /**
263
+ * Get a file by ID
264
+ */
265
+ async getFile(fileId) {
266
+ return this.request("GET", `/api/v1/files/${fileId}`);
267
+ }
268
+ /**
269
+ * List files
270
+ */
271
+ async listFiles(options) {
272
+ const params = new URLSearchParams();
273
+ if (options?.limit) params.set("limit", options.limit.toString());
274
+ if (options?.cursor) params.set("cursor", options.cursor);
275
+ if (options?.context) params.set("context", options.context);
276
+ if (options?.fileType) params.set("fileType", options.fileType);
277
+ const query = params.toString();
278
+ const path = query ? `/api/v1/files?${query}` : "/api/v1/files";
279
+ return this.request("GET", path);
280
+ }
281
+ /**
282
+ * Delete a file
283
+ */
284
+ async deleteFile(fileId) {
285
+ await this.request("DELETE", `/api/v1/files/${fileId}`);
286
+ }
287
+ /**
288
+ * Get quota usage
289
+ */
290
+ async getQuota() {
291
+ return this.request("GET", "/api/v1/tenant/quota");
292
+ }
293
+ /**
294
+ * Get tenant information
295
+ */
296
+ async getTenantInfo() {
297
+ return this.request("GET", "/api/v1/tenant/info");
298
+ }
299
+ /**
300
+ * Make an authenticated API request with retry logic
301
+ */
302
+ async request(method, path, body) {
303
+ const url = `${this.baseUrl}${path}`;
304
+ let lastError;
305
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
306
+ try {
307
+ const controller = new AbortController();
308
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
309
+ const response = await fetch(url, {
310
+ method,
311
+ headers: {
312
+ "Content-Type": "application/json",
313
+ Authorization: `Bearer ${this.apiKey}`
314
+ },
315
+ body: body ? JSON.stringify(body) : void 0,
316
+ signal: controller.signal
317
+ });
318
+ clearTimeout(timeoutId);
319
+ if (!response.ok) {
320
+ const errorBody = await response.json().catch(() => ({}));
321
+ throw parseApiError(response.status, errorBody);
322
+ }
323
+ return await response.json();
324
+ } catch (error) {
325
+ lastError = error instanceof Error ? error : new Error(String(error));
326
+ if (error instanceof StorageBrainError && error.statusCode && error.statusCode < 500) {
327
+ throw error;
328
+ }
329
+ if (attempt < this.maxRetries - 1) {
330
+ const delay = Math.min(
331
+ RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt),
332
+ RETRY_CONFIG.maxDelayMs
333
+ );
334
+ await new Promise((resolve) => setTimeout(resolve, delay));
335
+ }
336
+ }
337
+ }
338
+ throw new NetworkError(
339
+ `Request failed after ${this.maxRetries} attempts`,
340
+ lastError
341
+ );
342
+ }
343
+ };
344
+
345
+ export { ALLOWED_FILE_TYPES, ALLOWED_MIME_TYPES, AuthenticationError, DOCUMENT_MIME_TYPES, FileNotFoundError, FileTooLargeError, IMAGE_MIME_TYPES, InvalidFileTypeError, MAX_FILE_SIZE_BYTES, NetworkError, PROCESSING_CONTEXTS, PROCESSING_STATUSES, QuotaExceededError, StorageBrain, StorageBrainError, THUMBNAIL_SIZES, UploadError, ValidationError, StorageBrain as default };
346
+ //# sourceMappingURL=index.js.map
347
+ //# sourceMappingURL=index.js.map