@stack0/sdk 0.3.0 → 0.3.4

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.
Files changed (40) hide show
  1. package/README.md +33 -10
  2. package/dist/cdn/index.d.mts +56 -26
  3. package/dist/cdn/index.d.ts +56 -26
  4. package/dist/cdn/index.js +84 -23
  5. package/dist/cdn/index.js.map +1 -1
  6. package/dist/cdn/index.mjs +84 -23
  7. package/dist/cdn/index.mjs.map +1 -1
  8. package/dist/extraction/index.d.mts +1 -1
  9. package/dist/extraction/index.d.ts +1 -1
  10. package/dist/extraction/index.js +33 -29
  11. package/dist/extraction/index.js.map +1 -1
  12. package/dist/extraction/index.mjs +33 -29
  13. package/dist/extraction/index.mjs.map +1 -1
  14. package/dist/{http-client-DjrRWvXA.d.mts → http-client-Cgie_Rv6.d.mts} +1 -0
  15. package/dist/{http-client-DjrRWvXA.d.ts → http-client-Cgie_Rv6.d.ts} +1 -0
  16. package/dist/index.d.mts +475 -3
  17. package/dist/index.d.ts +475 -3
  18. package/dist/index.js +512 -72
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +512 -73
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/mail/index.d.mts +1 -1
  23. package/dist/mail/index.d.ts +1 -1
  24. package/dist/mail/index.js +13 -5
  25. package/dist/mail/index.js.map +1 -1
  26. package/dist/mail/index.mjs +13 -5
  27. package/dist/mail/index.mjs.map +1 -1
  28. package/dist/screenshots/index.d.mts +1 -1
  29. package/dist/screenshots/index.d.ts +1 -1
  30. package/dist/screenshots/index.js +32 -26
  31. package/dist/screenshots/index.js.map +1 -1
  32. package/dist/screenshots/index.mjs +32 -26
  33. package/dist/screenshots/index.mjs.map +1 -1
  34. package/dist/webdata/index.d.mts +1 -1
  35. package/dist/webdata/index.d.ts +1 -1
  36. package/dist/webdata/index.js +37 -8
  37. package/dist/webdata/index.js.map +1 -1
  38. package/dist/webdata/index.mjs +37 -8
  39. package/dist/webdata/index.mjs.map +1 -1
  40. package/package.json +1 -2
package/dist/index.js CHANGED
@@ -10,19 +10,24 @@ var HttpClient = class {
10
10
  this.apiKey = config.apiKey;
11
11
  this.baseUrl = config.baseUrl || "https://api.stack0.dev/v1";
12
12
  }
