@stack0/sdk 0.5.7 → 0.5.9

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/cdn/index.js CHANGED
@@ -182,6 +182,27 @@ var CDN = class {
182
182
  }
183
183
  return this.confirmUpload(assetId);
184
184
  }
185
+ /**
186
+ * Upload a file from a remote URL (server-side fetch).
187
+ * The server streams the file directly from the source URL into storage,
188
+ * so the client never touches the bytes. Ideal for serverless environments
189
+ * with tight memory limits.
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const asset = await cdn.uploadFromUrl({
194
+ * projectSlug: 'my-project',
195
+ * sourceUrl: 'https://replicate.delivery/output/video.mp4',
196
+ * filename: 'video.mp4',
197
+ * mimeType: 'video/mp4',
198
+ * });
199
+ * console.log(asset.cdnUrl);
200
+ * ```
201
+ */
202
+ async uploadFromUrl(request) {
203
+ const response = await this.http.post("/cdn/upload-from-url", request);
204
+ return this.convertAssetDates(response);
205
+ }
185
206
  /**
186
207
  * Get an asset by ID
187
208
  *
@@ -558,6 +579,80 @@ var CDN = class {
558
579
  async extractAudio(request) {
559
580
  return this.http.post("/cdn/video/extract-audio", request);
560
581
  }
582
+ // ============================================================================
583
+ // GIF Generation Methods
584
+ // ============================================================================
585
+ /**
586
+ * Generate an animated GIF from a video segment
587
+ *
588
+ * Creates an optimized GIF from a portion of the video. Uses two-pass
589
+ * palette generation by default for smaller file sizes with better quality.
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * const gif = await cdn.generateGif({
594
+ * projectSlug: 'my-project',
595
+ * assetId: 'video-asset-id',
596
+ * startTime: 5, // Start at 5 seconds
597
+ * duration: 3, // 3 second GIF
598
+ * width: 480, // 480px wide
599
+ * fps: 10, // 10 frames per second
600
+ * });
601
+ *
602
+ * // Poll for completion
603
+ * let result = await cdn.getGif(gif.id);
604
+ * while (result?.status === 'pending' || result?.status === 'processing') {
605
+ * await new Promise(r => setTimeout(r, 1000));
606
+ * result = await cdn.getGif(gif.id);
607
+ * }
608
+ *
609
+ * console.log(`GIF URL: ${result?.url}`);
610
+ * ```
611
+ */
612
+ async generateGif(request) {
613
+ const response = await this.http.post("/cdn/video/gif", request);
614
+ return this.convertGifDates(response);
615
+ }
616
+ /**
617
+ * Get a specific GIF by ID
618
+ *
619
+ * @example
620
+ * ```typescript
621
+ * const gif = await cdn.getGif('gif-id');
622
+ * if (gif?.status === 'completed') {
623
+ * console.log(`GIF URL: ${gif.url}`);
624
+ * console.log(`Size: ${gif.sizeBytes} bytes`);
625
+ * }
626
+ * ```
627
+ */
628
+ async getGif(gifId) {
629
+ const response = await this.http.get(`/cdn/video/gif/${gifId}`);
630
+ return response ? this.convertGifDates(response) : null;
631
+ }
632
+ /**
633
+ * List all GIFs generated for a video asset
634
+ *
635
+ * @example
636
+ * ```typescript
637
+ * const gifs = await cdn.listGifs({ assetId: 'video-asset-id' });
638
+ * for (const gif of gifs) {
639
+ * console.log(`GIF at ${gif.startTime}s: ${gif.url}`);
640
+ * }
641
+ * ```
642
+ */
643
+ async listGifs(request) {
644
+ const response = await this.http.get(`/cdn/video/${request.assetId}/gifs`);
645
+ return response.map((gif) => this.convertGifDates(gif));
646
+ }
647
+ convertGifDates(gif) {
648
+ if (typeof gif.createdAt === "string") {
649
+ gif.createdAt = new Date(gif.createdAt);
650
+ }
651
+ if (gif.completedAt && typeof gif.completedAt === "string") {
652
+ gif.completedAt = new Date(gif.completedAt);
653
+ }
654
+ return gif;
655
+ }
561
656
  convertJobDates(job) {
562
657
  if (typeof job.createdAt === "string") {
563
658
  job.createdAt = new Date(job.createdAt);
@@ -1160,6 +1255,204 @@ var CDN = class {
1160
1255
  convertMergeJobWithOutputDates(job) {
1161
1256
  return this.convertMergeJobDates(job);
1162
1257
  }
1258
+ // ============================================================================
1259
+ // S3 Import Methods
1260
+ // ============================================================================
1261
+ /**
1262
+ * Create an S3 import job to bulk import files from an external S3 bucket.
1263
+ * Requires Pro plan or higher.
1264
+ *
1265
+ * @example
1266
+ * ```typescript
1267
+ * // Using IAM credentials
1268
+ * const job = await cdn.createImport({
1269
+ * projectSlug: 'my-project',
1270
+ * sourceBucket: 'my-external-bucket',
1271
+ * sourceRegion: 'us-east-1',
1272
+ * sourcePrefix: 'images/',
1273
+ * authType: 'iam_credentials',
1274
+ * accessKeyId: 'AKIA...',
1275
+ * secretAccessKey: 'secret...',
1276
+ * pathMode: 'flatten',
1277
+ * notifyEmail: 'admin@example.com',
1278
+ * });
1279
+ *
1280
+ * // Using role assumption
1281
+ * const job = await cdn.createImport({
1282
+ * projectSlug: 'my-project',
1283
+ * sourceBucket: 'partner-bucket',
1284
+ * sourceRegion: 'eu-west-1',
1285
+ * authType: 'role_assumption',
1286
+ * roleArn: 'arn:aws:iam::123456789012:role/Stack0ImportRole',
1287
+ * externalId: 'optional-external-id',
1288
+ * pathMode: 'preserve',
1289
+ * targetFolder: '/imported',
1290
+ * });
1291
+ *
1292
+ * console.log(`Import job started: ${job.importId}`);
1293
+ * ```
1294
+ */
1295
+ async createImport(request) {
1296
+ const response = await this.http.post("/cdn/imports", request);
1297
+ return this.convertCreateImportDates(response);
1298
+ }
1299
+ /**
1300
+ * Get an import job by ID
1301
+ *
1302
+ * @example
1303
+ * ```typescript
1304
+ * const job = await cdn.getImport('import-id');
1305
+ * console.log(`Status: ${job.status}`);
1306
+ * console.log(`Progress: ${job.processedFiles}/${job.totalFiles} files`);
1307
+ * console.log(`Bytes: ${job.processedBytes}/${job.totalBytes}`);
1308
+ * ```
1309
+ */
1310
+ async getImport(importId) {
1311
+ const response = await this.http.get(`/cdn/imports/${importId}`);
1312
+ return response ? this.convertImportJobDates(response) : null;
1313
+ }
1314
+ /**
1315
+ * List import jobs with pagination and filters
1316
+ *
1317
+ * @example
1318
+ * ```typescript
1319
+ * const { imports, total, hasMore } = await cdn.listImports({
1320
+ * projectSlug: 'my-project',
1321
+ * status: 'importing',
1322
+ * limit: 20,
1323
+ * });
1324
+ *
1325
+ * for (const job of imports) {
1326
+ * console.log(`${job.sourceBucket}: ${job.processedFiles}/${job.totalFiles}`);
1327
+ * }
1328
+ * ```
1329
+ */
1330
+ async listImports(request) {
1331
+ const params = new URLSearchParams();
1332
+ params.set("projectSlug", request.projectSlug);
1333
+ if (request.environment) params.set("environment", request.environment);
1334
+ if (request.status) params.set("status", request.status);
1335
+ if (request.sortBy) params.set("sortBy", request.sortBy);
1336
+ if (request.sortOrder) params.set("sortOrder", request.sortOrder);
1337
+ if (request.limit) params.set("limit", request.limit.toString());
1338
+ if (request.offset) params.set("offset", request.offset.toString());
1339
+ const response = await this.http.get(`/cdn/imports?${params.toString()}`);
1340
+ return {
1341
+ ...response,
1342
+ imports: response.imports.map((job) => this.convertImportJobSummaryDates(job))
1343
+ };
1344
+ }
1345
+ /**
1346
+ * Cancel a running import job
1347
+ *
1348
+ * Files already imported will remain. Only pending/validating/importing jobs can be cancelled.
1349
+ *
1350
+ * @example
1351
+ * ```typescript
1352
+ * const result = await cdn.cancelImport('import-id');
1353
+ * console.log(`Cancelled: ${result.success}`);
1354
+ * ```
1355
+ */
1356
+ async cancelImport(importId) {
1357
+ return this.http.post(`/cdn/imports/${importId}/cancel`, {});
1358
+ }
1359
+ /**
1360
+ * Retry failed files in a completed or failed import job
1361
+ *
1362
+ * This resets failed files to pending and re-queues the import for processing.
1363
+ *
1364
+ * @example
1365
+ * ```typescript
1366
+ * const result = await cdn.retryImport('import-id');
1367
+ * console.log(`Retrying ${result.retriedCount} files`);
1368
+ * ```
1369
+ */
1370
+ async retryImport(importId) {
1371
+ return this.http.post(`/cdn/imports/${importId}/retry`, {});
1372
+ }
1373
+ /**
1374
+ * List files in an import job with optional status filter
1375
+ *
1376
+ * @example
1377
+ * ```typescript
1378
+ * // List all files
1379
+ * const { files, total } = await cdn.listImportFiles({
1380
+ * importId: 'import-id',
1381
+ * limit: 100,
1382
+ * });
1383
+ *
1384
+ * // List only failed files
1385
+ * const { files: failedFiles } = await cdn.listImportFiles({
1386
+ * importId: 'import-id',
1387
+ * status: 'failed',
1388
+ * });
1389
+ *
1390
+ * for (const file of failedFiles) {
1391
+ * console.log(`${file.sourceKey}: ${file.errorMessage}`);
1392
+ * }
1393
+ * ```
1394
+ */
1395
+ async listImportFiles(request) {
1396
+ const params = new URLSearchParams();
1397
+ if (request.status) params.set("status", request.status);
1398
+ if (request.sortBy) params.set("sortBy", request.sortBy);
1399
+ if (request.sortOrder) params.set("sortOrder", request.sortOrder);
1400
+ if (request.limit) params.set("limit", request.limit.toString());
1401
+ if (request.offset) params.set("offset", request.offset.toString());
1402
+ const query = params.toString();
1403
+ const response = await this.http.get(
1404
+ `/cdn/imports/${request.importId}/files${query ? `?${query}` : ""}`
1405
+ );
1406
+ return {
1407
+ ...response,
1408
+ files: response.files.map((file) => this.convertImportFileDates(file))
1409
+ };
1410
+ }
1411
+ convertCreateImportDates(response) {
1412
+ if (typeof response.createdAt === "string") {
1413
+ response.createdAt = new Date(response.createdAt);
1414
+ }
1415
+ return response;
1416
+ }
1417
+ convertImportJobDates(job) {
1418
+ if (typeof job.createdAt === "string") {
1419
+ job.createdAt = new Date(job.createdAt);
1420
+ }
1421
+ if (job.updatedAt && typeof job.updatedAt === "string") {
1422
+ job.updatedAt = new Date(job.updatedAt);
1423
+ }
1424
+ if (job.startedAt && typeof job.startedAt === "string") {
1425
+ job.startedAt = new Date(job.startedAt);
1426
+ }
1427
+ if (job.completedAt && typeof job.completedAt === "string") {
1428
+ job.completedAt = new Date(job.completedAt);
1429
+ }
1430
+ return job;
1431
+ }
1432
+ convertImportJobSummaryDates(job) {
1433
+ if (typeof job.createdAt === "string") {
1434
+ job.createdAt = new Date(job.createdAt);
1435
+ }
1436
+ if (job.startedAt && typeof job.startedAt === "string") {
1437
+ job.startedAt = new Date(job.startedAt);
1438
+ }
1439
+ if (job.completedAt && typeof job.completedAt === "string") {
1440
+ job.completedAt = new Date(job.completedAt);
1441
+ }
1442
+ return job;
1443
+ }
1444
+ convertImportFileDates(file) {
1445
+ if (typeof file.createdAt === "string") {
1446
+ file.createdAt = new Date(file.createdAt);
1447
+ }
1448
+ if (file.completedAt && typeof file.completedAt === "string") {
1449
+ file.completedAt = new Date(file.completedAt);
1450
+ }
1451
+ if (file.lastAttemptAt && typeof file.lastAttemptAt === "string") {
1452
+ file.lastAttemptAt = new Date(file.lastAttemptAt);
1453
+ }
1454
+ return file;
1455
+ }
1163
1456
  };
1164
1457
 
1165
1458
  exports.CDN = CDN;