@stack0/sdk 0.5.16 → 0.5.18

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.mjs CHANGED
@@ -1430,7 +1430,12 @@ var CDN = class {
1430
1430
  return this.http.get(`/cdn/video/${assetId}/stream`);
1431
1431
  }
1432
1432
  /**
1433
- * Generate a thumbnail from a video at a specific timestamp
1433
+ * Get the thumbnail at a specific timestamp, generating it on miss
1434
+ *
1435
+ * If no thumbnail exists at that timestamp yet, generation is queued
1436
+ * automatically and a response with `status: "pending"` (null url) is
1437
+ * returned. Poll until `status` is "ready", or use
1438
+ * {@link getThumbnailAndWait} to do that for you.
1434
1439
  *
1435
1440
  * @example
1436
1441
  * ```typescript
@@ -1440,7 +1445,9 @@ var CDN = class {
1440
1445
  * width: 320,
1441
1446
  * format: 'webp',
1442
1447
  * });
1443
- * console.log(`Thumbnail URL: ${thumbnail.url}`);
1448
+ * if (thumbnail.status === 'ready') {
1449
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1450
+ * }
1444
1451
  * ```
1445
1452
  */
1446
1453
  async getThumbnail(request) {
@@ -1450,20 +1457,52 @@ var CDN = class {
1450
1457
  if (request.format) params.set("format", request.format);
1451
1458
  return this.http.get(`/cdn/video/thumbnail/${request.assetId}?${params.toString()}`);
1452
1459
  }
1460
+ /**
1461
+ * Get the thumbnail at a specific timestamp, waiting for generation to complete
1462
+ *
1463
+ * Calls {@link getThumbnail} and polls until the thumbnail is ready.
1464
+ *
1465
+ * @example
1466
+ * ```typescript
1467
+ * const thumbnail = await cdn.getThumbnailAndWait({
1468
+ * assetId: 'video-asset-id',
1469
+ * timestamp: 3.5,
1470
+ * });
1471
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1472
+ * ```
1473
+ */
1474
+ async getThumbnailAndWait(request, options = {}) {
1475
+ const { pollInterval = 1e3, timeout = 6e4 } = options;
1476
+ const startTime = Date.now();
1477
+ while (true) {
1478
+ const thumbnail = await this.getThumbnail(request);
1479
+ if (thumbnail.status === "ready") {
1480
+ return thumbnail;
1481
+ }
1482
+ if (Date.now() - startTime >= timeout) {
1483
+ throw new Error("Thumbnail generation timed out");
1484
+ }
1485
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1486
+ }
1487
+ }
1453
1488
  /**
1454
1489
  * Regenerate a thumbnail for a video (force regeneration even if one exists)
1455
1490
  *
1456
- * Useful for retrying failed thumbnail generation or regenerating with different settings.
1491
+ * Useful for retrying failed thumbnail generation or regenerating with
1492
+ * different settings. Regeneration is async: poll {@link getThumbnail} (or
1493
+ * use {@link getThumbnailAndWait}) at the same timestamp until the new
1494
+ * frame is ready.
1457
1495
  *
1458
1496
  * @example
1459
1497
  * ```typescript
1460
- * const result = await cdn.regenerateThumbnail({
1498
+ * await cdn.regenerateThumbnail({
1461
1499
  * assetId: 'video-asset-id',
1462
1500
  * timestamp: 5, // 5 seconds into the video
1463
1501
  * width: 1280,
1464
1502
  * format: 'jpg',
1465
1503
  * });
1466
- * console.log(`Thumbnail regeneration queued: ${result.status}`);
1504
+ * const thumbnail = await cdn.getThumbnailAndWait({ assetId: 'video-asset-id', timestamp: 5 });
1505
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1467
1506
  * ```
1468
1507
  */
1469
1508
  async regenerateThumbnail(request) {
@@ -1914,6 +1953,36 @@ var CDN = class {
1914
1953
  const query = params.toString();
1915
1954
  return this.http.get(`/cdn/usage/storage-breakdown${query ? `?${query}` : ""}`);
1916
1955
  }
1956
+ /**
1957
+ * Get total stored bytes for a project or folder, derived assets included.
1958
+ *
1959
+ * Totals uploads plus everything the pipelines derived from them — video renditions, HLS
1960
+ * segments and manifests, thumbnails, GIFs — in one call. Use this to meter a plan limit:
1961
+ * paging `list()` and summing `size` counts uploads only, and an HLS ladder typically runs
1962
+ * well past the size of the source it came from.
1963
+ *
1964
+ * Check `unmeasuredAssets`. A nonzero value means some assets have derivatives whose bytes
1965
+ * have not been measured, so `totalBytes` is a floor rather than the full number.
1966
+ *
1967
+ * @example
1968
+ * ```typescript
1969
+ * const usage = await cdn.getStorageUsage({
1970
+ * projectSlug: 'my-project',
1971
+ * folder: '/customers/acme',
1972
+ * });
1973
+ * console.log(`${usage.totalFormatted} across ${usage.objectCount} objects`);
1974
+ * console.log(` uploads: ${usage.breakdown.originals.bytesFormatted}`);
1975
+ * console.log(` derived: ${usage.breakdown.derived.bytesFormatted}`);
1976
+ * ```
1977
+ */
1978
+ async getStorageUsage(request = {}) {
1979
+ const params = new URLSearchParams();
1980
+ if (request.projectSlug) params.set("projectSlug", request.projectSlug);
1981
+ if (request.environment) params.set("environment", request.environment);
1982
+ if (request.folder) params.set("folder", request.folder);
1983
+ const query = params.toString();
1984
+ return this.http.get(`/cdn/usage/storage${query ? `?${query}` : ""}`);
1985
+ }
1917
1986
  convertUsageDates(usage) {
1918
1987
  if (typeof usage.periodStart === "string") {
1919
1988
  usage.periodStart = new Date(usage.periodStart);
@@ -2088,6 +2157,33 @@ var CDN = class {
2088
2157
  * });
2089
2158
  * console.log(`Merge job started: ${job.id}`);
2090
2159
  * ```
2160
+ *
2161
+ * @example Composite a screen recording onto a held phone ("POV holding my phone"):
2162
+ * ```typescript
2163
+ * const job = await cdn.createMergeJob({
2164
+ * projectSlug: 'my-project',
2165
+ * inputs: [
2166
+ * { assetId: 'reaction-clip-id' },
2167
+ * {
2168
+ * assetId: 'app-recording-id', // the overlay clip
2169
+ * composite: {
2170
+ * backgroundAssetId: 'holding-phone-shot-id', // image or video
2171
+ * // Corner-pin onto the angled screen (background pixel coords, TL/TR/BL/BR)
2172
+ * quad: {
2173
+ * topLeft: { x: 320, y: 480 },
2174
+ * topRight: { x: 760, y: 500 },
2175
+ * bottomLeft: { x: 300, y: 1400 },
2176
+ * bottomRight: { x: 740, y: 1440 },
2177
+ * },
2178
+ * fit: 'cover',
2179
+ * // Screen area painted #F500FA in the background is keyed out, so the
2180
+ * // recording shows through and fingers over the screen occlude it
2181
+ * chromaKey: { color: '#F500FA', similarity: 0.34, blend: 0.06 },
2182
+ * },
2183
+ * },
2184
+ * ],
2185
+ * });
2186
+ * ```
2091
2187
  */
2092
2188
  async createMergeJob(request) {
2093
2189
  const response = await this.http.post("/cdn/video/merge", request);