13
- getHeaders() {
14
- return {
15
- "Content-Type": "application/json",
13
+ getHeaders(includeContentType = true) {
14
+ const headers = {
16
15
  Authorization: `Bearer ${this.apiKey}`
17
16
  };
17
+ if (includeContentType) {
18
+ headers["Content-Type"] = "application/json";
19
+ }
20
+ return headers;
18
21
  }
19
22
  async request(method, path, body) {
20
23
  const url = `${this.baseUrl}${path}`;
24
+ const bodyString = body === void 0 ? void 0 : JSON.stringify(body);
25
+ const hasBody = bodyString !== void 0;
21
26
  try {
22
27
  const response = await fetch(url, {
23
28
  method,
24
- headers: this.getHeaders(),
25
- body: body ? JSON.stringify(body) : void 0
29
+ headers: this.getHeaders(hasBody),
30
+ body: hasBody ? bodyString : void 0
26
31
  });
27
32
  if (!response.ok) {
28
33
  await this.handleErrorResponse(response);
@@ -66,6 +71,9 @@ var HttpClient = class {
66
71
  async delete(path) {
67
72
  return this.request("DELETE", path);
68
73
  }
74
+ async deleteWithBody(path, body) {
75
+ return this.request("DELETE", path, body);
76
+ }
69
77
  async patch(path, body) {
70
78
  return this.request("PATCH", path, body);
71
79
  }
@@ -119,10 +127,13 @@ var Mail = class {
119
127
  };
120
128
 
121
129
  // src/cdn/client.ts
130
+ var ALLOWED_WIDTHS = [256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840];
122
131
  var CDN = class {
123
132
  http;
124
- constructor(config) {
133
+ cdnUrl;
134
+ constructor(config, cdnUrl) {
125
135
  this.http = new HttpClient(config);
136
+ this.cdnUrl = cdnUrl;
126
137
  }
127
138
  /**
128
139
  * Generate a presigned URL for uploading a file
@@ -243,7 +254,7 @@ var CDN = class {
243
254
  * ```
244
255
  */
245
256
  async delete(id) {
246
- return this.http.delete(`/cdn/assets/${id}`);
257
+ return this.http.deleteWithBody(`/cdn/assets/${id}`, { id });
247
258
  }
248
259
  /**
249
260
  * Delete multiple assets
@@ -302,24 +313,73 @@ var CDN = class {
302
313
  return this.http.post("/cdn/assets/move", request);
303
314
  }
304
315
  /**
305
- * Get a transformed image URL
316
+ * Get a transformed image URL (client-side, no API call)
306
317
  *
307
318
  * @example
308
319
  * ```typescript
309
- * const { url } = await cdn.getTransformUrl({
310
- * assetId: 'asset-id',
311
- * options: {
312
- * width: 800,
313
- * height: 600,
314
- * fit: 'cover',
315
- * format: 'webp',
316
- * quality: 80,
317
- * },
320
+ * // Using asset's cdnUrl directly
321
+ * const url = cdn.getTransformUrl(asset.cdnUrl, {
322
+ * width: 800,
323
+ * height: 600,
324
+ * fit: 'cover',
325
+ * format: 'webp',
326
+ * quality: 80,
318
327
  * });
328
+ *
329
+ * // Or using cdnUrl from SDK config + s3Key
330
+ * const url = cdn.getTransformUrl(asset.s3Key, { width: 400 });
319
331
  * ```
320
332
  */
321
- async getTransformUrl(request) {
322
- return this.http.post("/cdn/transform", request);
333
+ getTransformUrl(assetUrlOrS3Key, options) {
334
+ let baseUrl;
335
+ if (assetUrlOrS3Key.startsWith("http://") || assetUrlOrS3Key.startsWith("https://")) {
336
+ const url = new URL(assetUrlOrS3Key);
337
+ baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
338
+ } else if (this.cdnUrl) {
339
+ const cdnBase = this.cdnUrl.endsWith("/") ? this.cdnUrl.slice(0, -1) : this.cdnUrl;
340
+ baseUrl = `${cdnBase}/${assetUrlOrS3Key}`;
341
+ } else {
342
+ throw new Error("getTransformUrl requires either a full URL or cdnUrl to be configured in Stack0 options");
343
+ }
344
+ const params = this.buildTransformQuery(options);
345
+ if (!params) {
346
+ return baseUrl;
347
+ }
348
+ return `${baseUrl}?${params}`;
349
+ }
350
+ /**
351
+ * Build transform query parameters
352
+ */
353
+ buildTransformQuery(options) {
354
+ const params = new URLSearchParams();
355
+ if (options.format) params.set("f", options.format);
356
+ if (options.quality !== void 0) params.set("q", options.quality.toString());
357
+ if (options.width !== void 0) {
358
+ const width = this.getNearestWidth(options.width);
359
+ params.set("w", width.toString());
360
+ }
361
+ if (options.height !== void 0) params.set("h", options.height.toString());
362
+ if (options.fit) params.set("fit", options.fit);
363
+ if (options.crop) params.set("crop", options.crop);
364
+ if (options.cropX !== void 0) params.set("crop-x", options.cropX.toString());
365
+ if (options.cropY !== void 0) params.set("crop-y", options.cropY.toString());
366
+ if (options.cropWidth !== void 0) params.set("crop-w", options.cropWidth.toString());
367
+ if (options.cropHeight !== void 0) params.set("crop-h", options.cropHeight.toString());
368
+ if (options.blur !== void 0) params.set("blur", options.blur.toString());
369
+ if (options.sharpen !== void 0) params.set("sharpen", options.sharpen.toString());
370
+ if (options.brightness !== void 0) params.set("brightness", options.brightness.toString());
371
+ if (options.saturation !== void 0) params.set("saturation", options.saturation.toString());
372
+ if (options.grayscale) params.set("grayscale", "true");
373
+ if (options.rotate !== void 0) params.set("rotate", options.rotate.toString());
374
+ if (options.flip) params.set("flip", "y");
375
+ if (options.flop) params.set("flop", "x");
376
+ return params.toString();
377
+ }
378
+ /**
379
+ * Find the nearest allowed width for optimal caching
380
+ */
381
+ getNearestWidth(width) {
382
+ return ALLOWED_WIDTHS.reduce((prev, curr) => Math.abs(curr - width) < Math.abs(prev - width) ? curr : prev);
323
383
  }
324
384
  /**
325
385
  * Get folder tree for navigation
@@ -365,7 +425,10 @@ var CDN = class {
365
425
  async deleteFolder(id, deleteContents = false) {
366
426
  const params = new URLSearchParams();
367
427
  if (deleteContents) params.set("deleteContents", "true");
368
- return this.http.delete(`/cdn/folders/${id}?${params.toString()}`);
428
+ return this.http.deleteWithBody(`/cdn/folders/${id}?${params.toString()}`, {
429
+ id,
430
+ deleteContents
431
+ });
369
432
  }
370
433
  convertAssetDates(asset) {
371
434
  if (typeof asset.createdAt === "string") {
@@ -491,9 +554,7 @@ var CDN = class {
491
554
  params.set("timestamp", request.timestamp.toString());
492
555
  if (request.width) params.set("width", request.width.toString());
493
556
  if (request.format) params.set("format", request.format);
494
- return this.http.get(
495
- `/cdn/video/thumbnail/${request.assetId}?${params.toString()}`
496
- );
557
+ return this.http.get(`/cdn/video/thumbnail/${request.assetId}?${params.toString()}`);
497
558
  }
498
559
  /**
499
560
  * Extract audio from a video file
@@ -594,9 +655,7 @@ var Screenshots = class {
594
655
  if (request.limit) params.set("limit", request.limit.toString());
595
656
  if (request.cursor) params.set("cursor", request.cursor);
596
657
  const query = params.toString();
597
- const response = await this.http.get(
598
- `/webdata/screenshots${query ? `?${query}` : ""}`
599
- );
658
+ const response = await this.http.get(`/webdata/screenshots${query ? `?${query}` : ""}`);
600
659
  return {
601
660
  ...response,
602
661
  items: response.items.map((item) => this.convertDates(item))
@@ -615,8 +674,13 @@ var Screenshots = class {
615
674
  if (request.environment) params.set("environment", request.environment);
616
675
  if (request.projectId) params.set("projectId", request.projectId);
617
676
  const query = params.toString();
618
- return this.http.delete(
619
- `/webdata/screenshots/${request.id}${query ? `?${query}` : ""}`
677
+ return this.http.deleteWithBody(
678
+ `/webdata/screenshots/${request.id}${query ? `?${query}` : ""}`,
679
+ {
680
+ id: request.id,
681
+ environment: request.environment,
682
+ projectId: request.projectId
683
+ }
620
684
  );
621
685
  }
622
686
  /**
@@ -699,9 +763,7 @@ var Screenshots = class {
699
763
  if (request.limit) params.set("limit", request.limit.toString());
700
764
  if (request.cursor) params.set("cursor", request.cursor);
701
765
  const query = params.toString();
702
- const response = await this.http.get(
703
- `/webdata/batch${query ? `?${query}` : ""}`
704
- );
766
+ const response = await this.http.get(`/webdata/batch${query ? `?${query}` : ""}`);
705
767
  return {
706
768
  ...response,
707
769
  items: response.items.map((item) => this.convertBatchJobDates(item))
@@ -715,10 +777,7 @@ var Screenshots = class {
715
777
  if (request.environment) params.set("environment", request.environment);
716
778
  if (request.projectId) params.set("projectId", request.projectId);
717
779
  const query = params.toString();
718
- return this.http.post(
719
- `/webdata/batch/${request.id}/cancel${query ? `?${query}` : ""}`,
720
- {}
721
- );
780
+ return this.http.post(`/webdata/batch/${request.id}/cancel${query ? `?${query}` : ""}`, {});
722
781
  }
723
782
  /**
724
783
  * Create a batch screenshot job and wait for completion
@@ -771,10 +830,7 @@ var Screenshots = class {
771
830
  if (environment) params.set("environment", environment);
772
831
  if (projectId) params.set("projectId", projectId);
773
832
  const query = params.toString();
774
- return this.http.post(
775
- `/webdata/schedules/${id}${query ? `?${query}` : ""}`,
776
- data
777
- );
833
+ return this.http.post(`/webdata/schedules/${id}${query ? `?${query}` : ""}`, data);
778
834
  }
779
835
  /**
780
836
  * Get a schedule by ID
@@ -800,9 +856,7 @@ var Screenshots = class {
800
856
  if (request.limit) params.set("limit", request.limit.toString());
801
857
  if (request.cursor) params.set("cursor", request.cursor);
802
858
  const query = params.toString();
803
- const response = await this.http.get(
804
- `/webdata/schedules${query ? `?${query}` : ""}`
805
- );
859
+ const response = await this.http.get(`/webdata/schedules${query ? `?${query}` : ""}`);
806
860
  return {
807
861
  ...response,
808
862
  items: response.items.map((item) => this.convertScheduleDates(item))
@@ -816,8 +870,13 @@ var Screenshots = class {
816
870
  if (request.environment) params.set("environment", request.environment);
817
871
  if (request.projectId) params.set("projectId", request.projectId);
818
872
  const query = params.toString();
819
- return this.http.delete(
820
- `/webdata/schedules/${request.id}${query ? `?${query}` : ""}`
873
+ return this.http.deleteWithBody(
874
+ `/webdata/schedules/${request.id}${query ? `?${query}` : ""}`,
875
+ {
876
+ id: request.id,
877
+ environment: request.environment,
878
+ projectId: request.projectId
879
+ }
821
880
  );
822
881
  }
823
882
  /**
@@ -943,9 +1002,7 @@ var Extraction = class {
943
1002
  if (request.limit) params.set("limit", request.limit.toString());
944
1003
  if (request.cursor) params.set("cursor", request.cursor);
945
1004
  const query = params.toString();
946
- const response = await this.http.get(
947
- `/webdata/extractions${query ? `?${query}` : ""}`
948
- );
1005
+ const response = await this.http.get(`/webdata/extractions${query ? `?${query}` : ""}`);
949
1006
  return {
950
1007
  ...response,
951
1008
  items: response.items.map((item) => this.convertDates(item))
@@ -964,8 +1021,13 @@ var Extraction = class {
964
1021
  if (request.environment) params.set("environment", request.environment);
965
1022
  if (request.projectId) params.set("projectId", request.projectId);
966
1023
  const query = params.toString();
967
- return this.http.delete(
968
- `/webdata/extractions/${request.id}${query ? `?${query}` : ""}`
1024
+ return this.http.deleteWithBody(
1025
+ `/webdata/extractions/${request.id}${query ? `?${query}` : ""}`,
1026
+ {
1027
+ id: request.id,
1028
+ environment: request.environment,
1029
+ projectId: request.projectId
1030
+ }
969
1031
  );
970
1032
  }
971
1033
  /**
@@ -1044,9 +1106,7 @@ var Extraction = class {
1044
1106
  if (request.limit) params.set("limit", request.limit.toString());
1045
1107
  if (request.cursor) params.set("cursor", request.cursor);
1046
1108
  const query = params.toString();
1047
- const response = await this.http.get(
1048
- `/webdata/batch${query ? `?${query}` : ""}`
1049
- );
1109
+ const response = await this.http.get(`/webdata/batch${query ? `?${query}` : ""}`);
1050
1110
  return {
1051
1111
  ...response,
1052
1112
  items: response.items.map((item) => this.convertBatchJobDates(item))
@@ -1060,10 +1120,7 @@ var Extraction = class {
1060
1120
  if (request.environment) params.set("environment", request.environment);
1061
1121
  if (request.projectId) params.set("projectId", request.projectId);
1062
1122
  const query = params.toString();
1063
- return this.http.post(
1064
- `/webdata/batch/${request.id}/cancel${query ? `?${query}` : ""}`,
1065
- {}
1066
- );
1123
+ return this.http.post(`/webdata/batch/${request.id}/cancel${query ? `?${query}` : ""}`, {});
1067
1124
  }
1068
1125
  /**
1069
1126
  * Create a batch extraction job and wait for completion
@@ -1116,10 +1173,7 @@ var Extraction = class {
1116
1173
  if (environment) params.set("environment", environment);
1117
1174
  if (projectId) params.set("projectId", projectId);
1118
1175
  const query = params.toString();
1119
- return this.http.post(
1120
- `/webdata/schedules/${id}${query ? `?${query}` : ""}`,
1121
- data
1122
- );
1176
+ return this.http.post(`/webdata/schedules/${id}${query ? `?${query}` : ""}`, data);
1123
1177
  }
1124
1178
  /**
1125
1179
  * Get a schedule by ID
@@ -1145,9 +1199,7 @@ var Extraction = class {
1145
1199
  if (request.limit) params.set("limit", request.limit.toString());
1146
1200
  if (request.cursor) params.set("cursor", request.cursor);
1147
1201
  const query = params.toString();
1148
- const response = await this.http.get(
1149
- `/webdata/schedules${query ? `?${query}` : ""}`
1150
- );
1202
+ const response = await this.http.get(`/webdata/schedules${query ? `?${query}` : ""}`);
1151
1203
  return {
1152
1204
  ...response,
1153
1205
  items: response.items.map((item) => this.convertScheduleDates(item))
@@ -1161,8 +1213,13 @@ var Extraction = class {
1161
1213
  if (request.environment) params.set("environment", request.environment);
1162
1214
  if (request.projectId) params.set("projectId", request.projectId);
1163
1215
  const query = params.toString();
1164
- return this.http.delete(
1165
- `/webdata/schedules/${request.id}${query ? `?${query}` : ""}`
1216
+ return this.http.deleteWithBody(
1217
+ `/webdata/schedules/${request.id}${query ? `?${query}` : ""}`,
1218
+ {
1219
+ id: request.id,
1220
+ environment: request.environment,
1221
+ projectId: request.projectId
1222
+ }
1166
1223
  );
1167
1224
  }
1168
1225
  /**
@@ -1200,9 +1257,7 @@ var Extraction = class {
1200
1257
  if (request.periodStart) params.set("periodStart", request.periodStart);
1201
1258
  if (request.periodEnd) params.set("periodEnd", request.periodEnd);
1202
1259
  const query = params.toString();
1203
- const response = await this.http.get(
1204
- `/webdata/usage${query ? `?${query}` : ""}`
1205
- );
1260
+ const response = await this.http.get(`/webdata/usage${query ? `?${query}` : ""}`);
1206
1261
  return this.convertUsageDates(response);
1207
1262
  }
1208
1263
  // ==========================================================================
@@ -1343,7 +1398,14 @@ var Webdata = class {
1343
1398
  if (request.environment) params.set("environment", request.environment);
1344
1399
  if (request.projectId) params.set("projectId", request.projectId);
1345
1400
  const query = params.toString();
1346
- return this.http.delete(`/webdata/screenshots/${request.id}${query ? `?${query}` : ""}`);
1401
+ return this.http.deleteWithBody(
1402
+ `/webdata/screenshots/${request.id}${query ? `?${query}` : ""}`,
1403
+ {
1404
+ id: request.id,
1405
+ environment: request.environment,
1406
+ projectId: request.projectId
1407
+ }
1408
+ );
1347
1409
  }
1348
1410
  /**
1349
1411
  * Capture a screenshot and wait for completion
@@ -1459,7 +1521,14 @@ var Webdata = class {
1459
1521
  if (request.environment) params.set("environment", request.environment);
1460
1522
  if (request.projectId) params.set("projectId", request.projectId);
1461
1523
  const query = params.toString();
1462
- return this.http.delete(`/webdata/extractions/${request.id}${query ? `?${query}` : ""}`);
1524
+ return this.http.deleteWithBody(
1525
+ `/webdata/extractions/${request.id}${query ? `?${query}` : ""}`,
1526
+ {
1527
+ id: request.id,
1528
+ environment: request.environment,
1529
+ projectId: request.projectId
1530
+ }
1531
+ );
1463
1532
  }
1464
1533
  /**
1465
1534
  * Extract content and wait for completion
@@ -1590,7 +1659,14 @@ var Webdata = class {
1590
1659
  if (request.environment) params.set("environment", request.environment);
1591
1660
  if (request.projectId) params.set("projectId", request.projectId);
1592
1661
  const query = params.toString();
1593
- return this.http.delete(`/webdata/schedules/${request.id}${query ? `?${query}` : ""}`);
1662
+ return this.http.deleteWithBody(
1663
+ `/webdata/schedules/${request.id}${query ? `?${query}` : ""}`,
1664
+ {
1665
+ id: request.id,
1666
+ environment: request.environment,
1667
+ projectId: request.projectId
1668
+ }
1669
+ );
1594
1670
  }
1595
1671
  /**
1596
1672
  * Toggle a schedule on or off
@@ -2163,6 +2239,367 @@ var Integrations = class {
2163
2239
  }
2164
2240
  };
2165
2241
 
2242
+ // src/marketing/client.ts
2243
+ var Marketing = class {
2244
+ http;
2245
+ constructor(config) {
2246
+ this.http = new HttpClient(config);
2247
+ }
2248
+ // ============================================================================
2249
+ // Trends
2250
+ // ============================================================================
2251
+ /**
2252
+ * Discover new trends from all sources
2253
+ *
2254
+ * @example
2255
+ * ```typescript
2256
+ * const { trendsDiscovered, trends } = await marketing.discoverTrends({
2257
+ * projectSlug: 'my-project',
2258
+ * environment: 'production',
2259
+ * });
2260
+ * console.log(`Discovered ${trendsDiscovered} new trends`);
2261
+ * ```
2262
+ */
2263
+ async discoverTrends(request) {
2264
+ return this.http.post("/marketing/trends/discover", request);
2265
+ }
2266
+ /**
2267
+ * List trends for a project
2268
+ *
2269
+ * @example
2270
+ * ```typescript
2271
+ * const trends = await marketing.listTrends({
2272
+ * projectSlug: 'my-project',
2273
+ * environment: 'production',
2274
+ * limit: 20,
2275
+ * });
2276
+ * ```
2277
+ */
2278
+ async listTrends(request) {
2279
+ return this.http.get(
2280
+ `/marketing/trends?${new URLSearchParams({
2281
+ projectSlug: request.projectSlug,
2282
+ environment: request.environment,
2283
+ ...request.status && { status: request.status },
2284
+ ...request.limit && { limit: request.limit.toString() }
2285
+ }).toString()}`
2286
+ );
2287
+ }
2288
+ /**
2289
+ * Get a single trend by ID
2290
+ */
2291
+ async getTrend(trendId) {
2292
+ const response = await this.http.get(`/marketing/trends/${trendId}`);
2293
+ return this.convertTrendDates(response);
2294
+ }
2295
+ // ============================================================================
2296
+ // Opportunities
2297
+ // ============================================================================
2298
+ /**
2299
+ * Generate content opportunities from active trends
2300
+ *
2301
+ * @example
2302
+ * ```typescript
2303
+ * const { opportunitiesGenerated, opportunities } = await marketing.generateOpportunities({
2304
+ * projectSlug: 'my-project',
2305
+ * environment: 'production',
2306
+ * });
2307
+ * console.log(`Generated ${opportunitiesGenerated} new content ideas`);
2308
+ * ```
2309
+ */
2310
+ async generateOpportunities(request) {
2311
+ return this.http.post("/marketing/opportunities/generate", request);
2312
+ }
2313
+ /**
2314
+ * List opportunities for a project
2315
+ *
2316
+ * @example
2317
+ * ```typescript
2318
+ * const opportunities = await marketing.listOpportunities({
2319
+ * projectSlug: 'my-project',
2320
+ * environment: 'production',
2321
+ * status: 'pending',
2322
+ * limit: 20,
2323
+ * });
2324
+ * ```
2325
+ */
2326
+ async listOpportunities(request) {
2327
+ const params = new URLSearchParams({
2328
+ projectSlug: request.projectSlug,
2329
+ environment: request.environment,
2330
+ ...request.status && { status: request.status },
2331
+ ...request.limit && { limit: request.limit.toString() }
2332
+ });
2333
+ return this.http.get(`/marketing/opportunities?${params.toString()}`);
2334
+ }
2335
+ /**
2336
+ * Get a single opportunity by ID
2337
+ */
2338
+ async getOpportunity(opportunityId) {
2339
+ const response = await this.http.get(`/marketing/opportunities/${opportunityId}`);
2340
+ return this.convertOpportunityDates(response);
2341
+ }
2342
+ /**
2343
+ * Dismiss an opportunity
2344
+ *
2345
+ * @example
2346
+ * ```typescript
2347
+ * await marketing.dismissOpportunity({ opportunityId: 'opp-id' });
2348
+ * ```
2349
+ */
2350
+ async dismissOpportunity(request) {
2351
+ return this.http.post(`/marketing/opportunities/${request.opportunityId}/dismiss`, {});
2352
+ }
2353
+ // ============================================================================
2354
+ // Content
2355
+ // ============================================================================
2356
+ /**
2357
+ * Create new marketing content
2358
+ *
2359
+ * @example
2360
+ * ```typescript
2361
+ * const content = await marketing.createContent({
2362
+ * projectSlug: 'my-project',
2363
+ * environment: 'production',
2364
+ * contentType: 'tiktok_slideshow',
2365
+ * title: 'How AI is Changing Marketing',
2366
+ * opportunityId: 'opp-id',
2367
+ * });
2368
+ * ```
2369
+ */
2370
+ async createContent(request) {
2371
+ const response = await this.http.post("/marketing/content", request);
2372
+ return this.convertContentDates(response);
2373
+ }
2374
+ /**
2375
+ * List content with filters
2376
+ *
2377
+ * @example
2378
+ * ```typescript
2379
+ * const content = await marketing.listContent({
2380
+ * projectSlug: 'my-project',
2381
+ * environment: 'production',
2382
+ * status: 'published',
2383
+ * limit: 20,
2384
+ * });
2385
+ * ```
2386
+ */
2387
+ async listContent(request) {
2388
+ const params = new URLSearchParams({
2389
+ projectSlug: request.projectSlug,
2390
+ environment: request.environment,
2391
+ ...request.status && { status: request.status },
2392
+ ...request.contentType && { contentType: request.contentType },
2393
+ ...request.approvalStatus && { approvalStatus: request.approvalStatus },
2394
+ ...request.limit && { limit: request.limit.toString() },
2395
+ ...request.offset && { offset: request.offset.toString() }
2396
+ });
2397
+ const response = await this.http.get(`/marketing/content?${params.toString()}`);
2398
+ return response.map((c) => this.convertContentDates(c));
2399
+ }
2400
+ /**
2401
+ * Get a single content by ID
2402
+ */
2403
+ async getContent(contentId) {
2404
+ const response = await this.http.get(`/marketing/content/${contentId}`);
2405
+ return this.convertContentDates(response);
2406
+ }
2407
+ /**
2408
+ * Update content
2409
+ *
2410
+ * @example
2411
+ * ```typescript
2412
+ * const updated = await marketing.updateContent({
2413
+ * contentId: 'content-id',
2414
+ * title: 'Updated Title',
2415
+ * status: 'published',
2416
+ * });
2417
+ * ```
2418
+ */
2419
+ async updateContent(request) {
2420
+ const { contentId, ...data } = request;
2421
+ const response = await this.http.patch(`/marketing/content/${contentId}`, data);
2422
+ return this.convertContentDates(response);
2423
+ }
2424
+ /**
2425
+ * Approve content for publishing
2426
+ *
2427
+ * @example
2428
+ * ```typescript
2429
+ * await marketing.approveContent({
2430
+ * contentId: 'content-id',
2431
+ * reviewNotes: 'Looks great!',
2432
+ * });
2433
+ * ```
2434
+ */
2435
+ async approveContent(request) {
2436
+ const response = await this.http.post(`/marketing/content/${request.contentId}/approve`, request);
2437
+ return this.convertContentDates(response);
2438
+ }
2439
+ /**
2440
+ * Reject content
2441
+ *
2442
+ * @example
2443
+ * ```typescript
2444
+ * await marketing.rejectContent({
2445
+ * contentId: 'content-id',
2446
+ * reviewNotes: 'Needs revisions',
2447
+ * });
2448
+ * ```
2449
+ */
2450
+ async rejectContent(request) {
2451
+ const response = await this.http.post(`/marketing/content/${request.contentId}/reject`, request);
2452
+ return this.convertContentDates(response);
2453
+ }
2454
+ /**
2455
+ * Delete content
2456
+ */
2457
+ async deleteContent(contentId) {
2458
+ return this.http.deleteWithBody(`/marketing/content/${contentId}`, { contentId });
2459
+ }
2460
+ // ============================================================================
2461
+ // Scripts
2462
+ // ============================================================================
2463
+ /**
2464
+ * Create a new script
2465
+ *
2466
+ * @example
2467
+ * ```typescript
2468
+ * const script = await marketing.createScript({
2469
+ * projectSlug: 'my-project',
2470
+ * environment: 'production',
2471
+ * hook: 'Are you ready to see the future?',
2472
+ * slides: [
2473
+ * { order: 0, text: 'AI is changing everything', voiceoverText: 'AI is transforming how we work', duration: 3 },
2474
+ * ],
2475
+ * cta: 'Follow for more insights!',
2476
+ * });
2477
+ * ```
2478
+ */
2479
+ async createScript(request) {
2480
+ const response = await this.http.post("/marketing/scripts", request);
2481
+ return this.convertScriptDates(response);
2482
+ }
2483
+ /**
2484
+ * List scripts
2485
+ */
2486
+ async listScripts(request) {
2487
+ const params = new URLSearchParams({
2488
+ projectSlug: request.projectSlug,
2489
+ environment: request.environment,
2490
+ ...request.contentId && { contentId: request.contentId },
2491
+ ...request.limit && { limit: request.limit.toString() }
2492
+ });
2493
+ const response = await this.http.get(`/marketing/scripts?${params.toString()}`);
2494
+ return response.map((s) => this.convertScriptDates(s));
2495
+ }
2496
+ /**
2497
+ * Get a single script by ID
2498
+ */
2499
+ async getScript(scriptId) {
2500
+ const response = await this.http.get(`/marketing/scripts/${scriptId}`);
2501
+ return this.convertScriptDates(response);
2502
+ }
2503
+ // ============================================================================
2504
+ // Analytics
2505
+ // ============================================================================
2506
+ /**
2507
+ * Get analytics overview
2508
+ *
2509
+ * @example
2510
+ * ```typescript
2511
+ * const analytics = await marketing.getAnalyticsOverview({
2512
+ * projectSlug: 'my-project',
2513
+ * environment: 'production',
2514
+ * });
2515
+ * console.log(`Total content: ${analytics.totalContent}`);
2516
+ * console.log(`Total views: ${analytics.engagement.views}`);
2517
+ * ```
2518
+ */
2519
+ async getAnalyticsOverview(request) {
2520
+ const params = new URLSearchParams({
2521
+ projectSlug: request.projectSlug,
2522
+ environment: request.environment,
2523
+ ...request.startDate && { startDate: request.startDate.toISOString() },
2524
+ ...request.endDate && { endDate: request.endDate.toISOString() }
2525
+ });
2526
+ return this.http.get(`/marketing/analytics/overview?${params.toString()}`);
2527
+ }
2528
+ /**
2529
+ * Get content performance metrics
2530
+ *
2531
+ * @example
2532
+ * ```typescript
2533
+ * const topContent = await marketing.getContentPerformance({
2534
+ * projectSlug: 'my-project',
2535
+ * environment: 'production',
2536
+ * contentType: 'tiktok_slideshow',
2537
+ * limit: 10,
2538
+ * });
2539
+ * ```
2540
+ */
2541
+ async getContentPerformance(request) {
2542
+ const params = new URLSearchParams({
2543
+ projectSlug: request.projectSlug,
2544
+ environment: request.environment,
2545
+ ...request.contentType && { contentType: request.contentType },
2546
+ ...request.limit && { limit: request.limit.toString() }
2547
+ });
2548
+ return this.http.get(`/marketing/analytics/performance?${params.toString()}`);
2549
+ }
2550
+ // ============================================================================
2551
+ // Date Conversion Helpers
2552
+ // ============================================================================
2553
+ convertTrendDates(trend) {
2554
+ if (typeof trend.firstSeenAt === "string") {
2555
+ trend.firstSeenAt = new Date(trend.firstSeenAt);
2556
+ }
2557
+ if (typeof trend.lastUpdatedAt === "string") {
2558
+ trend.lastUpdatedAt = new Date(trend.lastUpdatedAt);
2559
+ }
2560
+ if (trend.expiresAt && typeof trend.expiresAt === "string") {
2561
+ trend.expiresAt = new Date(trend.expiresAt);
2562
+ }
2563
+ if (typeof trend.createdAt === "string") {
2564
+ trend.createdAt = new Date(trend.createdAt);
2565
+ }
2566
+ return trend;
2567
+ }
2568
+ convertOpportunityDates(opp) {
2569
+ if (typeof opp.createdAt === "string") {
2570
+ opp.createdAt = new Date(opp.createdAt);
2571
+ }
2572
+ if (opp.expiresAt && typeof opp.expiresAt === "string") {
2573
+ opp.expiresAt = new Date(opp.expiresAt);
2574
+ }
2575
+ if (opp.usedAt && typeof opp.usedAt === "string") {
2576
+ opp.usedAt = new Date(opp.usedAt);
2577
+ }
2578
+ return opp;
2579
+ }
2580
+ convertContentDates(content) {
2581
+ if (typeof content.createdAt === "string") {
2582
+ content.createdAt = new Date(content.createdAt);
2583
+ }
2584
+ if (content.updatedAt && typeof content.updatedAt === "string") {
2585
+ content.updatedAt = new Date(content.updatedAt);
2586
+ }
2587
+ if (content.reviewedAt && typeof content.reviewedAt === "string") {
2588
+ content.reviewedAt = new Date(content.reviewedAt);
2589
+ }
2590
+ if (content.publishedAt && typeof content.publishedAt === "string") {
2591
+ content.publishedAt = new Date(content.publishedAt);
2592
+ }
2593
+ return content;
2594
+ }
2595
+ convertScriptDates(script) {
2596
+ if (typeof script.createdAt === "string") {
2597
+ script.createdAt = new Date(script.createdAt);
2598
+ }
2599
+ return script;
2600
+ }
2601
+ };
2602
+
2166
2603
  // src/index.ts
2167
2604
  var Stack0 = class {
2168
2605
  mail;
@@ -2170,6 +2607,7 @@ var Stack0 = class {
2170
2607
  screenshots;
2171
2608
  extraction;
2172
2609
  integrations;
2610
+ marketing;
2173
2611
  /**
2174
2612
  * @deprecated Use `screenshots` and `extraction` instead. Will be removed in a future version.
2175
2613
  */
@@ -2180,10 +2618,11 @@ var Stack0 = class {
2180
2618
  baseUrl: config.baseUrl
2181
2619
  };
2182
2620
  this.mail = new Mail(clientConfig);
2183
- this.cdn = new CDN(clientConfig);
2621
+ this.cdn = new CDN(clientConfig, config.cdnUrl);
2184
2622
  this.screenshots = new Screenshots(clientConfig);
2185
2623
  this.extraction = new Extraction(clientConfig);
2186
2624
  this.integrations = new Integrations(clientConfig);
2625
+ this.marketing = new Marketing(clientConfig);
2187
2626
  this.webdata = new Webdata(clientConfig);
2188
2627
  }
2189
2628
  };
@@ -2193,6 +2632,7 @@ exports.CDN = CDN;
2193
2632
  exports.Extraction = Extraction;
2194
2633
  exports.Integrations = Integrations;
2195
2634
  exports.Mail = Mail;
2635
+ exports.Marketing = Marketing;
2196
2636
  exports.Screenshots = Screenshots;
2197
2637
  exports.Stack0 = Stack0;
2198
2638
  exports.Webdata = Webdata;