@bigbucket-ai/sdk 0.0.0-stage → 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.
package/dist/index.js ADDED
@@ -0,0 +1,515 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthenticationError: () => AuthenticationError,
24
+ BigBucket: () => BigBucket,
25
+ BigBucketError: () => BigBucketError,
26
+ HttpTransport: () => HttpTransport,
27
+ ImagesResource: () => ImagesResource,
28
+ NotFoundError: () => NotFoundError,
29
+ PayloadTooLargeError: () => PayloadTooLargeError,
30
+ RateLimitError: () => RateLimitError,
31
+ StorageUploadError: () => StorageUploadError,
32
+ TimeoutError: () => TimeoutError,
33
+ UnsupportedFormatError: () => UnsupportedFormatError,
34
+ UploadsResource: () => UploadsResource,
35
+ UsageResource: () => UsageResource
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/errors.ts
40
+ var BigBucketError = class extends Error {
41
+ statusCode;
42
+ code;
43
+ requestId;
44
+ details;
45
+ isRetryable;
46
+ constructor(options) {
47
+ super(options.message);
48
+ this.name = "BigBucketError";
49
+ this.statusCode = options.statusCode ?? 500;
50
+ this.code = options.code ?? "INTERNAL_SERVER_ERROR";
51
+ this.requestId = options.requestId;
52
+ this.details = options.details;
53
+ this.cause = options.cause;
54
+ this.isRetryable = this.statusCode === 429 || this.statusCode >= 500 && this.statusCode <= 599;
55
+ Object.setPrototypeOf(this, new.target.prototype);
56
+ }
57
+ };
58
+ var AuthenticationError = class extends BigBucketError {
59
+ constructor(options) {
60
+ super({ ...options, statusCode: options.statusCode ?? 401 });
61
+ this.name = "AuthenticationError";
62
+ }
63
+ };
64
+ var NotFoundError = class extends BigBucketError {
65
+ constructor(options) {
66
+ super({ ...options, statusCode: 404 });
67
+ this.name = "NotFoundError";
68
+ }
69
+ };
70
+ var PayloadTooLargeError = class extends BigBucketError {
71
+ constructor(options) {
72
+ super({ ...options, statusCode: 413 });
73
+ this.name = "PayloadTooLargeError";
74
+ }
75
+ };
76
+ var UnsupportedFormatError = class extends BigBucketError {
77
+ constructor(options) {
78
+ super({ ...options, statusCode: 415 });
79
+ this.name = "UnsupportedFormatError";
80
+ }
81
+ };
82
+ var RateLimitError = class extends BigBucketError {
83
+ constructor(options) {
84
+ super({ ...options, statusCode: 429 });
85
+ this.name = "RateLimitError";
86
+ }
87
+ };
88
+ var StorageUploadError = class extends BigBucketError {
89
+ constructor(message, cause) {
90
+ super({
91
+ message,
92
+ statusCode: 502,
93
+ code: "STORAGE_UPLOAD_FAILED",
94
+ cause
95
+ });
96
+ this.name = "StorageUploadError";
97
+ }
98
+ };
99
+ var TimeoutError = class extends BigBucketError {
100
+ constructor(message = "Request timed out") {
101
+ super({
102
+ message,
103
+ statusCode: 408,
104
+ code: "REQUEST_TIMEOUT"
105
+ });
106
+ this.name = "TimeoutError";
107
+ }
108
+ };
109
+
110
+ // src/http/transport.ts
111
+ var HttpTransport = class {
112
+ apiKey;
113
+ baseUrl;
114
+ timeout;
115
+ customFetch;
116
+ constructor(config) {
117
+ if (!config.apiKey || typeof config.apiKey !== "string") {
118
+ throw new Error("A valid BigBucket API key is required (e.g. bb_live_...)");
119
+ }
120
+ this.apiKey = config.apiKey.trim();
121
+ this.baseUrl = (config.baseUrl || "http://localhost:8000/api/v1").replace(/\/+$/, "");
122
+ this.timeout = config.timeout ?? 3e4;
123
+ this.customFetch = config.fetch ?? globalThis.fetch;
124
+ if (!this.customFetch) {
125
+ throw new Error("No native fetch implementation found. Pass a custom fetch in options.");
126
+ }
127
+ }
128
+ /**
129
+ * Execute an authenticated request against the BigBucket API.
130
+ */
131
+ async request(path, options = {}) {
132
+ const url = `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
133
+ const headers = {
134
+ Accept: "application/json",
135
+ "X-API-Key": this.apiKey,
136
+ ...options.headers
137
+ };
138
+ if (options.body && typeof options.body === "string" && !headers["Content-Type"]) {
139
+ headers["Content-Type"] = "application/json";
140
+ }
141
+ const controller = new AbortController();
142
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
143
+ let response;
144
+ try {
145
+ response = await this.customFetch(url, {
146
+ ...options,
147
+ headers,
148
+ signal: controller.signal
149
+ });
150
+ } catch (err) {
151
+ clearTimeout(timeoutId);
152
+ if (err instanceof Error && err.name === "AbortError") {
153
+ throw new TimeoutError(`Request timed out after ${this.timeout}ms`);
154
+ }
155
+ throw new BigBucketError({
156
+ message: `Network request failed: ${err instanceof Error ? err.message : String(err)}`,
157
+ statusCode: 0,
158
+ cause: err
159
+ });
160
+ } finally {
161
+ clearTimeout(timeoutId);
162
+ }
163
+ const requestId = response.headers.get("X-Request-ID") ?? void 0;
164
+ let responseBody = null;
165
+ const contentType = response.headers.get("content-type");
166
+ if (contentType && contentType.includes("application/json")) {
167
+ try {
168
+ responseBody = await response.json();
169
+ } catch {
170
+ }
171
+ }
172
+ if (!response.ok) {
173
+ const errPayload = responseBody?.error ?? {};
174
+ const message = errPayload.message || `Request failed with status ${response.status}`;
175
+ const code = errPayload.code || "REQUEST_FAILED";
176
+ const details = errPayload.details;
177
+ const errorOptions = {
178
+ message,
179
+ statusCode: response.status,
180
+ code,
181
+ requestId: errPayload.request_id || requestId,
182
+ details
183
+ };
184
+ if (response.status === 401) throw new AuthenticationError(errorOptions);
185
+ if (response.status === 404) throw new NotFoundError(errorOptions);
186
+ if (response.status === 413) throw new PayloadTooLargeError(errorOptions);
187
+ if (response.status === 415) throw new UnsupportedFormatError(errorOptions);
188
+ if (response.status === 429) throw new RateLimitError(errorOptions);
189
+ throw new BigBucketError(errorOptions);
190
+ }
191
+ return responseBody;
192
+ }
193
+ /**
194
+ * Upload binary data directly to the storage provider (Cloudflare R2 or Local Storage).
195
+ * Note: Permanent BigBucket credentials MUST NOT be forwarded to the storage provider.
196
+ */
197
+ async uploadToPresignedUrl(uploadUrl, method, signedHeaders, body) {
198
+ const controller = new AbortController();
199
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout * 2);
200
+ try {
201
+ const response = await this.customFetch(uploadUrl, {
202
+ method: method || "PUT",
203
+ headers: signedHeaders,
204
+ body,
205
+ signal: controller.signal
206
+ });
207
+ if (!response.ok) {
208
+ throw new StorageUploadError(
209
+ `Direct storage upload failed with status ${response.status} ${response.statusText}`
210
+ );
211
+ }
212
+ } catch (err) {
213
+ clearTimeout(timeoutId);
214
+ if (err instanceof BigBucketError) {
215
+ throw err;
216
+ }
217
+ if (err instanceof Error && err.name === "AbortError") {
218
+ throw new TimeoutError(`Direct storage upload timed out after ${this.timeout * 2}ms`);
219
+ }
220
+ throw new StorageUploadError(
221
+ `Failed to upload image bytes to storage provider: ${err instanceof Error ? err.message : String(err)}`,
222
+ err
223
+ );
224
+ } finally {
225
+ clearTimeout(timeoutId);
226
+ }
227
+ }
228
+ };
229
+
230
+ // src/resources/images.ts
231
+ var ImagesResource = class {
232
+ constructor(transport) {
233
+ this.transport = transport;
234
+ }
235
+ transport;
236
+ mapAsset(d) {
237
+ return {
238
+ id: d.id,
239
+ projectId: d.project_id,
240
+ url: d.url,
241
+ originalFilename: d.original_filename,
242
+ mimeType: d.mime_type,
243
+ fileSize: d.file_size,
244
+ width: d.width,
245
+ height: d.height,
246
+ format: d.format,
247
+ status: d.status,
248
+ createdAt: d.created_at,
249
+ updatedAt: d.updated_at,
250
+ variants: (d.variants || []).map((v) => ({
251
+ variantName: v.variant_name,
252
+ url: v.url,
253
+ mimeType: v.mime_type,
254
+ fileSize: v.file_size,
255
+ width: v.width,
256
+ height: v.height,
257
+ format: v.format
258
+ }))
259
+ };
260
+ }
261
+ /**
262
+ * Retrieve metadata and stable delivery URL for an image.
263
+ */
264
+ async get(imageId) {
265
+ const res = await this.transport.request(
266
+ `/images/${encodeURIComponent(imageId)}`,
267
+ {
268
+ method: "GET"
269
+ }
270
+ );
271
+ return this.mapAsset(res.data);
272
+ }
273
+ /**
274
+ * List and paginate media assets belonging to the authenticated project.
275
+ */
276
+ async list(options = {}) {
277
+ const params = new URLSearchParams();
278
+ if (options.page !== void 0) params.set("page", String(options.page));
279
+ if (options.pageSize !== void 0) params.set("page_size", String(options.pageSize));
280
+ if (options.status !== void 0) params.set("status", options.status);
281
+ if (options.format !== void 0) params.set("format", options.format);
282
+ const query = params.toString() ? `?${params.toString()}` : "";
283
+ const res = await this.transport.request(
284
+ `/images${query}`,
285
+ {
286
+ method: "GET"
287
+ }
288
+ );
289
+ return {
290
+ data: (res.data || []).map((item) => this.mapAsset(item)),
291
+ pagination: {
292
+ total: res.pagination.total,
293
+ page: res.pagination.page,
294
+ pageSize: res.pagination.page_size,
295
+ hasNext: res.pagination.has_next
296
+ }
297
+ };
298
+ }
299
+ /**
300
+ * Delete an image from storage and mark it deleted in BigBucket.
301
+ */
302
+ async delete(imageId) {
303
+ const res = await this.transport.request(
304
+ `/images/${encodeURIComponent(imageId)}`,
305
+ {
306
+ method: "DELETE"
307
+ }
308
+ );
309
+ return {
310
+ success: true,
311
+ id: res.data.id
312
+ };
313
+ }
314
+ };
315
+
316
+ // src/resources/uploads.ts
317
+ var UploadsResource = class {
318
+ constructor(transport) {
319
+ this.transport = transport;
320
+ }
321
+ transport;
322
+ /**
323
+ * Request permission and generate a presigned upload URL.
324
+ */
325
+ async create(options) {
326
+ const res = await this.transport.request("/uploads", {
327
+ method: "POST",
328
+ body: JSON.stringify({
329
+ filename: options.filename,
330
+ content_type: options.contentType,
331
+ file_size: options.fileSize
332
+ })
333
+ });
334
+ const d = res.data;
335
+ return {
336
+ uploadId: d.upload_id,
337
+ objectKey: d.object_key,
338
+ presigned: {
339
+ url: d.presigned.url,
340
+ method: d.presigned.method,
341
+ headers: d.presigned.headers
342
+ },
343
+ expiresAt: d.expires_at
344
+ };
345
+ }
346
+ /**
347
+ * Finalize an upload session once the client has uploaded bytes to the presigned URL.
348
+ */
349
+ async complete(uploadId) {
350
+ const res = await this.transport.request(
351
+ `/uploads/${encodeURIComponent(uploadId)}/complete`,
352
+ {
353
+ method: "POST"
354
+ }
355
+ );
356
+ const d = res.data;
357
+ return {
358
+ id: d.id,
359
+ projectId: d.project_id,
360
+ url: d.url,
361
+ originalFilename: d.original_filename,
362
+ mimeType: d.mime_type,
363
+ fileSize: d.file_size,
364
+ width: d.width,
365
+ height: d.height,
366
+ format: d.format,
367
+ status: d.status,
368
+ createdAt: d.created_at,
369
+ updatedAt: d.updated_at,
370
+ variants: (d.variants || []).map((v) => ({
371
+ variantName: v.variant_name,
372
+ url: v.url,
373
+ mimeType: v.mime_type,
374
+ fileSize: v.file_size,
375
+ width: v.width,
376
+ height: v.height,
377
+ format: v.format
378
+ }))
379
+ };
380
+ }
381
+ /**
382
+ * Check the status of an ongoing or completed upload session.
383
+ */
384
+ async get(uploadId) {
385
+ const res = await this.transport.request(
386
+ `/uploads/${encodeURIComponent(uploadId)}`,
387
+ {
388
+ method: "GET"
389
+ }
390
+ );
391
+ const d = res.data;
392
+ return {
393
+ id: d.id,
394
+ projectId: d.project_id,
395
+ objectKey: d.object_key,
396
+ expectedFileSize: d.expected_file_size,
397
+ expectedMimeType: d.expected_mime_type,
398
+ status: d.status,
399
+ expiresAt: d.expires_at,
400
+ createdAt: d.created_at,
401
+ completedAt: d.completed_at ?? null,
402
+ assetId: d.asset_id ?? null
403
+ };
404
+ }
405
+ };
406
+
407
+ // src/resources/usage.ts
408
+ var UsageResource = class {
409
+ constructor(transport) {
410
+ this.transport = transport;
411
+ }
412
+ transport;
413
+ /**
414
+ * Retrieve usage metrics for a project or the account.
415
+ */
416
+ async get(projectId) {
417
+ const path = projectId ? `/projects/${encodeURIComponent(projectId)}/usage` : "/usage";
418
+ const res = await this.transport.request(path, {
419
+ method: "GET"
420
+ });
421
+ const d = res.data;
422
+ return {
423
+ totalStoredBytes: d.total_stored_bytes,
424
+ totalReadyImages: d.total_ready_images,
425
+ successfulUploads: d.successful_uploads,
426
+ deletedImages: d.deleted_images
427
+ };
428
+ }
429
+ };
430
+
431
+ // src/client.ts
432
+ var BigBucket = class {
433
+ transport;
434
+ images;
435
+ uploads;
436
+ usage;
437
+ constructor(options) {
438
+ this.transport = new HttpTransport({
439
+ apiKey: options.apiKey,
440
+ baseUrl: options.baseUrl,
441
+ timeout: options.timeout,
442
+ fetch: options.fetch
443
+ });
444
+ this.images = new ImagesResource(this.transport);
445
+ this.uploads = new UploadsResource(this.transport);
446
+ this.usage = new UsageResource(this.transport);
447
+ }
448
+ /**
449
+ * Helper method to normalize arbitrary input types (Buffer, Uint8Array, Blob, etc.)
450
+ * and resolve byte length.
451
+ */
452
+ resolveFileData(file, explicitSize) {
453
+ if (typeof Blob !== "undefined" && file instanceof Blob) {
454
+ return { body: file, size: explicitSize ?? file.size };
455
+ }
456
+ if (file instanceof ArrayBuffer) {
457
+ return { body: file, size: explicitSize ?? file.byteLength };
458
+ }
459
+ if (file instanceof Uint8Array) {
460
+ return { body: file, size: explicitSize ?? file.byteLength };
461
+ }
462
+ if (typeof file === "object" && file !== null && "buffer" in file && file.buffer instanceof ArrayBuffer) {
463
+ const buffer = file.buffer;
464
+ return { body: buffer, size: explicitSize ?? buffer.byteLength };
465
+ }
466
+ if (typeof file === "string") {
467
+ const encoded = new TextEncoder().encode(file);
468
+ return { body: encoded, size: explicitSize ?? encoded.byteLength };
469
+ }
470
+ if (explicitSize !== void 0) {
471
+ return { body: file, size: explicitSize };
472
+ }
473
+ throw new Error(
474
+ "Unable to determine file size. Please provide 'options.fileSize' explicitly."
475
+ );
476
+ }
477
+ /**
478
+ * Complete end-to-end image upload workflow:
479
+ * 1. Requests an upload session and receives presigned storage URL.
480
+ * 2. Uploads binary data directly to the storage provider (Cloudflare R2).
481
+ * 3. Finalizes the upload with server verification and content inspection.
482
+ * 4. Returns verified ImageAsset with delivery URL.
483
+ */
484
+ async upload(file, options) {
485
+ const { body, size } = this.resolveFileData(file, options.fileSize);
486
+ const session = await this.uploads.create({
487
+ filename: options.filename,
488
+ contentType: options.contentType,
489
+ fileSize: size
490
+ });
491
+ await this.transport.uploadToPresignedUrl(
492
+ session.presigned.url,
493
+ session.presigned.method,
494
+ session.presigned.headers,
495
+ body
496
+ );
497
+ return await this.uploads.complete(session.uploadId);
498
+ }
499
+ };
500
+ // Annotate the CommonJS export names for ESM import in node:
501
+ 0 && (module.exports = {
502
+ AuthenticationError,
503
+ BigBucket,
504
+ BigBucketError,
505
+ HttpTransport,
506
+ ImagesResource,
507
+ NotFoundError,
508
+ PayloadTooLargeError,
509
+ RateLimitError,
510
+ StorageUploadError,
511
+ TimeoutError,
512
+ UnsupportedFormatError,
513
+ UploadsResource,
514
+ UsageResource
515
+ });